@paigy/harness 0.3.15 → 0.3.16
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/cli.js +226 -122
- package/dist/main.js +226 -122
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -23937,6 +23937,7 @@ async function contact(input, opts = {}) {
|
|
|
23937
23937
|
const headers = { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token) ?? ""}`, "x-paigy-model": "goal-entry-v1" };
|
|
23938
23938
|
let deliveryId;
|
|
23939
23939
|
let deliveries = [];
|
|
23940
|
+
let recorded = [];
|
|
23940
23941
|
if ("deliveryId" in parsed) {
|
|
23941
23942
|
deliveryId = parsed.deliveryId;
|
|
23942
23943
|
} else {
|
|
@@ -23965,16 +23966,24 @@ async function contact(input, opts = {}) {
|
|
|
23965
23966
|
if (!res.ok) await fail("batch contact", res);
|
|
23966
23967
|
const receipt = await res.json();
|
|
23967
23968
|
deliveries = receipt.deliveries ?? [];
|
|
23969
|
+
recorded = receipt.recorded ?? [];
|
|
23970
|
+
if (deliveries.length === 0 && recorded.length) return { sent: false, recorded, message: NOT_SENT };
|
|
23968
23971
|
if (deliveries.length === 0) throw new Error("Goal contact returned no Delivery identities");
|
|
23969
23972
|
deliveryId = deliveries[0]?.deliveryId ?? "";
|
|
23970
23973
|
if (!deliveryId) throw new Error("Goal contact returned no valid Delivery identity");
|
|
23971
23974
|
}
|
|
23972
|
-
const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId);
|
|
23975
|
+
const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId).map(({ id, deliveryId: deliveryId2, goalId }) => ({ ...id ? { id } : {}, deliveryId: deliveryId2, goalId }));
|
|
23976
|
+
const joinedCard = deliveries[0]?.joinedCard === true;
|
|
23973
23977
|
const read = async (signal2) => {
|
|
23974
23978
|
const res = ensureAuthed(await send22(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, { headers, signal: signal2 }));
|
|
23975
23979
|
if (!res.ok) await fail("read_delivery", res);
|
|
23976
23980
|
const one = await res.json();
|
|
23977
|
-
return
|
|
23981
|
+
return {
|
|
23982
|
+
...one,
|
|
23983
|
+
...all.length > 1 ? { deliveries: all } : {},
|
|
23984
|
+
...recorded.length ? { recorded, notSent: NOT_SENT } : {},
|
|
23985
|
+
...joinedCard ? { joinedCard: true } : {}
|
|
23986
|
+
};
|
|
23978
23987
|
};
|
|
23979
23988
|
const settled = (d) => d.kind === "notification" || d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution");
|
|
23980
23989
|
if (opts.waits === false) return read(opts.signal);
|
|
@@ -24002,45 +24011,105 @@ async function checkReplies(opts = {}) {
|
|
|
24002
24011
|
return await res.json();
|
|
24003
24012
|
}
|
|
24004
24013
|
function frame(line, topic, event, payload) {
|
|
24014
|
+
const ref = String(++line.ref);
|
|
24005
24015
|
try {
|
|
24006
|
-
line.ws?.send(JSON.stringify({ topic, event, payload, ref
|
|
24016
|
+
line.ws?.send(JSON.stringify({ topic, event, payload, ref }));
|
|
24007
24017
|
} catch {
|
|
24008
24018
|
}
|
|
24019
|
+
return ref;
|
|
24009
24020
|
}
|
|
24010
24021
|
function stop(line) {
|
|
24011
24022
|
line.open = false;
|
|
24023
|
+
line.beat = void 0;
|
|
24012
24024
|
clearInterval(line.heart);
|
|
24013
24025
|
clearTimeout(line.retry);
|
|
24026
|
+
for (const t of line.topics.values()) {
|
|
24027
|
+
clearTimeout(t.timer);
|
|
24028
|
+
t.joined = false;
|
|
24029
|
+
t.ref = void 0;
|
|
24030
|
+
}
|
|
24031
|
+
}
|
|
24032
|
+
function join3(line, name, t) {
|
|
24033
|
+
clearTimeout(t.timer);
|
|
24034
|
+
t.joined = false;
|
|
24035
|
+
t.ref = frame(line, `realtime:${name}`, "phx_join", JOIN);
|
|
24036
|
+
t.timer = setTimeout(() => rejoin(line, name, t), JOIN_TIMEOUT_MS);
|
|
24037
|
+
}
|
|
24038
|
+
function rejoin(line, name, t) {
|
|
24039
|
+
clearTimeout(t.timer);
|
|
24040
|
+
t.joined = false;
|
|
24041
|
+
t.ref = void 0;
|
|
24042
|
+
if (!line.open || line.topics.get(name) !== t) return;
|
|
24043
|
+
t.timer = setTimeout(() => {
|
|
24044
|
+
if (line.open && line.topics.get(name) === t) join3(line, name, t);
|
|
24045
|
+
}, RETRY_MS[Math.min(t.attempt++, RETRY_MS.length - 1)]);
|
|
24046
|
+
}
|
|
24047
|
+
function joined(t) {
|
|
24048
|
+
clearTimeout(t.timer);
|
|
24049
|
+
t.joined = true;
|
|
24050
|
+
t.attempt = 0;
|
|
24051
|
+
for (const l of t.listeners) l.onJoined?.(++l.joins);
|
|
24014
24052
|
}
|
|
24015
24053
|
function connect(line) {
|
|
24016
24054
|
const base = line.url.replace(/^http/, "ws").replace(/\/+$/, "");
|
|
24017
24055
|
const ws = line.ws = new import_undici.WebSocket(`${base}/realtime/v1/websocket?apikey=${encodeURIComponent(line.anonKey)}&vsn=1.0.0`);
|
|
24056
|
+
let gone = false;
|
|
24057
|
+
const again = () => {
|
|
24058
|
+
if (gone) return;
|
|
24059
|
+
gone = true;
|
|
24060
|
+
stop(line);
|
|
24061
|
+
if (!line.topics.size) return;
|
|
24062
|
+
line.retry = setTimeout(() => connect(line), RETRY_MS[Math.min(line.attempt++, RETRY_MS.length - 1)]);
|
|
24063
|
+
};
|
|
24018
24064
|
ws.addEventListener("open", () => {
|
|
24065
|
+
if (gone) return;
|
|
24019
24066
|
line.open = true;
|
|
24020
24067
|
line.attempt = 0;
|
|
24021
|
-
for (const
|
|
24022
|
-
line.heart = setInterval(() =>
|
|
24068
|
+
for (const [name, t] of line.topics) join3(line, name, t);
|
|
24069
|
+
line.heart = setInterval(() => {
|
|
24070
|
+
if (line.beat) {
|
|
24071
|
+
try {
|
|
24072
|
+
ws.close();
|
|
24073
|
+
} catch {
|
|
24074
|
+
}
|
|
24075
|
+
again();
|
|
24076
|
+
return;
|
|
24077
|
+
}
|
|
24078
|
+
line.beat = frame(line, "phoenix", "heartbeat", {});
|
|
24079
|
+
}, HEARTBEAT_MS);
|
|
24023
24080
|
});
|
|
24024
24081
|
ws.addEventListener("message", (e) => {
|
|
24082
|
+
if (gone) return;
|
|
24025
24083
|
let m;
|
|
24026
24084
|
try {
|
|
24027
24085
|
m = JSON.parse(String(e.data));
|
|
24028
24086
|
} catch {
|
|
24029
24087
|
return;
|
|
24030
24088
|
}
|
|
24031
|
-
if (m.
|
|
24032
|
-
|
|
24089
|
+
if (m.topic === "phoenix") {
|
|
24090
|
+
if (m.event === "phx_reply" && m.ref === line.beat) line.beat = void 0;
|
|
24091
|
+
return;
|
|
24092
|
+
}
|
|
24093
|
+
if (!m.topic?.startsWith("realtime:")) return;
|
|
24094
|
+
const name = m.topic.slice("realtime:".length);
|
|
24095
|
+
const t = line.topics.get(name);
|
|
24096
|
+
if (!t) return;
|
|
24097
|
+
if (m.event === "phx_reply") {
|
|
24098
|
+
if (!t.ref || m.ref !== t.ref) return;
|
|
24099
|
+
if (m.payload?.status === "ok") joined(t);
|
|
24100
|
+
else rejoin(line, name, t);
|
|
24101
|
+
return;
|
|
24102
|
+
}
|
|
24103
|
+
if (m.event === "phx_error" || m.event === "phx_close") {
|
|
24104
|
+
if (m.ref && m.ref !== t.ref) return;
|
|
24105
|
+
rejoin(line, name, t);
|
|
24106
|
+
return;
|
|
24107
|
+
}
|
|
24108
|
+
if (m.event !== "broadcast") return;
|
|
24109
|
+
for (const l of t.listeners) {
|
|
24033
24110
|
if (m.payload?.event === l.event) l.onMessage(m.payload.payload);
|
|
24034
24111
|
}
|
|
24035
24112
|
});
|
|
24036
|
-
let gone = false;
|
|
24037
|
-
const again = () => {
|
|
24038
|
-
if (gone) return;
|
|
24039
|
-
gone = true;
|
|
24040
|
-
stop(line);
|
|
24041
|
-
if (!line.topics.size) return;
|
|
24042
|
-
line.retry = setTimeout(() => connect(line), RETRY_MS[Math.min(line.attempt++, RETRY_MS.length - 1)]);
|
|
24043
|
-
};
|
|
24044
24113
|
ws.addEventListener("close", again);
|
|
24045
24114
|
ws.addEventListener("error", again);
|
|
24046
24115
|
}
|
|
@@ -24048,17 +24117,22 @@ function subscribeRealtime(args) {
|
|
|
24048
24117
|
const key = `${args.url} ${args.anonKey}`;
|
|
24049
24118
|
let line = lines.get(key);
|
|
24050
24119
|
if (!line) lines.set(key, line = { url: args.url, anonKey: args.anonKey, open: false, attempt: 0, ref: 0, topics: /* @__PURE__ */ new Map() });
|
|
24051
|
-
let
|
|
24052
|
-
if (!
|
|
24053
|
-
line.topics.set(args.topic,
|
|
24054
|
-
if (line.open)
|
|
24055
|
-
}
|
|
24056
|
-
const
|
|
24057
|
-
|
|
24120
|
+
let t = line.topics.get(args.topic);
|
|
24121
|
+
if (!t) {
|
|
24122
|
+
line.topics.set(args.topic, t = { listeners: /* @__PURE__ */ new Set(), joined: false, attempt: 0 });
|
|
24123
|
+
if (line.open) join3(line, args.topic, t);
|
|
24124
|
+
}
|
|
24125
|
+
const topic = t;
|
|
24126
|
+
const me = { event: args.event, onMessage: args.onMessage, onJoined: args.onJoined, joins: 0 };
|
|
24127
|
+
topic.listeners.add(me);
|
|
24128
|
+
if (topic.joined && me.onJoined) queueMicrotask(() => {
|
|
24129
|
+
if (topic.joined && topic.listeners.has(me)) me.onJoined?.(++me.joins);
|
|
24130
|
+
});
|
|
24058
24131
|
if (!line.ws) connect(line);
|
|
24059
24132
|
return {
|
|
24060
24133
|
close: () => {
|
|
24061
|
-
if (!
|
|
24134
|
+
if (!topic.listeners.delete(me) || topic.listeners.size) return;
|
|
24135
|
+
clearTimeout(topic.timer);
|
|
24062
24136
|
line.topics.delete(args.topic);
|
|
24063
24137
|
if (line.topics.size) {
|
|
24064
24138
|
if (line.open) frame(line, `realtime:${args.topic}`, "phx_leave", {});
|
|
@@ -24074,7 +24148,7 @@ function subscribeRealtime(args) {
|
|
|
24074
24148
|
};
|
|
24075
24149
|
}
|
|
24076
24150
|
async function subscribeWake(onNudge, opts = {}) {
|
|
24077
|
-
const { release = true, ...client } = opts;
|
|
24151
|
+
const { release = true, onJoined, ...client } = opts;
|
|
24078
24152
|
const cfg = await registerDelivery("self_hosted", client);
|
|
24079
24153
|
const { url, anonKey } = cfg.realtime;
|
|
24080
24154
|
const channel = wakeChannel(cfg.tokenId);
|
|
@@ -24083,7 +24157,8 @@ async function subscribeWake(onNudge, opts = {}) {
|
|
|
24083
24157
|
anonKey,
|
|
24084
24158
|
topic: channel,
|
|
24085
24159
|
event: WAKE_EVENT,
|
|
24086
|
-
onMessage: (payload) => onNudge(payload ?? {})
|
|
24160
|
+
onMessage: (payload) => onNudge(payload ?? {}),
|
|
24161
|
+
onJoined
|
|
24087
24162
|
});
|
|
24088
24163
|
return {
|
|
24089
24164
|
tokenId: cfg.tokenId,
|
|
@@ -24095,7 +24170,7 @@ async function subscribeWake(onNudge, opts = {}) {
|
|
|
24095
24170
|
}
|
|
24096
24171
|
};
|
|
24097
24172
|
}
|
|
24098
|
-
var import_undici, require2, BACKEND_URL, NETWORK_MSG, PROXY_ENV, agent, INSTANCE_ID, WORKSPACE_ID, envSession, lineage, ignoreOverride, defaultOptions, getDefaultOptions, getRefs, getRelativePath, parseCatchDef, integerDateParser, isJsonSchema7AllOfType, emojiRegex2, zodPatterns, ALPHA_NUMERIC, primitiveMappings, asAnyOf, parseOptionalDef, parsePipelineDef, parseReadonlyDef, selectParser, get$ref, addMeta, zodToJsonSchema, OPTIONS_MIN, OPTIONS_MAX, OptionSchema, OptionInputSchema, NIGHT, AskInputSchema, StartContactSchema, ContactSchema, CONTACT_SCHEMA, CONTACT_DESCRIPTION, CreateGoalSchema, CreateGoalToolSchema, CREATE_GOAL_DESCRIPTION, UpdateGoalSchema, UpdateGoalToolSchema, ClaimGoalSchema, GetGoalSchema, GET_GOAL_DESCRIPTION, UPDATE_GOAL_DESCRIPTION, CLAIM_GOAL_DESCRIPTION, CHECK_REPLIES_DESCRIPTION, CheckRepliesSchema, AGENT_TOOLS, AGENT_TOOL_NAMES, LIVE_MS, ContextSchema, ParticipantSchema, TransformSchema, VisualSchema, NotifyLevelSchema, SelectShapeSchema, ReceiptEventSchema, AttentionSchema, NotifyRequestFields, NotifyRequestSchema, NotifyStatusSchema, AgentStateSchema, TurnSchema, UserAnswerSchema, IntentSchema, RideAlongSchema, AwaitItemSchema, VoiceKeySchema, AgendaTurnSchema, CLAIM_STALE_MS, InboxItemSchema, APNS_TOKEN_RE, PushTokenSchema, MissedCallSchema, clock, QUIET, BrokerTuningSchema, UserSettingsSchema, HistoryItemSchema, ACTIVITY_LINES, ACTIVITY_LINE_MAX, AgentActivitySchema, ConnectionSummarySchema, LedgerItemSchema, AgentLedgerSchema, ReassignResultSchema, MoveRingSchema, MoveSchema, QueueQuestionSchema, QueueItemSchema, COLD_AFTER_MS, NoteSourceSchema, NoteStatusSchema, NoteRepeatSchema, DecisionSchema, NoteSchema, TriageItemSchema, TriageAssignmentSchema, TriageStatusSchema, SubmitTriageSchema, TriageProposalSchema, AcceptTriageSchema, AcceptTriageResultSchema, DeliveryModeSchema, WAKE_EVENT, wakeChannel, RegisterDeliverySchema, OAuthStartSchema, DeliveryConfigSchema, WakeNudgeSchema, PairingStatusSchema, DeviceCodeSchema, DeviceInfoSchema, DeviceTokenSchema, SupportRequestSchema, NotificationFeedbackKindSchema, TOKEN_PATH, KEY_PATH, sleep, UnpairedError, ApiError, tokenOverride, authToken, AWAIT_WINDOW_MS, asked, HEARTBEAT_MS, RETRY_MS, JOIN, lines;
|
|
24173
|
+
var import_undici, require2, BACKEND_URL, NETWORK_MSG, PROXY_ENV, agent, INSTANCE_ID, WORKSPACE_ID, envSession, lineage, ignoreOverride, defaultOptions, getDefaultOptions, getRefs, getRelativePath, parseCatchDef, integerDateParser, isJsonSchema7AllOfType, emojiRegex2, zodPatterns, ALPHA_NUMERIC, primitiveMappings, asAnyOf, parseOptionalDef, parsePipelineDef, parseReadonlyDef, selectParser, get$ref, addMeta, zodToJsonSchema, OPTIONS_MIN, OPTIONS_MAX, OptionSchema, OptionInputSchema, NIGHT, AskInputSchema, StartContactSchema, ContactSchema, CONTACT_SCHEMA, CONTACT_DESCRIPTION, CreateGoalSchema, CreateGoalToolSchema, CREATE_GOAL_DESCRIPTION, UpdateGoalSchema, UpdateGoalToolSchema, ClaimGoalSchema, GetGoalSchema, GET_GOAL_DESCRIPTION, UPDATE_GOAL_DESCRIPTION, CLAIM_GOAL_DESCRIPTION, CHECK_REPLIES_DESCRIPTION, CheckRepliesSchema, AGENT_TOOLS, AGENT_TOOL_NAMES, LIVE_MS, ContextSchema, ParticipantSchema, TransformSchema, VisualSchema, NotifyLevelSchema, SelectShapeSchema, ReceiptEventSchema, AttentionSchema, NotifyRequestFields, NotifyRequestSchema, NotifyStatusSchema, AgentStateSchema, TurnSchema, UserAnswerSchema, IntentSchema, RideAlongSchema, AwaitItemSchema, VoiceKeySchema, AgendaTurnSchema, CLAIM_STALE_MS, InboxItemSchema, APNS_TOKEN_RE, PushTokenSchema, MissedCallSchema, clock, QUIET, BrokerTuningSchema, UserSettingsSchema, HistoryItemSchema, ACTIVITY_LINES, ACTIVITY_LINE_MAX, AgentActivitySchema, ConnectionSummarySchema, LedgerItemSchema, AgentLedgerSchema, ReassignResultSchema, MoveRingSchema, MoveSchema, QueueQuestionSchema, QueueReplySchema, QueueItemSchema, COLD_AFTER_MS, NoteSourceSchema, NoteStatusSchema, NoteRepeatSchema, DecisionSchema, NoteSchema, TriageItemSchema, TriageAssignmentSchema, TriageStatusSchema, SubmitTriageSchema, TriageProposalSchema, AcceptTriageSchema, AcceptTriageResultSchema, DeliveryModeSchema, WAKE_EVENT, wakeChannel, RegisterDeliverySchema, OAuthStartSchema, DeliveryConfigSchema, WakeNudgeSchema, PairingStatusSchema, DeviceCodeSchema, DeviceInfoSchema, DeviceTokenSchema, SupportRequestSchema, NotificationFeedbackKindSchema, TOKEN_PATH, KEY_PATH, sleep, UnpairedError, ApiError, tokenOverride, authToken, AWAIT_WINDOW_MS, asked, NOT_SENT, HEARTBEAT_MS, RETRY_MS, JOIN, JOIN_TIMEOUT_MS, lines;
|
|
24099
24174
|
var init_dist = __esm({
|
|
24100
24175
|
"../../packages/sdk/dist/index.js"() {
|
|
24101
24176
|
"use strict";
|
|
@@ -24508,7 +24583,7 @@ var init_dist = __esm({
|
|
|
24508
24583
|
parentId: external_exports.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."),
|
|
24509
24584
|
repo: external_exports.string().optional().describe("Optional repository context."),
|
|
24510
24585
|
ask: external_exports.string().trim().min(1).max(1e4).describe(
|
|
24511
|
-
"
|
|
24586
|
+
"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 parts of one piece of work under one Goal, separate things as separate Goals \u2014 but the words it splits are its reading, not yours. News, progress and findings are their own contact; a call contact JOINS a call already happening, so several arrive as one call."
|
|
24512
24587
|
),
|
|
24513
24588
|
options: external_exports.array(OptionInputSchema).min(2).max(6).optional()
|
|
24514
24589
|
}).strict();
|
|
@@ -24519,7 +24594,7 @@ var init_dist = __esm({
|
|
|
24519
24594
|
}).strict();
|
|
24520
24595
|
ContactSchema = external_exports.union([StartContactSchema, external_exports.object({ deliveryId: external_exports.string().uuid() }).strict()]);
|
|
24521
24596
|
CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
|
|
24522
|
-
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}.
|
|
24597
|
+
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).";
|
|
24523
24598
|
CreateGoalSchema = external_exports.object({
|
|
24524
24599
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
24525
24600
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -24566,9 +24641,9 @@ var init_dist = __esm({
|
|
|
24566
24641
|
ClaimGoalSchema = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
|
|
24567
24642
|
GetGoalSchema = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
|
|
24568
24643
|
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.";
|
|
24569
|
-
UPDATE_GOAL_DESCRIPTION =
|
|
24570
|
-
CLAIM_GOAL_DESCRIPTION = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
|
|
24571
|
-
CHECK_REPLIES_DESCRIPTION = "
|
|
24644
|
+
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.`;
|
|
24645
|
+
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.";
|
|
24646
|
+
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.";
|
|
24572
24647
|
CheckRepliesSchema = external_exports.object({}).strict();
|
|
24573
24648
|
AGENT_TOOLS = [
|
|
24574
24649
|
{ name: "contact", description: CONTACT_DESCRIPTION, inputSchema: CONTACT_SCHEMA },
|
|
@@ -24898,9 +24973,9 @@ var init_dist = __esm({
|
|
|
24898
24973
|
/** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
|
|
24899
24974
|
* the planner declares duplicates instead of asking them twice. */
|
|
24900
24975
|
coveredIds: external_exports.array(external_exports.string()).optional(),
|
|
24901
|
-
/**
|
|
24902
|
-
*
|
|
24903
|
-
info: external_exports.array(external_exports.string().min(1)).
|
|
24976
|
+
/** The spoken sentences of the turn, in order. No count: how long a turn is is the brain's call
|
|
24977
|
+
* (owner, 2026-09-25), and a count here refused whole plans. */
|
|
24978
|
+
info: external_exports.array(external_exports.string().min(1)).default([]),
|
|
24904
24979
|
question: external_exports.string().min(1).nullable(),
|
|
24905
24980
|
/** True on the one turn carrying the agent's own declared question. */
|
|
24906
24981
|
asks: external_exports.boolean().optional(),
|
|
@@ -24935,32 +25010,6 @@ var init_dist = __esm({
|
|
|
24935
25010
|
CLAIM_STALE_MS = 30 * 6e4;
|
|
24936
25011
|
InboxItemSchema = external_exports.object({
|
|
24937
25012
|
id: external_exports.string(),
|
|
24938
|
-
/** The conversation thread + connection this item lives on. Present on the replied
|
|
24939
|
-
* detail — they power History's "Continue" / "New session from this" (#57/#251). */
|
|
24940
|
-
parentId: external_exports.string().optional(),
|
|
24941
|
-
/** THE ARRIVAL this row is one unit of (`notifications.ask_id` → `asks`). A claim is one
|
|
24942
|
-
* arrival and its units are N rows of it, so this — not `parentId` — is what makes a
|
|
24943
|
-
* multi-part notification one thing on screen. The thread is the whole CONVERSATION: it
|
|
24944
|
-
* accumulates every message an agent ever sent, so grouping by it renders a day of
|
|
24945
|
-
* unrelated updates as a single "12-part request". Absent on rows written before the
|
|
24946
|
-
* `asks` table, and on anything that never went through `notify` — both fall back to the
|
|
24947
|
-
* thread, which is what the client did for all rows until now. */
|
|
24948
|
-
askId: external_exports.string().optional(),
|
|
24949
|
-
/** WHERE this unit sat in the message it was cut from (`notifications.seq`). The batch
|
|
24950
|
-
* shares one `created_at` to the microsecond, so without it the author's order is
|
|
24951
|
-
* unrecoverable client-side — a four-paragraph briefing rendered opening-paragraph-last
|
|
24952
|
-
* (live 2026-08-10, D35). The API already orders by it; this lets a reader that
|
|
24953
|
-
* re-sorts (grouping, filtering) put an arrival back in the order it was written. */
|
|
24954
|
-
seq: external_exports.number().int().optional(),
|
|
24955
|
-
/** HOW MANY units the arrival was cut into. A device reads a LENS, never the arrival —
|
|
24956
|
-
* `/api/inbox` serves `open`, so the units already settled are gone from it — and a client
|
|
24957
|
-
* counting what it can see is counting what is LEFT. Walking a three-unit ask on the answer
|
|
24958
|
-
* screen read "1 of 3", then "1 of 2", then no chip at all, each answer having removed the
|
|
24959
|
-
* only evidence of itself. How big an arrival is, is a fact about the arrival, so the
|
|
24960
|
-
* server that can still see every row states it. Absent on any row with no `askId`: a
|
|
24961
|
-
* unit knows WHICH ask it came from and WHERE it sat in it, and how many there were is
|
|
24962
|
-
* the one part of its own arrival a single row cannot answer. */
|
|
24963
|
-
units: external_exports.number().int().positive().optional(),
|
|
24964
25013
|
tokenId: external_exports.string().optional(),
|
|
24965
25014
|
status: NotifyStatusSchema,
|
|
24966
25015
|
context: ContextSchema,
|
|
@@ -25005,7 +25054,12 @@ var init_dist = __esm({
|
|
|
25005
25054
|
entryId: external_exports.string(),
|
|
25006
25055
|
title: external_exports.string(),
|
|
25007
25056
|
state: external_exports.enum(["open", "answered"]),
|
|
25008
|
-
answer: external_exports.string().nullable()
|
|
25057
|
+
answer: external_exports.string().nullable(),
|
|
25058
|
+
/** WHO ASKED IT (owner, 2026-09-23, Goal a345e906: each agenda row wears its agent's face) — the
|
|
25059
|
+
* request Entry's author, as the same three facts the item's own `tokenId`/`name`/`voice`
|
|
25060
|
+
* carry for the call's one agent, so the phone draws it with the same seed. Absent when the
|
|
25061
|
+
* author is not an agent this account holds (unpaired since, or a person). */
|
|
25062
|
+
agent: external_exports.object({ tokenId: external_exports.string(), name: external_exports.string(), voice: VoiceKeySchema.optional() }).optional()
|
|
25009
25063
|
})).optional(),
|
|
25010
25064
|
visuals: external_exports.array(VisualSchema).optional(),
|
|
25011
25065
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
@@ -25023,10 +25077,20 @@ var init_dist = __esm({
|
|
|
25023
25077
|
* agent (held in the inbox until the agent replies on the thread). */
|
|
25024
25078
|
turn: external_exports.enum(["you", "agent"]).default("you"),
|
|
25025
25079
|
/** Hard error reason on an awaiting request (turn="agent") — the wake failed to reach
|
|
25026
|
-
* the agent (provider-agnostic; set server-side). Absent = no hard error
|
|
25027
|
-
*
|
|
25080
|
+
* the agent (provider-agnostic; set server-side). Absent = no hard error. Drives the inbox
|
|
25081
|
+
* error badge + Retry. */
|
|
25028
25082
|
error: external_exports.string().optional(),
|
|
25083
|
+
/** WHEN THIS AGENT WORK WENT QUIET (turn="agent"), by the one rule (`coldSince`: three days
|
|
25084
|
+
* with nothing said), or absent while it is not stalled. The inbox's stalled badge reads
|
|
25085
|
+
* this and nothing else (2026-09-23: a 3-minute age rule badged every live Goal stalled,
|
|
25086
|
+
* and "dismiss the stalled ones" cancelled 37 pieces of live work). */
|
|
25087
|
+
cold: external_exports.string().datetime().optional(),
|
|
25029
25088
|
clarifies: external_exports.string().optional(),
|
|
25089
|
+
/** THIS CARD'S QUESTION IS ON A LIVE CALL (owner, 2026-09-24: "Mark it while the call is
|
|
25090
|
+
* live"). Present only while an open Call Delivery carries the card's request Entry — read
|
|
25091
|
+
* off the same open list the card came from, so it clears when the Call does. A card is the
|
|
25092
|
+
* backup for a call not taken; while the call has it, the call is where it is answered. */
|
|
25093
|
+
onCall: external_exports.literal(true).optional(),
|
|
25030
25094
|
/** THE RING, ON THE ITEM (walk/design.md §12 §17, #2251): the last ring on this card was
|
|
25031
25095
|
* declined, and what the ladder will do next — read off the cron's own row, never computed
|
|
25032
25096
|
* on the phone. Present only while a `declined` receipt stands on the card's last Call.
|
|
@@ -25075,7 +25139,10 @@ var init_dist = __esm({
|
|
|
25075
25139
|
goalIds: external_exports.array(external_exports.string()),
|
|
25076
25140
|
decisionNeedId: external_exports.string().optional(),
|
|
25077
25141
|
sealed: external_exports.boolean(),
|
|
25078
|
-
goalState: external_exports.string().optional()
|
|
25142
|
+
goalState: external_exports.string().optional(),
|
|
25143
|
+
/** THAT GOAL'S NAME (#2416) — what Activity's row is headed by, since a row there is one Goal
|
|
25144
|
+
* and the cards it holds sit behind it. Stamped by the same read as `goalState`. */
|
|
25145
|
+
goalTitle: external_exports.string().optional()
|
|
25079
25146
|
}).optional()
|
|
25080
25147
|
});
|
|
25081
25148
|
APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
|
|
@@ -25174,7 +25241,6 @@ var init_dist = __esm({
|
|
|
25174
25241
|
});
|
|
25175
25242
|
HistoryItemSchema = external_exports.object({
|
|
25176
25243
|
id: external_exports.string(),
|
|
25177
|
-
parentId: external_exports.string(),
|
|
25178
25244
|
/** 'user' = a request you sent; 'agent' = a notification an agent sent you. */
|
|
25179
25245
|
initiator: external_exports.enum(["user", "agent"]),
|
|
25180
25246
|
title: external_exports.string(),
|
|
@@ -25333,6 +25399,24 @@ var init_dist = __esm({
|
|
|
25333
25399
|
* gate at all. */
|
|
25334
25400
|
blocked: external_exports.boolean().default(false)
|
|
25335
25401
|
});
|
|
25402
|
+
QueueReplySchema = external_exports.object({
|
|
25403
|
+
/** The card this note was (`deliveryId:requestEntryId`, minted by the server like every card
|
|
25404
|
+
* id) — so the phone can tell a reply it just sent from one the queue already carries, and the
|
|
25405
|
+
* walk can name it in its zoom. */
|
|
25406
|
+
id: external_exports.string(),
|
|
25407
|
+
/** The Goal the note is on. */
|
|
25408
|
+
goalId: external_exports.string(),
|
|
25409
|
+
/** What the note said. */
|
|
25410
|
+
note: external_exports.string(),
|
|
25411
|
+
/** Where it was carried — where a second reply goes (`POST /api/entries`, #2252). */
|
|
25412
|
+
deliveryId: external_exports.string(),
|
|
25413
|
+
requestEntryId: external_exports.string(),
|
|
25414
|
+
askedAt: external_exports.string(),
|
|
25415
|
+
/** When the person last replied — the window's start. */
|
|
25416
|
+
repliedAt: external_exports.string(),
|
|
25417
|
+
/** The person's latest words about it; null when there is nothing readable in them. */
|
|
25418
|
+
reply: external_exports.string().nullable()
|
|
25419
|
+
});
|
|
25336
25420
|
QueueItemSchema = external_exports.object({
|
|
25337
25421
|
id: external_exports.string(),
|
|
25338
25422
|
/** One-line headline — the first sentence of the outcome. */
|
|
@@ -25360,6 +25444,10 @@ var init_dist = __esm({
|
|
|
25360
25444
|
blocked: external_exports.boolean().default(false),
|
|
25361
25445
|
/** Every decision need on it, open or settled — the page decides which to show. */
|
|
25362
25446
|
questions: external_exports.array(QueueQuestionSchema).default([]),
|
|
25447
|
+
/** Every note on it the person replied to (`QueueReplySchema`) — the page decides which to show.
|
|
25448
|
+
* Optional, not defaulted: absent is none, and every hand-built queue (fixtures, the demo) need
|
|
25449
|
+
* not spell an empty list. */
|
|
25450
|
+
replies: external_exports.array(QueueReplySchema).optional(),
|
|
25363
25451
|
/** The repository or project identifier this Goal belongs to (#2280), null if untracked. */
|
|
25364
25452
|
repo: external_exports.string().nullable().optional(),
|
|
25365
25453
|
createdAt: external_exports.string(),
|
|
@@ -25572,9 +25660,11 @@ var init_dist = __esm({
|
|
|
25572
25660
|
tokenOverride = null;
|
|
25573
25661
|
authToken = (explicit) => explicit ?? tokenOverride ?? readToken();
|
|
25574
25662
|
AWAIT_WINDOW_MS = 45e3;
|
|
25663
|
+
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.";
|
|
25575
25664
|
HEARTBEAT_MS = 25e3;
|
|
25576
25665
|
RETRY_MS = [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
25577
25666
|
JOIN = { config: { broadcast: { self: false } } };
|
|
25667
|
+
JOIN_TIMEOUT_MS = 1e4;
|
|
25578
25668
|
lines = /* @__PURE__ */ new Map();
|
|
25579
25669
|
}
|
|
25580
25670
|
});
|
|
@@ -30823,7 +30913,7 @@ import { hostname } from "os";
|
|
|
30823
30913
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
30824
30914
|
import { execFile as execFile2, execSync } from "child_process";
|
|
30825
30915
|
import { homedir as homedir8 } from "os";
|
|
30826
|
-
import { join as
|
|
30916
|
+
import { join as join8 } from "path";
|
|
30827
30917
|
|
|
30828
30918
|
// src/run.ts
|
|
30829
30919
|
init_dist();
|
|
@@ -32486,7 +32576,7 @@ var AskInputSchema2 = external_exports.object({
|
|
|
32486
32576
|
parentId: external_exports.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."),
|
|
32487
32577
|
repo: external_exports.string().optional().describe("Optional repository context."),
|
|
32488
32578
|
ask: external_exports.string().trim().min(1).max(1e4).describe(
|
|
32489
|
-
"
|
|
32579
|
+
"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 parts of one piece of work under one Goal, separate things as separate Goals \u2014 but the words it splits are its reading, not yours. News, progress and findings are their own contact; a call contact JOINS a call already happening, so several arrive as one call."
|
|
32490
32580
|
),
|
|
32491
32581
|
options: external_exports.array(OptionInputSchema2).min(2).max(6).optional()
|
|
32492
32582
|
}).strict();
|
|
@@ -32497,7 +32587,7 @@ var StartContactSchema2 = external_exports.object({
|
|
|
32497
32587
|
}).strict();
|
|
32498
32588
|
var ContactSchema2 = external_exports.union([StartContactSchema2, external_exports.object({ deliveryId: external_exports.string().uuid() }).strict()]);
|
|
32499
32589
|
var CONTACT_SCHEMA2 = { type: "object", ...mcpInputSchema2(ContactSchema2) };
|
|
32500
|
-
var CONTACT_DESCRIPTION2 = "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}.
|
|
32590
|
+
var CONTACT_DESCRIPTION2 = "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).";
|
|
32501
32591
|
var CreateGoalSchema2 = external_exports.object({
|
|
32502
32592
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
32503
32593
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -32544,9 +32634,9 @@ var UpdateGoalToolSchema2 = UpdateGoalSchema2.omit({ operationId: true }).extend
|
|
|
32544
32634
|
var ClaimGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
|
|
32545
32635
|
var GetGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid() }).strict();
|
|
32546
32636
|
var GET_GOAL_DESCRIPTION2 = "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.";
|
|
32547
|
-
var UPDATE_GOAL_DESCRIPTION2 =
|
|
32548
|
-
var CLAIM_GOAL_DESCRIPTION2 = "Claim the oldest runnable or review-pending Goal you own, or pass goalId to claim that Goal. Returns the Goal as get_goal reads it, and creates or renews the execution lease.";
|
|
32549
|
-
var CHECK_REPLIES_DESCRIPTION2 = "
|
|
32637
|
+
var UPDATE_GOAL_DESCRIPTION2 = `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.`;
|
|
32638
|
+
var CLAIM_GOAL_DESCRIPTION2 = "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.";
|
|
32639
|
+
var CHECK_REPLIES_DESCRIPTION2 = "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.";
|
|
32550
32640
|
var CheckRepliesSchema2 = external_exports.object({}).strict();
|
|
32551
32641
|
var AGENT_TOOLS2 = [
|
|
32552
32642
|
{ name: "contact", description: CONTACT_DESCRIPTION2, inputSchema: CONTACT_SCHEMA2 },
|
|
@@ -32891,9 +32981,9 @@ var AgendaTurnSchema2 = external_exports.object({
|
|
|
32891
32981
|
/** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
|
|
32892
32982
|
* the planner declares duplicates instead of asking them twice. */
|
|
32893
32983
|
coveredIds: external_exports.array(external_exports.string()).optional(),
|
|
32894
|
-
/**
|
|
32895
|
-
*
|
|
32896
|
-
info: external_exports.array(external_exports.string().min(1)).
|
|
32984
|
+
/** The spoken sentences of the turn, in order. No count: how long a turn is is the brain's call
|
|
32985
|
+
* (owner, 2026-09-25), and a count here refused whole plans. */
|
|
32986
|
+
info: external_exports.array(external_exports.string().min(1)).default([]),
|
|
32897
32987
|
question: external_exports.string().min(1).nullable(),
|
|
32898
32988
|
/** True on the one turn carrying the agent's own declared question. */
|
|
32899
32989
|
asks: external_exports.boolean().optional(),
|
|
@@ -32928,32 +33018,6 @@ var AgendaTurnSchema2 = external_exports.object({
|
|
|
32928
33018
|
var CLAIM_STALE_MS2 = 30 * 6e4;
|
|
32929
33019
|
var InboxItemSchema2 = external_exports.object({
|
|
32930
33020
|
id: external_exports.string(),
|
|
32931
|
-
/** The conversation thread + connection this item lives on. Present on the replied
|
|
32932
|
-
* detail — they power History's "Continue" / "New session from this" (#57/#251). */
|
|
32933
|
-
parentId: external_exports.string().optional(),
|
|
32934
|
-
/** THE ARRIVAL this row is one unit of (`notifications.ask_id` → `asks`). A claim is one
|
|
32935
|
-
* arrival and its units are N rows of it, so this — not `parentId` — is what makes a
|
|
32936
|
-
* multi-part notification one thing on screen. The thread is the whole CONVERSATION: it
|
|
32937
|
-
* accumulates every message an agent ever sent, so grouping by it renders a day of
|
|
32938
|
-
* unrelated updates as a single "12-part request". Absent on rows written before the
|
|
32939
|
-
* `asks` table, and on anything that never went through `notify` — both fall back to the
|
|
32940
|
-
* thread, which is what the client did for all rows until now. */
|
|
32941
|
-
askId: external_exports.string().optional(),
|
|
32942
|
-
/** WHERE this unit sat in the message it was cut from (`notifications.seq`). The batch
|
|
32943
|
-
* shares one `created_at` to the microsecond, so without it the author's order is
|
|
32944
|
-
* unrecoverable client-side — a four-paragraph briefing rendered opening-paragraph-last
|
|
32945
|
-
* (live 2026-08-10, D35). The API already orders by it; this lets a reader that
|
|
32946
|
-
* re-sorts (grouping, filtering) put an arrival back in the order it was written. */
|
|
32947
|
-
seq: external_exports.number().int().optional(),
|
|
32948
|
-
/** HOW MANY units the arrival was cut into. A device reads a LENS, never the arrival —
|
|
32949
|
-
* `/api/inbox` serves `open`, so the units already settled are gone from it — and a client
|
|
32950
|
-
* counting what it can see is counting what is LEFT. Walking a three-unit ask on the answer
|
|
32951
|
-
* screen read "1 of 3", then "1 of 2", then no chip at all, each answer having removed the
|
|
32952
|
-
* only evidence of itself. How big an arrival is, is a fact about the arrival, so the
|
|
32953
|
-
* server that can still see every row states it. Absent on any row with no `askId`: a
|
|
32954
|
-
* unit knows WHICH ask it came from and WHERE it sat in it, and how many there were is
|
|
32955
|
-
* the one part of its own arrival a single row cannot answer. */
|
|
32956
|
-
units: external_exports.number().int().positive().optional(),
|
|
32957
33021
|
tokenId: external_exports.string().optional(),
|
|
32958
33022
|
status: NotifyStatusSchema2,
|
|
32959
33023
|
context: ContextSchema2,
|
|
@@ -32998,7 +33062,12 @@ var InboxItemSchema2 = external_exports.object({
|
|
|
32998
33062
|
entryId: external_exports.string(),
|
|
32999
33063
|
title: external_exports.string(),
|
|
33000
33064
|
state: external_exports.enum(["open", "answered"]),
|
|
33001
|
-
answer: external_exports.string().nullable()
|
|
33065
|
+
answer: external_exports.string().nullable(),
|
|
33066
|
+
/** WHO ASKED IT (owner, 2026-09-23, Goal a345e906: each agenda row wears its agent's face) — the
|
|
33067
|
+
* request Entry's author, as the same three facts the item's own `tokenId`/`name`/`voice`
|
|
33068
|
+
* carry for the call's one agent, so the phone draws it with the same seed. Absent when the
|
|
33069
|
+
* author is not an agent this account holds (unpaired since, or a person). */
|
|
33070
|
+
agent: external_exports.object({ tokenId: external_exports.string(), name: external_exports.string(), voice: VoiceKeySchema2.optional() }).optional()
|
|
33002
33071
|
})).optional(),
|
|
33003
33072
|
visuals: external_exports.array(VisualSchema2).optional(),
|
|
33004
33073
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
@@ -33016,10 +33085,20 @@ var InboxItemSchema2 = external_exports.object({
|
|
|
33016
33085
|
* agent (held in the inbox until the agent replies on the thread). */
|
|
33017
33086
|
turn: external_exports.enum(["you", "agent"]).default("you"),
|
|
33018
33087
|
/** Hard error reason on an awaiting request (turn="agent") — the wake failed to reach
|
|
33019
|
-
* the agent (provider-agnostic; set server-side). Absent = no hard error
|
|
33020
|
-
*
|
|
33088
|
+
* the agent (provider-agnostic; set server-side). Absent = no hard error. Drives the inbox
|
|
33089
|
+
* error badge + Retry. */
|
|
33021
33090
|
error: external_exports.string().optional(),
|
|
33091
|
+
/** WHEN THIS AGENT WORK WENT QUIET (turn="agent"), by the one rule (`coldSince`: three days
|
|
33092
|
+
* with nothing said), or absent while it is not stalled. The inbox's stalled badge reads
|
|
33093
|
+
* this and nothing else (2026-09-23: a 3-minute age rule badged every live Goal stalled,
|
|
33094
|
+
* and "dismiss the stalled ones" cancelled 37 pieces of live work). */
|
|
33095
|
+
cold: external_exports.string().datetime().optional(),
|
|
33022
33096
|
clarifies: external_exports.string().optional(),
|
|
33097
|
+
/** THIS CARD'S QUESTION IS ON A LIVE CALL (owner, 2026-09-24: "Mark it while the call is
|
|
33098
|
+
* live"). Present only while an open Call Delivery carries the card's request Entry — read
|
|
33099
|
+
* off the same open list the card came from, so it clears when the Call does. A card is the
|
|
33100
|
+
* backup for a call not taken; while the call has it, the call is where it is answered. */
|
|
33101
|
+
onCall: external_exports.literal(true).optional(),
|
|
33023
33102
|
/** THE RING, ON THE ITEM (walk/design.md §12 §17, #2251): the last ring on this card was
|
|
33024
33103
|
* declined, and what the ladder will do next — read off the cron's own row, never computed
|
|
33025
33104
|
* on the phone. Present only while a `declined` receipt stands on the card's last Call.
|
|
@@ -33068,7 +33147,10 @@ var InboxItemSchema2 = external_exports.object({
|
|
|
33068
33147
|
goalIds: external_exports.array(external_exports.string()),
|
|
33069
33148
|
decisionNeedId: external_exports.string().optional(),
|
|
33070
33149
|
sealed: external_exports.boolean(),
|
|
33071
|
-
goalState: external_exports.string().optional()
|
|
33150
|
+
goalState: external_exports.string().optional(),
|
|
33151
|
+
/** THAT GOAL'S NAME (#2416) — what Activity's row is headed by, since a row there is one Goal
|
|
33152
|
+
* and the cards it holds sit behind it. Stamped by the same read as `goalState`. */
|
|
33153
|
+
goalTitle: external_exports.string().optional()
|
|
33072
33154
|
}).optional()
|
|
33073
33155
|
});
|
|
33074
33156
|
var APNS_TOKEN_RE2 = /^[0-9a-fA-F]{64}$/;
|
|
@@ -33167,7 +33249,6 @@ var UserSettingsSchema2 = external_exports.object({
|
|
|
33167
33249
|
});
|
|
33168
33250
|
var HistoryItemSchema2 = external_exports.object({
|
|
33169
33251
|
id: external_exports.string(),
|
|
33170
|
-
parentId: external_exports.string(),
|
|
33171
33252
|
/** 'user' = a request you sent; 'agent' = a notification an agent sent you. */
|
|
33172
33253
|
initiator: external_exports.enum(["user", "agent"]),
|
|
33173
33254
|
title: external_exports.string(),
|
|
@@ -33326,6 +33407,24 @@ var QueueQuestionSchema2 = external_exports.object({
|
|
|
33326
33407
|
* gate at all. */
|
|
33327
33408
|
blocked: external_exports.boolean().default(false)
|
|
33328
33409
|
});
|
|
33410
|
+
var QueueReplySchema2 = external_exports.object({
|
|
33411
|
+
/** The card this note was (`deliveryId:requestEntryId`, minted by the server like every card
|
|
33412
|
+
* id) — so the phone can tell a reply it just sent from one the queue already carries, and the
|
|
33413
|
+
* walk can name it in its zoom. */
|
|
33414
|
+
id: external_exports.string(),
|
|
33415
|
+
/** The Goal the note is on. */
|
|
33416
|
+
goalId: external_exports.string(),
|
|
33417
|
+
/** What the note said. */
|
|
33418
|
+
note: external_exports.string(),
|
|
33419
|
+
/** Where it was carried — where a second reply goes (`POST /api/entries`, #2252). */
|
|
33420
|
+
deliveryId: external_exports.string(),
|
|
33421
|
+
requestEntryId: external_exports.string(),
|
|
33422
|
+
askedAt: external_exports.string(),
|
|
33423
|
+
/** When the person last replied — the window's start. */
|
|
33424
|
+
repliedAt: external_exports.string(),
|
|
33425
|
+
/** The person's latest words about it; null when there is nothing readable in them. */
|
|
33426
|
+
reply: external_exports.string().nullable()
|
|
33427
|
+
});
|
|
33329
33428
|
var QueueItemSchema2 = external_exports.object({
|
|
33330
33429
|
id: external_exports.string(),
|
|
33331
33430
|
/** One-line headline — the first sentence of the outcome. */
|
|
@@ -33353,6 +33452,10 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
33353
33452
|
blocked: external_exports.boolean().default(false),
|
|
33354
33453
|
/** Every decision need on it, open or settled — the page decides which to show. */
|
|
33355
33454
|
questions: external_exports.array(QueueQuestionSchema2).default([]),
|
|
33455
|
+
/** Every note on it the person replied to (`QueueReplySchema`) — the page decides which to show.
|
|
33456
|
+
* Optional, not defaulted: absent is none, and every hand-built queue (fixtures, the demo) need
|
|
33457
|
+
* not spell an empty list. */
|
|
33458
|
+
replies: external_exports.array(QueueReplySchema2).optional(),
|
|
33356
33459
|
/** The repository or project identifier this Goal belongs to (#2280), null if untracked. */
|
|
33357
33460
|
repo: external_exports.string().nullable().optional(),
|
|
33358
33461
|
createdAt: external_exports.string(),
|
|
@@ -33721,6 +33824,7 @@ async function ask(state, rail, input) {
|
|
|
33721
33824
|
waiting: "hard",
|
|
33722
33825
|
channel: input.call ? "call" : "notification"
|
|
33723
33826
|
});
|
|
33827
|
+
if ("recorded" in sent) return null;
|
|
33724
33828
|
const entryId = sent.requestEntryIds[0];
|
|
33725
33829
|
if (!entryId) return null;
|
|
33726
33830
|
(state.seen ??= /* @__PURE__ */ new Set()).add(entryId);
|
|
@@ -33899,14 +34003,14 @@ init_dist();
|
|
|
33899
34003
|
init_catalog();
|
|
33900
34004
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
33901
34005
|
import { homedir as homedir6 } from "os";
|
|
33902
|
-
import { join as
|
|
34006
|
+
import { join as join7 } from "path";
|
|
33903
34007
|
|
|
33904
34008
|
// src/workspaces.ts
|
|
33905
34009
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
33906
|
-
import { dirname as dirname2, join as
|
|
34010
|
+
import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
|
|
33907
34011
|
import { homedir as homedir4 } from "os";
|
|
33908
34012
|
function workspacesFile() {
|
|
33909
|
-
return
|
|
34013
|
+
return join4(homedir4(), ".paigy", "workspaces.json");
|
|
33910
34014
|
}
|
|
33911
34015
|
function listWorkspaces(deps) {
|
|
33912
34016
|
try {
|
|
@@ -33941,7 +34045,7 @@ function resolveWakeDir(pinned, deps) {
|
|
|
33941
34045
|
// src/owner.ts
|
|
33942
34046
|
import { closeSync as closeSync2, openSync as openSync2, readSync, readdirSync } from "fs";
|
|
33943
34047
|
import { homedir as homedir5 } from "os";
|
|
33944
|
-
import { join as
|
|
34048
|
+
import { join as join5 } from "path";
|
|
33945
34049
|
var HEAD_BYTES = 64e3;
|
|
33946
34050
|
function readHead(file) {
|
|
33947
34051
|
const fd = openSync2(file, "r");
|
|
@@ -33957,7 +34061,7 @@ function ownerSession(slot, deps = {}) {
|
|
|
33957
34061
|
const m = /^session:([0-9a-f]{8})$/.exec(slot);
|
|
33958
34062
|
if (!m) return null;
|
|
33959
34063
|
const prefix = m[1];
|
|
33960
|
-
const root = deps.projectsDir ??
|
|
34064
|
+
const root = deps.projectsDir ?? join5(homedir5(), ".claude", "projects");
|
|
33961
34065
|
const list = deps.list ?? ((dir) => readdirSync(dir));
|
|
33962
34066
|
const head = deps.head ?? readHead;
|
|
33963
34067
|
let folders;
|
|
@@ -33969,7 +34073,7 @@ function ownerSession(slot, deps = {}) {
|
|
|
33969
34073
|
for (const folder of folders) {
|
|
33970
34074
|
let files;
|
|
33971
34075
|
try {
|
|
33972
|
-
files = list(
|
|
34076
|
+
files = list(join5(root, folder));
|
|
33973
34077
|
} catch {
|
|
33974
34078
|
continue;
|
|
33975
34079
|
}
|
|
@@ -33977,7 +34081,7 @@ function ownerSession(slot, deps = {}) {
|
|
|
33977
34081
|
if (!file) continue;
|
|
33978
34082
|
let text;
|
|
33979
34083
|
try {
|
|
33980
|
-
text = head(
|
|
34084
|
+
text = head(join5(root, folder, file));
|
|
33981
34085
|
} catch {
|
|
33982
34086
|
continue;
|
|
33983
34087
|
}
|
|
@@ -33997,7 +34101,7 @@ function ownerSession(slot, deps = {}) {
|
|
|
33997
34101
|
// src/update.ts
|
|
33998
34102
|
import { execFile } from "child_process";
|
|
33999
34103
|
import { createRequire } from "module";
|
|
34000
|
-
import { dirname as dirname3, join as
|
|
34104
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
34001
34105
|
import { promisify } from "util";
|
|
34002
34106
|
var SERVICE_LABEL = "ai.paigy.harness";
|
|
34003
34107
|
var VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
@@ -34024,7 +34128,7 @@ async function latestVersion() {
|
|
|
34024
34128
|
}
|
|
34025
34129
|
async function install(version) {
|
|
34026
34130
|
const bin = dirname3(process.execPath);
|
|
34027
|
-
await promisify(execFile)(
|
|
34131
|
+
await promisify(execFile)(join6(bin, "npm"), ["install", "-g", `@paigy/harness@${version}`], {
|
|
34028
34132
|
env: { ...process.env, PATH: `${bin}:${process.env.PATH ?? ""}`, npm_config_update_notifier: "false" },
|
|
34029
34133
|
timeout: 5 * 6e4
|
|
34030
34134
|
});
|
|
@@ -34075,7 +34179,7 @@ function selfUpdater(deps) {
|
|
|
34075
34179
|
}
|
|
34076
34180
|
|
|
34077
34181
|
// src/host.ts
|
|
34078
|
-
var HOST_FILE =
|
|
34182
|
+
var HOST_FILE = join7(homedir6(), ".paigy", "host.json");
|
|
34079
34183
|
var runKey = (slot) => `slot:${slot}`;
|
|
34080
34184
|
var claimedRun = (sessionId2) => {
|
|
34081
34185
|
const slot = sessionSlot(sessionId2);
|
|
@@ -34441,7 +34545,7 @@ async function pairThisMachine() {
|
|
|
34441
34545
|
}
|
|
34442
34546
|
}
|
|
34443
34547
|
function forwardCodexThreadId() {
|
|
34444
|
-
const cfg =
|
|
34548
|
+
const cfg = join8(process.env.CODEX_HOME || join8(homedir8(), ".codex"), "config.toml");
|
|
34445
34549
|
if (!existsSync5(cfg)) return;
|
|
34446
34550
|
const text = readFileSync5(cfg, "utf8");
|
|
34447
34551
|
const head = "[mcp_servers.paigy]\n";
|
|
@@ -34543,7 +34647,7 @@ async function main() {
|
|
|
34543
34647
|
const wsDeps = { file: workspacesFile() };
|
|
34544
34648
|
for (const dir of parsed.grant) addWorkspace(dir, wsDeps);
|
|
34545
34649
|
if (!listWorkspaces(wsDeps).length) {
|
|
34546
|
-
const guess =
|
|
34650
|
+
const guess = join8(homedir8(), "projects");
|
|
34547
34651
|
if (existsSync5(guess)) {
|
|
34548
34652
|
addWorkspace(guess, wsDeps);
|
|
34549
34653
|
console.log(`\u2713 workspace granted: ${guess} (change with --grant DIR)`);
|
|
@@ -34584,7 +34688,7 @@ async function main() {
|
|
|
34584
34688
|
}
|
|
34585
34689
|
}
|
|
34586
34690
|
try {
|
|
34587
|
-
const settings =
|
|
34691
|
+
const settings = join8(homedir8(), ".claude", "settings.json");
|
|
34588
34692
|
if (which("claude") && existsSync5(settings)) {
|
|
34589
34693
|
const cfg = JSON.parse(readFileSync5(settings, "utf8"));
|
|
34590
34694
|
if (!cfg["statusLine"]) {
|
|
@@ -34642,13 +34746,13 @@ async function main() {
|
|
|
34642
34746
|
<key>SoftResourceLimits</key><dict>
|
|
34643
34747
|
<key>NumberOfFiles</key><integer>65536</integer>
|
|
34644
34748
|
</dict>
|
|
34645
|
-
<key>StandardOutPath</key><string>${
|
|
34646
|
-
<key>StandardErrorPath</key><string>${
|
|
34749
|
+
<key>StandardOutPath</key><string>${join8(homedir8(), ".paigy", "host.log")}</string>
|
|
34750
|
+
<key>StandardErrorPath</key><string>${join8(homedir8(), ".paigy", "host.log")}</string>
|
|
34647
34751
|
</dict></plist>
|
|
34648
34752
|
`;
|
|
34649
|
-
const dir =
|
|
34753
|
+
const dir = join8(homedir8(), "Library", "LaunchAgents");
|
|
34650
34754
|
mkdirSync3(dir, { recursive: true });
|
|
34651
|
-
const path =
|
|
34755
|
+
const path = join8(dir, `${SERVICE_LABEL}.plist`);
|
|
34652
34756
|
writeFileSync4(path, plist);
|
|
34653
34757
|
execSync(`launchctl unload ${path} 2>/dev/null; launchctl load ${path}`, { shell: "/bin/sh" });
|
|
34654
34758
|
console.log(`\u2713 host installed as a login service (${path}) \u2014 logs at ~/.paigy/host.log`);
|