@paigy/mcp 0.40.1 → 0.40.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -22
- package/dist/{chunk-TLASZUJD.js → chunk-4JDG67EF.js} +2 -2
- package/dist/{chunk-4UZAPBKK.js → chunk-6QQHTUKJ.js} +195 -325
- package/dist/{chunk-CXABA27N.js → chunk-TNQOFCZC.js} +1 -1
- package/dist/{chunk-3QUIB42M.js → chunk-V3VVCML7.js} +113 -142
- package/dist/enable.js +3 -3
- package/dist/index.js +4 -4
- package/dist/listen.js +34 -102
- package/dist/onboard.js +4 -4
- package/dist/slot.js +1 -1
- package/dist/statusline.js +1 -1
- package/package.json +13 -13
|
@@ -36,7 +36,22 @@ function mcpInputSchema(s) {
|
|
|
36
36
|
}
|
|
37
37
|
var StartContactSchema = z.object({
|
|
38
38
|
goalIds: z.tuple([z.string().uuid()]),
|
|
39
|
-
|
|
39
|
+
// THE ASK IS THE QUESTION, not a bulletin with a question at the end (2026-09-16).
|
|
40
|
+
//
|
|
41
|
+
// A DecisionNeed is settled only by an answer in the shape THIS ask declares. Bundle news,
|
|
42
|
+
// findings and a decision into one, and the person answers whichever part engaged them —
|
|
43
|
+
// which settles nothing, leaves the need open, and gets them asked again. On call 619c5a92
|
|
44
|
+
// one ask carried a briefing, a side question ("the ring should show the agent's name now —
|
|
45
|
+
// what did you actually see?") and a one-of-four about how far an erase reaches. The owner
|
|
46
|
+
// answered the side question. The need stayed open; the card asked again. Of the 45 call
|
|
47
|
+
// asks carrying a decision, 27 are over 600 characters.
|
|
48
|
+
//
|
|
49
|
+
// Sending them separately costs nothing, because a CALL contact joins the call already
|
|
50
|
+
// happening (`goal/store.ts`, JOIN BEFORE MINTING) — several contacts on one Goal arrive as
|
|
51
|
+
// one call, planned as its turns, and each keeps its own settleable need.
|
|
52
|
+
ask: z.string().trim().min(1).max(1e4).describe(
|
|
53
|
+
"The question, and only what is needed to answer it. News, progress and findings are their own contact \u2014 a call contact JOINS a call already happening, so several arrive as one call. Do not bundle: a DecisionNeed is settled only by an answer in the shape this ask declares, so someone who answers the part that interested them settles nothing and is asked again."
|
|
54
|
+
),
|
|
40
55
|
waiting: z.enum(["none", "hard"]).default("none"),
|
|
41
56
|
channel: z.enum(["notification", "call"]).default("notification"),
|
|
42
57
|
options: z.array(z.object({ label: z.string().trim().min(1).max(1e3), image: z.string().url().optional(), html: z.string().max(16384).optional() }).strict()).min(2).max(6).optional(),
|
|
@@ -48,12 +63,25 @@ var CONTACT_DESCRIPTION = "Contact the user about exactly one existing Goal: pas
|
|
|
48
63
|
var CreateGoalSchema = z2.object({
|
|
49
64
|
outcome: z2.string().trim().min(1).max(1e4),
|
|
50
65
|
ownerParticipant: z2.string().trim().min(1).optional(),
|
|
51
|
-
idempotencyKey: z2.string().trim().min(1).max(200)
|
|
66
|
+
idempotencyKey: z2.string().trim().min(1).max(200),
|
|
67
|
+
/** A past conversation this Goal should be read against — History's "new session from this"
|
|
68
|
+
* (owner, on the call of 2026-09-14: "let's do the reference with the threading"). A
|
|
69
|
+
* reference only: the owner reads it through `get_thread`, which does its own scoping, and
|
|
70
|
+
* the writer refuses a thread belonging to another account. */
|
|
71
|
+
contextThreadId: z2.string().uuid().optional(),
|
|
72
|
+
/** THE GOAL THIS ONE BELONGS UNDER (owner, 2026-09-15: "the ask I gave for the design doc
|
|
73
|
+
* didn't get created as a child goal of the voice UI goal, which is how it should've
|
|
74
|
+
* worked"). It could not have been: this door took no parent, so the only route was
|
|
75
|
+
* create -> claim -> `update_goal`, three calls with a lease in the middle, and every agent
|
|
76
|
+
* took the short one. The hierarchy has been modelled since Goals existed and had been used
|
|
77
|
+
* ZERO times in 2,031 of them. Absent, the server judges it against the caller's open Goals
|
|
78
|
+
* (`apps/api/src/goal/intake.ts`). The writer refuses a Goal belonging to another account. */
|
|
79
|
+
parentGoalId: z2.string().uuid().optional()
|
|
52
80
|
});
|
|
53
81
|
var CreateGoalToolSchema = CreateGoalSchema.extend({
|
|
54
82
|
idempotencyKey: CreateGoalSchema.shape.idempotencyKey.optional().describe("Optional. One is minted per call; pass your own only so a retry lands on the same Goal.")
|
|
55
83
|
}).strict();
|
|
56
|
-
var CREATE_GOAL_DESCRIPTION = "Create a durable
|
|
84
|
+
var CREATE_GOAL_DESCRIPTION = "Create a durable Goal for an outcome. Without parentGoalId it is placed against your open Goals: if one already IS this work, that Goal comes back (existing: true) and nothing new is created \u2014 continue it; if the work belongs under one, it is created there (parentGoalId in the receipt); otherwise it is a root. Pass parentGoalId yourself to put it under a specific Goal. Admission only: the owner must claim it before doing work, then update it as it advances. Returns an admission receipt with goalId, current state, revision, ownerParticipant, and the next step; no Goal content or execution lease.";
|
|
57
85
|
var UpdateGoalSchema = z2.object({
|
|
58
86
|
revision: z2.number().int().positive(),
|
|
59
87
|
changes: z2.object({
|
|
@@ -99,6 +127,21 @@ var AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
|
|
|
99
127
|
function serverInstructions(_opts) {
|
|
100
128
|
return "On startup and after a wake, call check_replies for the open Deliveries addressed to you, and claim_goal for your runnable or review-pending Goal and its durable Entries and accepted decisions. get_goal rereads the same evidence without consuming it. Contact only for an existing Goal. Notifications return immediately: keep working and collect answers through claim_goal/get_goal. On stdio, Calls hold one bounded window; on hosted MCP, Calls return after one read; continue with contact({deliveryId}) to reread that exact Delivery. Evidence can repeat: reads do not acknowledge or hide it. Use update_goal to report progress and explicitly acknowledge review. Never infer ringing from an open Call Delivery. Soft waiting, multipart contact, and re-presentation are unsupported. In an interactive session, ask the user directly; use Paigy when they asked for it or are away.";
|
|
101
129
|
}
|
|
130
|
+
function entryWords(entry) {
|
|
131
|
+
const content = entry.content;
|
|
132
|
+
if (content && "sealed" in content) return "";
|
|
133
|
+
const plain = content?.plain;
|
|
134
|
+
if (typeof plain === "string") return plain;
|
|
135
|
+
if (plain && typeof plain === "object") {
|
|
136
|
+
const text = plain.text;
|
|
137
|
+
if (typeof text === "string") return text;
|
|
138
|
+
const title = plain.title;
|
|
139
|
+
const description = plain.description;
|
|
140
|
+
const parts = [title, ...Array.isArray(description) ? description : []].filter((v) => typeof v === "string");
|
|
141
|
+
if (parts.length) return parts.join("\n\n");
|
|
142
|
+
}
|
|
143
|
+
return entry.sources.map((source) => source.text).join("\n");
|
|
144
|
+
}
|
|
102
145
|
var ContextSchema = z3.object({
|
|
103
146
|
title: z3.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
104
147
|
description: z3.array(z3.string().min(1)).describe(
|
|
@@ -329,16 +372,6 @@ var NotifyRequestSchema = NotifyRequestFields.superRefine((r, ctx) => {
|
|
|
329
372
|
});
|
|
330
373
|
var NotifyStatusSchema = z3.enum(["pending", "answered", "ignored"]);
|
|
331
374
|
var AgentStateSchema = z3.enum(["idle", "in_progress", "completed", "needs_input"]);
|
|
332
|
-
var SetTaskStateSchema = z3.object({
|
|
333
|
-
state: z3.enum(["in_progress", "completed", "needs_input"])
|
|
334
|
-
});
|
|
335
|
-
var SetWorkStateSchema = z3.object({
|
|
336
|
-
workId: z3.string().uuid().optional(),
|
|
337
|
-
notificationId: z3.string().optional(),
|
|
338
|
-
state: SetTaskStateSchema.shape.state
|
|
339
|
-
}).refine((value) => Number(Boolean(value.workId)) + Number(Boolean(value.notificationId)) === 1, {
|
|
340
|
-
message: "exactly one of workId or notificationId is required"
|
|
341
|
-
});
|
|
342
375
|
var TurnSchema = z3.object({
|
|
343
376
|
prompt: z3.string(),
|
|
344
377
|
reply: z3.string()
|
|
@@ -468,108 +501,6 @@ var AwaitItemSchema = z3.discriminatedUnion("type", [
|
|
|
468
501
|
})
|
|
469
502
|
]);
|
|
470
503
|
var CallbackTriggerSchema = z3.enum(["on_done", "on_blocked", "scheduled"]);
|
|
471
|
-
var ScheduleCallbackSchema = z3.object({
|
|
472
|
-
parentId: z3.string().describe("The thread to call back on (from a prior contact / reply / request)."),
|
|
473
|
-
goalId: z3.string().uuid().optional().describe("The Goal this callback advances; preferred for Goal-owned work."),
|
|
474
|
-
trigger: CallbackTriggerSchema,
|
|
475
|
-
dueInSeconds: z3.number().int().positive().optional().describe("For 'scheduled' only: how many seconds from now to fire."),
|
|
476
|
-
note: z3.string().optional().describe("What to tell the user when you follow up.")
|
|
477
|
-
});
|
|
478
|
-
var PendingRepliesSchema = z3.object({
|
|
479
|
-
replies: z3.array(
|
|
480
|
-
z3.object({
|
|
481
|
-
parentId: z3.string(),
|
|
482
|
-
notificationId: z3.string(),
|
|
483
|
-
workId: z3.string().uuid().optional(),
|
|
484
|
-
decisionId: z3.string().uuid().optional(),
|
|
485
|
-
answer: UserAnswerSchema,
|
|
486
|
-
/** E2EE: the sealed answer (opaque envelope + plaintext `ignored` hint) when the
|
|
487
|
-
* pairing is E2EE — the agent opens it and re-derives the real answer. Absent =
|
|
488
|
-
* plaintext answer (in `answer`). See AwaitItemSchema's reply variant. */
|
|
489
|
-
sealed: z3.lazy(() => SealedAnswerSchema).optional(),
|
|
490
|
-
/** The call record rendered for THIS agent: the raw words the shaped answer was
|
|
491
|
-
* mapped from, filtered to its own claims. See AwaitItemSchema's reply variant. */
|
|
492
|
-
transcript: z3.string().optional(),
|
|
493
|
-
/** Coverage report (#396): which declared `points` this answer addressed. */
|
|
494
|
-
covered: z3.array(z3.string()).optional()
|
|
495
|
-
})
|
|
496
|
-
),
|
|
497
|
-
pending: z3.array(
|
|
498
|
-
z3.object({ parentId: z3.string(), notificationId: z3.string(), createdAt: z3.string() })
|
|
499
|
-
),
|
|
500
|
-
/** WHO YOU ARE on this account (field report 2026-08-28): the name and device the user
|
|
501
|
-
* sees for this session's identity. From inside a session there was no way to find out —
|
|
502
|
-
* `pair` with no arguments can HATCH a fresh identity, so it is not a safe probe — and an
|
|
503
|
-
* agent that cannot tell which agent it is cannot tell whether work addressed to
|
|
504
|
-
* "Reta" was addressed to it. Absent only for a token with no pairing behind it. */
|
|
505
|
-
you: z3.object({ name: z3.string(), device: z3.string().nullable(), tokenId: z3.string() }).optional(),
|
|
506
|
-
/** User-initiated requests addressed to this agent; act on them and reply via
|
|
507
|
-
* contact on the same parentId. Keeps reappearing until you call
|
|
508
|
-
* set_work_state on its workId (or notificationId once while bootstrapping). */
|
|
509
|
-
requests: z3.array(
|
|
510
|
-
z3.object({
|
|
511
|
-
parentId: z3.string(),
|
|
512
|
-
notificationId: z3.string(),
|
|
513
|
-
workId: z3.string().uuid().optional(),
|
|
514
|
-
text: z3.string(),
|
|
515
|
-
createdAt: z3.string(),
|
|
516
|
-
/** The user seeded this request with a past conversation — call get_thread on it
|
|
517
|
-
* FIRST and treat the transcript as prior context (#57/#251). */
|
|
518
|
-
contextParentId: z3.string().optional(),
|
|
519
|
-
/** STRANDED (field report 2026-08-28): this request was addressed to ANOTHER agent on
|
|
520
|
-
* the account — the name here — which has not been seen since it landed, so nobody
|
|
521
|
-
* came for it. Handed to you because you are the session that is here. Take it like
|
|
522
|
-
* any request (set_work_state claims it, reply with contact on its parentId), and say
|
|
523
|
-
* whose it was, because the user chose that agent on purpose. */
|
|
524
|
-
stranded: z3.string().optional()
|
|
525
|
-
})
|
|
526
|
-
),
|
|
527
|
-
/** Durable outcomes currently owned by this agent. This is the Work-native queue; flat
|
|
528
|
-
* notification lists remain during migration so old clients keep their existing view. */
|
|
529
|
-
work: z3.array(z3.object({
|
|
530
|
-
workId: z3.string().uuid(),
|
|
531
|
-
parentId: z3.string().optional(),
|
|
532
|
-
objective: z3.string().nullable(),
|
|
533
|
-
state: z3.enum(["active", "blocked", "waiting_external"]),
|
|
534
|
-
blockedOn: z3.array(z3.string().uuid()),
|
|
535
|
-
updatedAt: z3.string()
|
|
536
|
-
})).optional(),
|
|
537
|
-
/** Callbacks you owe the user that are now DUE (you said you'd follow up when done,
|
|
538
|
-
* if blocked, or at a time that has passed). Re-surfaced every sweep until you
|
|
539
|
-
* fulfill one by calling contact on its parentId. */
|
|
540
|
-
/** Ride-alongs (RideAlongSchema): notes assigned to this agent that no wake could
|
|
541
|
-
* reach. Same array the contact/await replies carry — one queue, every carrier. */
|
|
542
|
-
also: z3.array(RideAlongSchema).optional(),
|
|
543
|
-
owedCallbacks: z3.array(
|
|
544
|
-
z3.object({ parentId: z3.string(), trigger: CallbackTriggerSchema, note: z3.string() })
|
|
545
|
-
),
|
|
546
|
-
/** Work (either direction) you reported in_progress a while ago and never reported
|
|
547
|
-
* completed — likely left half-done by this session or a prior one that crashed or
|
|
548
|
-
* went idle. Report a real state (set_work_state) or continue the work. */
|
|
549
|
-
stalled: z3.array(
|
|
550
|
-
z3.object({ parentId: z3.string(), notificationId: z3.string(), title: z3.string().nullable(), startedAt: z3.string() })
|
|
551
|
-
),
|
|
552
|
-
/** The queue rail (#614, pending/design.md): the same replies + requests, grouped by
|
|
553
|
-
* thread and ordered oldest-thread-first, so you work ONE thread at a time — fold all of
|
|
554
|
-
* a thread's `items` into a single turn rather than interleaving threads. `busy` = the
|
|
555
|
-
* thread already has a turn in progress (younger than the stall cutoff); let it finish and
|
|
556
|
-
* ride the next turn. `items` are that thread's replies/requests in arrival order; the
|
|
557
|
-
* full payload for each is in the flat `replies`/`requests` arrays (matched by
|
|
558
|
-
* notificationId). Derived, never stored — a crashed agent recomputes it exactly. */
|
|
559
|
-
threads: z3.array(
|
|
560
|
-
z3.object({
|
|
561
|
-
parentId: z3.string(),
|
|
562
|
-
busy: z3.boolean(),
|
|
563
|
-
items: z3.array(
|
|
564
|
-
z3.object({
|
|
565
|
-
kind: z3.enum(["reply", "request"]),
|
|
566
|
-
notificationId: z3.string(),
|
|
567
|
-
at: z3.string()
|
|
568
|
-
})
|
|
569
|
-
)
|
|
570
|
-
})
|
|
571
|
-
)
|
|
572
|
-
});
|
|
573
504
|
var NotifyResponseSchema = z3.object({
|
|
574
505
|
notificationId: z3.string(),
|
|
575
506
|
workId: z3.string().uuid().optional(),
|
|
@@ -797,7 +728,21 @@ var InboxItemSchema = z3.object({
|
|
|
797
728
|
/** E2EE: the agent's device X25519 public key to seal the user's answer BACK to (the
|
|
798
729
|
* sender the phone replies to). Sourced server-side from this item's pairing credential.
|
|
799
730
|
* Present only alongside `envelope`; the phone seals via sealAnswer(answer, this, id). */
|
|
800
|
-
agentX25519: z3.string().optional()
|
|
731
|
+
agentX25519: z3.string().optional(),
|
|
732
|
+
/** THE TARGET FACTS A CARD RENDERS (#1796 point 5, 2026-09-11): the Delivery it is a view of,
|
|
733
|
+
* that Delivery's kind, the request Entry, the Goals it answers for, the exact DecisionNeed (none
|
|
734
|
+
* for a request that asks nothing), whether its content is sealed, and that Goal's state. The
|
|
735
|
+
* answer writer (`POST /api/entries`) and the disposition (`close_delivery`) take their ids from
|
|
736
|
+
* here. The server projects it (`apps/api/src/inbox/project.ts`); a client never builds it. */
|
|
737
|
+
communication: z3.object({
|
|
738
|
+
deliveryId: z3.string(),
|
|
739
|
+
kind: z3.enum(["notification", "call"]),
|
|
740
|
+
entryId: z3.string(),
|
|
741
|
+
goalIds: z3.array(z3.string()),
|
|
742
|
+
decisionNeedId: z3.string().optional(),
|
|
743
|
+
sealed: z3.boolean(),
|
|
744
|
+
goalState: z3.string().optional()
|
|
745
|
+
}).optional()
|
|
801
746
|
});
|
|
802
747
|
var SnoozeRequestSchema = z3.object({
|
|
803
748
|
requestId: z3.string(),
|
|
@@ -1033,26 +978,54 @@ var CreateRequestSchema = z3.object({
|
|
|
1033
978
|
* the agent reads it via get_thread. Must belong to the requesting user. */
|
|
1034
979
|
contextParentId: z3.string().optional()
|
|
1035
980
|
});
|
|
1036
|
-
var
|
|
1037
|
-
/**
|
|
1038
|
-
|
|
1039
|
-
/**
|
|
1040
|
-
|
|
1041
|
-
/**
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
/**
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
981
|
+
var QueueQuestionSchema = z3.object({
|
|
982
|
+
/** The decision need's id — what an answer is accepted against. */
|
|
983
|
+
id: z3.string(),
|
|
984
|
+
/** The words that were asked, from the request Entry that asked them. */
|
|
985
|
+
question: z3.string(),
|
|
986
|
+
/** Where it was asked — which is where the ruling goes (`POST /api/entries`). Null only
|
|
987
|
+
* for a need whose request Entry is carried by no interactive Delivery, which nothing
|
|
988
|
+
* can answer. */
|
|
989
|
+
deliveryId: z3.string().nullable().default(null),
|
|
990
|
+
/** The Entry the ruling is about. */
|
|
991
|
+
aboutId: z3.string().nullable().default(null),
|
|
992
|
+
/** Empty for a free-text question. */
|
|
993
|
+
options: z3.array(OptionSchema).default([]),
|
|
994
|
+
select: z3.enum(["one", "many", "rank", "confirm", "text"]).default("text"),
|
|
995
|
+
askedAt: z3.string(),
|
|
996
|
+
/** Null while the question is open — which is how the page tells the two apart. */
|
|
997
|
+
answeredAt: z3.string().nullable().default(null),
|
|
998
|
+
/** The ruling in the person's own words, from the contribution that replied — not the
|
|
999
|
+
* option id, which is not something anyone reads back. Null while it is open, and null
|
|
1000
|
+
* for a settled question whose reply carried nothing readable. */
|
|
1001
|
+
answer: z3.string().nullable().default(null)
|
|
1002
|
+
});
|
|
1003
|
+
var QueueItemSchema = z3.object({
|
|
1004
|
+
id: z3.string(),
|
|
1005
|
+
/** One-line headline — the first sentence of the outcome. */
|
|
1006
|
+
title: z3.string(),
|
|
1007
|
+
/** The outcome in full, verbatim: the person's own words are what an assignee sees. */
|
|
1008
|
+
intent: z3.string(),
|
|
1009
|
+
/** `ready` | `active` | `waiting` | `done` | `cancelled`, straight off the Goal. */
|
|
1010
|
+
state: z3.string(),
|
|
1011
|
+
/** Who holds it (a participant ref); null when nobody does yet. */
|
|
1012
|
+
assignee: z3.string().nullable().default(null),
|
|
1013
|
+
/** What the agent last said it was doing; null if it has said nothing. */
|
|
1014
|
+
progress: z3.string().nullable().default(null),
|
|
1015
|
+
reviewPending: z3.boolean().default(false),
|
|
1016
|
+
dueAt: z3.string().nullable().default(null),
|
|
1017
|
+
/** The Goal this one was opened under; null at the root. */
|
|
1018
|
+
parentGoalId: z3.string().nullable().default(null),
|
|
1019
|
+
/** Goals opened under this one — only those the same list holds. */
|
|
1020
|
+
childGoalIds: z3.array(z3.string()).default([]),
|
|
1021
|
+
/** Goals this one waits on (start or finish gates). */
|
|
1022
|
+
dependencyGoalIds: z3.array(z3.string()).default([]),
|
|
1023
|
+
/** True while any gate is on a Goal that is not done — the walk draws it dashed. */
|
|
1024
|
+
blocked: z3.boolean().default(false),
|
|
1025
|
+
/** Every decision need on it, open or settled — the page decides which to show. */
|
|
1026
|
+
questions: z3.array(QueueQuestionSchema).default([]),
|
|
1027
|
+
createdAt: z3.string(),
|
|
1028
|
+
updatedAt: z3.string().nullable().default(null)
|
|
1056
1029
|
});
|
|
1057
1030
|
var NoteSourceSchema = z3.enum(["app", "call"]);
|
|
1058
1031
|
var NoteStatusSchema = z3.enum(["open", "assigned", "in_progress", "done"]);
|
|
@@ -1181,8 +1154,6 @@ var AcceptTriageResultSchema = z3.object({
|
|
|
1181
1154
|
failed: z3.array(z3.object({ noteId: z3.string(), reason: z3.string() }))
|
|
1182
1155
|
});
|
|
1183
1156
|
var DeliveryModeSchema = z3.enum(["poll", "self_hosted"]);
|
|
1184
|
-
var WAKE_EVENT = "wake";
|
|
1185
|
-
var wakeChannel = (tokenId) => `wake:${tokenId}`;
|
|
1186
1157
|
var RegisterDeliverySchema = z3.object({ mode: DeliveryModeSchema });
|
|
1187
1158
|
var OAuthStartSchema = z3.object({
|
|
1188
1159
|
provider: z3.enum(["cma"]),
|
|
@@ -1191,8 +1162,9 @@ var OAuthStartSchema = z3.object({
|
|
|
1191
1162
|
var DeliveryConfigSchema = z3.object({
|
|
1192
1163
|
tokenId: z3.string(),
|
|
1193
1164
|
mode: DeliveryModeSchema,
|
|
1194
|
-
/** null when the
|
|
1195
|
-
*
|
|
1165
|
+
/** null when the deployment has no anon key configured. `self_hosted` is then REFUSED
|
|
1166
|
+
* (503 `self_hosted_unavailable`) rather than registered, so a self_hosted config always
|
|
1167
|
+
* carries credentials; only a `poll` registration can come back with null here. */
|
|
1196
1168
|
realtime: z3.object({ url: z3.string(), anonKey: z3.string() }).nullable()
|
|
1197
1169
|
});
|
|
1198
1170
|
var StatusSchema = z3.object({
|
|
@@ -1383,6 +1355,5 @@ export {
|
|
|
1383
1355
|
mcpInputSchema,
|
|
1384
1356
|
AGENT_TOOLS,
|
|
1385
1357
|
serverInstructions,
|
|
1386
|
-
|
|
1387
|
-
wakeChannel
|
|
1358
|
+
entryWords
|
|
1388
1359
|
};
|
package/dist/enable.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
PAIGY_TOOL_IDS,
|
|
4
4
|
enablePaigyTools
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-4JDG67EF.js";
|
|
6
|
+
import "./chunk-V3VVCML7.js";
|
|
7
|
+
import "./chunk-6QQHTUKJ.js";
|
|
8
8
|
|
|
9
9
|
// src/enable.ts
|
|
10
10
|
function main() {
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
resolvePairing,
|
|
6
6
|
startPairing,
|
|
7
7
|
suggestedAgentName
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-TNQOFCZC.js";
|
|
9
9
|
import {
|
|
10
10
|
ENABLE_COMMAND,
|
|
11
11
|
OnboardSchema,
|
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
autoConfigureClients,
|
|
17
17
|
claudeInstallHint,
|
|
18
18
|
paigyToolsAllowlisted
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
19
|
+
} from "./chunk-4JDG67EF.js";
|
|
20
|
+
import "./chunk-V3VVCML7.js";
|
|
21
21
|
import {
|
|
22
22
|
clearSurface,
|
|
23
23
|
writeSurface
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
setIdentity,
|
|
39
39
|
slotName,
|
|
40
40
|
whoAmI
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-6QQHTUKJ.js";
|
|
42
42
|
|
|
43
43
|
// src/index.ts
|
|
44
44
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
package/dist/listen.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "./chunk-3QUIB42M.js";
|
|
3
|
+
entryWords
|
|
4
|
+
} from "./chunk-V3VVCML7.js";
|
|
6
5
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
sweepLeased
|
|
12
|
-
} from "./chunk-4UZAPBKK.js";
|
|
6
|
+
checkReplies,
|
|
7
|
+
getGoal,
|
|
8
|
+
subscribeWake
|
|
9
|
+
} from "./chunk-6QQHTUKJ.js";
|
|
13
10
|
|
|
14
11
|
// src/listen.ts
|
|
15
|
-
import { createClient } from "@supabase/supabase-js";
|
|
16
12
|
import { spawn } from "child_process";
|
|
17
13
|
import { realpathSync } from "fs";
|
|
18
14
|
import { fileURLToPath } from "url";
|
|
@@ -24,7 +20,7 @@ import { homedir } from "os";
|
|
|
24
20
|
import { join } from "path";
|
|
25
21
|
var LABEL = "ai.paigy.listen";
|
|
26
22
|
function frozenEnv() {
|
|
27
|
-
const keys = ["PAIGY_BACKEND_URL", "PAIGY_ON_WAKE"
|
|
23
|
+
const keys = ["PAIGY_BACKEND_URL", "PAIGY_ON_WAKE"];
|
|
28
24
|
return Object.fromEntries(keys.flatMap((k) => process.env[k] ? [[k, process.env[k]]] : []));
|
|
29
25
|
}
|
|
30
26
|
function renderLaunchd(nodePath, scriptPath, env) {
|
|
@@ -114,114 +110,49 @@ function uninstallService() {
|
|
|
114
110
|
return unit;
|
|
115
111
|
}
|
|
116
112
|
|
|
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
|
-
|
|
140
113
|
// src/listen.ts
|
|
141
114
|
var emit = (obj) => void process.stdout.write(JSON.stringify(obj) + "\n");
|
|
142
|
-
function
|
|
143
|
-
|
|
115
|
+
function saidIn(delivery) {
|
|
116
|
+
return delivery.entries.filter((entry) => entry.kind === "contribution").map((entry) => entryWords(entry)).filter(Boolean).join("\n");
|
|
117
|
+
}
|
|
118
|
+
function launchEnv(waiting, reason, goalId) {
|
|
119
|
+
const env = { PAIGY_WORK: JSON.stringify(waiting), PAIGY_EVENT: reason };
|
|
144
120
|
const put = (key, value) => {
|
|
145
121
|
if (value) env[key] = value;
|
|
146
122
|
};
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
if (request) {
|
|
158
|
-
put("PAIGY_THREAD_ID", request.parentId);
|
|
159
|
-
put("PAIGY_PARENT_ID", request.parentId);
|
|
160
|
-
put("PAIGY_NOTIFICATION_ID", request.notificationId);
|
|
161
|
-
put("PAIGY_CONTEXT_THREAD_ID", request.contextParentId);
|
|
162
|
-
put("PAIGY_TEXT", request.text);
|
|
163
|
-
return env;
|
|
164
|
-
}
|
|
165
|
-
const callback = work.owedCallbacks[0];
|
|
166
|
-
if (callback) {
|
|
167
|
-
put("PAIGY_THREAD_ID", callback.parentId);
|
|
168
|
-
put("PAIGY_PARENT_ID", callback.parentId);
|
|
169
|
-
put("PAIGY_TEXT", callback.note);
|
|
170
|
-
}
|
|
123
|
+
const named = goalId ? waiting.deliveries.find((d) => d.goalIds.includes(goalId)) : void 0;
|
|
124
|
+
const lead = named ?? waiting.deliveries[0];
|
|
125
|
+
if (!lead) return env;
|
|
126
|
+
put("PAIGY_THREAD_ID", lead.threadId);
|
|
127
|
+
put("PAIGY_PARENT_ID", lead.threadId);
|
|
128
|
+
put("PAIGY_DELIVERY_ID", lead.deliveryId);
|
|
129
|
+
put("PAIGY_GOAL_ID", waiting.goal?.goalId ?? lead.goalIds[0]);
|
|
130
|
+
put("PAIGY_CONTEXT_THREAD_ID", waiting.goal?.contextThreadId);
|
|
131
|
+
put("PAIGY_TEXT", saidIn(lead) || lead.message);
|
|
171
132
|
return env;
|
|
172
133
|
}
|
|
173
|
-
|
|
174
|
-
async function sweep(reason) {
|
|
134
|
+
async function sweep(reason, goalId) {
|
|
175
135
|
try {
|
|
176
136
|
const cmd = process.env.PAIGY_ON_WAKE;
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const { type: _type, ...reply } = decodeReply({ ...r, type: "reply" });
|
|
186
|
-
return reply;
|
|
187
|
-
})
|
|
188
|
-
};
|
|
189
|
-
emit({ type: "sweep", reason, ...work });
|
|
190
|
-
const inbound = work.replies.length > 0 || work.requests.length > 0;
|
|
191
|
-
const reengage = work.owedCallbacks.length > 0 && (reason === "boot" || reason.includes("callback"));
|
|
192
|
-
if (cmd && (inbound || reengage)) {
|
|
193
|
-
spawn(cmd, { shell: true, stdio: "inherit", env: { ...process.env, ...launchEnv(work, reason) } });
|
|
194
|
-
}
|
|
195
|
-
if (cmd) for (const r of leased) await ackReply({ ...r, type: "reply" }, token);
|
|
196
|
-
if (leased.length === 0) return;
|
|
137
|
+
const { deliveries } = await checkReplies();
|
|
138
|
+
const lead = (goalId ? deliveries.find((d) => d.goalIds.includes(goalId)) : void 0) ?? deliveries[0];
|
|
139
|
+
const leadGoalId = lead?.goalIds[0];
|
|
140
|
+
const goal = leadGoalId ? await getGoal(leadGoalId).then((g) => ({ goalId: leadGoalId, contextThreadId: g.contextThreadId, progress: g.progress })).catch(() => null) : null;
|
|
141
|
+
const waiting = { deliveries, goal };
|
|
142
|
+
emit({ type: "sweep", reason, ...goalId ? { about: goalId } : {}, ...waiting });
|
|
143
|
+
if (cmd && deliveries.length) {
|
|
144
|
+
spawn(cmd, { shell: true, stdio: "inherit", env: { ...process.env, ...launchEnv(waiting, reason, goalId) } });
|
|
197
145
|
}
|
|
198
146
|
} catch (e) {
|
|
199
147
|
emit({ type: "error", reason, message: e.message });
|
|
200
148
|
}
|
|
201
149
|
}
|
|
202
150
|
async function main() {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
const anonKey = cfg.realtime?.anonKey ?? process.env.PAIGY_SUPABASE_ANON_KEY;
|
|
206
|
-
if (!url || !anonKey) {
|
|
207
|
-
throw new Error(
|
|
208
|
-
"No Realtime credentials \u2014 set SUPABASE_ANON_KEY on the server, or PAIGY_SUPABASE_URL + PAIGY_SUPABASE_ANON_KEY locally."
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
const channel = wakeChannel(cfg.tokenId);
|
|
212
|
-
emit({ type: "listening", channel });
|
|
151
|
+
const sub = await subscribeWake((nudge) => void sweep(`wake:${nudge.kind ?? "?"}`, nudge.parentId));
|
|
152
|
+
emit({ type: "listening", channel: sub.channel });
|
|
213
153
|
await sweep("boot");
|
|
214
|
-
const sb = createClient(url, anonKey, { auth: { persistSession: false } });
|
|
215
|
-
sb.channel(channel).on(
|
|
216
|
-
"broadcast",
|
|
217
|
-
{ event: WAKE_EVENT },
|
|
218
|
-
(msg) => void sweep(`wake:${msg.payload?.kind ?? "?"}`)
|
|
219
|
-
).subscribe();
|
|
220
154
|
const shutdown = async () => {
|
|
221
|
-
|
|
222
|
-
await registerDelivery("poll");
|
|
223
|
-
} catch {
|
|
224
|
-
}
|
|
155
|
+
await sub.close();
|
|
225
156
|
process.exit(0);
|
|
226
157
|
};
|
|
227
158
|
process.on("SIGINT", () => void shutdown());
|
|
@@ -251,5 +182,6 @@ if (isEntry()) {
|
|
|
251
182
|
}
|
|
252
183
|
export {
|
|
253
184
|
launchEnv,
|
|
185
|
+
saidIn,
|
|
254
186
|
sweep
|
|
255
187
|
};
|
package/dist/onboard.js
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
resolvePairing,
|
|
4
4
|
startPairing,
|
|
5
5
|
suggestedAgentName
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TNQOFCZC.js";
|
|
7
7
|
import {
|
|
8
8
|
autoConfigureClients,
|
|
9
9
|
claudeInstallHint,
|
|
10
10
|
openBrowser
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-4JDG67EF.js";
|
|
12
|
+
import "./chunk-V3VVCML7.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-
|
|
23
|
+
} from "./chunk-6QQHTUKJ.js";
|
|
24
24
|
|
|
25
25
|
// src/onboard.ts
|
|
26
26
|
function reportRegistered() {
|
package/dist/slot.js
CHANGED
package/dist/statusline.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paigy/mcp",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.3",
|
|
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,28 +24,28 @@
|
|
|
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
|
+
},
|
|
27
34
|
"dependencies": {
|
|
28
35
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
29
|
-
"@supabase/supabase-js": "^2.47.10",
|
|
30
36
|
"qrcode-generator": "^2.0.4",
|
|
31
37
|
"undici": "^6.27.0",
|
|
32
38
|
"zod": "^3.24.1",
|
|
33
39
|
"zod-to-json-schema": "^3.24.1"
|
|
34
40
|
},
|
|
35
41
|
"devDependencies": {
|
|
42
|
+
"@paigy/crypto": "workspace:*",
|
|
43
|
+
"@paigy/schema": "workspace:*",
|
|
44
|
+
"@paigy/sdk": "workspace:*",
|
|
36
45
|
"@types/node": "^22.0.0",
|
|
37
46
|
"@types/qrcode-generator": "^1.0.6",
|
|
38
47
|
"tsup": "^8.3.5",
|
|
39
48
|
"typescript": "^5.7.2",
|
|
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"
|
|
49
|
+
"vitest": "^2.1.8"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|