@paigy/mcp 0.40.5 → 0.40.7

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.
package/README.md CHANGED
@@ -205,11 +205,11 @@ codex exec "Paigy goal $PAIGY_GOAL_ID on thread $PAIGY_THREAD_ID. claim_goal it
205
205
 
206
206
  ## Statusline (Claude Code)
207
207
 
208
- `paigy-statusline` prints a one-line connection status — the account's session
209
- mode and whether a phone is paired — for Claude Code's status bar:
208
+ `paigy-statusline` prints which Paigy identity this Claude Code session is, for the
209
+ status bar:
210
210
 
211
211
  ```
212
- paigy: all calls · phone ✓
212
+ paigy: Marlow
213
213
  ```
214
214
 
215
215
  Enable it in `~/.claude/settings.json`:
@@ -218,11 +218,9 @@ Enable it in `~/.claude/settings.json`:
218
218
  "statusLine": { "type": "command", "command": "npx -y -p @paigy/mcp@latest paigy-statusline" }
219
219
  ```
220
220
 
221
- It reads `GET /api/status` with the pairing token and caches the result in
222
- `~/.paigy/status.json` for 60 seconds, so the bar's constant refreshes never
223
- hammer the API; when offline it shows the last known state. `paigy: unpaired`
224
- means there's no token (or the pairing was revoked) — run `/paigy-onboard` to
225
- pair; `paigy: …` means the status isn't known yet (first run while offline).
221
+ It reads only `~/.paigy` — no network. `paigy: paired · session hatches on first use` means the
222
+ machine is paired and this session's identity is minted by its first tool call;
223
+ `paigy: unpaired` means there's no token — run `/paigy-onboard` to pair.
226
224
 
227
225
  ## Publishing (maintainers)
228
226
 
@@ -92,7 +92,8 @@ var UpdateGoalSchema = z2.object({
92
92
  children: z2.array(z2.object({ outcome: z2.string().trim().min(1).max(1e4), ownerParticipant: z2.string().trim().min(1), gate: z2.enum(["start", "finish"]).optional() }).strict()).optional(),
93
93
  state: z2.enum(["active", "done", "cancelled"]).optional(),
94
94
  progress: z2.string().trim().min(1).max(1e4).optional(),
95
- reviewed: z2.literal(true).optional()
95
+ reviewed: z2.literal(true).optional(),
96
+ dueAt: z2.string().datetime({ offset: true }).nullable().optional()
96
97
  }).strict().refine((v) => Object.keys(v).length > 0),
97
98
  reason: z2.string().trim().min(1).max(2e3),
98
99
  operationId: z2.string().uuid().optional()
@@ -100,10 +101,10 @@ var UpdateGoalSchema = z2.object({
100
101
  var UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: z2.string().uuid() }).strict();
101
102
  var ClaimGoalSchema = z2.object({ goalId: z2.string().uuid().optional() }).strict();
102
103
  var GetGoalSchema = z2.object({ goalId: z2.string().uuid() }).strict();
103
- var GET_GOAL_DESCRIPTION = "Read the current authorized Goal brief: state, owner, blockers, open decisions, progress, and the next operation. Foreign or sibling-owned Goals are not disclosed.";
104
- var UPDATE_GOAL_DESCRIPTION = "Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, and review acknowledgement are explicit; stale revisions are rejected. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. Returns the new revision and a prose summary.";
105
- var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns a Goal-scoped brief, current revision, blockers, and the next valid operation. Claiming creates or renews the execution lease.";
106
- var CHECK_REPLIES_DESCRIPTION = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals, the newest words in brief, accepted decisions and open decision needs, without the Entries. 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 it in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true closes the Deliveries addressed to you on that Goal. Your runnable and review-pending Goals come from claim_goal, not from here.";
104
+ 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), and `next`, the one step to take. Foreign or sibling-owned Goals are not disclosed.";
105
+ var UPDATE_GOAL_DESCRIPTION = "Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, and review acknowledgement are explicit; stale revisions are rejected. 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. Returns the Goal as get_goal reads it, at its new revision.";
106
+ var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
107
+ var CHECK_REPLIES_DESCRIPTION = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals, how many decisions are still open, and the newest words in brief. 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 it in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true closes the Deliveries addressed to you on that Goal. Your runnable and review-pending Goals come from claim_goal, not from here.";
107
108
  var CheckRepliesSchema = z2.object({}).strict();
108
109
  var GetThreadSchema = z2.object({
109
110
  parentId: z2.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -125,7 +126,7 @@ var AGENT_TOOLS = [
125
126
  ];
126
127
  var AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
127
128
  function serverInstructions(_opts) {
128
- return "On startup and after a wake, call check_replies for the open Deliveries addressed to you, and claim_goal for your runnable or review-pending Goal and its durable Entries and accepted decisions. get_goal rereads the same evidence without consuming it. Contact only for an existing Goal. Notifications return immediately: keep working and collect answers through claim_goal/get_goal. On stdio, Calls hold one bounded window; on hosted MCP, Calls return after one read; continue with contact({deliveryId}) to reread that exact Delivery. Evidence can repeat: reads do not acknowledge or hide it. Use update_goal to report progress and explicitly acknowledge review. Never infer ringing from an open Call Delivery. Soft waiting, multipart contact, and re-presentation are unsupported. In an interactive session, ask the user directly; use Paigy when they asked for it or are away.";
129
+ return "On startup and after a wake, call check_replies for the open Deliveries addressed to you, and claim_goal for your runnable or review-pending Goal and the conversation on it. get_goal rereads it without claiming. Every read ends in `next`, the one step to take. Contact only for an existing Goal. Notifications return immediately: keep working and collect answers through claim_goal/get_goal. On stdio, Calls hold one bounded window; on hosted MCP, Calls return after one read; continue with contact({deliveryId}) to reread that exact Delivery. Evidence can repeat: reads do not acknowledge or hide it. Use update_goal to report progress and explicitly acknowledge review. Never infer ringing from an open Call Delivery. Soft waiting, multipart contact, and re-presentation are unsupported. In an interactive session, ask the user directly; use Paigy when they asked for it or are away.";
129
130
  }
130
131
  function entryWords(entry) {
131
132
  const content = entry.content;
@@ -1060,25 +1061,6 @@ var DeliveryConfigSchema = z3.object({
1060
1061
  * carries credentials; only a `poll` registration can come back with null here. */
1061
1062
  realtime: z3.object({ url: z3.string(), anonKey: z3.string() }).nullable()
1062
1063
  });
1063
- var StatusSchema = z3.object({
1064
- name: z3.string(),
1065
- sessionMode: z3.enum(["default", "all_calls", "silent"]),
1066
- /** A phone is registered for push/ring (any push token on the account). */
1067
- phone: z3.boolean(),
1068
- /** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
1069
- * it never picked up. THE SAME NUMBER the harness's wake gate reads
1070
- * (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
1071
- * zero while the sweep sees one is two ideas of "waiting".
1072
- *
1073
- * Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
1074
- * that nobody spawned, so a terminal session only learns of work by asking. The harness
1075
- * used to paper over that by spawning a SECOND process on the identity; now it stands
1076
- * back, correctly, and the person sitting at the terminal is the one who can act. A
1077
- * coffee-beans request sat unread for three days.
1078
- *
1079
- * Optional: an older API sends no field, and the statusline then renders exactly as before. */
1080
- waiting: z3.number().int().nonnegative().optional()
1081
- });
1082
1064
  var EnvelopeRecipientSchema = z3.object({
1083
1065
  keyId: z3.string(),
1084
1066
  epk: z3.string(),
@@ -2,10 +2,10 @@ import {
2
2
  AGENT_TOOLS,
3
3
  mcpInputSchema,
4
4
  serverInstructions
5
- } from "./chunk-AOQ2CNMG.js";
5
+ } from "./chunk-4XG3YLGY.js";
6
6
  import {
7
7
  AGENT_NAME
8
- } from "./chunk-JSQ6MXBI.js";
8
+ } from "./chunk-UZPBDJ37.js";
9
9
 
10
10
  // src/toolset.ts
11
11
  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.";
@@ -10,7 +10,7 @@ import {
10
10
  saveToken,
11
11
  sleep,
12
12
  startE2ee
13
- } from "./chunk-JSQ6MXBI.js";
13
+ } from "./chunk-UZPBDJ37.js";
14
14
 
15
15
  // src/identity.ts
16
16
  var CLIENT_LABELS = {
@@ -3608,7 +3608,8 @@ var UpdateGoalSchema = z2.object({
3608
3608
  children: z2.array(z2.object({ outcome: z2.string().trim().min(1).max(1e4), ownerParticipant: z2.string().trim().min(1), gate: z2.enum(["start", "finish"]).optional() }).strict()).optional(),
3609
3609
  state: z2.enum(["active", "done", "cancelled"]).optional(),
3610
3610
  progress: z2.string().trim().min(1).max(1e4).optional(),
3611
- reviewed: z2.literal(true).optional()
3611
+ reviewed: z2.literal(true).optional(),
3612
+ dueAt: z2.string().datetime({ offset: true }).nullable().optional()
3612
3613
  }).strict().refine((v) => Object.keys(v).length > 0),
3613
3614
  reason: z2.string().trim().min(1).max(2e3),
3614
3615
  operationId: z2.string().uuid().optional()
@@ -3616,10 +3617,10 @@ var UpdateGoalSchema = z2.object({
3616
3617
  var UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: z2.string().uuid() }).strict();
3617
3618
  var ClaimGoalSchema = z2.object({ goalId: z2.string().uuid().optional() }).strict();
3618
3619
  var GetGoalSchema = z2.object({ goalId: z2.string().uuid() }).strict();
3619
- var GET_GOAL_DESCRIPTION = "Read the current authorized Goal brief: state, owner, blockers, open decisions, progress, and the next operation. Foreign or sibling-owned Goals are not disclosed.";
3620
- var UPDATE_GOAL_DESCRIPTION = "Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, and review acknowledgement are explicit; stale revisions are rejected. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. Returns the new revision and a prose summary.";
3621
- var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns a Goal-scoped brief, current revision, blockers, and the next valid operation. Claiming creates or renews the execution lease.";
3622
- var CHECK_REPLIES_DESCRIPTION = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals, the newest words in brief, accepted decisions and open decision needs, without the Entries. 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 it in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true closes the Deliveries addressed to you on that Goal. Your runnable and review-pending Goals come from claim_goal, not from here.";
3620
+ 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), and `next`, the one step to take. Foreign or sibling-owned Goals are not disclosed.";
3621
+ var UPDATE_GOAL_DESCRIPTION = "Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, and review acknowledgement are explicit; stale revisions are rejected. 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. Returns the Goal as get_goal reads it, at its new revision.";
3622
+ var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
3623
+ var CHECK_REPLIES_DESCRIPTION = "Your open Deliveries: every Notification or Call currently addressed to you \u2014 a request the user started toward you, an answer relayed to something you asked, a handoff \u2014 one row each: its Goals, how many decisions are still open, and the newest words in brief. 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 it in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true closes the Deliveries addressed to you on that Goal. Your runnable and review-pending Goals come from claim_goal, not from here.";
3623
3624
  var CheckRepliesSchema = z2.object({}).strict();
3624
3625
  var GetThreadSchema = z2.object({
3625
3626
  parentId: z2.string().describe("The Thread to read \u2014 the threadId a Delivery returned, or the parentId of a search hit.")
@@ -3640,6 +3641,21 @@ var AGENT_TOOLS = [
3640
3641
  { name: "update_goal", description: UPDATE_GOAL_DESCRIPTION, inputSchema: mcpInputSchema(UpdateGoalToolSchema) }
3641
3642
  ];
3642
3643
  var AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
3644
+ function entryWords(entry) {
3645
+ const content = entry.content;
3646
+ if (content && "sealed" in content) return "";
3647
+ const plain = content?.plain;
3648
+ if (typeof plain === "string") return plain;
3649
+ if (plain && typeof plain === "object") {
3650
+ const text = plain.text;
3651
+ if (typeof text === "string") return text;
3652
+ const title = plain.title;
3653
+ const description = plain.description;
3654
+ const parts = [title, ...Array.isArray(description) ? description : []].filter((v) => typeof v === "string");
3655
+ if (parts.length) return parts.join("\n\n");
3656
+ }
3657
+ return entry.sources.map((source) => source.text).join("\n");
3658
+ }
3643
3659
  var ContextSchema = z3.object({
3644
3660
  title: z3.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
3645
3661
  description: z3.array(z3.string().min(1)).describe(
@@ -4615,25 +4631,6 @@ var DeliveryConfigSchema = z3.object({
4615
4631
  * carries credentials; only a `poll` registration can come back with null here. */
4616
4632
  realtime: z3.object({ url: z3.string(), anonKey: z3.string() }).nullable()
4617
4633
  });
4618
- var StatusSchema = z3.object({
4619
- name: z3.string(),
4620
- sessionMode: z3.enum(["default", "all_calls", "silent"]),
4621
- /** A phone is registered for push/ring (any push token on the account). */
4622
- phone: z3.boolean(),
4623
- /** HOW MANY THINGS ARE WAITING ON THIS IDENTITY — replies it never collected and requests
4624
- * it never picked up. THE SAME NUMBER the harness's wake gate reads
4625
- * (`pendingSummary.unacknowledged`), from the same function, because a statusline saying
4626
- * zero while the sweep sees one is two ideas of "waiting".
4627
- *
4628
- * Why it is here at all (owner, 2026-09-07): nothing can interrupt an idle agent process
4629
- * that nobody spawned, so a terminal session only learns of work by asking. The harness
4630
- * used to paper over that by spawning a SECOND process on the identity; now it stands
4631
- * back, correctly, and the person sitting at the terminal is the one who can act. A
4632
- * coffee-beans request sat unread for three days.
4633
- *
4634
- * Optional: an older API sends no field, and the statusline then renders exactly as before. */
4635
- waiting: z3.number().int().nonnegative().optional()
4636
- });
4637
4634
  var EnvelopeRecipientSchema = z3.object({
4638
4635
  keyId: z3.string(),
4639
4636
  epk: z3.string(),
@@ -5271,9 +5268,7 @@ async function contact(input, opts = {}) {
5271
5268
  const read = async (signal2) => {
5272
5269
  const res = ensureAuthed(await send(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
5273
5270
  if (!res.ok) await fail("read_delivery", res);
5274
- const delivery = await res.json();
5275
- return delivery.kind === "notification" ? { ...delivery, message: `${delivery.message}
5276
- Inbox delivery is asynchronous; use claim_goal/get_goal to collect durable answers. Do not poll this Notification.` } : delivery;
5271
+ return await res.json();
5277
5272
  };
5278
5273
  const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
5279
5274
  if (opts.waits === false) return read(opts.signal);
@@ -5300,18 +5295,115 @@ async function checkReplies(opts = {}) {
5300
5295
  if (!res.ok) await fail("check_replies", res);
5301
5296
  return await res.json();
5302
5297
  }
5298
+ function compact(o) {
5299
+ return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0 && v !== null && v !== "" && !(Array.isArray(v) && v.length === 0)));
5300
+ }
5301
+ var who = (participant) => participant.startsWith("human:") ? "person" : participant.startsWith("agent:") ? "agent" : participant;
5302
+ var at = (iso) => {
5303
+ const t = Date.parse(iso);
5304
+ return Number.isFinite(t) ? new Date(t).toISOString().replace(/\.\d{3}Z$/, "Z") : iso;
5305
+ };
5306
+ function decided(result) {
5307
+ if ("sealed" in result) return "[encrypted]";
5308
+ switch (result.kind) {
5309
+ case "confirm":
5310
+ return result.approved ? "yes" : "no";
5311
+ case "one":
5312
+ return result.option.label;
5313
+ case "many":
5314
+ return result.options.map((o) => o.label).join(", ");
5315
+ case "rank":
5316
+ return result.options.map((o) => o.label).join(" > ");
5317
+ default:
5318
+ return void 0;
5319
+ }
5320
+ }
5321
+ function conversation(e) {
5322
+ const answers = new Map((e.answers ?? []).map((a) => [a.decisionNeedId, a]));
5323
+ return (e.entries ?? []).map((entry) => {
5324
+ const sealed = !!entry.content && "sealed" in entry.content;
5325
+ const plain = entry.content && "plain" in entry.content ? entry.content.plain : null;
5326
+ const options = plain && typeof plain === "object" && Array.isArray(plain.options) ? plain.options.map((o) => o.label).filter((l) => typeof l === "string") : [];
5327
+ const need = (e.decisionNeeds ?? []).find((n) => n.requestEntryId === entry.entryId);
5328
+ const answer = need ? answers.get(need.decisionNeedId) : void 0;
5329
+ const answered = answer ? decided(answer.result) : void 0;
5330
+ return compact({
5331
+ id: entry.entryId,
5332
+ from: who(entry.authorParticipant),
5333
+ at: at(entry.createdAt),
5334
+ said: sealed ? "[encrypted]" : entryWords(entry).trim(),
5335
+ re: entry.aboutId ?? void 0,
5336
+ options,
5337
+ decision: need ? compact({ id: need.decisionNeedId, state: need.state, answer: answered }) : void 0
5338
+ });
5339
+ });
5340
+ }
5341
+ function goalView(g) {
5342
+ if (!g.goalId) return compact({ state: g.state, next: g.message });
5343
+ return compact({
5344
+ goalId: g.goalId,
5345
+ revision: g.revision,
5346
+ state: g.state,
5347
+ outcome: g.outcome,
5348
+ progress: g.progress,
5349
+ reviewPending: g.reviewPending || void 0,
5350
+ owner: g.ownerParticipant,
5351
+ parentGoalId: g.parentGoalId,
5352
+ childGoalIds: g.childGoalIds,
5353
+ blockedBy: g.blockedDependencyGoalIds,
5354
+ dueAt: g.dueAt,
5355
+ leaseExpiresAt: g.leaseExpiresAt,
5356
+ contextThreadId: g.contextThreadId,
5357
+ conversation: conversation(g),
5358
+ next: g.message
5359
+ });
5360
+ }
5361
+ function deliveryView(d) {
5362
+ return compact({
5363
+ deliveryId: d.deliveryId,
5364
+ kind: d.kind,
5365
+ state: d.state,
5366
+ goalIds: d.goalIds,
5367
+ threadId: d.threadId,
5368
+ conversation: conversation(d),
5369
+ next: d.kind === "notification" ? "Answers land on the Goal: read them with claim_goal or get_goal. Do not poll this notification." : d.message
5370
+ });
5371
+ }
5372
+ function threadView(t) {
5373
+ return {
5374
+ parentId: t.parentId,
5375
+ turns: (t.turns ?? []).map((turn) => {
5376
+ const lines2 = turn.role === "user" ? [turn.text ?? ""] : [turn.title ?? "", ...turn.description ?? []];
5377
+ const said = lines2.map((l) => l.trim()).filter((l, i, all) => l && all.indexOf(l) === i).join("\n");
5378
+ return compact({ from: turn.role === "user" ? "person" : "agent", at: turn.at ? at(turn.at) : void 0, said });
5379
+ })
5380
+ };
5381
+ }
5382
+ function repliesView(r) {
5383
+ const deliveries = r.deliveries ?? [];
5384
+ return {
5385
+ deliveries: deliveries.map((d) => compact({
5386
+ deliveryId: d.deliveryId,
5387
+ kind: d.kind,
5388
+ goalIds: d.goalIds,
5389
+ openDecisions: d.decisionNeeds.filter((n) => n.state === "open").length || void 0,
5390
+ latest: "about" in d ? d.about : void 0
5391
+ })),
5392
+ next: deliveries.length ? "Act on a Goal with claim_goal; reread one Delivery in full with contact({deliveryId})." : "Nothing is waiting."
5393
+ };
5394
+ }
5303
5395
  async function runTool(name, args, opts) {
5304
5396
  const { waits, signal, ...client } = opts;
5305
5397
  const input = args ?? {};
5306
5398
  switch (name) {
5307
5399
  case "contact":
5308
- return contact(input, { ...client, signal, waits });
5400
+ return deliveryView(await contact(input, { ...client, signal, waits }));
5309
5401
  case "check_replies":
5310
5402
  CheckRepliesSchema.parse(input);
5311
- return checkReplies(client);
5403
+ return repliesView(await checkReplies(client));
5312
5404
  case "get_thread": {
5313
5405
  const { parentId } = GetThreadSchema.parse(input);
5314
- return getThread(parentId, client);
5406
+ return threadView(await getThread(parentId, client));
5315
5407
  }
5316
5408
  case "search_threads": {
5317
5409
  const { q } = SearchThreadsSchema.parse(input);
@@ -5323,15 +5415,15 @@ async function runTool(name, args, opts) {
5323
5415
  }
5324
5416
  case "claim_goal": {
5325
5417
  const { goalId } = ClaimGoalSchema.parse(input);
5326
- return claimGoal(goalId, client);
5418
+ return goalView(await claimGoal(goalId, client));
5327
5419
  }
5328
5420
  case "get_goal": {
5329
5421
  const { goalId } = GetGoalSchema.parse(input);
5330
- return getGoal(goalId, client);
5422
+ return goalView(await getGoal(goalId, client));
5331
5423
  }
5332
5424
  case "update_goal": {
5333
5425
  const { goalId, ...body } = UpdateGoalToolSchema.parse(input);
5334
- return updateGoal(goalId, body, client);
5426
+ return goalView(await updateGoal(goalId, body, client));
5335
5427
  }
5336
5428
  default:
5337
5429
  throw new Error(`Unknown tool: ${name}`);
@@ -5436,9 +5528,7 @@ async function subscribeWake(onNudge, opts = {}) {
5436
5528
  }
5437
5529
 
5438
5530
  export {
5439
- BACKEND_URL,
5440
5531
  isNetworkError,
5441
- reach,
5442
5532
  sessionSlot,
5443
5533
  AGENT_NAME,
5444
5534
  TOKEN_PATH,
package/dist/enable.js CHANGED
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  PAIGY_TOOL_IDS,
4
4
  enablePaigyTools
5
- } from "./chunk-4KT2EBPI.js";
6
- import "./chunk-AOQ2CNMG.js";
7
- import "./chunk-JSQ6MXBI.js";
5
+ } from "./chunk-CAKRIAGP.js";
6
+ import "./chunk-4XG3YLGY.js";
7
+ import "./chunk-UZPBDJ37.js";
8
8
 
9
9
  // src/enable.ts
10
10
  function main() {
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  resolvePairing,
6
6
  startPairing,
7
7
  suggestedAgentName
8
- } from "./chunk-WW5Y5D42.js";
8
+ } from "./chunk-RJV6U6U4.js";
9
9
  import {
10
10
  ENABLE_COMMAND,
11
11
  OnboardSchema,
@@ -16,8 +16,8 @@ import {
16
16
  autoConfigureClients,
17
17
  claudeInstallHint,
18
18
  paigyToolsAllowlisted
19
- } from "./chunk-4KT2EBPI.js";
20
- import "./chunk-AOQ2CNMG.js";
19
+ } from "./chunk-CAKRIAGP.js";
20
+ import "./chunk-4XG3YLGY.js";
21
21
  import {
22
22
  clearSurface,
23
23
  writeSurface
@@ -38,7 +38,7 @@ import {
38
38
  setIdentity,
39
39
  slotName,
40
40
  whoAmI
41
- } from "./chunk-JSQ6MXBI.js";
41
+ } from "./chunk-UZPBDJ37.js";
42
42
 
43
43
  // src/index.ts
44
44
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/dist/listen.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  entryWords
4
- } from "./chunk-AOQ2CNMG.js";
4
+ } from "./chunk-4XG3YLGY.js";
5
5
  import {
6
6
  checkReplies,
7
7
  contact,
8
8
  getGoal,
9
9
  subscribeWake
10
- } from "./chunk-JSQ6MXBI.js";
10
+ } from "./chunk-UZPBDJ37.js";
11
11
 
12
12
  // src/listen.ts
13
13
  import { spawn } from "child_process";
@@ -129,7 +129,7 @@ function launchEnv(waiting, reason, goalId) {
129
129
  put("PAIGY_DELIVERY_ID", lead.deliveryId);
130
130
  put("PAIGY_GOAL_ID", waiting.goal?.goalId ?? lead.goalIds[0]);
131
131
  put("PAIGY_CONTEXT_THREAD_ID", waiting.goal?.contextThreadId);
132
- put("PAIGY_TEXT", saidIn(lead) || lead.message);
132
+ put("PAIGY_TEXT", saidIn(lead) || ("about" in lead ? lead.about : ""));
133
133
  return env;
134
134
  }
135
135
  async function sweep(reason, goalId) {
package/dist/onboard.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  resolvePairing,
4
4
  startPairing,
5
5
  suggestedAgentName
6
- } from "./chunk-WW5Y5D42.js";
6
+ } from "./chunk-RJV6U6U4.js";
7
7
  import {
8
8
  autoConfigureClients,
9
9
  claudeInstallHint,
10
10
  openBrowser
11
- } from "./chunk-4KT2EBPI.js";
12
- import "./chunk-AOQ2CNMG.js";
11
+ } from "./chunk-CAKRIAGP.js";
12
+ import "./chunk-4XG3YLGY.js";
13
13
  import {
14
14
  AGENT_NAME,
15
15
  TOKEN_PATH,
@@ -20,7 +20,7 @@ import {
20
20
  saveToken,
21
21
  setIdentity,
22
22
  whoAmI
23
- } from "./chunk-JSQ6MXBI.js";
23
+ } from "./chunk-UZPBDJ37.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
  AGENT_NAME
4
- } from "./chunk-JSQ6MXBI.js";
4
+ } from "./chunk-UZPBDJ37.js";
5
5
 
6
6
  // src/slot.ts
7
7
  process.stdout.write(AGENT_NAME);
@@ -3,27 +3,15 @@ import {
3
3
  readSurface
4
4
  } from "./chunk-3SVNPJPN.js";
5
5
  import {
6
- BACKEND_URL,
7
6
  listSlots,
8
- reach,
9
7
  readToken,
10
8
  sessionSlot,
11
9
  slotName
12
- } from "./chunk-JSQ6MXBI.js";
10
+ } from "./chunk-UZPBDJ37.js";
13
11
 
14
12
  // src/statusline.ts
15
- import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
16
- import { homedir } from "os";
17
- import { join } from "path";
13
+ import { realpathSync } from "fs";
18
14
  import { fileURLToPath } from "url";
19
- var DIR = join(homedir(), ".paigy");
20
- var CACHE = join(DIR, "status.json");
21
- var TTL_MS = 6e4;
22
- var MODE_LABEL = {
23
- default: "default",
24
- all_calls: "all calls",
25
- silent: "silent"
26
- };
27
15
  function render(v) {
28
16
  switch (v.kind) {
29
17
  case "surface":
@@ -32,13 +20,8 @@ function render(v) {
32
20
  return "paigy: unpaired";
33
21
  case "device-only":
34
22
  return "paigy: paired \xB7 session hatches on first use";
35
- case "session": {
36
- const who = v.name ? `${v.name} \xB7 ` : "";
37
- if (!v.status) return `paigy: ${who}revoked`;
38
- const n = v.status.waiting ?? 0;
39
- const waiting = n > 0 ? ` \xB7 ${n} waiting` : "";
40
- return `paigy: ${who}${MODE_LABEL[v.status.sessionMode]} \xB7 ${v.status.phone ? "phone \u2713" : "no phone"}${waiting}`;
41
- }
23
+ case "session":
24
+ return `paigy: ${v.name ?? "paired"}`;
42
25
  }
43
26
  }
44
27
  async function stdinSessionId(input = process.stdin) {
@@ -64,27 +47,6 @@ async function stdinSessionId(input = process.stdin) {
64
47
  function resolveSlot(sid) {
65
48
  return process.env.PAIGY_AGENT || sessionSlot(sid);
66
49
  }
67
- function readCache() {
68
- try {
69
- return JSON.parse(readFileSync(CACHE, "utf8"));
70
- } catch {
71
- return null;
72
- }
73
- }
74
- async function fetchStatus(token) {
75
- const res = await reach(`${BACKEND_URL}/api/status`, {
76
- headers: { authorization: `Bearer ${token}` },
77
- signal: AbortSignal.timeout(2e3)
78
- // a statusline must render fast or not at all
79
- });
80
- if (res.status === 401) return null;
81
- if (res.status === 404) {
82
- if ((await res.text()).includes("not_found")) return null;
83
- throw new Error("status 404");
84
- }
85
- if (!res.ok) throw new Error(`status ${res.status}`);
86
- return await res.json();
87
- }
88
50
  async function main() {
89
51
  const surface = readSurface();
90
52
  if (surface) return render({ kind: "surface", ...surface });
@@ -93,17 +55,7 @@ async function main() {
93
55
  if (!token) {
94
56
  return render(listSlots().includes("Desktop") ? { kind: "device-only" } : { kind: "unpaired" });
95
57
  }
96
- const name = slotName(slot);
97
- const cached = readCache();
98
- if (cached && Date.now() - cached.at < TTL_MS) return render({ kind: "session", name, status: cached.status });
99
- try {
100
- const status = await fetchStatus(token);
101
- mkdirSync(DIR, { recursive: true });
102
- writeFileSync(CACHE, JSON.stringify({ at: Date.now(), status }));
103
- return render({ kind: "session", name, status });
104
- } catch {
105
- return cached ? render({ kind: "session", name, status: cached.status }) : "paigy: \u2026";
106
- }
58
+ return render({ kind: "session", name: slotName(slot) });
107
59
  }
108
60
  function isEntry() {
109
61
  const arg = process.argv[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.40.5",
3
+ "version": "0.40.7",
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",