mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-21 02:26:42 +02:00
Migrates the background tick from buildPlannerPrompt + PlannerClient +
parsePlannerResponse to the shared runPlannerLoop with native function
calling. Structurally identical to the webapp runner (commit 5a) —
same catalog, same compact system prompt, same multi-turn chat.
Server-specific twist: the ``onToolCall`` callback is a no-op stub
(returns {success:true, message:'recorded — pending client
application'}). The server has no Dexie access, so it can't actually
execute writes; instead it captures the LLM's chosen tool_calls and
writes them as PlanStep entries on the iteration. The user's client
picks up those planned steps on sync — same shape as before, just
sourced from the LLM's native tool_calls instead of a regex-extracted
JSON block.
Scope trimmed by the SERVER_TOOLS filter: only propose-default (write)
tools go to the server planner. Read-only tools (list_*, get_*) are
hidden because stubbing a response would let the LLM hallucinate that
it saw real data. Read-then-act chains stay with the foreground
runner, which has a real executor.
Deleted: planner/client.ts (old PlannerClient; replaced by
planner/llm-client.ts). Drift guard in tools.ts collapses into a
SERVER_TOOLS = AI_TOOL_CATALOG.filter(propose) derivation — no more
hand-maintained duplicate list; the contract test now asserts the
inverse round-trip against AI_PROPOSABLE_TOOL_SET.
TODO (follow-up): token usage tracking is temporarily set to 0 because
runPlannerLoop doesn't expose per-message usage yet. Budget
enforcement on the server is effectively disabled until the loop
returns that data — the webapp runner is unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
41 lines
1.2 KiB
TypeScript
41 lines
1.2 KiB
TypeScript
import { describe, it, expect } from 'bun:test';
|
|
import { AI_PROPOSABLE_TOOL_SET } from '@mana/shared-ai';
|
|
import { SERVER_TOOLS, SERVER_TOOL_NAMES } from './tools';
|
|
|
|
describe('SERVER_TOOLS contract', () => {
|
|
it('every server tool is in the shared proposable set', () => {
|
|
for (const tool of SERVER_TOOLS) {
|
|
expect(
|
|
AI_PROPOSABLE_TOOL_SET.has(tool.name),
|
|
`"${tool.name}" missing from @mana/shared-ai AI_PROPOSABLE_TOOL_NAMES`
|
|
).toBe(true);
|
|
}
|
|
});
|
|
|
|
it('every shared proposable is reachable from the server', () => {
|
|
for (const name of AI_PROPOSABLE_TOOL_SET) {
|
|
expect(
|
|
SERVER_TOOL_NAMES.has(name),
|
|
`"${name}" missing from SERVER_TOOLS — catalog propose-tool not exposed`
|
|
).toBe(true);
|
|
}
|
|
});
|
|
|
|
it('every tool has a name, module, and description', () => {
|
|
for (const tool of SERVER_TOOLS) {
|
|
expect(tool.name.length).toBeGreaterThan(0);
|
|
expect(tool.module.length).toBeGreaterThan(0);
|
|
expect(tool.description.length).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
|
|
it('required params carry a non-empty description', () => {
|
|
for (const tool of SERVER_TOOLS) {
|
|
for (const p of tool.parameters) {
|
|
if (p.required) {
|
|
expect(p.description.length, `${tool.name}.${p.name}.description`).toBeGreaterThan(0);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|