feat(invoices): M8 AI tools — create/mark_paid/list/stats

The last open item from the plan. Missions can now draft invoices from
chat context, mark customer payments, and read status for autonomous
follow-up cadences.

Tool catalog (packages/shared-ai/src/tools/schemas.ts)
- create_invoice (propose) — clientName + lines[] + currency + due
- mark_invoice_paid (propose) — by id, optional back-dated paidAt
- list_invoices (auto) — with status + limit filter
- get_invoice_stats (auto) — open/overdue/YTD per currency

Had to widen the tool-parameter type vocabulary so create_invoice can
declare lines as a typed array. Touched three places:
- ToolSchema-side: the catalog's `type` string is already free-form so
  'array' / 'object' just pass through
- ModuleTool-side (apps/mana/apps/web/src/lib/data/tools/types.ts): added
  'array' | 'object' to the union so TS doesn't narrow the executor's
  param signatures
- function-schema translator (packages/shared-ai): mapParamType +
  JsonSchemaProperty both gained the two new types; the catalog-typo
  guard test now uses 'fruit' as its sentinel (array no longer unknown)

Executor (apps/mana/apps/web/src/lib/modules/invoices/tools.ts)
- coerceLines accepts either a real array or a JSON-stringified array
  (planners vary), skips malformed entries, converts major→minor units
- create_invoice pulls the generated number back from Dexie so the
  success message shows "Entwurf 2026-0042 …" — the user recognises it
- mark_invoice_paid normalises YYYY-MM-DD → ISO so the store's timestamp
  invariant (ISO throughout) stays intact
- list_invoices derives overdue on read (consistent with useAllInvoices),
  returns major-unit amounts so the LLM reasons in user-facing numbers
- get_invoice_stats returns counts + open/overdue/YTD per currency

Registration: invoicesTools added to tools/init.ts. mana-ai drift guard
is happy (41/41 green); webapp + shared-ai type-check 0 errors; full
invoice test suite 59/59 green.

Closes: docs/plans/invoices-module.md §M8. All plan milestones now DONE.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-20 18:22:20 +02:00
parent 0d613e1846
commit a4bc7d2ee3
7 changed files with 427 additions and 4 deletions

View file

@ -16,9 +16,14 @@
import type { ToolSchema } from './schemas';
/** OpenAI-compatible JSON-Schema property type. */
/** OpenAI-compatible JSON-Schema property type. `array` / `object` are
* emitted without a nested `items` / `properties` schema tools that
* take structured payloads describe the expected shape inside their
* human-readable description and parse in the executor. Tightening to
* a full JSON-Schema tree would be strictly better but isn't required
* by the OpenAI / Anthropic function-calling specs. */
export interface JsonSchemaProperty {
type: 'string' | 'number' | 'integer' | 'boolean';
type: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object';
description?: string;
enum?: readonly string[];
}
@ -53,6 +58,8 @@ function mapParamType(t: string): JsonSchemaProperty['type'] {
case 'number':
case 'integer':
case 'boolean':
case 'array':
case 'object':
return t;
default:
// Unknown types in the catalog are a bug, but don't silently