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>
This commit is contained in:
46
packages/core/src/agent/agent-tools.ts
Normal file
46
packages/core/src/agent/agent-tools.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Reginald's tool surface. v0 ships the one read tool (`query_project`); the
|
||||
* three write tools (capture_work, apply_changes, record_directive) layer on
|
||||
* later against the same loop. Few, fat tools so a small local model survives
|
||||
* with one thing to reach for (docs/agent-tools.md).
|
||||
*/
|
||||
|
||||
import type { ToolDecl } from './chat-client.js'
|
||||
|
||||
export const QUERY_PROJECT_TOOL: ToolDecl = {
|
||||
name: 'query_project',
|
||||
description:
|
||||
'Read the current project state. A `view` selects the shape; deterministic code (scheduler, ' +
|
||||
'lifecycle inference, calibration) backs every number — you report it, you never compute it.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
view: {
|
||||
type: 'string',
|
||||
enum: ['focus', 'board', 'calibration', 'issue', 'search'],
|
||||
description:
|
||||
'focus = Now/Next/Later; board = issues by lifecycle column; calibration = estimate-vs-actual; ' +
|
||||
'issue = one issue (needs filters.issueId); search = issues matching filters.query.',
|
||||
},
|
||||
filters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
issueId: { type: 'number' },
|
||||
query: { type: 'string' },
|
||||
limit: { type: 'number' },
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['view'],
|
||||
},
|
||||
}
|
||||
|
||||
export const REGINALD_TOOLS: ToolDecl[] = [QUERY_PROJECT_TOOL]
|
||||
|
||||
export const REGINALD_SYSTEM = [
|
||||
'You are Reginald, the calm, dry project manager inside CommiTea — a tool that runs projects on Gitea.',
|
||||
'Call query_project to ground every answer in the real project; never invent issues, numbers, or dates.',
|
||||
'The scheduler and forecasts are deterministic code — report their output, do not recompute it.',
|
||||
'Forecasts are ranges, never single dates. Refer to issues as #<number>.',
|
||||
'Be brief and plain. A sentence or two is usually enough. No preamble, no bullet-point dumps.',
|
||||
].join(' ')
|
||||
Reference in New Issue
Block a user