@paigy/mcp 0.40.20 → 0.40.21

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.
@@ -1592,7 +1592,7 @@ var GetGoalSchema = z3.object({
1592
1592
  history: z3.boolean().optional(),
1593
1593
  /** WHEN A READ HAS CONFUSED YOU. Adds `diagnosis`: every reader that already answers a question
1594
1594
  * about this work, each answer attributed to the reader that gave it, and every disagreement
1595
- * between two of them named. Off by default; `apps/api/src/goal/diagnose-design.md`. */
1595
+ * between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
1596
1596
  diagnose: z3.boolean().optional()
1597
1597
  }).strict();
1598
1598
  var GET_GOAL_DESCRIPTION = "Read one Goal without claiming it: its 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. 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. Another account's Goals, and another agent's Goal that is not below one of yours, are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
@@ -1625,6 +1625,7 @@ function entryWords(entry) {
1625
1625
  return entry.sources.map((source) => source.text).join("\n");
1626
1626
  }
1627
1627
  var LIVE_MS = 3 * 6e4;
1628
+ var WORKING_MS = 30 * 6e4;
1628
1629
  var ContextSchema = z4.object({
1629
1630
  title: z4.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
1630
1631
  description: z4.array(z4.string().min(1)).describe(
@@ -1695,7 +1696,7 @@ var AttentionSchema = z4.object({
1695
1696
  points: z4.array(z4.string()).nullable(),
1696
1697
  /** Whether the ask blocks the sender — what lets arbitration escalate it on silence. */
1697
1698
  blocking: z4.boolean(),
1698
- /** Reserved (MODEL.md lists it): a response deadline. No row column yet — a later Phase 2
1699
+ /** Reserved (docs/model/model.md lists it): a response deadline. No row column yet — a later Phase 2
1699
1700
  * slice wires it; optional so today's rows/callers project cleanly. */
1700
1701
  deadline: z4.string().datetime().nullable().optional()
1701
1702
  });
@@ -1746,7 +1747,7 @@ var NotifyRequestFields = z4.object({
1746
1747
  * derive agent-side before sealing, so the server only ever shapes plaintext). */
1747
1748
  // 10k, not a sentence budget. What the human hears is bounded by the BROKER — it splits
1748
1749
  // the ask into topics and gives each one at most three sentences and one question
1749
- // (broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
1750
+ // (docs/brain/broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
1750
1751
  // Owner, 2026-07-28: "our actual limitation on how long something is to the user should
1751
1752
  // come from the broker splitting and summarizing." The cap that remains is a size guard.
1752
1753
  ask: z4.string().min(1).max(1e4).optional().describe(
@@ -1823,7 +1824,7 @@ var UserAnswerSchema = z4.discriminatedUnion("kind", [
1823
1824
  z4.object({ kind: z4.literal("clarify"), chunks: z4.array(z4.string()).min(1) }),
1824
1825
  z4.object({ kind: z4.literal("confirm"), approved: z4.boolean() }),
1825
1826
  z4.object({ kind: z4.literal("turns"), turns: z4.array(TurnSchema).min(1) }),
1826
- /** An auto-answer derived from the user's PAST decisions (broker/precedent-design.md §2):
1827
+ /** An auto-answer derived from the user's PAST decisions (docs/brain/broker/precedent-design.md §2):
1827
1828
  * delivered through the same settle/await path as a human answer, carrying the judge's
1828
1829
  * derivation and the precedent ids it grew from. Always paired with a visible trail
1829
1830
  * card the user can reply to — the broker never overrides the user. */
@@ -1872,7 +1873,7 @@ var AwaitItemSchema = z4.discriminatedUnion("type", [
1872
1873
  * and now knows exactly which call to make. Absent when either half is missing —
1873
1874
  * a sentence with a hole in it is worse than no sentence. */
1874
1875
  note: z4.string().optional(),
1875
- /** The call record rendered for THIS agent (`voice/record-design.md`): the words the
1876
+ /** The call record rendered for THIS agent (`docs/brain/voice/record-design.md`): the words the
1876
1877
  * shaped answer was mapped from, filtered to its own claims. There is no second list
1877
1878
  * of labels beside it — the acts went 2026-09-04 and `intents` went with them (owner,
1878
1879
  * 2026-09-04): the agent reads the sentence and decides. */
@@ -1968,10 +1969,11 @@ var AgendaTurnSchema = z4.object({
1968
1969
  agentId: z4.string().optional(),
1969
1970
  select: SelectShapeSchema.optional(),
1970
1971
  options: z4.array(OptionSchema.omit({ id: true })).optional(),
1971
- /** Pacing (#826, owner 2026-08-03: "how fast we move through them ... are parameters"):
1972
- * seconds the floor stays open after this turn speaks. Absent = the bot's defaults
1973
- * (the beat for context, the answer window for asks). Clamped bot-side. */
1974
- pace: z4.number().positive().optional(),
1972
+ /* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
1973
+ paces itself now — a short beat between the sentences of a turn, the longer one at its end
1974
+ (owner, 2026-09-30: "remove the bot deciding pace") — and it does that where the words are
1975
+ spoken, not where the plan is written, so nothing between the brain and the walk decides
1976
+ anything. A caller's own `beat_s` tuning is what it used to override. */
1975
1977
  /** Whether the walk WAITS for an answer before moving on. Absent = derived as today
1976
1978
  * (a question blocks, context flows). blocking:false on a question = ask and move
1977
1979
  * on, the claim stays pending; blocking:true on context = hold for a reply. */
@@ -2006,7 +2008,7 @@ var InboxItemSchema = z4.object({
2006
2008
  * instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
2007
2009
  * `createdAt`. */
2008
2010
  agentStateAt: z4.string().datetime().optional(),
2009
- /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (walk/design.md §11, owner
2011
+ /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (docs/clients/app/walk/design.md §11, owner
2010
2012
  * 2026-09-22). One per DecisionNeed on the Call, in the Call's order, answered or open (a
2011
2013
  * superseded or cancelled need is no longer a question anyone is asked). Present only on a
2012
2014
  * Call's cards, and every card of that Call carries the same list: the call screen reads it
@@ -2067,7 +2069,7 @@ var InboxItemSchema = z4.object({
2067
2069
  * off the same open list the card came from, so it clears when the Call does. A card is the
2068
2070
  * backup for a call not taken; while the call has it, the call is where it is answered. */
2069
2071
  onCall: z4.literal(true).optional(),
2070
- /** THE RING, ON THE ITEM (walk/design.md §12 §17, #2251): the last ring on this card was
2072
+ /** THE RING, ON THE ITEM (docs/clients/app/walk/design.md §12 §17, #2251): the last ring on this card was
2071
2073
  * declined, and what the ladder will do next — read off the cron's own row, never computed
2072
2074
  * on the phone. Present only while a `declined` receipt stands on the card's last Call.
2073
2075
  * The ladder is ACCOUNT-WIDE (#2259): `anchorAt` and `step` are the account's position;
@@ -2098,7 +2100,7 @@ var InboxItemSchema = z4.object({
2098
2100
  "Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
2099
2101
  ),
2100
2102
  /** Real downstream work is stuck behind this one — set by the agent, independent of
2101
- * urgency (see the main README's "premier use case" + notify/states.md). Drives the
2103
+ * urgency (see the main README's "premier use case" + docs/delivery/notify/states.md). Drives the
2102
2104
  * inbox's blocking badge and the extra confirm step before dismissing it. */
2103
2105
  blocking: z4.boolean().default(false),
2104
2106
  /** The user's locked-in answer; present only for replied items (GET /api/replied/:id). */
@@ -2172,9 +2174,16 @@ var UserSettingsSchema = z4.object({
2172
2174
  banner: z4.boolean(),
2173
2175
  push: z4.boolean()
2174
2176
  }),
2177
+ /** LockedIn / Default / DateNight on screen; the stored words are unchanged on purpose —
2178
+ * they are an enum on a live column across every account, and the rename is a rename of
2179
+ * what people read (owner, 2026-09-30). */
2175
2180
  sessionMode: z4.enum(["default", "all_calls", "silent"]),
2176
- silentPush: z4.boolean(),
2177
- autoCallback: z4.boolean(),
2181
+ /** DEAD, AND ACCEPTED ANYWAY (#2813). DateNight is inbox-only with no way back, so nothing
2182
+ * reads this. It stays OPTIONAL rather than deleted because a phone on the old bundle
2183
+ * PATCHes the whole settings object and would be refused for sending a key we stopped
2184
+ * wanting — the same trap `voiceMode` below already documents. #2813 drops the field and
2185
+ * its column once no installed build still sends it. */
2186
+ silentPush: z4.boolean().optional(),
2178
2187
  /** Opt-in (default false) to using your content to improve Paigy and train models. */
2179
2188
  improveConsent: z4.boolean(),
2180
2189
  missedCall: MissedCallSchema.default("backoff_standard"),
@@ -2185,12 +2194,16 @@ var UserSettingsSchema = z4.object({
2185
2194
  * must not silently reset this privacy choice. Absent = leave unchanged on
2186
2195
  * write, 'hosted' on read (see store.ts). */
2187
2196
  voiceMode: z4.enum(["hosted", "on_device"]).optional(),
2188
- /** Talk — after you answer, the next step is read aloud (walk/design.md §6). ALWAYS ON until
2197
+ /** Talk — after you answer, the next step is read aloud (docs/clients/app/walk/design.md §6). ALWAYS ON until
2189
2198
  * turned off (owner, 2026-09-18, #2249): a setting, not a per-walk toggle. Optional, NOT
2190
2199
  * defaulted, for the same reason `voiceMode` is: a stale client PATCHing the full settings
2191
2200
  * object must not silently turn it back on. Absent = leave unchanged on write, true on
2192
2201
  * read (see store.ts). */
2193
2202
  talk: z4.boolean().optional(),
2203
+ /** CALL DIAGNOSTICS (owner, 2026-10-01): the call report carries each listen and the bot's own
2204
+ * load timings. SERVER-SET, no UI — on for every account that existed on 2026-10-01, off for
2205
+ * newer ones (migration 20261001132859). Read-only here: the settings PATCH never writes it. */
2206
+ callDiagnostics: z4.boolean().optional(),
2194
2207
  /** Per-user ring budget (#603): calls per rolling day before further calls
2195
2208
  * degrade to banner. Absent = the global default (25). A number, never a
2196
2209
  * bypass — every account keeps a ceiling. No UI; set per user for testing. */
@@ -2199,9 +2212,6 @@ var UserSettingsSchema = z4.object({
2199
2212
  * slower speaker). No API-side semantics; the bot resolves each key with its
2200
2213
  * own defaults. Set per user (no UI yet); absent = bot defaults. */
2201
2214
  voiceTuning: z4.record(z4.string(), z4.union([z4.number(), z4.string()])).optional(),
2202
- /** Opt-in to real-phone (PSTN) calls when the app can't ring. Optional, not
2203
- * defaulted — an older client PATCHing the full object must not clobber it. */
2204
- pstnCalls: z4.boolean().optional(),
2205
2215
  /** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
2206
2216
  * only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
2207
2217
  * an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
@@ -2245,7 +2255,7 @@ var ConnectionSummarySchema = z4.object({
2245
2255
  /** The credential kind: "device" = a paired machine (mint-only — it hosts and mints, it
2246
2256
  * never talks); "agent" = an identity that sends. The roster and devices surfaces split
2247
2257
  * on this. Optional/absent reads as "agent" (a row predating the kind column). See
2248
- * apps/api/src/tokens/devices-vs-agents-design.md. */
2258
+ * docs/server/tokens/devices-vs-agents-design.md. */
2249
2259
  kind: z4.enum(["device", "agent"]).optional(),
2250
2260
  /** For an agent, the token id of the DEVICE that minted it — so agents group under their
2251
2261
  * machine, and revoking a device cascades to them. Null on devices, and on unlinked
@@ -2263,7 +2273,7 @@ var ConnectionSummarySchema = z4.object({
2263
2273
  * user on the agent's own page. null = no ceiling (today's behaviour for every
2264
2274
  * connection). Android binds importance to a relationship rather than to each message,
2265
2275
  * and that is the thing our roster could not say: "Marlow may always call me; Otto
2266
- * never may" (navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
2276
+ * never may" (docs/clients/app/party/navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
2267
2277
  * every surface at once and outranks even `sessionMode: all_calls` — a mode the user
2268
2278
  * set once must not overrule a rule they set about one agent. */
2269
2279
  reach: NotifyLevelSchema.nullable().optional(),
@@ -2274,7 +2284,14 @@ var ConnectionSummarySchema = z4.object({
2274
2284
  /** Last presence heartbeat from a running agent process (POST /api/presence) — the
2275
2285
  * desktop app while open. Null = never seen; stale = offline. */
2276
2286
  lastSeenAt: z4.string().datetime().nullable().optional(),
2277
- /** What a live desktop can run (companion.md §2.2), advertised on its heartbeat:
2287
+ /** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
2288
+ * its Goals — took its lease or recorded an operation (`tokens.last_worked_at`). Within
2289
+ * `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
2290
+ lastWorkedAt: z4.string().datetime().nullable().optional(),
2291
+ /** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
2292
+ * With no work of its own for `WORKING_MS`, an agent sitting on this is not taking its work. */
2293
+ oldestReadyAt: z4.string().datetime().nullable().optional(),
2294
+ /** What a live desktop can run (docs/clients/desktop/companion.md §2.2), advertised on its heartbeat:
2278
2295
  * harness availabilities + granted workspaces — the option set the phone's
2279
2296
  * "new session" sheet offers. Absent for ordinary MCP agents. */
2280
2297
  runtime: z4.object({
@@ -2282,7 +2299,12 @@ var ConnectionSummarySchema = z4.object({
2282
2299
  * shows its age here (`apps/desktop/src/update.ts`). */
2283
2300
  version: z4.string().optional(),
2284
2301
  harnesses: z4.array(z4.object({ name: z4.string(), label: z4.string(), status: z4.string() })).optional(),
2285
- workspaces: z4.array(z4.string()).optional()
2302
+ workspaces: z4.array(z4.string()).optional(),
2303
+ /** THE GIT REPOS IN THOSE FOLDERS (2026-10-01, Goal 26982211): each granted folder that is a
2304
+ * repo, and each repo directly inside one, with its `origin` remote. A session started for
2305
+ * work on `mauurda/paigy` opens in that repo rather than the folder above it, where the repo's
2306
+ * own AGENTS.md is never read (`workspaceForRepo`). Absent on hosts that predate it. */
2307
+ repos: z4.array(z4.object({ path: z4.string(), remote: z4.string() })).optional()
2286
2308
  }).optional(),
2287
2309
  /** The tail of this agent's working log, when a harness is driving it — the agent page's
2288
2310
  * live strip. Absent for anything the desktop harness isn't running (a hatched identity
@@ -2365,7 +2387,7 @@ var QueueQuestionSchema = z4.object({
2365
2387
  * for a settled question whose reply carried nothing readable. */
2366
2388
  answer: z4.string().nullable().default(null),
2367
2389
  /** The Goal this question belongs to — a step knows its Goal on its own, not only through
2368
- * an `InboxItem`'s `communication.goalIds[0]` (walk/design.md §12 item 3).
2390
+ * an `InboxItem`'s `communication.goalIds[0]` (docs/clients/app/walk/design.md §12 item 3).
2369
2391
  * READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
2370
2392
  * order, its route, home's trees and the list of steps all take a step's Goal from here, so
2371
2393
  * this is the field they agree through rather than each re-deriving it from the row it
@@ -2457,7 +2479,19 @@ var QueueItemSchema = z4.object({
2457
2479
  said: z4.string(),
2458
2480
  at: z4.string(),
2459
2481
  entryId: z4.string()
2460
- }).nullable().optional()
2482
+ }).nullable().optional(),
2483
+ /** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
2484
+ * the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
2485
+ * answer to a question, a voice note filed as work. Null when the only hands on it have been its
2486
+ * agent's; optional, so a hand-built queue (fixtures, the demo) and a door older than
2487
+ * 20260930222356 need not spell it — `work/list.ts`'s `yoursAt` keeps its other sources, so the
2488
+ * Work tab orders as it did before the migration rather than throwing on the missing key.
2489
+ *
2490
+ * It is a FACT, not a reconstruction: `latest` holds one Entry, so an agent's progress note a
2491
+ * minute after the person speaks erases their instant from it, and the durable traces the client
2492
+ * can see (`replies`, `questions[].answeredAt`) miss a spontaneous note entirely — a `request`
2493
+ * Entry with no `about_id` is in neither. */
2494
+ lastPersonAt: z4.string().nullable().optional()
2461
2495
  });
2462
2496
  var COLD_AFTER_MS = 3 * 24 * 60 * 60 * 1e3;
2463
2497
  var NoteSourceSchema = z4.enum(["app", "call"]);
@@ -2487,7 +2521,7 @@ var NoteSchema = z4.object({
2487
2521
  assignee: z4.string().nullable(),
2488
2522
  /** The request thread minted at assignment; null until assigned. */
2489
2523
  parentId: z4.string().nullable(),
2490
- /** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
2524
+ /** REMINDERS (docs/model/notes/reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
2491
2525
  * call — never a deadline. It only ever comes from the user's own words, so when it
2492
2526
  * passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
2493
2527
  * a ring at that time); after that it rides like any other. Null = "the very next
@@ -2749,6 +2783,29 @@ var SnapshotSchema = z4.object({
2749
2783
  computers: z4.array(ComputerRowSchema).nullable()
2750
2784
  }).nullable()
2751
2785
  });
2786
+ var CallRecapSchema = z4.object({
2787
+ call: z4.object({
2788
+ status: z4.string(),
2789
+ startedAt: z4.string(),
2790
+ durationMs: z4.number().nullable(),
2791
+ agents: z4.array(z4.object({ id: z4.string(), name: z4.string().nullable() }))
2792
+ }),
2793
+ topics: z4.array(z4.object({
2794
+ goalId: z4.string().uuid(),
2795
+ title: z4.string(),
2796
+ owner: z4.string(),
2797
+ state: z4.string(),
2798
+ questions: z4.array(z4.object({ id: z4.string().uuid(), state: z4.string(), title: z4.string() })),
2799
+ /** `words` is always what they SAID, verbatim — the record, never replaced. `headline` is
2800
+ * their answer on one line when the call's read wrote one (owner, 2026-10-01: "render them
2801
+ * summarized like a pre-made option is"), so the row scans like a chosen option and their
2802
+ * own words stay under it. Absent on every line stored before the read wrote them, and on
2803
+ * anything that is not an answer. */
2804
+ lines: z4.array(z4.object({ entryId: z4.string().uuid(), words: z4.string(), headline: z4.string().optional() }))
2805
+ })),
2806
+ unfiled: z4.array(z4.object({ lineId: z4.string().uuid(), words: z4.string(), atMs: z4.number() })),
2807
+ more: z4.object({ lines: z4.number(), entries: z4.number(), topics: z4.number() })
2808
+ });
2752
2809
  function sessionSlot(sessionId2) {
2753
2810
  const id = sessionId2 ?? sessionId();
2754
2811
  return `session:${id.slice(0, 8)}`;
@@ -4,10 +4,10 @@ import {
4
4
  serverInstructions,
5
5
  sessionStartHook,
6
6
  withSessionStartHook
7
- } from "./chunk-RLN2B5IZ.js";
7
+ } from "./chunk-OW65TWMX.js";
8
8
  import {
9
9
  agentName
10
- } from "./chunk-6C6CO7H3.js";
10
+ } from "./chunk-2UEIJRRC.js";
11
11
 
12
12
  // src/toolset.ts
13
13
  var ONBOARD_DESCRIPTION = "Get this agent talking to Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call, and it does whatever the situation needs: NOT SET UP \u2192 hatches an identity instantly if this machine holds a device credential (the user ran the Paigy desktop app or harness), otherwise starts the code ceremony; ALREADY SET UP \u2192 returns your current identity and offers the two things left to decide, renaming it or unpairing; TOKEN NO LONGER VALID \u2192 says so, then re-pairs. Pass { name, voice } to choose who you are when hatching, or to RENAME yourself when already set up (voices: rachel, george, jessica, brian, lily). Safe to call any time: idempotent, and it never writes settings \u2014 the tool-allowlist state it reports is read-only. If it returns a `user_code`, print it to the user immediately and call onboard again with the `device_code`. If it returns `enable_command`, PRINT that command for the user to run \u2014 you cannot apply it yourself (it writes your own permission allowlist, which hosts block as privilege escalation), so print it, don't wait for it, and carry on.";
@@ -6,7 +6,7 @@ import {
6
6
  saveToken,
7
7
  setIdentity,
8
8
  sleep
9
- } from "./chunk-6C6CO7H3.js";
9
+ } from "./chunk-2UEIJRRC.js";
10
10
 
11
11
  // src/identity.ts
12
12
  var CLIENT_LABELS = {
@@ -124,7 +124,7 @@ var GetGoalSchema = z3.object({
124
124
  history: z3.boolean().optional(),
125
125
  /** WHEN A READ HAS CONFUSED YOU. Adds `diagnosis`: every reader that already answers a question
126
126
  * about this work, each answer attributed to the reader that gave it, and every disagreement
127
- * between two of them named. Off by default; `apps/api/src/goal/diagnose-design.md`. */
127
+ * between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
128
128
  diagnose: z3.boolean().optional()
129
129
  }).strict();
130
130
  var GET_GOAL_DESCRIPTION = "Read one Goal without claiming it: its 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. 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. Another account's Goals, and another agent's Goal that is not below one of yours, are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
@@ -149,7 +149,11 @@ HOW TO ASK:
149
149
  1. One question per ask. Five questions are five objects in \`asks\`, in one contact, so each can be answered on its own; one question with five parts settles nothing until all five are answered.
150
150
  2. Name the Goal you are working on as the ask's \`parentId\`: the question goes onto that Goal and its answer comes back there. Without one, Paigy places it.
151
151
  3. \`waiting: hard\` only for a decision you are blocked on; \`waiting: none\` for a question you can keep working around.
152
- 4. Never hold the process open with while-loops. For an open Call with a pending decision, follow its \`next\` with another bounded \`contact({deliveryId})\` tool call; a returned wait window is not a completed conversation. Otherwise, yield only with a working listener or scheduled wakeup, and collect answers with \`check_replies\` or \`claim_goal\` on that wake.`;
152
+ 4. Never hold the process open with while-loops. For an open Call with a pending decision, follow its \`next\` with another bounded \`contact({deliveryId})\` tool call; a returned wait window is not a completed conversation. Otherwise, yield only with a working listener or scheduled wakeup, and collect answers with \`check_replies\` or \`claim_goal\` on that wake.
153
+ 5. One ask, one row. Never restate a question that is still waiting inside a new contact: keep waiting on the original, or the answer lands on one copy and the other stays open.
154
+ 6. Read the Goal's conversation before asking. Never ask again what was answered or already shipped.
155
+ 7. A question carries its options. Without them it reaches the person as a bare title nobody can answer.
156
+ 8. A diagnosis says when, why and how it happens, then proposes one fix. Never options first.`;
153
157
  }
154
158
  function entryWords(entry) {
155
159
  const content = entry.content;
@@ -167,6 +171,7 @@ function entryWords(entry) {
167
171
  return entry.sources.map((source) => source.text).join("\n");
168
172
  }
169
173
  var LIVE_MS = 3 * 6e4;
174
+ var WORKING_MS = 30 * 6e4;
170
175
  var ContextSchema = z4.object({
171
176
  title: z4.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
172
177
  description: z4.array(z4.string().min(1)).describe(
@@ -237,7 +242,7 @@ var AttentionSchema = z4.object({
237
242
  points: z4.array(z4.string()).nullable(),
238
243
  /** Whether the ask blocks the sender — what lets arbitration escalate it on silence. */
239
244
  blocking: z4.boolean(),
240
- /** Reserved (MODEL.md lists it): a response deadline. No row column yet — a later Phase 2
245
+ /** Reserved (docs/model/model.md lists it): a response deadline. No row column yet — a later Phase 2
241
246
  * slice wires it; optional so today's rows/callers project cleanly. */
242
247
  deadline: z4.string().datetime().nullable().optional()
243
248
  });
@@ -288,7 +293,7 @@ var NotifyRequestFields = z4.object({
288
293
  * derive agent-side before sealing, so the server only ever shapes plaintext). */
289
294
  // 10k, not a sentence budget. What the human hears is bounded by the BROKER — it splits
290
295
  // the ask into topics and gives each one at most three sentences and one question
291
- // (broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
296
+ // (docs/brain/broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
292
297
  // Owner, 2026-07-28: "our actual limitation on how long something is to the user should
293
298
  // come from the broker splitting and summarizing." The cap that remains is a size guard.
294
299
  ask: z4.string().min(1).max(1e4).optional().describe(
@@ -365,7 +370,7 @@ var UserAnswerSchema = z4.discriminatedUnion("kind", [
365
370
  z4.object({ kind: z4.literal("clarify"), chunks: z4.array(z4.string()).min(1) }),
366
371
  z4.object({ kind: z4.literal("confirm"), approved: z4.boolean() }),
367
372
  z4.object({ kind: z4.literal("turns"), turns: z4.array(TurnSchema).min(1) }),
368
- /** An auto-answer derived from the user's PAST decisions (broker/precedent-design.md §2):
373
+ /** An auto-answer derived from the user's PAST decisions (docs/brain/broker/precedent-design.md §2):
369
374
  * delivered through the same settle/await path as a human answer, carrying the judge's
370
375
  * derivation and the precedent ids it grew from. Always paired with a visible trail
371
376
  * card the user can reply to — the broker never overrides the user. */
@@ -414,7 +419,7 @@ var AwaitItemSchema = z4.discriminatedUnion("type", [
414
419
  * and now knows exactly which call to make. Absent when either half is missing —
415
420
  * a sentence with a hole in it is worse than no sentence. */
416
421
  note: z4.string().optional(),
417
- /** The call record rendered for THIS agent (`voice/record-design.md`): the words the
422
+ /** The call record rendered for THIS agent (`docs/brain/voice/record-design.md`): the words the
418
423
  * shaped answer was mapped from, filtered to its own claims. There is no second list
419
424
  * of labels beside it — the acts went 2026-09-04 and `intents` went with them (owner,
420
425
  * 2026-09-04): the agent reads the sentence and decides. */
@@ -510,10 +515,11 @@ var AgendaTurnSchema = z4.object({
510
515
  agentId: z4.string().optional(),
511
516
  select: SelectShapeSchema.optional(),
512
517
  options: z4.array(OptionSchema.omit({ id: true })).optional(),
513
- /** Pacing (#826, owner 2026-08-03: "how fast we move through them ... are parameters"):
514
- * seconds the floor stays open after this turn speaks. Absent = the bot's defaults
515
- * (the beat for context, the answer window for asks). Clamped bot-side. */
516
- pace: z4.number().positive().optional(),
518
+ /* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
519
+ paces itself now — a short beat between the sentences of a turn, the longer one at its end
520
+ (owner, 2026-09-30: "remove the bot deciding pace") — and it does that where the words are
521
+ spoken, not where the plan is written, so nothing between the brain and the walk decides
522
+ anything. A caller's own `beat_s` tuning is what it used to override. */
517
523
  /** Whether the walk WAITS for an answer before moving on. Absent = derived as today
518
524
  * (a question blocks, context flows). blocking:false on a question = ask and move
519
525
  * on, the claim stays pending; blocking:true on context = hold for a reply. */
@@ -548,7 +554,7 @@ var InboxItemSchema = z4.object({
548
554
  * instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
549
555
  * `createdAt`. */
550
556
  agentStateAt: z4.string().datetime().optional(),
551
- /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (walk/design.md §11, owner
557
+ /** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (docs/clients/app/walk/design.md §11, owner
552
558
  * 2026-09-22). One per DecisionNeed on the Call, in the Call's order, answered or open (a
553
559
  * superseded or cancelled need is no longer a question anyone is asked). Present only on a
554
560
  * Call's cards, and every card of that Call carries the same list: the call screen reads it
@@ -609,7 +615,7 @@ var InboxItemSchema = z4.object({
609
615
  * off the same open list the card came from, so it clears when the Call does. A card is the
610
616
  * backup for a call not taken; while the call has it, the call is where it is answered. */
611
617
  onCall: z4.literal(true).optional(),
612
- /** THE RING, ON THE ITEM (walk/design.md §12 §17, #2251): the last ring on this card was
618
+ /** THE RING, ON THE ITEM (docs/clients/app/walk/design.md §12 §17, #2251): the last ring on this card was
613
619
  * declined, and what the ladder will do next — read off the cron's own row, never computed
614
620
  * on the phone. Present only while a `declined` receipt stands on the card's last Call.
615
621
  * The ladder is ACCOUNT-WIDE (#2259): `anchorAt` and `step` are the account's position;
@@ -640,7 +646,7 @@ var InboxItemSchema = z4.object({
640
646
  "Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
641
647
  ),
642
648
  /** Real downstream work is stuck behind this one — set by the agent, independent of
643
- * urgency (see the main README's "premier use case" + notify/states.md). Drives the
649
+ * urgency (see the main README's "premier use case" + docs/delivery/notify/states.md). Drives the
644
650
  * inbox's blocking badge and the extra confirm step before dismissing it. */
645
651
  blocking: z4.boolean().default(false),
646
652
  /** The user's locked-in answer; present only for replied items (GET /api/replied/:id). */
@@ -714,9 +720,16 @@ var UserSettingsSchema = z4.object({
714
720
  banner: z4.boolean(),
715
721
  push: z4.boolean()
716
722
  }),
723
+ /** LockedIn / Default / DateNight on screen; the stored words are unchanged on purpose —
724
+ * they are an enum on a live column across every account, and the rename is a rename of
725
+ * what people read (owner, 2026-09-30). */
717
726
  sessionMode: z4.enum(["default", "all_calls", "silent"]),
718
- silentPush: z4.boolean(),
719
- autoCallback: z4.boolean(),
727
+ /** DEAD, AND ACCEPTED ANYWAY (#2813). DateNight is inbox-only with no way back, so nothing
728
+ * reads this. It stays OPTIONAL rather than deleted because a phone on the old bundle
729
+ * PATCHes the whole settings object and would be refused for sending a key we stopped
730
+ * wanting — the same trap `voiceMode` below already documents. #2813 drops the field and
731
+ * its column once no installed build still sends it. */
732
+ silentPush: z4.boolean().optional(),
720
733
  /** Opt-in (default false) to using your content to improve Paigy and train models. */
721
734
  improveConsent: z4.boolean(),
722
735
  missedCall: MissedCallSchema.default("backoff_standard"),
@@ -727,12 +740,16 @@ var UserSettingsSchema = z4.object({
727
740
  * must not silently reset this privacy choice. Absent = leave unchanged on
728
741
  * write, 'hosted' on read (see store.ts). */
729
742
  voiceMode: z4.enum(["hosted", "on_device"]).optional(),
730
- /** Talk — after you answer, the next step is read aloud (walk/design.md §6). ALWAYS ON until
743
+ /** Talk — after you answer, the next step is read aloud (docs/clients/app/walk/design.md §6). ALWAYS ON until
731
744
  * turned off (owner, 2026-09-18, #2249): a setting, not a per-walk toggle. Optional, NOT
732
745
  * defaulted, for the same reason `voiceMode` is: a stale client PATCHing the full settings
733
746
  * object must not silently turn it back on. Absent = leave unchanged on write, true on
734
747
  * read (see store.ts). */
735
748
  talk: z4.boolean().optional(),
749
+ /** CALL DIAGNOSTICS (owner, 2026-10-01): the call report carries each listen and the bot's own
750
+ * load timings. SERVER-SET, no UI — on for every account that existed on 2026-10-01, off for
751
+ * newer ones (migration 20261001132859). Read-only here: the settings PATCH never writes it. */
752
+ callDiagnostics: z4.boolean().optional(),
736
753
  /** Per-user ring budget (#603): calls per rolling day before further calls
737
754
  * degrade to banner. Absent = the global default (25). A number, never a
738
755
  * bypass — every account keeps a ceiling. No UI; set per user for testing. */
@@ -741,9 +758,6 @@ var UserSettingsSchema = z4.object({
741
758
  * slower speaker). No API-side semantics; the bot resolves each key with its
742
759
  * own defaults. Set per user (no UI yet); absent = bot defaults. */
743
760
  voiceTuning: z4.record(z4.string(), z4.union([z4.number(), z4.string()])).optional(),
744
- /** Opt-in to real-phone (PSTN) calls when the app can't ring. Optional, not
745
- * defaulted — an older client PATCHing the full object must not clobber it. */
746
- pstnCalls: z4.boolean().optional(),
747
761
  /** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
748
762
  * only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
749
763
  * an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
@@ -787,7 +801,7 @@ var ConnectionSummarySchema = z4.object({
787
801
  /** The credential kind: "device" = a paired machine (mint-only — it hosts and mints, it
788
802
  * never talks); "agent" = an identity that sends. The roster and devices surfaces split
789
803
  * on this. Optional/absent reads as "agent" (a row predating the kind column). See
790
- * apps/api/src/tokens/devices-vs-agents-design.md. */
804
+ * docs/server/tokens/devices-vs-agents-design.md. */
791
805
  kind: z4.enum(["device", "agent"]).optional(),
792
806
  /** For an agent, the token id of the DEVICE that minted it — so agents group under their
793
807
  * machine, and revoking a device cascades to them. Null on devices, and on unlinked
@@ -805,7 +819,7 @@ var ConnectionSummarySchema = z4.object({
805
819
  * user on the agent's own page. null = no ceiling (today's behaviour for every
806
820
  * connection). Android binds importance to a relationship rather than to each message,
807
821
  * and that is the thing our roster could not say: "Marlow may always call me; Otto
808
- * never may" (navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
822
+ * never may" (docs/clients/app/party/navigation-design.md, gap 2). Clamped in `arbitrateLevel`, so it binds
809
823
  * every surface at once and outranks even `sessionMode: all_calls` — a mode the user
810
824
  * set once must not overrule a rule they set about one agent. */
811
825
  reach: NotifyLevelSchema.nullable().optional(),
@@ -816,7 +830,14 @@ var ConnectionSummarySchema = z4.object({
816
830
  /** Last presence heartbeat from a running agent process (POST /api/presence) — the
817
831
  * desktop app while open. Null = never seen; stale = offline. */
818
832
  lastSeenAt: z4.string().datetime().nullable().optional(),
819
- /** What a live desktop can run (companion.md §2.2), advertised on its heartbeat:
833
+ /** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
834
+ * its Goals — took its lease or recorded an operation (`tokens.last_worked_at`). Within
835
+ * `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
836
+ lastWorkedAt: z4.string().datetime().nullable().optional(),
837
+ /** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
838
+ * With no work of its own for `WORKING_MS`, an agent sitting on this is not taking its work. */
839
+ oldestReadyAt: z4.string().datetime().nullable().optional(),
840
+ /** What a live desktop can run (docs/clients/desktop/companion.md §2.2), advertised on its heartbeat:
820
841
  * harness availabilities + granted workspaces — the option set the phone's
821
842
  * "new session" sheet offers. Absent for ordinary MCP agents. */
822
843
  runtime: z4.object({
@@ -824,7 +845,12 @@ var ConnectionSummarySchema = z4.object({
824
845
  * shows its age here (`apps/desktop/src/update.ts`). */
825
846
  version: z4.string().optional(),
826
847
  harnesses: z4.array(z4.object({ name: z4.string(), label: z4.string(), status: z4.string() })).optional(),
827
- workspaces: z4.array(z4.string()).optional()
848
+ workspaces: z4.array(z4.string()).optional(),
849
+ /** THE GIT REPOS IN THOSE FOLDERS (2026-10-01, Goal 26982211): each granted folder that is a
850
+ * repo, and each repo directly inside one, with its `origin` remote. A session started for
851
+ * work on `mauurda/paigy` opens in that repo rather than the folder above it, where the repo's
852
+ * own AGENTS.md is never read (`workspaceForRepo`). Absent on hosts that predate it. */
853
+ repos: z4.array(z4.object({ path: z4.string(), remote: z4.string() })).optional()
828
854
  }).optional(),
829
855
  /** The tail of this agent's working log, when a harness is driving it — the agent page's
830
856
  * live strip. Absent for anything the desktop harness isn't running (a hatched identity
@@ -907,7 +933,7 @@ var QueueQuestionSchema = z4.object({
907
933
  * for a settled question whose reply carried nothing readable. */
908
934
  answer: z4.string().nullable().default(null),
909
935
  /** The Goal this question belongs to — a step knows its Goal on its own, not only through
910
- * an `InboxItem`'s `communication.goalIds[0]` (walk/design.md §12 item 3).
936
+ * an `InboxItem`'s `communication.goalIds[0]` (docs/clients/app/walk/design.md §12 item 3).
911
937
  * READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
912
938
  * order, its route, home's trees and the list of steps all take a step's Goal from here, so
913
939
  * this is the field they agree through rather than each re-deriving it from the row it
@@ -999,7 +1025,19 @@ var QueueItemSchema = z4.object({
999
1025
  said: z4.string(),
1000
1026
  at: z4.string(),
1001
1027
  entryId: z4.string()
1002
- }).nullable().optional()
1028
+ }).nullable().optional(),
1029
+ /** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
1030
+ * the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
1031
+ * answer to a question, a voice note filed as work. Null when the only hands on it have been its
1032
+ * agent's; optional, so a hand-built queue (fixtures, the demo) and a door older than
1033
+ * 20260930222356 need not spell it — `work/list.ts`'s `yoursAt` keeps its other sources, so the
1034
+ * Work tab orders as it did before the migration rather than throwing on the missing key.
1035
+ *
1036
+ * It is a FACT, not a reconstruction: `latest` holds one Entry, so an agent's progress note a
1037
+ * minute after the person speaks erases their instant from it, and the durable traces the client
1038
+ * can see (`replies`, `questions[].answeredAt`) miss a spontaneous note entirely — a `request`
1039
+ * Entry with no `about_id` is in neither. */
1040
+ lastPersonAt: z4.string().nullable().optional()
1003
1041
  });
1004
1042
  var COLD_AFTER_MS = 3 * 24 * 60 * 60 * 1e3;
1005
1043
  var NoteSourceSchema = z4.enum(["app", "call"]);
@@ -1029,7 +1067,7 @@ var NoteSchema = z4.object({
1029
1067
  assignee: z4.string().nullable(),
1030
1068
  /** The request thread minted at assignment; null until assigned. */
1031
1069
  parentId: z4.string().nullable(),
1032
- /** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
1070
+ /** REMINDERS (docs/model/notes/reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
1033
1071
  * call — never a deadline. It only ever comes from the user's own words, so when it
1034
1072
  * passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
1035
1073
  * a ring at that time); after that it rides like any other. Null = "the very next
@@ -1289,6 +1327,29 @@ var SnapshotSchema = z4.object({
1289
1327
  computers: z4.array(ComputerRowSchema).nullable()
1290
1328
  }).nullable()
1291
1329
  });
1330
+ var CallRecapSchema = z4.object({
1331
+ call: z4.object({
1332
+ status: z4.string(),
1333
+ startedAt: z4.string(),
1334
+ durationMs: z4.number().nullable(),
1335
+ agents: z4.array(z4.object({ id: z4.string(), name: z4.string().nullable() }))
1336
+ }),
1337
+ topics: z4.array(z4.object({
1338
+ goalId: z4.string().uuid(),
1339
+ title: z4.string(),
1340
+ owner: z4.string(),
1341
+ state: z4.string(),
1342
+ questions: z4.array(z4.object({ id: z4.string().uuid(), state: z4.string(), title: z4.string() })),
1343
+ /** `words` is always what they SAID, verbatim — the record, never replaced. `headline` is
1344
+ * their answer on one line when the call's read wrote one (owner, 2026-10-01: "render them
1345
+ * summarized like a pre-made option is"), so the row scans like a chosen option and their
1346
+ * own words stay under it. Absent on every line stored before the read wrote them, and on
1347
+ * anything that is not an answer. */
1348
+ lines: z4.array(z4.object({ entryId: z4.string().uuid(), words: z4.string(), headline: z4.string().optional() }))
1349
+ })),
1350
+ unfiled: z4.array(z4.object({ lineId: z4.string().uuid(), words: z4.string(), atMs: z4.number() })),
1351
+ more: z4.object({ lines: z4.number(), entries: z4.number(), topics: z4.number() })
1352
+ });
1292
1353
 
1293
1354
  // src/listening.ts
1294
1355
  import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
@@ -55,7 +55,7 @@ import {
55
55
  updateGoal,
56
56
  updateSlot,
57
57
  whoAmI
58
- } from "./chunk-6C6CO7H3.js";
58
+ } from "./chunk-2UEIJRRC.js";
59
59
  export {
60
60
  AGENT_TOOLS,
61
61
  AGENT_TOOL_NAMES,
package/dist/enable.js CHANGED
@@ -3,9 +3,9 @@ import {
3
3
  PAIGY_TOOL_IDS,
4
4
  enablePaigyTools,
5
5
  installSessionListening
6
- } from "./chunk-6MEUSSRH.js";
7
- import "./chunk-RLN2B5IZ.js";
8
- import "./chunk-6C6CO7H3.js";
6
+ } from "./chunk-B7SEHASI.js";
7
+ import "./chunk-OW65TWMX.js";
8
+ import "./chunk-2UEIJRRC.js";
9
9
 
10
10
  // src/enable.ts
11
11
  function main() {
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  resolvePairing,
7
7
  startPairing,
8
8
  suggestedAgentName
9
- } from "./chunk-YNKTKLQD.js";
9
+ } from "./chunk-LRN27BMO.js";
10
10
  import {
11
11
  ENABLE_COMMAND,
12
12
  OnboardSchema,
@@ -15,11 +15,11 @@ import {
15
15
  SERVER_INSTRUCTIONS,
16
16
  TOOLS,
17
17
  paigyToolsAllowlisted
18
- } from "./chunk-6MEUSSRH.js";
18
+ } from "./chunk-B7SEHASI.js";
19
19
  import {
20
20
  decideListen,
21
21
  listenerAlive
22
- } from "./chunk-RLN2B5IZ.js";
22
+ } from "./chunk-OW65TWMX.js";
23
23
  import {
24
24
  clearSurface,
25
25
  writeSurface
@@ -39,7 +39,7 @@ import {
39
39
  sessionId,
40
40
  slotName,
41
41
  whoAmI
42
- } from "./chunk-6C6CO7H3.js";
42
+ } from "./chunk-2UEIJRRC.js";
43
43
 
44
44
  // src/index.ts
45
45
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -50,6 +50,40 @@ import {
50
50
  } from "@modelcontextprotocol/sdk/types.js";
51
51
  import { fileURLToPath } from "url";
52
52
  import qrcode from "qrcode-generator";
53
+
54
+ // src/survive.ts
55
+ function detail(cause) {
56
+ const stack = cause?.stack;
57
+ return stack ? stack : String(cause);
58
+ }
59
+ function guardTheProcess(proc, log) {
60
+ proc.on("unhandledRejection", (reason) => {
61
+ log(
62
+ `paigy: a promise failed with nobody waiting \u2014 the server stays up and its tools keep working (${detail(reason)})`
63
+ );
64
+ });
65
+ proc.on("uncaughtException", (error) => {
66
+ log(
67
+ `paigy: an error escaped every catch \u2014 the server stays up and its tools keep working (${detail(error)})`
68
+ );
69
+ });
70
+ }
71
+ function traceTheTransport(transport2, log) {
72
+ const theirError = transport2.onerror;
73
+ transport2.onerror = (error) => {
74
+ log(`paigy: the stdio stream errored \u2014 the host may be dropping this server (${detail(error)})`);
75
+ theirError?.(error);
76
+ };
77
+ const theirClose = transport2.onclose;
78
+ transport2.onclose = () => {
79
+ log(
80
+ "paigy: the stdio stream closed \u2014 the host dropped this server, so its tools are gone from this session until the host restarts it (in Claude Code: `/mcp paigy reconnect`)"
81
+ );
82
+ theirClose?.();
83
+ };
84
+ }
85
+
86
+ // src/index.ts
53
87
  var JOIN_CAP_MS = 45e3;
54
88
  function pairedResult(token, sas, note) {
55
89
  clearSurface();
@@ -349,5 +383,7 @@ async function beat() {
349
383
  }
350
384
  void beat();
351
385
  setInterval(() => void beat(), 6e4).unref();
386
+ guardTheProcess(process, (line) => console.error(line));
352
387
  var transport = new StdioServerTransport();
388
+ traceTheTransport(transport, (line) => console.error(line));
353
389
  await server.connect(transport);
package/dist/listen.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  entryWords,
4
4
  removeListenMark,
5
5
  writeListenMark
6
- } from "./chunk-RLN2B5IZ.js";
6
+ } from "./chunk-OW65TWMX.js";
7
7
  import {
8
8
  agentName,
9
9
  checkReplies,
@@ -11,7 +11,7 @@ import {
11
11
  getGoal,
12
12
  slotName,
13
13
  subscribeWake
14
- } from "./chunk-6C6CO7H3.js";
14
+ } from "./chunk-2UEIJRRC.js";
15
15
 
16
16
  // src/listen.ts
17
17
  import { spawn } from "child_process";
package/dist/onboard.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  resolvePairing,
4
4
  startPairing,
5
5
  suggestedAgentName
6
- } from "./chunk-YNKTKLQD.js";
6
+ } from "./chunk-LRN27BMO.js";
7
7
  import {
8
8
  autoConfigureClients,
9
9
  claudeInstallHint,
10
10
  openBrowser
11
- } from "./chunk-6MEUSSRH.js";
12
- import "./chunk-RLN2B5IZ.js";
11
+ } from "./chunk-B7SEHASI.js";
12
+ import "./chunk-OW65TWMX.js";
13
13
  import {
14
14
  TOKEN_PATH,
15
15
  agentName,
@@ -20,7 +20,7 @@ import {
20
20
  saveToken,
21
21
  setIdentity,
22
22
  whoAmI
23
- } from "./chunk-6C6CO7H3.js";
23
+ } from "./chunk-2UEIJRRC.js";
24
24
 
25
25
  // src/onboard.ts
26
26
  function reportRegistered() {
package/dist/slot.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  agentName
4
- } from "./chunk-6C6CO7H3.js";
4
+ } from "./chunk-2UEIJRRC.js";
5
5
 
6
6
  // src/slot.ts
7
7
  process.stdout.write(agentName());
package/dist/stalled.js CHANGED
@@ -29,7 +29,7 @@ async function main() {
29
29
  const dir = join(homedir(), ".paigy", "stalled-reminded");
30
30
  const mark = join(dir, `${session || "session"}-${day}`);
31
31
  if (existsSync(mark)) return;
32
- const { checkReplies } = await import("./dist-33DA5X2G.js");
32
+ const { checkReplies } = await import("./dist-VVU24KB2.js");
33
33
  const reply = stopReply((await checkReplies()).stalled ?? [], input.hook_event_name ?? "Stop");
34
34
  if (!reply) return;
35
35
  mkdirSync(dir, { recursive: true });
@@ -7,7 +7,7 @@ import {
7
7
  readToken,
8
8
  sessionSlot,
9
9
  slotName
10
- } from "./chunk-6C6CO7H3.js";
10
+ } from "./chunk-2UEIJRRC.js";
11
11
 
12
12
  // src/statusline.ts
13
13
  import { realpathSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.40.20",
3
+ "version": "0.40.21",
4
4
  "description": "Paigy MCP server — the AI agent harness that calls you. Lets an agent notify a user and await their reply.",
5
5
  "license": "MIT",
6
6
  "type": "module",