MEGAINFRA-WEBAPP/docs/ask-architecture.md
DEls ed69b517e8 Integrate agent work and add dashboard attention indicators
Remove repeated Plant, Financial and Workforce shortcut cards. Add site-scoped Plant and Financial sidebar attention status with automatic refresh.

Include accumulated delivery processing, purchase order recovery, supplier reconciliation, plant data and user management changes and audit records.
2026-10-04 21:53:32 +02:00

78 lines
5.4 KiB
Markdown

# How "Ask" works (MEGA) — brief for building the same thing in Healy
## Correction first
The in-app chat does NOT use MCP. It calls tool functions directly inside the Next.js server.
MCP (`src/mcp/server.ts`) is a separate, optional, read-only wrapper over the same tools, run locally
over stdio for Claude Desktop/Code. Build the tool layer first; treat MCP as a thin extra.
## Stack
- Next.js 16 (App Router), React 19, TypeScript, Tailwind 4, vitest
- PocketBase via `pocketbase` SDK, superuser client on the server only (`pbAdmin()`)
- Model: MiniMax M3 via OpenAI-compatible endpoint (`https://api.minimax.io/v1`); Anthropic `claude-opus-5`
behind the same interface. `CHAT_PROVIDER` selects.
- Streaming: SSE from `POST /api/chat` (plain `ReadableStream`; browser reads with `fetch`)
- Tool schemas: `zod` -> `zod-to-json-schema`
- UI markdown: `react-markdown` + `remark-gfm` mapped onto the app's own table styles
## Architecture
1. `src/lib/chat/` — provider-neutral core: `types.ts` (no SDK imports), `loop.ts` (model -> tools -> model,
with max-iterations valve), `provider-anthropic.ts`, `provider-openai.ts` (one SDK each),
`system-prompt.ts` (built per request: date + user memories), `think-filter.ts`.
2. `src/lib/agent/` — tools: `tools.ts` (read tools), `tools-actions.ts` (write tools), `load.ts` (fetching),
`format.ts` (shaping + `display` strings), `resolve.ts` (name -> id), `query.ts` (generic `query_records`),
`gateway.ts` (the write seam), `schema-cache.ts`.
3. `src/lib/metrics/` — ALL business calculations as pure tested functions, shared by dashboard and agent.
4. `/api/chat` — env gate, identify user, persist thread, run loop, stream events.
5. `/dashboard/chat` — client SSE reader, threads, attachments, confirm cards, charts, PDF.
## Rules that make it trustworthy
- The model never does arithmetic. Tools return finished numbers + preformatted `display`. Prompt bans
sum/average/percent/"combined" and says what to do instead (list rows, say aggregate not available).
The first wording failed a live probe — keep "combined value of top three" as a regression probe.
- Name resolution returns ambiguity lists instead of guessing.
- `query_records`: typed generic read, filters bound with `pb.filter()`, grouping/aggregation in JS.
- Tool failures go back to the model as error results and are logged server-side.
## Writes
Model calls `propose_changes` -> pending row in `ask_log` -> confirm card -> human presses Confirm ->
`applyAction` writes -> each op keeps a `before` snapshot -> Undo. Ops validated against live schema.
`authorize()` is the single permission seam with a `NEVER_WRITABLE` set (`_superusers`, `users`, chat tables).
Ops run sequentially so `$ref:` can reference records created earlier in the batch.
**For Healy: remove the `delete` op kind entirely** (union type, planner, apply). Decide deliberately
whether Undo may delete records the agent itself just created (recommended: yes, and nothing else).
## Gotchas
MiniMax
- M2/M2.7 are text-only. M3 is the first accepting BOTH `tools` and images. Test both together.
- Use the OpenAI-compatible route; the `/anthropic` shim mangles tool-call args.
- Reasoning streams inline in `<think>…</think>`; strip it with a stateful filter (tags split across chunks).
- Tool-call args arrive as string fragments per index; concatenate before `JSON.parse`.
- Key must be an Open Platform API key (not Token Plan subscription key, not Hailuo/MiniMax Agent).
- Image endpoint: `{base minus /v1}/v1/image_generation` (appending /v1 to base gave /v1/v1).
Anthropic
- `budget_tokens` and assistant prefill are removed (400). Use `thinking: {type: "adaptive"}`.
PocketBase
- `sort=-created` / `-updated` can 400 with an empty body. Sort a real field or use autodate fields.
- `getList(1, 500)` silently truncates; use `getFullList`.
- `fields:` that omits a date field makes later date logic silently skip rows.
- A detail-less 400 is usually a bad filter/sort; reproduce with a direct `getFullList`.
Chat plumbing
- History crosses JSON; types guarantee nothing. Normalise in the route (assistant turns arrive without `toolCalls`).
- Compare `Origin` to the `Host` header, not `request.url`.
- Agent loop needs more than 8 iterations for "read file -> match -> propose".
## Brief to give the Healy agent
> Build a chat agent for Healy shaped like MEGA's "Ask". Next.js + PocketBase, SSE from `POST /api/chat`.
> Provider-neutral loop with two adapters (MiniMax M3 via OpenAI-compatible endpoint; Anthropic fallback).
> Tools are zod-schema'd functions; all business maths lives in pure tested functions shared with the UI.
> Reads: typed tools per domain + generic `query_records`; model never does arithmetic; tools return `display`.
> Writes: create and update only, NO delete. Propose -> pending log row -> human confirm -> apply, with
> `before` snapshot and undo; validate against live schema; keep a never-writable list.
> API access: expose the same handlers as REST and as an MCP server, both authenticated.
> Also: strip `<think>`, reassemble fragmented tool args, normalise history at the route, log tool failures.
## Decide deliberately (API + MCP + writes)
- Auth: Ask used an `x-ask-user` header + shared PIN during testing. A create/update API or MCP surface needs
real per-user authentication.
- MCP is headless (no human to confirm). Either keep MCP read-only, or let it only create PENDING actions a
person confirms in the app (recommended).