fix(pi): reject null tool arguments before pi-schema coercion

Draft
G0
2h ago
+421-2

Refs #843 (kept draft until the issue carries design-approved).

Harness coverage (required)

  • OpenCode 1: not applicable — surface exists, but its zod schema rejects null before the handler; no change needed.
  • OpenCode 2: not applicable — same zod path as OpenCode 1.
  • Pi: covered — prepareArguments added to ctx_note and ctx_memory; tests added for both.
  • OMP: covered — shares the same pi-plugin tool definitions and hook.
  • Rust module: not applicable — no tool-argument schema there.

What

pi 1.5.0 validates tool arguments with @earendil-works/pi-schema, which coerces loosely typed arguments before validation. For required properties, null is not dropped: a string schema receives "null" and an array schema receives [null], whose number items become 0. As a result:

  • ctx_note with content: null writes the literal text "null" as the note body.
  • ctx_memory with content: null writes the literal text "null" as the memory body.
  • ctx_memory with ids: null becomes [0]; get returns nothing instead of rejecting, and update/archive look up id 0.

This change adds ToolDefinition.prepareArguments to both tools. The hook runs before schema validation and before execute(), so it still sees the raw argument:

  • reject content === null in both tools;
  • reject ids when it is present and not an array;
  • accept the literal string "null" unchanged.

It also tightens ctx_memory.ids from Type.Array(Type.Number()) to Type.Array(Type.Integer({ minimum: 1 })), matching ctx_note.note_ids; memory ids start at 1.

Testing

bun test packages/pi-plugin/src/tools/ctx-memory.test.ts packages/pi-plugin/src/tools/ctx-note.test.ts
50 pass / 0 fail

bun test packages/pi-plugin/src/tools/
162 pass / 0 fail

bun run --cwd packages/pi-plugin typecheck
exit 0

biome check (from packages/pi-plugin, repo-local 2.5.1)
Checked 4 files, no fixes applied

The new tests were also run without the source change: exactly the nine new cases fail, so they cover the regression rather than merely passing.


Summary by cubic

Fixes pi 1.5.0's @earendil-works/pi-schema coercion mangling tool arguments in ctx_note and ctx_memory. content: null was written as the literal text "null", ids: null became [0] so get/update/archive targeted id 0, and fractional ids like 1.5 could be truncated into the integer that modified the wrong record.

  • Adds prepareArguments to both tools, which runs before schema validation and execute() and rejects any non-string content and non-array ids; the literal string "null" remains accepted.
  • Rejects fractional ids in execution so they can't modify existing records, and adds minimum: 1 to ctx_memory.ids, matching memory ids starting at 1.