@paigy/mcp 0.40.40 → 0.40.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,22 +1,23 @@
1
1
  // ../../packages/schema/dist/index.js
2
- import { z as z12 } from "zod";
2
+ import { z as z13 } from "zod";
3
3
  import { z } from "zod";
4
4
  import { z as z3 } from "zod";
5
5
  import { z as z2 } from "zod";
6
- import { z as z5 } from "zod";
6
+ import { z as z6 } from "zod";
7
7
  import { zodToJsonSchema } from "zod-to-json-schema";
8
8
  import { z as z4 } from "zod";
9
- import { z as z6 } from "zod";
10
- import { zodToJsonSchema as zodToJsonSchema2 } from "zod-to-json-schema";
9
+ import { z as z5 } from "zod";
11
10
  import { z as z7 } from "zod";
12
- import { zodToJsonSchema as zodToJsonSchema3 } from "zod-to-json-schema";
11
+ import { zodToJsonSchema as zodToJsonSchema2 } from "zod-to-json-schema";
13
12
  import { z as z8 } from "zod";
14
- import { zodToJsonSchema as zodToJsonSchema4 } from "zod-to-json-schema";
13
+ import { zodToJsonSchema as zodToJsonSchema3 } from "zod-to-json-schema";
15
14
  import { z as z9 } from "zod";
16
- import { zodToJsonSchema as zodToJsonSchema5 } from "zod-to-json-schema";
15
+ import { zodToJsonSchema as zodToJsonSchema4 } from "zod-to-json-schema";
17
16
  import { z as z10 } from "zod";
18
- import { zodToJsonSchema as zodToJsonSchema6 } from "zod-to-json-schema";
17
+ import { zodToJsonSchema as zodToJsonSchema5 } from "zod-to-json-schema";
19
18
  import { z as z11 } from "zod";
19
+ import { zodToJsonSchema as zodToJsonSchema6 } from "zod-to-json-schema";
20
+ import { z as z12 } from "zod";
20
21
  import { zodToJsonSchema as zodToJsonSchema7 } from "zod-to-json-schema";
21
22
  var OPTIONS_MIN = 2;
22
23
  var OPTIONS_MAX = 6;
@@ -120,7 +121,7 @@ var ASKING_RULES = `HOW TO ASK:
120
121
  6. Read the Goal before asking; never re-ask what was answered or shipped. \`replied\` means they chose no option: it is still yours to settle.
121
122
  7. Give options when you have them. Each label names its choice in full (never "Option 2"); never add "Other". \`pickMode\`: one (default), many, rank.
122
123
  8. A diagnosis says when, why and how it happens, then proposes one fix.
123
- 9. Give a report its \`reply\`: one line in THEIR voice for THAT report, so you learn whether they verified it or only received it. Required when it reaches them or completes a Goal.`;
124
+ 9. Give a report its \`reply\`: one line in THEIR voice for THAT report, so you learn whether they verified it or only received it. Required when it is delivered or its Goal is done.`;
124
125
  function draft2020(node) {
125
126
  if (Array.isArray(node)) return node.map(draft2020);
126
127
  if (node && typeof node === "object") {
@@ -218,11 +219,11 @@ var questionFields = {
218
219
  ),
219
220
  goalId: z4.string().uuid().describe("The Goal the question is about, which must exist (create it with manage_goals first). Its answer comes back there."),
220
221
  units,
221
- options: z4.array(OptionInputSchema).min(1).max(6).optional().describe("The choices, when you have them. Each label stands on its own, at most 50 characters; a hint, at most 200. They can always answer in their own words, so no 'Other' or 'None of these'."),
222
+ options: z4.array(OptionInputSchema).min(1).max(6).optional().describe("The choices, when you have them. Each label stands on its own and differs from the others (duplicate_option_label), at most 50 characters; a hint, at most 200. They can always answer in their own words, so no 'Other' or 'None of these'."),
222
223
  // HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
223
224
  // brain_prompts.md §1: structured agent questions need no model). The app draws all three.
224
225
  pickMode: z4.enum(["one", "many", "rank"]).optional().describe(
225
- 'How the options are answered: "one" (the default) a pick, for alternatives; "many" a checklist, for independent options they may want several of; "rank" an order of all of them. Only with options.'
226
+ 'How the options are answered: "one" (the default) a pick, for alternatives; "many" a checklist, for independent options they may want several of; "rank" an order of all of them. Only with options; "many" and "rank" need at least two (too_few_options).'
226
227
  ),
227
228
  // WHAT THE QUESTION HOLDS UP (brain_prompts.md §2.1): replaces `waiting: 'hard'`. A question that
228
229
  // blocks work asks for a call (the person's settings and ring cooldowns still decide).
@@ -234,11 +235,39 @@ var questionFields = {
234
235
  var refineQuestion = (q, ctx) => {
235
236
  refuseOverCaps(q.question, q.units, "question", ctx);
236
237
  if (q.pickMode && !q.options) ctx.addIssue({ code: z4.ZodIssueCode.custom, path: ["pickMode"], message: "pickMode comes only with options" });
238
+ refuseThinOptions(q, ctx);
237
239
  const ids = (q.options ?? []).map((o) => o.id);
238
240
  if (ids.some((id2) => id2) && (ids.some((id2) => !id2) || new Set(ids).size !== ids.length)) {
239
241
  ctx.addIssue({ code: z4.ZodIssueCode.custom, path: ["options"], message: "give every option a distinct id, or none" });
240
242
  }
241
243
  };
244
+ var MIN_LIST_OPTIONS = 2;
245
+ function refuseThinOptions(q, ctx) {
246
+ const options = q.options ?? [];
247
+ if ((q.pickMode === "many" || q.pickMode === "rank") && options.length < MIN_LIST_OPTIONS) {
248
+ ctx.addIssue({
249
+ code: z4.ZodIssueCode.custom,
250
+ path: ["options"],
251
+ params: { refusal: "too_few_options", length: options.length, min: MIN_LIST_OPTIONS },
252
+ message: `too_few_options: pickMode "${q.pickMode}" needs at least ${MIN_LIST_OPTIONS} options; this question has ${options.length}. Add the other choices, or leave pickMode out.`
253
+ });
254
+ }
255
+ const seen = /* @__PURE__ */ new Map();
256
+ options.forEach((o, index) => {
257
+ const key2 = o.label.trim().toLowerCase();
258
+ const first = seen.get(key2);
259
+ if (first === void 0) {
260
+ seen.set(key2, index);
261
+ return;
262
+ }
263
+ ctx.addIssue({
264
+ code: z4.ZodIssueCode.custom,
265
+ path: ["options", index, "label"],
266
+ params: { refusal: "duplicate_option_label", index, first },
267
+ message: `duplicate_option_label: options[${index}] repeats the label of options[${first}]. Each option names a different choice.`
268
+ });
269
+ });
270
+ }
242
271
  var QuestionInputSchema = z4.object({
243
272
  questionId: z4.string().uuid().optional().describe("Optional: your own stable id for this question (a UUID), so a resend is the same question. Minted for you when omitted; the result returns it."),
244
273
  ...questionFields
@@ -264,12 +293,15 @@ var sendFields = {
264
293
  "COMPLETIONS close and report in one operation, the same contract as manage_goals kind:complete: closure and the report commit together or neither does. Required children and dependencies must already be finished. options suggest next steps: a tap acknowledges the report and requests that step, and never reopens the Goal (follow-up work needs a new one). No reviewer rejudges it: say only what your evidence supports. Ordinary updates never close a Goal."
265
294
  ),
266
295
  updates: z4.array(UpdateInputSchema).min(1).max(20).optional().describe(
267
- "News: progress, findings, a result. An update asks nothing. It reaches the person only when they asked for it (userExplicitlyRequested), when it answers something they said, or once its Goal is done; otherwise it is recorded as the Goal's progress and nobody is notified."
296
+ "News: progress, findings, a result. An update asks nothing. It reaches the person only when they asked for it (userExplicitlyRequested), when it answers something they said, or once its Goal is done; otherwise it is recorded as the Goal's progress, which the person is not told. Every update reaches the other agents that wrote on its Goal in the last hour: to tell another agent something, write it as an update on that agent's Goal."
268
297
  ),
269
298
  answers: z4.array(AnswerInputSchema).min(1).max(20).optional().describe("Answers to Questions addressed to you."),
270
299
  withdrawQuestionIds: z4.array(questionHandle).min(1).max(10).optional().describe(
271
300
  "Questions you asked that no longer need an answer: you acted on them yourself, they no longer matter, or you asked wrongly. Each is cancelled, not answered: its card closes and nothing rings for it."
272
301
  ),
302
+ bumpQuestionIds: z4.array(questionHandle).min(1).max(10).optional().describe(
303
+ "Your own questions still waiting on the person that you want them to notice again. Each moves back to the top of its card with one new notification; nothing is resent and no second card is made."
304
+ ),
273
305
  questionDependencies: z4.array(QuestionDependencySchema).min(1).max(20).optional().describe(
274
306
  "Add or remove what a Question you asked holds up, after asking it."
275
307
  )
@@ -285,7 +317,7 @@ var ContactSchema = z4.object({
285
317
  "Only controls when this returns. Sending: false (the default) returns at once; true waits up to about 45 seconds for a response to what you sent. Sending nothing: true (the default) waits up to about 45 seconds for anything addressed to you; false returns what is waiting now. It does not mean work is blocked, set urgency, or ask for a call. `waitOutcome` says which happened: `available`, `expired` (nothing arrived; wait again without sending again), or `not_waited`."
286
318
  )
287
319
  }).strict();
288
- var sendsAnything = (c) => !!(c.completions?.length || c.updates?.length || c.questions?.length || c.answers?.length || c.withdrawQuestionIds?.length || c.questionDependencies?.length);
320
+ var sendsAnything = (c) => !!(c.completions?.length || c.updates?.length || c.questions?.length || c.answers?.length || c.withdrawQuestionIds?.length || c.bumpQuestionIds?.length || c.questionDependencies?.length);
289
321
  var ContactWireSchema = z4.object({
290
322
  operationId: z4.string().uuid(),
291
323
  ...sendFields,
@@ -299,13 +331,70 @@ LENGTH: the question, message or answer is the one point, at most ${ASK_MAX} cha
299
331
  UPDATES ask nothing. One reaches the person only when they asked for it, it answers something they said, or its Goal is done; else it is kept as progress. One that reaches them joins an open report card or a live call.
300
332
 
301
333
  RECEIVING: contact({}) waits ~45 s; contact({wait:false}) returns \`events\` at once. Confirm the ones you handled with contact({ackEventIds}).`;
302
- var CreateGoalSchema = z5.object({
303
- outcome: z5.string().trim().min(1).max(1e4),
334
+ var GUIDE = '# Paigy\n\nThe user is remote. Reach them only through Paigy\'s tools, never the terminal. The server\'s own instructions and each tool\'s description are the contract: read them. Every read ends in `next`, the one step to take.\n\nThis same guide is inside the MCP: the `help` tool (whole, or one `topic`) and the `/mcp__paigy__help` prompt. `/mcp__paigy__welcome` starts a session.\n\nHosted (no machine): skip Pairing and Listening; answers wake you.\n\n## Pairing\n\n- Call `configure` first. With a device credential on this machine it hatches you at once.\n- Otherwise print its `user_message` to the user, then do what the result says:\n - `awaiting_approval`: call `configure` again with its `device_code` in the same turn. On `pending`, call again.\n - `awaiting_confirmation`: end your turn with the code as your whole reply; call `configure` with the `device_code` next turn. Calling it in the same turn drops the code.\n- When it returns `enable_command`, print it for the user to run. Do not run it yourself.\n- `configure({ logOut: true })` logs out only this agent, and only when the user asks.\n\n## Listening\n\n- Call `listen` once per session.\n - `listening`: done.\n - `receiving`: a listener runs but cannot wake you. Do not start a second.\n - `unpaired`: call `configure`.\n - `start`: run its `command` exactly, now.\n- Claude Code: run it with the **Monitor** tool, never a background Bash command. Start it again whenever the monitor expires.\n- Codex: run its `command` as a long-running background process. It queues each answer into this thread (`CODEX_THREAD_ID`).\n- With no `listen` tool, run `npx -y -p @paigy/mcp@latest paigy-listen --brief` the same way.\n- One listener per session. A `paigy-listen` in `ps` belongs to another session; ask `listen`, never `ps`.\n- Each line it prints is work: receive, read the Goal, act.\n\n## Receiving\n\n- On startup and after every wake or listener line: `contact({ wait: false })`.\n- Events: `question` (you owe an answer: send `answers`), `update` (something new on your Goal: an answer, a reply, a review), `instruction` (work sent to you).\n- Read the Goal with `get_goals` before acting. An answer arrives as an `update` event and lands on the question\'s Goal. A question reading `replied` means they chose no option: settle it from their words.\n- Acknowledge each event you handled: `contact({ ackEventIds })`. Reading acknowledges nothing; events repeat until acknowledged. A `question` event is answered, never acknowledged away.\n- `contact({ wait: true })` waits about 45 s. On `expired`, wait again without re-sending, or stop. No while-loops: while any Call is open, keep receiving through empty windows; otherwise stop at one empty window.\n\n## Asking\n\n- One question per object in `questions`, at most 80 characters. Context goes in `units`: at most 12, title \u226450, body \u2264200, one idea each.\n- `goalId` names an existing Goal. Create it with `manage_goals` first.\n- Give options when you have them: up to 6, label \u226450 naming the choice in full, `hint` \u2264200 saying what it does. Never "Other" or "Option 2". `pickMode`: `one` (default), `many`, `rank`.\n- `workItBlocks` only when you are truly stopped: it asks for a call.\n- Read the Goal before asking. Never re-ask an open or answered question, or one whose work shipped.\n- Take back a question that no longer matters: `withdrawQuestionIds`.\n- An answer you give (`answers`) is at most 80 characters.\n\n## Updates and completions\n\n- An update (`updates`: `message` \u226480, `goalIds`) asks nothing. It reaches the user only when they asked for it, when it answers something they said, or when its Goal is done; otherwise it stays on the Goal as progress.\n- Updates never complete a Goal.\n- `reply` is the button in the user\'s voice instead of "Got it", \u226450 characters ("Sounds good, I\'ll test later"). Required (`reply_required`) when the update is delivered on request (`userExplicitlyRequested`) or any Goal it names is already done.\n- Set `userExplicitlyRequested` only to relay the user\'s own words: `"any"` for "send it to me", `"call"` for "call me". Never to make a message louder.\n- Finish with a completion (`contact.completions`, or `manage_goals` kind `complete`): `goalId`, `message` \u226450, `reply` \u226450, optional `units`, optional next-step `options` {label, hint}. It closes and reports in one step. Say only what your evidence supports.\n- Read `results`: each item applies on its own, and a failed one names why.\n\n## Goals\n\n- `manage_goals` `changes`, applied in order:\n - `create`: `title` (1-5 words) and `outcome` (one paragraph, \u2264500). More work is child Goals: `parentGoalId`. New ids come back in `results[i].goalId`.\n - `assign`: `ownerId`, a participant as `check_activity` shows it.\n - `defer`: `until`. You are woken when it passes. This is how you follow up later.\n - `dependency`: `add`/`remove`; `goalId` waits on `dependsOnGoalId` to `start` or `complete`.\n - `edit`, `move`, `state` (open, completed, canceled).\n- Any open question on a Goal, anyone\'s, blocks its completion (`goal_blocked`). Before completing, withdraw yours, finish or move its required children, and clear its dependencies.\n- On `goal_revision_conflict`, read the Goal again and reconsider.\n- Before overlapping work, read `check_activity` and the Goal\'s other contributors. Every Goal of your person is open to you; your first write puts you on it.\n- `search` finds earlier words, Goals and answers. Nothing found is not proof of absence.\n\n## Calls\n\n- Call only when the user asked: `userExplicitlyRequested: "call"` on that item. A question with `workItBlocks` may ring too.\n- Any contact while the user is on a call joins that call (`joinedCall` in `results`). Several questions arrive as one call.\n- While any Call is open, keep receiving.\n- An open Call Delivery is not proof it rang. `get_delivery` reads a Call\'s state, and with `transcript: true` what was said.\n';
335
+ var HELP_TOPICS = ["pairing", "listening", "receiving", "asking", "updates", "goals", "calls"];
336
+ var unknownTopic = (topic) => `unknown_topic: ${JSON.stringify(topic)} is not a section of the guide. Topics: ${HELP_TOPICS.join(", ")}; or omit topic for the whole guide.`;
337
+ var HelpSchema = z5.object({
338
+ topic: z5.enum(HELP_TOPICS, { errorMap: (_issue, ctx) => ({ message: unknownTopic(ctx.data) }) }).optional().describe("One section of the guide. Omit it for the whole guide.")
339
+ }).strict();
340
+ var HELP_DESCRIPTION = "Start here: how to use Paigy, in full \u2014 pairing, listening, receiving, asking, updates and completions, Goals and calls. Pass a topic for one section. Read-only; needs no setup.";
341
+ function section(topic) {
342
+ const parts = GUIDE.split(/^(?=## )/m);
343
+ const found = parts.find((p) => p.slice(3).toLowerCase().startsWith(topic));
344
+ if (!found) throw new Error(`help_section_missing: the guide has no "## ${topic}" section`);
345
+ return found.trim() + "\n";
346
+ }
347
+ function helpView({ topic }) {
348
+ return {
349
+ topic: topic ?? "all",
350
+ guide: topic ? section(topic) : GUIDE,
351
+ topics: [...HELP_TOPICS],
352
+ next: "Not set up? Call configure. Then contact({wait:false}) for the work addressed to you."
353
+ };
354
+ }
355
+ var AGENT_PROMPTS = [
356
+ {
357
+ name: "help",
358
+ description: "How to use Paigy: the whole guide, or one topic of it.",
359
+ arguments: [{ name: "topic", description: `One section: ${HELP_TOPICS.join(", ")}. Omit it for the whole guide.`, required: false }]
360
+ },
361
+ { name: "welcome", description: "Start here: who you are on Paigy, then listen and collect your open work." }
362
+ ];
363
+ function promptText(name, facts) {
364
+ if (name === "help") {
365
+ const parsed = HelpSchema.safeParse(facts.topic ? { topic: facts.topic } : {});
366
+ if (!parsed.success) throw new Error(parsed.error.issues[0].message);
367
+ return `Here is how to use Paigy. Follow it from now on.
368
+
369
+ ${helpView(parsed.data).guide}`;
370
+ }
371
+ if (name === "welcome") {
372
+ const who = facts.identity;
373
+ const steps = [];
374
+ if (!who) steps.push("Call `configure` and do what it says: it hatches this session at once on a paired machine, or shows a code to approve.");
375
+ if (facts.listens) steps.push("Call `listen` once and do what it says (`start`: run its command with your Monitor tool, now).");
376
+ steps.push("Call contact({wait:false}) for the work addressed to you: answers, assignments, reviews and due Goals. Read each Goal with get_goals, act, and acknowledge what you handled with contact({ackEventIds}).");
377
+ steps.push("Tell me in a sentence or two what is waiting, or that nothing is.");
378
+ const head = !who ? "This session is not set up with Paigy yet." : who.name ? `You're connected to Paigy as ${who.name}.` : "You're connected to Paigy, with no name yet (configure({ name }) gives you one).";
379
+ return `${head} Start here:
380
+
381
+ ${steps.map((s, i) => `${i + 1}. ${s}`).join("\n")}
382
+
383
+ The whole guide is the \`help\` tool (or /mcp__paigy__help).`;
384
+ }
385
+ throw new Error(`unknown_prompt: ${JSON.stringify(name)}. Prompts: ${AGENT_PROMPTS.map((p) => p.name).join(", ")}.`);
386
+ }
387
+ function promptResult(name, facts) {
388
+ const text = promptText(name, facts);
389
+ return { description: AGENT_PROMPTS.find((p) => p.name === name).description, messages: [{ role: "user", content: { type: "text", text } }] };
390
+ }
391
+ var CreateGoalSchema = z6.object({
392
+ outcome: z6.string().trim().min(1).max(1e4),
304
393
  /** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
305
394
  * rings"). Omit it and the brain writes one at admission from the outcome. */
306
- title: z5.string().trim().min(1).max(80).optional(),
307
- ownerParticipant: z5.string().trim().min(1).optional(),
308
- idempotencyKey: z5.string().trim().min(1).max(200),
395
+ title: z6.string().trim().min(1).max(80).optional(),
396
+ ownerParticipant: z6.string().trim().min(1).optional(),
397
+ idempotencyKey: z6.string().trim().min(1).max(200),
309
398
  /** THE GOAL THIS ONE BELONGS UNDER (owner, 2026-09-15: "the ask I gave for the design doc
310
399
  * didn't get created as a child goal of the voice UI goal, which is how it should've
311
400
  * worked"). It could not have been: this door took no parent, so the only route was
@@ -313,63 +402,63 @@ var CreateGoalSchema = z5.object({
313
402
  * took the short one. The hierarchy has been modelled since Goals existed and had been used
314
403
  * ZERO times in 2,031 of them. Absent, the server judges it against the caller's open Goals
315
404
  * (`apps/api/src/goal/intake.ts`). The writer refuses a Goal belonging to another account. */
316
- parentGoalId: z5.string().uuid().optional(),
405
+ parentGoalId: z6.string().uuid().optional(),
317
406
  /** The repository or project identifier this Goal belongs to (#2280) — e.g. "owner/repo" or
318
407
  * repo name. Delegated work inherits this from its parent Goal when omitted. */
319
- repo: z5.string().trim().min(1).max(200).optional()
408
+ repo: z6.string().trim().min(1).max(200).optional()
320
409
  });
321
- var UpdateGoalSchema = z5.object({
322
- revision: z5.number().int().positive(),
323
- changes: z5.object({
324
- outcome: z5.string().trim().min(1).max(1e4).optional(),
410
+ var UpdateGoalSchema = z6.object({
411
+ revision: z6.number().int().positive(),
412
+ changes: z6.object({
413
+ outcome: z6.string().trim().min(1).max(1e4).optional(),
325
414
  /** The work's NAME (#2115) — one to five words, how a person refers to it out loud. The
326
415
  * brain writes one at admission; this is the owner saying it better. Null clears it. */
327
- title: z5.string().trim().min(1).max(80).nullable().optional(),
328
- ownerParticipant: z5.string().trim().min(1).optional(),
329
- parentGoalId: z5.string().uuid().nullable().optional(),
330
- dependencies: z5.array(z5.object({ goalId: z5.string().uuid(), gate: z5.enum(["start", "finish"]) }).strict()).optional(),
331
- children: z5.array(z5.object({ outcome: z5.string().trim().min(1).max(1e4), ownerParticipant: z5.string().trim().min(1), gate: z5.enum(["start", "finish"]).optional() }).strict()).optional(),
332
- state: z5.enum(["active", "done", "cancelled"]).optional(),
333
- progress: z5.string().trim().min(1).max(1e4).optional(),
334
- reviewed: z5.literal(true).optional(),
335
- dueAt: z5.string().datetime({ offset: true }).nullable().optional()
416
+ title: z6.string().trim().min(1).max(80).nullable().optional(),
417
+ ownerParticipant: z6.string().trim().min(1).optional(),
418
+ parentGoalId: z6.string().uuid().nullable().optional(),
419
+ dependencies: z6.array(z6.object({ goalId: z6.string().uuid(), gate: z6.enum(["start", "finish"]) }).strict()).optional(),
420
+ children: z6.array(z6.object({ outcome: z6.string().trim().min(1).max(1e4), ownerParticipant: z6.string().trim().min(1), gate: z6.enum(["start", "finish"]).optional() }).strict()).optional(),
421
+ state: z6.enum(["active", "done", "cancelled"]).optional(),
422
+ progress: z6.string().trim().min(1).max(1e4).optional(),
423
+ reviewed: z6.literal(true).optional(),
424
+ dueAt: z6.string().datetime({ offset: true }).nullable().optional()
336
425
  }).strict().refine((v) => Object.keys(v).length > 0),
337
- reason: z5.string().trim().min(1).max(2e3),
338
- operationId: z5.string().uuid().optional()
426
+ reason: z6.string().trim().min(1).max(2e3),
427
+ operationId: z6.string().uuid().optional()
339
428
  }).strict();
340
- var changedGoal = z5.string().uuid().describe("The Goal to change, as a read shows it.");
341
- var goalTitle = z5.string().trim().min(1).max(80).describe("The Goal's short display name: one to five words, how a person refers to it out loud.");
429
+ var changedGoal = z6.string().uuid().describe("The Goal to change, as a read shows it.");
430
+ var goalTitle = z6.string().trim().min(1).max(80).describe("The Goal's short display name: one to five words, how a person refers to it out loud.");
342
431
  var GOAL_OUTCOME_MAX = 500;
343
- var goalOutcome = z5.string().trim().min(1).max(GOAL_OUTCOME_MAX, `outcome_too_long: a Goal's outcome is one paragraph, at most ${GOAL_OUTCOME_MAX} characters; split the rest into child Goals (create with parentGoalId).`).describe(`The desired result, one paragraph of at most ${GOAL_OUTCOME_MAX} characters. More than that is more work: create child Goals under it (parentGoalId). There is no third description field.`);
344
- var CreateChange = z5.object({
345
- kind: z5.literal("create"),
432
+ var goalOutcome = z6.string().trim().min(1).max(GOAL_OUTCOME_MAX, `outcome_too_long: a Goal's outcome is one paragraph, at most ${GOAL_OUTCOME_MAX} characters; split the rest into child Goals (create with parentGoalId).`).describe(`The desired result, one paragraph of at most ${GOAL_OUTCOME_MAX} characters. More than that is more work: create child Goals under it (parentGoalId). There is no third description field.`);
433
+ var CreateChange = z6.object({
434
+ kind: z6.literal("create"),
346
435
  title: goalTitle,
347
436
  outcome: goalOutcome,
348
- ownerId: z5.string().trim().min(1).optional().describe("Who owns the work: an agent's participant (as check_activity shows it) or the person's. Omitted: you."),
349
- parentGoalId: z5.string().uuid().optional().describe("The Goal it belongs under. Omitted: a root."),
350
- sourceEntryIds: z5.array(z5.string().uuid()).max(20).optional().describe("The whole Entries the work came from, ones you can read.")
437
+ ownerId: z6.string().trim().min(1).optional().describe("Who owns the work: an agent's participant (as check_activity shows it) or the person's. Omitted: you."),
438
+ parentGoalId: z6.string().uuid().optional().describe("The Goal it belongs under. Omitted: a root."),
439
+ sourceEntryIds: z6.array(z6.string().uuid()).max(20).optional().describe("The whole Entries the work came from, ones you can read.")
351
440
  }).strict();
352
- var EditChange = z5.object({ kind: z5.literal("edit"), goalId: changedGoal, title: goalTitle.optional(), outcome: goalOutcome.optional() }).strict();
353
- var StateChange = z5.object({ kind: z5.literal("state"), goalId: changedGoal, state: z5.enum(["open", "completed", "canceled"]).describe(
441
+ var EditChange = z6.object({ kind: z6.literal("edit"), goalId: changedGoal, title: goalTitle.optional(), outcome: goalOutcome.optional() }).strict();
442
+ var StateChange = z6.object({ kind: z6.literal("state"), goalId: changedGoal, state: z6.enum(["open", "completed", "canceled"]).describe(
354
443
  "completed: the Goal's own work is done now (refused while required children are open); open: reopen it; canceled: it will not be done."
355
444
  ) }).strict();
356
- var AssignChange = z5.object({ kind: z5.literal("assign"), goalId: changedGoal, ownerId: z5.string().trim().min(1).describe("The new owner's participant.") }).strict();
357
- var DeferChange = z5.object({ kind: z5.literal("defer"), goalId: changedGoal, until: z5.string().datetime({ offset: true }).nullable().describe(
445
+ var AssignChange = z6.object({ kind: z6.literal("assign"), goalId: changedGoal, ownerId: z6.string().trim().min(1).describe("The new owner's participant.") }).strict();
446
+ var DeferChange = z6.object({ kind: z6.literal("defer"), goalId: changedGoal, until: z6.string().datetime({ offset: true }).nullable().describe(
358
447
  "Postpone it until this instant: it waits until then, and its owner is woken when it passes (a promise to follow up later). null takes the postponement back."
359
448
  ) }).strict();
360
- var MoveChange = z5.object({ kind: z5.literal("move"), goalId: changedGoal, parentGoalId: z5.string().uuid().nullable().describe(
449
+ var MoveChange = z6.object({ kind: z6.literal("move"), goalId: changedGoal, parentGoalId: z6.string().uuid().nullable().describe(
361
450
  "Its new parent, or null for a root. Only this Goal moves; its new parent's other children stay."
362
451
  ) }).strict();
363
- var DependencyChange = z5.object({
364
- kind: z5.literal("dependency"),
365
- change: z5.enum(["add", "remove"]),
452
+ var DependencyChange = z6.object({
453
+ kind: z6.literal("dependency"),
454
+ change: z6.enum(["add", "remove"]),
366
455
  goalId: changedGoal,
367
- dependsOnGoalId: z5.string().uuid().describe("The Goal it waits on."),
368
- action: z5.enum(["start", "complete"]).describe("What waits: starting goalId, or completing it."),
369
- reason: z5.string().trim().min(1).max(250).optional().describe("Why, at most 250 characters, as an explanation, never as policy.")
456
+ dependsOnGoalId: z6.string().uuid().describe("The Goal it waits on."),
457
+ action: z6.enum(["start", "complete"]).describe("What waits: starting goalId, or completing it."),
458
+ reason: z6.string().trim().min(1).max(250).optional().describe("Why, at most 250 characters, as an explanation, never as policy.")
370
459
  }).strict();
371
- var read = { revision: z5.number().int().positive().optional() };
372
- var GoalChangeSchema = z5.discriminatedUnion("kind", [
460
+ var read = { revision: z6.number().int().positive().optional() };
461
+ var GoalChangeSchema = z6.discriminatedUnion("kind", [
373
462
  CreateChange,
374
463
  CompleteChangeSchema.extend(read),
375
464
  EditChange.extend(read),
@@ -381,43 +470,46 @@ var GoalChangeSchema = z5.discriminatedUnion("kind", [
381
470
  ]);
382
471
  var editNames = (changes, ctx) => changes.forEach((c, i) => {
383
472
  if (c.kind === "edit" && c.title === void 0 && c.outcome === void 0) {
384
- ctx.addIssue({ code: z5.ZodIssueCode.custom, path: ["changes", i], message: "an edit supplies title, outcome, or both" });
473
+ ctx.addIssue({ code: z6.ZodIssueCode.custom, path: ["changes", i], message: "an edit supplies title, outcome, or both" });
385
474
  }
386
475
  });
387
- var ManageGoalsSchema = z5.object({
388
- requestId: z5.string().uuid().optional(),
389
- changes: z5.array(GoalChangeSchema).min(1).max(50)
476
+ var ManageGoalsSchema = z6.object({
477
+ requestId: z6.string().uuid().optional(),
478
+ changes: z6.array(GoalChangeSchema).min(1).max(50)
390
479
  }).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
391
- var ManageGoalsToolSchema = z5.object({
392
- changes: z5.array(z5.discriminatedUnion("kind", [CreateChange, CompleteChangeSchema, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange])).min(1).max(50).describe("The changes, applied in order, each on its own.")
480
+ var ManageGoalsToolSchema = z6.object({
481
+ changes: z6.array(z6.discriminatedUnion("kind", [CreateChange, CompleteChangeSchema, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange])).min(1).max(50).describe("The changes, applied in order, each on its own.")
393
482
  }).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
394
483
  var MANAGE_GOALS_DESCRIPTION = "Create, edit, assign, organize or close work. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: complete (goalId, message capped at 50 characters, units, reply, optional next-step options {label, hint}) closes and reports atomically, exactly as contact.completions; create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (get_goals) and reconsider. You may change any Goal of your person; a change puts you on it. Returns `ok` (every change applied), `results` per change (applied or failed), and `goals`, each changed Goal's id, state, title, outcome and revision.";
395
- var GetGoalsSchema = z5.object({
396
- goalIds: z5.array(z5.string().uuid()).min(1).max(10).describe("The Goals to read, in order; one to ten IDs."),
484
+ var GetGoalsSchema = z6.object({
485
+ goalIds: z6.array(z6.string().uuid()).min(1).max(10).describe("The Goals to read, in order; one to ten IDs."),
397
486
  /** Every entry in full. Without it the read carries the person's words, open questions and your
398
487
  * newest entry, and counts what it left out (`apps/api/src/goal/collapse.ts`). */
399
- history: z5.boolean().optional()
488
+ history: z6.boolean().optional()
400
489
  }).strict();
401
- var GetDeliverySchema = z5.object({
402
- deliveryId: z5.string().uuid().describe("The Delivery id returned by contact or receive.")
490
+ var GetDeliverySchema = z6.object({
491
+ deliveryId: z6.string().uuid().describe("The Delivery id returned by contact or receive."),
492
+ // WHAT WAS SAID ON A CALL (owner, 2026-10-26, Goal c743f4a4: "A flag on get_delivery", "Any of my agents").
493
+ transcript: z6.boolean().optional().describe("For a Call: also return what was said on it, one page of spoken lines."),
494
+ after: z6.string().max(40).optional().describe("With transcript: the next value the previous page returned, to read the next page.")
403
495
  }).strict();
404
- var GET_DELIVERY_DESCRIPTION = "Read a Delivery in full: its whole ask, units, options and answers, plus current callState and callOutcome for a Call. Any agent of the same account may read it; another account is not disclosed. Reading never acknowledges or resends anything. Use this after a call or when receive names a Call you are waiting on.";
496
+ var GET_DELIVERY_DESCRIPTION = "Read a Delivery in full: its whole ask, units, options and answers, plus current callState and callOutcome for a Call. For a Call, transcript: true adds what was said: each spoken line in order, who said it, and when from pickup, a page at a time. A line from the room, not the person, is marked room. Any agent of the same account may read it; another account is not disclosed. Reading never acknowledges or resends anything. Use this after a call or when receive names a Call you are waiting on.";
405
497
  var GET_GOALS_DESCRIPTION = "Read one to ten Goals by goalIds, returned in the same order under goals (a read never puts you on them). history applies to every requested Goal. A refused read fails the call with its original refusal; no partial result is presented as complete. Each Goal includes its ten most recent other contributors (names, latest entry headlines and times), outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full. Every Goal of your person is readable, whichever of their agents owns it; another account's Goals are not disclosed.";
406
- var SearchToolSchema = z5.object({
407
- query: z5.string().trim().min(1).max(500),
408
- types: z5.array(z5.enum(["entry", "goal", "answer"])).min(1).optional(),
409
- goalId: z5.string().uuid().optional(),
410
- limit: z5.number().int().min(1).max(20).optional()
498
+ var SearchToolSchema = z6.object({
499
+ query: z6.string().trim().min(1).max(500),
500
+ types: z6.array(z6.enum(["entry", "goal", "answer"])).min(1).optional(),
501
+ goalId: z6.string().uuid().optional(),
502
+ limit: z6.number().int().min(1).max(20).optional()
411
503
  }).strict();
412
504
  var SEARCH_DESCRIPTION = "Search your person's history across all of their agents: Entries (what anyone said or wrote, typed or spoken on a call), Goals (by title and outcome) and replies -- the person's answers, acknowledgements and deferrals (type answer; found by their summary, by the question or report they reply to, or by the words that gave them). query is words to look for; records sharing more of its words rank first, and exact names work. types narrows it to entry, goal and/or answer (default: all three). goalId searches under one Goal: its Entries, the replies to its items, and it and its immediate children. limit is matches per type, 1 to 20 (default 8). Read-only. Each match carries its whole saved words, its ID and its links (a reply carries its kind -- answered, acknowledged or deferred -- whether it is closed (a deferral is not), its summary in under ten words, its question or report, the choice made and the Entries that support it); `omitted` counts what matched but was left out, so narrow the words or add a goalId to see it. Nothing found is not proof that nothing exists; a refused search says why. Sealed (encrypted) content is never searched or returned.";
413
- var ConfigureIdentitySchema = z5.object({}).strict();
505
+ var ConfigureIdentitySchema = z6.object({}).strict();
414
506
  var CONFIGURE_IDENTITY_DESCRIPTION = "Read this authenticated agent\u2019s current name, voice and token ID. Read-only; no arguments. Pairing, renaming and logout are available only through the local configure driver.";
415
- var CheckActivitySchema = z5.object({}).strict();
507
+ var CheckActivitySchema = z6.object({}).strict();
416
508
  var FEEDBACK_TEXT_MAX = 5e4;
417
- var SendFeedbackSchema = z5.object({
418
- text: z5.string().trim().min(1).max(FEEDBACK_TEXT_MAX).describe(`The report, in markdown: what happened, what was expected, how to reproduce it, versions, evidence. At most ${FEEDBACK_TEXT_MAX} characters.`),
419
- title: z5.string().trim().min(1).max(200).optional().describe("One line naming it."),
420
- kind: z5.enum(["bug", "idea", "other"]).default("other").describe("bug: something Paigy does wrong. idea: something it should do. other: anything else.")
509
+ var SendFeedbackSchema = z6.object({
510
+ text: z6.string().trim().min(1).max(FEEDBACK_TEXT_MAX).describe(`The report, in markdown: what happened, what was expected, how to reproduce it, versions, evidence. At most ${FEEDBACK_TEXT_MAX} characters.`),
511
+ title: z6.string().trim().min(1).max(200).optional().describe("One line naming it."),
512
+ kind: z6.enum(["bug", "idea", "other"]).default("other").describe("bug: something Paigy does wrong. idea: something it should do. other: anything else.")
421
513
  }).strict();
422
514
  var SEND_FEEDBACK_DESCRIPTION = "Send feedback about Paigy itself (a bug report, an idea) straight to the Paigy team. Send it only when the person asked you to, or agreed when you offered: it goes to Paigy, never to the person, and nobody answers it here. Not for your work or a question for the person: use contact for those. Returns whether it was stored.";
423
515
  var AGENT_TOOLS = [
@@ -428,172 +520,176 @@ var AGENT_TOOLS = [
428
520
  { name: "get_goals", description: GET_GOALS_DESCRIPTION, inputSchema: mcpInputSchema(GetGoalsSchema) },
429
521
  { name: "search", description: SEARCH_DESCRIPTION, inputSchema: mcpInputSchema(SearchToolSchema) },
430
522
  { name: "check_activity", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goals to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema(CheckActivitySchema) },
431
- { name: "send_feedback", description: SEND_FEEDBACK_DESCRIPTION, inputSchema: mcpInputSchema(SendFeedbackSchema) }
523
+ { name: "send_feedback", description: SEND_FEEDBACK_DESCRIPTION, inputSchema: mcpInputSchema(SendFeedbackSchema) },
524
+ // THE GUIDE FROM INSIDE THE MCP (owner, 2026-10-27): the skill's text, needing no identity (`help.ts`).
525
+ { name: "help", description: HELP_DESCRIPTION, inputSchema: mcpInputSchema(HelpSchema) }
432
526
  ];
433
527
  var AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
528
+ var LISTEN_COMMAND = "npx -y -p @paigy/mcp@latest paigy-listen --brief";
529
+ var LISTEN_MONITOR = "your harness's MONITOR tool, the kind that wakes you on every line it prints (in Claude Code: Monitor, not a background Bash command, which only tells you when it exits)";
434
530
  function serverInstructions(opts) {
435
- const waits = opts.waits ? "contact({wait:true}) waits ~45 s for a reply to what you sent; contact({}) for anything addressed to you." : "A contact here returns after one read; answers wake you, and contact({wait:false}) collects them.";
436
- return `On startup and after a wake, contact({wait:false}) returns your events. They repeat until you acknowledge them with contact({ackEventIds}); reading acknowledges nothing. get_goals reads a Goal and its conversation; your first write to one puts you on it, and every Goal of your person is open to you. manage_goals creates, assigns and organizes work; to follow up later, defer the Goal with manage_goals (you're woken when due). Complete and report in one step: contact.completions or manage_goals kind:complete. Every read ends in \`next\`: your next step. ${waits} Report progress as a contact update; one they don't need yet stays on the Goal. An open Call is not proof it rang. Your user is remote: reach them only through Paigy, never the terminal; ask decisions with options.
531
+ const waits = opts.waits ? `contact({wait:true}) waits ~45 s for a reply to what you sent; contact({}) for anything addressed to you. Call \`listen\` once to wake at rest.` : "A contact here returns after one read; answers wake you, and contact({wait:false}) collects them.";
532
+ return `New here? Call help. On startup and after a wake, contact({wait:false}) returns your events. They repeat until acknowledged with contact({ackEventIds}); reading does not. get_goals reads a Goal's conversation; your first write puts you on it, and every Goal of your person is open to you. manage_goals creates, assigns and organizes work; to follow up, defer the Goal with manage_goals (woken when due). Complete and report at once: contact.completions or manage_goals kind:complete. Every read ends in \`next\`: your next step. ${waits} Report progress as a contact update; one they don't need yet stays on the Goal. An open Call is not proof it rang. Your user is remote: reach them only through Paigy, never the terminal.
437
533
 
438
534
  ${ASKING_RULES}`;
439
535
  }
440
- var id = z6.string().uuid();
441
- var key = z6.string().min(1).max(40);
442
- var participant = z6.string().regex(/^(human|agent):.+$/, "a participant ID such as agent:<uuid>");
443
- var SourceSchema = z6.object({ entryId: id }).strict();
444
- var ResultRefSchema = z6.union([z6.object({ id }).strict(), z6.object({ local: key }).strict()]);
445
- var NodeRefSchema = z6.object({ type: z6.enum(["goal", "question"]), id }).strict();
446
- var DependencySchema = z6.object({
536
+ var id = z7.string().uuid();
537
+ var key = z7.string().min(1).max(40);
538
+ var participant = z7.string().regex(/^(human|agent):.+$/, "a participant ID such as agent:<uuid>");
539
+ var SourceSchema = z7.object({ entryId: id }).strict();
540
+ var ResultRefSchema = z7.union([z7.object({ id }).strict(), z7.object({ local: key }).strict()]);
541
+ var NodeRefSchema = z7.object({ type: z7.enum(["goal", "question"]), id }).strict();
542
+ var DependencySchema = z7.object({
447
543
  blocker: NodeRefSchema,
448
544
  blocked: NodeRefSchema,
449
- action: z6.enum(["start", "complete", "answer"]),
450
- reason: z6.string().max(500).optional()
545
+ action: z7.enum(["start", "complete", "answer"]),
546
+ reason: z7.string().max(500).optional()
451
547
  }).strict();
452
- var goalState = z6.enum(["open", "completed", "canceled"]);
548
+ var goalState = z7.enum(["open", "completed", "canceled"]);
453
549
  var existingGoalChanges = [
454
- z6.object({ kind: z6.literal("edit"), goalId: id, title: z6.string().min(1).max(120).optional(), outcome: z6.string().min(1).optional() }).strict().refine((c) => c.title !== void 0 || c.outcome !== void 0, "an edit changes the title or the outcome"),
455
- z6.object({ kind: z6.literal("state"), goalId: id, state: goalState }).strict(),
456
- z6.object({ kind: z6.literal("assign"), goalId: id, ownerId: participant }).strict(),
457
- z6.object({ kind: z6.literal("defer"), goalId: id, until: z6.string().datetime().nullable() }).strict(),
458
- z6.object({ kind: z6.literal("move"), goalId: id, parentGoalId: id.nullable() }).strict(),
459
- z6.object({
460
- kind: z6.literal("dependency"),
461
- change: z6.enum(["add", "remove"]),
550
+ z7.object({ kind: z7.literal("edit"), goalId: id, title: z7.string().min(1).max(120).optional(), outcome: z7.string().min(1).optional() }).strict().refine((c) => c.title !== void 0 || c.outcome !== void 0, "an edit changes the title or the outcome"),
551
+ z7.object({ kind: z7.literal("state"), goalId: id, state: goalState }).strict(),
552
+ z7.object({ kind: z7.literal("assign"), goalId: id, ownerId: participant }).strict(),
553
+ z7.object({ kind: z7.literal("defer"), goalId: id, until: z7.string().datetime().nullable() }).strict(),
554
+ z7.object({ kind: z7.literal("move"), goalId: id, parentGoalId: id.nullable() }).strict(),
555
+ z7.object({
556
+ kind: z7.literal("dependency"),
557
+ change: z7.enum(["add", "remove"]),
462
558
  goalId: id,
463
559
  dependsOnGoalId: id,
464
- action: z6.enum(["start", "complete"]),
465
- reason: z6.string().max(500).optional()
560
+ action: z7.enum(["start", "complete"]),
561
+ reason: z7.string().max(500).optional()
466
562
  }).strict()
467
563
  ];
468
- var BrainGoalChangeSchema = z6.union([
564
+ var BrainGoalChangeSchema = z7.union([
469
565
  // newSession: Paigy starts a session to own it (owner, 2026-10-06); until it has, the person owns it.
470
- z6.object({
471
- kind: z6.literal("create"),
472
- outcome: z6.string().min(1),
473
- title: z6.string().min(1).max(120),
566
+ z7.object({
567
+ kind: z7.literal("create"),
568
+ outcome: z7.string().min(1),
569
+ title: z7.string().min(1).max(120),
474
570
  ownerId: participant,
475
571
  parentGoal: ResultRefSchema.nullable(),
476
- newSession: z6.literal(true).optional()
572
+ newSession: z7.literal(true).optional()
477
573
  }).strict(),
478
574
  ...existingGoalChanges
479
575
  ]);
480
- var option = z6.object({ id: z6.string().min(1).max(40), label: z6.string().min(1), hint: z6.string().optional() }).strict();
481
- var QuestionChangeSchema = z6.union([
482
- z6.object({
483
- kind: z6.literal("create"),
484
- text: z6.string().min(1),
485
- summary: z6.string().optional(),
576
+ var option = z7.object({ id: z7.string().min(1).max(40), label: z7.string().min(1), hint: z7.string().optional() }).strict();
577
+ var QuestionChangeSchema = z7.union([
578
+ z7.object({
579
+ kind: z7.literal("create"),
580
+ text: z7.string().min(1),
581
+ summary: z7.string().optional(),
486
582
  answererId: participant,
487
583
  goal: ResultRefSchema.optional(),
488
- blocks: z6.array(z6.object({ question: ResultRefSchema }).strict()),
489
- options: z6.array(option).min(2).optional(),
490
- pickMode: z6.enum(["one", "many", "rank"]).optional()
584
+ blocks: z7.array(z7.object({ question: ResultRefSchema }).strict()),
585
+ options: z7.array(option).min(2).optional(),
586
+ pickMode: z7.enum(["one", "many", "rank"]).optional()
491
587
  }).strict().refine((c) => c.options === void 0 === (c.pickMode === void 0), "options and pickMode come together"),
492
- z6.object({ kind: z6.literal("edit"), questionId: id, text: z6.string().min(1) }).strict(),
493
- z6.object({ kind: z6.literal("assign"), questionId: id, answererId: participant }).strict(),
494
- z6.object({ kind: z6.literal("withdraw"), questionId: id }).strict(),
495
- z6.object({ kind: z6.literal("dependency"), change: z6.enum(["add", "remove"]), edge: DependencySchema }).strict()
588
+ z7.object({ kind: z7.literal("edit"), questionId: id, text: z7.string().min(1) }).strict(),
589
+ z7.object({ kind: z7.literal("assign"), questionId: id, answererId: participant }).strict(),
590
+ z7.object({ kind: z7.literal("withdraw"), questionId: id }).strict(),
591
+ z7.object({ kind: z7.literal("dependency"), change: z7.enum(["add", "remove"]), edge: DependencySchema }).strict()
496
592
  ]);
497
- var lessonScope = z6.union([
498
- z6.object({ kind: z6.literal("user") }).strict(),
499
- z6.object({ kind: z6.literal("goal"), goal: ResultRefSchema }).strict()
593
+ var lessonScope = z7.union([
594
+ z7.object({ kind: z7.literal("user") }).strict(),
595
+ z7.object({ kind: z7.literal("goal"), goal: ResultRefSchema }).strict()
500
596
  ]);
501
- var LessonChangeSchema = z6.union([
502
- z6.object({ kind: z6.literal("remember"), text: z6.string().min(1), scope: lessonScope }).strict(),
503
- z6.object({ kind: z6.literal("revise"), lessonId: id, text: z6.string().min(1), scope: lessonScope }).strict(),
504
- z6.object({ kind: z6.literal("withdraw"), lessonId: id }).strict()
597
+ var LessonChangeSchema = z7.union([
598
+ z7.object({ kind: z7.literal("remember"), text: z7.string().min(1), scope: lessonScope }).strict(),
599
+ z7.object({ kind: z7.literal("revise"), lessonId: id, text: z7.string().min(1), scope: lessonScope }).strict(),
600
+ z7.object({ kind: z7.literal("withdraw"), lessonId: id }).strict()
505
601
  ]);
506
- var BrainNextSchema = z6.object({
507
- instructions: z6.array(z6.object({ entryKeys: z6.array(key).min(1) }).strict()),
508
- say: z6.array(key),
509
- then: z6.enum(["listen", "hold", "end", "none"]),
510
- waitFor: z6.array(ResultRefSchema),
511
- reason: z6.string()
602
+ var BrainNextSchema = z7.object({
603
+ instructions: z7.array(z7.object({ entryKeys: z7.array(key).min(1) }).strict()),
604
+ say: z7.array(key),
605
+ then: z7.enum(["listen", "hold", "end", "none"]),
606
+ waitFor: z7.array(ResultRefSchema),
607
+ reason: z7.string()
512
608
  }).strict();
513
609
  var messageBase = {
514
610
  key,
515
- text: z6.string().min(1),
516
- goals: z6.array(ResultRefSchema),
517
- questions: z6.array(ResultRefSchema),
518
- entryKeys: z6.array(key),
519
- obligationIds: z6.array(id)
611
+ text: z7.string().min(1),
612
+ goals: z7.array(ResultRefSchema),
613
+ questions: z7.array(ResultRefSchema),
614
+ entryKeys: z7.array(key),
615
+ obligationIds: z7.array(id)
520
616
  };
521
- var BrainMessageSchema = z6.union([
617
+ var BrainMessageSchema = z7.union([
522
618
  // A MESSAGE TO THE PERSON KEEPS ITS SENTENCES (turns.md): the talker writes them one by one, and each is
523
619
  // its own unit of speech (`<cycle>:<key>:<n>`). `text` is the same words joined, for every reader of a
524
620
  // message's words. Absent on results written before sentences.
525
- z6.object({
621
+ z7.object({
526
622
  ...messageBase,
527
- to: z6.object({ kind: z6.literal("user") }).strict(),
528
- sentences: z6.array(z6.string().min(1)).min(1).optional(),
623
+ to: z7.object({ kind: z7.literal("user") }).strict(),
624
+ sentences: z7.array(z7.string().min(1)).min(1).optional(),
529
625
  // THE REPORTS IT TELLS, BY THEIR OWN IDENTITY (call 9d1c096c, 2026-10-15): the talker's message names the report
530
626
  // Entries it speaks of, so the plan's line names that report rather than re-finding it by its Goal. Absent on
531
627
  // results written before it.
532
- tells: z6.array(z6.object({ id }).strict()).optional()
628
+ tells: z7.array(z7.object({ id }).strict()).optional()
533
629
  }).strict(),
534
- z6.object({
630
+ z7.object({
535
631
  ...messageBase,
536
- to: z6.object({ kind: z6.literal("agent"), agentId: z6.string().regex(/^agent:(?!paigy$)\S+$/) }).strict(),
537
- call: z6.object({ callId: id, listen: z6.literal(true) }).strict().optional(),
632
+ to: z7.object({ kind: z7.literal("agent"), agentId: z7.string().regex(/^agent:(?!paigy$)\S+$/) }).strict(),
633
+ call: z7.object({ callId: id, listen: z7.literal(true) }).strict().optional(),
538
634
  // WHAT IT ASKS THE AGENT, IN UNDER TEN WORDS (owner, 2026-10-15: "Yes, add the summary"): the talker's own, stored
539
635
  // as the sent Entry's summary, which the recap's "Sent to <agent>: …" row shows. Absent on results written before it.
540
- summary: z6.string().optional(),
636
+ summary: z7.string().optional(),
541
637
  // THE PERSON ASKED TO BE CALLED BACK ONCE THIS AGENT'S WORK IS DONE (turns.md, step 0): the agent owes
542
638
  // the call, and its next contact about it rings them (`public.callbacks`).
543
- callback: z6.literal(true).optional()
639
+ callback: z7.literal(true).optional()
544
640
  }).strict()
545
641
  ]);
546
- var BrainSearchSchema = z6.object({
547
- query: z6.string().min(1).max(500),
548
- within: z6.array(z6.enum(["entries", "goals", "answers"])).min(1),
642
+ var BrainSearchSchema = z7.object({
643
+ query: z7.string().min(1).max(500),
644
+ within: z7.array(z7.enum(["entries", "goals", "answers"])).min(1),
549
645
  goalId: id.optional()
550
646
  }).strict();
551
- var BrainResultSchema = z6.object({
552
- messages: z6.array(BrainMessageSchema),
553
- entries: z6.array(z6.object({ key, sources: z6.array(SourceSchema).min(1), goals: z6.array(ResultRefSchema), summary: z6.string().optional() }).strict()),
554
- questions: z6.array(z6.object({ key, entryKeys: z6.array(key), change: QuestionChangeSchema }).strict()),
555
- answers: z6.array(z6.object({
647
+ var BrainResultSchema = z7.object({
648
+ messages: z7.array(BrainMessageSchema),
649
+ entries: z7.array(z7.object({ key, sources: z7.array(SourceSchema).min(1), goals: z7.array(ResultRefSchema), summary: z7.string().optional() }).strict()),
650
+ questions: z7.array(z7.object({ key, entryKeys: z7.array(key), change: QuestionChangeSchema }).strict()),
651
+ answers: z7.array(z7.object({
556
652
  question: ResultRefSchema,
557
- entryKeys: z6.array(key).min(1),
558
- summary: z6.string(),
559
- selectedOptionIds: z6.array(z6.string().min(1)).optional()
653
+ entryKeys: z7.array(key).min(1),
654
+ summary: z7.string(),
655
+ selectedOptionIds: z7.array(z7.string().min(1)).optional()
560
656
  }).strict()),
561
- goals: z6.array(z6.object({ key, entryKeys: z6.array(key), change: BrainGoalChangeSchema }).strict()),
562
- feedback: z6.array(z6.object({ entryKeys: z6.array(key).min(1) }).strict()),
563
- lessons: z6.array(z6.object({ entryKeys: z6.array(key).min(1), change: LessonChangeSchema }).strict()),
657
+ goals: z7.array(z7.object({ key, entryKeys: z7.array(key), change: BrainGoalChangeSchema }).strict()),
658
+ feedback: z7.array(z7.object({ entryKeys: z7.array(key).min(1) }).strict()),
659
+ lessons: z7.array(z7.object({ entryKeys: z7.array(key).min(1), change: LessonChangeSchema }).strict()),
564
660
  next: BrainNextSchema,
565
661
  search: BrainSearchSchema.optional()
566
662
  }).strict();
567
- var str2 = z6.string();
568
- var strs = z6.array(z6.string());
569
- var CompactResultSchema = z6.object({
570
- next: z6.object({ say: strs, then: z6.enum(["listen", "hold", "end", "none"]), waitFor: strs, reason: str2, instructions: strs }).strict(),
571
- messages: z6.array(z6.object({ key: str2, to: str2, text: str2, about: strs, entries: strs, owed: strs, inviteToCall: z6.boolean() }).strict()),
572
- entries: z6.array(z6.object({ key: str2, from: str2, on: strs }).strict()),
573
- answers: z6.array(z6.object({ question: str2, entries: strs, summary: str2, picked: strs }).strict()),
574
- changes: z6.array(z6.object({
663
+ var str2 = z7.string();
664
+ var strs = z7.array(z7.string());
665
+ var CompactResultSchema = z7.object({
666
+ next: z7.object({ say: strs, then: z7.enum(["listen", "hold", "end", "none"]), waitFor: strs, reason: str2, instructions: strs }).strict(),
667
+ messages: z7.array(z7.object({ key: str2, to: str2, text: str2, about: strs, entries: strs, owed: strs, inviteToCall: z7.boolean() }).strict()),
668
+ entries: z7.array(z7.object({ key: str2, from: str2, on: strs }).strict()),
669
+ answers: z7.array(z7.object({ question: str2, entries: strs, summary: str2, picked: strs }).strict()),
670
+ changes: z7.array(z7.object({
575
671
  key: str2,
576
- what: z6.enum(["goal", "question", "lesson"]),
577
- op: z6.enum(["create", "edit", "assign", "open", "complete", "cancel", "defer", "move", "block", "unblock", "withdraw", "remember", "revise"]),
672
+ what: z7.enum(["goal", "question", "lesson"]),
673
+ op: z7.enum(["create", "edit", "assign", "open", "complete", "cancel", "defer", "move", "block", "unblock", "withdraw", "remember", "revise"]),
578
674
  id: str2,
579
675
  text: str2,
580
676
  title: str2,
581
677
  who: str2,
582
678
  under: str2,
583
- gate: z6.enum(["start", "complete", "answer", "none"]),
584
- options: z6.array(z6.object({ id: str2, label: str2 }).strict()),
585
- pick: z6.enum(["one", "many", "rank", "words"]),
679
+ gate: z7.enum(["start", "complete", "answer", "none"]),
680
+ options: z7.array(z7.object({ id: str2, label: str2 }).strict()),
681
+ pick: z7.enum(["one", "many", "rank", "words"]),
586
682
  entries: strs
587
683
  }).strict()),
588
- feedback: z6.array(strs),
589
- search: z6.object({ query: str2, within: strs, goal: str2 }).strict()
684
+ feedback: z7.array(strs),
685
+ search: z7.object({ query: str2, within: strs, goal: str2 }).strict()
590
686
  }).strict();
591
687
  var COMPACT_RESULT_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
592
688
  zodToJsonSchema2(CompactResultSchema, { $refStrategy: "none" })
593
689
  );
594
- var str3 = z7.string();
595
- var strs2 = z7.array(z7.string());
596
- var TalkerReplySchema = z7.object({
690
+ var str3 = z8.string();
691
+ var strs2 = z8.array(z8.string());
692
+ var TalkerReplySchema = z8.object({
597
693
  /** What to say and send, messages to the person first: spoken in the order written. `to` is "user"
598
694
  * or an agent's handle; `sentences` is what a message to the person says, one short sentence each
599
695
  * ([] to an agent); `text` is what a message to an agent says ("" to the person); `summary` is what it
@@ -602,35 +698,36 @@ var TalkerReplySchema = z7.object({
602
698
  * handles; `blocks` is the item a question to an agent must be answered before ("" for none);
603
699
  * `callback` is true on a message to the agent doing work the person asked to be called back about
604
700
  * once it is done (false otherwise): that agent then owes the call (turns.md, step 0). */
605
- messages: z7.array(z7.object({ key: str3, to: str3, sentences: strs2, text: str3, summary: str3, about: strs2, blocks: str3, callback: z7.boolean() }).strict()),
701
+ messages: z8.array(z8.object({ key: str3, to: str3, sentences: strs2, text: str3, summary: str3, about: strs2, blocks: str3, callback: z8.boolean() }).strict()),
606
702
  /** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
607
- then: z7.enum(["listen", "hold", "end"]),
703
+ then: z8.enum(["listen", "hold", "end"]),
608
704
  /** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
609
705
  * IDs, the line handles, how they replied in under ten words (keeping any condition), and `defer`:
610
706
  * "" when their words settle the item, else what a reply that puts it off waits for -- "call" (the
611
707
  * end of this call), a question's handle (that question's answer) or an ISO time. */
612
- replies: z7.array(z7.object({ item: str3, options: strs2, lines: strs2, summary: str3, defer: str3 }).strict()),
708
+ replies: z8.array(z8.object({ item: str3, options: strs2, lines: strs2, summary: str3, defer: str3 }).strict()),
613
709
  /** The line handles that tell the talker how to run this call; they last until it ends and reach no agent. */
614
710
  instruction: strs2,
615
711
  /** The line handles the filer acts on, and the only ones it reads (3e): new work, a change to existing
616
712
  * work, feedback on Paigy, anything said to an agent, and any rule meant to outlast the call. */
617
713
  file: strs2,
618
714
  /** Evidence to fetch for a second round: a search, an item's full text, or an agent by name. */
619
- need: z7.object({ kind: z7.enum(["search", "item", "agent", ""]), text: str3 }).strict(),
715
+ need: z8.object({ kind: z8.enum(["search", "item", "agent", ""]), text: str3 }).strict(),
620
716
  /** The handle of the item to explain ("" for none). */
621
717
  explain: str3
622
718
  }).strict();
623
719
  var TALKER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
624
720
  zodToJsonSchema3(TalkerReplySchema, { $refStrategy: "none" })
625
721
  );
626
- var str4 = z8.string();
627
- var strs3 = z8.array(z8.string());
628
- var FilerReplySchema = z8.object({
629
- /** Which Goals each new line goes on: a line's handle, Goal handles (or keys of new Goals), and what
630
- * the line says in at most ten words. */
631
- filed: z8.array(z8.object({ line: str4, goals: strs3, summary: str4 }).strict()),
632
- goals: z8.array(z8.object({
633
- op: z8.enum(["create", "edit", "complete", "cancel", "reopen", "assign", "defer", "move", "block", "unblock"]),
722
+ var str4 = z9.string();
723
+ var strs3 = z9.array(z9.string());
724
+ var FilerReplySchema = z9.object({
725
+ /** Which Goals each new line goes on: a line's handle, Goal handles (or keys of new Goals: a line that asks
726
+ * for new work goes on the Goal it creates, owner 2026-10-26), and what the line says in at most twenty
727
+ * words (owner, 2026-10-26: "bump it from 10 to 20 words"). */
728
+ filed: z9.array(z9.object({ line: str4, goals: strs3, summary: str4 }).strict()),
729
+ goals: z9.array(z9.object({
730
+ op: z9.enum(["create", "edit", "complete", "cancel", "reopen", "assign", "defer", "move", "block", "unblock"]),
634
731
  key: str4,
635
732
  goal: str4,
636
733
  title: str4,
@@ -639,33 +736,33 @@ var FilerReplySchema = z8.object({
639
736
  under: str4,
640
737
  until: str4,
641
738
  on: str4,
642
- gate: z8.enum(["start", "complete", "answer", ""]),
739
+ gate: z9.enum(["start", "complete", "answer", ""]),
643
740
  why: str4
644
741
  }).strict()),
645
- questions: z8.array(z8.object({
646
- op: z8.enum(["create", "edit", "assign", "withdraw"]),
742
+ questions: z9.array(z9.object({
743
+ op: z9.enum(["create", "edit", "assign", "withdraw"]),
647
744
  key: str4,
648
745
  question: str4,
649
746
  text: str4,
650
747
  summary: str4,
651
748
  to: str4,
652
749
  goal: str4,
653
- options: z8.array(z8.object({ id: str4, label: str4 }).strict()),
654
- pick: z8.enum(["one", "many", "rank", "words", ""]),
750
+ options: z9.array(z9.object({ id: str4, label: str4 }).strict()),
751
+ pick: z9.enum(["one", "many", "rank", "words", ""]),
655
752
  blocks: str4
656
753
  }).strict()),
657
- lessons: z8.array(z8.object({ op: z8.enum(["remember", "revise", "withdraw"]), lesson: str4, text: str4, scope: str4 }).strict()),
754
+ lessons: z9.array(z9.object({ op: z9.enum(["remember", "revise", "withdraw"]), lesson: str4, text: str4, scope: str4 }).strict()),
658
755
  /** Line handles whose words comment on Paigy itself. */
659
756
  feedback: strs3
660
757
  }).strict();
661
758
  var FILER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
662
759
  zodToJsonSchema4(FilerReplySchema, { $refStrategy: "none" })
663
760
  );
664
- var ExplainerReplySchema = z9.object({
761
+ var ExplainerReplySchema = z10.object({
665
762
  /** The explanation, as sentences, spoken in order. */
666
- say: z9.array(z9.string()),
763
+ say: z10.array(z10.string()),
667
764
  /** Questions to an agent about gaps in what it read: the agent's handle and the question. */
668
- followUps: z9.array(z9.object({ to: z9.string(), text: z9.string() }).strict())
765
+ followUps: z10.array(z10.object({ to: z10.string(), text: z10.string() }).strict())
669
766
  }).strict();
670
767
  var EXPLAINER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
671
768
  zodToJsonSchema5(ExplainerReplySchema, { $refStrategy: "none" })
@@ -688,49 +785,49 @@ var DISTILLER_DEFECTS = [
688
785
  "dropped"
689
786
  // something it had to say never played and was never said later (owner, 2026-10-08, call 65433a9d)
690
787
  ];
691
- var DistillerReplySchema = z10.object({
692
- defects: z10.array(z10.object({
693
- kind: z10.enum(DISTILLER_DEFECTS),
788
+ var DistillerReplySchema = z11.object({
789
+ defects: z11.array(z11.object({
790
+ kind: z11.enum(DISTILLER_DEFECTS),
694
791
  /** The handles of the lines it shows in. */
695
- lines: z10.array(z10.string()),
792
+ lines: z11.array(z11.string()),
696
793
  /** The handle of the record it concerns, or "". */
697
- about: z10.string(),
794
+ about: z11.string(),
698
795
  /** One plain sentence, in words, never handles: it is what the feedback row keeps. */
699
- why: z10.string()
796
+ why: z11.string()
700
797
  }).strict())
701
798
  }).strict();
702
799
  var DISTILLER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
703
800
  zodToJsonSchema6(DistillerReplySchema, { $refStrategy: "none" })
704
801
  );
705
802
  var TIDY_AUDIENCES = ["talker", "filer", "agents"];
706
- var TidyReplySchema = z10.object({
707
- lessons: z10.array(z10.object({
708
- lesson: z10.string(),
709
- op: z10.enum(["keep", "revise", "withdraw"]),
710
- text: z10.string(),
711
- for: z10.enum([...TIDY_AUDIENCES, ""]),
712
- into: z10.string()
803
+ var TidyReplySchema = z11.object({
804
+ lessons: z11.array(z11.object({
805
+ lesson: z11.string(),
806
+ op: z11.enum(["keep", "revise", "withdraw"]),
807
+ text: z11.string(),
808
+ for: z11.enum([...TIDY_AUDIENCES, ""]),
809
+ into: z11.string()
713
810
  }).strict())
714
811
  }).strict();
715
812
  var TIDY_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
716
813
  zodToJsonSchema6(TidyReplySchema, { $refStrategy: "none" })
717
814
  );
718
- var HandoutReplySchema = z11.object({
719
- assign: z11.array(z11.object({
815
+ var HandoutReplySchema = z12.object({
816
+ assign: z12.array(z12.object({
720
817
  /** The Goal's handle (G). */
721
- goal: z11.string(),
818
+ goal: z12.string(),
722
819
  /** The agent's handle (A). */
723
- to: z11.string(),
820
+ to: z12.string(),
724
821
  /** At most ten words, naming the Goal of theirs it is most like: the reason the hand-out records. */
725
- why: z11.string()
822
+ why: z12.string()
726
823
  }).strict()),
727
824
  /** The Goals no listed agent's work is even loosely like: Paigy starts a session for them (owner, 2026-10-24,
728
825
  * Goal 37603006, "One per family of related Goals"). */
729
- new: z11.array(z11.object({
826
+ new: z12.array(z12.object({
730
827
  /** The Goal's handle (G). */
731
- goal: z11.string(),
828
+ goal: z12.string(),
732
829
  /** At most ten words: why nothing active fits it. */
733
- why: z11.string()
830
+ why: z12.string()
734
831
  }).strict())
735
832
  }).strict();
736
833
  var HANDOUT_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
@@ -764,20 +861,20 @@ var LIVE_MS = 3 * 6e4;
764
861
  var WORKING_MS = 60 * 6e4;
765
862
  var WORKING_TIER_MS = 2 * 60 * 6e4;
766
863
  var RECENT_TIER_MS = 24 * 60 * 6e4;
767
- var ContextSchema = z12.object({
768
- title: z12.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
769
- description: z12.array(z12.string().min(1)).describe(
864
+ var ContextSchema = z13.object({
865
+ title: z13.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
866
+ description: z13.array(z13.string().min(1)).describe(
770
867
  "Semantic chunks of detail (each a standalone, non-empty piece). The user can select chunks to ask you to expand. MAY BE EMPTY: a claim whose whole content is its heading \u2014 a single sentence \u2014 has no body, and saying so beats repeating the heading underneath itself. That repeat is what `min(1)` used to force, at 2x the storage, with every reader subtracting it back out at render time."
771
868
  ),
772
869
  /** THE ASK'S UNITS (3c): its context as titled units, in order. The app lists the titles and opens
773
870
  * a body on a tap. Absent on an ask sent without units, which renders as before. */
774
- units: z12.array(z12.object({ title: z12.string(), body: z12.string() })).optional()
871
+ units: z13.array(z13.object({ title: z13.string(), body: z13.string() })).optional()
775
872
  });
776
- var ParticipantSchema = z12.object({
777
- kind: z12.enum(["human", "agent"]),
778
- id: z12.string()
873
+ var ParticipantSchema = z13.object({
874
+ kind: z13.enum(["human", "agent"]),
875
+ id: z13.string()
779
876
  });
780
- var TransformSchema = z12.enum([
877
+ var TransformSchema = z13.enum([
781
878
  "structure",
782
879
  // shape intent into an answer contract; pick channel/urgency — broker `ask`, `select` shapes, `points`
783
880
  "request_more",
@@ -793,13 +890,13 @@ var TransformSchema = z12.enum([
793
890
  "summarize"
794
891
  // reduce volume, keep decision value — 30-turn cap, spoken briefing
795
892
  ]);
796
- var VisualSchema = z12.object({
797
- url: z12.string().url(),
798
- label: z12.string().optional()
893
+ var VisualSchema = z13.object({
894
+ url: z13.string().url(),
895
+ label: z13.string().optional()
799
896
  });
800
- var NotifyLevelSchema = z12.enum(["inbox", "push", "banner", "call"]);
801
- var SelectShapeSchema = z12.enum(["one", "many", "rank", "confirm", "text"]);
802
- var ReceiptEventSchema = z12.enum([
897
+ var NotifyLevelSchema = z13.enum(["inbox", "push", "banner", "call"]);
898
+ var SelectShapeSchema = z13.enum(["one", "many", "rank", "confirm", "text"]);
899
+ var ReceiptEventSchema = z13.enum([
803
900
  "delivered",
804
901
  // the bundle reached the recipient at some level
805
902
  "seen",
@@ -829,47 +926,47 @@ var ReceiptEventSchema = z12.enum([
829
926
  // be rewound by a writer that forgot to advance it.
830
927
  "restarted"
831
928
  ]);
832
- var AttentionSchema = z12.object({
929
+ var AttentionSchema = z13.object({
833
930
  urgency: NotifyLevelSchema,
834
931
  /** The required answer shape, or null for a plain notify that asks nothing back. */
835
932
  select: SelectShapeSchema.nullable(),
836
933
  /** Coverage contract (#396) — points the answer must address; null = none declared. */
837
- points: z12.array(z12.string()).nullable(),
934
+ points: z13.array(z13.string()).nullable(),
838
935
  /** Whether the ask blocks the sender — what lets arbitration escalate it on silence. */
839
- blocking: z12.boolean(),
936
+ blocking: z13.boolean(),
840
937
  /** Reserved (docs/model/model.md lists it): a response deadline. No row column yet — a later Phase 2
841
938
  * slice wires it; optional so today's rows/callers project cleanly. */
842
- deadline: z12.string().datetime().nullable().optional()
939
+ deadline: z13.string().datetime().nullable().optional()
843
940
  });
844
- var NotifyRequestFields = z12.object({
941
+ var NotifyRequestFields = z13.object({
845
942
  /** Plaintext message content. Present on the plaintext path (today's shape);
846
943
  * ABSENT on the E2EE path, where the sealed `envelope` below carries it. The
847
944
  * superRefine at the bottom enforces exactly one of the two. */
848
945
  context: ContextSchema.optional(),
849
- options: z12.array(OptionSchema.omit({ id: true }).extend({ label: z12.string().trim().min(1).max(1e3) }).strict()).min(OPTIONS_MIN).max(OPTIONS_MAX).optional().describe(
946
+ options: z13.array(OptionSchema.omit({ id: true }).extend({ label: z13.string().trim().min(1).max(1e3) }).strict()).min(OPTIONS_MIN).max(OPTIONS_MAX).optional().describe(
850
947
  "The choices, in order \u2014 required when select is 'one'/'many'/'rank', omitted otherwise. Ids are assigned automatically by position ('1', '2', \u2026); the user's answer references them as optionId(s)."
851
948
  ),
852
- points: z12.array(z12.string().min(1)).optional().describe(
949
+ points: z13.array(z13.string().min(1)).optional().describe(
853
950
  "The distinct things you need answered, each a short phrase \u2014 on a call the broker keeps the conversation going until each is addressed, and the reply reports which were covered, so a half-answer is never silently returned as final. Omit for single-part asks."
854
951
  ),
855
- visuals: z12.array(VisualSchema).optional().describe(
952
+ visuals: z13.array(VisualSchema).optional().describe(
856
953
  "Images attached to the message itself \u2014 context for the whole question (a screenshot, a chart). For a preview on one selectable choice, use that option's `html`/`image` instead."
857
954
  ),
858
955
  /** Git repo the agent is working in ("owner/name"). Local MCP fills this from the checkout — omit unless overriding. */
859
- repo: z12.string().optional(),
956
+ repo: z13.string().optional(),
860
957
  /** Git branch the agent is on. Local MCP fills this from the checkout — omit unless overriding. */
861
- branch: z12.string().optional(),
958
+ branch: z13.string().optional(),
862
959
  /** Continue an existing conversation — the id of any notification in it (its root
863
960
  * is the conversation's identity). Omitted = start a new conversation. Renamed
864
961
  * from `parentId` (2026-08-03): one linkage system, the parent; the API edge
865
962
  * still accepts the old name from older clients. */
866
- parentId: z12.string().uuid().optional(),
963
+ parentId: z13.string().uuid().optional(),
867
964
  /** The durable outcome this contact advances. Optional during the notification-to-Work
868
965
  * migration; when present, a blocking ask creates a DecisionNeed for this Work. */
869
- workId: z12.string().uuid().optional(),
966
+ workId: z13.string().uuid().optional(),
870
967
  /** Target Goal scope. During staged migration this is accepted by the shared contract but
871
968
  * target delivery activation remains model-gated; workId and goalId are mutually exclusive. */
872
- goalId: z12.string().uuid().optional(),
969
+ goalId: z13.string().uuid().optional(),
873
970
  urgency: NotifyLevelSchema.default("inbox").describe(
874
971
  "The level you're requesting \u2014 the user's account permissions + session mode can lower it. 'inbox' (default) = sits silently in the inbox for the user to get to. 'push' = a quiet passive push (lands in Notification Center, no sound) \u2014 a gentle heads-up. 'banner' = a time-sensitive banner/lock-screen push with sound (a 'paige') they tap to open \u2014 use when you need them soon-ish but it's not worth ringing them. 'call' = rings the user's phone now (a CallKit voice call) \u2014 use only when you genuinely need them in the moment (blocked and waiting, time-sensitive). context.title is what they see on the banner/ring, so make it specific."
875
972
  ),
@@ -877,7 +974,7 @@ var NotifyRequestFields = z12.object({
877
974
  * visible and marks it needs_input. Renamed from the old `parentId` (2026-08-03)
878
975
  * when `parentId` became the conversation handle: `parentId` says WHERE, this
879
976
  * says HOW. */
880
- clarifies: z12.string().optional(),
977
+ clarifies: z13.string().optional(),
881
978
  select: SelectShapeSchema.optional().describe(
882
979
  "How the user answers \u2014 required on the fully-shaped form, pick the shape that fits the question: 'one' = pick one option, 'many' = pick several, 'rank' = pick & order (each needs `options`); 'confirm' = yes/no or approve/deny; 'text' = free-form reply only (status updates, open questions). 'confirm' and 'text' take no options. Omit only when sending the simplified `ask` form \u2014 the broker picks the shape."
883
980
  ),
@@ -888,23 +985,23 @@ var NotifyRequestFields = z12.object({
888
985
  * derive agent-side before sealing, so the server only ever shapes plaintext). */
889
986
  // 10k, not a sentence budget. What the human hears is bounded by the BROKER — it splits
890
987
  // the ask into topics and gives each one at most three sentences and one question
891
- // (docs/brain/broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
988
+ // (docs/history/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
892
989
  // Owner, 2026-07-28: "our actual limitation on how long something is to the user should
893
990
  // come from the broker splitting and summarizing." The cap that remains is a size guard.
894
- ask: z12.string().min(1).max(1e4).optional().describe(
991
+ ask: z13.string().min(1).max(1e4).optional().describe(
895
992
  'SIMPLIFIED FORM \u2014 state in plain prose what you need to learn from the user and why it matters now (e.g. "I need to know whether to deploy the auth fix \u2014 tests are green, staging verified"). Write as much as the situation needs (up to 10k characters) \u2014 Paigy breaks it into topics and reads it back a few sentences at a time; do NOT pre-summarize it into one line. Paigy derives the title, answer shape, options, and delivery channel for you. Mutually exclusive with context/select/options \u2014 send one form or the other.'
896
993
  ),
897
- needs: z12.array(z12.string().min(1)).optional().describe(
994
+ needs: z13.array(z13.string().min(1)).optional().describe(
898
995
  "With `ask` only: the distinct things you need answered when the ask is multi-part \u2014 becomes the coverage contract (`points`), so a half-answer is never silently final."
899
996
  ),
900
- urgencyHint: z12.enum(["whenever", "soon", "now"]).optional().describe(
997
+ urgencyHint: z13.enum(["whenever", "soon", "now"]).optional().describe(
901
998
  "With `ask` only: how urgently you need the answer \u2014 'whenever' (inbox), 'soon' (worth a heads-up), 'now' (you're blocked this minute). A hint, not a command: the user's settings still have the final word."
902
999
  ),
903
1000
  /** #575: the ONE self-report that replaces urgencyHint + blocking — what happens
904
1001
  * to the agent's work while it waits. Normalized server-side into those two
905
1002
  * fields (normalizeWaiting) so everything downstream is untouched; explicit
906
1003
  * urgencyHint/blocking win when both are sent. */
907
- waiting: z12.enum(["none", "soft", "hard"]).optional().describe(
1004
+ waiting: z13.enum(["none", "soft", "hard"]).optional().describe(
908
1005
  "With `ask`: what happens to your work while you wait. 'none' = you're just informing the user. 'soft' = you'd like an answer but can keep working. 'hard' = you are stopped until they answer (reaches them urgently and escalates to a real phone call if unanswered). Replaces urgencyHint + blocking \u2014 send this one field."
909
1006
  ),
910
1007
  /** Δ9b (#895): HOLD this claim so the sender can correct the plan before anyone is
@@ -912,98 +1009,98 @@ var NotifyRequestFields = z12.object({
912
1009
  * holding by default would charge every quiet claim that minute before any agent could
913
1010
  * correct anything. Ignored for `waiting: 'hard'`: a blocking ask rings on what we have,
914
1011
  * and the enrichment can still land mid-call (#781 re-plans the unspoken tail). */
915
- confirm: z12.boolean().optional().describe(
1012
+ confirm: z13.boolean().optional().describe(
916
1013
  "Hold this one so you can correct the plan before the user is interrupted. The response comes back with `held: true` and the plan; POST the confirm route to release it (with options/visuals/urgency corrections, or nothing at all). If you never do, it is announced anyway a couple of minutes later. Ignored when waiting is 'hard'."
917
1014
  ),
918
1015
  /** #575: a RELAY of the user's explicitly stated preference, never the agent's
919
1016
  * choice. Outranks waiting in both directions: 'call' rings even for a
920
1017
  * waiting:'none' "call me when it's done"; 'message' never rings even for
921
1018
  * waiting:'hard'. */
922
- channel: z12.enum(["call", "message"]).optional().describe(
1019
+ channel: z13.enum(["call", "message"]).optional().describe(
923
1020
  "Only if the user explicitly said how to reach them \u2014 'call me' \u2192 'call', 'just message/text me' \u2192 'message'. Omit otherwise; Paigy picks."
924
1021
  ),
925
- confirmStyle: z12.enum(["yesno", "approve"]).default("yesno").describe(
1022
+ confirmStyle: z13.enum(["yesno", "approve"]).default("yesno").describe(
926
1023
  "Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
927
1024
  ),
928
- blocking: z12.boolean().default(false).describe(
1025
+ blocking: z13.boolean().default(false).describe(
929
1026
  "Set true when real downstream work is stuck behind this specific decision \u2014 you can't make meaningful progress until it's answered. This is the real signal for how urgently the user should be reached; it's what the premier use case (an agent that stays unblocked instead of going idle) depends on. Independent of `urgency`: a `banner`-level question can still be `blocking` (something IS stuck, just not time-critical enough to ring for immediately) \u2014 if it goes unanswered a while, Paigy escalates it to a real call using this flag rather than guessing from how many other things happen to be pending. Leave false for anything you could work around, defer, or where other useful work exists meanwhile."
930
1027
  )
931
1028
  });
932
1029
  var NotifyRequestSchema = NotifyRequestFields.superRefine((r, ctx) => {
933
- if (r.workId && r.goalId) ctx.addIssue({ code: z12.ZodIssueCode.custom, path: ["goalId"], message: "pass goalId or workId, not both" });
1030
+ if (r.workId && r.goalId) ctx.addIssue({ code: z13.ZodIssueCode.custom, path: ["goalId"], message: "pass goalId or workId, not both" });
934
1031
  if (r.ask !== void 0) {
935
1032
  for (const f of ["context", "select", "points"]) {
936
1033
  if (r[f] !== void 0)
937
- ctx.addIssue({ code: z12.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives the answer shape from your prose. Drop ${f} and say it in \`ask\` instead ("should I\u2026" for approve/deny, "which of these\u2026" for a pick), passing \`options\` when you're offering concrete alternatives.` });
1034
+ ctx.addIssue({ code: z13.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives the answer shape from your prose. Drop ${f} and say it in \`ask\` instead ("should I\u2026" for approve/deny, "which of these\u2026" for a pick), passing \`options\` when you're offering concrete alternatives.` });
938
1035
  }
939
1036
  return;
940
1037
  }
941
1038
  if (r.needs !== void 0 || r.urgencyHint !== void 0)
942
- ctx.addIssue({ code: z12.ZodIssueCode.custom, path: ["needs"], message: "needs/urgencyHint belong to the simplified `ask` form \u2014 with a shaped request use points/urgency" });
1039
+ ctx.addIssue({ code: z13.ZodIssueCode.custom, path: ["needs"], message: "needs/urgencyHint belong to the simplified `ask` form \u2014 with a shaped request use points/urgency" });
943
1040
  if (!r.context)
944
- ctx.addIssue({ code: z12.ZodIssueCode.custom, path: ["context"], message: "context is required (plaintext path)" });
1041
+ ctx.addIssue({ code: z13.ZodIssueCode.custom, path: ["context"], message: "context is required (plaintext path)" });
945
1042
  if (!r.select)
946
- ctx.addIssue({ code: z12.ZodIssueCode.custom, path: ["select"], message: "select is required on the shaped form" });
1043
+ ctx.addIssue({ code: z13.ZodIssueCode.custom, path: ["select"], message: "select is required on the shaped form" });
947
1044
  const needsOptions = r.select === "one" || r.select === "many" || r.select === "rank";
948
1045
  if (needsOptions && !r.options?.length)
949
- ctx.addIssue({ code: z12.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' requires options` });
1046
+ ctx.addIssue({ code: z13.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' requires options` });
950
1047
  if (!needsOptions && r.options?.length)
951
- ctx.addIssue({ code: z12.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' takes no options` });
1048
+ ctx.addIssue({ code: z13.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' takes no options` });
952
1049
  });
953
- var NotifyStatusSchema = z12.enum(["pending", "answered", "ignored"]);
954
- var AgentStateSchema = z12.enum(["idle", "in_progress", "completed", "needs_input"]);
955
- var TurnSchema = z12.object({
956
- prompt: z12.string(),
957
- reply: z12.string()
1050
+ var NotifyStatusSchema = z13.enum(["pending", "answered", "ignored"]);
1051
+ var AgentStateSchema = z13.enum(["idle", "in_progress", "completed", "needs_input"]);
1052
+ var TurnSchema = z13.object({
1053
+ prompt: z13.string(),
1054
+ reply: z13.string()
958
1055
  });
959
- var UserAnswerSchema = z12.discriminatedUnion("kind", [
960
- z12.object({ kind: z12.literal("option"), optionId: z12.string(), label: z12.string().optional() }),
961
- z12.object({ kind: z12.literal("text"), text: z12.string() }),
962
- z12.object({ kind: z12.literal("ignored") }),
963
- z12.object({ kind: z12.literal("multi"), optionIds: z12.array(z12.string()), labels: z12.array(z12.string()).optional() }),
964
- z12.object({ kind: z12.literal("ranked"), optionIds: z12.array(z12.string()), labels: z12.array(z12.string()).optional() }),
965
- z12.object({ kind: z12.literal("clarify"), chunks: z12.array(z12.string()).min(1) }),
966
- z12.object({ kind: z12.literal("confirm"), approved: z12.boolean() }),
967
- z12.object({ kind: z12.literal("turns"), turns: z12.array(TurnSchema).min(1) }),
968
- /** An auto-answer derived from the user's PAST decisions (docs/brain/broker/precedent-design.md §2):
1056
+ var UserAnswerSchema = z13.discriminatedUnion("kind", [
1057
+ z13.object({ kind: z13.literal("option"), optionId: z13.string(), label: z13.string().optional() }),
1058
+ z13.object({ kind: z13.literal("text"), text: z13.string() }),
1059
+ z13.object({ kind: z13.literal("ignored") }),
1060
+ z13.object({ kind: z13.literal("multi"), optionIds: z13.array(z13.string()), labels: z13.array(z13.string()).optional() }),
1061
+ z13.object({ kind: z13.literal("ranked"), optionIds: z13.array(z13.string()), labels: z13.array(z13.string()).optional() }),
1062
+ z13.object({ kind: z13.literal("clarify"), chunks: z13.array(z13.string()).min(1) }),
1063
+ z13.object({ kind: z13.literal("confirm"), approved: z13.boolean() }),
1064
+ z13.object({ kind: z13.literal("turns"), turns: z13.array(TurnSchema).min(1) }),
1065
+ /** An auto-answer derived from the user's PAST decisions (docs/history/precedent-design.md §2):
969
1066
  * delivered through the same settle/await path as a human answer, carrying the judge's
970
1067
  * derivation and the precedent ids it grew from. Always paired with a visible trail
971
1068
  * card the user can reply to — the broker never overrides the user. */
972
- z12.object({ kind: z12.literal("precedent"), answer: z12.string(), derivation: z12.string(), sources: z12.array(z12.string()).min(1) })
1069
+ z13.object({ kind: z13.literal("precedent"), answer: z13.string(), derivation: z13.string(), sources: z13.array(z13.string()).min(1) })
973
1070
  ]);
974
- var IntentSchema = z12.object({
1071
+ var IntentSchema = z13.object({
975
1072
  // The full vocabulary the bot's mapper emits (mapper.INTENT_KINDS) — the schema lagged
976
1073
  // it by two ("detail", "feedback"), and because the settle handler parsed the array
977
1074
  // all-or-nothing, ONE feedback act silently dropped EVERY intent on the call,
978
1075
  // questions included. Found auditing five calls' stored feedback, 2026-08-01.
979
- kind: z12.enum(["defer", "delegate", "channel", "question", "detail", "feedback", "command", "control"]),
980
- detail: z12.string(),
1076
+ kind: z13.enum(["defer", "delegate", "channel", "question", "detail", "feedback", "command", "control"]),
1077
+ detail: z13.string(),
981
1078
  /** Defer only: seconds until the callback the caller asked for, when something upstream
982
1079
  * already read the time. Nothing sets it today (#397 documented an MCP parser that was
983
1080
  * never written) — the API reads the defer's `detail` itself with `notes/when.ts`
984
1081
  * (`parseDelay`, #1292), and a value here simply wins over that reading. */
985
- dueInSeconds: z12.number().int().positive().optional(),
1082
+ dueInSeconds: z13.number().int().positive().optional(),
986
1083
  /** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
987
1084
  * already read the utterance, so `signals.kind` stops defaulting to
988
1085
  * 'other' on every row. A table that records that something was wrong and nothing
989
1086
  * about what cannot answer "is the bot looping less this week?". */
990
- fault: z12.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
1087
+ fault: z13.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
991
1088
  });
992
- var RideAlongSchema = z12.object({
1089
+ var RideAlongSchema = z13.object({
993
1090
  /** The note this came from — assign/clarify/close it through /api/notes/:id. */
994
- noteId: z12.string(),
1091
+ noteId: z13.string(),
995
1092
  /** What to do, in the owner's own words (the note's headline). Never model-rewritten. */
996
- text: z12.string(),
1093
+ text: z13.string(),
997
1094
  /** The thread to report back on, when the note was dispatched over the request rail. */
998
- parentId: z12.string().nullable()
1095
+ parentId: z13.string().nullable()
999
1096
  });
1000
- var AwaitItemSchema = z12.discriminatedUnion("type", [
1001
- z12.object({
1002
- type: z12.literal("reply"),
1003
- parentId: z12.string(),
1004
- notificationId: z12.string(),
1005
- workId: z12.string().uuid().optional(),
1006
- decisionId: z12.string().uuid().optional(),
1097
+ var AwaitItemSchema = z13.discriminatedUnion("type", [
1098
+ z13.object({
1099
+ type: z13.literal("reply"),
1100
+ parentId: z13.string(),
1101
+ notificationId: z13.string(),
1102
+ workId: z13.string().uuid().optional(),
1103
+ decisionId: z13.string().uuid().optional(),
1007
1104
  answer: UserAnswerSchema,
1008
1105
  /** WHAT THE AGENT CANNOT KNOW FROM THE FIELDS BESIDE IT (owner, 2026-09-04, issue
1009
1106
  * #1537). One line, built from the record: the ask and the caller's reply VERBATIM,
@@ -1013,109 +1110,109 @@ var AwaitItemSchema = z12.discriminatedUnion("type", [
1013
1110
  * "call me back after you merge" in their own words decides for itself what to do,
1014
1111
  * and now knows exactly which call to make. Absent when either half is missing —
1015
1112
  * a sentence with a hole in it is worse than no sentence. */
1016
- note: z12.string().optional(),
1113
+ note: z13.string().optional(),
1017
1114
  /** The call record rendered for THIS agent (`docs/brain/voice/record-design.md`): the words the
1018
1115
  * shaped answer was mapped from, filtered to its own claims. There is no second list
1019
1116
  * of labels beside it — the acts went 2026-09-04 and `intents` went with them (owner,
1020
1117
  * 2026-09-04): the agent reads the sentence and decides. */
1021
- transcript: z12.string().optional(),
1118
+ transcript: z13.string().optional(),
1022
1119
  /** Coverage report (#396), when the ask declared `points`: which of them this
1023
1120
  * answer addressed. Missing points = re-ask or proceed knowingly partial. */
1024
- covered: z12.array(z12.string()).optional(),
1121
+ covered: z13.array(z13.string()).optional(),
1025
1122
  /** Ride-alongs (RideAlongSchema) — pending work for you, attached to the moment you
1026
1123
  * became free. Only `reply` and `idle` carry it: those are the two outcomes that
1027
1124
  * END a wait. `remind`, `superseded` and `turn` are mid-flight, and handing an
1028
1125
  * agent a side-quest while it is still holding the line is how the main thing gets
1029
1126
  * dropped. Absent/empty = nothing owed. */
1030
- also: z12.array(RideAlongSchema).optional()
1127
+ also: z13.array(RideAlongSchema).optional()
1031
1128
  }),
1032
- z12.object({
1033
- type: z12.literal("remind"),
1034
- parentId: z12.string(),
1035
- notificationId: z12.string(),
1036
- remindAt: z12.string().datetime({ offset: true }),
1129
+ z13.object({
1130
+ type: z13.literal("remind"),
1131
+ parentId: z13.string(),
1132
+ notificationId: z13.string(),
1133
+ remindAt: z13.string().datetime({ offset: true }),
1037
1134
  /** Seconds until remindAt, server-computed — pass straight to ScheduleWakeup. */
1038
- remindInSeconds: z12.number()
1135
+ remindInSeconds: z13.number()
1039
1136
  }),
1040
1137
  /** The awaited ask was REPLACED by a newer notification on its thread (e.g. a
1041
1138
  * post-feedback revision, #633) — the user will never answer this id. Stop
1042
1139
  * awaiting it; the live ask is the thread's newest turn (await that one, or
1043
1140
  * re-orient via contact({})). */
1044
- z12.object({
1045
- type: z12.literal("superseded"),
1046
- parentId: z12.string(),
1047
- notificationId: z12.string()
1141
+ z13.object({
1142
+ type: z13.literal("superseded"),
1143
+ parentId: z13.string(),
1144
+ notificationId: z13.string()
1048
1145
  }),
1049
1146
  /** A LIVE call's turn, streamed as it lands (#783). PROVISIONAL: the user can still
1050
1147
  * revise any of these until the final reply arrives — partial = intelligence,
1051
1148
  * settled = authorization. Use it to PREPARE (fetch, draft, warm), never to act
1052
1149
  * irreversibly. If `acts` carries a question aimed at you and you know the answer,
1053
1150
  * contact on the same thread right away — the caller hears it on the same call. */
1054
- z12.object({
1055
- type: z12.literal("partial"),
1056
- notificationId: z12.string(),
1057
- inFlight: z12.literal(true),
1058
- turn: z12.object({
1059
- idx: z12.number(),
1060
- prompt: z12.string(),
1061
- reply: z12.string(),
1062
- acts: z12.array(IntentSchema).nullable().optional()
1151
+ z13.object({
1152
+ type: z13.literal("partial"),
1153
+ notificationId: z13.string(),
1154
+ inFlight: z13.literal(true),
1155
+ turn: z13.object({
1156
+ idx: z13.number(),
1157
+ prompt: z13.string(),
1158
+ reply: z13.string(),
1159
+ acts: z13.array(IntentSchema).nullable().optional()
1063
1160
  })
1064
1161
  }),
1065
- z12.object({
1066
- type: z12.literal("idle"),
1067
- also: z12.array(RideAlongSchema).optional(),
1162
+ z13.object({
1163
+ type: z13.literal("idle"),
1164
+ also: z13.array(RideAlongSchema).optional(),
1068
1165
  /** Is a call live for this agent's user right now? The SDK polls the partial stream
1069
1166
  * (#783) between idle ticks ONLY while this is not `false` — a partial can only exist
1070
1167
  * during a live call, and polling for one on a banner/message was a wasted HTTP call +
1071
1168
  * 3 queries on every idle tick of every waiting agent (~80% of all traffic at scale).
1072
1169
  * Absent = an older API → the SDK keeps polling, exactly as before. */
1073
- inFlight: z12.boolean().optional()
1170
+ inFlight: z13.boolean().optional()
1074
1171
  })
1075
1172
  ]);
1076
- var VoiceKeySchema = z12.enum(["rachel", "george", "jessica", "brian", "lily"]);
1077
- var AgendaTurnSchema = z12.object({
1173
+ var VoiceKeySchema = z13.enum(["rachel", "george", "jessica", "brian", "lily"]);
1174
+ var AgendaTurnSchema = z13.object({
1078
1175
  /** THE TURN'S IDENTITY (the first-sentence stream, 2026-09-09): the brain call that wrote
1079
1176
  * it and its place in that reply — `<brainCallId>:<index>`, with `:p` on the first
1080
1177
  * sentence a re-plan publishes ahead of the rest. A turn is spoken once, by this id: the
1081
1178
  * completion of a streamed re-plan carries the published sentence again, and the walk
1082
1179
  * drops what it already said by identity, never by the API's guess of what was polled.
1083
1180
  * Absent on plans nothing streams (a ring plan, a floor). */
1084
- id: z12.string().optional(),
1181
+ id: z13.string().optional(),
1085
1182
  /** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
1086
1183
  * the planner declares duplicates instead of asking them twice. */
1087
- coveredIds: z12.array(z12.string()).optional(),
1184
+ coveredIds: z13.array(z13.string()).optional(),
1088
1185
  /** The spoken sentences of the turn, in order. No count: how long a turn is is the brain's call
1089
1186
  * (owner, 2026-09-25), and a count here refused whole plans. */
1090
- info: z12.array(z12.string().min(1)).default([]),
1187
+ info: z13.array(z13.string().min(1)).default([]),
1091
1188
  /** THE ID OF EACH SENTENCE, when the plan names them (turns.md): a talker message's sentences are its
1092
1189
  * `info`, one piece each, and `units[i]` is the id the bot's line receipt carries for `info[i]`
1093
1190
  * (`<cycle>:<key>:<n>`), so what was heard is known per sentence. Absent on every other turn. */
1094
- units: z12.array(z12.string().min(1)).optional(),
1095
- question: z12.string().min(1).nullable(),
1191
+ units: z13.array(z13.string().min(1)).optional(),
1192
+ question: z13.string().min(1).nullable(),
1096
1193
  /** True on the one turn carrying the agent's own declared question. */
1097
- asks: z12.boolean().optional(),
1194
+ asks: z13.boolean().optional(),
1098
1195
  /** The claim this turn belongs to (#781) — the RETURN identity: answers route by it.
1099
1196
  * Absent on a single-claim plan (the session's own claim) and on shared context turns,
1100
1197
  * which route nothing. */
1101
- claimId: z12.string().optional(),
1198
+ claimId: z13.string().optional(),
1102
1199
  /** The claim's voice key (#462) — the OUTBOUND identity, audible who-is-asking. */
1103
- voice: z12.string().optional(),
1200
+ voice: z13.string().optional(),
1104
1201
  /** The claim's AGENT NAME (#838) — the spoken identity. A voice alone doesn't say
1105
1202
  * whose request this is: an item that folded in from another agent arrived as a bare
1106
1203
  * non-sequitur ("First real production sign-in is yours to make whenever you want.")
1107
1204
  * and the owner answered "What?". The bot names the agent before its first turn. */
1108
- agent: z12.string().optional(),
1205
+ agent: z13.string().optional(),
1109
1206
  /** The claim's agent by ID — the pairing's connection id (`notifications.token_id`), the
1110
1207
  * same id a face is minted from. A name is not an identity: two pairings may be called
1111
1208
  * "Claude", and a name cannot be joined on. The record's entries carry it (`agent_id`)
1112
1209
  * so "who said that" survives the call, and it rides PER TURN because a coalesced call
1113
1210
  * speaks for several agents — the turn is the only place that knows which. */
1114
- agentId: z12.string().optional(),
1211
+ agentId: z13.string().optional(),
1115
1212
  /** ITS QUESTION IS ON THE TABLE (#3292): a talker line about a question the person owes. The walk tells
1116
1213
  * the phone (`asking`, so the agenda goes gold and its options show) without treating the line as a
1117
1214
  * question turn — the talker owns re-asking and the waits, so `question` stays null. */
1118
- onTable: z12.boolean().optional(),
1215
+ onTable: z13.boolean().optional(),
1119
1216
  /** THE CARRIED ITEM THIS LINE TELLS (Goal 2a1e50eb, owner 2026-10-14: "Build the whole design") — a
1120
1217
  * request the Call carries that asks NOTHING: a report, a progress note, another agent's waiting news.
1121
1218
  * Its request Entry id, so the phone can make that agenda row the current one while it is read
@@ -1125,14 +1222,14 @@ var AgendaTurnSchema = z12.object({
1125
1222
  * record tags the line with it (`workId`) — and an answer must never route to an Entry that asks
1126
1223
  * nothing. Absent on a turn speaking for a question (`claimId` says that) and on one speaking for no
1127
1224
  * carried item at all (an opener, a sign-off, Paigy's own line). */
1128
- telling: z12.string().optional(),
1225
+ telling: z13.string().optional(),
1129
1226
  /** PAIGY'S OWN LINE (owner, 2026-10-15; calls ed2cbdce, ad50ad25): a turn that speaks for no item -- an
1130
1227
  * opener, a sign-off, a reply about nothing an agent asked or told -- is recorded as Paigy's, not as the
1131
1228
  * agent that rang. Absent on every turn that speaks for an item (`agentId` names whose), and on plans
1132
1229
  * from before it: a bot that does not know it records the ringing agent, as before. */
1133
- speaksFor: z12.literal("paigy").optional(),
1230
+ speaksFor: z13.literal("paigy").optional(),
1134
1231
  select: SelectShapeSchema.optional(),
1135
- options: z12.array(OptionSchema.omit({ id: true })).optional(),
1232
+ options: z13.array(OptionSchema.omit({ id: true })).optional(),
1136
1233
  /* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
1137
1234
  paces itself now — a short beat between the sentences of a turn, the longer one at its end
1138
1235
  (owner, 2026-09-30: "remove the bot deciding pace") — and it does that where the words are
@@ -1141,58 +1238,58 @@ var AgendaTurnSchema = z12.object({
1141
1238
  /** Whether the walk WAITS for an answer before moving on. Absent = derived as today
1142
1239
  * (a question blocks, context flows). blocking:false on a question = ask and move
1143
1240
  * on, the claim stays pending; blocking:true on context = hold for a reply. */
1144
- blocking: z12.boolean().optional(),
1241
+ blocking: z13.boolean().optional(),
1145
1242
  /** SPOKEN ONLY IF THEY SAY NOTHING (owner, 2026-10-01, call 812de935: "you're gonna re-ask, but it
1146
1243
  * shouldn't be the same words … more like, hey, are you still there, or are you able to answer, or
1147
1244
  * would you need more information"). The walk holds this turn out of its queue; at the queue's end it
1148
1245
  * listens for the last word, and only if that listen is silent is this turn said and asked. If they
1149
1246
  * speak, it is dropped and their words are taken like any reply. */
1150
- ifSilent: z12.boolean().optional()
1247
+ ifSilent: z13.boolean().optional()
1151
1248
  });
1152
1249
  var CLAIM_STALE_MS = 30 * 6e4;
1153
- var CallQuestionSchema = z12.object({
1154
- id: z12.string(),
1155
- entryId: z12.string(),
1156
- title: z12.string(),
1157
- state: z12.enum(["open", "answered"]),
1158
- answer: z12.string().nullable(),
1250
+ var CallQuestionSchema = z13.object({
1251
+ id: z13.string(),
1252
+ entryId: z13.string(),
1253
+ title: z13.string(),
1254
+ state: z13.enum(["open", "answered"]),
1255
+ answer: z13.string().nullable(),
1159
1256
  /** WHO ASKED IT (owner, 2026-09-23, Goal a345e906: each agenda row wears its agent's face) — the
1160
1257
  * request Entry's author, as the same three facts the item's own `tokenId`/`name`/`voice`
1161
1258
  * carry for the call's one agent, so the phone draws it with the same seed. Absent when the
1162
1259
  * author is not an agent this account holds (unpaired since, or a person). */
1163
- agent: z12.object({ tokenId: z12.string(), name: z12.string(), voice: VoiceKeySchema.optional() }).optional(),
1260
+ agent: z13.object({ tokenId: z13.string(), name: z13.string(), voice: VoiceKeySchema.optional() }).optional(),
1164
1261
  /** ITS OPTIONS, WHEN THERE IS SOMETHING TO SEE (owner, 2026-09-25: "Yes, add it"): the options
1165
1262
  * its need offers, exactly as its own card carries them, present only when one of them has a
1166
1263
  * preview (`html` or `image`). The call screen opens them from the agenda row, so a preview is
1167
1264
  * never re-sent as a second card to be seen mid-call. Words-only options are `choices`. */
1168
- options: z12.array(OptionSchema).optional(),
1265
+ options: z13.array(OptionSchema).optional(),
1169
1266
  /** ITS OPTIONS WHEN THEY ARE WORDS (owner, 2026-10-02, on a call: "whenever there are pre-made
1170
1267
  * options … now for a checklist, we should also show them on the screen"; design C, 2026-10-08):
1171
1268
  * numbered chips above the call's controls, so a checklist is never only something read aloud. A
1172
1269
  * field of its own so `options` keeps meaning "previews": a phone on an older bundle opens the
1173
1270
  * preview layout for any `options`, and must see exactly what it did. */
1174
- choices: z12.array(OptionSchema).optional(),
1271
+ choices: z13.array(OptionSchema).optional(),
1175
1272
  /** HOW MANY MAY BE PICKED, beside its `choices`: one, any (`many`) or an order (`rank`). */
1176
- select: z12.enum(["one", "many", "rank"]).optional()
1273
+ select: z13.enum(["one", "many", "rank"]).optional()
1177
1274
  });
1178
- var InboxItemSchema = z12.object({
1179
- id: z12.string(),
1180
- tokenId: z12.string().optional(),
1275
+ var InboxItemSchema = z13.object({
1276
+ id: z13.string(),
1277
+ tokenId: z13.string().optional(),
1181
1278
  status: NotifyStatusSchema,
1182
1279
  context: ContextSchema,
1183
- options: z12.array(OptionSchema).optional(),
1280
+ options: z13.array(OptionSchema).optional(),
1184
1281
  /** The ask's declared coverage points (#396), when the agent sent them. */
1185
- points: z12.array(z12.string()).optional(),
1282
+ points: z13.array(z13.string()).optional(),
1186
1283
  /** Does this claim want an ANSWER, or is it telling you something? Written per row from
1187
1284
  * `requestAsks` — the agent's own declaration, not a guess. `false` is what earns a card
1188
1285
  * its acknowledge affordance: without it a status update offers a text box and a dismiss,
1189
1286
  * and neither of those is "got it" (owner, 2026-08-10). */
1190
- asks: z12.boolean().optional(),
1287
+ asks: z13.boolean().optional(),
1191
1288
  /** When a live process last pulsed for this row's agent — the liveness input for
1192
1289
  * "working requires a pulse" (#928): the list said "Working…" from agent_state alone
1193
1290
  * while the party called the same dead claim stalled. Absent = no token/no data,
1194
1291
  * which must never CLAIM stalled. */
1195
- lastSeenAt: z12.string().optional(),
1292
+ lastSeenAt: z13.string().optional(),
1196
1293
  /** WHEN THE AGENT LAST SAID ANYTHING ABOUT THIS CLAIM — the newest `agent_state` row in
1197
1294
  * the `notification_events` ledger (trigger-written since 20260621010000, so every row a
1198
1295
  * user can see has one). The age input for `CLAIM_STALE_MS`, and it has to be this rather
@@ -1202,7 +1299,7 @@ var InboxItemSchema = z12.object({
1202
1299
  * work. Reading the row's birth as the claim's age brands that "No update in 8h" the
1203
1300
  * instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
1204
1301
  * `createdAt`. */
1205
- agentStateAt: z12.string().datetime().optional(),
1302
+ agentStateAt: z13.string().datetime().optional(),
1206
1303
  /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (docs/clients/app/walk/design.md §11, owner
1207
1304
  * 2026-09-22). One per DecisionNeed on the Call, in the Call's order, answered or open (a
1208
1305
  * superseded or cancelled need is no longer a question anyone is asked). Present only on a
@@ -1216,40 +1313,35 @@ var InboxItemSchema = z12.object({
1216
1313
  * `turn` topic (`asking`, `settled`), because the bot never sees a DecisionNeed id. `title` is
1217
1314
  * the card's own concise heading; `answer` the accepted answer in words, null while open. It
1218
1315
  * REPLACED `agenda` (turns), which nothing ever filled. */
1219
- questions: z12.array(CallQuestionSchema).optional(),
1316
+ questions: z13.array(CallQuestionSchema).optional(),
1220
1317
  /** The same call carrying order, including request Entries with no DecisionNeed as reports.
1221
1318
  * Additive: questions stays decision-only for every other surface and older bundles. */
1222
- callItems: z12.array(CallQuestionSchema.extend({ state: z12.enum(["open", "answered", "report"]), acknowledged: z12.boolean().optional() })).optional(),
1223
- visuals: z12.array(VisualSchema).optional(),
1319
+ callItems: z13.array(CallQuestionSchema.extend({ state: z13.enum(["open", "answered", "report"]), acknowledged: z13.boolean().optional() })).optional(),
1320
+ visuals: z13.array(VisualSchema).optional(),
1224
1321
  /** The connected agent's name (the single pairing name — user-typed, or the
1225
1322
  * agent's suggestion, or a default silly name). */
1226
- name: z12.string(),
1323
+ name: z13.string(),
1227
1324
  /** The pairing's assigned voice (#462); absent = the default voice. */
1228
1325
  voice: VoiceKeySchema.optional(),
1229
- repo: z12.string().optional(),
1230
- branch: z12.string().optional(),
1231
- createdAt: z12.string().datetime(),
1232
- snoozedUntil: z12.string().datetime().optional(),
1326
+ repo: z13.string().optional(),
1327
+ branch: z13.string().optional(),
1328
+ createdAt: z13.string().datetime(),
1329
+ snoozedUntil: z13.string().datetime().optional(),
1233
1330
  agentState: AgentStateSchema.default("idle"),
1234
1331
  /** Whose action the item is waiting on: "you" = an agent asked you (the default,
1235
1332
  * every agent→user notification); "agent" = you sent a request and it's awaiting the
1236
1333
  * agent (held in the inbox until the agent replies on the thread). */
1237
- turn: z12.enum(["you", "agent"]).default("you"),
1334
+ turn: z13.enum(["you", "agent"]).default("you"),
1238
1335
  /** Hard error reason on an awaiting request (turn="agent") — the wake failed to reach
1239
1336
  * the agent (provider-agnostic; set server-side). Absent = no hard error. Drives the inbox
1240
1337
  * error badge + Retry. */
1241
- error: z12.string().optional(),
1242
- /** WHEN THIS AGENT WORK WENT QUIET (turn="agent"), by the one rule (`coldSince`: three days
1243
- * with nothing said), or absent while it is not stalled. The inbox's stalled badge reads
1244
- * this and nothing else (2026-09-23: a 3-minute age rule badged every live Goal stalled,
1245
- * and "dismiss the stalled ones" cancelled 37 pieces of live work). */
1246
- cold: z12.string().datetime().optional(),
1247
- clarifies: z12.string().optional(),
1338
+ error: z13.string().optional(),
1339
+ clarifies: z13.string().optional(),
1248
1340
  /** THIS CARD'S QUESTION IS ON A LIVE CALL (owner, 2026-09-24: "Mark it while the call is
1249
1341
  * live"). Present only while an open Call Delivery carries the card's request Entry — read
1250
1342
  * off the same open list the card came from, so it clears when the Call does. A card is the
1251
1343
  * backup for a call not taken; while the call has it, the call is where it is answered. */
1252
- onCall: z12.literal(true).optional(),
1344
+ onCall: z13.literal(true).optional(),
1253
1345
  /** THE RING, ON THE ITEM (docs/clients/app/walk/design.md §12 §17, #2251): the last ring on this card was
1254
1346
  * declined, and what the ladder will do next — read off the cron's own row, never computed
1255
1347
  * on the phone. Present only while a `declined` receipt stands on the card's last Call.
@@ -1259,31 +1351,31 @@ var InboxItemSchema = z12.object({
1259
1351
  * It replaced `gaveUp` (deleted 2026-09-22): "the ladder spent" was a boolean the projection
1260
1352
  * never set, and it is `nextRingAt === null` here — the party's *Missed you* (`party/dress.ts`)
1261
1353
  * and the roster's `unreached` read `declinedAt`, and stand while it does. */
1262
- ring: z12.object({
1263
- declinedAt: z12.string().datetime(),
1264
- anchorAt: z12.string().datetime(),
1265
- nextRingAt: z12.string().datetime().nullable(),
1266
- step: z12.number().int()
1354
+ ring: z13.object({
1355
+ declinedAt: z13.string().datetime(),
1356
+ anchorAt: z13.string().datetime(),
1357
+ nextRingAt: z13.string().datetime().nullable(),
1358
+ step: z13.number().int()
1267
1359
  }).optional(),
1268
1360
  /** Why this arrived the way it did, read back off the delivery receipt (`notify/why.ts`).
1269
1361
  * Absent for anything never delivered through a push, and for older rows written before
1270
1362
  * the reason was recorded. Deliberately a debug affordance, shown small (owner,
1271
1363
  * 2026-08-07) — its real job is to give "this didn't need a call" something to be
1272
1364
  * feedback ABOUT. */
1273
- why: z12.object({
1365
+ why: z13.object({
1274
1366
  asked: NotifyLevelSchema,
1275
1367
  got: NotifyLevelSchema,
1276
- because: z12.enum(["unresponsive", "dismissed", "not_permitted", "silent", "coalesced", "agent_capped", "unplanned", "learned_raise"]).optional(),
1277
- line: z12.string()
1368
+ because: z13.enum(["unresponsive", "dismissed", "not_permitted", "silent", "coalesced", "agent_capped", "unplanned", "learned_raise"]).optional(),
1369
+ line: z13.string()
1278
1370
  }).optional(),
1279
- select: z12.enum(["one", "many", "rank", "confirm", "text"]).default("one"),
1280
- confirmStyle: z12.enum(["yesno", "approve"]).default("yesno").describe(
1371
+ select: z13.enum(["one", "many", "rank", "confirm", "text"]).default("one"),
1372
+ confirmStyle: z13.enum(["yesno", "approve"]).default("yesno").describe(
1281
1373
  "Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
1282
1374
  ),
1283
1375
  /** Real downstream work is stuck behind this one — set by the agent, independent of
1284
- * urgency (see the main README's "premier use case" + docs/delivery/notify/states.md). Drives the
1376
+ * urgency (see the main README's "premier use case" + docs/history/notify-states.md). Drives the
1285
1377
  * inbox's blocking badge and the extra confirm step before dismissing it. */
1286
- blocking: z12.boolean().default(false),
1378
+ blocking: z13.boolean().default(false),
1287
1379
  /** WHAT FREED THIS QUESTION (owner, 2026-10-16, Goal 2383ef14: "we don't really show the question
1288
1380
  * at the top until the question it could reply to with is resolved. And then we show it again
1289
1381
  * with the context of the other thing that was resolved"; asked how it should carry that, "The
@@ -1291,14 +1383,25 @@ var InboxItemSchema = z12.object({
1291
1383
  * (`private.ask_back`) and drops out of the front while it waits; when it comes back, the card
1292
1384
  * draws their question and the answer it got ABOVE the ask, as one card. Absent on a question
1293
1385
  * nothing ever held back. */
1294
- freedBy: z12.object({
1295
- questionId: z12.string(),
1296
- asked: z12.string(),
1297
- answer: z12.string(),
1298
- answeredAt: z12.string(),
1386
+ freedBy: z13.object({
1387
+ questionId: z13.string(),
1388
+ asked: z13.string(),
1389
+ answer: z13.string(),
1390
+ answeredAt: z13.string(),
1299
1391
  /** The answer in ten words, when the talker wrote one beside it. */
1300
- summary: z12.string().optional()
1392
+ summary: z13.string().optional()
1301
1393
  }).optional(),
1394
+ /** WHAT THIS CARD ANSWERS (owner, 2026-10-27, Goal 81b6a9d6: "this reads very clearly as an answer to
1395
+ * something I asked, but I don't see the question… To show the question near the top of the card").
1396
+ * An agent's card that is its answer to one of the person's own questions carries that question's
1397
+ * words, drawn above it as "You asked" (`public.questions_answered_by`, read beside the card read).
1398
+ * Absent on every card that answers nothing of theirs, and on a held question, which has `freedBy`. */
1399
+ answering: z13.object({ asked: z13.string() }).optional(),
1400
+ /** WHEN ITS AGENT LAST BUMPED THIS QUESTION (#2855, `contact`'s `bumpQuestionIds`): the agent asked
1401
+ * the person to notice it again, and For you puts it first, the latest bump on top
1402
+ * (`question/order.ts`). Absent on a question never bumped, and on every card from an API older
1403
+ * than the bump. */
1404
+ bumpedAt: z13.string().optional(),
1302
1405
  /** The user's locked-in answer; present only for replied items (GET /api/replied/:id). */
1303
1406
  answer: UserAnswerSchema.optional(),
1304
1407
  /** THE TARGET FACTS A CARD RENDERS (#1796 point 5, 2026-09-11): the Delivery it is a view of,
@@ -1306,17 +1409,17 @@ var InboxItemSchema = z12.object({
1306
1409
  * for a request that asks nothing), whether its content is sealed, and that Goal's state. The
1307
1410
  * answer writer (`POST /api/entries`) and the disposition (`close_delivery`) take their ids from
1308
1411
  * here. The server projects it (`apps/api/src/inbox/project.ts`); a client never builds it. */
1309
- communication: z12.object({
1310
- deliveryId: z12.string(),
1311
- kind: z12.enum(["notification", "call"]),
1312
- entryId: z12.string(),
1313
- goalIds: z12.array(z12.string()),
1314
- decisionNeedId: z12.string().optional(),
1315
- sealed: z12.boolean(),
1316
- goalState: z12.string().optional(),
1412
+ communication: z13.object({
1413
+ deliveryId: z13.string(),
1414
+ kind: z13.enum(["notification", "call"]),
1415
+ entryId: z13.string(),
1416
+ goalIds: z13.array(z13.string()),
1417
+ decisionNeedId: z13.string().optional(),
1418
+ sealed: z13.boolean(),
1419
+ goalState: z13.string().optional(),
1317
1420
  /** THAT GOAL'S NAME (#2416) — what Activity's row is headed by, since a row there is one Goal
1318
1421
  * and the cards it holds sit behind it. Stamped by the same read as `goalState`. */
1319
- goalTitle: z12.string().optional()
1422
+ goalTitle: z13.string().optional()
1320
1423
  }).optional(),
1321
1424
  /** WHAT THIS CARD IS, IN TWELVE CHARACTERS (#3019) — the hash of every other field on it, stamped
1322
1425
  * by the one read that serves the open list (`apps/api/src/inbox/project.ts` `inboxFor`). It is
@@ -1325,30 +1428,30 @@ var InboxItemSchema = z12.object({
1325
1428
  *
1326
1429
  * THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK — the same mechanism as `QueueItem.rev`
1327
1430
  * (#2928) and an entry's (#3018), for the same reason: a card shows facts no `updated_at` of its
1328
- * own moves (its Goal's state and name, how cold the work behind it has gone, whether a ring is
1431
+ * own moves (its Goal's state and name, whether a ring is
1329
1432
  * live). Optional, so a fixture, the demo and the archive lens need not spell one, and a card
1330
1433
  * with no rev is simply always re-sent. */
1331
- rev: z12.string().optional()
1434
+ rev: z13.string().optional()
1332
1435
  });
1333
1436
  var APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
1334
- var PushTokenSchema = z12.object({
1335
- voipToken: z12.string().min(1).optional(),
1336
- alertToken: z12.string().min(1).optional(),
1337
- fcmToken: z12.string().min(1).optional(),
1338
- platform: z12.enum(["ios", "android"])
1437
+ var PushTokenSchema = z13.object({
1438
+ voipToken: z13.string().min(1).optional(),
1439
+ alertToken: z13.string().min(1).optional(),
1440
+ fcmToken: z13.string().min(1).optional(),
1441
+ platform: z13.enum(["ios", "android"])
1339
1442
  }).superRefine((v, ctx) => {
1340
1443
  if (v.platform !== "ios") return;
1341
1444
  for (const field of ["voipToken", "alertToken"]) {
1342
1445
  const token = v[field];
1343
1446
  if (token === void 0 || APNS_TOKEN_RE.test(token)) continue;
1344
1447
  ctx.addIssue({
1345
- code: z12.ZodIssueCode.custom,
1448
+ code: z13.ZodIssueCode.custom,
1346
1449
  path: [field],
1347
1450
  message: `not an APNs device token (want 64 hex chars, got ${token.length})`
1348
1451
  });
1349
1452
  }
1350
1453
  });
1351
- var MissedCallSchema = z12.enum([
1454
+ var MissedCallSchema = z13.enum([
1352
1455
  "retry_10m",
1353
1456
  "retry_30m",
1354
1457
  "retry_60m",
@@ -1360,31 +1463,32 @@ var MissedCallSchema = z12.enum([
1360
1463
  ]);
1361
1464
  var clock = (h) => h === 0 ? "midnight" : h === 12 ? "noon" : h < 12 ? `${h} am` : `${h - 12} pm`;
1362
1465
  var QUIET = ` Nothing rings from ${clock(NIGHT.from)} to ${clock(NIGHT.to)} your time; the count waits for morning.`;
1363
- var BrokerTuningSchema = z12.object({
1466
+ var THEIR_QUIET = ` Nothing rings from ${clock(NIGHT.from)} to ${clock(NIGHT.to)} their time; the count waits for morning.`;
1467
+ var BrokerTuningSchema = z13.object({
1364
1468
  /** 'none' = skip the spoken ack after a mapped answer (power users find it slow). */
1365
- ackVerbosity: z12.enum(["normal", "none"]).optional(),
1469
+ ackVerbosity: z13.enum(["normal", "none"]).optional(),
1366
1470
  /** How readily the mapper asks its one clarification: 'low' = only when truly
1367
1471
  * uninterpretable, 'high' = whenever not fully certain. */
1368
- clarifyEagerness: z12.enum(["low", "normal", "high"]).optional(),
1472
+ clarifyEagerness: z13.enum(["low", "normal", "high"]).optional(),
1369
1473
  /** The user's own shorthand: when they say `say`, they mean `mean`. */
1370
- phrasebook: z12.array(z12.object({ say: z12.string().min(1).max(60), mean: z12.string().min(1).max(120) })).max(24).optional(),
1474
+ phrasebook: z13.array(z13.object({ say: z13.string().min(1).max(60), mean: z13.string().min(1).max(120) })).max(24).optional(),
1371
1475
  /** The language calls are PLANNED in, when the account has chosen one (#1272). Absent —
1372
1476
  * which is every account today — means the agent's own words decide, per ask: a call
1373
1477
  * about an English ask opens in English. This is the only thing that overrides that,
1374
1478
  * and a live caller who switches language mid-call still outranks it (broker/lang.ts).
1375
1479
  * Set per user (no UI yet), like `voiceTuning`. */
1376
- language: z12.enum(["en", "es"]).optional()
1480
+ language: z13.enum(["en", "es"]).optional()
1377
1481
  });
1378
- var UserSettingsSchema = z12.object({
1379
- permissions: z12.object({
1380
- call: z12.boolean(),
1381
- banner: z12.boolean(),
1382
- push: z12.boolean()
1482
+ var UserSettingsSchema = z13.object({
1483
+ permissions: z13.object({
1484
+ call: z13.boolean(),
1485
+ banner: z13.boolean(),
1486
+ push: z13.boolean()
1383
1487
  }),
1384
1488
  /** LockedIn / Default / DateNight on screen; the stored words are unchanged on purpose —
1385
1489
  * they are an enum on a live column across every account, and the rename is a rename of
1386
1490
  * what people read (owner, 2026-09-30). */
1387
- sessionMode: z12.enum(["default", "all_calls", "silent"]),
1491
+ sessionMode: z13.enum(["default", "all_calls", "silent"]),
1388
1492
  /** `silentPush` lived here until #2813 and is now GONE, field and column both. It was kept as an
1389
1493
  * optional long after DateNight stopped reading it, on the theory that a phone on an older
1390
1494
  * bundle PATCHing the whole settings object would be REFUSED for sending a key we had stopped
@@ -1393,7 +1497,7 @@ var UserSettingsSchema = z12.object({
1393
1497
  * an old bundle's `silentPush` is accepted and ignored. Worth remembering before keeping the
1394
1498
  * next dead field for the same reason. */
1395
1499
  /** Opt-in (default false) to using your content to improve Paigy and train models. */
1396
- improveConsent: z12.boolean(),
1500
+ improveConsent: z13.boolean(),
1397
1501
  missedCall: MissedCallSchema.default("backoff_standard"),
1398
1502
  /** Where voice audio is processed. 'hosted' (default) = Paigy's voice services
1399
1503
  * (ElevenLabs TTS, faster-whisper STT, the call bot); 'on_device' = the phone
@@ -1401,17 +1505,14 @@ var UserSettingsSchema = z12.object({
1401
1505
  * Optional, NOT defaulted: a stale client PATCHing the full settings object
1402
1506
  * must not silently reset this privacy choice. Absent = leave unchanged on
1403
1507
  * write, 'hosted' on read (see store.ts). */
1404
- voiceMode: z12.enum(["hosted", "on_device"]).optional(),
1405
- /** Talk — after you answer, the next step is read aloud (docs/clients/app/walk/design.md §6). ALWAYS ON until
1406
- * turned off (owner, 2026-09-18, #2249): a setting, not a per-walk toggle. Optional, NOT
1407
- * defaulted, for the same reason `voiceMode` is: a stale client PATCHing the full settings
1408
- * object must not silently turn it back on. Absent = leave unchanged on write, true on
1409
- * read (see store.ts). */
1410
- talk: z12.boolean().optional(),
1508
+ voiceMode: z13.enum(["hosted", "on_device"]).optional(),
1509
+ /** `talk` lived here until #2373: it gated auto-play, which went in #2374. An older bundle's
1510
+ * `talk` is stripped like `silentPush` was; the `user_settings.talk` column outlives it until
1511
+ * its own drop migration (#2373, step two). */
1411
1512
  /** CALL DIAGNOSTICS (owner, 2026-10-01): the call report carries each listen and the bot's own
1412
1513
  * load timings. SERVER-SET, no UI — on for every account that existed on 2026-10-01, off for
1413
1514
  * newer ones (migration 20261001132859). Read-only here: the settings PATCH never writes it. */
1414
- callDiagnostics: z12.boolean().optional(),
1515
+ callDiagnostics: z13.boolean().optional(),
1415
1516
  /** Per-user ring budget (#603): calls per rolling day before further calls
1416
1517
  * degrade to banner. Absent = the global default (25). A number, never a
1417
1518
  * bypass — every account keeps a ceiling. No UI; set per user for testing. */
@@ -1419,7 +1520,7 @@ var UserSettingsSchema = z12.object({
1419
1520
  * payload['tuning'] (e.g. { silence_s: 3.5 } — a longer pause window for a
1420
1521
  * slower speaker). No API-side semantics; the bot resolves each key with its
1421
1522
  * own defaults. Set per user (no UI yet); absent = bot defaults. */
1422
- voiceTuning: z12.record(z12.string(), z12.union([z12.number(), z12.string()])).optional(),
1523
+ voiceTuning: z13.record(z13.string(), z13.union([z13.number(), z13.string()])).optional(),
1423
1524
  /** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
1424
1525
  * only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
1425
1526
  * an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
@@ -1428,108 +1529,109 @@ var UserSettingsSchema = z12.object({
1428
1529
  * that failure reads as the reminder rail being unreliable rather than as a missing
1429
1530
  * setting. Absent = a spoken time can't be landed, so the reminder rides the next
1430
1531
  * call — honest about what we know. */
1431
- timezone: z12.string().min(1).max(64).optional(),
1532
+ timezone: z13.string().min(1).max(64).optional(),
1432
1533
  /** Rung-2 broker tuning (#381). Optional and NOT defaulted, same stale-client
1433
1534
  * clobber guard as voiceMode: absent = leave unchanged on write. */
1434
1535
  broker: BrokerTuningSchema.optional()
1435
1536
  });
1436
- var GoalLatestSchema = z12.object({
1437
- from: z12.enum(["person", "agent"]),
1438
- said: z12.string(),
1439
- at: z12.string(),
1440
- entryId: z12.string(),
1537
+ var GoalLatestSchema = z13.object({
1538
+ from: z13.enum(["person", "agent"]),
1539
+ said: z13.string(),
1540
+ at: z13.string(),
1541
+ entryId: z13.string(),
1441
1542
  /** ITS SUMMARY IN AT MOST TEN WORDS (owner, 2026-10-08: "the same switch to the more summarized sources
1442
1543
  * at the work tab top level screen too"): what the talker or filer wrote on the Entry; absent when
1443
1544
  * none was. The Work row reads it before `said`. */
1444
- summary: z12.string().optional(),
1545
+ summary: z13.string().optional(),
1445
1546
  /** AN AGENT'S UPDATE OR QUESTION AS ITS UNITS' TITLES (owner, 2026-10-08: "send the 50 chars from the title
1446
1547
  * of each unit"): each at most 50 characters, in order; absent when it has none. */
1447
- titles: z12.array(z12.string()).optional(),
1548
+ titles: z13.array(z13.string()).optional(),
1448
1549
  /** A PROGRESS NOTE'S ONE POINT ("An agent's progress note -> maybe let's cap it at 200 chars"): the
1449
1550
  * update's own line, before its units; the row caps it at 200. Absent on anything else. */
1450
- line: z12.string().optional()
1551
+ line: z13.string().optional()
1451
1552
  });
1452
- var HistoryWorkSchema = z12.object({
1453
- id: z12.string(),
1454
- title: z12.string(),
1455
- state: z12.enum(["done", "cancelled"]),
1553
+ var HistoryWorkSchema = z13.object({
1554
+ id: z13.string(),
1555
+ title: z13.string(),
1556
+ state: z13.enum(["done", "cancelled"]),
1456
1557
  /** Who held it (`agent:<tokenId>` or `human:<userId>`). */
1457
- assignee: z12.string(),
1558
+ assignee: z13.string(),
1458
1559
  latest: GoalLatestSchema.nullable().optional()
1459
1560
  });
1460
- var HistoryEntrySchema = z12.union([
1461
- z12.object({ at: z12.string(), card: InboxItemSchema }),
1462
- z12.object({ at: z12.string(), work: HistoryWorkSchema })
1561
+ var HistoryEntrySchema = z13.union([
1562
+ z13.object({ at: z13.string(), card: InboxItemSchema }),
1563
+ z13.object({ at: z13.string(), work: HistoryWorkSchema })
1463
1564
  ]);
1464
- var HistoryPageSchema = z12.object({
1465
- entries: z12.array(HistoryEntrySchema),
1466
- next: z12.string().nullable()
1565
+ var HistoryPageSchema = z13.object({
1566
+ entries: z13.array(HistoryEntrySchema),
1567
+ next: z13.string().nullable()
1467
1568
  });
1468
1569
  var ACTIVITY_LINES = 2;
1469
1570
  var ACTIVITY_LINE_MAX = 80;
1470
- var AgentActivitySchema = z12.object({
1571
+ var AgentActivitySchema = z13.object({
1471
1572
  /** Oldest first, so the newest line is last — the one that replaces in place. */
1472
- lines: z12.array(z12.string().max(ACTIVITY_LINE_MAX)).max(ACTIVITY_LINES),
1573
+ lines: z13.array(z13.string().max(ACTIVITY_LINE_MAX)).max(ACTIVITY_LINES),
1473
1574
  /** When the harness observed this tail. Its own timestamp, not the heartbeat's: a beat
1474
1575
  * that carries an UNCHANGED tail must not make a stalled agent look like it just moved. */
1475
- at: z12.string().datetime()
1576
+ at: z13.string().datetime()
1476
1577
  });
1477
- var ConnectionSummarySchema = z12.object({
1578
+ var ConnectionSummarySchema = z13.object({
1478
1579
  /** The connection = the agent's token id (used to address a request). */
1479
- id: z12.string(),
1580
+ id: z13.string(),
1480
1581
  /** The credential kind: "device" = a paired machine (mint-only — it hosts and mints, it
1481
1582
  * never talks); "agent" = an identity that sends. The roster and devices surfaces split
1482
1583
  * on this. Optional/absent reads as "agent" (a row predating the kind column). See
1483
1584
  * docs/server/tokens/devices-vs-agents-design.md. */
1484
- kind: z12.enum(["device", "agent"]).optional(),
1585
+ kind: z13.enum(["device", "agent"]).optional(),
1485
1586
  /** For an agent, the token id of the DEVICE that minted it — so agents group under their
1486
1587
  * machine, and revoking a device cascades to them. Null on devices, and on unlinked
1487
- * agents (phone-launched, provider-managed, or minted before the link existed). */
1488
- mintedByDevice: z12.string().nullable().optional(),
1489
- device: z12.string().nullable(),
1588
+ * agents (provider-managed, hatched by another agent, or minted before the link existed);
1589
+ * a phone-launched agent is linked to the computer it runs on since #1602. */
1590
+ mintedByDevice: z13.string().nullable().optional(),
1591
+ device: z13.string().nullable(),
1490
1592
  /** The agent's display name (the single pairing name). */
1491
- name: z12.string(),
1593
+ name: z13.string(),
1492
1594
  /** For a managed connection, the provider key (e.g. "cma") that agentOrigin maps to a
1493
1595
  * label; null for a local connection. Sourced from the token's provider, not the name. */
1494
- provider: z12.string().nullable(),
1596
+ provider: z13.string().nullable(),
1495
1597
  /** The pairing's assigned voice (#462); null = the default voice. */
1496
1598
  voice: VoiceKeySchema.nullable(),
1497
1599
  /** The LOUDEST this agent may ever reach you — a ceiling on `NOTIFY_LADDER`, set by the
1498
1600
  * user on the agent's own page. null = no ceiling (today's behaviour for every
1499
1601
  * connection). Android binds importance to a relationship rather than to each message,
1500
1602
  * and that is the thing our roster could not say: "Marlow may always call me; Otto
1501
- * never may" (docs/clients/app/party/navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
1603
+ * never may" (docs/history/party-navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
1502
1604
  * every surface at once and outranks even `sessionMode: all_calls` — a mode the user
1503
1605
  * set once must not overrule a rule they set about one agent. */
1504
1606
  reach: NotifyLevelSchema.nullable().optional(),
1505
- createdAt: z12.string().datetime(),
1607
+ createdAt: z13.string().datetime(),
1506
1608
  /** Most recent notification on this connection, either direction. Null = no contact yet.
1507
1609
  * Drives the agents-page recency grouping (Today / This week / …). */
1508
- lastContactAt: z12.string().datetime().nullable(),
1610
+ lastContactAt: z13.string().datetime().nullable(),
1509
1611
  /** Last presence heartbeat from a running agent process (POST /api/presence) — the
1510
1612
  * desktop app while open. Null = never seen; stale = offline. */
1511
- lastSeenAt: z12.string().datetime().nullable().optional(),
1613
+ lastSeenAt: z13.string().datetime().nullable().optional(),
1512
1614
  /** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
1513
1615
  * its Goals — wrote on one or recorded an operation (`tokens.last_worked_at`). Within
1514
1616
  * `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
1515
- lastWorkedAt: z12.string().datetime().nullable().optional(),
1617
+ lastWorkedAt: z13.string().datetime().nullable().optional(),
1516
1618
  /** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
1517
1619
  * With no work of its own for `WORKING_MS`, an agent sitting on this is not taking its work. */
1518
- oldestReadyAt: z12.string().datetime().nullable().optional(),
1620
+ oldestReadyAt: z13.string().datetime().nullable().optional(),
1519
1621
  /** What a live desktop can run (docs/clients/desktop/companion.md §2.2), advertised on its heartbeat:
1520
1622
  * harness availabilities + granted workspaces — the option set the phone's
1521
1623
  * "new session" sheet offers. Absent for ordinary MCP agents. */
1522
- runtime: z12.object({
1624
+ runtime: z13.object({
1523
1625
  /** The @paigy/harness this host is running — a machine the self-update has not reached
1524
1626
  * shows its age here (`apps/desktop/src/update.ts`). */
1525
- version: z12.string().optional(),
1526
- harnesses: z12.array(z12.object({ name: z12.string(), label: z12.string(), status: z12.string(), models: z12.array(z12.string()).optional() })).optional(),
1527
- workspaces: z12.array(z12.string()).optional(),
1627
+ version: z13.string().optional(),
1628
+ harnesses: z13.array(z13.object({ name: z13.string(), label: z13.string(), status: z13.string(), models: z13.array(z13.string()).optional() })).optional(),
1629
+ workspaces: z13.array(z13.string()).optional(),
1528
1630
  /** THE GIT REPOS IN THOSE FOLDERS (2026-10-01, Goal 26982211): each granted folder that is a
1529
1631
  * repo, and each repo directly inside one, with its `origin` remote. A session started for
1530
1632
  * work on `mauurda/paigy` opens in that repo rather than the folder above it, where the repo's
1531
1633
  * own AGENTS.md is never read (`workspaceForRepo`). Absent on hosts that predate it. */
1532
- repos: z12.array(z12.object({ path: z12.string(), remote: z12.string() })).optional()
1634
+ repos: z13.array(z13.object({ path: z13.string(), remote: z13.string() })).optional()
1533
1635
  }).optional(),
1534
1636
  /** The tail of this agent's working log, when a harness is driving it — the agent page's
1535
1637
  * live strip. Absent for anything the desktop harness isn't running (a hatched identity
@@ -1538,145 +1640,151 @@ var ConnectionSummarySchema = z12.object({
1538
1640
  activity: AgentActivitySchema.optional(),
1539
1641
  /** True = a provider-managed agent running in the provider's cloud (e.g. Anthropic CMA);
1540
1642
  * false = a local MCP connection running on the user's computer (Claude Code/Codex/…). */
1541
- managed: z12.boolean()
1643
+ managed: z13.boolean()
1542
1644
  });
1543
- var LedgerItemSchema = z12.object({ id: z12.string(), parentId: z12.string(), title: z12.string(), createdAt: z12.string() });
1544
- var AgentLedgerSchema = z12.object({
1645
+ var LedgerItemSchema = z13.object({ id: z13.string(), parentId: z13.string(), title: z13.string(), createdAt: z13.string() });
1646
+ var AgentLedgerSchema = z13.object({
1545
1647
  /** Null when the agent has not named itself yet — never a placeholder (owner, 2026-10-01). */
1546
- agent: z12.object({ id: z12.string(), name: z12.string().nullable(), revokedAt: z12.string().nullable() }),
1648
+ agent: z13.object({ id: z13.string(), name: z13.string().nullable(), revokedAt: z13.string().nullable() }),
1547
1649
  /** Its own questions you have not answered. */
1548
- asks: z12.array(LedgerItemSchema),
1650
+ asks: z13.array(LedgerItemSchema),
1549
1651
  /** Its questions you answered that nobody acted on — still owed to somebody. */
1550
- answered: z12.array(LedgerItemSchema),
1652
+ answered: z13.array(LedgerItemSchema),
1551
1653
  /** Requests you sent it that it never took. */
1552
- requests: z12.array(LedgerItemSchema),
1553
- goals: z12.array(z12.object({ id: z12.string(), outcome: z12.string(), state: z12.string() })),
1554
- callbacks: z12.array(z12.object({ id: z12.string(), parentId: z12.string(), trigger: z12.string(), note: z12.string(), dueAt: z12.string().nullable() }))
1654
+ requests: z13.array(LedgerItemSchema),
1655
+ goals: z13.array(z13.object({ id: z13.string(), outcome: z13.string(), state: z13.string() })),
1656
+ callbacks: z13.array(z13.object({ id: z13.string(), parentId: z13.string(), trigger: z13.string(), note: z13.string(), dueAt: z13.string().nullable() }))
1555
1657
  });
1556
- var ReassignResultSchema = z12.object({
1557
- moved: z12.object({ asks: z12.number(), answered: z12.number(), requests: z12.number(), goals: z12.number(), callbacks: z12.number() }),
1558
- parentId: z12.string().nullable()
1658
+ var ReassignResultSchema = z13.object({
1659
+ moved: z13.object({ asks: z13.number(), answered: z13.number(), requests: z13.number(), goals: z13.number(), callbacks: z13.number() }),
1660
+ parentId: z13.string().nullable()
1559
1661
  });
1560
- var LessonStateSchema = z12.enum(["active", "proposed", "retired"]);
1561
- var LessonViewSchema = z12.object({
1562
- id: z12.string(),
1563
- text: z12.string(),
1662
+ var LessonStateSchema = z13.enum(["active", "proposed", "retired"]);
1663
+ var LessonViewSchema = z13.object({
1664
+ id: z13.string(),
1665
+ text: z13.string(),
1564
1666
  state: LessonStateSchema,
1565
1667
  /** The Goal it is scoped to; null = the whole account. */
1566
- scopeGoalId: z12.string().nullable(),
1567
- goalTitle: z12.string().nullable(),
1568
- version: z12.number(),
1569
- pinned: z12.boolean(),
1668
+ scopeGoalId: z13.string().nullable(),
1669
+ goalTitle: z13.string().nullable(),
1670
+ version: z13.number(),
1671
+ pinned: z13.boolean(),
1570
1672
  /** When the person last wrote its text themselves. */
1571
- editedAt: z12.string().nullable(),
1572
- createdAt: z12.string(),
1573
- updatedAt: z12.string(),
1673
+ editedAt: z13.string().nullable(),
1674
+ createdAt: z13.string(),
1675
+ updatedAt: z13.string(),
1574
1676
  /** The Entries it came from, oldest first; `words` is null when an Entry has none to show (sealed). */
1575
- sources: z12.array(z12.object({ entryId: z12.string(), words: z12.string().nullable(), at: z12.string() }))
1677
+ sources: z13.array(z13.object({ entryId: z13.string(), words: z13.string().nullable(), at: z13.string() }))
1576
1678
  });
1577
- var QueueQuestionSchema = z12.object({
1679
+ var QueueQuestionSchema = z13.object({
1578
1680
  /** The decision need's id — what an answer is accepted against. */
1579
- id: z12.string(),
1681
+ id: z13.string(),
1580
1682
  /** The words that were asked, from the request Entry that asked them. */
1581
- question: z12.string(),
1683
+ question: z13.string(),
1582
1684
  /** Where it was asked — which is where the ruling goes (`POST /api/entries`). Null only
1583
1685
  * for a need whose request Entry is carried by no interactive Delivery, which nothing
1584
1686
  * can answer. */
1585
- deliveryId: z12.string().nullable().default(null),
1687
+ deliveryId: z13.string().nullable().default(null),
1586
1688
  /** The Entry the ruling is about. */
1587
- aboutId: z12.string().nullable().default(null),
1689
+ aboutId: z13.string().nullable().default(null),
1588
1690
  /** Empty for a free-text question. */
1589
- options: z12.array(OptionSchema).default([]),
1590
- select: z12.enum(["one", "many", "rank", "confirm", "text"]).default("text"),
1591
- askedAt: z12.string(),
1691
+ options: z13.array(OptionSchema).default([]),
1692
+ select: z13.enum(["one", "many", "rank", "confirm", "text"]).default("text"),
1693
+ askedAt: z13.string(),
1592
1694
  /** Null while the question is open — which is how the page tells the two apart. */
1593
- answeredAt: z12.string().nullable().default(null),
1695
+ answeredAt: z13.string().nullable().default(null),
1594
1696
  /** The ruling in the person's own words, from the contribution that replied — not the
1595
1697
  * option id, which is not something anyone reads back. Null while it is open, and null
1596
1698
  * for a settled question whose reply carried nothing readable. */
1597
- answer: z12.string().nullable().default(null),
1699
+ answer: z13.string().nullable().default(null),
1598
1700
  /** The Goal this question belongs to — a step knows its Goal on its own, not only through
1599
1701
  * an `InboxItem`'s `communication.goalIds[0]` (docs/clients/app/walk/design.md §12 item 3).
1600
1702
  * READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
1601
1703
  * order, its route, home's trees and the list of steps all take a step's Goal from here, so
1602
1704
  * this is the field they agree through rather than each re-deriving it from the row it
1603
1705
  * arrived under. Required because the API projects it on every need it sends. */
1604
- goalId: z12.string(),
1706
+ goalId: z13.string(),
1605
1707
  /** True only while an unmet START gate holds the Goal — a Goal that merely waits to
1606
1708
  * *finish* does not stop a person from answering (owner, 2026-09-16: "per need gate from
1607
1709
  * the API"; §4's dashed node). Not the same fact as `QueueItem.blocked`, which counts any
1608
1710
  * gate at all. */
1609
- blocked: z12.boolean().default(false)
1711
+ blocked: z13.boolean().default(false)
1610
1712
  });
1611
- var QueueReplySchema = z12.object({
1713
+ var QueueReplySchema = z13.object({
1612
1714
  /** The card this note was (`deliveryId:requestEntryId`, minted by the server like every card
1613
1715
  * id) — so the phone can tell a reply it just sent from one the queue already carries, and the
1614
1716
  * walk can name it in its zoom. */
1615
- id: z12.string(),
1717
+ id: z13.string(),
1616
1718
  /** The Goal the note is on. */
1617
- goalId: z12.string(),
1719
+ goalId: z13.string(),
1618
1720
  /** What the note said. */
1619
- note: z12.string(),
1721
+ note: z13.string(),
1620
1722
  /** Where it was carried — where a second reply goes (`POST /api/entries`, #2252). */
1621
- deliveryId: z12.string(),
1622
- requestEntryId: z12.string(),
1623
- askedAt: z12.string(),
1723
+ deliveryId: z13.string(),
1724
+ requestEntryId: z13.string(),
1725
+ askedAt: z13.string(),
1624
1726
  /** When the person last replied — the window's start. */
1625
- repliedAt: z12.string(),
1727
+ repliedAt: z13.string(),
1626
1728
  /** The person's latest words about it; null when there is nothing readable in them. */
1627
- reply: z12.string().nullable()
1729
+ reply: z13.string().nullable()
1628
1730
  });
1629
- var QueueItemSchema = z12.object({
1630
- id: z12.string(),
1731
+ var QueueItemSchema = z13.object({
1732
+ id: z13.string(),
1631
1733
  /** One-line headline — the first sentence of the outcome. */
1632
- title: z12.string(),
1734
+ title: z13.string(),
1633
1735
  /** The outcome in full, verbatim: the person's own words are what an assignee sees. */
1634
- intent: z12.string(),
1736
+ intent: z13.string(),
1635
1737
  /** `ready` | `active` | `waiting` | `done` | `cancelled`, straight off the Goal. */
1636
- state: z12.string(),
1738
+ state: z13.string(),
1637
1739
  /** Who holds it (a participant ref); null when nobody does yet. */
1638
- assignee: z12.string().nullable().default(null),
1740
+ assignee: z13.string().nullable().default(null),
1639
1741
  /** What the agent last said it was doing; null if it has said nothing. */
1640
- progress: z12.string().nullable().default(null),
1742
+ progress: z13.string().nullable().default(null),
1641
1743
  /** HOME'S LINE FOR THAT NOTE (owner, 2026-09-23): a few plain words one read wrote from `progress`,
1642
1744
  * served only while it was written for the current note. Null means show the Goal's name. */
1643
- progressLine: z12.string().nullable().optional(),
1644
- reviewPending: z12.boolean().default(false),
1645
- dueAt: z12.string().nullable().default(null),
1745
+ progressLine: z13.string().nullable().optional(),
1746
+ reviewPending: z13.boolean().default(false),
1747
+ dueAt: z13.string().nullable().default(null),
1646
1748
  /** WHEN ITS OWNER SAID DONE WHILE CHILDREN WERE OPEN (#2704): its own work is finished and it closes
1647
1749
  * with its last open child. Null otherwise; optional, so hand-built queues need not spell it. */
1648
- finishedAt: z12.string().nullable().optional(),
1750
+ finishedAt: z13.string().nullable().optional(),
1649
1751
  /** The Goal this one was opened under; null at the root. */
1650
- parentGoalId: z12.string().nullable().default(null),
1752
+ parentGoalId: z13.string().nullable().default(null),
1651
1753
  /** Goals opened under this one — only those the same list holds. */
1652
- childGoalIds: z12.array(z12.string()).default([]),
1754
+ childGoalIds: z13.array(z13.string()).default([]),
1653
1755
  /** Goals this one waits on (start or finish gates). */
1654
- dependencyGoalIds: z12.array(z12.string()).default([]),
1756
+ dependencyGoalIds: z13.array(z13.string()).default([]),
1655
1757
  /** True while any gate is on a Goal that is not done — the walk draws it dashed. */
1656
- blocked: z12.boolean().default(false),
1758
+ blocked: z13.boolean().default(false),
1657
1759
  /** Its questions: every OPEN one, and at most ten settled, newest settled first
1658
1760
  * (20260929133308) — the page decides which of them to show. NOT the whole set: `asked` and
1659
1761
  * `answered` are, and a settled one's words are a line (280 characters), its body read when the
1660
- * question is opened. */
1661
- questions: z12.array(QueueQuestionSchema).default([]),
1762
+ * question is opened. Never a CANCELLED one (withdrawn, dismissed, or its Goal or call ended): it
1763
+ * asks nothing of anyone (#3723). */
1764
+ questions: z13.array(QueueQuestionSchema).default([]),
1662
1765
  /** HOW MANY QUESTIONS THIS WORK HAS ASKED, and how many are answered — the Goal's own totals,
1663
1766
  * bounded at 100 server-side. A tally counted off `questions` is a wrong number that looks
1664
1767
  * right once the cap bites (`walk/trees.ts` `tallyOf`). Optional, and defaulted from the array
1665
- * by the projection, so hand-built queues (fixtures, the demo) need not spell them. */
1666
- asked: z12.number().optional(),
1667
- answered: z12.number().optional(),
1768
+ * by the projection, so hand-built queues (fixtures, the demo) need not spell them. A cancelled
1769
+ * question is not asked (#3725). */
1770
+ asked: z13.number().optional(),
1771
+ answered: z13.number().optional(),
1668
1772
  /** Every note on it the person replied to (`QueueReplySchema`) — the page decides which to show.
1669
1773
  * Optional, not defaulted: absent is none, and every hand-built queue (fixtures, the demo) need
1670
1774
  * not spell an empty list. */
1671
- replies: z12.array(QueueReplySchema).optional(),
1775
+ replies: z13.array(QueueReplySchema).optional(),
1672
1776
  /** The repository or project identifier this Goal belongs to (#2280), null if untracked. */
1673
- repo: z12.string().nullable().optional(),
1674
- createdAt: z12.string(),
1675
- updatedAt: z12.string().nullable().default(null),
1777
+ repo: z13.string().nullable().optional(),
1778
+ createdAt: z13.string(),
1779
+ updatedAt: z13.string().nullable().default(null),
1676
1780
  /** When its owner last SAID something about it (the newest `progress` Entry: a contact update kept
1677
1781
  * as progress). `updatedAt` moves for reasons nobody chose — a
1678
1782
  * state recomputed, a review flag — so it cannot tell work in hand from work gone quiet. */
1679
- lastProgressAt: z12.string().nullable().optional(),
1783
+ lastProgressAt: z13.string().nullable().optional(),
1784
+ /** WHEN AN AGENT LAST ACTED ON IT (owner, 2026-10-27: "Move it now", Goal 567258dc): its newest agent write, an
1785
+ * Entry or a Goal change, Paigy's excluded (`goals.worked_at`). What keeps work an agent is writing on from
1786
+ * reading as gone quiet (`coldSince`); absent on a door that does not carry it. */
1787
+ workedAt: z13.string().nullable().optional(),
1680
1788
  /** THE GOAL'S NEWEST WORD, FROM EITHER SIDE (owner, 2026-09-27): the newest Entry on it, of any
1681
1789
  * kind — what the person added ("Add to this"), their reply, the agent's ask or its progress
1682
1790
  * note. A progress note is an Entry, so this is already the newer of the two: the person's note
@@ -1695,7 +1803,7 @@ var QueueItemSchema = z12.object({
1695
1803
  * minute after the person speaks erases their instant from it, and the durable traces the client
1696
1804
  * can see (`replies`, `questions[].answeredAt`) miss a spontaneous note entirely — a `request`
1697
1805
  * Entry with no `about_id` is in neither. */
1698
- lastPersonAt: z12.string().nullable().optional(),
1806
+ lastPersonAt: z13.string().nullable().optional(),
1699
1807
  /** EACH SIDE'S NEWEST WORD IN THE LAST DAY (owner, 2026-10-13, the Work tab's order): the person's
1700
1808
  * (`yours`) and the agents' (`theirs`), each null when that side wrote nothing on it in 24 hours.
1701
1809
  * `latest` holds one Entry, so whichever side wrote first is hidden there; Work orders a Goal by
@@ -1713,40 +1821,40 @@ var QueueItemSchema = z12.object({
1713
1821
  * the row shows that no `updated_at` moves for (`active` lapsing, a dependency's state, a
1714
1822
  * sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
1715
1823
  * queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
1716
- rev: z12.string().optional()
1824
+ rev: z13.string().optional()
1717
1825
  });
1718
- var QueueDeltaSchema = z12.object({
1719
- ids: z12.array(z12.string()),
1720
- items: z12.array(QueueItemSchema)
1826
+ var QueueDeltaSchema = z13.object({
1827
+ ids: z13.array(z13.string()),
1828
+ items: z13.array(QueueItemSchema)
1721
1829
  });
1722
1830
  var COLD_AFTER_MS = 3 * 24 * 60 * 60 * 1e3;
1723
- var NoteSourceSchema = z12.enum(["app", "call"]);
1724
- var NoteStatusSchema = z12.enum(["open", "assigned", "in_progress", "done"]);
1725
- var NoteRepeatSchema = z12.enum(["once", "until_done"]);
1726
- var DecisionSchema = z12.object({
1727
- id: z12.string(),
1831
+ var NoteSourceSchema = z13.enum(["app", "call"]);
1832
+ var NoteStatusSchema = z13.enum(["open", "assigned", "in_progress", "done"]);
1833
+ var NoteRepeatSchema = z13.enum(["once", "until_done"]);
1834
+ var DecisionSchema = z13.object({
1835
+ id: z13.string(),
1728
1836
  /** The note this decision refines; null = recorded on a bare thread (the
1729
1837
  * extensibility seam — any conversation can accrue decisions). */
1730
- noteId: z12.string().nullable(),
1838
+ noteId: z13.string().nullable(),
1731
1839
  /** What was ambiguous — the broker's (or the user's own) question. */
1732
- question: z12.string(),
1840
+ question: z13.string(),
1733
1841
  /** The user's ruling; null while the question is open. */
1734
- answer: z12.string().nullable(),
1735
- decidedAt: z12.string().nullable(),
1736
- createdAt: z12.string()
1842
+ answer: z13.string().nullable(),
1843
+ decidedAt: z13.string().nullable(),
1844
+ createdAt: z13.string()
1737
1845
  });
1738
- var NoteSchema = z12.object({
1739
- id: z12.string(),
1846
+ var NoteSchema = z13.object({
1847
+ id: z13.string(),
1740
1848
  /** One-line headline (broker-titled; deterministic floor). */
1741
- title: z12.string(),
1849
+ title: z13.string(),
1742
1850
  /** The original intent, verbatim — assignees always see the user's own words. */
1743
- intent: z12.string(),
1851
+ intent: z13.string(),
1744
1852
  source: NoteSourceSchema,
1745
1853
  status: NoteStatusSchema,
1746
1854
  /** Who it was assigned to (a participant ref, 'agent:<tokenId>'); null = unassigned. */
1747
- assignee: z12.string().nullable(),
1855
+ assignee: z13.string().nullable(),
1748
1856
  /** The request thread minted at assignment; null until assigned. */
1749
- parentId: z12.string().nullable(),
1857
+ parentId: z13.string().nullable(),
1750
1858
  /** REMINDERS (docs/model/notes/reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
1751
1859
  * call — never a deadline. It only ever comes from the user's own words, so when it
1752
1860
  * passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
@@ -1755,100 +1863,100 @@ var NoteSchema = z12.object({
1755
1863
  // Defaulted, not required: a Note from an API deploy older than the reminders
1756
1864
  // migration has none of these, and the defaults ARE what it means — no not-before,
1757
1865
  // one ride, never ridden. Parsing must not fail across a rolling deploy.
1758
- dueAt: z12.string().nullable().default(null),
1866
+ dueAt: z13.string().nullable().default(null),
1759
1867
  repeat: NoteRepeatSchema.default("once"),
1760
1868
  /** How many calls have already carried it — the fatigue cap counts rides, not days. */
1761
- rides: z12.number().int().default(0),
1762
- lastRideAt: z12.string().nullable().default(null),
1763
- createdAt: z12.string()
1869
+ rides: z13.number().int().default(0),
1870
+ lastRideAt: z13.string().nullable().default(null),
1871
+ createdAt: z13.string()
1764
1872
  });
1765
- var DeliveryModeSchema = z12.enum(["poll", "self_hosted"]);
1766
- var RegisterDeliverySchema = z12.object({ mode: DeliveryModeSchema });
1767
- var OAuthStartSchema = z12.object({
1768
- provider: z12.enum(["cma"]),
1769
- returnTo: z12.string().min(1)
1873
+ var DeliveryModeSchema = z13.enum(["poll", "self_hosted"]);
1874
+ var RegisterDeliverySchema = z13.object({ mode: DeliveryModeSchema });
1875
+ var OAuthStartSchema = z13.object({
1876
+ provider: z13.enum(["cma"]),
1877
+ returnTo: z13.string().min(1)
1770
1878
  });
1771
- var DeliveryConfigSchema = z12.object({
1772
- tokenId: z12.string(),
1879
+ var DeliveryConfigSchema = z13.object({
1880
+ tokenId: z13.string(),
1773
1881
  mode: DeliveryModeSchema,
1774
1882
  /** null when the deployment has no anon key configured. `self_hosted` is then REFUSED
1775
1883
  * (503 `self_hosted_unavailable`) rather than registered, so a self_hosted config always
1776
1884
  * carries credentials; only a `poll` registration can come back with null here. */
1777
- realtime: z12.object({ url: z12.string(), anonKey: z12.string() }).nullable()
1885
+ realtime: z13.object({ url: z13.string(), anonKey: z13.string() }).nullable()
1778
1886
  });
1779
- var HostDecisionSchema = z12.object({
1887
+ var HostDecisionSchema = z13.object({
1780
1888
  /** The agent's token id: the row's `recipient`. */
1781
- agent: z12.string().uuid(),
1782
- decision: z12.enum(["stood_back", "took_over"]),
1889
+ agent: z13.string().uuid(),
1890
+ decision: z13.enum(["stood_back", "took_over"]),
1783
1891
  /** The work it was about: the Goal waiting on that agent next (`claimable` on its `contact({})` read). */
1784
- goalId: z12.string().uuid().nullable().optional(),
1892
+ goalId: z13.string().uuid().nullable().optional(),
1785
1893
  /** When the server last heard from the agent, as the host read it: the presence it stood back for. */
1786
- seenAt: z12.string().datetime().nullable().optional(),
1894
+ seenAt: z13.string().datetime().nullable().optional(),
1787
1895
  /** When that work last moved (`claimable.since` on an agent's `contact({})` read), the fact the bound is judged on. */
1788
- since: z12.string().datetime().nullable().optional(),
1896
+ since: z13.string().datetime().nullable().optional(),
1789
1897
  /** What the host said, in its log's own words: why it stood back, or what the take-over did. */
1790
- said: z12.string().max(300).optional()
1898
+ said: z13.string().max(300).optional()
1791
1899
  });
1792
- var WakeNudgeSchema = z12.object({
1793
- kind: z12.enum(["reply", "request", "callback"]),
1794
- notificationId: z12.string().optional(),
1795
- parentId: z12.string()
1900
+ var WakeNudgeSchema = z13.object({
1901
+ kind: z13.enum(["reply", "request", "callback"]),
1902
+ notificationId: z13.string().optional(),
1903
+ parentId: z13.string()
1796
1904
  });
1797
- var PairingStatusSchema = z12.enum(["pending", "approved", "denied", "expired"]);
1798
- var DeviceCodeSchema = z12.object({
1799
- device_code: z12.string(),
1800
- user_code: z12.string(),
1801
- verification_uri: z12.string().url(),
1802
- verification_uri_complete: z12.string().url(),
1803
- interval: z12.number(),
1804
- expires_in: z12.number()
1905
+ var PairingStatusSchema = z13.enum(["pending", "approved", "denied", "expired"]);
1906
+ var DeviceCodeSchema = z13.object({
1907
+ device_code: z13.string(),
1908
+ user_code: z13.string(),
1909
+ verification_uri: z13.string().url(),
1910
+ verification_uri_complete: z13.string().url(),
1911
+ interval: z13.number(),
1912
+ expires_in: z13.number()
1805
1913
  });
1806
- var DeviceInfoSchema = z12.object({
1807
- code: z12.string(),
1914
+ var DeviceInfoSchema = z13.object({
1915
+ code: z13.string(),
1808
1916
  /** The agent's suggested name (from /device/code) — shown on the approval screen,
1809
1917
  * pre-filling the name field the human can edit. */
1810
- name: z12.string(),
1918
+ name: z13.string(),
1811
1919
  /** @deprecated Legacy alias of `name` for the pre-#531 embedded bundle in App Store
1812
1920
  * build 35, whose DeviceFlow renders `info.agent.slice(0, 2)` — without this a FRESH
1813
1921
  * install crashes on the pairing screen on first launch, before the OTA lands
1814
1922
  * (seen live: PAIGY-5T, 2026-07-21). Remove once a newer binary is the floor. */
1815
- agent: z12.string().optional(),
1816
- device: z12.string().nullable(),
1923
+ agent: z13.string().optional(),
1924
+ device: z13.string().nullable(),
1817
1925
  status: PairingStatusSchema
1818
1926
  });
1819
- var DeviceTokenSchema = z12.object({
1820
- access_token: z12.string(),
1927
+ var DeviceTokenSchema = z13.object({
1928
+ access_token: z13.string(),
1821
1929
  /** The pairing's single name (user-typed at approval, the agent's suggestion, or
1822
1930
  * a default silly name). */
1823
- name: z12.string(),
1824
- device: z12.string().nullable(),
1931
+ name: z13.string(),
1932
+ device: z13.string().nullable(),
1825
1933
  /** The pairing's assigned voice, cached so the desktop can seed the SAME face the phone
1826
1934
  * draws — voice is the third ingredient of a hatchling's build (party/traits.ts). */
1827
- voice: z12.string().nullable().optional(),
1935
+ voice: z13.string().nullable().optional(),
1828
1936
  /** The token's server-side id — the face's COLOUR anchor, and the only seed ingredient
1829
1937
  * that survives a rename. Cached by the host's identity beat. */
1830
- token_id: z12.string().nullable().optional(),
1938
+ token_id: z13.string().nullable().optional(),
1831
1939
  /** WHERE this identity works — the folder a wake should land it in. Written by the host
1832
1940
  * at spawn and by `paigy-harness handoff` from a live terminal. Without it every wake
1833
1941
  * landed in the FIRST granted workspace and the agent rediscovered its own repo from
1834
1942
  * the thread each time (host.ts, live catch 2026-08-06 — prompt-papered until now). */
1835
- workspace: z12.string().nullable().optional(),
1943
+ workspace: z13.string().nullable().optional(),
1836
1944
  /** Local host recovery must preserve the launch's runtime and Paigy identity. */
1837
- harness: z12.enum(["claude", "codex", "agy"]).optional(),
1838
- session_id: z12.string().uuid().optional(),
1945
+ harness: z13.enum(["claude", "codex", "agy"]).optional(),
1946
+ session_id: z13.string().uuid().optional(),
1839
1947
  /** A conversation the host must not resume: its context is full, so every turn fails
1840
1948
  * ("Prompt is too long"). Written when a run hits it (`run.ts` `onFull`); the host skips a slot
1841
1949
  * whose resumable session is this one, so its Goals reach the dead-agent handoff instead of a
1842
1950
  * copy that types the person's words into a turn that cannot run (Calls, 2026-10-06). */
1843
- full_session: z12.string().optional(),
1844
- uik_pub: z12.string().nullable().optional()
1951
+ full_session: z13.string().optional(),
1952
+ uik_pub: z13.string().nullable().optional()
1845
1953
  });
1846
- var SupportRequestSchema = z12.object({
1847
- email: z12.string().email().max(320),
1848
- message: z12.string().trim().min(1).max(5e3),
1849
- name: z12.string().trim().max(120).optional()
1954
+ var SupportRequestSchema = z13.object({
1955
+ email: z13.string().email().max(320),
1956
+ message: z13.string().trim().min(1).max(5e3),
1957
+ name: z13.string().trim().max(120).optional()
1850
1958
  });
1851
- var NotificationFeedbackKindSchema = z12.enum([
1959
+ var NotificationFeedbackKindSchema = z13.enum([
1852
1960
  "break_down",
1853
1961
  // "This should be more than one ask — break it down."
1854
1962
  "regenerate_options",
@@ -1863,131 +1971,133 @@ var NotificationFeedbackKindSchema = z12.enum([
1863
1971
  // anything else — the note carries it.
1864
1972
  ]);
1865
1973
  var SlimOptionSchema = OptionSchema.omit({ html: true });
1866
- var QuestionRowSchema = z12.object({
1974
+ var QuestionRowSchema = z13.object({
1867
1975
  /** The card's id (`deliveryId:needId`, or `deliveryId:entryId` for an update), as the inbox mints it. */
1868
- id: z12.string(),
1869
- deliveryId: z12.string(),
1870
- entryId: z12.string(),
1976
+ id: z13.string(),
1977
+ deliveryId: z13.string(),
1978
+ entryId: z13.string(),
1871
1979
  /** The decision it waits on; null for an update, which asks nothing. */
1872
- needId: z12.string().nullable(),
1873
- goalIds: z12.array(z12.string()),
1980
+ needId: z13.string().nullable(),
1981
+ goalIds: z13.array(z13.string()),
1874
1982
  /** The name of the work it is about, when the read could word it. */
1875
- goalTitle: z12.string().optional(),
1876
- tokenId: z12.string().optional(),
1877
- name: z12.string(),
1878
- title: z12.string(),
1879
- body: z12.string(),
1880
- select: z12.enum(["one", "many", "rank", "confirm", "text"]),
1881
- options: z12.array(SlimOptionSchema),
1882
- hasPreview: z12.boolean(),
1883
- blocking: z12.boolean(),
1884
- askedAt: z12.string().datetime(),
1983
+ goalTitle: z13.string().optional(),
1984
+ tokenId: z13.string().optional(),
1985
+ name: z13.string(),
1986
+ title: z13.string(),
1987
+ body: z13.string(),
1988
+ select: z13.enum(["one", "many", "rank", "confirm", "text"]),
1989
+ options: z13.array(SlimOptionSchema),
1990
+ hasPreview: z13.boolean(),
1991
+ blocking: z13.boolean(),
1992
+ askedAt: z13.string().datetime(),
1885
1993
  ring: InboxItemSchema.shape.ring,
1886
- onCall: z12.literal(true).optional(),
1887
- sealed: z12.boolean()
1994
+ onCall: z13.literal(true).optional(),
1995
+ sealed: z13.boolean()
1888
1996
  });
1889
- var WorkStateSchema = z12.enum(["ready", "active", "waiting", "done", "cancelled"]);
1890
- var WorkRowSchema = z12.object({
1891
- id: z12.string(),
1892
- parentId: z12.string().nullable(),
1893
- title: z12.string(),
1997
+ var WorkStateSchema = z13.enum(["ready", "active", "waiting", "done", "cancelled"]);
1998
+ var WorkRowSchema = z13.object({
1999
+ id: z13.string(),
2000
+ parentId: z13.string().nullable(),
2001
+ title: z13.string(),
1894
2002
  /** Straight off the Goal. */
1895
2003
  state: WorkStateSchema,
1896
- owner: z12.string().nullable(),
1897
- revision: z12.number().int(),
2004
+ owner: z13.string().nullable(),
2005
+ revision: z13.number().int(),
1898
2006
  /** Open questions on it, counted to 100. */
1899
- waiting: z12.number().int(),
2007
+ waiting: z13.number().int(),
1900
2008
  /** Held by a gate on work that is not done. */
1901
- blocked: z12.boolean(),
1902
- lastProgressAt: z12.string().datetime().nullable(),
2009
+ blocked: z13.boolean(),
2010
+ lastProgressAt: z13.string().datetime().nullable(),
1903
2011
  /** The line written for its newest progress note, else that note's first words. */
1904
- line: z12.string().nullable(),
2012
+ line: z13.string().nullable(),
1905
2013
  /** Work directly under it, counted to 100; the list carries up to 12 of them. */
1906
- children: z12.number().int(),
1907
- createdAt: z12.string().datetime(),
1908
- updatedAt: z12.string().datetime(),
2014
+ children: z13.number().int(),
2015
+ createdAt: z13.string().datetime(),
2016
+ updatedAt: z13.string().datetime(),
1909
2017
  /** When anything at or under it last moved — the order the list is in. */
1910
- activeAt: z12.string().datetime(),
2018
+ activeAt: z13.string().datetime(),
1911
2019
  /** A sealed outcome has no title here; the work's page opens it. */
1912
- sealed: z12.boolean()
2020
+ sealed: z13.boolean()
1913
2021
  });
1914
2022
  var ComputerRowSchema = ConnectionSummarySchema.omit({ activity: true });
1915
2023
  var AgentRowSchema = ComputerRowSchema.extend({
1916
2024
  /** Open questions it is asking the person, over every open card; null when that read failed. */
1917
- asking: z12.number().int().nullable(),
1918
- oldestAskAt: z12.string().datetime().nullable(),
2025
+ asking: z13.number().int().nullable(),
2026
+ oldestAskAt: z13.string().datetime().nullable(),
1919
2027
  /** Up to three of the live Goals it holds, oldest first (the order it picks them up), and how
1920
2028
  * many in all among the account's 200 most recently active agent-held live Goals
1921
2029
  * (`agent_holds`); null when that read failed. */
1922
- holds: z12.array(z12.object({ id: z12.string(), title: z12.string() })).nullable(),
1923
- held: z12.number().int().nullable(),
1924
- /** The earliest instant any Goal it holds went quiet, by the one rule (`coldSince`); null
1925
- * while none has, or when that read failed. */
1926
- cold: z12.string().datetime().nullable(),
2030
+ holds: z13.array(z13.object({ id: z13.string(), title: z13.string() })).nullable(),
2031
+ held: z13.number().int().nullable(),
1927
2032
  /** The newest line of its working log, and when the harness saw it. */
1928
- line: z12.string().nullable(),
1929
- lineAt: z12.string().datetime().nullable()
2033
+ line: z13.string().nullable(),
2034
+ lineAt: z13.string().datetime().nullable()
1930
2035
  });
1931
- var SnapshotSchema = z12.object({
2036
+ var SnapshotSchema = z13.object({
1932
2037
  /** The API's clock, taken before the first read: what a later delta will start from. */
1933
- at: z12.string().datetime(),
1934
- questions: z12.object({
2038
+ at: z13.string().datetime(),
2039
+ questions: z13.object({
1935
2040
  /** The newest 30 open cards, questions before updates. */
1936
- items: z12.array(QuestionRowSchema),
2041
+ items: z13.array(QuestionRowSchema),
1937
2042
  /** Every open question, and apart from them every update, and what was put off. */
1938
- total: z12.number().int(),
1939
- updates: z12.number().int(),
1940
- putOff: z12.number().int()
2043
+ total: z13.number().int(),
2044
+ updates: z13.number().int(),
2045
+ putOff: z13.number().int()
1941
2046
  }).nullable(),
1942
- agents: z12.object({
2047
+ agents: z13.object({
1943
2048
  /** Up to 60, most recently seen first. */
1944
- items: z12.array(AgentRowSchema),
1945
- more: z12.boolean()
2049
+ items: z13.array(AgentRowSchema),
2050
+ more: z13.boolean()
1946
2051
  }).nullable(),
1947
- work: z12.object({
2052
+ work: z13.object({
1948
2053
  /** The 60 most recently active roots, each followed by up to 12 children; 240 rows at most. */
1949
- items: z12.array(WorkRowSchema),
2054
+ items: z13.array(WorkRowSchema),
1950
2055
  /** How much work is behind each of the Work tab's four filters, each counted to 100, read with
1951
2056
  * the rows. `work_list` (20260928023533) owns the predicates: Live is `ready`, `active` or
1952
2057
  * `waiting`; Waiting on you is live work with an open question or an unmet gate; Not started
1953
2058
  * is `ready`; Done is `done` or `cancelled`. */
1954
- counts: z12.object({ live: z12.number().int(), waiting: z12.number().int(), notStarted: z12.number().int(), done: z12.number().int() })
2059
+ counts: z13.object({ live: z13.number().int(), waiting: z13.number().int(), notStarted: z13.number().int(), done: z13.number().int() })
1955
2060
  }).nullable(),
1956
- you: z12.object({
2061
+ you: z13.object({
1957
2062
  settings: UserSettingsSchema,
1958
- callable: z12.boolean(),
2063
+ callable: z13.boolean(),
1959
2064
  /** Up to 20 paired computers; null when the roster read failed. */
1960
- computers: z12.array(ComputerRowSchema).nullable()
2065
+ computers: z13.array(ComputerRowSchema).nullable()
1961
2066
  }).nullable()
1962
2067
  });
1963
- var CallRecapSchema = z12.object({
1964
- call: z12.object({
1965
- status: z12.string(),
1966
- startedAt: z12.string(),
1967
- durationMs: z12.number().nullable(),
2068
+ var CallRecapSchema = z13.object({
2069
+ call: z13.object({
2070
+ status: z13.string(),
2071
+ startedAt: z13.string(),
2072
+ durationMs: z13.number().nullable(),
1968
2073
  /** Bot-clock pickup offset; absent before the timestamp release, null before its report lands. */
1969
- pickupMs: z12.number().nullable().optional(),
1970
- agents: z12.array(z12.object({ id: z12.string(), name: z12.string().nullable() }))
2074
+ pickupMs: z13.number().nullable().optional(),
2075
+ /** STILL FILING (20261026182117): the call has not ended on the server, or it has and a brain cycle on it is
2076
+ * still pending, the next writer, which may still settle, file or start something. What the close itself
2077
+ * waits for (`private.close_call`). `status` cannot say it: nothing writes "finalizing", and a call reads
2078
+ * "open" until the sweep closes it, after nothing is pending. Absent from an API before it. */
2079
+ filing: z13.boolean().optional(),
2080
+ agents: z13.array(z13.object({ id: z13.string(), name: z13.string().nullable() }))
1971
2081
  }),
1972
- topics: z12.array(z12.object({
1973
- goalId: z12.string().uuid(),
1974
- title: z12.string(),
1975
- owner: z12.string(),
1976
- state: z12.string(),
2082
+ topics: z13.array(z13.object({
2083
+ goalId: z13.string().uuid(),
2084
+ title: z13.string(),
2085
+ owner: z13.string(),
2086
+ state: z13.string(),
1977
2087
  /** Presented Entry content and its author, separately from the current Goal owner. */
1978
- items: z12.array(z12.object({ entryId: z12.string(), author: z12.string(), text: z12.string(), summary: z12.string().nullable(), acknowledged: z12.boolean().optional() })).optional(),
2088
+ items: z13.array(z13.object({ entryId: z13.string(), author: z13.string(), text: z13.string(), summary: z13.string().nullable(), acknowledged: z13.boolean().optional() })).optional(),
1979
2089
  /** Whether the call put it to the person. An item it carried and never said has no row on the
1980
2090
  * review (owner, 2026-10-08: "why do we get the third one when it didn't happen in the call?"). */
1981
- presented: z12.boolean(),
2091
+ presented: z13.boolean(),
1982
2092
  /** Whether THIS call created it — the third way a call touches work, beside carrying a request
1983
2093
  * there and filing words there (owner, 2026-10-08, Goal `5b4b1a0a`: "Show the new work, keep my
1984
2094
  * words under it"). Created work is never carried, so it is never `presented`; without this the
1985
2095
  * row is dropped by the "only what happened on the call" filter. False on a database before
1986
2096
  * 20261013080133. */
1987
- created: z12.boolean(),
2097
+ created: z13.boolean(),
1988
2098
  /** `request` is the Entry that asked it — what a line's `about` names when the line answered or put
1989
2099
  * it off. Optional: an older API sent none. */
1990
- questions: z12.array(z12.object({ id: z12.string().uuid(), state: z12.string(), title: z12.string(), request: z12.string().optional() })),
2100
+ questions: z13.array(z13.object({ id: z13.string().uuid(), state: z13.string(), title: z13.string(), request: z13.string().optional() })),
1991
2101
  /** `words` is always what they SAID, verbatim — the record, never replaced. `summary` is what
1992
2102
  * the line says in ten words (owner, 2026-10-07): the filer's, or the talker's for the answer it
1993
2103
  * gave, so the row scans like a chosen option and their own words stay under it. Absent when
@@ -1997,33 +2107,34 @@ var CallRecapSchema = z12.object({
1997
2107
  /** `reply` is the reply the line makes up (an answer, an ok, a deferral), so one reply is one row
1998
2108
  * however many lines it cites; `at` is the call lines it came from, so a filed line an answer already
1999
2109
  * said is not drawn again (owner, 2026-10-07: "fix them all"); `replyKind` says which replies are answers. */
2000
- lines: z12.array(z12.object({
2001
- entryId: z12.string().uuid(),
2002
- words: z12.string(),
2003
- summary: z12.string().optional(),
2004
- about: z12.string().nullable().optional(),
2005
- reply: z12.string().nullable().optional(),
2006
- replyKind: z12.string().nullable().optional(),
2007
- at: z12.array(z12.number()).optional()
2110
+ lines: z13.array(z13.object({
2111
+ entryId: z13.string().uuid(),
2112
+ words: z13.string(),
2113
+ summary: z13.string().optional(),
2114
+ about: z13.string().nullable().optional(),
2115
+ reply: z13.string().nullable().optional(),
2116
+ replyKind: z13.string().nullable().optional(),
2117
+ at: z13.array(z13.number()).optional()
2008
2118
  }))
2009
2119
  })),
2010
- unfiled: z12.array(z12.object({ lineId: z12.string().uuid(), words: z12.string(), atMs: z12.number() })),
2120
+ unfiled: z13.array(z13.object({ lineId: z13.string().uuid(), words: z13.string(), atMs: z13.number() })),
2011
2121
  /** THE PUBLIC TRANSCRIPT — every line said on the call, in the order it was said, and WHO SAID IT
2012
2122
  * (owner, 2026-10-08: "the public view should only have the lines and who said them, and that's
2013
2123
  * it"; `docs/brain/phase-three/modular.md` build step 3). `speaker` is the role: "user" is the
2014
2124
  * person, "agent" an agent speaking, named in `agent`. The timeline's action lines are ours, not
2015
2125
  * theirs, and are not here; nor is a sealed line, which the server has no words for. It stands
2016
2126
  * BESIDE `topics` and `unfiled` — what the call did with the words — never instead of them. */
2017
- said: z12.array(z12.object({
2018
- lineId: z12.string().uuid(),
2019
- speaker: z12.enum(["user", "agent"]),
2020
- agent: z12.object({ id: z12.string().nullable(), name: z12.string().nullable() }).nullable(),
2021
- words: z12.string(),
2022
- atMs: z12.number()
2127
+ said: z13.array(z13.object({
2128
+ lineId: z13.string().uuid(),
2129
+ speaker: z13.enum(["user", "agent"]),
2130
+ agent: z13.object({ id: z13.string().nullable(), name: z13.string().nullable() }).nullable(),
2131
+ words: z13.string(),
2132
+ atMs: z13.number()
2023
2133
  })),
2024
- more: z12.object({ lines: z12.number(), entries: z12.number(), topics: z12.number() }),
2134
+ /** What did not fit, counted. `settles` counts items past the recap's 100; absent before 20261026165518. */
2135
+ more: z13.object({ lines: z13.number(), entries: z13.number(), topics: z13.number(), settles: z13.number().optional() }),
2025
2136
  /** Last stored position on this page when another exists; absent before paging shipped. */
2026
- next: z12.number().int().nonnegative().nullable().optional(),
2137
+ next: z13.number().int().nonnegative().nullable().optional(),
2027
2138
  /** THE CALL AS ONE LIST OF ROWS (owner, 2026-10-14: "we only really want a thing like transcript but
2028
2139
  * built from the summaries", then "the transcript tabs should show the same but with full words").
2029
2140
  * Both tabs draw these rows, in this order: Summary from `summary`, Transcript from `words`.
@@ -2034,61 +2145,79 @@ var CallRecapSchema = z12.object({
2034
2145
  * `by` is the row's real author, never inferred from `kind`: a relay Paigy sent to an agent is
2035
2146
  * Paigy's, not the person's. Optional and additive (owner: "make sure whatever you do is backward
2036
2147
  * compatible"): an API before it sends none, and the screen falls back to `topics` and `said`. */
2037
- timeline: z12.array(z12.object({
2038
- id: z12.string(),
2039
- position: z12.number(),
2040
- atMs: z12.number(),
2041
- kind: z12.enum(["put", "said", "did"]),
2042
- by: z12.object({ participant: z12.string().nullable(), agentId: z12.string().nullable(), name: z12.string().nullable() }),
2043
- goalId: z12.string().uuid().nullable(),
2044
- requestId: z12.string().nullable(),
2045
- summary: z12.string().nullable(),
2046
- words: z12.string(),
2148
+ timeline: z13.array(z13.object({
2149
+ id: z13.string(),
2150
+ position: z13.number(),
2151
+ atMs: z13.number(),
2152
+ kind: z13.enum(["put", "said", "did"]),
2153
+ by: z13.object({ participant: z13.string().nullable(), agentId: z13.string().nullable(), name: z13.string().nullable() }),
2154
+ goalId: z13.string().uuid().nullable(),
2155
+ requestId: z13.string().nullable(),
2156
+ summary: z13.string().nullable(),
2157
+ words: z13.string(),
2047
2158
  /** Paigy's sentences folded into this row: on a `put` row, what it said putting the item to the
2048
2159
  * person (before their next row); on a `said` row, what it said in reply (until their next row).
2049
2160
  * `atMs` is when each was said; optional, absent before 2026-10-14's fix. */
2050
- spoken: z12.array(z12.object({ lineId: z12.string(), words: z12.string(), atMs: z12.number().optional() })),
2161
+ spoken: z13.array(z13.object({ lineId: z13.string(), words: z13.string(), atMs: z13.number().optional() })),
2051
2162
  /** On a `said` row: the work these words were filed on, and whether they answered (recorded) a
2052
2163
  * question. They replace the "Filed on ..." and "Recorded: ..." rows that repeated the person's own
2053
2164
  * summary (Brain, 2026-10-14). Optional: absent from an API before it. */
2054
- filedOn: z12.array(z12.object({ goalId: z12.string().uuid(), title: z12.string() })).optional(),
2055
- recorded: z12.boolean().optional(),
2056
- settled: z12.object({ requestId: z12.string(), answer: z12.string() }).nullable().optional(),
2057
- acknowledged: z12.boolean().optional(),
2165
+ filedOn: z13.array(z13.object({ goalId: z13.string().uuid(), title: z13.string() })).optional(),
2166
+ recorded: z13.boolean().optional(),
2167
+ settled: z13.object({ requestId: z13.string(), answer: z13.string() }).nullable().optional(),
2168
+ acknowledged: z13.boolean().optional(),
2058
2169
  /** On a `put` row: still waiting for the person's answer (not settled, not answered, not put off).
2059
2170
  * The server's judgement; the screen draws "Still waiting for your answer" from it. Optional: absent
2060
2171
  * from an API before it, where the screen falls back to its own reading. */
2061
- waiting: z12.boolean().optional(),
2062
- lineIds: z12.array(z12.string())
2172
+ waiting: z13.boolean().optional(),
2173
+ lineIds: z13.array(z13.string())
2174
+ })).optional(),
2175
+ /** THE RECAP (owner, 2026-10-26: "the transcript should just be the entries that settled or
2176
+ * acknowledged the items; that's it"). Each item the person's words settled on this call, in the
2177
+ * order they said them: who asked it (`by`), the item in its agent's words (`title`), and only the
2178
+ * words of theirs that settled it (`lines`: each line once, verbatim, with when it was said on the
2179
+ * bot clock like `said[].atMs`). Built from their replies, not from what the call put, so an item
2180
+ * settled without being put is still here. Beside them (owner: "List them too, with the words that
2181
+ * said them"), each Goal his words told something (`kind` "told") or the call started ("started"),
2182
+ * with `requestId` null and the Goal's name as `title`; a started Goal may have no `lines` yet. `kind`
2183
+ * is a string, not an enum, so a later kind never fails an older build's parse; `requestId` is the
2184
+ * request the words settled. On the first page only; absent from an API before it. */
2185
+ settles: z13.array(z13.object({
2186
+ requestId: z13.string().nullable(),
2187
+ kind: z13.string(),
2188
+ by: z13.object({ participant: z13.string(), agentId: z13.string().nullable(), name: z13.string() }),
2189
+ goalId: z13.string().uuid().nullable(),
2190
+ title: z13.string(),
2191
+ lines: z13.array(z13.object({ lineId: z13.string(), words: z13.string(), atMs: z13.number() }))
2063
2192
  })).optional()
2064
2193
  });
2065
2194
 
2066
2195
  // src/listening.ts
2067
2196
  import { createHash } from "crypto";
2068
- import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
2197
+ import { chmodSync, closeSync, fsyncSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, writeSync } from "fs";
2069
2198
  import { homedir } from "os";
2070
2199
  import { dirname, join } from "path";
2071
2200
  var HARNESS_MARKS = ["PAIGY_HARNESS"];
2072
2201
  function harnessDriven(env) {
2073
2202
  return HARNESS_MARKS.some((mark) => !!env[mark]);
2074
2203
  }
2204
+ var LISTEN_LINES = "Each line it prints is something new addressed to you (a Delivery, an answer to your question, new words on your Goal, a Goal assigned to you): read the Goal it names with get_goals, and answer with contact.";
2205
+ var HOOK_SERIAL = 2;
2075
2206
  function sessionStartHook() {
2076
2207
  return `#!/bin/sh
2077
- # Paigy \u2014 written by \`paigy-enable-tools\`. Prints one line into a starting session.
2208
+ # Paigy \u2014 written by \`paigy-enable-tools\` (hook v${HOOK_SERIAL}). Prints one line into a starting session.
2078
2209
  # Never edits, never calls the network, and exits 0 whatever happens: a hook that fails
2079
2210
  # is a session that fails to start.
2080
2211
  [ -n "${HARNESS_MARKS.map((m) => `\${${m}}`).join("")}" ] && exit 0 # harness-spawned (either mark): its pump already delivers
2081
2212
  [ -f "\${HOME}/.paigy/token.json" ] || exit 0 # not paired on this machine: nothing to listen as
2082
2213
  cat <<'PAIGY'
2083
2214
  Paigy: nothing pushes answers into a session started in a terminal \u2014 an answer waits for your
2084
- next contact({ wait: false }). Start your own listener now with your harness's MONITOR tool, the kind
2085
- that wakes you on every line it prints (in Claude Code: Monitor, not a background Bash command, which
2086
- only tells you when it exits). When the monitor expires, start it again:
2215
+ next contact({ wait: false }). Start your own listener now with ${LISTEN_MONITOR}.
2216
+ When the monitor expires, start it again:
2087
2217
 
2088
- npx -y -p @paigy/mcp@latest paigy-listen --brief
2218
+ ${LISTEN_COMMAND}
2089
2219
 
2090
- Each line it prints is a Delivery waiting for you: read the Goal, and reply with contact when
2091
- the answer needs a decision.
2220
+ ${LISTEN_LINES}
2092
2221
 
2093
2222
  ONE LISTENER PER SESSION, AND ANOTHER SESSION'S IS NOT YOURS. Identities here are per session, so
2094
2223
  a paigy-listen you find in ps is almost certainly another session listening as itself, and
@@ -2131,7 +2260,7 @@ function decideListen(f) {
2131
2260
  return {
2132
2261
  status: "start",
2133
2262
  command: `PAIGY_AGENT=${word(f.slot)} PAIGY_SESSION_ID=${word(f.session)} ${f.env.CODEX_THREAD_ID ? `CODEX_THREAD_ID=${word(f.env.CODEX_THREAD_ID)} ` : ""}${f.env.PAIGY_ON_WAKE ? `PAIGY_ON_WAKE=${word(f.env.PAIGY_ON_WAKE)} ` : ""}${word(f.execPath)} ${word(f.listenJs)} --brief`,
2134
- how: "Run this command NOW with your harness's MONITOR tool, the kind that wakes you on every line it prints (in Claude Code: Monitor, not a background Bash command, which only tells you when it exits); start it again when the monitor expires. Each line it prints is a Delivery waiting for you: read the Goal it names with get_goals, and answer with contact. Start it once."
2263
+ how: `Run this command NOW with ${LISTEN_MONITOR}; start it again when the monitor expires. ${LISTEN_LINES} Start it once.`
2135
2264
  };
2136
2265
  }
2137
2266
  function listenMark(slot, home = homedir()) {
@@ -2202,9 +2331,9 @@ function unlistenedNote(f, _now) {
2202
2331
  case "no":
2203
2332
  return { unlistened: `${head}, and this host cannot run a listener. Work reaches you only as you act \u2014 it rides your tool calls, as it did this one \u2014 so call contact({ wait: false }) before you stop.` };
2204
2333
  case "yes":
2205
- return { unlistened: `${head}. Start the listener below as a watched background task; a listener you see in ps belongs to another session's slot, never to yours.`, listen_command: decision.command };
2334
+ return { unlistened: `${head}. Start the listener below with ${LISTEN_MONITOR}; a listener you see in ps belongs to another session's slot, never to yours.`, listen_command: decision.command };
2206
2335
  default:
2207
- return { unlistened: `${head}. If this host can run a watched background task, start the listener below; if it cannot, work reaches you only as you act, so call contact({ wait: false }) before you stop.`, listen_command: decision.command };
2336
+ return { unlistened: `${head}. If this host can run one, start the listener below with ${LISTEN_MONITOR}; if it cannot, work reaches you only as you act, so call contact({ wait: false }) before you stop.`, listen_command: decision.command };
2208
2337
  }
2209
2338
  }
2210
2339
  var ReachTeller = class {
@@ -2246,20 +2375,67 @@ function listenBlock(decision, f, stale) {
2246
2375
  listen_permanent: `\`${ENABLE_LISTEN_COMMAND}\` installs the SessionStart hook for ${HOOKED_HARNESSES}, so every future session there is told this at its start. Other hosts are not hooked; their sessions learn it as they act.`
2247
2376
  };
2248
2377
  if (decision.status === "start") {
2249
- block.listen_prompt = canWatch(f) === "no" ? "This host cannot run a listener, so nothing reaches this session while it rests. Work arrives with your tool calls; call contact({ wait: false }) before you stop." : "Start your own listener NOW, as described in `listen` \u2014 a watched background task, left running for this session. Without it, every answer waits until you call contact({ wait: false }).";
2378
+ block.listen_prompt = canWatch(f) === "no" ? "This host cannot run a listener, so nothing reaches this session while it rests. Work arrives with your tool calls; call contact({ wait: false }) before you stop." : "Start your own listener NOW, as described in `listen` \u2014 under your harness's Monitor tool, restarted whenever the monitor expires. Without it, every answer waits until you call contact({ wait: false }).";
2250
2379
  }
2251
2380
  if (stale) {
2252
- block.hook_refresh = `The SessionStart hook on this machine is an older text. Ask the user to re-run \`${ENABLE_LISTEN_COMMAND}\` to refresh it \u2014 the old one tells a session to skip its listener when it sees another session's.`;
2381
+ block.hook_refresh = `The SessionStart hook on this machine is an older text this server could not refresh. Ask the user to re-run \`${ENABLE_LISTEN_COMMAND}\` to refresh it \u2014 an old one tells a session to skip its listener, or to run it where it never wakes the session.`;
2253
2382
  }
2254
2383
  return block;
2255
2384
  }
2256
2385
  var ENABLE_LISTEN_COMMAND = "npx -y -p @paigy/mcp@latest paigy-enable-tools";
2386
+ var hookScript = (home = homedir()) => join(home, ".paigy", "hooks", "session-start.sh");
2257
2387
  function hookStale(home = homedir()) {
2388
+ let text;
2258
2389
  try {
2259
- return readFileSync(join(home, ".paigy", "hooks", "session-start.sh"), "utf8") !== sessionStartHook();
2390
+ text = readFileSync(hookScript(home), "utf8");
2260
2391
  } catch {
2261
2392
  return false;
2262
2393
  }
2394
+ if (text === sessionStartHook()) return false;
2395
+ return !(text.startsWith(OURS) && serialOf(text) >= HOOK_SERIAL);
2396
+ }
2397
+ var OURS = "#!/bin/sh\n# Paigy \u2014 written by ";
2398
+ function serialOf(text) {
2399
+ const m = /\(hook v(\d+)\)/.exec(text.split("\n", 2)[1] ?? "");
2400
+ return m ? Number(m[1]) : 1;
2401
+ }
2402
+ function refreshSessionHook(home = homedir()) {
2403
+ const path = hookScript(home);
2404
+ let text;
2405
+ try {
2406
+ text = readFileSync(path, "utf8");
2407
+ } catch {
2408
+ return "absent";
2409
+ }
2410
+ const current = sessionStartHook();
2411
+ if (text === current) return "current";
2412
+ if (!text.startsWith(OURS)) return "foreign";
2413
+ if (serialOf(text) >= HOOK_SERIAL) return "newer";
2414
+ let real;
2415
+ let next = "";
2416
+ try {
2417
+ real = realpathSync(path);
2418
+ const mode = statSync(real).mode & 511;
2419
+ next = `${real}.${process.pid}.tmp`;
2420
+ const fd = openSync(next, "w", 384);
2421
+ try {
2422
+ writeSync(fd, current);
2423
+ fsyncSync(fd);
2424
+ } finally {
2425
+ closeSync(fd);
2426
+ }
2427
+ renameSync(next, real);
2428
+ chmodSync(real, mode);
2429
+ return "refreshed";
2430
+ } catch {
2431
+ if (next) {
2432
+ try {
2433
+ rmSync(next, { force: true });
2434
+ } catch {
2435
+ }
2436
+ }
2437
+ return "failed";
2438
+ }
2263
2439
  }
2264
2440
  function waitingSummary(received) {
2265
2441
  const r = received ?? {};
@@ -2280,7 +2456,10 @@ function waitingSummary(received) {
2280
2456
 
2281
2457
  export {
2282
2458
  mcpInputSchema,
2459
+ AGENT_PROMPTS,
2460
+ promptResult,
2283
2461
  AGENT_TOOLS,
2462
+ LISTEN_COMMAND,
2284
2463
  serverInstructions,
2285
2464
  entryWords,
2286
2465
  entryUnit,
@@ -2297,5 +2476,6 @@ export {
2297
2476
  withReach,
2298
2477
  listenBlock,
2299
2478
  ENABLE_LISTEN_COMMAND,
2300
- hookStale
2479
+ hookStale,
2480
+ refreshSessionHook
2301
2481
  };