@paigy/mcp 0.40.16 → 0.40.17
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-JWMR6K6D.js → chunk-7VLZIOLF.js} +2 -2
- package/dist/{chunk-SFGGZLVO.js → chunk-MWHYYLCE.js} +13 -7
- package/dist/{chunk-OGTH55QH.js → chunk-UIZLBPRG.js} +1 -1
- package/dist/{chunk-6OBUH2FB.js → chunk-V5AQASR7.js} +126 -37
- package/dist/{dist-6F22VALF.js → dist-PJLF3B27.js} +1 -1
- package/dist/enable.js +3 -3
- package/dist/index.js +4 -4
- package/dist/listen.js +21 -9
- package/dist/onboard.js +4 -4
- package/dist/slot.js +1 -1
- package/dist/stalled.js +1 -1
- package/dist/statusline.js +1 -1
- package/package.json +1 -1
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
serverInstructions,
|
|
5
5
|
sessionStartHook,
|
|
6
6
|
withSessionStartHook
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MWHYYLCE.js";
|
|
8
8
|
import {
|
|
9
9
|
agentName
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-V5AQASR7.js";
|
|
11
11
|
|
|
12
12
|
// src/toolset.ts
|
|
13
13
|
var ONBOARD_DESCRIPTION = "Get this agent talking to Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call, and it does whatever the situation needs: NOT SET UP \u2192 hatches an identity instantly if this machine holds a device credential (the user ran the Paigy desktop app or harness), otherwise starts the code ceremony; ALREADY SET UP \u2192 returns your current identity and offers the two things left to decide, renaming it or unpairing; TOKEN NO LONGER VALID \u2192 says so, then re-pairs. Pass { name, voice } to choose who you are when hatching, or to RENAME yourself when already set up (voices: rachel, george, jessica, brian, lily). Safe to call any time: idempotent, and it never writes settings \u2014 the tool-allowlist state it reports is read-only. If it returns a `user_code`, print it to the user immediately and call onboard again with the `device_code`. If it returns `enable_command`, PRINT that command for the user to run \u2014 you cannot apply it yourself (it writes your own permission allowlist, which hosts block as privilege escalation), so print it, don't wait for it, and carry on.";
|
|
@@ -68,7 +68,7 @@ var StartContactSchema = z2.object({
|
|
|
68
68
|
}).strict();
|
|
69
69
|
var ContactSchema = z2.union([StartContactSchema, z2.object({ deliveryId: z2.string().uuid() }).strict()]);
|
|
70
70
|
var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
|
|
71
|
-
var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. A bundled ask is taken apart into its own cards by Paigy's one intake read \u2014 parts of one piece of work under one Goal, separate things as separate Goals.";
|
|
71
|
+
var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. A bundled ask is taken apart into its own cards by Paigy's one intake read \u2014 parts of one piece of work under one Goal, separate things as separate Goals. An ask with no options that is not blocking is a report: on a Goal whose report card is still open it is added to that card, with no new push; one that only says where your work stands while under way is recorded as the Goal's progress, not sent (the result says so).";
|
|
72
72
|
var CreateGoalSchema = z3.object({
|
|
73
73
|
outcome: z3.string().trim().min(1).max(1e4),
|
|
74
74
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -115,9 +115,9 @@ var UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({
|
|
|
115
115
|
var ClaimGoalSchema = z3.object({ goalId: z3.string().uuid().optional() }).strict();
|
|
116
116
|
var GetGoalSchema = z3.object({ goalId: z3.string().uuid() }).strict();
|
|
117
117
|
var GET_GOAL_DESCRIPTION = "Read one Goal without claiming it: its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), and `next`, the one step to take. Foreign or sibling-owned Goals are not disclosed.";
|
|
118
|
-
var UPDATE_GOAL_DESCRIPTION =
|
|
118
|
+
var UPDATE_GOAL_DESCRIPTION = `Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. state: done means the work was accomplished, and it can be reopened: when the person says it is not done ("it's not working"), set state: active on that same Goal instead of starting a new one; its parent reopens with it. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
|
|
119
119
|
var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Another agent's Goal that has gone quiet for 3 days (check_replies lists them as stalledOthers) is taken over when you claim it, and becomes yours to finish or cancel. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
|
|
120
|
-
var CHECK_REPLIES_DESCRIPTION = "What is waiting for you: your open Deliveries (a request the user started toward you, a handoff), one row each with its Goals, how many decisions are still open, and the newest words in brief; and `review`, your Goals with something new on them \u2014 the user's answers and notes are Entries on the Goal, not Deliveries. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread a Delivery in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true clears the Goal from `review` and closes the Deliveries addressed to you on it.";
|
|
120
|
+
var CHECK_REPLIES_DESCRIPTION = "What is waiting for you: your open Deliveries (a request the user started toward you, a handoff), one row each with its Goals, how many decisions are still open, and the newest words in brief; `assigned`, your Goals nobody has started yet, however they became yours (claim_goal({goalId}) starts one); and `review`, your Goals with something new on them \u2014 the user's answers and notes are Entries on the Goal, not Deliveries. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread a Delivery in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true clears the Goal from `review` and closes the Deliveries addressed to you on it.";
|
|
121
121
|
var CheckRepliesSchema = z3.object({}).strict();
|
|
122
122
|
var AGENT_TOOLS = [
|
|
123
123
|
{ name: "contact", description: CONTACT_DESCRIPTION, inputSchema: CONTACT_SCHEMA },
|
|
@@ -472,9 +472,9 @@ var AgendaTurnSchema = z4.object({
|
|
|
472
472
|
/** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
|
|
473
473
|
* the planner declares duplicates instead of asking them twice. */
|
|
474
474
|
coveredIds: z4.array(z4.string()).optional(),
|
|
475
|
-
/**
|
|
476
|
-
*
|
|
477
|
-
info: z4.array(z4.string().min(1)).
|
|
475
|
+
/** The spoken sentences of the turn, in order. No count: how long a turn is is the brain's call
|
|
476
|
+
* (owner, 2026-09-25), and a count here refused whole plans. */
|
|
477
|
+
info: z4.array(z4.string().min(1)).default([]),
|
|
478
478
|
question: z4.string().min(1).nullable(),
|
|
479
479
|
/** True on the one turn carrying the agent's own declared question. */
|
|
480
480
|
asks: z4.boolean().optional(),
|
|
@@ -585,6 +585,11 @@ var InboxItemSchema = z4.object({
|
|
|
585
585
|
* and "dismiss the stalled ones" cancelled 37 pieces of live work). */
|
|
586
586
|
cold: z4.string().datetime().optional(),
|
|
587
587
|
clarifies: z4.string().optional(),
|
|
588
|
+
/** THIS CARD'S QUESTION IS ON A LIVE CALL (owner, 2026-09-24: "Mark it while the call is
|
|
589
|
+
* live"). Present only while an open Call Delivery carries the card's request Entry — read
|
|
590
|
+
* off the same open list the card came from, so it clears when the Call does. A card is the
|
|
591
|
+
* backup for a call not taken; while the call has it, the call is where it is answered. */
|
|
592
|
+
onCall: z4.literal(true).optional(),
|
|
588
593
|
/** THE RING, ON THE ITEM (walk/design.md §12 §17, #2251): the last ring on this card was
|
|
589
594
|
* declined, and what the ladder will do next — read off the cron's own row, never computed
|
|
590
595
|
* on the phone. Present only while a `declined` receipt stands on the card's last Call.
|
|
@@ -1149,7 +1154,8 @@ next check_replies. Start your own listener now, as a BACKGROUND task whose outp
|
|
|
1149
1154
|
npx -y -p @paigy/mcp@latest paigy-listen
|
|
1150
1155
|
|
|
1151
1156
|
Each line it prints is a Delivery waiting for you: read the Goal, and reply with contact when
|
|
1152
|
-
the answer needs a decision. Start it once; if it is already running, do nothing.
|
|
1157
|
+
the answer needs a decision. Start it once; if it is already running, do nothing. Then call
|
|
1158
|
+
claim_goal once: work handed to you before this session started waits there.
|
|
1153
1159
|
PAIGY
|
|
1154
1160
|
exit 0
|
|
1155
1161
|
`;
|
|
@@ -1536,7 +1536,7 @@ var StartContactSchema = z2.object({
|
|
|
1536
1536
|
}).strict();
|
|
1537
1537
|
var ContactSchema = z2.union([StartContactSchema, z2.object({ deliveryId: z2.string().uuid() }).strict()]);
|
|
1538
1538
|
var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
|
|
1539
|
-
var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. A bundled ask is taken apart into its own cards by Paigy's one intake read \u2014 parts of one piece of work under one Goal, separate things as separate Goals.";
|
|
1539
|
+
var CONTACT_DESCRIPTION = "Contact the user with one or more asks/questions. Pass an array of asks (each with 'ask', optional 'options', 'id', 'parentId', 'repo'), plus channel:'notification'|'call', and waiting:'none'|'hard'. An ask that names a Goal (parentId) goes onto that Goal; one that names none is placed in the person's tree by Paigy. Notification returns immediately; collect durable answers with check_replies. On stdio, a Call holds one cancellable ~45s window; continue with ONLY {deliveryId}. One question per 'ask'; several questions are several objects in 'asks'. A bundled ask is taken apart into its own cards by Paigy's one intake read \u2014 parts of one piece of work under one Goal, separate things as separate Goals. An ask with no options that is not blocking is a report: on a Goal whose report card is still open it is added to that card, with no new push; one that only says where your work stands while under way is recorded as the Goal's progress, not sent (the result says so).";
|
|
1540
1540
|
var CreateGoalSchema = z3.object({
|
|
1541
1541
|
outcome: z3.string().trim().min(1).max(1e4),
|
|
1542
1542
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -1583,9 +1583,9 @@ var UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({
|
|
|
1583
1583
|
var ClaimGoalSchema = z3.object({ goalId: z3.string().uuid().optional() }).strict();
|
|
1584
1584
|
var GetGoalSchema = z3.object({ goalId: z3.string().uuid() }).strict();
|
|
1585
1585
|
var GET_GOAL_DESCRIPTION = "Read one Goal without claiming it: its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), and `next`, the one step to take. Foreign or sibling-owned Goals are not disclosed.";
|
|
1586
|
-
var UPDATE_GOAL_DESCRIPTION =
|
|
1586
|
+
var UPDATE_GOAL_DESCRIPTION = `Update an owned Goal at an exact revision. State, ownership, dependencies, children, progress, title, and review acknowledgement are explicit; stale revisions are rejected. title is the work's name in one to five words, as a person would refer to it out loud (it is spoken on a call and heads every list); null clears it. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision. dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. state: done means the work was accomplished, and it can be reopened: when the person says it is not done ("it's not working"), set state: active on that same Goal instead of starting a new one; its parent reopens with it. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
|
|
1587
1587
|
var CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Another agent's Goal that has gone quiet for 3 days (check_replies lists them as stalledOthers) is taken over when you claim it, and becomes yours to finish or cancel. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
|
|
1588
|
-
var CHECK_REPLIES_DESCRIPTION = "What is waiting for you: your open Deliveries (a request the user started toward you, a handoff), one row each with its Goals, how many decisions are still open, and the newest words in brief; and `review`, your Goals with something new on them \u2014 the user's answers and notes are Entries on the Goal, not Deliveries. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread a Delivery in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true clears the Goal from `review` and closes the Deliveries addressed to you on it.";
|
|
1588
|
+
var CHECK_REPLIES_DESCRIPTION = "What is waiting for you: your open Deliveries (a request the user started toward you, a handoff), one row each with its Goals, how many decisions are still open, and the newest words in brief; `assigned`, your Goals nobody has started yet, however they became yours (claim_goal({goalId}) starts one); and `review`, your Goals with something new on them \u2014 the user's answers and notes are Entries on the Goal, not Deliveries. A pure read with no arguments: nothing is consumed, acknowledged or claimed by reading it, so call it on startup, after a long wait, or whenever you want to know what is outstanding. To act on one, claim its Goal (claim_goal) or reread a Delivery in full with contact({deliveryId}). Once you have acted on what arrived, update_goal with reviewed: true clears the Goal from `review` and closes the Deliveries addressed to you on it.";
|
|
1589
1589
|
var CheckRepliesSchema = z3.object({}).strict();
|
|
1590
1590
|
var AGENT_TOOLS = [
|
|
1591
1591
|
{ name: "contact", description: CONTACT_DESCRIPTION, inputSchema: CONTACT_SCHEMA },
|
|
@@ -1930,9 +1930,9 @@ var AgendaTurnSchema = z4.object({
|
|
|
1930
1930
|
/** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
|
|
1931
1931
|
* the planner declares duplicates instead of asking them twice. */
|
|
1932
1932
|
coveredIds: z4.array(z4.string()).optional(),
|
|
1933
|
-
/**
|
|
1934
|
-
*
|
|
1935
|
-
info: z4.array(z4.string().min(1)).
|
|
1933
|
+
/** The spoken sentences of the turn, in order. No count: how long a turn is is the brain's call
|
|
1934
|
+
* (owner, 2026-09-25), and a count here refused whole plans. */
|
|
1935
|
+
info: z4.array(z4.string().min(1)).default([]),
|
|
1936
1936
|
question: z4.string().min(1).nullable(),
|
|
1937
1937
|
/** True on the one turn carrying the agent's own declared question. */
|
|
1938
1938
|
asks: z4.boolean().optional(),
|
|
@@ -2043,6 +2043,11 @@ var InboxItemSchema = z4.object({
|
|
|
2043
2043
|
* and "dismiss the stalled ones" cancelled 37 pieces of live work). */
|
|
2044
2044
|
cold: z4.string().datetime().optional(),
|
|
2045
2045
|
clarifies: z4.string().optional(),
|
|
2046
|
+
/** THIS CARD'S QUESTION IS ON A LIVE CALL (owner, 2026-09-24: "Mark it while the call is
|
|
2047
|
+
* live"). Present only while an open Call Delivery carries the card's request Entry — read
|
|
2048
|
+
* off the same open list the card came from, so it clears when the Call does. A card is the
|
|
2049
|
+
* backup for a call not taken; while the call has it, the call is where it is answered. */
|
|
2050
|
+
onCall: z4.literal(true).optional(),
|
|
2046
2051
|
/** THE RING, ON THE ITEM (walk/design.md §12 §17, #2251): the last ring on this card was
|
|
2047
2052
|
* declined, and what the ladder will do next — read off the cron's own row, never computed
|
|
2048
2053
|
* on the phone. Present only while a `declined` receipt stands on the card's last Call.
|
|
@@ -2899,6 +2904,7 @@ function currentRepo(cwd = process.cwd()) {
|
|
|
2899
2904
|
}
|
|
2900
2905
|
return asked;
|
|
2901
2906
|
}
|
|
2907
|
+
var NOT_SENT = "Recorded as progress, not sent: nobody was notified. If the person should see it (a result, something to try or check, a finding), contact again and say so; report progress with update_goal.";
|
|
2902
2908
|
async function contact(input, opts = {}) {
|
|
2903
2909
|
const parsed = ContactSchema.parse(input);
|
|
2904
2910
|
opts.signal?.throwIfAborted();
|
|
@@ -2906,6 +2912,7 @@ async function contact(input, opts = {}) {
|
|
|
2906
2912
|
const headers = { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token) ?? ""}`, "x-paigy-model": "goal-entry-v1" };
|
|
2907
2913
|
let deliveryId;
|
|
2908
2914
|
let deliveries = [];
|
|
2915
|
+
let recorded = [];
|
|
2909
2916
|
if ("deliveryId" in parsed) {
|
|
2910
2917
|
deliveryId = parsed.deliveryId;
|
|
2911
2918
|
} else {
|
|
@@ -2934,19 +2941,24 @@ async function contact(input, opts = {}) {
|
|
|
2934
2941
|
if (!res.ok) await fail("batch contact", res);
|
|
2935
2942
|
const receipt = await res.json();
|
|
2936
2943
|
deliveries = receipt.deliveries ?? [];
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
}
|
|
2944
|
+
recorded = receipt.recorded ?? [];
|
|
2945
|
+
if (deliveries.length === 0 && recorded.length) return { sent: false, recorded, message: NOT_SENT };
|
|
2940
2946
|
if (deliveries.length === 0) throw new Error("Goal contact returned no Delivery identities");
|
|
2941
2947
|
deliveryId = deliveries[0]?.deliveryId ?? "";
|
|
2942
2948
|
if (!deliveryId) throw new Error("Goal contact returned no valid Delivery identity");
|
|
2943
2949
|
}
|
|
2944
|
-
const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId);
|
|
2950
|
+
const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId).map(({ id, deliveryId: deliveryId2, goalId }) => ({ ...id ? { id } : {}, deliveryId: deliveryId2, goalId }));
|
|
2951
|
+
const joinedCard = deliveries[0]?.joinedCard === true;
|
|
2945
2952
|
const read = async (signal2) => {
|
|
2946
2953
|
const res = ensureAuthed(await send2(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
|
|
2947
2954
|
if (!res.ok) await fail("read_delivery", res);
|
|
2948
2955
|
const one = await res.json();
|
|
2949
|
-
return
|
|
2956
|
+
return {
|
|
2957
|
+
...one,
|
|
2958
|
+
...all.length > 1 ? { deliveries: all } : {},
|
|
2959
|
+
...recorded.length ? { recorded, notSent: NOT_SENT } : {},
|
|
2960
|
+
...joinedCard ? { joinedCard: true } : {}
|
|
2961
|
+
};
|
|
2950
2962
|
};
|
|
2951
2963
|
const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
|
|
2952
2964
|
if (opts.waits === false) return read(opts.signal);
|
|
@@ -3044,6 +3056,7 @@ function goalView(g) {
|
|
|
3044
3056
|
});
|
|
3045
3057
|
}
|
|
3046
3058
|
function deliveryView(d) {
|
|
3059
|
+
const answers = "Answers land on the Goal: read them with claim_goal or get_goal. Do not poll this notification.";
|
|
3047
3060
|
return compact({
|
|
3048
3061
|
deliveryId: d.deliveryId,
|
|
3049
3062
|
kind: d.kind,
|
|
@@ -3052,11 +3065,16 @@ function deliveryView(d) {
|
|
|
3052
3065
|
conversation: conversation(d),
|
|
3053
3066
|
// Each ask's own Delivery when a contact carried several — the only handle on the others.
|
|
3054
3067
|
deliveries: d.deliveries,
|
|
3055
|
-
|
|
3068
|
+
// WHAT THIS CONTACT DID NOT SEND (#2450): asks the intake read kept as progress, by name.
|
|
3069
|
+
recorded: d.recorded,
|
|
3070
|
+
notSent: d.notSent,
|
|
3071
|
+
joinedCard: d.joinedCard,
|
|
3072
|
+
next: d.kind === "notification" ? d.joinedCard ? `Added to the open report card on its Goal; no new push was sent. ${answers}` : answers : d.message
|
|
3056
3073
|
});
|
|
3057
3074
|
}
|
|
3058
3075
|
function repliesView(r) {
|
|
3059
3076
|
const deliveries = r.deliveries ?? [];
|
|
3077
|
+
const assigned = r.assigned ?? [];
|
|
3060
3078
|
const review = r.review ?? [];
|
|
3061
3079
|
const stalled = r.stalled ?? [];
|
|
3062
3080
|
const others = r.stalledOthers ?? [];
|
|
@@ -3068,6 +3086,9 @@ function repliesView(r) {
|
|
|
3068
3086
|
openDecisions: d.decisionNeeds.filter((n) => n.state === "open").length || void 0,
|
|
3069
3087
|
latest: "about" in d ? d.about : void 0
|
|
3070
3088
|
})),
|
|
3089
|
+
// WORK ASSIGNED TO YOU (owner, 2026-09-24): your Goals nobody has started, however they
|
|
3090
|
+
// became yours — a Goal handed to you is not a Delivery, and must not wait for a claim to be seen.
|
|
3091
|
+
...assigned.length ? { assigned } : {},
|
|
3071
3092
|
// NEW ON YOUR GOALS (owner, 2026-09-23): a reply is an Entry on the Goal, not a Delivery, so the
|
|
3072
3093
|
// Goals flagged for your review are listed here until you acknowledge them.
|
|
3073
3094
|
...review.length ? { review } : {},
|
|
@@ -3077,7 +3098,8 @@ function repliesView(r) {
|
|
|
3077
3098
|
// OTHER AGENTS' STALLED WORK (owner, 2026-09-23): any agent may take it over and close it.
|
|
3078
3099
|
...others.length ? { stalledOthers: others } : {},
|
|
3079
3100
|
next: [
|
|
3080
|
-
deliveries.length || review.length ? "Act on a Goal with claim_goal; reread one Delivery in full with contact({deliveryId})." : "Nothing is waiting.",
|
|
3101
|
+
deliveries.length || review.length || assigned.length ? "Act on a Goal with claim_goal; reread one Delivery in full with contact({deliveryId})." : "Nothing is waiting.",
|
|
3102
|
+
...assigned.length ? [`${assigned.length} of your Goals are assigned to you and not started (assigned): claim_goal({goalId}) to start one.`] : [],
|
|
3081
3103
|
...review.length ? [`${review.length} of your Goals have something new (review): read each with claim_goal or get_goal, then update_goal with reviewed: true.`] : [],
|
|
3082
3104
|
...stalled.length ? [`${stalled.length} of your Goals have had no progress for 3 days: update_goal each with progress, finish it, or cancel it.`] : [],
|
|
3083
3105
|
...others.length ? [`${others.length} of other agents' Goals have gone quiet for 3 days: claim_goal({goalId}) takes one over, then finish or cancel it with update_goal.`] : []
|
|
@@ -3090,7 +3112,7 @@ async function runTool(name, args, opts) {
|
|
|
3090
3112
|
switch (name) {
|
|
3091
3113
|
case "contact": {
|
|
3092
3114
|
const sent = await contact(input, { ...client, signal, waits });
|
|
3093
|
-
return "
|
|
3115
|
+
return "sent" in sent ? sent : deliveryView(sent);
|
|
3094
3116
|
}
|
|
3095
3117
|
case "check_replies":
|
|
3096
3118
|
CheckRepliesSchema.parse(input);
|
|
@@ -3118,47 +3140,108 @@ async function runTool(name, args, opts) {
|
|
|
3118
3140
|
var HEARTBEAT_MS = 25e3;
|
|
3119
3141
|
var RETRY_MS = [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
3120
3142
|
var JOIN = { config: { broadcast: { self: false } } };
|
|
3143
|
+
var JOIN_TIMEOUT_MS = 1e4;
|
|
3121
3144
|
var lines = /* @__PURE__ */ new Map();
|
|
3122
3145
|
function frame(line, topic, event, payload) {
|
|
3146
|
+
const ref = String(++line.ref);
|
|
3123
3147
|
try {
|
|
3124
|
-
line.ws?.send(JSON.stringify({ topic, event, payload, ref
|
|
3148
|
+
line.ws?.send(JSON.stringify({ topic, event, payload, ref }));
|
|
3125
3149
|
} catch {
|
|
3126
3150
|
}
|
|
3151
|
+
return ref;
|
|
3127
3152
|
}
|
|
3128
3153
|
function stop(line) {
|
|
3129
3154
|
line.open = false;
|
|
3155
|
+
line.beat = void 0;
|
|
3130
3156
|
clearInterval(line.heart);
|
|
3131
3157
|
clearTimeout(line.retry);
|
|
3158
|
+
for (const t of line.topics.values()) {
|
|
3159
|
+
clearTimeout(t.timer);
|
|
3160
|
+
t.joined = false;
|
|
3161
|
+
t.ref = void 0;
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
function join3(line, name, t) {
|
|
3165
|
+
clearTimeout(t.timer);
|
|
3166
|
+
t.joined = false;
|
|
3167
|
+
t.ref = frame(line, `realtime:${name}`, "phx_join", JOIN);
|
|
3168
|
+
t.timer = setTimeout(() => rejoin(line, name, t), JOIN_TIMEOUT_MS);
|
|
3169
|
+
}
|
|
3170
|
+
function rejoin(line, name, t) {
|
|
3171
|
+
clearTimeout(t.timer);
|
|
3172
|
+
t.joined = false;
|
|
3173
|
+
t.ref = void 0;
|
|
3174
|
+
if (!line.open || line.topics.get(name) !== t) return;
|
|
3175
|
+
t.timer = setTimeout(() => {
|
|
3176
|
+
if (line.open && line.topics.get(name) === t) join3(line, name, t);
|
|
3177
|
+
}, RETRY_MS[Math.min(t.attempt++, RETRY_MS.length - 1)]);
|
|
3178
|
+
}
|
|
3179
|
+
function joined(t) {
|
|
3180
|
+
clearTimeout(t.timer);
|
|
3181
|
+
t.joined = true;
|
|
3182
|
+
t.attempt = 0;
|
|
3183
|
+
for (const l of t.listeners) l.onJoined?.(++l.joins);
|
|
3132
3184
|
}
|
|
3133
3185
|
function connect(line) {
|
|
3134
3186
|
const base = line.url.replace(/^http/, "ws").replace(/\/+$/, "");
|
|
3135
3187
|
const ws = line.ws = new WebSocket(`${base}/realtime/v1/websocket?apikey=${encodeURIComponent(line.anonKey)}&vsn=1.0.0`);
|
|
3188
|
+
let gone = false;
|
|
3189
|
+
const again = () => {
|
|
3190
|
+
if (gone) return;
|
|
3191
|
+
gone = true;
|
|
3192
|
+
stop(line);
|
|
3193
|
+
if (!line.topics.size) return;
|
|
3194
|
+
line.retry = setTimeout(() => connect(line), RETRY_MS[Math.min(line.attempt++, RETRY_MS.length - 1)]);
|
|
3195
|
+
};
|
|
3136
3196
|
ws.addEventListener("open", () => {
|
|
3197
|
+
if (gone) return;
|
|
3137
3198
|
line.open = true;
|
|
3138
3199
|
line.attempt = 0;
|
|
3139
|
-
for (const
|
|
3140
|
-
line.heart = setInterval(() =>
|
|
3200
|
+
for (const [name, t] of line.topics) join3(line, name, t);
|
|
3201
|
+
line.heart = setInterval(() => {
|
|
3202
|
+
if (line.beat) {
|
|
3203
|
+
try {
|
|
3204
|
+
ws.close();
|
|
3205
|
+
} catch {
|
|
3206
|
+
}
|
|
3207
|
+
again();
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
line.beat = frame(line, "phoenix", "heartbeat", {});
|
|
3211
|
+
}, HEARTBEAT_MS);
|
|
3141
3212
|
});
|
|
3142
3213
|
ws.addEventListener("message", (e) => {
|
|
3214
|
+
if (gone) return;
|
|
3143
3215
|
let m;
|
|
3144
3216
|
try {
|
|
3145
3217
|
m = JSON.parse(String(e.data));
|
|
3146
3218
|
} catch {
|
|
3147
3219
|
return;
|
|
3148
3220
|
}
|
|
3149
|
-
if (m.
|
|
3150
|
-
|
|
3221
|
+
if (m.topic === "phoenix") {
|
|
3222
|
+
if (m.event === "phx_reply" && m.ref === line.beat) line.beat = void 0;
|
|
3223
|
+
return;
|
|
3224
|
+
}
|
|
3225
|
+
if (!m.topic?.startsWith("realtime:")) return;
|
|
3226
|
+
const name = m.topic.slice("realtime:".length);
|
|
3227
|
+
const t = line.topics.get(name);
|
|
3228
|
+
if (!t) return;
|
|
3229
|
+
if (m.event === "phx_reply") {
|
|
3230
|
+
if (!t.ref || m.ref !== t.ref) return;
|
|
3231
|
+
if (m.payload?.status === "ok") joined(t);
|
|
3232
|
+
else rejoin(line, name, t);
|
|
3233
|
+
return;
|
|
3234
|
+
}
|
|
3235
|
+
if (m.event === "phx_error" || m.event === "phx_close") {
|
|
3236
|
+
if (m.ref && m.ref !== t.ref) return;
|
|
3237
|
+
rejoin(line, name, t);
|
|
3238
|
+
return;
|
|
3239
|
+
}
|
|
3240
|
+
if (m.event !== "broadcast") return;
|
|
3241
|
+
for (const l of t.listeners) {
|
|
3151
3242
|
if (m.payload?.event === l.event) l.onMessage(m.payload.payload);
|
|
3152
3243
|
}
|
|
3153
3244
|
});
|
|
3154
|
-
let gone = false;
|
|
3155
|
-
const again = () => {
|
|
3156
|
-
if (gone) return;
|
|
3157
|
-
gone = true;
|
|
3158
|
-
stop(line);
|
|
3159
|
-
if (!line.topics.size) return;
|
|
3160
|
-
line.retry = setTimeout(() => connect(line), RETRY_MS[Math.min(line.attempt++, RETRY_MS.length - 1)]);
|
|
3161
|
-
};
|
|
3162
3245
|
ws.addEventListener("close", again);
|
|
3163
3246
|
ws.addEventListener("error", again);
|
|
3164
3247
|
}
|
|
@@ -3166,17 +3249,22 @@ function subscribeRealtime(args) {
|
|
|
3166
3249
|
const key = `${args.url} ${args.anonKey}`;
|
|
3167
3250
|
let line = lines.get(key);
|
|
3168
3251
|
if (!line) lines.set(key, line = { url: args.url, anonKey: args.anonKey, open: false, attempt: 0, ref: 0, topics: /* @__PURE__ */ new Map() });
|
|
3169
|
-
let
|
|
3170
|
-
if (!
|
|
3171
|
-
line.topics.set(args.topic,
|
|
3172
|
-
if (line.open)
|
|
3173
|
-
}
|
|
3174
|
-
const
|
|
3175
|
-
|
|
3252
|
+
let t = line.topics.get(args.topic);
|
|
3253
|
+
if (!t) {
|
|
3254
|
+
line.topics.set(args.topic, t = { listeners: /* @__PURE__ */ new Set(), joined: false, attempt: 0 });
|
|
3255
|
+
if (line.open) join3(line, args.topic, t);
|
|
3256
|
+
}
|
|
3257
|
+
const topic = t;
|
|
3258
|
+
const me = { event: args.event, onMessage: args.onMessage, onJoined: args.onJoined, joins: 0 };
|
|
3259
|
+
topic.listeners.add(me);
|
|
3260
|
+
if (topic.joined && me.onJoined) queueMicrotask(() => {
|
|
3261
|
+
if (topic.joined && topic.listeners.has(me)) me.onJoined?.(++me.joins);
|
|
3262
|
+
});
|
|
3176
3263
|
if (!line.ws) connect(line);
|
|
3177
3264
|
return {
|
|
3178
3265
|
close: () => {
|
|
3179
|
-
if (!
|
|
3266
|
+
if (!topic.listeners.delete(me) || topic.listeners.size) return;
|
|
3267
|
+
clearTimeout(topic.timer);
|
|
3180
3268
|
line.topics.delete(args.topic);
|
|
3181
3269
|
if (line.topics.size) {
|
|
3182
3270
|
if (line.open) frame(line, `realtime:${args.topic}`, "phx_leave", {});
|
|
@@ -3192,7 +3280,7 @@ function subscribeRealtime(args) {
|
|
|
3192
3280
|
};
|
|
3193
3281
|
}
|
|
3194
3282
|
async function subscribeWake(onNudge, opts = {}) {
|
|
3195
|
-
const { release = true, ...client } = opts;
|
|
3283
|
+
const { release = true, onJoined, ...client } = opts;
|
|
3196
3284
|
const cfg = await registerDelivery("self_hosted", client);
|
|
3197
3285
|
const { url, anonKey } = cfg.realtime;
|
|
3198
3286
|
const channel = wakeChannel(cfg.tokenId);
|
|
@@ -3201,7 +3289,8 @@ async function subscribeWake(onNudge, opts = {}) {
|
|
|
3201
3289
|
anonKey,
|
|
3202
3290
|
topic: channel,
|
|
3203
3291
|
event: WAKE_EVENT,
|
|
3204
|
-
onMessage: (payload) => onNudge(payload ?? {})
|
|
3292
|
+
onMessage: (payload) => onNudge(payload ?? {}),
|
|
3293
|
+
onJoined
|
|
3205
3294
|
});
|
|
3206
3295
|
return {
|
|
3207
3296
|
tokenId: cfg.tokenId,
|
package/dist/enable.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
PAIGY_TOOL_IDS,
|
|
4
4
|
enablePaigyTools,
|
|
5
5
|
installSessionListening
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-7VLZIOLF.js";
|
|
7
|
+
import "./chunk-MWHYYLCE.js";
|
|
8
|
+
import "./chunk-V5AQASR7.js";
|
|
9
9
|
|
|
10
10
|
// src/enable.ts
|
|
11
11
|
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-UIZLBPRG.js";
|
|
9
9
|
import {
|
|
10
10
|
ENABLE_COMMAND,
|
|
11
11
|
OnboardSchema,
|
|
@@ -14,11 +14,11 @@ import {
|
|
|
14
14
|
SERVER_INSTRUCTIONS,
|
|
15
15
|
TOOLS,
|
|
16
16
|
paigyToolsAllowlisted
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-7VLZIOLF.js";
|
|
18
18
|
import {
|
|
19
19
|
decideListen,
|
|
20
20
|
listenerAlive
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-MWHYYLCE.js";
|
|
22
22
|
import {
|
|
23
23
|
clearSurface,
|
|
24
24
|
writeSurface
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
setIdentity,
|
|
40
40
|
slotName,
|
|
41
41
|
whoAmI
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-V5AQASR7.js";
|
|
43
43
|
|
|
44
44
|
// src/index.ts
|
|
45
45
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
package/dist/listen.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
entryWords,
|
|
4
4
|
removeListenMark,
|
|
5
5
|
writeListenMark
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MWHYYLCE.js";
|
|
7
7
|
import {
|
|
8
8
|
agentName,
|
|
9
9
|
checkReplies,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
getGoal,
|
|
12
12
|
slotName,
|
|
13
13
|
subscribeWake
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-V5AQASR7.js";
|
|
15
15
|
|
|
16
16
|
// src/listen.ts
|
|
17
17
|
import { spawn } from "child_process";
|
|
@@ -136,11 +136,13 @@ function brief(waiting, reason, as, goalId) {
|
|
|
136
136
|
const review = waiting.review ?? [];
|
|
137
137
|
const named = (goalId ? review.find((g) => g.goalId === goalId) : void 0) ?? review[0];
|
|
138
138
|
const fresh = review.length ? ` \xB7 new on ${review.length === 1 ? "your Goal" : `${review.length} of your Goals`}, first ${named.goalId} ("${named.title}"): claim_goal to read it` : "";
|
|
139
|
-
|
|
139
|
+
const assigned = waiting.assigned ?? [];
|
|
140
|
+
const mine = assigned.length ? ` \xB7 assigned to you: ${assigned.length === 1 ? "1 Goal" : `${assigned.length} Goals`}, first ${assigned[0].goalId} ("${assigned[0].title}"): claim_goal({goalId}) to start it` : "";
|
|
141
|
+
if (!lead) return `Paigy \xB7 ${reason === "boot" ? `listening as ${as}` : reason}${mine}${fresh || (mine ? "" : " \xB7 nothing waiting")}${stalled}`;
|
|
140
142
|
const said = saidIn(lead) || ("about" in lead ? lead.about : "");
|
|
141
143
|
const shown = said.length > 160 ? `${said.slice(0, 160)}\u2026` : said;
|
|
142
144
|
const goal = waiting.goal?.goalId ?? lead.goalIds[0];
|
|
143
|
-
return `Paigy \xB7 ${reason} \xB7 ${waiting.deliveries.length} waiting \xB7 lead ${lead.deliveryId} on Goal ${goal}: "${shown}"${fresh}${stalled}`;
|
|
145
|
+
return `Paigy \xB7 ${reason} \xB7 ${waiting.deliveries.length} waiting \xB7 lead ${lead.deliveryId} on Goal ${goal}: "${shown}"${mine}${fresh}${stalled}`;
|
|
144
146
|
}
|
|
145
147
|
function launchEnv(waiting, reason, goalId) {
|
|
146
148
|
const env = { PAIGY_WORK: JSON.stringify(waiting), PAIGY_EVENT: reason };
|
|
@@ -157,16 +159,16 @@ function launchEnv(waiting, reason, goalId) {
|
|
|
157
159
|
async function sweep(reason, goalId) {
|
|
158
160
|
try {
|
|
159
161
|
const cmd = process.env.PAIGY_ON_WAKE;
|
|
160
|
-
const { deliveries: rows, stalled, review } = await checkReplies();
|
|
162
|
+
const { deliveries: rows, stalled, review, assigned } = await checkReplies();
|
|
161
163
|
const lead = leadOf(rows, goalId);
|
|
162
|
-
const full = lead ? await contact({ deliveryId: lead.deliveryId }, { waits: false }).then((d) => "
|
|
164
|
+
const full = lead ? await contact({ deliveryId: lead.deliveryId }, { waits: false }).then((d) => "sent" in d ? null : d).catch(() => null) : null;
|
|
163
165
|
const deliveries = rows.map((d) => d === lead && full ? full : d);
|
|
164
166
|
const leadGoalId = lead?.goalIds[0];
|
|
165
167
|
const goal = leadGoalId ? await getGoal(leadGoalId).then((g) => ({ goalId: leadGoalId, progress: g.progress })).catch(() => null) : null;
|
|
166
|
-
const waiting = { deliveries, goal, ...stalled?.length ? { stalled } : {}, ...review?.length ? { review } : {} };
|
|
168
|
+
const waiting = { deliveries, goal, ...stalled?.length ? { stalled } : {}, ...review?.length ? { review } : {}, ...assigned?.length ? { assigned } : {} };
|
|
167
169
|
if (BRIEF) line(brief(waiting, reason, listeningAs(), goalId));
|
|
168
170
|
else emit({ type: "sweep", reason, ...goalId ? { about: goalId } : {}, ...waiting });
|
|
169
|
-
if (cmd && (deliveries.length || waiting.review?.length || reason === "boot" && waiting.stalled?.length)) {
|
|
171
|
+
if (cmd && (deliveries.length || waiting.review?.length || waiting.assigned?.length || reason === "boot" && waiting.stalled?.length)) {
|
|
170
172
|
spawn(cmd, { shell: true, stdio: "inherit", env: { ...process.env, ...launchEnv(waiting, reason, goalId) } });
|
|
171
173
|
}
|
|
172
174
|
} catch (e) {
|
|
@@ -174,8 +176,17 @@ async function sweep(reason, goalId) {
|
|
|
174
176
|
else emit({ type: "error", reason, message: e.message });
|
|
175
177
|
}
|
|
176
178
|
}
|
|
179
|
+
function catchUpReason(joins) {
|
|
180
|
+
return joins > 1 ? "resubscribed" : null;
|
|
181
|
+
}
|
|
177
182
|
async function main() {
|
|
178
|
-
const sub = await subscribeWake((nudge) => void sweep(`wake:${nudge.kind ?? "?"}`, nudge.parentId), {
|
|
183
|
+
const sub = await subscribeWake((nudge) => void sweep(`wake:${nudge.kind ?? "?"}`, nudge.parentId), {
|
|
184
|
+
release: RELEASE,
|
|
185
|
+
onJoined: (joins) => {
|
|
186
|
+
const reason = catchUpReason(joins);
|
|
187
|
+
if (reason) void sweep(reason);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
179
190
|
const slot = agentName();
|
|
180
191
|
writeListenMark(slot, process.pid);
|
|
181
192
|
process.on("exit", () => removeListenMark(slot));
|
|
@@ -214,6 +225,7 @@ if (isEntry()) {
|
|
|
214
225
|
}
|
|
215
226
|
export {
|
|
216
227
|
brief,
|
|
228
|
+
catchUpReason,
|
|
217
229
|
launchEnv,
|
|
218
230
|
leadOf,
|
|
219
231
|
listenOptions,
|
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-UIZLBPRG.js";
|
|
7
7
|
import {
|
|
8
8
|
autoConfigureClients,
|
|
9
9
|
claudeInstallHint,
|
|
10
10
|
openBrowser
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-7VLZIOLF.js";
|
|
12
|
+
import "./chunk-MWHYYLCE.js";
|
|
13
13
|
import {
|
|
14
14
|
TOKEN_PATH,
|
|
15
15
|
agentName,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
saveToken,
|
|
21
21
|
setIdentity,
|
|
22
22
|
whoAmI
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-V5AQASR7.js";
|
|
24
24
|
|
|
25
25
|
// src/onboard.ts
|
|
26
26
|
function reportRegistered() {
|
package/dist/slot.js
CHANGED
package/dist/stalled.js
CHANGED
|
@@ -22,7 +22,7 @@ async function main() {
|
|
|
22
22
|
const dir = join(homedir(), ".paigy", "stalled-reminded");
|
|
23
23
|
const mark = join(dir, `${session || "session"}-${day}`);
|
|
24
24
|
if (existsSync(mark)) return;
|
|
25
|
-
const { checkReplies } = await import("./dist-
|
|
25
|
+
const { checkReplies } = await import("./dist-PJLF3B27.js");
|
|
26
26
|
const reply = stopReply((await checkReplies()).stalled ?? []);
|
|
27
27
|
if (!reply) return;
|
|
28
28
|
mkdirSync(dir, { recursive: true });
|
package/dist/statusline.js
CHANGED
package/package.json
CHANGED