Commit Graph

2 Commits

Author SHA1 Message Date
Croissant Le Doux
dbcdcda5e7 feat: stream Reginald's replies token-by-token
The 26b is slow (~30s/call); the chat now shows the answer forming instead of
freezing until it's done. The final prose streams over SSE; tool-calling turns
stay structured (no partial tokens), so streaming kicks in for the narration.

core (@commitea/core):
- chat-client.complete gains an optional onToken — when set, it requests
  stream:true and parses the OpenAI SSE stream, emitting content deltas and
  assembling streamed tool-call argument fragments into the final result.
- GiteaHttpResponse exposes the optional `body` stream (real fetch has it; stubs
  don't). agent-loop threads onToken to each completion.

app:
- model:chat forwards each delta to the renderer (event.sender.send); preload
  exposes model.onToken(cb) → unsubscribe. useChat accumulates the live stream
  into a growing bubble (with a cursor), replaced by the authoritative final
  content when the turn resolves. Unconfigured → scripted reply, unchanged.

Verified: 118 core tests green (2 streaming: SSE content deltas + tool-call
fragment assembly), desktop typecheck clean, 14 fixture e2e green. Live: a real
turn against gemma-4-26b assembles the correct answer via the streaming path
(live-reginald green) — the reply now renders token-by-token.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 00:22:12 -04:00
Croissant Le Doux
3de887417c feat: Reginald is real — model router + agent loop + query_project (P4)
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>
2026-07-08 21:23:14 -04:00