@paigy/mcp 0.40.0 → 0.40.1

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.
@@ -10,7 +10,7 @@ import {
10
10
  saveToken,
11
11
  sleep,
12
12
  startE2ee
13
- } from "./chunk-CWGZVTPZ.js";
13
+ } from "./chunk-4UZAPBKK.js";
14
14
 
15
15
  // src/identity.ts
16
16
  var CLIENT_LABELS = {
@@ -1,22 +1,11 @@
1
1
  import {
2
- CHECK_REPLIES_DESCRIPTION,
3
- CONTACT_DESCRIPTION,
4
- CONTACT_SCHEMA,
5
- CREATE_GOAL_DESCRIPTION,
6
- CreateGoalSchema,
7
- HandoffSchema,
8
- SCHEDULE_CALLBACK_DESCRIPTION,
9
- SEARCH_THREADS_DESCRIPTION,
10
- SET_WORK_STATE_DESCRIPTION,
11
- ScheduleCallbackSchema,
12
- SetTaskStateSchema,
13
- SetWorkStateSchema,
2
+ AGENT_TOOLS,
14
3
  mcpInputSchema,
15
4
  serverInstructions
16
- } from "./chunk-DM6YPBQH.js";
5
+ } from "./chunk-3QUIB42M.js";
17
6
  import {
18
7
  AGENT_NAME
19
- } from "./chunk-CWGZVTPZ.js";
8
+ } from "./chunk-4UZAPBKK.js";
20
9
 
21
10
  // src/toolset.ts
22
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.";
@@ -24,8 +13,6 @@ var SERVER_INSTRUCTIONS = serverInstructions({ waits: true });
24
13
 
25
14
  // src/tools.ts
26
15
  import { z } from "zod";
27
- var UpdateGoalToolSchema = z.object({ goalId: z.string().uuid(), revision: z.number().int().positive(), changes: z.record(z.unknown()), reason: z.string().min(1) });
28
- var ClaimGoalSchema = z.object({ goalId: z.string().uuid() });
29
16
  var OnboardSchema = z.object({
30
17
  name: z.string().max(60).optional(),
31
18
  voice: z.string().max(40).optional(),
@@ -39,29 +26,7 @@ var PairSchema = z.object({
39
26
  name: z.string().min(1).max(60).optional().describe("Hatch path only: the name you choose for this identity. Pick your own \u2014 ONE or TWO words, the way you'd introduce yourself on a call (it is spoken aloud and shown in lists). 'Piper', 'Blue Heron' \u2014 never a sentence or a task description."),
40
27
  voice: z.string().optional().describe("Hatch path only: your voice on calls \u2014 one of rachel, george, jessica, brian, lily.")
41
28
  });
42
- var GetThreadSchema = z.object({
43
- parentId: z.string().describe("The thread to read \u2014 from a reply, request, or past notification.")
44
- });
45
- var SearchThreadsSchema = z.object({
46
- q: z.string().describe("What to look for \u2014 plain words or a phrase (e.g. 'the livekit timeout', 'deploy to prod').")
47
- });
48
- var SetTaskStateToolSchema = z.object({
49
- notificationId: z.string(),
50
- state: SetTaskStateSchema.shape.state
51
- });
52
- var AnswerCallerQuestionSchema = z.object({
53
- notificationId: z.string().describe("The notification whose call carried the caller's question \u2014 from the partial turn or the settled reply."),
54
- answer: z.string().min(1).max(1500).describe("The answer, as one or two short SPOKEN sentences \u2014 it may be read aloud on the live call.")
55
- });
56
- var TOOLS = [
57
- {
58
- // #575: THE attention verb — the one model-facing surface, every agent.
59
- // The retired names (notify/notify_user) stay callable as hidden aliases
60
- // for stale prompts and cached servers; see toolset.ts for the design.
61
- name: "contact",
62
- description: CONTACT_DESCRIPTION,
63
- inputSchema: CONTACT_SCHEMA
64
- },
29
+ var IDENTITY_TOOLS = [
65
30
  {
66
31
  // ONE rail for starting a session (#875). `pair`, the `paigy-mcp-onboard` CLI and
67
32
  // `enable_tools` were three doors into one flow, and "onboard" was the word people
@@ -79,55 +44,11 @@ var TOOLS = [
79
44
  name: "unpair",
80
45
  description: "Log out / unpair this agent from the user's Paigy account: revokes the token server-side (it stops working everywhere) and deletes the local ~/.paigy/token.json. Takes no arguments. After this, contact won't work until the user pairs again with the pair tool.",
81
46
  inputSchema: mcpInputSchema(z.object({}))
82
- },
83
- // `await_reply` is GONE (owner, 2026-09-06, hard cutover): the wait is contact's — it holds
84
- // the first window of a call it placed, and `contact({ wait: notificationId })` holds the
85
- // next. One tool, one loop; the reply-reading guidance moved into CONTACT_DESCRIPTION.
86
- {
87
- name: "check_replies",
88
- description: CHECK_REPLIES_DESCRIPTION,
89
- inputSchema: mcpInputSchema(z.object({}))
90
- },
91
- {
92
- name: "get_thread",
93
- description: "The chronological transcript of one Paigy conversation thread \u2014 every past ask, answer, and user request on it. Call this to REHYDRATE when you're resuming or being seeded: a check_replies request whose parentId you don't recognize means the user is continuing an old conversation with you, and one carrying a contextParentId means they want a past conversation (possibly with a DIFFERENT agent) as your starting context \u2014 in both cases call get_thread FIRST and read the turns as prior conversation you were part of, not as new input. Turns: { role:'agent', title, description[], answer }, { role:'user', text }, and context turns { role:'handoff'|'recap', title, description[] } \u2014 a handoff is a predecessor's brief for you; a recap SUMMARIZES everything before it (the transcript starts at the latest recap, so treat it as the base and the turns after it as what happened since). Oldest first, capped at the most recent 30.",
94
- inputSchema: mcpInputSchema(GetThreadSchema)
95
- },
96
- {
97
- name: "search_threads",
98
- description: SEARCH_THREADS_DESCRIPTION,
99
- inputSchema: mcpInputSchema(SearchThreadsSchema)
100
- },
101
- {
102
- name: "create_goal",
103
- description: CREATE_GOAL_DESCRIPTION,
104
- inputSchema: mcpInputSchema(CreateGoalSchema)
105
- },
106
- { name: "claim_goal", description: "Claim a ready Goal you own and begin work. Returns its active revision.", inputSchema: mcpInputSchema(ClaimGoalSchema) },
107
- { name: "update_goal", description: "Update an owned Goal at an exact revision; stale revisions are rejected.", inputSchema: mcpInputSchema(UpdateGoalToolSchema) },
108
- {
109
- name: "set_work_state",
110
- description: SET_WORK_STATE_DESCRIPTION,
111
- inputSchema: mcpInputSchema(SetWorkStateSchema)
112
- },
113
- {
114
- name: "answer_caller_question",
115
- description: "Answer a question the user asked DURING a live call, while they're still on it. When a partial turn or a settled reply carries a `question` intent aimed at you, answer it here immediately: if their call is still live, your answer is spoken to them on that same call (returns live: true). If the call already ended (live: false), send the answer as a threaded contact instead \u2014 never drop it. Short spoken sentences only; this may be read aloud.",
116
- inputSchema: mcpInputSchema(AnswerCallerQuestionSchema)
117
- },
118
- {
119
- name: "schedule_callback",
120
- description: SCHEDULE_CALLBACK_DESCRIPTION,
121
- inputSchema: mcpInputSchema(ScheduleCallbackSchema)
122
- },
123
- {
124
- name: "handoff",
125
- description: "Deposit your working context for a SUCCESSOR agent \u2014 what you did, what's left, links, gotchas \u2014 as one note on a thread ({ title, notes[] }). This does NOT ring the user or enter their inbox: it's context, not a question. The successor reads it back with get_thread. Pass `target` (a sibling connection's token id or agent name, SAME account only) to hand off DIRECTLY to that agent \u2014 the note is dispatched to it as a request it picks up. Pass `workId` to move that existing outcome and its pending replies to the successor without reminting it; a target is required in that form. Omit `target` to leave the thread for the user to hand off to an agent themselves in the app. Pass `parentId` to land the handoff on an existing conversation; omit it to mint a fresh thread. Returns { parentId }. Pass recap:true when the note SUMMARIZES the thread so far (for a successor OR for your own later session): a recap resets the rehydration window \u2014 get_thread returns the latest recap + only the turns after it. Write one whenever a thread has grown long and you're pausing, handing off, or nearing your context limit.",
126
- inputSchema: mcpInputSchema(HandoffSchema)
127
47
  }
128
48
  ];
49
+ var TOOLS = [...AGENT_TOOLS, ...IDENTITY_TOOLS];
129
50
  var TOOL_NAMES = TOOLS.map((t) => t.name);
130
- var HIDDEN_ALIASES = ["notify", "notify_user", "set_task_state"];
51
+ var HIDDEN_ALIASES = [];
131
52
 
132
53
  // src/clients.ts
133
54
  import { execFile } from "child_process";
@@ -290,14 +211,8 @@ function enablePaigyTools(scope = "user", cwd = process.cwd()) {
290
211
 
291
212
  export {
292
213
  SERVER_INSTRUCTIONS,
293
- UpdateGoalToolSchema,
294
- ClaimGoalSchema,
295
214
  OnboardSchema,
296
215
  PairSchema,
297
- GetThreadSchema,
298
- SearchThreadsSchema,
299
- SetTaskStateToolSchema,
300
- AnswerCallerQuestionSchema,
301
216
  TOOLS,
302
217
  openBrowser,
303
218
  autoConfigureClients,
package/dist/enable.js CHANGED
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  PAIGY_TOOL_IDS,
4
4
  enablePaigyTools
5
- } from "./chunk-FYH6FW2D.js";
6
- import "./chunk-DM6YPBQH.js";
7
- import "./chunk-CWGZVTPZ.js";
5
+ } from "./chunk-TLASZUJD.js";
6
+ import "./chunk-3QUIB42M.js";
7
+ import "./chunk-4UZAPBKK.js";
8
8
 
9
9
  // src/enable.ts
10
10
  function main() {
package/dist/index.js CHANGED
@@ -1,74 +1,44 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- awaitMessage,
4
- contactMessage,
5
- waitNow
6
- } from "./chunk-2SWUY5WK.js";
7
2
  import {
8
3
  clearPairing,
9
4
  joinBackgroundPair,
10
5
  resolvePairing,
11
6
  startPairing,
12
7
  suggestedAgentName
13
- } from "./chunk-BOLKVO6W.js";
8
+ } from "./chunk-CXABA27N.js";
14
9
  import {
15
- AnswerCallerQuestionSchema,
16
- ClaimGoalSchema,
17
10
  ENABLE_COMMAND,
18
- GetThreadSchema,
19
11
  OnboardSchema,
20
12
  PAIGY_TOOL_IDS,
21
13
  PairSchema,
22
14
  SERVER_INSTRUCTIONS,
23
- SearchThreadsSchema,
24
- SetTaskStateToolSchema,
25
15
  TOOLS,
26
- UpdateGoalToolSchema,
27
16
  autoConfigureClients,
28
17
  claudeInstallHint,
29
18
  paigyToolsAllowlisted
30
- } from "./chunk-FYH6FW2D.js";
31
- import {
32
- CreateGoalSchema,
33
- HandoffSchema
34
- } from "./chunk-DM6YPBQH.js";
19
+ } from "./chunk-TLASZUJD.js";
20
+ import "./chunk-3QUIB42M.js";
35
21
  import {
36
22
  clearSurface,
37
23
  writeSurface
38
24
  } from "./chunk-3SVNPJPN.js";
39
25
  import {
40
26
  AGENT_NAME,
41
- NotifyRequestSchema,
42
- ScheduleCallbackSchema,
43
- SetWorkStateSchema,
44
27
  UnpairedError,
45
- answerCallerQuestion,
46
- awaitReply2,
47
- checkReplies,
48
- claimGoal,
49
- contactParentId,
50
- createGoal,
51
28
  deleteKeyFile,
52
29
  deleteToken,
53
- getThread,
54
- handoff,
55
30
  hatch,
56
31
  heartbeat,
57
32
  listSlots,
58
33
  overrideToken,
59
34
  readToken,
60
35
  revokeToken,
36
+ runTool,
61
37
  saveToken,
62
- scheduleCallback,
63
- searchThreads,
64
38
  setIdentity,
65
- setTaskState,
66
- setWorkState,
67
39
  slotName,
68
- submitNotification,
69
- updateGoal,
70
40
  whoAmI
71
- } from "./chunk-CWGZVTPZ.js";
41
+ } from "./chunk-4UZAPBKK.js";
72
42
 
73
43
  // src/index.ts
74
44
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -77,36 +47,8 @@ import {
77
47
  CallToolRequestSchema,
78
48
  ListToolsRequestSchema
79
49
  } from "@modelcontextprotocol/sdk/types.js";
80
- import { execSync } from "child_process";
81
50
  import qrcode from "qrcode-generator";
82
51
  var JOIN_CAP_MS = 45e3;
83
- function fixAndRetry(err) {
84
- const msg = String(err?.message ?? err);
85
- const m = msg.match(/^notify failed: 422 (\{[\s\S]*\})$/);
86
- if (!m) return null;
87
- try {
88
- const body = JSON.parse(m[1]);
89
- return body.error === "fix_and_retry" ? { error: "fix_and_retry", problems: body.problems ?? [] } : null;
90
- } catch {
91
- return null;
92
- }
93
- }
94
- function detectGit() {
95
- const run = (cmd) => {
96
- try {
97
- return execSync(cmd, { stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" }).trim();
98
- } catch {
99
- return "";
100
- }
101
- };
102
- const remote = run("git remote get-url origin");
103
- const match = remote.match(/[:/]([^/:]+\/[^/]+?)(?:\.git)?$/);
104
- const branch = run("git rev-parse --abbrev-ref HEAD");
105
- return {
106
- repo: match ? match[1] : void 0,
107
- branch: branch && branch !== "HEAD" ? branch : void 0
108
- };
109
- }
110
52
  function pairedResult(token, sas, note, e2ee) {
111
53
  clearSurface();
112
54
  const base = {
@@ -379,97 +321,13 @@ async function handleTool(request, signal) {
379
321
  }]
380
322
  };
381
323
  }
382
- // `contact` (#575) is the one LISTED name; `notify` (#496) and `notify_user`
383
- // (the original) are HIDDEN aliases — unlisted but accepted, so stale prompts,
384
- // hooks, and cached 0.24 servers' habits keep working. Same handler, same wire.
385
- case "contact":
386
- case "notify":
387
- case "notify_user": {
388
- const args = request.params.arguments ?? {};
389
- if (typeof args.wait === "string" && args.ask === void 0) {
390
- const item = await awaitReply2(args.wait, { ...signal ? { signal } : {} });
391
- return { content: [{ type: "text", text: JSON.stringify({ ...item, message: awaitMessage(item) }) }] };
392
- }
393
- const parsed = NotifyRequestSchema.parse(request.params.arguments);
394
- const git = detectGit();
395
- const enriched = {
396
- ...parsed,
397
- repo: parsed.repo ?? git.repo,
398
- branch: parsed.branch ?? git.branch,
399
- // THREAD-SCOPED IDENTITY (stage 3, identity-ownership-design.md). A worker subagent
400
- // shares the parent's identity/slot, so its requests would otherwise scatter across
401
- // fresh threads and mix with a sibling's. Default an unthreaded contact to THIS
402
- // subagent's own thread so its requests group and siblings never collide. An explicit
403
- // parentId (continuing a specific conversation) always wins; a top-level parent session
404
- // is left untouched (undefined ⇒ the server mints a fresh thread, exactly as today).
405
- parentId: contactParentId(parsed.parentId)
406
- };
407
- let result;
408
- try {
409
- result = await submitNotification(enriched);
410
- } catch (err) {
411
- const fixable = fixAndRetry(err);
412
- if (fixable) return { isError: true, content: [{ type: "text", text: JSON.stringify(fixable) }] };
413
- throw err;
414
- }
415
- const sentAs = slotName(AGENT_NAME);
416
- const shown = waitNow(result) ? await (async () => {
417
- const wait = await awaitReply2(result.notificationId, { ...signal ? { signal } : {} });
418
- return { ...result, wait, message: awaitMessage(wait) };
419
- })() : { ...result, message: contactMessage(result) };
420
- return { content: [{ type: "text", text: JSON.stringify(sentAs ? { ...shown, sentAs } : shown) }] };
421
- }
422
- case "check_replies": {
423
- const result = await checkReplies();
424
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
425
- }
426
- case "get_thread": {
427
- const { parentId } = GetThreadSchema.parse(request.params.arguments);
428
- return { content: [{ type: "text", text: JSON.stringify(await getThread(parentId)) }] };
429
- }
430
- case "search_threads": {
431
- const { q } = SearchThreadsSchema.parse(request.params.arguments);
432
- return { content: [{ type: "text", text: JSON.stringify(await searchThreads(q)) }] };
433
- }
434
- case "answer_caller_question": {
435
- const { notificationId, answer } = AnswerCallerQuestionSchema.parse(request.params.arguments);
436
- const result = await answerCallerQuestion(notificationId, answer);
437
- return { content: [{ type: "text", text: JSON.stringify({
438
- ...result,
439
- ...result.live ? {} : { note: "The call already ended \u2014 send this as a threaded contact instead so the answer still reaches them." }
440
- }) }] };
441
- }
442
- case "set_task_state": {
443
- const { notificationId, state } = SetTaskStateToolSchema.parse(request.params.arguments);
444
- const result = await setTaskState(notificationId, state);
445
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
446
- }
447
- case "set_work_state": {
448
- const result = await setWorkState(SetWorkStateSchema.parse(request.params.arguments));
449
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
450
- }
451
- case "create_goal": {
452
- const result = await createGoal(CreateGoalSchema.parse(request.params.arguments));
453
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
454
- }
455
- case "claim_goal": {
456
- const { goalId } = ClaimGoalSchema.parse(request.params.arguments);
457
- return { content: [{ type: "text", text: JSON.stringify(await claimGoal(goalId)) }] };
458
- }
459
- case "update_goal": {
460
- const { goalId, ...input } = UpdateGoalToolSchema.parse(request.params.arguments);
461
- return { content: [{ type: "text", text: JSON.stringify(await updateGoal(goalId, input)) }] };
462
- }
463
- case "schedule_callback": {
464
- const result = await scheduleCallback(ScheduleCallbackSchema.parse(request.params.arguments));
465
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
466
- }
467
- case "handoff": {
468
- const result = await handoff(HandoffSchema.parse(request.params.arguments));
469
- return { content: [{ type: "text", text: JSON.stringify(result) }] };
324
+ default: {
325
+ const name = request.params.name;
326
+ const result = await runTool(name, request.params.arguments ?? {}, { waits: true, signal });
327
+ const sentAs = name === "contact" ? slotName(AGENT_NAME) : null;
328
+ const shaped = sentAs && result && typeof result === "object" ? { ...result, sentAs } : result;
329
+ return { content: [{ type: "text", text: JSON.stringify(shaped) }] };
470
330
  }
471
- default:
472
- throw new Error(`Unknown tool: ${request.params.name}`);
473
331
  }
474
332
  }
475
333
  var beatDown = false;
package/dist/listen.js CHANGED
@@ -1,18 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- answerText
4
- } from "./chunk-2SWUY5WK.js";
5
2
  import {
6
3
  WAKE_EVENT,
7
4
  wakeChannel
8
- } from "./chunk-DM6YPBQH.js";
5
+ } from "./chunk-3QUIB42M.js";
9
6
  import {
10
7
  ackReply,
11
8
  authToken,
12
9
  decodeReply,
13
10
  registerDelivery,
14
11
  sweepLeased
15
- } from "./chunk-CWGZVTPZ.js";
12
+ } from "./chunk-4UZAPBKK.js";
16
13
 
17
14
  // src/listen.ts
18
15
  import { createClient } from "@supabase/supabase-js";
@@ -117,6 +114,29 @@ function uninstallService() {
117
114
  return unit;
118
115
  }
119
116
 
117
+ // src/message.ts
118
+ function answerText(answer) {
119
+ switch (answer.kind) {
120
+ case "text":
121
+ return answer.text;
122
+ case "option":
123
+ return answer.label ?? answer.optionId;
124
+ case "multi":
125
+ case "ranked":
126
+ return (answer.labels ?? answer.optionIds).join(", ");
127
+ case "confirm":
128
+ return answer.approved ? "approved" : "declined";
129
+ case "clarify":
130
+ return answer.chunks.join(" ");
131
+ case "turns":
132
+ return answer.turns.map((turn) => turn.reply).join(" ");
133
+ case "precedent":
134
+ return answer.answer;
135
+ case "ignored":
136
+ return "";
137
+ }
138
+ }
139
+
120
140
  // src/listen.ts
121
141
  var emit = (obj) => void process.stdout.write(JSON.stringify(obj) + "\n");
122
142
  function launchEnv(work, reason) {
package/dist/onboard.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  resolvePairing,
4
4
  startPairing,
5
5
  suggestedAgentName
6
- } from "./chunk-BOLKVO6W.js";
6
+ } from "./chunk-CXABA27N.js";
7
7
  import {
8
8
  autoConfigureClients,
9
9
  claudeInstallHint,
10
10
  openBrowser
11
- } from "./chunk-FYH6FW2D.js";
12
- import "./chunk-DM6YPBQH.js";
11
+ } from "./chunk-TLASZUJD.js";
12
+ import "./chunk-3QUIB42M.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-CWGZVTPZ.js";
23
+ } from "./chunk-4UZAPBKK.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-CWGZVTPZ.js";
4
+ } from "./chunk-4UZAPBKK.js";
5
5
 
6
6
  // src/slot.ts
7
7
  process.stdout.write(AGENT_NAME);
@@ -9,7 +9,7 @@ import {
9
9
  readToken,
10
10
  sessionSlot,
11
11
  slotName
12
- } from "./chunk-CWGZVTPZ.js";
12
+ } from "./chunk-4UZAPBKK.js";
13
13
 
14
14
  // src/statusline.ts
15
15
  import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
@@ -35,7 +35,9 @@ function render(v) {
35
35
  case "session": {
36
36
  const who = v.name ? `${v.name} \xB7 ` : "";
37
37
  if (!v.status) return `paigy: ${who}revoked`;
38
- return `paigy: ${who}${MODE_LABEL[v.status.sessionMode]} \xB7 ${v.status.phone ? "phone \u2713" : "no phone"}`;
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}`;
39
41
  }
40
42
  }
41
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.40.0",
3
+ "version": "0.40.1",
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",
@@ -24,13 +24,6 @@
24
24
  "url": "git+https://github.com/mauurda/paigy.git",
25
25
  "directory": "apps/mcp"
26
26
  },
27
- "scripts": {
28
- "build": "tsup",
29
- "dev": "tsup --watch",
30
- "typecheck": "tsc --noEmit",
31
- "test": "vitest run",
32
- "prepublishOnly": "pnpm --filter @paigy/schema build && pnpm --filter @paigy/crypto build && pnpm --filter @paigy/sdk build && pnpm build"
33
- },
34
27
  "dependencies": {
35
28
  "@modelcontextprotocol/sdk": "^1.0.4",
36
29
  "@supabase/supabase-js": "^2.47.10",
@@ -40,13 +33,19 @@
40
33
  "zod-to-json-schema": "^3.24.1"
41
34
  },
42
35
  "devDependencies": {
43
- "@paigy/crypto": "workspace:*",
44
- "@paigy/schema": "workspace:*",
45
- "@paigy/sdk": "workspace:*",
46
36
  "@types/node": "^22.0.0",
47
37
  "@types/qrcode-generator": "^1.0.6",
48
38
  "tsup": "^8.3.5",
49
39
  "typescript": "^5.7.2",
50
- "vitest": "^2.1.8"
40
+ "vitest": "^2.1.8",
41
+ "@paigy/crypto": "0.0.0",
42
+ "@paigy/schema": "0.0.0",
43
+ "@paigy/sdk": "0.2.1"
44
+ },
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "dev": "tsup --watch",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run"
51
50
  }
52
- }
51
+ }
@@ -1,57 +0,0 @@
1
- // src/message.ts
2
- function waitNow(result) {
3
- const units = result.plan?.units ?? [];
4
- return units.some((unit) => unit.level === "call") || units.length > 0 && units.every((unit) => unit.settled);
5
- }
6
- function contactMessage(result) {
7
- return result.message;
8
- }
9
- function answerText(answer) {
10
- switch (answer.kind) {
11
- case "text":
12
- return answer.text;
13
- case "option":
14
- return answer.label ?? answer.optionId;
15
- case "multi":
16
- case "ranked":
17
- return (answer.labels ?? answer.optionIds).join(", ");
18
- case "confirm":
19
- return answer.approved ? "approved" : "declined";
20
- case "clarify":
21
- return answer.chunks.join(" ");
22
- case "turns":
23
- return answer.turns.map((turn) => turn.reply).join(" ");
24
- case "precedent":
25
- return answer.answer;
26
- case "ignored":
27
- return "";
28
- }
29
- }
30
- function awaitMessage(item) {
31
- switch (item.type) {
32
- case "partial":
33
- return `The user is on the call. This is provisional: ${item.turn.reply}`;
34
- case "idle":
35
- return item.inFlight === false ? "No call is live for this ask \u2014 it was delivered as a message. The reply arrives through check_replies (or your wake); do not wait on it again." : "Still waiting for the user. Call contact again with only { wait: notificationId } to keep waiting.";
36
- case "remind":
37
- return `The user asked to be reminded in ${item.remindInSeconds} seconds. Schedule a wake-up, then call contact again with only { wait: notificationId }.`;
38
- case "superseded":
39
- return "This ask was superseded. Stop waiting on it and re-orient on the thread.";
40
- case "reply": {
41
- const ended = item.transcript !== void 0 || item.answer.kind === "turns" ? "The call ended. " : "";
42
- if (item.answer.kind === "option" || item.answer.kind === "multi" || item.answer.kind === "ranked") {
43
- return `${ended}The user chose ${answerText(item.answer)}.`;
44
- }
45
- if (item.answer.kind === "confirm") return `${ended}The user ${answerText(item.answer)}.`;
46
- if (item.answer.kind === "ignored") return `${ended}The user did not answer.`;
47
- return `${ended}The user replied: ${answerText(item.answer)}`;
48
- }
49
- }
50
- }
51
-
52
- export {
53
- waitNow,
54
- contactMessage,
55
- answerText,
56
- awaitMessage
57
- };