@paigy/mcp 0.40.21 → 0.40.22

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.
@@ -4,10 +4,10 @@ import {
4
4
  serverInstructions,
5
5
  sessionStartHook,
6
6
  withSessionStartHook
7
- } from "./chunk-OW65TWMX.js";
7
+ } from "./chunk-WDZ67U4G.js";
8
8
  import {
9
9
  agentName
10
- } from "./chunk-2UEIJRRC.js";
10
+ } from "./chunk-KSXJCZ2L.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-2UEIJRRC.js";
9
+ } from "./chunk-KSXJCZ2L.js";
10
10
 
11
11
  // src/identity.ts
12
12
  var CLIENT_LABELS = {
@@ -1527,7 +1527,20 @@ var AskInputSchema = z2.object({
1527
1527
  ask: z2.string().trim().min(1).max(1e4).describe(
1528
1528
  "ONE question, and only what is needed to answer it. Several questions are several asks in the array, one each: an answer settles the one ask it was given in the shape that ask declares, so a person who answers the part of a bundled ask that interested them settles nothing and is asked again. Paigy reads every ask once and takes a bundled one apart into its own cards anyway \u2014 each question a Question on the one Goal the ask lands on, never a Goal of its own \u2014 but the words it splits are its reading, not yours. News, progress and findings are their own contact; ANY contact for a person already on a call JOINS that call, whatever channel you asked for, so several arrive as one call."
1529
1529
  ),
1530
- options: z2.array(OptionInputSchema).min(1).max(6).optional()
1530
+ options: z2.array(OptionInputSchema).min(1).max(6).optional(),
1531
+ // A CHECKLIST WHEN THE CHOICES ARE INDEPENDENT (owner, 2026-10-02 01:44 UTC: "bring back the
1532
+ // checklist mode of answering"). The answer shape was taken off this surface with the wire form
1533
+ // (#575) on the promise that Paigy would derive it, and the Goal-scoped contact that replaced it
1534
+ // only ever sends pick-one or words (`goal/intake.ts` `answerOf`): three independent fixes went out
1535
+ // as "All three / 1 and 3 only / Just log it", which the person could not read without the text and
1536
+ // could not answer as the ticks he wanted. "many" is the one shape an agent knows and the read does
1537
+ // not: whether its own options exclude each other.
1538
+ select: z2.enum(["one", "many"]).optional().describe(
1539
+ 'How the options are answered: "one" (default) to pick one, "many" to tick any number of them. Use "many" when the options are independent things they may want several of \u2014 never combinations of them like "all three" or "1 and 3". Ignored without options.'
1540
+ ),
1541
+ answers: z2.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/).optional().describe(
1542
+ "Your reply answers a question the person asked you: the id the conversation shows for it (8 characters or whole). Their question closes with this reply as its answer, and any decision of yours it was holding back goes back to them. Needs parentId: the Goal the question is on. The reply is sent to them as it is; to also ask something new, send that as its own ask."
1543
+ )
1531
1544
  }).strict();
1532
1545
  var StartContactSchema = z2.object({
1533
1546
  asks: z2.array(AskInputSchema).min(1).describe("The questions to pose, one per object. An ask with a parentId is filed on that Goal as it is; only an ask naming no Goal is placed in the tree by Paigy."),
@@ -1536,7 +1549,7 @@ var StartContactSchema = z2.object({
1536
1549
  }).strict();
1537
1550
  var ContactSchema = z2.union([StartContactSchema, z2.object({ deliveryId: z2.string().uuid() }).strict()]);
1538
1551
  var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
1539
- var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. A bundled ask is taken apart into its own cards by Paigy's one intake read, every one on the Goal the ask lands on; separate pieces of work are separate asks. An ask with no options that is not blocking is a report, and a report is an UPDATE, never a claim on them: it reaches them and is listed apart from what waits on them, so no answer is owed and none should be awaited. On a Goal whose report card is still open it is added to that card, with no new push; one that only says where your work stands while under way is recorded as the Goal's progress, not sent (the result says so). Send options (or waiting:'hard') when you actually need an answer. If the person is already on a call, your contact joins that call automatically \u2014 whatever channel you asked for, with no ring \u2014 and the Delivery it returns IS that call: reread it with contact({deliveryId}) to see everything answered on it so far, and contact again while it is live to add information or a further question to the same call.";
1552
+ var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'select', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. A bundled ask is taken apart into its own cards by Paigy's one intake read, every one on the Goal the ask lands on; separate pieces of work are separate asks. An ask with no options that is not blocking is a report, and a report is an UPDATE, never a claim on them: it reaches them and is listed apart from what waits on them, so no answer is owed and none should be awaited. On a Goal whose report card is still open it is added to that card, with no new push; one that only says where your work stands while under way is recorded as the Goal's progress, not sent (the result says so). Send options (or waiting:'hard') when you actually need an answer. Each option's label must stand on its own \u2014 the person may see only the labels \u2014 so never a label that points into your text ('All three', 'Option 2', '1 and 3 only'); when the options are independent and they may want several, pass select:'many' and they get a checklist. If the person is already on a call, your contact joins that call automatically \u2014 whatever channel you asked for, with no ring \u2014 and the Delivery it returns IS that call: reread it with contact({deliveryId}) to see everything answered on it so far, and contact again while it is live to add information or a further question to the same call.";
1540
1553
  var CreateGoalSchema = z3.object({
1541
1554
  outcome: z3.string().trim().min(1).max(1e4),
1542
1555
  /** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
@@ -1595,8 +1608,8 @@ var GetGoalSchema = z3.object({
1595
1608
  * between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
1596
1609
  diagnose: z3.boolean().optional()
1597
1610
  }).strict();
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.";
1599
- var UPDATE_GOAL_DESCRIPTION = `Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. state: done means the work was accomplished, and it can be reopened: when the person says it is not done ("it's not working"), set state: active on that same Goal instead of starting a new one; its parent reopens with it. state: done while children are still open records your part as done: the Goal waits and closes by itself when its last open child closes. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
1611
+ 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. 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. 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.";
1612
+ var UPDATE_GOAL_DESCRIPTION = `Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. A CONTACT ON THIS GOAL MOVES ITS REVISION: a question filed on a Goal is a change to it, so an update prepared before a contact and sent after it is refused as stale (409 goal_revision_conflict) \u2014 re-read the Goal, then write. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. state: done means the work was accomplished, and it can be reopened: when the person says it is not done ("it's not working"), set state: active on that same Goal instead of starting a new one; its parent reopens with it. state: done while children are still open records your part as done: the Goal waits and closes by itself when its last open child closes. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
1600
1613
  var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Another agent's Goal that has gone quiet for 3 days (check_replies lists them as stalledOthers) is taken over when you claim it, and becomes yours to finish or cancel. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
1601
1614
  var CHECK_REPLIES_DESCRIPTION = "What is waiting for you: your open Deliveries (a request the user started toward you, a handoff), one row each with its Goals, how many decisions are still open, and the newest words in brief; `assigned`, your Goals nobody has started yet, however they became yours (claim_goal({goalId}) starts one); and `review`, your Goals with something new on them \u2014 the user's answers and notes are Entries on the Goal, not Deliveries. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread a Delivery in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true clears the Goal from `review` and closes the Deliveries addressed to you on it.";
1602
1615
  var CheckRepliesSchema = z3.object({}).strict();
@@ -1652,6 +1665,13 @@ var TransformSchema = z4.enum([
1652
1665
  "summarize"
1653
1666
  // reduce volume, keep decision value — 30-turn cap, spoken briefing
1654
1667
  ]);
1668
+ function participantRef(p) {
1669
+ return `${p.kind}:${p.id}`;
1670
+ }
1671
+ var PAIGY_SELF = { kind: "agent", id: "paigy" };
1672
+ function isPaigy(ref) {
1673
+ return ref === participantRef(PAIGY_SELF);
1674
+ }
1655
1675
  var VisualSchema = z4.object({
1656
1676
  url: z4.string().url(),
1657
1677
  label: z4.string().optional()
@@ -2178,12 +2198,13 @@ var UserSettingsSchema = z4.object({
2178
2198
  * they are an enum on a live column across every account, and the rename is a rename of
2179
2199
  * what people read (owner, 2026-09-30). */
2180
2200
  sessionMode: z4.enum(["default", "all_calls", "silent"]),
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(),
2201
+ /** `silentPush` lived here until #2813 and is now GONE, field and column both. It was kept as an
2202
+ * optional long after DateNight stopped reading it, on the theory that a phone on an older
2203
+ * bundle PATCHing the whole settings object would be REFUSED for sending a key we had stopped
2204
+ * wanting. That theory was wrong about this schema: these are plain `z.object`s with no
2205
+ * `.strict()` anywhere in this file, and zod STRIPS unknown keys rather than rejecting them, so
2206
+ * an old bundle's `silentPush` is accepted and ignored. Worth remembering before keeping the
2207
+ * next dead field for the same reason. */
2187
2208
  /** Opt-in (default false) to using your content to improve Paigy and train models. */
2188
2209
  improveConsent: z4.boolean(),
2189
2210
  missedCall: MissedCallSchema.default("backoff_standard"),
@@ -2317,7 +2338,8 @@ var ConnectionSummarySchema = z4.object({
2317
2338
  });
2318
2339
  var LedgerItemSchema = z4.object({ id: z4.string(), parentId: z4.string(), title: z4.string(), createdAt: z4.string() });
2319
2340
  var AgentLedgerSchema = z4.object({
2320
- agent: z4.object({ id: z4.string(), name: z4.string(), revokedAt: z4.string().nullable() }),
2341
+ /** Null when the agent has not named itself yet — never a placeholder (owner, 2026-10-01). */
2342
+ agent: z4.object({ id: z4.string(), name: z4.string().nullable(), revokedAt: z4.string().nullable() }),
2321
2343
  /** Its own questions you have not answered. */
2322
2344
  asks: z4.array(LedgerItemSchema),
2323
2345
  /** Its questions you answered that nobody acted on — still owed to somebody. */
@@ -2484,7 +2506,7 @@ var QueueItemSchema = z4.object({
2484
2506
  * the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
2485
2507
  * answer to a question, a voice note filed as work. Null when the only hands on it have been its
2486
2508
  * 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
2509
+ * 20261001030911 need not spell it — `work/list.ts`'s `yoursAt` keeps its other sources, so the
2488
2510
  * Work tab orders as it did before the migration rather than throwing on the missing key.
2489
2511
  *
2490
2512
  * It is a FACT, not a reconstruction: `latest` holds one Entry, so an agent's progress note a
@@ -2801,7 +2823,9 @@ var CallRecapSchema = z4.object({
2801
2823
  * summarized like a pre-made option is"), so the row scans like a chosen option and their
2802
2824
  * own words stay under it. Absent on every line stored before the read wrote them, and on
2803
2825
  * anything that is not an answer. */
2804
- lines: z4.array(z4.object({ entryId: z4.string().uuid(), words: z4.string(), headline: z4.string().optional() }))
2826
+ /** `about` is the request the line answered (its question), null for words that answered none —
2827
+ * the key the screen groups on, so one question is one row however many times it was answered. */
2828
+ lines: z4.array(z4.object({ entryId: z4.string().uuid(), words: z4.string(), headline: z4.string().optional(), about: z4.string().nullable().optional() }))
2805
2829
  })),
2806
2830
  unfiled: z4.array(z4.object({ lineId: z4.string().uuid(), words: z4.string(), atMs: z4.number() })),
2807
2831
  more: z4.object({ lines: z4.number(), entries: z4.number(), topics: z4.number() })
@@ -3053,6 +3077,22 @@ async function claimSessions(opts = {}) {
3053
3077
  if (!res.ok) throw new Error(`claim_sessions failed: ${res.status}`);
3054
3078
  return (await res.json()).sessions;
3055
3079
  }
3080
+ async function claimSessionEnds(opts = {}) {
3081
+ const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/sessions/ends/claim`, {
3082
+ headers: { authorization: `Bearer ${authToken(opts.token)}` }
3083
+ // the HOST's identity
3084
+ }));
3085
+ if (!res.ok) throw new Error(`claim_session_ends failed: ${res.status}`);
3086
+ return (await res.json()).ends ?? [];
3087
+ }
3088
+ async function finishSessionEnd(endId, result, opts = {}) {
3089
+ const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/sessions/ends/${encodeURIComponent(endId)}`, {
3090
+ method: "POST",
3091
+ headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
3092
+ body: JSON.stringify({ result })
3093
+ }));
3094
+ if (!res.ok) await fail("finish_session_end", res);
3095
+ }
3056
3096
  async function recordDecision(decision, opts = {}) {
3057
3097
  const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/host/decisions`, {
3058
3098
  method: "POST",
@@ -3176,7 +3216,9 @@ async function contact(input, opts = {}) {
3176
3216
  // this cannot see.
3177
3217
  repo: repoFromRemote(a.repo) ?? a.repo ?? currentRepo() ?? void 0,
3178
3218
  ask: a.ask,
3179
- options: a.options ?? []
3219
+ options: a.options ?? [],
3220
+ ...a.select ? { select: a.select } : {},
3221
+ ...a.answers ? { answers: a.answers } : {}
3180
3222
  }));
3181
3223
  const res = await ensureAuthed(await send2(`${BACKEND_URL}/api/goals/contact`, {
3182
3224
  method: "POST",
@@ -3241,7 +3283,7 @@ async function checkReplies(opts = {}) {
3241
3283
  function compact(o) {
3242
3284
  return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0 && v !== null && v !== "" && !(Array.isArray(v) && v.length === 0)));
3243
3285
  }
3244
- var who = (participant) => participant.startsWith("human:") ? "person" : participant.startsWith("agent:") ? "agent" : participant;
3286
+ var who = (participant) => participant.startsWith("human:") ? "person" : isPaigy(participant) ? "paigy" : participant.startsWith("agent:") ? "agent" : participant;
3245
3287
  var at = (iso) => {
3246
3288
  const t = Date.parse(iso);
3247
3289
  return Number.isFinite(t) ? new Date(t).toISOString().replace(/\.\d{3}Z$/, "Z") : iso;
@@ -3261,6 +3303,9 @@ function decided(result) {
3261
3303
  return void 0;
3262
3304
  }
3263
3305
  }
3306
+ function stateOf(state, answer, decided2) {
3307
+ return state === "answered" && answer && decided2 === void 0 ? "replied" : state;
3308
+ }
3264
3309
  var handle = (entryId) => entryId.slice(0, 8);
3265
3310
  function conversation(e) {
3266
3311
  const answers = new Map((e.answers ?? []).map((a) => [a.decisionNeedId, a]));
@@ -3273,14 +3318,15 @@ function conversation(e) {
3273
3318
  const need = (e.decisionNeeds ?? []).find((n) => n.requestEntryId === entry.entryId);
3274
3319
  const answer = need ? answers.get(need.decisionNeedId) : void 0;
3275
3320
  const decision = answer ? decided(answer.result) : void 0;
3321
+ const owed = need?.state === "open" && entry.authorParticipant.startsWith("human:");
3276
3322
  return compact({
3277
- id: repliedTo.has(entry.entryId) ? handle(entry.entryId) : void 0,
3323
+ id: repliedTo.has(entry.entryId) || owed ? handle(entry.entryId) : void 0,
3278
3324
  from: who(entry.authorParticipant),
3279
3325
  at: at(entry.createdAt),
3280
3326
  said: sealed ? "[encrypted]" : entryWords(entry).trim(),
3281
3327
  re: entry.aboutId && shown.has(entry.aboutId) ? handle(entry.aboutId) : void 0,
3282
3328
  options,
3283
- decision: need ? compact({ state: need.state, answer: decision }) : void 0
3329
+ decision: need ? compact({ state: stateOf(need.state, answer, decision), answer: decision }) : void 0
3284
3330
  });
3285
3331
  });
3286
3332
  }
@@ -3296,7 +3342,9 @@ function more(g) {
3296
3342
  function goalView(g) {
3297
3343
  if (!g.goalId) return compact({ state: g.state, next: g.message });
3298
3344
  const lines2 = conversation(g);
3299
- const newest = [...lines2].reverse().find((l) => l.from !== "person");
3345
+ const owed = lines2.filter((l) => l.from === "person" && l.decision?.state === "open" && l.id);
3346
+ const owes = owed.length ? `The person asked you ${owed.length === 1 ? "a question" : `${owed.length} questions`} (${owed.map((l) => `id ${l.id}: "${l.said.slice(0, 80)}"`).join("; ")}) \u2014 answer with contact({ asks: [{ ask: <your answer>, parentId: "${g.goalId}", answers: "<id>" }] }). ` : "";
3347
+ const newest = [...lines2].reverse().find((l) => l.from === "agent");
3300
3348
  return compact({
3301
3349
  goalId: g.goalId,
3302
3350
  revision: g.revision,
@@ -3320,7 +3368,7 @@ function goalView(g) {
3320
3368
  dueAt: g.dueAt,
3321
3369
  leaseExpiresAt: g.leaseExpiresAt,
3322
3370
  conversation: lines2,
3323
- next: g.message,
3371
+ next: owes ? `${owes}${g.message ?? ""}`.trim() : g.message,
3324
3372
  more: more(g)
3325
3373
  });
3326
3374
  }
@@ -3340,7 +3388,7 @@ function deliveryView(d) {
3340
3388
  notSent: d.notSent,
3341
3389
  joinedCard: d.joinedCard,
3342
3390
  joinedCall: d.joinedCall,
3343
- next: d.joinedCall ? `${JOINED_CALL}${d.message ? ` ${d.message}` : ""}` : d.kind === "notification" ? d.joinedCard ? `Added to the open report card on its Goal; no new push was sent. ${answers}` : answers : d.state === "open" && d.decisionNeeds.some((n) => n.state === "open") ? `Decision pending. Call contact(${JSON.stringify({ deliveryId: d.deliveryId })}) now to continue this same Call; do not resend the ask or end your turn merely because this wait window returned. ${d.message ?? ""}`.trim() : d.message
3391
+ next: d.joinedCall ? `${JOINED_CALL}${d.message ? ` ${d.message}` : ""}` : d.kind === "notification" ? d.joinedCard ? `Added to the open report card on its Goal; no new push was sent. ${answers}` : answers : d.state === "open" && d.decisionNeeds.some((n) => n.state === "open") ? `Decision pending. Call contact(${JSON.stringify({ deliveryId: d.deliveryId })}) to continue this same Call \u2014 each call is ONE bounded window, and never resend the ask. When a window comes back with nothing new said, they are not typing: stop rereading, leave the question open, and collect the answer with check_replies or claim_goal on your next wake. ${d.message ?? ""}`.trim() : d.message
3344
3392
  });
3345
3393
  }
3346
3394
  function repliesView(r) {
@@ -3647,6 +3695,8 @@ export {
3647
3695
  NameTakenError,
3648
3696
  setIdentity,
3649
3697
  claimSessions,
3698
+ claimSessionEnds,
3699
+ finishSessionEnd,
3650
3700
  recordDecision,
3651
3701
  heartbeat,
3652
3702
  registerDelivery,
@@ -59,7 +59,20 @@ var AskInputSchema = z2.object({
59
59
  ask: z2.string().trim().min(1).max(1e4).describe(
60
60
  "ONE question, and only what is needed to answer it. Several questions are several asks in the array, one each: an answer settles the one ask it was given in the shape that ask declares, so a person who answers the part of a bundled ask that interested them settles nothing and is asked again. Paigy reads every ask once and takes a bundled one apart into its own cards anyway \u2014 each question a Question on the one Goal the ask lands on, never a Goal of its own \u2014 but the words it splits are its reading, not yours. News, progress and findings are their own contact; ANY contact for a person already on a call JOINS that call, whatever channel you asked for, so several arrive as one call."
61
61
  ),
62
- options: z2.array(OptionInputSchema).min(1).max(6).optional()
62
+ options: z2.array(OptionInputSchema).min(1).max(6).optional(),
63
+ // A CHECKLIST WHEN THE CHOICES ARE INDEPENDENT (owner, 2026-10-02 01:44 UTC: "bring back the
64
+ // checklist mode of answering"). The answer shape was taken off this surface with the wire form
65
+ // (#575) on the promise that Paigy would derive it, and the Goal-scoped contact that replaced it
66
+ // only ever sends pick-one or words (`goal/intake.ts` `answerOf`): three independent fixes went out
67
+ // as "All three / 1 and 3 only / Just log it", which the person could not read without the text and
68
+ // could not answer as the ticks he wanted. "many" is the one shape an agent knows and the read does
69
+ // not: whether its own options exclude each other.
70
+ select: z2.enum(["one", "many"]).optional().describe(
71
+ 'How the options are answered: "one" (default) to pick one, "many" to tick any number of them. Use "many" when the options are independent things they may want several of \u2014 never combinations of them like "all three" or "1 and 3". Ignored without options.'
72
+ ),
73
+ answers: z2.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/).optional().describe(
74
+ "Your reply answers a question the person asked you: the id the conversation shows for it (8 characters or whole). Their question closes with this reply as its answer, and any decision of yours it was holding back goes back to them. Needs parentId: the Goal the question is on. The reply is sent to them as it is; to also ask something new, send that as its own ask."
75
+ )
63
76
  }).strict();
64
77
  var StartContactSchema = z2.object({
65
78
  asks: z2.array(AskInputSchema).min(1).describe("The questions to pose, one per object. An ask with a parentId is filed on that Goal as it is; only an ask naming no Goal is placed in the tree by Paigy."),
@@ -68,7 +81,7 @@ var StartContactSchema = z2.object({
68
81
  }).strict();
69
82
  var ContactSchema = z2.union([StartContactSchema, z2.object({ deliveryId: z2.string().uuid() }).strict()]);
70
83
  var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
71
- var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. A bundled ask is taken apart into its own cards by Paigy's one intake read, every one on the Goal the ask lands on; separate pieces of work are separate asks. An ask with no options that is not blocking is a report, and a report is an UPDATE, never a claim on them: it reaches them and is listed apart from what waits on them, so no answer is owed and none should be awaited. On a Goal whose report card is still open it is added to that card, with no new push; one that only says where your work stands while under way is recorded as the Goal's progress, not sent (the result says so). Send options (or waiting:'hard') when you actually need an answer. If the person is already on a call, your contact joins that call automatically \u2014 whatever channel you asked for, with no ring \u2014 and the Delivery it returns IS that call: reread it with contact({deliveryId}) to see everything answered on it so far, and contact again while it is live to add information or a further question to the same call.";
84
+ var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'select', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. A bundled ask is taken apart into its own cards by Paigy's one intake read, every one on the Goal the ask lands on; separate pieces of work are separate asks. An ask with no options that is not blocking is a report, and a report is an UPDATE, never a claim on them: it reaches them and is listed apart from what waits on them, so no answer is owed and none should be awaited. On a Goal whose report card is still open it is added to that card, with no new push; one that only says where your work stands while under way is recorded as the Goal's progress, not sent (the result says so). Send options (or waiting:'hard') when you actually need an answer. Each option's label must stand on its own \u2014 the person may see only the labels \u2014 so never a label that points into your text ('All three', 'Option 2', '1 and 3 only'); when the options are independent and they may want several, pass select:'many' and they get a checklist. If the person is already on a call, your contact joins that call automatically \u2014 whatever channel you asked for, with no ring \u2014 and the Delivery it returns IS that call: reread it with contact({deliveryId}) to see everything answered on it so far, and contact again while it is live to add information or a further question to the same call.";
72
85
  var CreateGoalSchema = z3.object({
73
86
  outcome: z3.string().trim().min(1).max(1e4),
74
87
  /** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
@@ -127,8 +140,8 @@ var GetGoalSchema = z3.object({
127
140
  * between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
128
141
  diagnose: z3.boolean().optional()
129
142
  }).strict();
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.";
131
- var UPDATE_GOAL_DESCRIPTION = `Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. state: done means the work was accomplished, and it can be reopened: when the person says it is not done ("it's not working"), set state: active on that same Goal instead of starting a new one; its parent reopens with it. state: done while children are still open records your part as done: the Goal waits and closes by itself when its last open child closes. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
143
+ 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. 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. 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.";
144
+ var UPDATE_GOAL_DESCRIPTION = `Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. A CONTACT ON THIS GOAL MOVES ITS REVISION: a question filed on a Goal is a change to it, so an update prepared before a contact and sent after it is refused as stale (409 goal_revision_conflict) \u2014 re-read the Goal, then write. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. state: done means the work was accomplished, and it can be reopened: when the person says it is not done ("it's not working"), set state: active on that same Goal instead of starting a new one; its parent reopens with it. state: done while children are still open records your part as done: the Goal waits and closes by itself when its last open child closes. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
132
145
  var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Another agent's Goal that has gone quiet for 3 days (check_replies lists them as stalledOthers) is taken over when you claim it, and becomes yours to finish or cancel. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
133
146
  var CHECK_REPLIES_DESCRIPTION = "What is waiting for you: your open Deliveries (a request the user started toward you, a handoff), one row each with its Goals, how many decisions are still open, and the newest words in brief; `assigned`, your Goals nobody has started yet, however they became yours (claim_goal({goalId}) starts one); and `review`, your Goals with something new on them \u2014 the user's answers and notes are Entries on the Goal, not Deliveries. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread a Delivery in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true clears the Goal from `review` and closes the Deliveries addressed to you on it.";
134
147
  var CheckRepliesSchema = z3.object({}).strict();
@@ -149,10 +162,10 @@ HOW TO ASK:
149
162
  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
163
  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
164
  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.
165
+ 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. STOP REREADING when a window comes back with nothing new said \u2014 they are not typing, the question stays open, and its answer reaches you on the Goal. Otherwise, yield only with a working listener or scheduled wakeup, and collect answers with \`check_replies\` or \`claim_goal\` on that wake.
153
166
  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.
167
+ 6. Read the Goal's conversation before asking. Never ask again what was answered or already shipped \u2014 and tell its states apart: \`answered\` carries the choice in \`answer\`, while \`replied\` means they said something and chose none of your options, so that question is still yours to settle and their words are the line beside it.
168
+ 7. A question carries its options. Without them it reaches the person as a bare title nobody can answer. Each option names its choice in full -- never \`All three\` or \`Option 2\` -- and options they may want several of are \`select: "many"\`, a checklist.
156
169
  8. A diagnosis says when, why and how it happens, then proposes one fix. Never options first.`;
157
170
  }
158
171
  function entryWords(entry) {
@@ -724,12 +737,13 @@ var UserSettingsSchema = z4.object({
724
737
  * they are an enum on a live column across every account, and the rename is a rename of
725
738
  * what people read (owner, 2026-09-30). */
726
739
  sessionMode: z4.enum(["default", "all_calls", "silent"]),
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(),
740
+ /** `silentPush` lived here until #2813 and is now GONE, field and column both. It was kept as an
741
+ * optional long after DateNight stopped reading it, on the theory that a phone on an older
742
+ * bundle PATCHing the whole settings object would be REFUSED for sending a key we had stopped
743
+ * wanting. That theory was wrong about this schema: these are plain `z.object`s with no
744
+ * `.strict()` anywhere in this file, and zod STRIPS unknown keys rather than rejecting them, so
745
+ * an old bundle's `silentPush` is accepted and ignored. Worth remembering before keeping the
746
+ * next dead field for the same reason. */
733
747
  /** Opt-in (default false) to using your content to improve Paigy and train models. */
734
748
  improveConsent: z4.boolean(),
735
749
  missedCall: MissedCallSchema.default("backoff_standard"),
@@ -863,7 +877,8 @@ var ConnectionSummarySchema = z4.object({
863
877
  });
864
878
  var LedgerItemSchema = z4.object({ id: z4.string(), parentId: z4.string(), title: z4.string(), createdAt: z4.string() });
865
879
  var AgentLedgerSchema = z4.object({
866
- agent: z4.object({ id: z4.string(), name: z4.string(), revokedAt: z4.string().nullable() }),
880
+ /** Null when the agent has not named itself yet — never a placeholder (owner, 2026-10-01). */
881
+ agent: z4.object({ id: z4.string(), name: z4.string().nullable(), revokedAt: z4.string().nullable() }),
867
882
  /** Its own questions you have not answered. */
868
883
  asks: z4.array(LedgerItemSchema),
869
884
  /** Its questions you answered that nobody acted on — still owed to somebody. */
@@ -1030,7 +1045,7 @@ var QueueItemSchema = z4.object({
1030
1045
  * the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
1031
1046
  * answer to a question, a voice note filed as work. Null when the only hands on it have been its
1032
1047
  * 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
1048
+ * 20261001030911 need not spell it — `work/list.ts`'s `yoursAt` keeps its other sources, so the
1034
1049
  * Work tab orders as it did before the migration rather than throwing on the missing key.
1035
1050
  *
1036
1051
  * It is a FACT, not a reconstruction: `latest` holds one Entry, so an agent's progress note a
@@ -1345,7 +1360,9 @@ var CallRecapSchema = z4.object({
1345
1360
  * summarized like a pre-made option is"), so the row scans like a chosen option and their
1346
1361
  * own words stay under it. Absent on every line stored before the read wrote them, and on
1347
1362
  * anything that is not an answer. */
1348
- lines: z4.array(z4.object({ entryId: z4.string().uuid(), words: z4.string(), headline: z4.string().optional() }))
1363
+ /** `about` is the request the line answered (its question), null for words that answered none —
1364
+ * the key the screen groups on, so one question is one row however many times it was answered. */
1365
+ lines: z4.array(z4.object({ entryId: z4.string().uuid(), words: z4.string(), headline: z4.string().optional(), about: z4.string().nullable().optional() }))
1349
1366
  })),
1350
1367
  unfiled: z4.array(z4.object({ lineId: z4.string().uuid(), words: z4.string(), atMs: z4.number() })),
1351
1368
  more: z4.object({ lines: z4.number(), entries: z4.number(), topics: z4.number() })
@@ -17,12 +17,14 @@ import {
17
17
  authToken,
18
18
  checkReplies,
19
19
  claimGoal,
20
+ claimSessionEnds,
20
21
  claimSessions,
21
22
  contact,
22
23
  createGoal,
23
24
  currentRepo,
24
25
  deleteToken,
25
26
  dismissTriage,
27
+ finishSessionEnd,
26
28
  getGoal,
27
29
  getTriage,
28
30
  hatch,
@@ -55,7 +57,7 @@ import {
55
57
  updateGoal,
56
58
  updateSlot,
57
59
  whoAmI
58
- } from "./chunk-2UEIJRRC.js";
60
+ } from "./chunk-KSXJCZ2L.js";
59
61
  export {
60
62
  AGENT_TOOLS,
61
63
  AGENT_TOOL_NAMES,
@@ -75,12 +77,14 @@ export {
75
77
  authToken,
76
78
  checkReplies,
77
79
  claimGoal,
80
+ claimSessionEnds,
78
81
  claimSessions,
79
82
  contact,
80
83
  createGoal,
81
84
  currentRepo,
82
85
  deleteToken,
83
86
  dismissTriage,
87
+ finishSessionEnd,
84
88
  getGoal,
85
89
  getTriage,
86
90
  hatch,
package/dist/enable.js CHANGED
@@ -3,9 +3,9 @@ import {
3
3
  PAIGY_TOOL_IDS,
4
4
  enablePaigyTools,
5
5
  installSessionListening
6
- } from "./chunk-B7SEHASI.js";
7
- import "./chunk-OW65TWMX.js";
8
- import "./chunk-2UEIJRRC.js";
6
+ } from "./chunk-2ED4GL4N.js";
7
+ import "./chunk-WDZ67U4G.js";
8
+ import "./chunk-KSXJCZ2L.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-LRN27BMO.js";
9
+ } from "./chunk-BFN2GJ42.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-B7SEHASI.js";
18
+ } from "./chunk-2ED4GL4N.js";
19
19
  import {
20
20
  decideListen,
21
21
  listenerAlive
22
- } from "./chunk-OW65TWMX.js";
22
+ } from "./chunk-WDZ67U4G.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-2UEIJRRC.js";
42
+ } from "./chunk-KSXJCZ2L.js";
43
43
 
44
44
  // src/index.ts
45
45
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/dist/listen.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  entryWords,
4
4
  removeListenMark,
5
5
  writeListenMark
6
- } from "./chunk-OW65TWMX.js";
6
+ } from "./chunk-WDZ67U4G.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-2UEIJRRC.js";
14
+ } from "./chunk-KSXJCZ2L.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-LRN27BMO.js";
6
+ } from "./chunk-BFN2GJ42.js";
7
7
  import {
8
8
  autoConfigureClients,
9
9
  claudeInstallHint,
10
10
  openBrowser
11
- } from "./chunk-B7SEHASI.js";
12
- import "./chunk-OW65TWMX.js";
11
+ } from "./chunk-2ED4GL4N.js";
12
+ import "./chunk-WDZ67U4G.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-2UEIJRRC.js";
23
+ } from "./chunk-KSXJCZ2L.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-2UEIJRRC.js";
4
+ } from "./chunk-KSXJCZ2L.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-VVU24KB2.js");
32
+ const { checkReplies } = await import("./dist-YAKVSYVQ.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-2UEIJRRC.js";
10
+ } from "./chunk-KSXJCZ2L.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.21",
3
+ "version": "0.40.22",
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",