@paigy/mcp 0.40.17 → 0.40.20
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 +32 -3
- package/dist/{chunk-V5AQASR7.js → chunk-6C6CO7H3.js} +297 -60
- package/dist/{chunk-7VLZIOLF.js → chunk-6MEUSSRH.js} +71 -2
- package/dist/{chunk-MWHYYLCE.js → chunk-RLN2B5IZ.js} +181 -22
- package/dist/{chunk-UIZLBPRG.js → chunk-YNKTKLQD.js} +19 -1
- package/dist/{dist-PJLF3B27.js → dist-33DA5X2G.js} +7 -1
- package/dist/enable.js +3 -3
- package/dist/index.js +12 -20
- package/dist/listen.js +7 -2
- package/dist/onboard.js +4 -4
- package/dist/slot.js +1 -1
- package/dist/stalled.js +15 -7
- package/dist/statusline.js +1 -1
- package/package.json +1 -1
|
@@ -1525,18 +1525,18 @@ var AskInputSchema = z2.object({
|
|
|
1525
1525
|
parentId: z2.string().uuid().optional().describe("The Goal this question is about \u2014 usually the one you are working on. The question goes onto that Goal and its answer comes back there. Omit it and Paigy places the question in the tree itself."),
|
|
1526
1526
|
repo: z2.string().optional().describe("Optional repository context."),
|
|
1527
1527
|
ask: z2.string().trim().min(1).max(1e4).describe(
|
|
1528
|
-
"ONE question, and only what is needed to answer it. Several questions are several asks in the array, one each: an answer settles the one ask it was given in the shape that ask declares, so a person who answers the part of a bundled ask that interested them settles nothing and is asked again. Paigy reads every ask once and takes a bundled one apart into its own cards anyway \u2014
|
|
1528
|
+
"ONE question, and only what is needed to answer it. Several questions are several asks in the array, one each: an answer settles the one ask it was given in the shape that ask declares, so a person who answers the part of a bundled ask that interested them settles nothing and is asked again. Paigy reads every ask once and takes a bundled one apart into its own cards anyway \u2014 each question a Question on the one Goal the ask lands on, never a Goal of its own \u2014 but the words it splits are its reading, not yours. News, progress and findings are their own contact; ANY contact for a person already on a call JOINS that call, whatever channel you asked for, so several arrive as one call."
|
|
1529
1529
|
),
|
|
1530
|
-
options: z2.array(OptionInputSchema).min(
|
|
1530
|
+
options: z2.array(OptionInputSchema).min(1).max(6).optional()
|
|
1531
1531
|
}).strict();
|
|
1532
1532
|
var StartContactSchema = z2.object({
|
|
1533
1533
|
asks: z2.array(AskInputSchema).min(1).describe("The questions to pose, one per object. An ask with a parentId is filed on that Goal as it is; only an ask naming no Goal is placed in the tree by Paigy."),
|
|
1534
|
-
waiting: z2.enum(["none", "hard"]).default("none"),
|
|
1534
|
+
waiting: z2.enum(["none", "hard"]).default("none").describe("hard requests a call even when channel is notification; user permissions and ring cooldowns still apply."),
|
|
1535
1535
|
channel: z2.enum(["notification", "call"]).default("notification")
|
|
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
|
|
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, every one on the Goal the ask lands on; separate pieces of work are separate asks. An ask with no options that is not blocking is a report, and a report is an UPDATE, never a claim on them: it reaches them and is listed apart from what waits on them, so no answer is owed and none should be awaited. 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). Send options (or waiting:'hard') when you actually need an answer. If the person is already on a call, your contact joins that call automatically \u2014 whatever channel you asked for, with no ring \u2014 and the Delivery it returns IS that call: reread it with contact({deliveryId}) to see everything answered on it so far, and contact again while it is live to add information or a further question to the same call.";
|
|
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
|
|
@@ -1574,16 +1574,29 @@ var UpdateGoalSchema = z3.object({
|
|
|
1574
1574
|
state: z3.enum(["active", "done", "cancelled"]).optional(),
|
|
1575
1575
|
progress: z3.string().trim().min(1).max(1e4).optional(),
|
|
1576
1576
|
reviewed: z3.literal(true).optional(),
|
|
1577
|
-
dueAt: z3.string().datetime({ offset: true }).nullable().optional()
|
|
1577
|
+
dueAt: z3.string().datetime({ offset: true }).nullable().optional(),
|
|
1578
|
+
/** WITHDRAW YOUR OWN QUESTION (#2777, owner 2026-09-30). The question's id as every read shows it
|
|
1579
|
+
* (the conversation's `id`, eight characters, or the whole request Entry id), on THIS Goal, asked
|
|
1580
|
+
* by you and still open. It is cancelled, not answered: its cards close and nothing rings for it. */
|
|
1581
|
+
withdraw: z3.array(z3.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/)).min(1).max(10).optional()
|
|
1578
1582
|
}).strict().refine((v) => Object.keys(v).length > 0),
|
|
1579
1583
|
reason: z3.string().trim().min(1).max(2e3),
|
|
1580
1584
|
operationId: z3.string().uuid().optional()
|
|
1581
1585
|
}).strict();
|
|
1582
1586
|
var UpdateGoalToolSchema = UpdateGoalSchema.omit({ operationId: true }).extend({ goalId: z3.string().uuid() }).strict();
|
|
1583
1587
|
var ClaimGoalSchema = z3.object({ goalId: z3.string().uuid().optional() }).strict();
|
|
1584
|
-
var GetGoalSchema = z3.object({
|
|
1585
|
-
|
|
1586
|
-
|
|
1588
|
+
var GetGoalSchema = z3.object({
|
|
1589
|
+
goalId: z3.string().uuid(),
|
|
1590
|
+
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
1591
|
+
* newest entry, and counts what it left out (`apps/api/src/goal/collapse.ts`). */
|
|
1592
|
+
history: z3.boolean().optional(),
|
|
1593
|
+
/** WHEN A READ HAS CONFUSED YOU. Adds `diagnosis`: every reader that already answers a question
|
|
1594
|
+
* about this work, each answer attributed to the reader that gave it, and every disagreement
|
|
1595
|
+
* between two of them named. Off by default; `apps/api/src/goal/diagnose-design.md`. */
|
|
1596
|
+
diagnose: z3.boolean().optional()
|
|
1597
|
+
}).strict();
|
|
1598
|
+
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), `next`, the one step to take, and `more`, where to read further. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full. Another account's Goals, and another agent's Goal that is not below one of yours, are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
|
|
1599
|
+
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. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. 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. state: done while children are still open records your part as done: the Goal waits and closes by itself when its last open child closes. cancelled is final. Returns the Goal as get_goal reads it, at its new revision.`;
|
|
1587
1600
|
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
1601
|
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
1602
|
var CheckRepliesSchema = z3.object({}).strict();
|
|
@@ -2016,7 +2029,13 @@ var InboxItemSchema = z4.object({
|
|
|
2016
2029
|
* request Entry's author, as the same three facts the item's own `tokenId`/`name`/`voice`
|
|
2017
2030
|
* carry for the call's one agent, so the phone draws it with the same seed. Absent when the
|
|
2018
2031
|
* author is not an agent this account holds (unpaired since, or a person). */
|
|
2019
|
-
agent: z4.object({ tokenId: z4.string(), name: z4.string(), voice: VoiceKeySchema.optional() }).optional()
|
|
2032
|
+
agent: z4.object({ tokenId: z4.string(), name: z4.string(), voice: VoiceKeySchema.optional() }).optional(),
|
|
2033
|
+
/** ITS OPTIONS, WHEN THERE IS SOMETHING TO SEE (owner, 2026-09-25: "Yes, add it"): the options
|
|
2034
|
+
* its need offers, exactly as its own card carries them, present only when one of them has a
|
|
2035
|
+
* preview (`html` or `image`). The call screen opens them from the agenda row, so a preview is
|
|
2036
|
+
* never re-sent as a second card to be seen mid-call. Words-only options are absent — the bot
|
|
2037
|
+
* says those, and the list stays small (an `html` is up to 16 KB). */
|
|
2038
|
+
options: z4.array(OptionSchema).optional()
|
|
2020
2039
|
})).optional(),
|
|
2021
2040
|
visuals: z4.array(VisualSchema).optional(),
|
|
2022
2041
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
@@ -2196,18 +2215,20 @@ var UserSettingsSchema = z4.object({
|
|
|
2196
2215
|
* clobber guard as voiceMode: absent = leave unchanged on write. */
|
|
2197
2216
|
broker: BrokerTuningSchema.optional()
|
|
2198
2217
|
});
|
|
2199
|
-
var
|
|
2218
|
+
var HistoryWorkSchema = z4.object({
|
|
2200
2219
|
id: z4.string(),
|
|
2201
|
-
/** 'user' = a request you sent; 'agent' = a notification an agent sent you. */
|
|
2202
|
-
initiator: z4.enum(["user", "agent"]),
|
|
2203
2220
|
title: z4.string(),
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2221
|
+
state: z4.enum(["done", "cancelled"]),
|
|
2222
|
+
/** Who held it (`agent:<tokenId>` or `human:<userId>`). */
|
|
2223
|
+
assignee: z4.string()
|
|
2224
|
+
});
|
|
2225
|
+
var HistoryEntrySchema = z4.union([
|
|
2226
|
+
z4.object({ at: z4.string(), card: InboxItemSchema }),
|
|
2227
|
+
z4.object({ at: z4.string(), work: HistoryWorkSchema })
|
|
2228
|
+
]);
|
|
2229
|
+
var HistoryPageSchema = z4.object({
|
|
2230
|
+
entries: z4.array(HistoryEntrySchema),
|
|
2231
|
+
next: z4.string().nullable()
|
|
2211
2232
|
});
|
|
2212
2233
|
var ACTIVITY_LINES = 2;
|
|
2213
2234
|
var ACTIVITY_LINE_MAX = 80;
|
|
@@ -2391,6 +2412,9 @@ var QueueItemSchema = z4.object({
|
|
|
2391
2412
|
progressLine: z4.string().nullable().optional(),
|
|
2392
2413
|
reviewPending: z4.boolean().default(false),
|
|
2393
2414
|
dueAt: z4.string().nullable().default(null),
|
|
2415
|
+
/** WHEN ITS OWNER SAID DONE WHILE CHILDREN WERE OPEN (#2704): its own work is finished and it closes
|
|
2416
|
+
* with its last open child. Null otherwise; optional, so hand-built queues need not spell it. */
|
|
2417
|
+
finishedAt: z4.string().nullable().optional(),
|
|
2394
2418
|
/** The Goal this one was opened under; null at the root. */
|
|
2395
2419
|
parentGoalId: z4.string().nullable().default(null),
|
|
2396
2420
|
/** Goals opened under this one — only those the same list holds. */
|
|
@@ -2399,8 +2423,17 @@ var QueueItemSchema = z4.object({
|
|
|
2399
2423
|
dependencyGoalIds: z4.array(z4.string()).default([]),
|
|
2400
2424
|
/** True while any gate is on a Goal that is not done — the walk draws it dashed. */
|
|
2401
2425
|
blocked: z4.boolean().default(false),
|
|
2402
|
-
/**
|
|
2426
|
+
/** Its questions: every OPEN one, and at most ten settled, newest settled first
|
|
2427
|
+
* (20260929133308) — the page decides which of them to show. NOT the whole set: `asked` and
|
|
2428
|
+
* `answered` are, and a settled one's words are a line (280 characters), its body read when the
|
|
2429
|
+
* question is opened. */
|
|
2403
2430
|
questions: z4.array(QueueQuestionSchema).default([]),
|
|
2431
|
+
/** HOW MANY QUESTIONS THIS WORK HAS ASKED, and how many are answered — the Goal's own totals,
|
|
2432
|
+
* bounded at 100 server-side. A tally counted off `questions` is a wrong number that looks
|
|
2433
|
+
* right once the cap bites (`walk/trees.ts` `tallyOf`). Optional, and defaulted from the array
|
|
2434
|
+
* by the projection, so hand-built queues (fixtures, the demo) need not spell them. */
|
|
2435
|
+
asked: z4.number().optional(),
|
|
2436
|
+
answered: z4.number().optional(),
|
|
2404
2437
|
/** Every note on it the person replied to (`QueueReplySchema`) — the page decides which to show.
|
|
2405
2438
|
* Optional, not defaulted: absent is none, and every hand-built queue (fixtures, the demo) need
|
|
2406
2439
|
* not spell an empty list. */
|
|
@@ -2412,7 +2445,19 @@ var QueueItemSchema = z4.object({
|
|
|
2412
2445
|
/** When its owner last SAID something about it (`goals.last_progress_at`, written by every
|
|
2413
2446
|
* `update_goal` that changes `progress`). `updatedAt` moves for reasons nobody chose — a
|
|
2414
2447
|
* state recomputed, a review flag — so it cannot tell work in hand from work gone quiet. */
|
|
2415
|
-
lastProgressAt: z4.string().nullable().optional()
|
|
2448
|
+
lastProgressAt: z4.string().nullable().optional(),
|
|
2449
|
+
/** THE GOAL'S NEWEST WORD, FROM EITHER SIDE (owner, 2026-09-27): the newest Entry on it, of any
|
|
2450
|
+
* kind — what the person added ("Add to this"), their reply, the agent's ask or its progress
|
|
2451
|
+
* note. A progress note is an Entry, so this is already the newer of the two: the person's note
|
|
2452
|
+
* shows the moment it is written, and the agent's reply or next note replaces it by being newer.
|
|
2453
|
+
* `said` is bounded to 280 characters server-side (a line, not the conversation). Null when the
|
|
2454
|
+
* Goal carries no readable Entry; optional, so hand-built queues need not spell it. */
|
|
2455
|
+
latest: z4.object({
|
|
2456
|
+
from: z4.enum(["person", "agent"]),
|
|
2457
|
+
said: z4.string(),
|
|
2458
|
+
at: z4.string(),
|
|
2459
|
+
entryId: z4.string()
|
|
2460
|
+
}).nullable().optional()
|
|
2416
2461
|
});
|
|
2417
2462
|
var COLD_AFTER_MS = 3 * 24 * 60 * 60 * 1e3;
|
|
2418
2463
|
var NoteSourceSchema = z4.enum(["app", "call"]);
|
|
@@ -2525,6 +2570,19 @@ var DeliveryConfigSchema = z4.object({
|
|
|
2525
2570
|
* carries credentials; only a `poll` registration can come back with null here. */
|
|
2526
2571
|
realtime: z4.object({ url: z4.string(), anonKey: z4.string() }).nullable()
|
|
2527
2572
|
});
|
|
2573
|
+
var HostDecisionSchema = z4.object({
|
|
2574
|
+
/** The agent's token id: the row's `recipient`. */
|
|
2575
|
+
agent: z4.string().uuid(),
|
|
2576
|
+
decision: z4.enum(["stood_back", "took_over"]),
|
|
2577
|
+
/** The work it was about: the Goal `claim_goal` would hand that agent next. */
|
|
2578
|
+
goalId: z4.string().uuid().nullable().optional(),
|
|
2579
|
+
/** When the server last heard from the agent, as the host read it: the presence it stood back for. */
|
|
2580
|
+
seenAt: z4.string().datetime().nullable().optional(),
|
|
2581
|
+
/** When that work last moved (`claimable.since` on `check_replies`), the fact the bound is judged on. */
|
|
2582
|
+
since: z4.string().datetime().nullable().optional(),
|
|
2583
|
+
/** What the host said, in its log's own words: why it stood back, or what the take-over did. */
|
|
2584
|
+
said: z4.string().max(300).optional()
|
|
2585
|
+
});
|
|
2528
2586
|
var WakeNudgeSchema = z4.object({
|
|
2529
2587
|
kind: z4.enum(["reply", "request", "callback"]),
|
|
2530
2588
|
notificationId: z4.string().optional(),
|
|
@@ -2569,6 +2627,9 @@ var DeviceTokenSchema = z4.object({
|
|
|
2569
2627
|
* landed in the FIRST granted workspace and the agent rediscovered its own repo from
|
|
2570
2628
|
* the thread each time (host.ts, live catch 2026-08-06 — prompt-papered until now). */
|
|
2571
2629
|
workspace: z4.string().nullable().optional(),
|
|
2630
|
+
/** Local host recovery must preserve the launch's runtime and Paigy identity. */
|
|
2631
|
+
harness: z4.enum(["claude", "codex", "agy"]).optional(),
|
|
2632
|
+
session_id: z4.string().uuid().optional(),
|
|
2572
2633
|
uik_pub: z4.string().nullable().optional()
|
|
2573
2634
|
});
|
|
2574
2635
|
var SupportRequestSchema = z4.object({
|
|
@@ -2590,6 +2651,104 @@ var NotificationFeedbackKindSchema = z4.enum([
|
|
|
2590
2651
|
"other"
|
|
2591
2652
|
// anything else — the note carries it.
|
|
2592
2653
|
]);
|
|
2654
|
+
var SlimOptionSchema = OptionSchema.omit({ html: true });
|
|
2655
|
+
var QuestionRowSchema = z4.object({
|
|
2656
|
+
/** The card's id (`deliveryId:needId`, or `deliveryId:entryId` for an update), as the inbox mints it. */
|
|
2657
|
+
id: z4.string(),
|
|
2658
|
+
deliveryId: z4.string(),
|
|
2659
|
+
entryId: z4.string(),
|
|
2660
|
+
/** The decision it waits on; null for an update, which asks nothing. */
|
|
2661
|
+
needId: z4.string().nullable(),
|
|
2662
|
+
goalIds: z4.array(z4.string()),
|
|
2663
|
+
/** The name of the work it is about, when the read could word it. */
|
|
2664
|
+
goalTitle: z4.string().optional(),
|
|
2665
|
+
tokenId: z4.string().optional(),
|
|
2666
|
+
name: z4.string(),
|
|
2667
|
+
title: z4.string(),
|
|
2668
|
+
body: z4.string(),
|
|
2669
|
+
select: z4.enum(["one", "many", "rank", "confirm", "text"]),
|
|
2670
|
+
options: z4.array(SlimOptionSchema),
|
|
2671
|
+
hasPreview: z4.boolean(),
|
|
2672
|
+
blocking: z4.boolean(),
|
|
2673
|
+
askedAt: z4.string().datetime(),
|
|
2674
|
+
ring: InboxItemSchema.shape.ring,
|
|
2675
|
+
onCall: z4.literal(true).optional(),
|
|
2676
|
+
sealed: z4.boolean()
|
|
2677
|
+
});
|
|
2678
|
+
var WorkStateSchema = z4.enum(["ready", "active", "waiting", "done", "cancelled"]);
|
|
2679
|
+
var WorkRowSchema = z4.object({
|
|
2680
|
+
id: z4.string(),
|
|
2681
|
+
parentId: z4.string().nullable(),
|
|
2682
|
+
title: z4.string(),
|
|
2683
|
+
/** Straight off the Goal. */
|
|
2684
|
+
state: WorkStateSchema,
|
|
2685
|
+
owner: z4.string().nullable(),
|
|
2686
|
+
revision: z4.number().int(),
|
|
2687
|
+
/** Open questions on it, counted to 100. */
|
|
2688
|
+
waiting: z4.number().int(),
|
|
2689
|
+
/** Held by a gate on work that is not done. */
|
|
2690
|
+
blocked: z4.boolean(),
|
|
2691
|
+
lastProgressAt: z4.string().datetime().nullable(),
|
|
2692
|
+
/** The line written for its newest progress note, else that note's first words. */
|
|
2693
|
+
line: z4.string().nullable(),
|
|
2694
|
+
/** Work directly under it, counted to 100; the list carries up to 12 of them. */
|
|
2695
|
+
children: z4.number().int(),
|
|
2696
|
+
createdAt: z4.string().datetime(),
|
|
2697
|
+
updatedAt: z4.string().datetime(),
|
|
2698
|
+
/** When anything at or under it last moved — the order the list is in. */
|
|
2699
|
+
activeAt: z4.string().datetime(),
|
|
2700
|
+
/** A sealed outcome has no title here; the work's page opens it. */
|
|
2701
|
+
sealed: z4.boolean()
|
|
2702
|
+
});
|
|
2703
|
+
var ComputerRowSchema = ConnectionSummarySchema.omit({ activity: true });
|
|
2704
|
+
var AgentRowSchema = ComputerRowSchema.extend({
|
|
2705
|
+
/** Open questions it is asking the person, over every open card; null when that read failed. */
|
|
2706
|
+
asking: z4.number().int().nullable(),
|
|
2707
|
+
oldestAskAt: z4.string().datetime().nullable(),
|
|
2708
|
+
/** Up to three of the live Goals it holds, oldest first (the order it picks them up), and how
|
|
2709
|
+
* many in all among the account's 200 most recently active agent-held live Goals
|
|
2710
|
+
* (`agent_holds`); null when that read failed. */
|
|
2711
|
+
holds: z4.array(z4.object({ id: z4.string(), title: z4.string() })).nullable(),
|
|
2712
|
+
held: z4.number().int().nullable(),
|
|
2713
|
+
/** The earliest instant any Goal it holds went quiet, by the one rule (`coldSince`); null
|
|
2714
|
+
* while none has, or when that read failed. */
|
|
2715
|
+
cold: z4.string().datetime().nullable(),
|
|
2716
|
+
/** The newest line of its working log, and when the harness saw it. */
|
|
2717
|
+
line: z4.string().nullable(),
|
|
2718
|
+
lineAt: z4.string().datetime().nullable()
|
|
2719
|
+
});
|
|
2720
|
+
var SnapshotSchema = z4.object({
|
|
2721
|
+
/** The API's clock, taken before the first read: what a later delta will start from. */
|
|
2722
|
+
at: z4.string().datetime(),
|
|
2723
|
+
questions: z4.object({
|
|
2724
|
+
/** The newest 30 open cards, questions before updates. */
|
|
2725
|
+
items: z4.array(QuestionRowSchema),
|
|
2726
|
+
/** Every open question, and apart from them every update, and what was put off. */
|
|
2727
|
+
total: z4.number().int(),
|
|
2728
|
+
updates: z4.number().int(),
|
|
2729
|
+
putOff: z4.number().int()
|
|
2730
|
+
}).nullable(),
|
|
2731
|
+
agents: z4.object({
|
|
2732
|
+
/** Up to 60, most recently seen first. */
|
|
2733
|
+
items: z4.array(AgentRowSchema),
|
|
2734
|
+
more: z4.boolean()
|
|
2735
|
+
}).nullable(),
|
|
2736
|
+
work: z4.object({
|
|
2737
|
+
/** The 60 most recently active roots, each followed by up to 12 children; 240 rows at most. */
|
|
2738
|
+
items: z4.array(WorkRowSchema),
|
|
2739
|
+
/** How much work is behind each of the Work tab's four filters, each counted to 100, read with
|
|
2740
|
+
* the rows. `work_list` (20260928023533) owns the predicates: Live is `ready`, `active` or
|
|
2741
|
+
* `waiting`; Waiting on you is live work with an open question or an unmet gate; Not started
|
|
2742
|
+
* is `ready`; Done is `done` or `cancelled`. */
|
|
2743
|
+
counts: z4.object({ live: z4.number().int(), waiting: z4.number().int(), notStarted: z4.number().int(), done: z4.number().int() })
|
|
2744
|
+
}).nullable(),
|
|
2745
|
+
you: z4.object({
|
|
2746
|
+
settings: UserSettingsSchema,
|
|
2747
|
+
callable: z4.boolean(),
|
|
2748
|
+
/** Up to 20 paired computers; null when the roster read failed. */
|
|
2749
|
+
computers: z4.array(ComputerRowSchema).nullable()
|
|
2750
|
+
}).nullable()
|
|
2751
|
+
});
|
|
2593
2752
|
function sessionSlot(sessionId2) {
|
|
2594
2753
|
const id = sessionId2 ?? sessionId();
|
|
2595
2754
|
return `session:${id.slice(0, 8)}`;
|
|
@@ -2650,7 +2809,7 @@ function updateSlot(agent2, patch) {
|
|
|
2650
2809
|
}
|
|
2651
2810
|
function slotIdentity(agent2) {
|
|
2652
2811
|
const t = readTokenFile()[agent2];
|
|
2653
|
-
return { name: t?.name ?? null, voice: t?.voice ?? null, tokenId: t?.token_id ?? null, workspace: t?.workspace ?? null };
|
|
2812
|
+
return { name: t?.name ?? null, voice: t?.voice ?? null, tokenId: t?.token_id ?? null, workspace: t?.workspace ?? null, harness: t?.harness ?? null, sessionId: t?.session_id ?? null };
|
|
2654
2813
|
}
|
|
2655
2814
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2656
2815
|
function readToken(agent2 = agentName()) {
|
|
@@ -2714,9 +2873,21 @@ var UnpairedError = class extends Error {
|
|
|
2714
2873
|
this.name = "UnpairedError";
|
|
2715
2874
|
}
|
|
2716
2875
|
};
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2876
|
+
var RevokedError = class extends UnpairedError {
|
|
2877
|
+
constructor() {
|
|
2878
|
+
super();
|
|
2879
|
+
this.message = "This identity was revoked (the API answered 401 revoked): its credential is gone for good. Pairing again makes a new identity.";
|
|
2880
|
+
this.name = "RevokedError";
|
|
2881
|
+
}
|
|
2882
|
+
};
|
|
2883
|
+
async function ensureAuthed(res) {
|
|
2884
|
+
if (res.status !== 401) return res;
|
|
2885
|
+
let body = null;
|
|
2886
|
+
try {
|
|
2887
|
+
body = await res.json();
|
|
2888
|
+
} catch {
|
|
2889
|
+
}
|
|
2890
|
+
throw body?.error === "revoked" ? new RevokedError() : new UnpairedError();
|
|
2720
2891
|
}
|
|
2721
2892
|
var ApiError = class extends Error {
|
|
2722
2893
|
status;
|
|
@@ -2743,7 +2914,7 @@ async function fail(what, res) {
|
|
|
2743
2914
|
}
|
|
2744
2915
|
async function createGoal(input, opts = {}) {
|
|
2745
2916
|
const token = authToken(opts.token) ?? "";
|
|
2746
|
-
const res = ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals`, {
|
|
2917
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals`, {
|
|
2747
2918
|
method: "POST",
|
|
2748
2919
|
headers: { "content-type": "application/json", authorization: `Bearer ${token}`, "x-paigy-model": "goal-entry-v1" },
|
|
2749
2920
|
body: JSON.stringify(input)
|
|
@@ -2753,13 +2924,14 @@ async function createGoal(input, opts = {}) {
|
|
|
2753
2924
|
}
|
|
2754
2925
|
async function claimGoal(goalId, opts = {}) {
|
|
2755
2926
|
const token = authToken(opts.token) ?? "";
|
|
2756
|
-
const res = ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals/claim`, { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${token}`, "x-paigy-model": "goal-entry-v1" }, body: JSON.stringify(goalId ? { goalId } : {}) }));
|
|
2927
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals/claim`, { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${token}`, "x-paigy-model": "goal-entry-v1" }, body: JSON.stringify(goalId ? { goalId } : {}) }));
|
|
2757
2928
|
if (!res.ok) await fail("claim_goal", res);
|
|
2758
2929
|
return await res.json();
|
|
2759
2930
|
}
|
|
2760
|
-
async function getGoal(goalId, opts = {}) {
|
|
2931
|
+
async function getGoal(goalId, opts = {}, read = {}) {
|
|
2761
2932
|
const token = authToken(opts.token) ?? "";
|
|
2762
|
-
const
|
|
2933
|
+
const query = [read.history ? "history=1" : "", read.diagnose ? "diagnose=1" : ""].filter(Boolean).join("&");
|
|
2934
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals/${encodeURIComponent(goalId)}${query ? `?${query}` : ""}`, {
|
|
2763
2935
|
method: "GET",
|
|
2764
2936
|
headers: { authorization: `Bearer ${token}`, "x-paigy-model": "goal-entry-v1" }
|
|
2765
2937
|
}));
|
|
@@ -2769,13 +2941,13 @@ async function getGoal(goalId, opts = {}) {
|
|
|
2769
2941
|
async function updateGoal(goalId, input, opts = {}) {
|
|
2770
2942
|
const token = authToken(opts.token) ?? "";
|
|
2771
2943
|
const operationId = input.operationId ?? randomUUID2();
|
|
2772
|
-
const res = ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals/${encodeURIComponent(goalId)}`, { method: "PATCH", headers: { "content-type": "application/json", authorization: `Bearer ${token}`, "x-paigy-model": "goal-entry-v1" }, body: JSON.stringify({ ...input, operationId }) }));
|
|
2944
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals/${encodeURIComponent(goalId)}`, { method: "PATCH", headers: { "content-type": "application/json", authorization: `Bearer ${token}`, "x-paigy-model": "goal-entry-v1" }, body: JSON.stringify({ ...input, operationId }) }));
|
|
2773
2945
|
if (!res.ok) await fail("update_goal", res);
|
|
2774
2946
|
return await res.json();
|
|
2775
2947
|
}
|
|
2776
2948
|
var AWAIT_WINDOW_MS = 45e3;
|
|
2777
2949
|
async function hatch(name, voice = null) {
|
|
2778
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/hatch`, {
|
|
2950
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/hatch`, {
|
|
2779
2951
|
method: "POST",
|
|
2780
2952
|
headers: { "content-type": "application/json", authorization: `Bearer ${authToken()}` },
|
|
2781
2953
|
body: JSON.stringify({ name, voice })
|
|
@@ -2793,29 +2965,51 @@ async function whoAmI(opts = {}) {
|
|
|
2793
2965
|
return null;
|
|
2794
2966
|
}
|
|
2795
2967
|
}
|
|
2968
|
+
var NameTakenError = class extends Error {
|
|
2969
|
+
code = "agent_name_taken";
|
|
2970
|
+
};
|
|
2796
2971
|
async function setIdentity(patch, opts = {}) {
|
|
2797
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/identity`, {
|
|
2972
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/identity`, {
|
|
2798
2973
|
method: "PATCH",
|
|
2799
2974
|
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
2800
2975
|
body: JSON.stringify(patch)
|
|
2801
2976
|
}));
|
|
2802
|
-
if (!res.ok)
|
|
2977
|
+
if (!res.ok) {
|
|
2978
|
+
const text = await res.text();
|
|
2979
|
+
const refusal = (() => {
|
|
2980
|
+
try {
|
|
2981
|
+
return JSON.parse(text);
|
|
2982
|
+
} catch {
|
|
2983
|
+
return null;
|
|
2984
|
+
}
|
|
2985
|
+
})();
|
|
2986
|
+
if (res.status === 409 && refusal?.error === "agent_name_taken" && refusal.message) throw new NameTakenError(refusal.message);
|
|
2987
|
+
throw new Error(`set_identity failed: ${res.status} ${text}`);
|
|
2988
|
+
}
|
|
2803
2989
|
return await res.json();
|
|
2804
2990
|
}
|
|
2805
2991
|
async function claimSessions(opts = {}) {
|
|
2806
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/sessions/claim`, {
|
|
2992
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/sessions/claim`, {
|
|
2807
2993
|
headers: { authorization: `Bearer ${authToken(opts.token)}` }
|
|
2808
2994
|
// the HOST's identity
|
|
2809
2995
|
}));
|
|
2810
2996
|
if (!res.ok) throw new Error(`claim_sessions failed: ${res.status}`);
|
|
2811
2997
|
return (await res.json()).sessions;
|
|
2812
2998
|
}
|
|
2999
|
+
async function recordDecision(decision, opts = {}) {
|
|
3000
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/host/decisions`, {
|
|
3001
|
+
method: "POST",
|
|
3002
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
3003
|
+
body: JSON.stringify(decision)
|
|
3004
|
+
}));
|
|
3005
|
+
if (!res.ok) await fail("record_decision", res);
|
|
3006
|
+
}
|
|
2813
3007
|
async function heartbeat(runtime, opts = {}) {
|
|
2814
3008
|
const body = {
|
|
2815
3009
|
...runtime !== void 0 ? { runtime } : {},
|
|
2816
3010
|
...opts.activity !== void 0 ? { activity: opts.activity } : {}
|
|
2817
3011
|
};
|
|
2818
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/presence`, {
|
|
3012
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/presence`, {
|
|
2819
3013
|
method: "POST",
|
|
2820
3014
|
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
2821
3015
|
...Object.keys(body).length > 0 ? { body: JSON.stringify(body) } : {}
|
|
@@ -2823,7 +3017,7 @@ async function heartbeat(runtime, opts = {}) {
|
|
|
2823
3017
|
if (!res.ok) throw new Error(`heartbeat failed: ${res.status}`);
|
|
2824
3018
|
}
|
|
2825
3019
|
async function registerDelivery(mode, opts = {}) {
|
|
2826
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/delivery`, {
|
|
3020
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/delivery`, {
|
|
2827
3021
|
method: "POST",
|
|
2828
3022
|
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
2829
3023
|
body: JSON.stringify({ mode })
|
|
@@ -2832,21 +3026,21 @@ async function registerDelivery(mode, opts = {}) {
|
|
|
2832
3026
|
return await res.json();
|
|
2833
3027
|
}
|
|
2834
3028
|
async function listNotes(opts = {}) {
|
|
2835
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/notes`, {
|
|
3029
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/notes`, {
|
|
2836
3030
|
headers: { authorization: `Bearer ${authToken(opts.token)}` }
|
|
2837
3031
|
}));
|
|
2838
3032
|
if (!res.ok) throw new Error(`list_notes failed: ${res.status} ${await res.text()}`);
|
|
2839
3033
|
return await res.json();
|
|
2840
3034
|
}
|
|
2841
3035
|
async function listConnections(opts = {}) {
|
|
2842
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/tokens`, {
|
|
3036
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/tokens`, {
|
|
2843
3037
|
headers: { authorization: `Bearer ${authToken(opts.token)}`, "x-paigy-model": "goal-entry-v1" }
|
|
2844
3038
|
}));
|
|
2845
3039
|
if (!res.ok) throw new Error(`list_connections failed: ${res.status} ${await res.text()}`);
|
|
2846
3040
|
return await res.json();
|
|
2847
3041
|
}
|
|
2848
3042
|
async function submitTriage(run, opts = {}) {
|
|
2849
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/triage`, {
|
|
3043
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/triage`, {
|
|
2850
3044
|
method: "POST",
|
|
2851
3045
|
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
2852
3046
|
body: JSON.stringify(run)
|
|
@@ -2855,14 +3049,14 @@ async function submitTriage(run, opts = {}) {
|
|
|
2855
3049
|
return await res.json();
|
|
2856
3050
|
}
|
|
2857
3051
|
async function getTriage(opts = {}) {
|
|
2858
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/triage`, {
|
|
3052
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/triage`, {
|
|
2859
3053
|
headers: { authorization: `Bearer ${authToken(opts.token)}` }
|
|
2860
3054
|
}));
|
|
2861
3055
|
if (!res.ok) throw new Error(`get_triage failed: ${res.status} ${await res.text()}`);
|
|
2862
3056
|
return await res.json();
|
|
2863
3057
|
}
|
|
2864
3058
|
async function acceptTriage(proposalId, body, opts = {}) {
|
|
2865
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/triage/${encodeURIComponent(proposalId)}/accept`, {
|
|
3059
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/triage/${encodeURIComponent(proposalId)}/accept`, {
|
|
2866
3060
|
method: "POST",
|
|
2867
3061
|
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
2868
3062
|
body: JSON.stringify(body)
|
|
@@ -2871,7 +3065,7 @@ async function acceptTriage(proposalId, body, opts = {}) {
|
|
|
2871
3065
|
return await res.json();
|
|
2872
3066
|
}
|
|
2873
3067
|
async function dismissTriage(proposalId, opts = {}) {
|
|
2874
|
-
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/triage/${encodeURIComponent(proposalId)}/dismiss`, {
|
|
3068
|
+
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/triage/${encodeURIComponent(proposalId)}/dismiss`, {
|
|
2875
3069
|
method: "POST",
|
|
2876
3070
|
headers: { authorization: `Bearer ${authToken(opts.token)}` }
|
|
2877
3071
|
}));
|
|
@@ -2927,7 +3121,7 @@ async function contact(input, opts = {}) {
|
|
|
2927
3121
|
ask: a.ask,
|
|
2928
3122
|
options: a.options ?? []
|
|
2929
3123
|
}));
|
|
2930
|
-
const res = ensureAuthed(await send2(`${BACKEND_URL}/api/goals/contact`, {
|
|
3124
|
+
const res = await ensureAuthed(await send2(`${BACKEND_URL}/api/goals/contact`, {
|
|
2931
3125
|
method: "POST",
|
|
2932
3126
|
headers,
|
|
2933
3127
|
signal: opts.signal,
|
|
@@ -2949,15 +3143,17 @@ async function contact(input, opts = {}) {
|
|
|
2949
3143
|
}
|
|
2950
3144
|
const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId).map(({ id, deliveryId: deliveryId2, goalId }) => ({ ...id ? { id } : {}, deliveryId: deliveryId2, goalId }));
|
|
2951
3145
|
const joinedCard = deliveries[0]?.joinedCard === true;
|
|
3146
|
+
const joinedCall = deliveries[0]?.joinedCall === true;
|
|
2952
3147
|
const read = async (signal2) => {
|
|
2953
|
-
const res = ensureAuthed(await send2(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
|
|
3148
|
+
const res = await ensureAuthed(await send2(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
|
|
2954
3149
|
if (!res.ok) await fail("read_delivery", res);
|
|
2955
3150
|
const one = await res.json();
|
|
2956
3151
|
return {
|
|
2957
3152
|
...one,
|
|
2958
3153
|
...all.length > 1 ? { deliveries: all } : {},
|
|
2959
3154
|
...recorded.length ? { recorded, notSent: NOT_SENT } : {},
|
|
2960
|
-
...joinedCard ? { joinedCard: true } : {}
|
|
3155
|
+
...joinedCard ? { joinedCard: true } : {},
|
|
3156
|
+
...joinedCall ? { joinedCall: true } : {}
|
|
2961
3157
|
};
|
|
2962
3158
|
};
|
|
2963
3159
|
const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
|
|
@@ -2979,7 +3175,7 @@ async function contact(input, opts = {}) {
|
|
|
2979
3175
|
}
|
|
2980
3176
|
}
|
|
2981
3177
|
async function checkReplies(opts = {}) {
|
|
2982
|
-
const res = ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/deliveries`, {
|
|
3178
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/deliveries`, {
|
|
2983
3179
|
headers: { authorization: `Bearer ${authToken(opts.token) ?? ""}`, "x-paigy-model": "goal-entry-v1" }
|
|
2984
3180
|
}));
|
|
2985
3181
|
if (!res.ok) await fail("check_replies", res);
|
|
@@ -3008,38 +3204,53 @@ function decided(result) {
|
|
|
3008
3204
|
return void 0;
|
|
3009
3205
|
}
|
|
3010
3206
|
}
|
|
3207
|
+
var handle = (entryId) => entryId.slice(0, 8);
|
|
3011
3208
|
function conversation(e) {
|
|
3012
3209
|
const answers = new Map((e.answers ?? []).map((a) => [a.decisionNeedId, a]));
|
|
3210
|
+
const shown = new Set((e.entries ?? []).map((entry) => entry.entryId));
|
|
3211
|
+
const repliedTo = new Set((e.entries ?? []).flatMap((entry) => entry.aboutId && shown.has(entry.aboutId) ? [entry.aboutId] : []));
|
|
3013
3212
|
return (e.entries ?? []).map((entry) => {
|
|
3014
3213
|
const sealed = !!entry.content && "sealed" in entry.content;
|
|
3015
3214
|
const plain = entry.content && "plain" in entry.content ? entry.content.plain : null;
|
|
3016
3215
|
const options = plain && typeof plain === "object" && Array.isArray(plain.options) ? plain.options.map((o) => o.label).filter((l) => typeof l === "string") : [];
|
|
3017
3216
|
const need = (e.decisionNeeds ?? []).find((n) => n.requestEntryId === entry.entryId);
|
|
3018
3217
|
const answer = need ? answers.get(need.decisionNeedId) : void 0;
|
|
3019
|
-
const
|
|
3218
|
+
const decision = answer ? decided(answer.result) : void 0;
|
|
3020
3219
|
return compact({
|
|
3021
|
-
id: entry.entryId,
|
|
3220
|
+
id: repliedTo.has(entry.entryId) ? handle(entry.entryId) : void 0,
|
|
3022
3221
|
from: who(entry.authorParticipant),
|
|
3023
3222
|
at: at(entry.createdAt),
|
|
3024
3223
|
said: sealed ? "[encrypted]" : entryWords(entry).trim(),
|
|
3025
|
-
re: entry.aboutId
|
|
3224
|
+
re: entry.aboutId && shown.has(entry.aboutId) ? handle(entry.aboutId) : void 0,
|
|
3026
3225
|
options,
|
|
3027
|
-
decision: need ? compact({
|
|
3226
|
+
decision: need ? compact({ state: need.state, answer: decision }) : void 0
|
|
3028
3227
|
});
|
|
3029
3228
|
});
|
|
3030
3229
|
}
|
|
3230
|
+
function more(g) {
|
|
3231
|
+
const earlier = g.earlier && g.earlier.omitted + g.earlier.shortened ? `${[g.earlier.omitted ? `${g.earlier.omitted} earlier entries left out` : "", g.earlier.shortened ? `${g.earlier.shortened} shortened to their headline` : ""].filter(Boolean).join(", ")}: get_goal({ goalId: "${g.goalId}", history: true }) reads every one in full.` : null;
|
|
3232
|
+
return [
|
|
3233
|
+
earlier,
|
|
3234
|
+
g.subtree?.length ? "A Goal below, in full: get_goal({ goalId }) with its goalId above." : null,
|
|
3235
|
+
g.parentGoalId ? `The Goal above: get_goal({ goalId: "${g.parentGoalId}" }).` : null,
|
|
3236
|
+
"Anything else waiting for you (other requests, answers, reviews): check_replies()."
|
|
3237
|
+
].filter((line) => !!line);
|
|
3238
|
+
}
|
|
3031
3239
|
function goalView(g) {
|
|
3032
3240
|
if (!g.goalId) return compact({ state: g.state, next: g.message });
|
|
3241
|
+
const lines2 = conversation(g);
|
|
3242
|
+
const newest = [...lines2].reverse().find((l) => l.from !== "person");
|
|
3033
3243
|
return compact({
|
|
3034
3244
|
goalId: g.goalId,
|
|
3035
3245
|
revision: g.revision,
|
|
3036
3246
|
state: g.state,
|
|
3037
3247
|
outcome: g.outcome,
|
|
3038
|
-
progress: g.progress,
|
|
3248
|
+
progress: g.progress && g.progress.trim() !== newest?.said ? g.progress : void 0,
|
|
3039
3249
|
reviewPending: g.reviewPending || void 0,
|
|
3040
3250
|
owner: g.ownerParticipant,
|
|
3041
3251
|
parentGoalId: g.parentGoalId,
|
|
3042
|
-
|
|
3252
|
+
// Every child is in the subtree below, with its goalId: listed again, it was said twice.
|
|
3253
|
+
childGoalIds: g.subtree?.length ? void 0 : g.childGoalIds,
|
|
3043
3254
|
// EVERYTHING BELOW, read through this Goal (umbrella reads): one line per Goal, indented by
|
|
3044
3255
|
// depth, then each question with its answer — so an agent that asked through an umbrella
|
|
3045
3256
|
// reads every answer in one call, whoever owns the child it landed on.
|
|
@@ -3051,10 +3262,12 @@ function goalView(g) {
|
|
|
3051
3262
|
blockedBy: g.blockedDependencyGoalIds,
|
|
3052
3263
|
dueAt: g.dueAt,
|
|
3053
3264
|
leaseExpiresAt: g.leaseExpiresAt,
|
|
3054
|
-
conversation:
|
|
3055
|
-
next: g.message
|
|
3265
|
+
conversation: lines2,
|
|
3266
|
+
next: g.message,
|
|
3267
|
+
more: more(g)
|
|
3056
3268
|
});
|
|
3057
3269
|
}
|
|
3270
|
+
var JOINED_CALL = "This ask joined the call the person is ALREADY ON \u2014 no ring, no card of its own. `conversation` above is that whole call, so the answers already given on it before your ask arrived are in it. Yours is queued for the bot's next turn: call contact({deliveryId}) again to keep reading this same call until your answer lands \u2014 do not resend the ask. To add information or a further question to the same call, contact again while it is live; that joins it too.";
|
|
3058
3271
|
function deliveryView(d) {
|
|
3059
3272
|
const answers = "Answers land on the Goal: read them with claim_goal or get_goal. Do not poll this notification.";
|
|
3060
3273
|
return compact({
|
|
@@ -3069,7 +3282,8 @@ function deliveryView(d) {
|
|
|
3069
3282
|
recorded: d.recorded,
|
|
3070
3283
|
notSent: d.notSent,
|
|
3071
3284
|
joinedCard: d.joinedCard,
|
|
3072
|
-
|
|
3285
|
+
joinedCall: d.joinedCall,
|
|
3286
|
+
next: d.joinedCall ? `${JOINED_CALL}${d.message ? ` ${d.message}` : ""}` : d.kind === "notification" ? d.joinedCard ? `Added to the open report card on its Goal; no new push was sent. ${answers}` : answers : d.state === "open" && d.decisionNeeds.some((n) => n.state === "open") ? `Decision pending. Call contact(${JSON.stringify({ deliveryId: d.deliveryId })}) now to continue this same Call; do not resend the ask or end your turn merely because this wait window returned. ${d.message ?? ""}`.trim() : d.message
|
|
3073
3287
|
});
|
|
3074
3288
|
}
|
|
3075
3289
|
function repliesView(r) {
|
|
@@ -3089,6 +3303,10 @@ function repliesView(r) {
|
|
|
3089
3303
|
// WORK ASSIGNED TO YOU (owner, 2026-09-24): your Goals nobody has started, however they
|
|
3090
3304
|
// became yours — a Goal handed to you is not a Delivery, and must not wait for a claim to be seen.
|
|
3091
3305
|
...assigned.length ? { assigned } : {},
|
|
3306
|
+
// WHAT claim_goal HANDS YOU NEXT (owner, 2026-09-27: "Wake on any work"): the same predicate that
|
|
3307
|
+
// starts a sleeping agent, so "nothing is waiting" is never said while a claim would hand you work
|
|
3308
|
+
// — a Goal you started in a process that died is on no other list.
|
|
3309
|
+
...r.claimable ? { claimable: r.claimable } : {},
|
|
3092
3310
|
// NEW ON YOUR GOALS (owner, 2026-09-23): a reply is an Entry on the Goal, not a Delivery, so the
|
|
3093
3311
|
// Goals flagged for your review are listed here until you acknowledge them.
|
|
3094
3312
|
...review.length ? { review } : {},
|
|
@@ -3098,14 +3316,27 @@ function repliesView(r) {
|
|
|
3098
3316
|
// OTHER AGENTS' STALLED WORK (owner, 2026-09-23): any agent may take it over and close it.
|
|
3099
3317
|
...others.length ? { stalledOthers: others } : {},
|
|
3100
3318
|
next: [
|
|
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.",
|
|
3319
|
+
deliveries.length || r.claimable || review.length || assigned.length ? "Act on a Goal with claim_goal; reread one Delivery in full with contact({deliveryId})." : "Nothing is waiting.",
|
|
3102
3320
|
...assigned.length ? [`${assigned.length} of your Goals are assigned to you and not started (assigned): claim_goal({goalId}) to start one.`] : [],
|
|
3103
|
-
|
|
3321
|
+
// THE PERSON SPOKE, AND THAT IS NOT "SOMETHING NEW" (2026-09-29). This line said only how
|
|
3322
|
+
// many Goals were flagged, so four things the owner said on a call read as four chores. The
|
|
3323
|
+
// rows carry their words now (`said`, `saidAt`, newest first, `delivery/read.ts`); the
|
|
3324
|
+
// sentence names the person and quotes the freshest one, because a count is not a message.
|
|
3325
|
+
...review.length ? [spoke(review) ?? `${review.length} of your Goals have something new (review): read each with claim_goal or get_goal, then update_goal with reviewed: true.`] : [],
|
|
3104
3326
|
...stalled.length ? [`${stalled.length} of your Goals have had no progress for 3 days: update_goal each with progress, finish it, or cancel it.`] : [],
|
|
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.`] : []
|
|
3327
|
+
...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.`] : [],
|
|
3328
|
+
// No name of its own (#2525): the API's ask, verbatim.
|
|
3329
|
+
...r.unnamed ? [r.unnamed] : []
|
|
3106
3330
|
].join(" ")
|
|
3107
3331
|
};
|
|
3108
3332
|
}
|
|
3333
|
+
function spoke(review) {
|
|
3334
|
+
const withWords = review.filter((r) => r.said?.trim());
|
|
3335
|
+
if (!withWords.length) return null;
|
|
3336
|
+
const newest = withWords[0];
|
|
3337
|
+
const rest = review.length - 1;
|
|
3338
|
+
return `The person has spoken on ${withWords.length === 1 ? "one of your Goals" : `${withWords.length} of your Goals`} \u2014 newest, on ${newest.title ?? newest.goalId}: "${newest.said}". Read it with claim_goal or get_goal, answer what it asks, then update_goal with reviewed: true.${rest > 0 ? ` (${rest} more flagged; see review.)` : ""}`;
|
|
3339
|
+
}
|
|
3109
3340
|
async function runTool(name, args, opts) {
|
|
3110
3341
|
const { waits, signal, ...client } = opts;
|
|
3111
3342
|
const input = args ?? {};
|
|
@@ -3126,8 +3357,11 @@ async function runTool(name, args, opts) {
|
|
|
3126
3357
|
return goalView(await claimGoal(goalId, client));
|
|
3127
3358
|
}
|
|
3128
3359
|
case "get_goal": {
|
|
3129
|
-
const { goalId } = GetGoalSchema.parse(input);
|
|
3130
|
-
|
|
3360
|
+
const { goalId, history, diagnose } = GetGoalSchema.parse(input);
|
|
3361
|
+
const goal = await getGoal(goalId, client, { history, diagnose });
|
|
3362
|
+
const view = goalView(goal);
|
|
3363
|
+
const diagnosis = goal.diagnosis;
|
|
3364
|
+
return diagnosis === void 0 ? view : { ...view, diagnosis };
|
|
3131
3365
|
}
|
|
3132
3366
|
case "update_goal": {
|
|
3133
3367
|
const { goalId, ...body } = UpdateGoalToolSchema.parse(input);
|
|
@@ -3342,6 +3576,7 @@ export {
|
|
|
3342
3576
|
requestCode,
|
|
3343
3577
|
pollToken,
|
|
3344
3578
|
UnpairedError,
|
|
3579
|
+
RevokedError,
|
|
3345
3580
|
ApiError,
|
|
3346
3581
|
overrideToken,
|
|
3347
3582
|
authToken,
|
|
@@ -3352,8 +3587,10 @@ export {
|
|
|
3352
3587
|
AWAIT_WINDOW_MS,
|
|
3353
3588
|
hatch,
|
|
3354
3589
|
whoAmI,
|
|
3590
|
+
NameTakenError,
|
|
3355
3591
|
setIdentity,
|
|
3356
3592
|
claimSessions,
|
|
3593
|
+
recordDecision,
|
|
3357
3594
|
heartbeat,
|
|
3358
3595
|
registerDelivery,
|
|
3359
3596
|
listNotes,
|