@paigy/mcp 0.30.0 → 0.31.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.
- package/dist/{chunk-3QSGCROD.js → chunk-2WPQJYJH.js} +55 -0
- package/dist/{chunk-57N3NFYG.js → chunk-ENE65M5Y.js} +1 -1
- package/dist/{chunk-KMPAFUT6.js → chunk-PYORUJ7Q.js} +46 -0
- package/dist/index.js +21 -4
- package/dist/listen.js +2 -2
- package/dist/onboard.js +2 -2
- package/dist/statusline.js +1 -1
- package/package.json +1 -1
|
@@ -2490,6 +2490,14 @@ var NotifyRequestSchema = z.object({
|
|
|
2490
2490
|
waiting: z.enum(["none", "soft", "hard"]).optional().describe(
|
|
2491
2491
|
"With `ask`: what happens to your work while you wait. 'none' = you're just informing the user. 'soft' = you'd like an answer but can keep working. 'hard' = you are stopped until they answer (reaches them urgently and escalates to a real phone call if unanswered). Replaces urgencyHint + blocking \u2014 send this one field."
|
|
2492
2492
|
),
|
|
2493
|
+
/** Δ9b (#895): HOLD this claim so the sender can correct the plan before anyone is
|
|
2494
|
+
* interrupted. Opt-in per claim, because the fail-open is a minute-granularity cron —
|
|
2495
|
+
* holding by default would charge every quiet claim that minute before any agent could
|
|
2496
|
+
* correct anything. Ignored for `waiting: 'hard'`: a blocking ask rings on what we have,
|
|
2497
|
+
* and the enrichment can still land mid-call (#781 re-plans the unspoken tail). */
|
|
2498
|
+
confirm: z.boolean().optional().describe(
|
|
2499
|
+
"Hold this one so you can correct the plan before the user is interrupted. The response comes back with `held: true` and the plan; POST the confirm route to release it (with options/visuals/urgency corrections, or nothing at all). If you never do, it is announced anyway a couple of minutes later. Ignored when waiting is 'hard'."
|
|
2500
|
+
),
|
|
2493
2501
|
/** #575: a RELAY of the user's explicitly stated preference, never the agent's
|
|
2494
2502
|
* choice. Outranks waiting in both directions: 'call' rings even for a
|
|
2495
2503
|
* waiting:'none' "call me when it's done"; 'message' never rings even for
|
|
@@ -2776,6 +2784,25 @@ var NotifyResponseSchema = z.object({
|
|
|
2776
2784
|
answer: UserAnswerSchema.optional(),
|
|
2777
2785
|
answeredAt: z.string().datetime().optional()
|
|
2778
2786
|
});
|
|
2787
|
+
var NotifyPlanUnitSchema = z.object({
|
|
2788
|
+
notificationId: z.string(),
|
|
2789
|
+
/** The unit's own heading, so the agent can tell which of its paragraphs this became. */
|
|
2790
|
+
title: z.string(),
|
|
2791
|
+
/** How loudly this unit was arbitrated to arrive — per unit, which is the point of units. */
|
|
2792
|
+
level: NotifyLevelSchema,
|
|
2793
|
+
/** Answered from something the user already decided: nobody is interrupted, and a trail card
|
|
2794
|
+
* says so. The agent should not wait on this one. */
|
|
2795
|
+
settled: z.literal(true).optional(),
|
|
2796
|
+
/** What this unit would need to be answerable and does not carry (#894). A PROPOSAL to the
|
|
2797
|
+
* agent — nothing here changed the ask, and ignoring it costs nothing. */
|
|
2798
|
+
needs: z.array(z.enum(["options", "visuals"])).optional()
|
|
2799
|
+
});
|
|
2800
|
+
var NotifyPlanSchema = z.object({
|
|
2801
|
+
units: z.array(NotifyPlanUnitSchema),
|
|
2802
|
+
/** Which unit is this arrival's ONE interruption (units-design.md D23). Absent means nobody
|
|
2803
|
+
* was interrupted — every unit was either settled or quiet enough to sit in the inbox. */
|
|
2804
|
+
speaks: z.string().optional()
|
|
2805
|
+
});
|
|
2779
2806
|
var UserResponseSchema = z.object({
|
|
2780
2807
|
requestId: z.string(),
|
|
2781
2808
|
answer: UserAnswerSchema,
|
|
@@ -2830,6 +2857,20 @@ var InboxItemSchema = z.object({
|
|
|
2830
2857
|
/** The conversation thread + connection this item lives on. Present on the replied
|
|
2831
2858
|
* detail — they power History's "Continue" / "New session from this" (#57/#251). */
|
|
2832
2859
|
parentId: z.string().optional(),
|
|
2860
|
+
/** THE ARRIVAL this row is one unit of (`notifications.ask_id` → `asks`). A claim is one
|
|
2861
|
+
* arrival and its units are N rows of it, so this — not `parentId` — is what makes a
|
|
2862
|
+
* multi-part notification one thing on screen. The thread is the whole CONVERSATION: it
|
|
2863
|
+
* accumulates every message an agent ever sent, so grouping by it renders a day of
|
|
2864
|
+
* unrelated updates as a single "12-part request". Absent on rows written before the
|
|
2865
|
+
* `asks` table, and on anything that never went through `notify` — both fall back to the
|
|
2866
|
+
* thread, which is what the client did for all rows until now. */
|
|
2867
|
+
askId: z.string().optional(),
|
|
2868
|
+
/** WHERE this unit sat in the message it was cut from (`notifications.seq`). The batch
|
|
2869
|
+
* shares one `created_at` to the microsecond, so without it the author's order is
|
|
2870
|
+
* unrecoverable client-side — a four-paragraph briefing rendered opening-paragraph-last
|
|
2871
|
+
* (live 2026-08-10, D35). The API already orders by it; this lets a reader that
|
|
2872
|
+
* re-sorts (grouping, filtering) put an arrival back in the order it was written. */
|
|
2873
|
+
seq: z.number().int().optional(),
|
|
2833
2874
|
tokenId: z.string().optional(),
|
|
2834
2875
|
status: NotifyStatusSchema,
|
|
2835
2876
|
context: ContextSchema,
|
|
@@ -3279,6 +3320,11 @@ var DeviceTokenSchema = z.object({
|
|
|
3279
3320
|
/** The token's server-side id — the face's COLOUR anchor, and the only seed ingredient
|
|
3280
3321
|
* that survives a rename. Cached by the host's identity beat. */
|
|
3281
3322
|
token_id: z.string().nullable().optional(),
|
|
3323
|
+
/** WHERE this identity works — the folder a wake should land it in. Written by the host
|
|
3324
|
+
* at spawn and by `paigy-harness handoff` from a live terminal. Without it every wake
|
|
3325
|
+
* landed in the FIRST granted workspace and the agent rediscovered its own repo from
|
|
3326
|
+
* the thread each time (host.ts, live catch 2026-08-06 — prompt-papered until now). */
|
|
3327
|
+
workspace: z.string().nullable().optional(),
|
|
3282
3328
|
phone_reveal: PairingRevealSchema.nullable().optional(),
|
|
3283
3329
|
// present once the phone reveals
|
|
3284
3330
|
uik_pub: z.string().nullable().optional()
|
|
@@ -4067,6 +4113,14 @@ async function setIdentity(patch, opts = {}) {
|
|
|
4067
4113
|
if (!res.ok) throw new Error(`set_identity failed: ${res.status} ${await res.text()}`);
|
|
4068
4114
|
return await res.json();
|
|
4069
4115
|
}
|
|
4116
|
+
async function heartbeat(runtime, opts = {}) {
|
|
4117
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/presence`, {
|
|
4118
|
+
method: "POST",
|
|
4119
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
4120
|
+
...runtime !== void 0 ? { body: JSON.stringify({ runtime }) } : {}
|
|
4121
|
+
}));
|
|
4122
|
+
if (!res.ok) throw new Error(`heartbeat failed: ${res.status}`);
|
|
4123
|
+
}
|
|
4070
4124
|
async function setTaskState(notificationId, state, opts = {}) {
|
|
4071
4125
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/notify/${notificationId}/state`, {
|
|
4072
4126
|
method: "PATCH",
|
|
@@ -4143,6 +4197,7 @@ export {
|
|
|
4143
4197
|
hatch,
|
|
4144
4198
|
whoAmI,
|
|
4145
4199
|
setIdentity,
|
|
4200
|
+
heartbeat,
|
|
4146
4201
|
setTaskState,
|
|
4147
4202
|
registerDelivery,
|
|
4148
4203
|
scheduleCallback,
|
|
@@ -151,6 +151,14 @@ var NotifyRequestSchema = z.object({
|
|
|
151
151
|
waiting: z.enum(["none", "soft", "hard"]).optional().describe(
|
|
152
152
|
"With `ask`: what happens to your work while you wait. 'none' = you're just informing the user. 'soft' = you'd like an answer but can keep working. 'hard' = you are stopped until they answer (reaches them urgently and escalates to a real phone call if unanswered). Replaces urgencyHint + blocking \u2014 send this one field."
|
|
153
153
|
),
|
|
154
|
+
/** Δ9b (#895): HOLD this claim so the sender can correct the plan before anyone is
|
|
155
|
+
* interrupted. Opt-in per claim, because the fail-open is a minute-granularity cron —
|
|
156
|
+
* holding by default would charge every quiet claim that minute before any agent could
|
|
157
|
+
* correct anything. Ignored for `waiting: 'hard'`: a blocking ask rings on what we have,
|
|
158
|
+
* and the enrichment can still land mid-call (#781 re-plans the unspoken tail). */
|
|
159
|
+
confirm: z.boolean().optional().describe(
|
|
160
|
+
"Hold this one so you can correct the plan before the user is interrupted. The response comes back with `held: true` and the plan; POST the confirm route to release it (with options/visuals/urgency corrections, or nothing at all). If you never do, it is announced anyway a couple of minutes later. Ignored when waiting is 'hard'."
|
|
161
|
+
),
|
|
154
162
|
/** #575: a RELAY of the user's explicitly stated preference, never the agent's
|
|
155
163
|
* choice. Outranks waiting in both directions: 'call' rings even for a
|
|
156
164
|
* waiting:'none' "call me when it's done"; 'message' never rings even for
|
|
@@ -382,6 +390,25 @@ var NotifyResponseSchema = z.object({
|
|
|
382
390
|
answer: UserAnswerSchema.optional(),
|
|
383
391
|
answeredAt: z.string().datetime().optional()
|
|
384
392
|
});
|
|
393
|
+
var NotifyPlanUnitSchema = z.object({
|
|
394
|
+
notificationId: z.string(),
|
|
395
|
+
/** The unit's own heading, so the agent can tell which of its paragraphs this became. */
|
|
396
|
+
title: z.string(),
|
|
397
|
+
/** How loudly this unit was arbitrated to arrive — per unit, which is the point of units. */
|
|
398
|
+
level: NotifyLevelSchema,
|
|
399
|
+
/** Answered from something the user already decided: nobody is interrupted, and a trail card
|
|
400
|
+
* says so. The agent should not wait on this one. */
|
|
401
|
+
settled: z.literal(true).optional(),
|
|
402
|
+
/** What this unit would need to be answerable and does not carry (#894). A PROPOSAL to the
|
|
403
|
+
* agent — nothing here changed the ask, and ignoring it costs nothing. */
|
|
404
|
+
needs: z.array(z.enum(["options", "visuals"])).optional()
|
|
405
|
+
});
|
|
406
|
+
var NotifyPlanSchema = z.object({
|
|
407
|
+
units: z.array(NotifyPlanUnitSchema),
|
|
408
|
+
/** Which unit is this arrival's ONE interruption (units-design.md D23). Absent means nobody
|
|
409
|
+
* was interrupted — every unit was either settled or quiet enough to sit in the inbox. */
|
|
410
|
+
speaks: z.string().optional()
|
|
411
|
+
});
|
|
385
412
|
var UserResponseSchema = z.object({
|
|
386
413
|
requestId: z.string(),
|
|
387
414
|
answer: UserAnswerSchema,
|
|
@@ -436,6 +463,20 @@ var InboxItemSchema = z.object({
|
|
|
436
463
|
/** The conversation thread + connection this item lives on. Present on the replied
|
|
437
464
|
* detail — they power History's "Continue" / "New session from this" (#57/#251). */
|
|
438
465
|
parentId: z.string().optional(),
|
|
466
|
+
/** THE ARRIVAL this row is one unit of (`notifications.ask_id` → `asks`). A claim is one
|
|
467
|
+
* arrival and its units are N rows of it, so this — not `parentId` — is what makes a
|
|
468
|
+
* multi-part notification one thing on screen. The thread is the whole CONVERSATION: it
|
|
469
|
+
* accumulates every message an agent ever sent, so grouping by it renders a day of
|
|
470
|
+
* unrelated updates as a single "12-part request". Absent on rows written before the
|
|
471
|
+
* `asks` table, and on anything that never went through `notify` — both fall back to the
|
|
472
|
+
* thread, which is what the client did for all rows until now. */
|
|
473
|
+
askId: z.string().optional(),
|
|
474
|
+
/** WHERE this unit sat in the message it was cut from (`notifications.seq`). The batch
|
|
475
|
+
* shares one `created_at` to the microsecond, so without it the author's order is
|
|
476
|
+
* unrecoverable client-side — a four-paragraph briefing rendered opening-paragraph-last
|
|
477
|
+
* (live 2026-08-10, D35). The API already orders by it; this lets a reader that
|
|
478
|
+
* re-sorts (grouping, filtering) put an arrival back in the order it was written. */
|
|
479
|
+
seq: z.number().int().optional(),
|
|
439
480
|
tokenId: z.string().optional(),
|
|
440
481
|
status: NotifyStatusSchema,
|
|
441
482
|
context: ContextSchema,
|
|
@@ -887,6 +928,11 @@ var DeviceTokenSchema = z.object({
|
|
|
887
928
|
/** The token's server-side id — the face's COLOUR anchor, and the only seed ingredient
|
|
888
929
|
* that survives a rename. Cached by the host's identity beat. */
|
|
889
930
|
token_id: z.string().nullable().optional(),
|
|
931
|
+
/** WHERE this identity works — the folder a wake should land it in. Written by the host
|
|
932
|
+
* at spawn and by `paigy-harness handoff` from a live terminal. Without it every wake
|
|
933
|
+
* landed in the FIRST granted workspace and the agent rediscovered its own repo from
|
|
934
|
+
* the thread each time (host.ts, live catch 2026-08-06 — prompt-papered until now). */
|
|
935
|
+
workspace: z.string().nullable().optional(),
|
|
890
936
|
phone_reveal: PairingRevealSchema.nullable().optional(),
|
|
891
937
|
// present once the phone reveals
|
|
892
938
|
uik_pub: z.string().nullable().optional()
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
HandoffSchema
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PYORUJ7Q.js";
|
|
5
5
|
import {
|
|
6
6
|
PAIGY_TOOL_IDS,
|
|
7
7
|
autoConfigureClients,
|
|
8
8
|
claudeInstallHint,
|
|
9
9
|
enablePaigyTools,
|
|
10
10
|
paigyToolsAllowlisted
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ENE65M5Y.js";
|
|
12
12
|
import {
|
|
13
13
|
clearSurface,
|
|
14
14
|
writeSurface
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
getThread,
|
|
30
30
|
handoff,
|
|
31
31
|
hatch,
|
|
32
|
+
heartbeat,
|
|
32
33
|
lintNotify,
|
|
33
34
|
listSlots,
|
|
34
35
|
normalizeSpeech,
|
|
@@ -49,7 +50,7 @@ import {
|
|
|
49
50
|
startE2ee,
|
|
50
51
|
submitNotification,
|
|
51
52
|
whoAmI
|
|
52
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-2WPQJYJH.js";
|
|
53
54
|
|
|
54
55
|
// src/index.ts
|
|
55
56
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -128,7 +129,7 @@ var CONTACT_SCHEMA = {
|
|
|
128
129
|
},
|
|
129
130
|
required: ["ask"]
|
|
130
131
|
};
|
|
131
|
-
var CONTACT_DESCRIPTION = "Reach the user through Paigy \u2014 tell them something, or ask and get their answer. State what you need in `ask`, say what happens to your work while you wait in `waiting`, and Paigy handles the rest (channel, phrasing, answer format). If the user explicitly asks you to CALL them, send waiting:'hard' and say so in the ask. Returns { notificationId, parentId } \u2014 pass notificationId to await_reply for the answer, parentId to a later contact to continue the conversation. THREADING REPLACES: a threaded follow-up SUPERSEDES your earlier pending items on that thread \u2014 right for updates to one ask, WRONG for a checklist (send independent to-dos un-threaded). A threaded re-send with IDENTICAL content escalates the pending ask in place. If a reply comes back as {kind:'clarify', chunks:[...]}, the user wants more detail \u2014 contact again on the SAME parentId with an expanded ask. ONE ASK, ONE ROW: never restate a still-pending ask's question inside a NEW contact (e.g. weaving it into a briefing) \u2014 the whole answer settles on the new row and the original can never receive it. Keep waiting on the original (a live call reads every pending ask out separately, each answer routes to its own row), and use `needs` for a genuinely multi-part NEW ask.";
|
|
132
|
+
var CONTACT_DESCRIPTION = "Reach the user through Paigy \u2014 tell them something, or ask and get their answer. State what you need in `ask`, say what happens to your work while you wait in `waiting`, and Paigy handles the rest (channel, phrasing, answer format). If the user explicitly asks you to CALL them, send waiting:'hard' and say so in the ask. Returns { notificationId, parentId } \u2014 pass notificationId to await_reply for the answer, parentId to a later contact to continue the conversation. When it rang, the reply also carries { ifMissed: { mode, means } }: what the user's own policy does with a call they don't take (\"Rings again 10 min, 30 min and 2 hr after the missed call, then leaves it in your inbox\"), so a no-answer tells you how long to wait before coming back. THREADING REPLACES: a threaded follow-up SUPERSEDES your earlier pending items on that thread \u2014 right for updates to one ask, WRONG for a checklist (send independent to-dos un-threaded). A threaded re-send with IDENTICAL content escalates the pending ask in place. If a reply comes back as {kind:'clarify', chunks:[...]}, the user wants more detail \u2014 contact again on the SAME parentId with an expanded ask. ONE ASK, ONE ROW: never restate a still-pending ask's question inside a NEW contact (e.g. weaving it into a briefing) \u2014 the whole answer settles on the new row and the original can never receive it. Keep waiting on the original (a live call reads every pending ask out separately, each answer routes to its own row), and use `needs` for a genuinely multi-part NEW ask.";
|
|
132
133
|
var ONBOARD_DESCRIPTION = "Get this agent talking to Paigy \u2014 call it FIRST, before contact/await_reply, 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_prompt`, ask the user that question and call `enable_tools` on yes.";
|
|
133
134
|
|
|
134
135
|
// src/pairing.ts
|
|
@@ -687,5 +688,21 @@ async function handleTool(request, signal) {
|
|
|
687
688
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
688
689
|
}
|
|
689
690
|
}
|
|
691
|
+
var beatDown = false;
|
|
692
|
+
async function beat() {
|
|
693
|
+
try {
|
|
694
|
+
const token = readToken();
|
|
695
|
+
if (!token) return;
|
|
696
|
+
await heartbeat(void 0, { token });
|
|
697
|
+
if (beatDown) console.error("paigy: presence restored \u2014 the live dot is honest again");
|
|
698
|
+
beatDown = false;
|
|
699
|
+
} catch (err) {
|
|
700
|
+
if (err instanceof UnpairedError) return;
|
|
701
|
+
if (!beatDown) console.error(`paigy: presence beat failed \u2014 the phone may show this agent offline (${String(err)})`);
|
|
702
|
+
beatDown = true;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
void beat();
|
|
706
|
+
setInterval(() => void beat(), 6e4).unref();
|
|
690
707
|
var transport = new StdioServerTransport();
|
|
691
708
|
await server.connect(transport);
|
package/dist/listen.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
WAKE_EVENT,
|
|
4
4
|
wakeChannel
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-PYORUJ7Q.js";
|
|
6
6
|
import {
|
|
7
7
|
checkReplies,
|
|
8
8
|
registerDelivery
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-2WPQJYJH.js";
|
|
10
10
|
|
|
11
11
|
// src/listen.ts
|
|
12
12
|
import { createClient } from "@supabase/supabase-js";
|
package/dist/onboard.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
autoConfigureClients,
|
|
4
4
|
claudeInstallHint,
|
|
5
5
|
openBrowser
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ENE65M5Y.js";
|
|
7
7
|
import {
|
|
8
8
|
AGENT_NAME,
|
|
9
9
|
TOKEN_PATH,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
setIdentity,
|
|
18
18
|
sleep,
|
|
19
19
|
whoAmI
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-2WPQJYJH.js";
|
|
21
21
|
|
|
22
22
|
// src/onboard.ts
|
|
23
23
|
async function main() {
|
package/dist/statusline.js
CHANGED