@paigy/mcp 0.32.0 → 0.34.0
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-XSHUDPJ3.js → chunk-45CIZLCJ.js} +115 -7
- package/dist/{chunk-LKA7UMRZ.js → chunk-G2WR7FCJ.js} +1 -1
- package/dist/{chunk-7SFOTYXZ.js → chunk-X42EUXML.js} +115 -7
- package/dist/enable.js +2 -2
- package/dist/index.js +5 -5
- package/dist/listen.js +2 -2
- package/dist/onboard.js +2 -2
- package/dist/statusline.js +1 -1
- package/package.json +12 -13
|
@@ -51,6 +51,12 @@ var ReceiptEventSchema = z.enum([
|
|
|
51
51
|
// the recipient opened it
|
|
52
52
|
"answered",
|
|
53
53
|
// the recipient replied
|
|
54
|
+
// The recipient TURNED THE RING DOWN — CallKit ended it and no answer was ever tapped.
|
|
55
|
+
// Written by the phone, on the same door that reports the ring itself, so it exists only
|
|
56
|
+
// when a ring reached a running app and a person did not take it. That is what separates
|
|
57
|
+
// it from "a ring with no answer", which our own crashes wrote just as readily and which
|
|
58
|
+
// is why the responsiveness back-off had to be removed (#1144).
|
|
59
|
+
"declined",
|
|
54
60
|
"escalated",
|
|
55
61
|
// re-reached at a higher level (re-ring / promote)
|
|
56
62
|
"coalesced",
|
|
@@ -59,8 +65,14 @@ var ReceiptEventSchema = z.enum([
|
|
|
59
65
|
// deadline passed unanswered
|
|
60
66
|
"woke",
|
|
61
67
|
// the agent was woken for an owed obligation (callback)
|
|
62
|
-
"gave_up"
|
|
68
|
+
"gave_up",
|
|
63
69
|
// the budget was spent — stopped re-engaging
|
|
70
|
+
// The ladder starts over — a silent pickup (the owner's fresh-miss rule, 2026-07-28), a
|
|
71
|
+
// promote to call, a re-delivery. APPENDED, never a rewind: `ring_step` was a cache of
|
|
72
|
+
// the escalated-count and a writer rewound it to 0 on every unanswered call (2026-08-24,
|
|
73
|
+
// nine rings in an hour, `gaveUp` unreachable). A count since the last `restarted` cannot
|
|
74
|
+
// be rewound by a writer that forgot to advance it.
|
|
75
|
+
"restarted"
|
|
64
76
|
]);
|
|
65
77
|
var AttentionSchema = z.object({
|
|
66
78
|
urgency: NotifyLevelSchema,
|
|
@@ -250,6 +262,14 @@ var IntentSchema = z.object({
|
|
|
250
262
|
* about what cannot answer "is the bot looping less this week?". */
|
|
251
263
|
fault: z.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
|
|
252
264
|
});
|
|
265
|
+
var RideAlongSchema = z.object({
|
|
266
|
+
/** The note this came from — assign/clarify/close it through /api/notes/:id. */
|
|
267
|
+
noteId: z.string(),
|
|
268
|
+
/** What to do, in the owner's own words (the note's headline). Never model-rewritten. */
|
|
269
|
+
text: z.string(),
|
|
270
|
+
/** The thread to report back on, when the note was dispatched over the request rail. */
|
|
271
|
+
parentId: z.string().nullable()
|
|
272
|
+
});
|
|
253
273
|
var AwaitItemSchema = z.discriminatedUnion("type", [
|
|
254
274
|
z.object({
|
|
255
275
|
type: z.literal("reply"),
|
|
@@ -270,7 +290,13 @@ var AwaitItemSchema = z.discriminatedUnion("type", [
|
|
|
270
290
|
transcript: z.string().optional(),
|
|
271
291
|
/** Coverage report (#396), when the ask declared `points`: which of them this
|
|
272
292
|
* answer addressed. Missing points = re-ask or proceed knowingly partial. */
|
|
273
|
-
covered: z.array(z.string()).optional()
|
|
293
|
+
covered: z.array(z.string()).optional(),
|
|
294
|
+
/** Ride-alongs (RideAlongSchema) — pending work for you, attached to the moment you
|
|
295
|
+
* became free. Only `reply` and `idle` carry it: those are the two outcomes that
|
|
296
|
+
* END a wait. `remind`, `superseded` and `turn` are mid-flight, and handing an
|
|
297
|
+
* agent a side-quest while it is still holding the line is how the main thing gets
|
|
298
|
+
* dropped. Absent/empty = nothing owed. */
|
|
299
|
+
also: z.array(RideAlongSchema).optional()
|
|
274
300
|
}),
|
|
275
301
|
z.object({
|
|
276
302
|
type: z.literal("remind"),
|
|
@@ -305,7 +331,7 @@ var AwaitItemSchema = z.discriminatedUnion("type", [
|
|
|
305
331
|
acts: z.array(IntentSchema).nullable().optional()
|
|
306
332
|
})
|
|
307
333
|
}),
|
|
308
|
-
z.object({ type: z.literal("idle") })
|
|
334
|
+
z.object({ type: z.literal("idle"), also: z.array(RideAlongSchema).optional() })
|
|
309
335
|
]);
|
|
310
336
|
var CallbackTriggerSchema = z.enum(["on_done", "on_blocked", "scheduled"]);
|
|
311
337
|
var ScheduleCallbackSchema = z.object({
|
|
@@ -336,6 +362,12 @@ var PendingRepliesSchema = z.object({
|
|
|
336
362
|
pending: z.array(
|
|
337
363
|
z.object({ parentId: z.string(), notificationId: z.string(), createdAt: z.string() })
|
|
338
364
|
),
|
|
365
|
+
/** WHO YOU ARE on this account (field report 2026-08-28): the name and device the user
|
|
366
|
+
* sees for this session's identity. From inside a session there was no way to find out —
|
|
367
|
+
* `pair` with no arguments can HATCH a fresh identity, so it is not a safe probe — and an
|
|
368
|
+
* agent that cannot tell which agent it is cannot tell whether work addressed to
|
|
369
|
+
* "Reta" was addressed to it. Absent only for a token with no pairing behind it. */
|
|
370
|
+
you: z.object({ name: z.string(), device: z.string().nullable(), tokenId: z.string() }).optional(),
|
|
339
371
|
/** User-initiated requests addressed to this agent; act on them and reply via
|
|
340
372
|
* contact on the same parentId. Keeps reappearing until you call
|
|
341
373
|
* set_task_state on its notificationId. */
|
|
@@ -347,12 +379,21 @@ var PendingRepliesSchema = z.object({
|
|
|
347
379
|
createdAt: z.string(),
|
|
348
380
|
/** The user seeded this request with a past conversation — call get_thread on it
|
|
349
381
|
* FIRST and treat the transcript as prior context (#57/#251). */
|
|
350
|
-
contextParentId: z.string().optional()
|
|
382
|
+
contextParentId: z.string().optional(),
|
|
383
|
+
/** STRANDED (field report 2026-08-28): this request was addressed to ANOTHER agent on
|
|
384
|
+
* the account — the name here — which has not been seen since it landed, so nobody
|
|
385
|
+
* came for it. Handed to you because you are the session that is here. Take it like
|
|
386
|
+
* any request (set_task_state claims it, reply with contact on its parentId), and say
|
|
387
|
+
* whose it was, because the user chose that agent on purpose. */
|
|
388
|
+
stranded: z.string().optional()
|
|
351
389
|
})
|
|
352
390
|
),
|
|
353
391
|
/** Callbacks you owe the user that are now DUE (you said you'd follow up when done,
|
|
354
392
|
* if blocked, or at a time that has passed). Re-surfaced every sweep until you
|
|
355
393
|
* fulfill one by calling contact on its parentId. */
|
|
394
|
+
/** Ride-alongs (RideAlongSchema): notes assigned to this agent that no wake could
|
|
395
|
+
* reach. Same array the contact/await replies carry — one queue, every carrier. */
|
|
396
|
+
also: z.array(RideAlongSchema).optional(),
|
|
356
397
|
owedCallbacks: z.array(
|
|
357
398
|
z.object({ parentId: z.string(), trigger: CallbackTriggerSchema, note: z.string() })
|
|
358
399
|
),
|
|
@@ -388,7 +429,11 @@ var NotifyResponseSchema = z.object({
|
|
|
388
429
|
status: NotifyStatusSchema,
|
|
389
430
|
createdAt: z.string().datetime(),
|
|
390
431
|
answer: UserAnswerSchema.optional(),
|
|
391
|
-
answeredAt: z.string().datetime().optional()
|
|
432
|
+
answeredAt: z.string().datetime().optional(),
|
|
433
|
+
/** Ride-alongs for THIS agent — pending work it should pick up when it's done with
|
|
434
|
+
* what it came for. Present on any reply, because an unwakeable agent's only
|
|
435
|
+
* reliable moment is one it initiated. Absent/empty = nothing owed. */
|
|
436
|
+
also: z.array(RideAlongSchema).optional()
|
|
392
437
|
});
|
|
393
438
|
var NotifyPlanUnitSchema = z.object({
|
|
394
439
|
notificationId: z.string(),
|
|
@@ -440,6 +485,9 @@ var UserResponseSchema = z.object({
|
|
|
440
485
|
});
|
|
441
486
|
var VoiceKeySchema = z.enum(["rachel", "george", "jessica", "brian", "lily"]);
|
|
442
487
|
var AgendaTurnSchema = z.object({
|
|
488
|
+
/** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
|
|
489
|
+
* the planner declares duplicates instead of asking them twice. */
|
|
490
|
+
coveredIds: z.array(z.string()).optional(),
|
|
443
491
|
/** At most three short spoken sentences. Capped because a turn is a breath: a 1031-char
|
|
444
492
|
* line went out on 2026-07-28 and the caller could not answer it at all. */
|
|
445
493
|
info: z.array(z.string().min(1)).max(3).default([]),
|
|
@@ -473,6 +521,7 @@ var AgendaTurnSchema = z.object({
|
|
|
473
521
|
* on, the claim stays pending; blocking:true on context = hold for a reply. */
|
|
474
522
|
blocking: z.boolean().optional()
|
|
475
523
|
});
|
|
524
|
+
var CLAIM_STALE_MS = 30 * 6e4;
|
|
476
525
|
var InboxItemSchema = z.object({
|
|
477
526
|
id: z.string(),
|
|
478
527
|
/** The conversation thread + connection this item lives on. Present on the replied
|
|
@@ -492,6 +541,15 @@ var InboxItemSchema = z.object({
|
|
|
492
541
|
* (live 2026-08-10, D35). The API already orders by it; this lets a reader that
|
|
493
542
|
* re-sorts (grouping, filtering) put an arrival back in the order it was written. */
|
|
494
543
|
seq: z.number().int().optional(),
|
|
544
|
+
/** HOW MANY units the arrival was cut into. A device reads a LENS, never the arrival —
|
|
545
|
+
* `/api/inbox` serves `open`, so the units already settled are gone from it — and a client
|
|
546
|
+
* counting what it can see is counting what is LEFT. Walking a three-unit ask on the answer
|
|
547
|
+
* screen read "1 of 3", then "1 of 2", then no chip at all, each answer having removed the
|
|
548
|
+
* only evidence of itself. How big an arrival is, is a fact about the arrival, so the
|
|
549
|
+
* server that can still see every row states it. Absent on any row with no `askId`: a
|
|
550
|
+
* unit knows WHICH ask it came from and WHERE it sat in it, and how many there were is
|
|
551
|
+
* the one part of its own arrival a single row cannot answer. */
|
|
552
|
+
units: z.number().int().positive().optional(),
|
|
495
553
|
tokenId: z.string().optional(),
|
|
496
554
|
status: NotifyStatusSchema,
|
|
497
555
|
context: ContextSchema,
|
|
@@ -512,6 +570,16 @@ var InboxItemSchema = z.object({
|
|
|
512
570
|
* while the party called the same dead claim stalled. Absent = no token/no data,
|
|
513
571
|
* which must never CLAIM stalled. */
|
|
514
572
|
lastSeenAt: z.string().optional(),
|
|
573
|
+
/** WHEN THE AGENT LAST SAID ANYTHING ABOUT THIS CLAIM — the newest `agent_state` row in
|
|
574
|
+
* the `notification_events` ledger (trigger-written since 20260621010000, so every row a
|
|
575
|
+
* user can see has one). The age input for `CLAIM_STALE_MS`, and it has to be this rather
|
|
576
|
+
* than `createdAt`: a claim is very often picked up long after the row was born — the
|
|
577
|
+
* inbox keeps an ANSWERED row visible while the agent works the follow-up, so a question
|
|
578
|
+
* asked this morning and claimed a minute ago is eight hours old and one minute into its
|
|
579
|
+
* work. Reading the row's birth as the claim's age brands that "No update in 8h" the
|
|
580
|
+
* instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
|
|
581
|
+
* `createdAt`. */
|
|
582
|
+
agentStateAt: z.string().datetime().optional(),
|
|
515
583
|
agenda: z.array(AgendaTurnSchema).optional(),
|
|
516
584
|
/** On a replied detail (#397): the next steps the user attached to the answer
|
|
517
585
|
* ("call back after lunch") — shown so they can see the commitment was captured. */
|
|
@@ -583,11 +651,23 @@ var SnoozeRequestSchema = z.object({
|
|
|
583
651
|
requestId: z.string(),
|
|
584
652
|
until: z.string().datetime()
|
|
585
653
|
});
|
|
654
|
+
var APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
|
|
586
655
|
var PushTokenSchema = z.object({
|
|
587
656
|
voipToken: z.string().min(1).optional(),
|
|
588
657
|
alertToken: z.string().min(1).optional(),
|
|
589
658
|
fcmToken: z.string().min(1).optional(),
|
|
590
659
|
platform: z.enum(["ios", "android"])
|
|
660
|
+
}).superRefine((v, ctx) => {
|
|
661
|
+
if (v.platform !== "ios") return;
|
|
662
|
+
for (const field of ["voipToken", "alertToken"]) {
|
|
663
|
+
const token = v[field];
|
|
664
|
+
if (token === void 0 || APNS_TOKEN_RE.test(token)) continue;
|
|
665
|
+
ctx.addIssue({
|
|
666
|
+
code: z.ZodIssueCode.custom,
|
|
667
|
+
path: [field],
|
|
668
|
+
message: `not an APNs device token (want 64 hex chars, got ${token.length})`
|
|
669
|
+
});
|
|
670
|
+
}
|
|
591
671
|
});
|
|
592
672
|
var MissedCallSchema = z.enum([
|
|
593
673
|
"retry_10m",
|
|
@@ -648,6 +728,15 @@ var UserSettingsSchema = z.object({
|
|
|
648
728
|
/** Opt-in to real-phone (PSTN) calls when the app can't ring. Optional, not
|
|
649
729
|
* defaulted — an older client PATCHing the full object must not clobber it. */
|
|
650
730
|
pstnCalls: z.boolean().optional(),
|
|
731
|
+
/** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
|
|
732
|
+
* only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
|
|
733
|
+
* an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
|
|
734
|
+
* the same reason `voiceMode` is (a stale client PATCHing the whole object must not
|
|
735
|
+
* clobber it) and one more: a GUESSED timezone schedules reminders hours off, and
|
|
736
|
+
* that failure reads as the reminder rail being unreliable rather than as a missing
|
|
737
|
+
* setting. Absent = a spoken time can't be landed, so the reminder rides the next
|
|
738
|
+
* call — honest about what we know. */
|
|
739
|
+
timezone: z.string().min(1).max(64).optional(),
|
|
651
740
|
/** Account E2EE state (text lane): 'off' (default) = today's plaintext; 'on' =
|
|
652
741
|
* content is sealed end-to-end between the local agent and the phone. Like
|
|
653
742
|
* voiceMode, OPTIONAL and NOT defaulted so a stale client PATCHing the full
|
|
@@ -788,7 +877,8 @@ var HandoffSchema = z.object({
|
|
|
788
877
|
recap: z.boolean().optional()
|
|
789
878
|
});
|
|
790
879
|
var NoteSourceSchema = z.enum(["app", "call"]);
|
|
791
|
-
var NoteStatusSchema = z.enum(["open", "assigned", "done"]);
|
|
880
|
+
var NoteStatusSchema = z.enum(["open", "assigned", "in_progress", "done"]);
|
|
881
|
+
var NoteRepeatSchema = z.enum(["once", "until_done"]);
|
|
792
882
|
var DecisionSchema = z.object({
|
|
793
883
|
id: z.string(),
|
|
794
884
|
/** The note this decision refines; null = recorded on a bare thread (the
|
|
@@ -813,11 +903,29 @@ var NoteSchema = z.object({
|
|
|
813
903
|
assignee: z.string().nullable(),
|
|
814
904
|
/** The request thread minted at assignment; null until assigned. */
|
|
815
905
|
parentId: z.string().nullable(),
|
|
906
|
+
/** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
|
|
907
|
+
* call — never a deadline, and nothing rings when it passes. Null = "the very next
|
|
908
|
+
* call", the right reading of "remind me to…" with no time attached. */
|
|
909
|
+
// Defaulted, not required: a Note from an API deploy older than the reminders
|
|
910
|
+
// migration has none of these, and the defaults ARE what it means — no not-before,
|
|
911
|
+
// one ride, never ridden. Parsing must not fail across a rolling deploy.
|
|
912
|
+
dueAt: z.string().nullable().default(null),
|
|
913
|
+
repeat: NoteRepeatSchema.default("once"),
|
|
914
|
+
/** How many calls have already carried it — the fatigue cap counts rides, not days. */
|
|
915
|
+
rides: z.number().int().default(0),
|
|
916
|
+
lastRideAt: z.string().nullable().default(null),
|
|
816
917
|
createdAt: z.string()
|
|
817
918
|
});
|
|
818
919
|
var CreateNoteSchema = z.object({
|
|
819
920
|
/** The intent, in the user's own words. Stored verbatim; the broker only titles it. */
|
|
820
|
-
text: z.string().min(1).max(4e3)
|
|
921
|
+
text: z.string().min(1).max(4e3),
|
|
922
|
+
/** Capture it as a REMINDER — a note assigned to the user themselves, which rides
|
|
923
|
+
* their next call instead of being handed to an agent. Everything else about the
|
|
924
|
+
* note is identical; this is the one parameter that separates the two. */
|
|
925
|
+
forMe: z.boolean().optional(),
|
|
926
|
+
/** The not-before, when the user already said one. Absent = the very next call. */
|
|
927
|
+
dueAt: z.string().datetime().optional(),
|
|
928
|
+
repeat: NoteRepeatSchema.optional()
|
|
821
929
|
});
|
|
822
930
|
var RecordDecisionSchema = z.object({
|
|
823
931
|
/** An open decision (from /clarify) to answer. */
|
|
@@ -2390,6 +2390,12 @@ var ReceiptEventSchema = z.enum([
|
|
|
2390
2390
|
// the recipient opened it
|
|
2391
2391
|
"answered",
|
|
2392
2392
|
// the recipient replied
|
|
2393
|
+
// The recipient TURNED THE RING DOWN — CallKit ended it and no answer was ever tapped.
|
|
2394
|
+
// Written by the phone, on the same door that reports the ring itself, so it exists only
|
|
2395
|
+
// when a ring reached a running app and a person did not take it. That is what separates
|
|
2396
|
+
// it from "a ring with no answer", which our own crashes wrote just as readily and which
|
|
2397
|
+
// is why the responsiveness back-off had to be removed (#1144).
|
|
2398
|
+
"declined",
|
|
2393
2399
|
"escalated",
|
|
2394
2400
|
// re-reached at a higher level (re-ring / promote)
|
|
2395
2401
|
"coalesced",
|
|
@@ -2398,8 +2404,14 @@ var ReceiptEventSchema = z.enum([
|
|
|
2398
2404
|
// deadline passed unanswered
|
|
2399
2405
|
"woke",
|
|
2400
2406
|
// the agent was woken for an owed obligation (callback)
|
|
2401
|
-
"gave_up"
|
|
2407
|
+
"gave_up",
|
|
2402
2408
|
// the budget was spent — stopped re-engaging
|
|
2409
|
+
// The ladder starts over — a silent pickup (the owner's fresh-miss rule, 2026-07-28), a
|
|
2410
|
+
// promote to call, a re-delivery. APPENDED, never a rewind: `ring_step` was a cache of
|
|
2411
|
+
// the escalated-count and a writer rewound it to 0 on every unanswered call (2026-08-24,
|
|
2412
|
+
// nine rings in an hour, `gaveUp` unreachable). A count since the last `restarted` cannot
|
|
2413
|
+
// be rewound by a writer that forgot to advance it.
|
|
2414
|
+
"restarted"
|
|
2403
2415
|
]);
|
|
2404
2416
|
var AttentionSchema = z.object({
|
|
2405
2417
|
urgency: NotifyLevelSchema,
|
|
@@ -2644,6 +2656,14 @@ var IntentSchema = z.object({
|
|
|
2644
2656
|
* about what cannot answer "is the bot looping less this week?". */
|
|
2645
2657
|
fault: z.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
|
|
2646
2658
|
});
|
|
2659
|
+
var RideAlongSchema = z.object({
|
|
2660
|
+
/** The note this came from — assign/clarify/close it through /api/notes/:id. */
|
|
2661
|
+
noteId: z.string(),
|
|
2662
|
+
/** What to do, in the owner's own words (the note's headline). Never model-rewritten. */
|
|
2663
|
+
text: z.string(),
|
|
2664
|
+
/** The thread to report back on, when the note was dispatched over the request rail. */
|
|
2665
|
+
parentId: z.string().nullable()
|
|
2666
|
+
});
|
|
2647
2667
|
var AwaitItemSchema = z.discriminatedUnion("type", [
|
|
2648
2668
|
z.object({
|
|
2649
2669
|
type: z.literal("reply"),
|
|
@@ -2664,7 +2684,13 @@ var AwaitItemSchema = z.discriminatedUnion("type", [
|
|
|
2664
2684
|
transcript: z.string().optional(),
|
|
2665
2685
|
/** Coverage report (#396), when the ask declared `points`: which of them this
|
|
2666
2686
|
* answer addressed. Missing points = re-ask or proceed knowingly partial. */
|
|
2667
|
-
covered: z.array(z.string()).optional()
|
|
2687
|
+
covered: z.array(z.string()).optional(),
|
|
2688
|
+
/** Ride-alongs (RideAlongSchema) — pending work for you, attached to the moment you
|
|
2689
|
+
* became free. Only `reply` and `idle` carry it: those are the two outcomes that
|
|
2690
|
+
* END a wait. `remind`, `superseded` and `turn` are mid-flight, and handing an
|
|
2691
|
+
* agent a side-quest while it is still holding the line is how the main thing gets
|
|
2692
|
+
* dropped. Absent/empty = nothing owed. */
|
|
2693
|
+
also: z.array(RideAlongSchema).optional()
|
|
2668
2694
|
}),
|
|
2669
2695
|
z.object({
|
|
2670
2696
|
type: z.literal("remind"),
|
|
@@ -2699,7 +2725,7 @@ var AwaitItemSchema = z.discriminatedUnion("type", [
|
|
|
2699
2725
|
acts: z.array(IntentSchema).nullable().optional()
|
|
2700
2726
|
})
|
|
2701
2727
|
}),
|
|
2702
|
-
z.object({ type: z.literal("idle") })
|
|
2728
|
+
z.object({ type: z.literal("idle"), also: z.array(RideAlongSchema).optional() })
|
|
2703
2729
|
]);
|
|
2704
2730
|
var CallbackTriggerSchema = z.enum(["on_done", "on_blocked", "scheduled"]);
|
|
2705
2731
|
var ScheduleCallbackSchema = z.object({
|
|
@@ -2730,6 +2756,12 @@ var PendingRepliesSchema = z.object({
|
|
|
2730
2756
|
pending: z.array(
|
|
2731
2757
|
z.object({ parentId: z.string(), notificationId: z.string(), createdAt: z.string() })
|
|
2732
2758
|
),
|
|
2759
|
+
/** WHO YOU ARE on this account (field report 2026-08-28): the name and device the user
|
|
2760
|
+
* sees for this session's identity. From inside a session there was no way to find out —
|
|
2761
|
+
* `pair` with no arguments can HATCH a fresh identity, so it is not a safe probe — and an
|
|
2762
|
+
* agent that cannot tell which agent it is cannot tell whether work addressed to
|
|
2763
|
+
* "Reta" was addressed to it. Absent only for a token with no pairing behind it. */
|
|
2764
|
+
you: z.object({ name: z.string(), device: z.string().nullable(), tokenId: z.string() }).optional(),
|
|
2733
2765
|
/** User-initiated requests addressed to this agent; act on them and reply via
|
|
2734
2766
|
* contact on the same parentId. Keeps reappearing until you call
|
|
2735
2767
|
* set_task_state on its notificationId. */
|
|
@@ -2741,12 +2773,21 @@ var PendingRepliesSchema = z.object({
|
|
|
2741
2773
|
createdAt: z.string(),
|
|
2742
2774
|
/** The user seeded this request with a past conversation — call get_thread on it
|
|
2743
2775
|
* FIRST and treat the transcript as prior context (#57/#251). */
|
|
2744
|
-
contextParentId: z.string().optional()
|
|
2776
|
+
contextParentId: z.string().optional(),
|
|
2777
|
+
/** STRANDED (field report 2026-08-28): this request was addressed to ANOTHER agent on
|
|
2778
|
+
* the account — the name here — which has not been seen since it landed, so nobody
|
|
2779
|
+
* came for it. Handed to you because you are the session that is here. Take it like
|
|
2780
|
+
* any request (set_task_state claims it, reply with contact on its parentId), and say
|
|
2781
|
+
* whose it was, because the user chose that agent on purpose. */
|
|
2782
|
+
stranded: z.string().optional()
|
|
2745
2783
|
})
|
|
2746
2784
|
),
|
|
2747
2785
|
/** Callbacks you owe the user that are now DUE (you said you'd follow up when done,
|
|
2748
2786
|
* if blocked, or at a time that has passed). Re-surfaced every sweep until you
|
|
2749
2787
|
* fulfill one by calling contact on its parentId. */
|
|
2788
|
+
/** Ride-alongs (RideAlongSchema): notes assigned to this agent that no wake could
|
|
2789
|
+
* reach. Same array the contact/await replies carry — one queue, every carrier. */
|
|
2790
|
+
also: z.array(RideAlongSchema).optional(),
|
|
2750
2791
|
owedCallbacks: z.array(
|
|
2751
2792
|
z.object({ parentId: z.string(), trigger: CallbackTriggerSchema, note: z.string() })
|
|
2752
2793
|
),
|
|
@@ -2782,7 +2823,11 @@ var NotifyResponseSchema = z.object({
|
|
|
2782
2823
|
status: NotifyStatusSchema,
|
|
2783
2824
|
createdAt: z.string().datetime(),
|
|
2784
2825
|
answer: UserAnswerSchema.optional(),
|
|
2785
|
-
answeredAt: z.string().datetime().optional()
|
|
2826
|
+
answeredAt: z.string().datetime().optional(),
|
|
2827
|
+
/** Ride-alongs for THIS agent — pending work it should pick up when it's done with
|
|
2828
|
+
* what it came for. Present on any reply, because an unwakeable agent's only
|
|
2829
|
+
* reliable moment is one it initiated. Absent/empty = nothing owed. */
|
|
2830
|
+
also: z.array(RideAlongSchema).optional()
|
|
2786
2831
|
});
|
|
2787
2832
|
var NotifyPlanUnitSchema = z.object({
|
|
2788
2833
|
notificationId: z.string(),
|
|
@@ -2834,6 +2879,9 @@ var UserResponseSchema = z.object({
|
|
|
2834
2879
|
});
|
|
2835
2880
|
var VoiceKeySchema = z.enum(["rachel", "george", "jessica", "brian", "lily"]);
|
|
2836
2881
|
var AgendaTurnSchema = z.object({
|
|
2882
|
+
/** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
|
|
2883
|
+
* the planner declares duplicates instead of asking them twice. */
|
|
2884
|
+
coveredIds: z.array(z.string()).optional(),
|
|
2837
2885
|
/** At most three short spoken sentences. Capped because a turn is a breath: a 1031-char
|
|
2838
2886
|
* line went out on 2026-07-28 and the caller could not answer it at all. */
|
|
2839
2887
|
info: z.array(z.string().min(1)).max(3).default([]),
|
|
@@ -2867,6 +2915,7 @@ var AgendaTurnSchema = z.object({
|
|
|
2867
2915
|
* on, the claim stays pending; blocking:true on context = hold for a reply. */
|
|
2868
2916
|
blocking: z.boolean().optional()
|
|
2869
2917
|
});
|
|
2918
|
+
var CLAIM_STALE_MS = 30 * 6e4;
|
|
2870
2919
|
var InboxItemSchema = z.object({
|
|
2871
2920
|
id: z.string(),
|
|
2872
2921
|
/** The conversation thread + connection this item lives on. Present on the replied
|
|
@@ -2886,6 +2935,15 @@ var InboxItemSchema = z.object({
|
|
|
2886
2935
|
* (live 2026-08-10, D35). The API already orders by it; this lets a reader that
|
|
2887
2936
|
* re-sorts (grouping, filtering) put an arrival back in the order it was written. */
|
|
2888
2937
|
seq: z.number().int().optional(),
|
|
2938
|
+
/** HOW MANY units the arrival was cut into. A device reads a LENS, never the arrival —
|
|
2939
|
+
* `/api/inbox` serves `open`, so the units already settled are gone from it — and a client
|
|
2940
|
+
* counting what it can see is counting what is LEFT. Walking a three-unit ask on the answer
|
|
2941
|
+
* screen read "1 of 3", then "1 of 2", then no chip at all, each answer having removed the
|
|
2942
|
+
* only evidence of itself. How big an arrival is, is a fact about the arrival, so the
|
|
2943
|
+
* server that can still see every row states it. Absent on any row with no `askId`: a
|
|
2944
|
+
* unit knows WHICH ask it came from and WHERE it sat in it, and how many there were is
|
|
2945
|
+
* the one part of its own arrival a single row cannot answer. */
|
|
2946
|
+
units: z.number().int().positive().optional(),
|
|
2889
2947
|
tokenId: z.string().optional(),
|
|
2890
2948
|
status: NotifyStatusSchema,
|
|
2891
2949
|
context: ContextSchema,
|
|
@@ -2906,6 +2964,16 @@ var InboxItemSchema = z.object({
|
|
|
2906
2964
|
* while the party called the same dead claim stalled. Absent = no token/no data,
|
|
2907
2965
|
* which must never CLAIM stalled. */
|
|
2908
2966
|
lastSeenAt: z.string().optional(),
|
|
2967
|
+
/** WHEN THE AGENT LAST SAID ANYTHING ABOUT THIS CLAIM — the newest `agent_state` row in
|
|
2968
|
+
* the `notification_events` ledger (trigger-written since 20260621010000, so every row a
|
|
2969
|
+
* user can see has one). The age input for `CLAIM_STALE_MS`, and it has to be this rather
|
|
2970
|
+
* than `createdAt`: a claim is very often picked up long after the row was born — the
|
|
2971
|
+
* inbox keeps an ANSWERED row visible while the agent works the follow-up, so a question
|
|
2972
|
+
* asked this morning and claimed a minute ago is eight hours old and one minute into its
|
|
2973
|
+
* work. Reading the row's birth as the claim's age brands that "No update in 8h" the
|
|
2974
|
+
* instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
|
|
2975
|
+
* `createdAt`. */
|
|
2976
|
+
agentStateAt: z.string().datetime().optional(),
|
|
2909
2977
|
agenda: z.array(AgendaTurnSchema).optional(),
|
|
2910
2978
|
/** On a replied detail (#397): the next steps the user attached to the answer
|
|
2911
2979
|
* ("call back after lunch") — shown so they can see the commitment was captured. */
|
|
@@ -2977,11 +3045,23 @@ var SnoozeRequestSchema = z.object({
|
|
|
2977
3045
|
requestId: z.string(),
|
|
2978
3046
|
until: z.string().datetime()
|
|
2979
3047
|
});
|
|
3048
|
+
var APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
|
|
2980
3049
|
var PushTokenSchema = z.object({
|
|
2981
3050
|
voipToken: z.string().min(1).optional(),
|
|
2982
3051
|
alertToken: z.string().min(1).optional(),
|
|
2983
3052
|
fcmToken: z.string().min(1).optional(),
|
|
2984
3053
|
platform: z.enum(["ios", "android"])
|
|
3054
|
+
}).superRefine((v, ctx) => {
|
|
3055
|
+
if (v.platform !== "ios") return;
|
|
3056
|
+
for (const field of ["voipToken", "alertToken"]) {
|
|
3057
|
+
const token = v[field];
|
|
3058
|
+
if (token === void 0 || APNS_TOKEN_RE.test(token)) continue;
|
|
3059
|
+
ctx.addIssue({
|
|
3060
|
+
code: z.ZodIssueCode.custom,
|
|
3061
|
+
path: [field],
|
|
3062
|
+
message: `not an APNs device token (want 64 hex chars, got ${token.length})`
|
|
3063
|
+
});
|
|
3064
|
+
}
|
|
2985
3065
|
});
|
|
2986
3066
|
var MissedCallSchema = z.enum([
|
|
2987
3067
|
"retry_10m",
|
|
@@ -3032,6 +3112,15 @@ var UserSettingsSchema = z.object({
|
|
|
3032
3112
|
/** Opt-in to real-phone (PSTN) calls when the app can't ring. Optional, not
|
|
3033
3113
|
* defaulted — an older client PATCHing the full object must not clobber it. */
|
|
3034
3114
|
pstnCalls: z.boolean().optional(),
|
|
3115
|
+
/** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
|
|
3116
|
+
* only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
|
|
3117
|
+
* an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
|
|
3118
|
+
* the same reason `voiceMode` is (a stale client PATCHing the whole object must not
|
|
3119
|
+
* clobber it) and one more: a GUESSED timezone schedules reminders hours off, and
|
|
3120
|
+
* that failure reads as the reminder rail being unreliable rather than as a missing
|
|
3121
|
+
* setting. Absent = a spoken time can't be landed, so the reminder rides the next
|
|
3122
|
+
* call — honest about what we know. */
|
|
3123
|
+
timezone: z.string().min(1).max(64).optional(),
|
|
3035
3124
|
/** Account E2EE state (text lane): 'off' (default) = today's plaintext; 'on' =
|
|
3036
3125
|
* content is sealed end-to-end between the local agent and the phone. Like
|
|
3037
3126
|
* voiceMode, OPTIONAL and NOT defaulted so a stale client PATCHing the full
|
|
@@ -3172,7 +3261,8 @@ var HandoffSchema = z.object({
|
|
|
3172
3261
|
recap: z.boolean().optional()
|
|
3173
3262
|
});
|
|
3174
3263
|
var NoteSourceSchema = z.enum(["app", "call"]);
|
|
3175
|
-
var NoteStatusSchema = z.enum(["open", "assigned", "done"]);
|
|
3264
|
+
var NoteStatusSchema = z.enum(["open", "assigned", "in_progress", "done"]);
|
|
3265
|
+
var NoteRepeatSchema = z.enum(["once", "until_done"]);
|
|
3176
3266
|
var DecisionSchema = z.object({
|
|
3177
3267
|
id: z.string(),
|
|
3178
3268
|
/** The note this decision refines; null = recorded on a bare thread (the
|
|
@@ -3197,11 +3287,29 @@ var NoteSchema = z.object({
|
|
|
3197
3287
|
assignee: z.string().nullable(),
|
|
3198
3288
|
/** The request thread minted at assignment; null until assigned. */
|
|
3199
3289
|
parentId: z.string().nullable(),
|
|
3290
|
+
/** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
|
|
3291
|
+
* call — never a deadline, and nothing rings when it passes. Null = "the very next
|
|
3292
|
+
* call", the right reading of "remind me to…" with no time attached. */
|
|
3293
|
+
// Defaulted, not required: a Note from an API deploy older than the reminders
|
|
3294
|
+
// migration has none of these, and the defaults ARE what it means — no not-before,
|
|
3295
|
+
// one ride, never ridden. Parsing must not fail across a rolling deploy.
|
|
3296
|
+
dueAt: z.string().nullable().default(null),
|
|
3297
|
+
repeat: NoteRepeatSchema.default("once"),
|
|
3298
|
+
/** How many calls have already carried it — the fatigue cap counts rides, not days. */
|
|
3299
|
+
rides: z.number().int().default(0),
|
|
3300
|
+
lastRideAt: z.string().nullable().default(null),
|
|
3200
3301
|
createdAt: z.string()
|
|
3201
3302
|
});
|
|
3202
3303
|
var CreateNoteSchema = z.object({
|
|
3203
3304
|
/** The intent, in the user's own words. Stored verbatim; the broker only titles it. */
|
|
3204
|
-
text: z.string().min(1).max(4e3)
|
|
3305
|
+
text: z.string().min(1).max(4e3),
|
|
3306
|
+
/** Capture it as a REMINDER — a note assigned to the user themselves, which rides
|
|
3307
|
+
* their next call instead of being handed to an agent. Everything else about the
|
|
3308
|
+
* note is identical; this is the one parameter that separates the two. */
|
|
3309
|
+
forMe: z.boolean().optional(),
|
|
3310
|
+
/** The not-before, when the user already said one. Absent = the very next call. */
|
|
3311
|
+
dueAt: z.string().datetime().optional(),
|
|
3312
|
+
repeat: NoteRepeatSchema.optional()
|
|
3205
3313
|
});
|
|
3206
3314
|
var RecordDecisionSchema = z.object({
|
|
3207
3315
|
/** An open decision (from /clarify) to answer. */
|
package/dist/enable.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HandoffSchema,
|
|
4
4
|
MISSED_CALL_PLAN
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-45CIZLCJ.js";
|
|
6
6
|
import {
|
|
7
7
|
ENABLE_COMMAND,
|
|
8
8
|
PAIGY_TOOL_IDS,
|
|
9
9
|
autoConfigureClients,
|
|
10
10
|
claudeInstallHint,
|
|
11
11
|
paigyToolsAllowlisted
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-G2WR7FCJ.js";
|
|
13
13
|
import {
|
|
14
14
|
clearSurface,
|
|
15
15
|
writeSurface
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
startE2ee,
|
|
52
52
|
submitNotification,
|
|
53
53
|
whoAmI
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-X42EUXML.js";
|
|
55
55
|
|
|
56
56
|
// src/index.ts
|
|
57
57
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -140,7 +140,7 @@ var CONTACT_SCHEMA = {
|
|
|
140
140
|
};
|
|
141
141
|
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 ("${STANDARD_MEANS}"), 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. ANSWERABLE, NOT JUST ASKED: when the reply comes back carrying \`plan.units[].needs\`, that unit asked for something it gave the user no way to answer \u2014 'options' means it posed a choice with nothing to choose from, 'visuals' means it asked about something to look at with nothing to look at. Send it again on the SAME parentId with 2-4 options (or the image), drawn from your own sentence. Paigy will not add them for you: a shape it guessed wrong cannot be undone, and you are the one who knows what the real alternatives are. \`units\` reports WHAT BECAME OF YOUR PROSE \u2014 { kept, raw, why }: how many topics Paigy compressed for delivery, how many kept your exact words, and the reason when it kept them (e.g. 'no_output' = compression produced nothing usable, so the user got your raw sentence). It needs no action and is not an error \u2014 read it only when the delivered wording matters to you; a high \`raw\` count means the user is hearing you verbatim.`;
|
|
142
142
|
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_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.";
|
|
143
|
-
var SERVER_INSTRUCTIONS = "On startup, call check_replies once to pick up any replies or pending work you missed while away. A check_replies request whose parentId you don't recognize, or one carrying a contextParentId, means the user is resuming or seeding a past conversation \u2014 call get_thread on it FIRST and treat the transcript as prior conversation, not new input. To wait for the answer to something you just asked, call await_reply with that notificationId \u2014 it's scoped to that one notification, so it never returns replies meant for other notifications. Use check_replies again only when re-booting or after waiting a long time on something else. Never end a turn that still needs the user without contact + await_reply. When you need a decision or input, WRITE THE QUESTION and Paigy derives the answer shape from it \u2014 there is no shape parameter to set, and passing one is an error. Ask a yes/no question and they get yes/no; ask them to approve an action and they get approve/deny; ask them to pick, and to pick several, and to rank, and each gets the control it needs. So phrase the ask precisely: 'which of these should I do first' and 'should I do this' are different questions and become different answers. The one thing the prose can't supply is the CHOICES themselves \u2014 when you're asking them to pick between concrete alternatives, pass `options` (2-4 of them, drawn from your own sentence), because Paigy will not invent alternatives it can't know. On a { kind: 'clarify' } reply, see contact's own description for how to respond. When you send waiting:'hard' (or the user asked you to call), remember the ask may be spoken aloud \u2014 write it short and conversational, and name things instead of using IDs (e.g. 'the pull request about the agents page', not 'PR #235'). When the user asks you to follow up later \u2014 when you're done, if you're blocked, or at a set time \u2014 record it with schedule_callback so you don't drop it if you go idle. If you're about to start a genuinely long-running or blocking piece of work \u2014 one where the user would otherwise sit and wait \u2014 mention ONCE, in passing, that you can reach them when it's done or if you hit a blocker, instead of them needing to babysit the terminal. Don't offer this for quick tasks, and don't repeat the offer if they've already said yes or no earlier in the conversation. NEVER go quietly idle while something might still be pending for you: whenever you end a turn with any Paigy notification unanswered (or any chance the user replied through the app while you worked), schedule your own ~2-minute wake-up (harness ScheduleWakeup or equivalent) and call check_replies when it fires; if still nothing, re-schedule and keep looping until resolved or the user says stop. For legibility, always use this exact wording \u2014 reason: 'Paigy idle check \u2014 waiting on <thing>', wake-up prompt: 'Paigy idle check: call check_replies and engage with anything unacknowledged; if idle, re-schedule (~2min).' \u2014 so the user can recognize every idle check at a glance. This self-polling in your own live session (full context intact) is the PRIMARY mechanism; the plugin's Stop hooks are only the dead-session safety net.";
|
|
143
|
+
var SERVER_INSTRUCTIONS = "On startup, call check_replies once to pick up any replies or pending work you missed while away. A check_replies request whose parentId you don't recognize, or one carrying a contextParentId, means the user is resuming or seeding a past conversation \u2014 call get_thread on it FIRST and treat the transcript as prior conversation, not new input. To wait for the answer to something you just asked, call await_reply with that notificationId \u2014 it's scoped to that one notification, so it never returns replies meant for other notifications. Use check_replies again only when re-booting or after waiting a long time on something else. Never end a turn that still needs the user without contact + await_reply \u2014 WHEN THEY ARE NOT THERE TO ASK. In an interactive session with the user at the terminal, the prompt IS the channel: ending your turn with the question already reaches them, faster and richer than a push, so do NOT send one for an ordinary blocked-or-done moment. Reach for Paigy exactly when the terminal is not enough \u2014 work that will run more than a few minutes unattended (a long build, a deploy, a background job, a cloud session), anything you finish or get stuck on while they are away, or when they have said they are stepping out. If you cannot tell which situation you are in, ask them once, in passing, how they want to be reached, and follow that for the rest of the session. When you need a decision or input, WRITE THE QUESTION and Paigy derives the answer shape from it \u2014 there is no shape parameter to set, and passing one is an error. Ask a yes/no question and they get yes/no; ask them to approve an action and they get approve/deny; ask them to pick, and to pick several, and to rank, and each gets the control it needs. So phrase the ask precisely: 'which of these should I do first' and 'should I do this' are different questions and become different answers. The one thing the prose can't supply is the CHOICES themselves \u2014 when you're asking them to pick between concrete alternatives, pass `options` (2-4 of them, drawn from your own sentence), because Paigy will not invent alternatives it can't know. On a { kind: 'clarify' } reply, see contact's own description for how to respond. When you send waiting:'hard' (or the user asked you to call), remember the ask may be spoken aloud \u2014 write it short and conversational, and name things instead of using IDs (e.g. 'the pull request about the agents page', not 'PR #235'). When the user asks you to follow up later \u2014 when you're done, if you're blocked, or at a set time \u2014 record it with schedule_callback so you don't drop it if you go idle. If you're about to start a genuinely long-running or blocking piece of work \u2014 one where the user would otherwise sit and wait \u2014 mention ONCE, in passing, that you can reach them when it's done or if you hit a blocker, instead of them needing to babysit the terminal. Don't offer this for quick tasks, and don't repeat the offer if they've already said yes or no earlier in the conversation. NEVER go quietly idle while something might still be pending for you: whenever you end a turn with any Paigy notification unanswered (or any chance the user replied through the app while you worked), schedule your own ~2-minute wake-up (harness ScheduleWakeup or equivalent) and call check_replies when it fires; if still nothing, re-schedule and keep looping until resolved or the user says stop. For legibility, always use this exact wording \u2014 reason: 'Paigy idle check \u2014 waiting on <thing>', wake-up prompt: 'Paigy idle check: call check_replies and engage with anything unacknowledged; if idle, re-schedule (~2min).' \u2014 so the user can recognize every idle check at a glance. This self-polling in your own live session (full context intact) is the PRIMARY mechanism; the plugin's Stop hooks are only the dead-session safety net.";
|
|
144
144
|
|
|
145
145
|
// src/pairing.ts
|
|
146
146
|
async function resolvePairing(deviceCode, capMs, pollMs = 2e3) {
|
|
@@ -468,7 +468,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
468
468
|
},
|
|
469
469
|
{
|
|
470
470
|
name: "check_replies",
|
|
471
|
-
description: "The catch-up sweep for everything outstanding \u2014 a PURE read, takes no arguments, safe to call as often as you like: nothing here is consumed by reading it. Returns `replies` (answers to notifications you sent), your still-pending notifications, and `requests` \u2014 requests the user started toward you (each { notificationId, parentId, text }). Each keeps reappearing on every call until you actually engage with it: call set_task_state on its notificationId, which is what claims/acknowledges it \u2014 a human-initiated reply or request must never be silently dropped just because you read the list without acting. Also returns `threads` \u2014 the SAME replies + requests grouped by conversation, oldest thread first, each with a `busy` flag and its `items` in arrival order. WORK ONE THREAD AT A TIME: take the oldest thread whose `busy` is false, handle ALL of its items together in a single turn (one set_task_state), then go to the next \u2014 don't interleave threads item-by-item. A `busy` thread already has a turn in progress; leave it and let its new items ride the next turn. Use check_replies when booting up / starting a session, or when you've been waiting a long time on something else. To wait on an answer to a contact call you just made, use await_reply instead. Also returns owedCallbacks: callbacks now due that you promised \u2014 fulfill each with contact on its parentId. Also returns `stalled`: work (either direction) you reported in_progress via set_task_state a while ago and never reported completed \u2014 likely left half-done by this session or a prior one that crashed or went idle. For each, either continue the work and report a real state, or investigate why it stalled. Replies may carry `intents`/`transcript`/`covered` (call-mapped answers) \u2014 handle intents exactly as await_reply's description says (defer \u2192 schedule_callback now; delegate \u2192 decide and say so; channel \u2192 honor next contact), and treat a `covered` list missing one of your declared points as that part still unanswered.",
|
|
471
|
+
description: "The catch-up sweep for everything outstanding \u2014 a PURE read, takes no arguments, safe to call as often as you like: nothing here is consumed by reading it. Returns `replies` (answers to notifications you sent), your still-pending notifications, and `requests` \u2014 requests the user started toward you (each { notificationId, parentId, text }). Each keeps reappearing on every call until you actually engage with it: call set_task_state on its notificationId, which is what claims/acknowledges it \u2014 a human-initiated reply or request must never be silently dropped just because you read the list without acting. Also returns `threads` \u2014 the SAME replies + requests grouped by conversation, oldest thread first, each with a `busy` flag and its `items` in arrival order. WORK ONE THREAD AT A TIME: take the oldest thread whose `busy` is false, handle ALL of its items together in a single turn (one set_task_state), then go to the next \u2014 don't interleave threads item-by-item. A `busy` thread already has a turn in progress; leave it and let its new items ride the next turn. Use check_replies when booting up / starting a session, or when you've been waiting a long time on something else. To wait on an answer to a contact call you just made, use await_reply instead. Also returns owedCallbacks: callbacks now due that you promised \u2014 fulfill each with contact on its parentId. EVERY Paigy reply \u2014 this one, await_reply's, and contact's \u2014 may carry `also`: work assigned to you that no wake could reach, handed to you because you happened to be here. It is NOT what you asked about and it is never urgent: FINISH what you came for first, then take it up. Each entry has a `noteId` and the owner's own words; report on its `parentId` thread when it has one, and call set_task_state on that thread as you would for any assigned work. Ignoring it costs nothing \u2014 it rides your next reply too. Also returns `stalled`: work (either direction) you reported in_progress via set_task_state a while ago and never reported completed \u2014 likely left half-done by this session or a prior one that crashed or went idle. For each, either continue the work and report a real state, or investigate why it stalled. Also returns `you` \u2014 WHICH IDENTITY you are speaking as ({ name, device, tokenId }), the same name and device the user sees on their Agents screen. This is the only safe way to find out (calling `pair` can MINT a new identity instead of telling you about the current one). Use it when the user asks who you are, and to tell whether work addressed to a name is addressed to you. A request may also carry `stranded`: it was addressed to ANOTHER agent on this account (that name) which has not been seen since it landed, so nobody came for it and it is handed to you because you are the session that is here. Take it exactly like your own \u2014 set_task_state claims it, reply with contact on its parentId \u2014 and say whose it was, because the user picked that agent on purpose. Replies may carry `intents`/`transcript`/`covered` (call-mapped answers) \u2014 handle intents exactly as await_reply's description says (defer \u2192 schedule_callback now; delegate \u2192 decide and say so; channel \u2192 honor next contact), and treat a `covered` list missing one of your declared points as that part still unanswered.",
|
|
472
472
|
inputSchema: json(z.object({}))
|
|
473
473
|
},
|
|
474
474
|
{
|
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-45CIZLCJ.js";
|
|
6
6
|
import {
|
|
7
7
|
checkReplies,
|
|
8
8
|
registerDelivery
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-X42EUXML.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-G2WR7FCJ.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-X42EUXML.js";
|
|
21
21
|
|
|
22
22
|
// src/onboard.ts
|
|
23
23
|
async function main() {
|
package/dist/statusline.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paigy/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
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",
|
|
@@ -23,13 +23,6 @@
|
|
|
23
23
|
"url": "git+https://github.com/mauurda/paigy.git",
|
|
24
24
|
"directory": "apps/mcp"
|
|
25
25
|
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "tsup",
|
|
28
|
-
"dev": "tsup --watch",
|
|
29
|
-
"typecheck": "tsc --noEmit",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"prepublishOnly": "pnpm --filter @paigy/schema build && pnpm --filter @paigy/crypto build && pnpm --filter @paigy/sdk build && pnpm build"
|
|
32
|
-
},
|
|
33
26
|
"dependencies": {
|
|
34
27
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
35
28
|
"@supabase/supabase-js": "^2.47.10",
|
|
@@ -39,13 +32,19 @@
|
|
|
39
32
|
"zod-to-json-schema": "^3.24.1"
|
|
40
33
|
},
|
|
41
34
|
"devDependencies": {
|
|
42
|
-
"@paigy/crypto": "workspace:*",
|
|
43
|
-
"@paigy/schema": "workspace:*",
|
|
44
|
-
"@paigy/sdk": "workspace:*",
|
|
45
35
|
"@types/node": "^22.0.0",
|
|
46
36
|
"@types/qrcode-generator": "^1.0.6",
|
|
47
37
|
"tsup": "^8.3.5",
|
|
48
38
|
"typescript": "^5.7.2",
|
|
49
|
-
"vitest": "^2.1.8"
|
|
39
|
+
"vitest": "^2.1.8",
|
|
40
|
+
"@paigy/crypto": "0.0.0",
|
|
41
|
+
"@paigy/schema": "0.0.0",
|
|
42
|
+
"@paigy/sdk": "0.2.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup",
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"test": "vitest run"
|
|
50
49
|
}
|
|
51
|
-
}
|
|
50
|
+
}
|