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>
23 lines
827 B
TypeScript
23 lines
827 B
TypeScript
/**
|
|
* Model router (per PLAN.md / decisions.md): prose + the read tool run on a
|
|
* small local model; decomposition/negotiation earns the big one. Reginald v0
|
|
* only exercises the small model (read + prose); the `big` slot is declared so
|
|
* `capture_work` / `record_directive` can route to it without a rewrite.
|
|
*/
|
|
|
|
import type { ModelConfig } from './chat-client.js'
|
|
|
|
export type TaskKind = 'ritual' | 'plan'
|
|
|
|
export interface ModelRouter {
|
|
/** Small local model — standups, focus prose, the read tool. */
|
|
small: ModelConfig
|
|
/** Big model — capture decomposition, directive negotiation. */
|
|
big: ModelConfig
|
|
}
|
|
|
|
/** `plan` → the big model; everything else → the small one. */
|
|
export function pickModel(router: ModelRouter, kind: TaskKind): ModelConfig {
|
|
return kind === 'plan' ? router.big : router.small
|
|
}
|