P4: Reginald is real — model router + agent loop + query_project #42

Merged
christian merged 2 commits from p4/reginald into main 2026-07-09 01:28:40 +00:00
Owner

The fixture chat panel is now a working agent. Ask Reginald a question → it consults the real project through a tool loop → answers in grounded prose. Read-only v0 (writes still go through propose-approve). Branch cut from main, targets main — no stacking.

Core (@commitea/core/agent)

  • chat-client — OpenAI-wire chat completions over an injected fetch (same seam as gitea). Any OpenAI-compatible endpoint: LM Studio, Ollama, OpenAI.
  • model-router — small model for prose + the read tool; big model reserved for later decomposition.
  • agent-looprunAgentTurn drives call→tool→result→call until prose (or a step budget), recording each step. Injected complete + execute → fully testable offline.
  • query-project — the single read tool's engine: compact focus/board/calibration/issue/search views from scheduler + lifecycle + calibration. Unbuilt views return a notImplemented marker — never fabricated. The model reports, never computes.
  • agent-toolsquery_project declaration + Reginald's system prompt.

App

  • main model bridge (model:status, model:chat) runs the loop; query_project reconciles the repo and builds the view. Model traffic stays in main (token/CSP). gitea.ts refactored to share getGiteaClient + reconcileSnapshot.
  • preload + global.d.ts expose the bridge; useChat drives the panel — real agent turn when a model is configured, scripted fixture reply otherwise (fixture e2e unchanged). Subtle "consulted the project" activity line.

Config

Env, defaults to LM Studio on :1234: COMMITEA_MODEL_URL / _SMALL (google/gemma-4-e4b) / _BIG (qwen/qwen3.6-35b-a3b). COMMITEA_E2E=1 keeps it unconfigured so the panel stays scripted.

Verified

88 core tests green (14 agent: client parse, loop tool/error/step-budget, all views) + a gated live integration test against the real model. Desktop typecheck clean, 14 fixture e2e green.

Gated live e2e drives the real app against gitea + gemma-4-e4b: asked "What should I work on right now?", Reginald called query_project(focus) and answered "The current focus is on issue #2" — the real scheduler pick. Screenshot in the PR.

Next (Reginald, continued)

  • Writes via chat: route apply_changes (engine from #41) through the loop with mid-turn propose-approve — chat becomes the write-path in full.
  • Big-model routing for capture_work decomposition; record_directive → the pm-state ledger.
  • More query_project views (milestone/runway/standup) + streaming.

Depends on nothing outstanding; #41 (apply_changes) is independent and can merge in either order.

🤖 Generated with Claude Code

The fixture chat panel is now a **working agent**. Ask Reginald a question → it consults the real project through a tool loop → answers in grounded prose. Read-only v0 (writes still go through propose-approve). Branch cut from `main`, targets `main` — no stacking. ## Core (`@commitea/core/agent`) - **chat-client** — OpenAI-wire chat completions over an injected fetch (same seam as gitea). Any OpenAI-compatible endpoint: LM Studio, Ollama, OpenAI. - **model-router** — small model for prose + the read tool; big model reserved for later decomposition. - **agent-loop** — `runAgentTurn` drives call→tool→result→call until prose (or a step budget), recording each step. Injected `complete` + `execute` → fully testable offline. - **query-project** — the single read tool's engine: compact `focus`/`board`/`calibration`/`issue`/`search` views from scheduler + lifecycle + calibration. Unbuilt views return a `notImplemented` marker — **never fabricated**. The model reports, never computes. - **agent-tools** — `query_project` declaration + Reginald's system prompt. ## App - main **model bridge** (`model:status`, `model:chat`) runs the loop; `query_project` reconciles the repo and builds the view. **Model traffic stays in main** (token/CSP). `gitea.ts` refactored to share `getGiteaClient` + `reconcileSnapshot`. - preload + `global.d.ts` expose the bridge; **`useChat`** drives the panel — real agent turn when a model is configured, scripted fixture reply otherwise (fixture e2e unchanged). Subtle *"consulted the project"* activity line. ## Config Env, defaults to LM Studio on `:1234`: `COMMITEA_MODEL_URL` / `_SMALL` (`google/gemma-4-e4b`) / `_BIG` (`qwen/qwen3.6-35b-a3b`). `COMMITEA_E2E=1` keeps it unconfigured so the panel stays scripted. ## Verified 88 core tests green (14 agent: client parse, loop tool/error/step-budget, all views) + a **gated live integration test** against the real model. Desktop typecheck clean, 14 fixture e2e green. **Gated live e2e drives the real app** against gitea + `gemma-4-e4b`: asked *"What should I work on right now?"*, Reginald called `query_project(focus)` and answered *"The current focus is on issue #2"* — the real scheduler pick. Screenshot in the PR. ## Next (Reginald, continued) - **Writes via chat**: route `apply_changes` (engine from #41) through the loop with mid-turn propose-approve — chat becomes the write-path in full. - Big-model routing for `capture_work` decomposition; `record_directive` → the pm-state ledger. - More `query_project` views (milestone/runway/standup) + streaming. _Depends on nothing outstanding; #41 (apply_changes) is independent and can merge in either order._ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
christian added 1 commit 2026-07-09 00:43:35 +00:00
The fixture chat panel is now a working agent. Ask Reginald a question and it
consults the real project through a tool loop, then answers in grounded prose.
Read-only v0 — writes still go through the propose-approve controls.

core (@commitea/core/agent):
- chat-client: OpenAI-wire chat completions over an injected fetch (same seam as
  gitea). Points at any OpenAI-compatible endpoint (LM Studio/Ollama/OpenAI).
- model-router: small model for prose + the read tool; big model reserved for
  later decomposition (pickModel).
- agent-loop: runAgentTurn drives call→tool→result→call until prose (or a step
  budget), recording each tool step. Injected complete + execute → fully testable.
- query-project: the single read tool's engine — compact focus/board/calibration/
  issue/search views built from scheduler + lifecycle + calibration; unbuilt views
  return a notImplemented marker (never fabricated). The model reports, never computes.
- agent-tools: query_project declaration + Reginald's system prompt.

app:
- main model bridge (model:status, model:chat) runs the loop; query_project
  reconciles the repo and builds the view. Model traffic stays in main (token/CSP).
  gitea.ts refactored to share getGiteaClient + reconcileSnapshot.
- preload + global.d.ts expose the model bridge; useChat drives the panel — real
  agent turn when a model is configured, scripted fixture reply otherwise (so
  fixture e2e is unchanged). A subtle "consulted the project" activity line.

Model config (env, defaults to LM Studio on :1234): COMMITEA_MODEL_URL /
_SMALL (google/gemma-4-e4b) / _BIG (qwen/qwen3.6-35b-a3b). COMMITEA_E2E=1 keeps
it unconfigured so the panel stays scripted.

Verified: 88 core tests green (14 agent: client parse, loop tool/error/budget,
all views) + a gated live integration test. Desktop typecheck clean, 14 fixture
e2e green. Gated live e2e drives the real app against gitea + gemma-4-e4b: asked
"what now?", Reginald called query_project and answered "focus is on issue #2"
(the real scheduler pick).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian added 1 commit 2026-07-09 01:03:26 +00:00
Instead of a hardcoded model name (which forces LM Studio to JIT-swap your loaded
model out — and fails when a big model already fills memory), resolve the model
at request time: an explicit env override wins, else ask the server which model
is *loaded* (LM Studio's native /api/v0/models), else the first non-embedding
model, else a default. Reginald now uses whatever you load, no config churn.

- main/model.ts: resolveLoadedModel() drives both model:status and model:chat;
  COMMITEA_MODEL_SMALL still overrides.
- useChat exposes the resolved model id; the panel header shows it
  (google/gemma-4-26b-a4b-qat → "gemma-4-26b-a4b · local").
- live-reginald e2e: header assertion relaxed to the loaded model; timeouts
  raised for a slow big local model (~2 calls/turn + a reconcile).

Verified: 14 fixture e2e green; live e2e drives the app against the loaded
gemma-4-26b — "What now?" → "You should work on #2 … on the critical path,
unblocks #33 and #4" (the real scheduler pick), header shows the live model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
christian force-pushed p4/reginald from d70c6e2542 to 25cf0a6d39 2026-07-09 01:26:07 +00:00 Compare
christian merged commit 4fc2e83cd5 into main 2026-07-09 01:28:40 +00:00
Sign in to join this conversation.
No description provided.