@paigy/harness 0.3.38 → 0.3.40
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 +348 -169
- package/dist/main.js +3613 -3447
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -77,7 +77,7 @@ function runProbe(argv) {
|
|
|
77
77
|
function detect(entry, deps = {}) {
|
|
78
78
|
const find = deps.which ?? which;
|
|
79
79
|
const probe = deps.probe ?? runProbe;
|
|
80
|
-
const base = { name: entry.name, label: entry.label
|
|
80
|
+
const base = { name: entry.name, label: entry.label };
|
|
81
81
|
const cliPath = find(entry.cli);
|
|
82
82
|
if (!cliPath) return { ...base, status: "missing", hint: `${entry.label} is not installed.`, install: entry.installCli };
|
|
83
83
|
const adapterPath = entry.adapter ? find(entry.adapter) : null;
|
|
@@ -109,8 +109,7 @@ var init_catalog = __esm({
|
|
|
109
109
|
authProbe: ["claude", "auth", "status"],
|
|
110
110
|
loginHint: "Run `claude` once in a terminal and sign in.",
|
|
111
111
|
installCli: "curl -fsSL https://claude.ai/install.sh | bash",
|
|
112
|
-
installAdapter: "npm install -g @agentclientprotocol/claude-agent-acp"
|
|
113
|
-
models: ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", "claude-3-opus-20240229"]
|
|
112
|
+
installAdapter: "npm install -g @agentclientprotocol/claude-agent-acp"
|
|
114
113
|
},
|
|
115
114
|
{
|
|
116
115
|
name: "codex",
|
|
@@ -120,8 +119,7 @@ var init_catalog = __esm({
|
|
|
120
119
|
authProbe: ["codex", "login", "status"],
|
|
121
120
|
loginHint: "Run `codex login` to authenticate.",
|
|
122
121
|
installCli: "curl -fsSL https://chatgpt.com/codex/install.sh | sh",
|
|
123
|
-
installAdapter: "npm install -g @agentclientprotocol/codex-acp"
|
|
124
|
-
models: ["gpt-4o", "gpt-4o-mini", "o1", "o1-mini"]
|
|
122
|
+
installAdapter: "npm install -g @agentclientprotocol/codex-acp"
|
|
125
123
|
},
|
|
126
124
|
{
|
|
127
125
|
name: "agy",
|
|
@@ -131,8 +129,7 @@ var init_catalog = __esm({
|
|
|
131
129
|
adapter: "paigy-agy-acp",
|
|
132
130
|
authProbe: ["agy", "help"],
|
|
133
131
|
loginHint: "Install and set up Antigravity (`agy`).",
|
|
134
|
-
installCli: "curl -fsSL https://antigravity.dev/install.sh | bash"
|
|
135
|
-
models: ["gemini-1.5-pro", "gemini-1.5-flash", "gemini-2.0-pro-exp"]
|
|
132
|
+
installCli: "curl -fsSL https://antigravity.dev/install.sh | bash"
|
|
136
133
|
}
|
|
137
134
|
];
|
|
138
135
|
}
|
|
@@ -301,8 +298,8 @@ var init_ZodError = __esm({
|
|
|
301
298
|
"not_finite"
|
|
302
299
|
]);
|
|
303
300
|
quotelessJson = (obj) => {
|
|
304
|
-
const
|
|
305
|
-
return
|
|
301
|
+
const json3 = JSON.stringify(obj, null, 2);
|
|
302
|
+
return json3.replace(/"([^"]+)":/g, "$1:");
|
|
306
303
|
};
|
|
307
304
|
ZodError = class _ZodError extends Error {
|
|
308
305
|
get errors() {
|
|
@@ -23699,35 +23696,6 @@ function parseDef(def, refs, forceResolution = false) {
|
|
|
23699
23696
|
newItem.jsonSchema = jsonSchema;
|
|
23700
23697
|
return jsonSchema;
|
|
23701
23698
|
}
|
|
23702
|
-
function draft2020(node) {
|
|
23703
|
-
if (Array.isArray(node)) return node.map(draft2020);
|
|
23704
|
-
if (node && typeof node === "object") {
|
|
23705
|
-
const o = node;
|
|
23706
|
-
if (Array.isArray(o.items)) {
|
|
23707
|
-
o.prefixItems = o.items;
|
|
23708
|
-
if ("additionalItems" in o) {
|
|
23709
|
-
o.items = o.additionalItems;
|
|
23710
|
-
delete o.additionalItems;
|
|
23711
|
-
} else delete o.items;
|
|
23712
|
-
}
|
|
23713
|
-
for (const [excl, lim] of [["exclusiveMinimum", "minimum"], ["exclusiveMaximum", "maximum"]]) {
|
|
23714
|
-
if (typeof o[excl] === "boolean") {
|
|
23715
|
-
if (o[excl] === true && typeof o[lim] === "number") {
|
|
23716
|
-
o[excl] = o[lim];
|
|
23717
|
-
delete o[lim];
|
|
23718
|
-
} else delete o[excl];
|
|
23719
|
-
}
|
|
23720
|
-
}
|
|
23721
|
-
for (const k of Object.keys(o)) o[k] = draft2020(o[k]);
|
|
23722
|
-
return o;
|
|
23723
|
-
}
|
|
23724
|
-
return node;
|
|
23725
|
-
}
|
|
23726
|
-
function mcpInputSchema(s) {
|
|
23727
|
-
const schema = zodToJsonSchema(s, { target: "jsonSchema2019-09", $refStrategy: "none" });
|
|
23728
|
-
delete schema.$schema;
|
|
23729
|
-
return draft2020(schema);
|
|
23730
|
-
}
|
|
23731
23699
|
function capRefusals(text, units22, field) {
|
|
23732
23700
|
const out = [];
|
|
23733
23701
|
const name = field === "text" ? "answer" : field;
|
|
@@ -23769,6 +23737,35 @@ function capRefusals(text, units22, field) {
|
|
|
23769
23737
|
function refuseOverCaps(text, units22, field, ctx) {
|
|
23770
23738
|
for (const { path, message, ...params } of capRefusals(text, units22, field)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message, params });
|
|
23771
23739
|
}
|
|
23740
|
+
function draft2020(node) {
|
|
23741
|
+
if (Array.isArray(node)) return node.map(draft2020);
|
|
23742
|
+
if (node && typeof node === "object") {
|
|
23743
|
+
const o = node;
|
|
23744
|
+
if (Array.isArray(o.items)) {
|
|
23745
|
+
o.prefixItems = o.items;
|
|
23746
|
+
if ("additionalItems" in o) {
|
|
23747
|
+
o.items = o.additionalItems;
|
|
23748
|
+
delete o.additionalItems;
|
|
23749
|
+
} else delete o.items;
|
|
23750
|
+
}
|
|
23751
|
+
for (const [excl, lim] of [["exclusiveMinimum", "minimum"], ["exclusiveMaximum", "maximum"]]) {
|
|
23752
|
+
if (typeof o[excl] === "boolean") {
|
|
23753
|
+
if (o[excl] === true && typeof o[lim] === "number") {
|
|
23754
|
+
o[excl] = o[lim];
|
|
23755
|
+
delete o[lim];
|
|
23756
|
+
} else delete o[excl];
|
|
23757
|
+
}
|
|
23758
|
+
}
|
|
23759
|
+
for (const k of Object.keys(o)) o[k] = draft2020(o[k]);
|
|
23760
|
+
return o;
|
|
23761
|
+
}
|
|
23762
|
+
return node;
|
|
23763
|
+
}
|
|
23764
|
+
function mcpInputSchema(s) {
|
|
23765
|
+
const schema = zodToJsonSchema(s, { target: "jsonSchema2019-09", $refStrategy: "none" });
|
|
23766
|
+
delete schema.$schema;
|
|
23767
|
+
return draft2020(schema);
|
|
23768
|
+
}
|
|
23772
23769
|
function entryUnit(entry) {
|
|
23773
23770
|
if (entry.kind !== "contribution" || !entry.aboutId) return null;
|
|
23774
23771
|
const plain = entry.content && "plain" in entry.content ? entry.content.plain : null;
|
|
@@ -23901,15 +23898,15 @@ async function manageGoals(input, opts = {}) {
|
|
|
23901
23898
|
if (!res.ok) await fail("manage_goals", res);
|
|
23902
23899
|
return await res.json();
|
|
23903
23900
|
}
|
|
23904
|
-
async function
|
|
23901
|
+
async function getGoals(goalIds, opts = {}, read3 = {}) {
|
|
23905
23902
|
const token = authToken(opts.token) ?? "";
|
|
23906
|
-
const
|
|
23907
|
-
|
|
23908
|
-
|
|
23909
|
-
|
|
23903
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals/read`, {
|
|
23904
|
+
method: "POST",
|
|
23905
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
23906
|
+
body: JSON.stringify({ goalIds, ...read3 })
|
|
23910
23907
|
}));
|
|
23911
|
-
if (!res.ok) await fail("
|
|
23912
|
-
return await res.json();
|
|
23908
|
+
if (!res.ok) await fail("get_goals", res);
|
|
23909
|
+
return (await res.json()).goals;
|
|
23913
23910
|
}
|
|
23914
23911
|
async function hatch(name, voice = null, opts = {}) {
|
|
23915
23912
|
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/hatch`, {
|
|
@@ -23927,14 +23924,18 @@ async function hatch(name, voice = null, opts = {}) {
|
|
|
23927
23924
|
}
|
|
23928
23925
|
async function whoAmI(opts = {}) {
|
|
23929
23926
|
try {
|
|
23930
|
-
|
|
23931
|
-
headers: { authorization: `Bearer ${authToken(opts.token)}` }
|
|
23932
|
-
});
|
|
23933
|
-
return res.ok ? await res.json() : null;
|
|
23927
|
+
return await readIdentity(opts);
|
|
23934
23928
|
} catch {
|
|
23935
23929
|
return null;
|
|
23936
23930
|
}
|
|
23937
23931
|
}
|
|
23932
|
+
async function readIdentity(opts = {}) {
|
|
23933
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/identity`, {
|
|
23934
|
+
headers: { authorization: `Bearer ${authToken(opts.token)}` }
|
|
23935
|
+
}));
|
|
23936
|
+
if (!res.ok) await fail("read_identity", res);
|
|
23937
|
+
return await res.json();
|
|
23938
|
+
}
|
|
23938
23939
|
async function claimSessions(opts = {}) {
|
|
23939
23940
|
const res = await ensureAuthed(await reach(`${BACKEND_URL}/api/sessions/claim`, {
|
|
23940
23941
|
headers: { authorization: `Bearer ${authToken(opts.token)}` }
|
|
@@ -24044,6 +24045,7 @@ async function contact(input, opts = {}) {
|
|
|
24044
24045
|
body: JSON.stringify({
|
|
24045
24046
|
operationId: opts.operationId ?? randomUUID2(),
|
|
24046
24047
|
...parsed.updates ? { updates: parsed.updates } : {},
|
|
24048
|
+
...parsed.completions ? { completions: parsed.completions } : {},
|
|
24047
24049
|
...questions ? { questions } : {},
|
|
24048
24050
|
...parsed.answers ? { answers: parsed.answers } : {},
|
|
24049
24051
|
...parsed.withdrawQuestionIds ? { withdrawQuestionIds: parsed.withdrawQuestionIds } : {},
|
|
@@ -24294,13 +24296,15 @@ ${block2}`;
|
|
|
24294
24296
|
}
|
|
24295
24297
|
return text.slice(0, start) + updated + text.slice(end);
|
|
24296
24298
|
}
|
|
24297
|
-
var import_undici, require2, CODEX_ENV, BACKEND_URL, NETWORK_MSG, PROXY_ENV, agent, INSTANCE_ID, WORKSPACE_ID, envSession, lineage, CLIENT, 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, OPTION_LABEL_MAX, OPTION_HINT_MAX, OptionSchema, OptionInputSchema, NIGHT, ASK_MAX, UNITS_MAX, UNIT_TITLE_MAX, UNIT_BODY_MAX, UnitInputSchema, questionHandle, NodeRef, BlockedAction, reason, actionFits, ACTION_FITS, userExplicitlyRequested, units, UpdateInputSchema, questionFields, refineQuestion, QuestionInputSchema, QuestionWireSchema, AnswerInputSchema, QuestionDependencySchema, sendFields, ContactSchema, sendsAnything, ContactWireSchema, CONTACT_SCHEMA, CONTACT_DESCRIPTION, CreateGoalSchema, UpdateGoalSchema, changedGoal, goalTitle, GOAL_OUTCOME_MAX, goalOutcome, CreateChange, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange, read, GoalChangeSchema, editNames, ManageGoalsSchema, ManageGoalsToolSchema, MANAGE_GOALS_DESCRIPTION,
|
|
24299
|
+
var import_undici, require2, CODEX_ENV, BACKEND_URL, NETWORK_MSG, PROXY_ENV, agent, INSTANCE_ID, WORKSPACE_ID, envSession, lineage, CLIENT, 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, OPTION_LABEL_MAX, OPTION_HINT_MAX, OptionSchema, OptionInputSchema, NIGHT, ASK_MAX, UNITS_MAX, UNIT_TITLE_MAX, UNIT_BODY_MAX, UnitInputSchema, COMPLETION_MESSAGE_MAX, CompletionSchema, CompleteChangeSchema, ASKING_RULES, questionHandle, NodeRef, BlockedAction, reason, actionFits, ACTION_FITS, userExplicitlyRequested, units, UpdateInputSchema, questionFields, refineQuestion, QuestionInputSchema, QuestionWireSchema, AnswerInputSchema, QuestionDependencySchema, sendFields, ContactSchema, sendsAnything, ContactWireSchema, CONTACT_SCHEMA, CONTACT_DESCRIPTION, CreateGoalSchema, UpdateGoalSchema, changedGoal, goalTitle, GOAL_OUTCOME_MAX, goalOutcome, CreateChange, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange, read, GoalChangeSchema, editNames, ManageGoalsSchema, ManageGoalsToolSchema, MANAGE_GOALS_DESCRIPTION, GetGoalsSchema, GetDeliverySchema, GET_DELIVERY_DESCRIPTION, GET_GOALS_DESCRIPTION, SearchToolSchema, SEARCH_DESCRIPTION, ConfigureIdentitySchema, CONFIGURE_IDENTITY_DESCRIPTION, CheckActivitySchema, FEEDBACK_TEXT_MAX, SendFeedbackSchema, SEND_FEEDBACK_DESCRIPTION, AGENT_TOOLS, AGENT_TOOL_NAMES, id, key, participant, SourceSchema, ResultRefSchema, NodeRefSchema, DependencySchema, goalState, existingGoalChanges, BrainGoalChangeSchema, option, QuestionChangeSchema, lessonScope, LessonChangeSchema, BrainNextSchema, messageBase, BrainMessageSchema, BrainSearchSchema, BrainResultSchema, str, strs, CompactResultSchema, COMPACT_RESULT_JSON_SCHEMA, str2, strs2, TalkerReplySchema, TALKER_REPLY_JSON_SCHEMA, str3, strs3, FilerReplySchema, FILER_REPLY_JSON_SCHEMA, ExplainerReplySchema, EXPLAINER_REPLY_JSON_SCHEMA, DISTILLER_DEFECTS, DistillerReplySchema, DISTILLER_REPLY_JSON_SCHEMA, TIDY_AUDIENCES, TidyReplySchema, TIDY_REPLY_JSON_SCHEMA, LIVE_MS, WORKING_MS, WORKING_TIER_MS, RECENT_TIER_MS, json, clock, HeardEventSchema, HeardCursorSchema, HeardPageSchema, MAX_EVENTS, MAX_BYTES, batchBytes, HeardBatchSchema, 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, clock2, QUIET, BrokerTuningSchema, UserSettingsSchema, HistoryWorkSchema, HistoryEntrySchema, HistoryPageSchema, ACTIVITY_LINES, ACTIVITY_LINE_MAX, AgentActivitySchema, ConnectionSummarySchema, LedgerItemSchema, AgentLedgerSchema, ReassignResultSchema, LessonStateSchema, LessonViewSchema, QueueQuestionSchema, QueueReplySchema, QueueItemSchema, QueueDeltaSchema, COLD_AFTER_MS, NoteSourceSchema, NoteStatusSchema, NoteRepeatSchema, DecisionSchema, NoteSchema, TriageItemSchema, TriageAssignmentSchema, TriageStatusSchema, SubmitTriageSchema, TriageProposalSchema, AcceptTriageSchema, AcceptTriageResultSchema, DeliveryModeSchema, WAKE_EVENT, wakeChannel, RegisterDeliverySchema, OAuthStartSchema, DeliveryConfigSchema, HostDecisionSchema, WakeNudgeSchema, PairingStatusSchema, DeviceCodeSchema, DeviceInfoSchema, DeviceTokenSchema, SupportRequestSchema, NotificationFeedbackKindSchema, SlimOptionSchema, QuestionRowSchema, WorkStateSchema, WorkRowSchema, ComputerRowSchema, AgentRowSchema, SnapshotSchema, CallRecapSchema, TOKEN_PATH, KEY_PATH, sleep, UnpairedError, RevokedError, ApiError, tokenOverride, authToken, AWAIT_WINDOW_MS, NOT_SENT, APPLIED, DEMOTED, HEARTBEAT_MS, RETRY_MS, JOIN, JOIN_TIMEOUT_MS, lines;
|
|
24298
24300
|
var init_dist = __esm({
|
|
24299
24301
|
"../../packages/sdk/dist/index.js"() {
|
|
24300
24302
|
"use strict";
|
|
24301
24303
|
init_zod();
|
|
24302
24304
|
init_zod();
|
|
24303
24305
|
init_zod();
|
|
24306
|
+
init_zod();
|
|
24307
|
+
init_zod();
|
|
24304
24308
|
init_v3();
|
|
24305
24309
|
init_v3();
|
|
24306
24310
|
init_v3();
|
|
@@ -24310,6 +24314,7 @@ var init_dist = __esm({
|
|
|
24310
24314
|
init_zod();
|
|
24311
24315
|
init_zod();
|
|
24312
24316
|
init_zod();
|
|
24317
|
+
init_zod();
|
|
24313
24318
|
import_undici = __toESM(require_undici(), 1);
|
|
24314
24319
|
require2 = __sdkCreateRequire(import.meta.url);
|
|
24315
24320
|
CODEX_ENV = ["CODEX_THREAD_ID", "CODEX_SESSION_ID", "CODEX_HOME", "PAIGY_TOKEN", "PAIGY_SESSION_ID", "PAIGY_HARNESS", "PAIGY_INSTANCE_ID", "PAIGY_ON_WAKE"];
|
|
@@ -24726,6 +24731,31 @@ var init_dist = __esm({
|
|
|
24726
24731
|
title: external_exports.string().trim().min(1).describe(`What this unit is about, at most ${UNIT_TITLE_MAX} characters. The app lists the titles; the person taps one to read its body.`),
|
|
24727
24732
|
body: external_exports.string().trim().min(1).describe(`The unit itself, at most ${UNIT_BODY_MAX} characters.`)
|
|
24728
24733
|
}).strict();
|
|
24734
|
+
COMPLETION_MESSAGE_MAX = 50;
|
|
24735
|
+
CompletionSchema = external_exports.object({
|
|
24736
|
+
goalId: external_exports.string().uuid().describe("The existing Goal to complete."),
|
|
24737
|
+
message: external_exports.string().trim().min(1).max(COMPLETION_MESSAGE_MAX, "completion_message_too_long: at most 50 characters; put detail in units.").describe("Completion headline, at most 50 characters."),
|
|
24738
|
+
units: external_exports.array(UnitInputSchema.extend({
|
|
24739
|
+
title: external_exports.string().trim().min(1).max(UNIT_TITLE_MAX),
|
|
24740
|
+
body: external_exports.string().trim().min(1).max(UNIT_BODY_MAX)
|
|
24741
|
+
})).max(UNITS_MAX).optional().describe("Suggest initial state, changes, current state with verification and limits, and next steps / leftover work with its owner when useful. One idea per unit; omit unnecessary sections."),
|
|
24742
|
+
reply: external_exports.string().trim().min(1).max(OPTION_LABEL_MAX).describe("One acknowledgement in the person's voice, at most 50 characters. Acknowledgement alone requests no follow-up and does not claim they tested the work."),
|
|
24743
|
+
options: external_exports.array(external_exports.object({
|
|
24744
|
+
label: external_exports.string().trim().min(1).max(OPTION_LABEL_MAX),
|
|
24745
|
+
hint: external_exports.string().trim().min(1).max(OPTION_HINT_MAX)
|
|
24746
|
+
}).strict()).max(5).optional().describe("Optional next steps the person may initiate: label (50 characters) and consequence in hint (200). Selecting one acknowledges the report and requests that step from its author; it never reopens this Goal.")
|
|
24747
|
+
}).strict();
|
|
24748
|
+
CompleteChangeSchema = CompletionSchema.extend({ kind: external_exports.literal("complete") });
|
|
24749
|
+
ASKING_RULES = `HOW TO ASK:
|
|
24750
|
+
1. Write one question per object. Five questions are five objects in \`questions\`, in one contact, so each can be answered on its own; one question with five parts settles nothing until all five are answered.
|
|
24751
|
+
2. Name the Goal you are working on as the question's \`goalId\`: the question goes onto that Goal and its answer comes back there. It must exist: create it with manage_goals first.
|
|
24752
|
+
3. \`workItBlocks\` only for work you are truly blocked on (it asks for a call); leave it out for a question you can keep working around.
|
|
24753
|
+
4. Never hold the process open with while-loops. A wait that comes back \`expired\` is not a finished conversation: wait again with contact({wait:true}) only while you have nothing else to do. While a Call you placed is open, keep receiving window after window until it ends, even when a window is empty: the person may give follow-ups or instructions. With no open Call, STOP WAITING when a window comes back with nothing new \u2014 the question stays open, and its answer reaches you on the Goal. Otherwise, yield only with a working listener or scheduled wakeup, and collect answers with \`contact({wait:false})\` or \`get_goals\` on that wake.
|
|
24754
|
+
5. One question, one row. Never restate a question that is still waiting inside a new contact: keep waiting on the original, or the answer lands on one copy and the other stays open. Take back one that no longer matters with \`withdrawQuestionIds\`.
|
|
24755
|
+
6. Read the Goal's conversation before asking. Never ask again what was answered or already shipped \u2014 and tell its states apart: \`answered\` carries the choice in \`answer\`, while \`replied\` means they said something and chose none of your options, so that question is still yours to settle and their words are the line beside it.
|
|
24756
|
+
7. A question carries its options when you have them; a free-text question may omit them. Each option\u2019s label must stand on its own and name its choice in full -- never \`All three\` or \`Option 2\`. \`pickMode: "many"\` makes the card a checklist (options they may want several of); \`"one"\`, the default, a pick (one rules out the others); \`"rank"\` an ordering. Each question is sent exactly as written, with no reading in between. They can always answer in their own words, so never add an 'Other' option.
|
|
24757
|
+
8. A diagnosis says when, why and how it happens, then proposes one fix. Never options first.
|
|
24758
|
+
9. GIVE A REPORT ITS REPLY: an update takes \`reply\` too, one line the person can tap instead of typing, written in THEIR voice for THAT report ("Sounds good, I'll test later") \u2014 the same care you give a question's options. What they tap is what you read back, which tells you whether they verified your work or only received it. One line, because a report has one way to close, and it may not change what happens next: a reply that would is a question. Requested delivery and completed-Goal reports require a reply (reply_required); ordinary progress may omit it.`;
|
|
24729
24759
|
questionHandle = external_exports.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/, "a question id, whole or its first eight characters");
|
|
24730
24760
|
NodeRef = external_exports.object({
|
|
24731
24761
|
type: external_exports.enum(["goal", "question"]),
|
|
@@ -24766,10 +24796,18 @@ var init_dist = __esm({
|
|
|
24766
24796
|
// MECHANISM). It rides the door as the report's single stored option (`storedOption`,
|
|
24767
24797
|
// `apps/api/src/goal/router.ts`), so no reader below the door learns a second shape.
|
|
24768
24798
|
reply: external_exports.string().trim().min(1).describe(
|
|
24769
|
-
`What this report's button says instead of "Got it", at most ${OPTION_LABEL_MAX} characters, written in THEIR voice for THIS report ("Sounds good, I'll test later") -- the care you give a question's options, so what they tap tells you whether they verified your work or only received it. One tap sends exactly these words back to you. Words that would CHANGE what happens next are a question, not a reply to a report: it still asks nothing and still owes you no answer.
|
|
24799
|
+
`What this report's button says instead of "Got it", at most ${OPTION_LABEL_MAX} characters, written in THEIR voice for THIS report ("Sounds good, I'll test later") -- the care you give a question's options, so what they tap tells you whether they verified your work or only received it. One tap sends exactly these words back to you. Words that would CHANGE what happens next are a question, not a reply to a report: it still asks nothing and still owes you no answer. The reply is required for requested delivery and completed Goals; ordinary progress may omit it.`
|
|
24770
24800
|
).optional(),
|
|
24771
24801
|
userExplicitlyRequested
|
|
24772
24802
|
}).strict().superRefine((u, ctx) => {
|
|
24803
|
+
if (u.userExplicitlyRequested && !u.reply) {
|
|
24804
|
+
ctx.addIssue({
|
|
24805
|
+
code: external_exports.ZodIssueCode.custom,
|
|
24806
|
+
path: ["reply"],
|
|
24807
|
+
params: { refusal: "reply_required" },
|
|
24808
|
+
message: "reply_required: an update requested for delivery needs one reply, written in the person\u2019s voice for this report."
|
|
24809
|
+
});
|
|
24810
|
+
}
|
|
24773
24811
|
refuseOverCaps(u.message, u.units, "message", ctx);
|
|
24774
24812
|
if (u.reply && u.reply.length > OPTION_LABEL_MAX) {
|
|
24775
24813
|
ctx.addIssue({
|
|
@@ -24828,6 +24866,7 @@ var init_dist = __esm({
|
|
|
24828
24866
|
reason: reason.optional()
|
|
24829
24867
|
}).strict().refine(actionFits, ACTION_FITS);
|
|
24830
24868
|
sendFields = {
|
|
24869
|
+
completions: external_exports.array(CompletionSchema).min(1).max(20).optional().describe("Close existing Goals and deliver their completion reports atomically. Ordinary updates never close work."),
|
|
24831
24870
|
updates: external_exports.array(UpdateInputSchema).min(1).max(20).optional().describe(
|
|
24832
24871
|
"News: progress, findings, a result. An update asks nothing. It reaches the person only when they asked for it (userExplicitlyRequested), when it answers something they said, or once its Goal is done; otherwise it is recorded as the Goal's progress and nobody is notified."
|
|
24833
24872
|
),
|
|
@@ -24850,24 +24889,22 @@ var init_dist = __esm({
|
|
|
24850
24889
|
"Only controls when this returns. Sending: false (the default) returns at once; true waits up to about 45 seconds for a response to what you sent. Sending nothing: true (the default) waits up to about 45 seconds for anything addressed to you; false returns what is waiting now. It does not mean work is blocked, set urgency, or ask for a call. `waitOutcome` says which happened: `available`, `expired` (nothing arrived; wait again without sending again), or `not_waited`."
|
|
24851
24890
|
)
|
|
24852
24891
|
}).strict();
|
|
24853
|
-
sendsAnything = (c) => !!(c.updates?.length || c.questions?.length || c.answers?.length || c.withdrawQuestionIds?.length || c.questionDependencies?.length);
|
|
24892
|
+
sendsAnything = (c) => !!(c.completions?.length || c.updates?.length || c.questions?.length || c.answers?.length || c.withdrawQuestionIds?.length || c.questionDependencies?.length);
|
|
24854
24893
|
ContactWireSchema = external_exports.object({
|
|
24855
24894
|
operationId: external_exports.string().uuid(),
|
|
24856
24895
|
...sendFields,
|
|
24857
24896
|
questions: external_exports.array(QuestionWireSchema).min(1).max(20).optional()
|
|
24858
24897
|
}).strict().refine(sendsAnything, "a contact sends at least one update, question, answer, withdrawal or question dependency");
|
|
24859
24898
|
CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
|
|
24860
|
-
CONTACT_DESCRIPTION =
|
|
24861
|
-
|
|
24862
|
-
QUESTIONS: one question per object; several questions are several objects in one contact, so each can be answered on its own. Each is sent exactly as written, with no reading in between: a bundled question arrives as one card. Give options when you have them. Each option's label must stand on its own \u2014 the person may see only the labels \u2014 so never a label that points into your text ('All three', 'Option 2', '1 and 3 only'). pickMode:'one' (the default) makes a pick, for alternatives; 'many' a checklist, for independent options they may want several of; 'rank' an ordering. The person can always answer in their own words, so never add an 'Other' option. workItBlocks names the work a question holds up, only when you are truly blocked: it asks for a call. Answers land on the question's Goal: collect them by receiving (below) or with get_goal.
|
|
24899
|
+
CONTACT_DESCRIPTION = "Send to and receive from the person: completions, updates, questions, answers to questions addressed to you, withdrawals of your own questions, and what a question holds up. Every Goal you name must already exist: create it with manage_goals first; an unknown Goal is refused and nothing is sent. Every item applies on its own: read `results`, where a failed item names why while the rest still apply.\n\n" + ASKING_RULES + `
|
|
24863
24900
|
|
|
24864
24901
|
CUT A LONG MESSAGE BEFORE YOU SEND IT: a question, an update's message or an answer is the one point, in at most ${ASK_MAX} characters; everything else it needs goes in 'units', up to ${UNITS_MAX} {title, body} in reading order, one idea each, every title at most ${UNIT_TITLE_MAX} characters and every body at most ${UNIT_BODY_MAX}. The person sees the main text and the titles, and opens a unit to read its body. Over a cap is refused by name (ask_too_long, too_many_units, unit_title_too_long, unit_body_too_long) and nothing is sent.
|
|
24865
24902
|
|
|
24866
|
-
|
|
24903
|
+
UPDATES ask nothing, so no answer is owed and none should be awaited. An update reaches the person only when they asked you for it (userExplicitlyRequested), when it answers something they said, or once its Goal is done; any other is recorded as the Goal's progress and nobody is notified. Ordinary updates never close a Goal. On a Goal whose report card is still open, an update that reaches them is added to that card, with no new push. If the person is already on a call, anything that reaches them joins that call, with no ring.
|
|
24867
24904
|
|
|
24868
|
-
|
|
24905
|
+
COMPLETIONS close and report in one operation: completions:[{goalId,message,units?,reply,options?}]. The same contract works in manage_goals changes with kind:complete. message is at most 50 characters; use units when useful for initial state, changes, current state (verification and limits), and next steps / leftover work with its owner. reply is one acknowledgement in the person's voice. Optional options:[{label,hint}] suggest next steps: a tap acknowledges the report and requests that step from its author. It never reopens the completed Goal; follow-up work needs a new Goal. Closure and the report commit together or neither does. Required children and dependencies must already be finished. No Reviewer rejudges your completion; say only what your evidence supports.
|
|
24869
24906
|
|
|
24870
|
-
RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of what is addressed to you (not a history page): \`question\`, a Question you owe (answer it in answers, with its questionId); \`update\`, something new on one of your Goals (a reply, an answer: read it with
|
|
24907
|
+
RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of what is addressed to you (not a history page): \`question\`, a Question you owe (answer it in answers, with its questionId); \`update\`, something new on one of your Goals (a reply, an answer: read it with get_goals); \`instruction\`, a request or note sent to you. \`hasMore\` says more are waiting. Reading acknowledges nothing: once you have handled events, confirm their eventIds with contact({ackEventIds}), and the next batch can come. contact({}) waits up to about 45 seconds for something to arrive; contact({wait:false}) returns at once. It also lists work given to you that nobody has started (\`assigned\`; your first write to it starts it) and your work gone quiet (\`stalled\`).`;
|
|
24871
24908
|
CreateGoalSchema = external_exports.object({
|
|
24872
24909
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
24873
24910
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -24940,6 +24977,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24940
24977
|
read = { revision: external_exports.number().int().positive().optional() };
|
|
24941
24978
|
GoalChangeSchema = external_exports.discriminatedUnion("kind", [
|
|
24942
24979
|
CreateChange,
|
|
24980
|
+
CompleteChangeSchema.extend(read),
|
|
24943
24981
|
EditChange.extend(read),
|
|
24944
24982
|
StateChange.extend(read),
|
|
24945
24983
|
AssignChange.extend(read),
|
|
@@ -24957,20 +24995,20 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24957
24995
|
changes: external_exports.array(GoalChangeSchema).min(1).max(50)
|
|
24958
24996
|
}).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
|
|
24959
24997
|
ManageGoalsToolSchema = external_exports.object({
|
|
24960
|
-
changes: external_exports.array(external_exports.discriminatedUnion("kind", [CreateChange, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange])).min(1).max(50).describe("The changes, applied in order, each on its own.")
|
|
24998
|
+
changes: external_exports.array(external_exports.discriminatedUnion("kind", [CreateChange, CompleteChangeSchema, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange])).min(1).max(50).describe("The changes, applied in order, each on its own.")
|
|
24961
24999
|
}).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
|
|
24962
|
-
MANAGE_GOALS_DESCRIPTION = "Create, edit, assign, organize or close work. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (
|
|
24963
|
-
|
|
24964
|
-
|
|
25000
|
+
MANAGE_GOALS_DESCRIPTION = "Create, edit, assign, organize or close work. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: complete (goalId, message capped at 50 characters, units, reply, optional next-step options {label, hint}) closes and reports atomically, exactly as contact.completions; create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (get_goals) and reconsider. You may change any Goal of your person; a change puts you on it. Returns `ok` (every change applied), `results` per change (applied or failed), and `goals`, each changed Goal's id, state, title, outcome and revision.";
|
|
25001
|
+
GetGoalsSchema = external_exports.object({
|
|
25002
|
+
goalIds: external_exports.array(external_exports.string().uuid()).min(1).max(10).describe("The Goals to read, in order; one to ten IDs."),
|
|
24965
25003
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
24966
25004
|
* newest entry, and counts what it left out (`apps/api/src/goal/collapse.ts`). */
|
|
24967
|
-
history: external_exports.boolean().optional()
|
|
24968
|
-
/** WHEN A READ HAS CONFUSED YOU. Adds `diagnosis`: every reader that already answers a question
|
|
24969
|
-
* about this work, each answer attributed to the reader that gave it, and every disagreement
|
|
24970
|
-
* between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
|
|
24971
|
-
diagnose: external_exports.boolean().optional()
|
|
25005
|
+
history: external_exports.boolean().optional()
|
|
24972
25006
|
}).strict();
|
|
24973
|
-
|
|
25007
|
+
GetDeliverySchema = external_exports.object({
|
|
25008
|
+
deliveryId: external_exports.string().uuid().describe("The Delivery id returned by contact or receive.")
|
|
25009
|
+
}).strict();
|
|
25010
|
+
GET_DELIVERY_DESCRIPTION = "Read a Delivery in full: its whole ask, units, options and answers, plus current callState and callOutcome for a Call. Any agent of the same account may read it; another account is not disclosed. Reading never acknowledges or resends anything. Use this after a call or when receive names a Call you are waiting on.";
|
|
25011
|
+
GET_GOALS_DESCRIPTION = "Read one to ten Goals by goalIds, returned in the same order under goals (a read never puts you on them). history applies to every requested Goal. A refused read fails the call with its original refusal; no partial result is presented as complete. Each Goal includes its ten most recent other contributors (names, latest entry headlines and times), 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. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. 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. Every Goal of your person is readable, whichever of their agents owns it; another account's Goals are not disclosed.";
|
|
24974
25012
|
SearchToolSchema = external_exports.object({
|
|
24975
25013
|
query: external_exports.string().trim().min(1).max(500),
|
|
24976
25014
|
types: external_exports.array(external_exports.enum(["entry", "goal", "answer"])).min(1).optional(),
|
|
@@ -24978,6 +25016,8 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24978
25016
|
limit: external_exports.number().int().min(1).max(20).optional()
|
|
24979
25017
|
}).strict();
|
|
24980
25018
|
SEARCH_DESCRIPTION = "Search your person's history across all of their agents: Entries (what anyone said or wrote, typed or spoken on a call), Goals (by title and outcome) and replies -- the person's answers, acknowledgements and deferrals (type answer; found by their summary, by the question or report they reply to, or by the words that gave them). query is words to look for; records sharing more of its words rank first, and exact names work. types narrows it to entry, goal and/or answer (default: all three). goalId searches under one Goal: its Entries, the replies to its items, and it and its immediate children. limit is matches per type, 1 to 20 (default 8). Read-only. Each match carries its whole saved words, its ID and its links (a reply carries its kind -- answered, acknowledged or deferred -- whether it is closed (a deferral is not), its summary in under ten words, its question or report, the choice made and the Entries that support it); `omitted` counts what matched but was left out, so narrow the words or add a goalId to see it. Nothing found is not proof that nothing exists; a refused search says why. Sealed (encrypted) content is never searched or returned.";
|
|
25019
|
+
ConfigureIdentitySchema = external_exports.object({}).strict();
|
|
25020
|
+
CONFIGURE_IDENTITY_DESCRIPTION = "Read this authenticated agent\u2019s current name, voice and token ID. Read-only; no arguments. Pairing, renaming and logout are available only through the local configure driver.";
|
|
24981
25021
|
CheckActivitySchema = external_exports.object({}).strict();
|
|
24982
25022
|
FEEDBACK_TEXT_MAX = 5e4;
|
|
24983
25023
|
SendFeedbackSchema = external_exports.object({
|
|
@@ -24987,11 +25027,13 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24987
25027
|
}).strict();
|
|
24988
25028
|
SEND_FEEDBACK_DESCRIPTION = "Send feedback about Paigy itself (a bug report, an idea) straight to the Paigy team. Send it only when the person asked you to, or agreed when you offered: it goes to Paigy, never to the person, and nobody answers it here. Not for your work or a question for the person: use contact for those. Returns whether it was stored.";
|
|
24989
25029
|
AGENT_TOOLS = [
|
|
25030
|
+
{ name: "configure", description: CONFIGURE_IDENTITY_DESCRIPTION, inputSchema: mcpInputSchema(ConfigureIdentitySchema) },
|
|
24990
25031
|
{ name: "contact", description: CONTACT_DESCRIPTION, inputSchema: CONTACT_SCHEMA },
|
|
24991
25032
|
{ name: "manage_goals", description: MANAGE_GOALS_DESCRIPTION, inputSchema: mcpInputSchema(ManageGoalsToolSchema) },
|
|
24992
|
-
{ name: "
|
|
25033
|
+
{ name: "get_delivery", description: GET_DELIVERY_DESCRIPTION, inputSchema: mcpInputSchema(GetDeliverySchema) },
|
|
25034
|
+
{ name: "get_goals", description: GET_GOALS_DESCRIPTION, inputSchema: mcpInputSchema(GetGoalsSchema) },
|
|
24993
25035
|
{ name: "search", description: SEARCH_DESCRIPTION, inputSchema: mcpInputSchema(SearchToolSchema) },
|
|
24994
|
-
{ name: "check_activity", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use
|
|
25036
|
+
{ name: "check_activity", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goals to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema(CheckActivitySchema) },
|
|
24995
25037
|
{ name: "send_feedback", description: SEND_FEEDBACK_DESCRIPTION, inputSchema: mcpInputSchema(SendFeedbackSchema) }
|
|
24996
25038
|
];
|
|
24997
25039
|
AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
|
|
@@ -25255,6 +25297,42 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25255
25297
|
WORKING_MS = 60 * 6e4;
|
|
25256
25298
|
WORKING_TIER_MS = 2 * 60 * 6e4;
|
|
25257
25299
|
RECENT_TIER_MS = 24 * 60 * 6e4;
|
|
25300
|
+
json = external_exports.lazy(() => external_exports.union([
|
|
25301
|
+
external_exports.string(),
|
|
25302
|
+
external_exports.number().finite(),
|
|
25303
|
+
external_exports.boolean(),
|
|
25304
|
+
external_exports.null(),
|
|
25305
|
+
external_exports.array(json),
|
|
25306
|
+
external_exports.record(json)
|
|
25307
|
+
]));
|
|
25308
|
+
clock = external_exports.number().int().nonnegative().safe();
|
|
25309
|
+
HeardEventSchema = external_exports.object({
|
|
25310
|
+
eventId: external_exports.string().uuid(),
|
|
25311
|
+
source: external_exports.enum(["phone", "bot"]),
|
|
25312
|
+
sequence: clock,
|
|
25313
|
+
kind: external_exports.string().min(1),
|
|
25314
|
+
phoneAtMs: clock.optional(),
|
|
25315
|
+
botAtMs: clock.optional(),
|
|
25316
|
+
payload: external_exports.record(json)
|
|
25317
|
+
}).strict();
|
|
25318
|
+
HeardCursorSchema = HeardEventSchema.pick({ source: true, sequence: true, eventId: true });
|
|
25319
|
+
HeardPageSchema = external_exports.object({
|
|
25320
|
+
events: external_exports.array(HeardEventSchema).max(200),
|
|
25321
|
+
next: HeardCursorSchema.nullable()
|
|
25322
|
+
}).strict();
|
|
25323
|
+
MAX_EVENTS = 50;
|
|
25324
|
+
MAX_BYTES = 8e4;
|
|
25325
|
+
batchBytes = (events) => {
|
|
25326
|
+
let bytes = 0;
|
|
25327
|
+
for (const char of JSON.stringify({ events })) {
|
|
25328
|
+
const point = char.codePointAt(0);
|
|
25329
|
+
bytes += point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
|
|
25330
|
+
}
|
|
25331
|
+
return bytes;
|
|
25332
|
+
};
|
|
25333
|
+
HeardBatchSchema = external_exports.object({
|
|
25334
|
+
events: external_exports.array(HeardEventSchema).min(1).max(MAX_EVENTS)
|
|
25335
|
+
}).strict().refine((value) => batchBytes(value.events) <= MAX_BYTES, "heard_batch_too_large");
|
|
25258
25336
|
ContextSchema = external_exports.object({
|
|
25259
25337
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
25260
25338
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -25811,8 +25889,8 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25811
25889
|
"inbox",
|
|
25812
25890
|
"dismiss"
|
|
25813
25891
|
]);
|
|
25814
|
-
|
|
25815
|
-
QUIET = ` Nothing rings from ${
|
|
25892
|
+
clock2 = (h) => h === 0 ? "midnight" : h === 12 ? "noon" : h < 12 ? `${h} am` : `${h - 12} pm`;
|
|
25893
|
+
QUIET = ` Nothing rings from ${clock2(NIGHT.from)} to ${clock2(NIGHT.to)} your time; the count waits for morning.`;
|
|
25816
25894
|
BrokerTuningSchema = external_exports.object({
|
|
25817
25895
|
/** 'none' = skip the spoken ack after a mapped answer (power users find it slow). */
|
|
25818
25896
|
ackVerbosity: external_exports.enum(["normal", "none"]).optional(),
|
|
@@ -26127,7 +26205,13 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
26127
26205
|
/** ITS SUMMARY IN AT MOST TEN WORDS (owner, 2026-10-08: "the same switch to the more summarized sources
|
|
26128
26206
|
* at the work tab top level screen too"): what the talker or filer wrote on the Entry; absent when
|
|
26129
26207
|
* none was. The Work row reads it before `said`. */
|
|
26130
|
-
summary: external_exports.string().optional()
|
|
26208
|
+
summary: external_exports.string().optional(),
|
|
26209
|
+
/** AN AGENT'S UPDATE OR QUESTION AS ITS UNITS' TITLES (owner, 2026-10-08: "send the 50 chars from the title
|
|
26210
|
+
* of each unit"): each at most 50 characters, in order; absent when it has none. */
|
|
26211
|
+
titles: external_exports.array(external_exports.string()).optional(),
|
|
26212
|
+
/** A PROGRESS NOTE'S ONE POINT ("An agent's progress note -> maybe let's cap it at 200 chars"): the
|
|
26213
|
+
* update's own line, before its units; the row caps it at 200. Absent on anything else. */
|
|
26214
|
+
line: external_exports.string().optional()
|
|
26131
26215
|
}).nullable().optional(),
|
|
26132
26216
|
/** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
|
|
26133
26217
|
* the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
|
|
@@ -26457,6 +26541,8 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
26457
26541
|
status: external_exports.string(),
|
|
26458
26542
|
startedAt: external_exports.string(),
|
|
26459
26543
|
durationMs: external_exports.number().nullable(),
|
|
26544
|
+
/** Bot-clock pickup offset; absent before the timestamp release, null before its report lands. */
|
|
26545
|
+
pickupMs: external_exports.number().nullable().optional(),
|
|
26460
26546
|
agents: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string().nullable() }))
|
|
26461
26547
|
}),
|
|
26462
26548
|
topics: external_exports.array(external_exports.object({
|
|
@@ -26501,7 +26587,9 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
26501
26587
|
words: external_exports.string(),
|
|
26502
26588
|
atMs: external_exports.number()
|
|
26503
26589
|
})),
|
|
26504
|
-
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
26590
|
+
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() }),
|
|
26591
|
+
/** Last stored position on this page when another exists; absent before paging shipped. */
|
|
26592
|
+
next: external_exports.number().int().nonnegative().nullable().optional()
|
|
26505
26593
|
});
|
|
26506
26594
|
TOKEN_PATH = join22(homedir22(), ".paigy", "token.json");
|
|
26507
26595
|
KEY_PATH = join22(homedir22(), ".paigy", "key.json");
|
|
@@ -26537,7 +26625,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
26537
26625
|
tokenOverride = null;
|
|
26538
26626
|
authToken = (explicit) => explicit ?? tokenOverride ?? readToken();
|
|
26539
26627
|
AWAIT_WINDOW_MS = 45e3;
|
|
26540
|
-
NOT_SENT = `Recorded as the Goal's progress, not sent: nobody was notified. The person is told only a question, an answer to one they asked you, and what you say once the Goal is done. To close the work,
|
|
26628
|
+
NOT_SENT = `Recorded as the Goal's progress, not sent: nobody was notified. The person is told only a question, an answer to one they asked you, and what you say once the Goal is done. To close the work, send contact.completions or manage_goals kind:complete with the shared completion report, closing and reporting together. If the person asked you, in so many words, to send them this, send it again with userExplicitlyRequested: "any" on that update.`;
|
|
26541
26629
|
APPLIED = "Applied; nothing was sent to the person.";
|
|
26542
26630
|
DEMOTED = "Delivered as a card, not a call: the person's settings do not allow a call right now (silent, quiet hours, or a mode). It has reached them; do not send it again.";
|
|
26543
26631
|
HEARTBEAT_MS = 25e3;
|
|
@@ -31430,11 +31518,11 @@ function extractJson(raw) {
|
|
|
31430
31518
|
return null;
|
|
31431
31519
|
}
|
|
31432
31520
|
function parseVerdicts(raw, knownIds, knownAgents) {
|
|
31433
|
-
const
|
|
31434
|
-
if (!
|
|
31521
|
+
const json3 = extractJson(raw);
|
|
31522
|
+
if (!json3) return { ok: false, error: "no JSON found in the model's answer", raw };
|
|
31435
31523
|
let parsed;
|
|
31436
31524
|
try {
|
|
31437
|
-
parsed = JSON.parse(
|
|
31525
|
+
parsed = JSON.parse(json3);
|
|
31438
31526
|
} catch (e) {
|
|
31439
31527
|
return { ok: false, error: `JSON.parse failed: ${e.message}`, raw };
|
|
31440
31528
|
}
|
|
@@ -31680,13 +31768,17 @@ var init_triage = __esm({
|
|
|
31680
31768
|
init_catalog();
|
|
31681
31769
|
|
|
31682
31770
|
// src/cli-args.ts
|
|
31771
|
+
init_zod();
|
|
31683
31772
|
var TRIAGE_VIA = ["ollama", "claude", "codex", "agy"];
|
|
31684
31773
|
var USAGE = "usage: paigy-harness setup | pair | host [--grant DIR]\u2026 | service | enable-tools [--scope user|project] | hatch NAME [--voice KEY] [--slot SLOT] | launch NAME [--under GOAL] [--voice KEY] [--harness \u2026] [--mode \u2026] [--cwd DIR] BRIEF\u2026 | handoff | triage [--via ollama|claude|codex|agy] [--json] [--apply [--yes-close] [--yes-stale] [--yes-assign]] | [--harness claude|codex|agy] [--mode bypass|ask] [--cwd DIR] [--grace SECONDS] [--doctor] PROMPT\u2026";
|
|
31685
31774
|
function parseArgs(argv) {
|
|
31686
31775
|
const args = { harness: "claude", mode: "bypass", cwd: process.cwd(), doctor: false, help: false, host: false, pair: false, service: false, setup: false, enableTools: false, scope: "user", grant: [], hatch: null, voice: null, slot: null, launch: null, under: null, graceSeconds: 90, prompt: "", handoff: false, triage: false, via: null, json: false, apply: false, yesClose: false, yesStale: false, yesAssign: false };
|
|
31687
31776
|
const words2 = [];
|
|
31777
|
+
let commandPosition = true;
|
|
31688
31778
|
for (let i = 0; i < argv.length; i++) {
|
|
31689
31779
|
const arg = argv[i];
|
|
31780
|
+
const command = commandPosition && !!arg && !arg.startsWith("-");
|
|
31781
|
+
if (command) commandPosition = false;
|
|
31690
31782
|
const next = () => argv[++i];
|
|
31691
31783
|
if (arg === "--harness") {
|
|
31692
31784
|
const v = next();
|
|
@@ -31702,7 +31794,7 @@ function parseArgs(argv) {
|
|
|
31702
31794
|
args.cwd = v;
|
|
31703
31795
|
} else if (arg === "--doctor") {
|
|
31704
31796
|
args.doctor = true;
|
|
31705
|
-
} else if (arg === "-h" || arg === "--help" || arg === "help") {
|
|
31797
|
+
} else if (arg === "-h" || arg === "--help" || command && arg === "help") {
|
|
31706
31798
|
args.help = true;
|
|
31707
31799
|
} else if (arg === "--scope") {
|
|
31708
31800
|
const v = next();
|
|
@@ -31712,21 +31804,21 @@ function parseArgs(argv) {
|
|
|
31712
31804
|
const v = Number(next());
|
|
31713
31805
|
if (!Number.isFinite(v) || v < 0) return { error: "--grace needs seconds (0 = straight to phone)" };
|
|
31714
31806
|
args.graceSeconds = v;
|
|
31715
|
-
} else if (arg === "host") {
|
|
31807
|
+
} else if (command && arg === "host") {
|
|
31716
31808
|
args.host = true;
|
|
31717
|
-
} else if (arg === "pair") {
|
|
31809
|
+
} else if (command && arg === "pair") {
|
|
31718
31810
|
args.pair = true;
|
|
31719
|
-
} else if (arg === "setup") {
|
|
31811
|
+
} else if (command && arg === "setup") {
|
|
31720
31812
|
args.setup = true;
|
|
31721
|
-
} else if (arg === "service") {
|
|
31813
|
+
} else if (command && arg === "service") {
|
|
31722
31814
|
args.service = true;
|
|
31723
31815
|
} else if (arg === "--grant") {
|
|
31724
31816
|
const v = next();
|
|
31725
31817
|
if (!v) return { error: "--grant needs a folder to allow" };
|
|
31726
31818
|
args.grant.push(v);
|
|
31727
|
-
} else if (arg === "handoff") {
|
|
31819
|
+
} else if (command && arg === "handoff") {
|
|
31728
31820
|
args.handoff = true;
|
|
31729
|
-
} else if (arg === "triage") {
|
|
31821
|
+
} else if (command && arg === "triage") {
|
|
31730
31822
|
args.triage = true;
|
|
31731
31823
|
} else if (arg === "--via") {
|
|
31732
31824
|
const v = next();
|
|
@@ -31743,9 +31835,9 @@ function parseArgs(argv) {
|
|
|
31743
31835
|
args.yesStale = true;
|
|
31744
31836
|
} else if (arg === "--yes-assign") {
|
|
31745
31837
|
args.yesAssign = true;
|
|
31746
|
-
} else if (arg === "enable-tools") {
|
|
31838
|
+
} else if (command && arg === "enable-tools") {
|
|
31747
31839
|
args.enableTools = true;
|
|
31748
|
-
} else if (arg === "hatch") {
|
|
31840
|
+
} else if (command && arg === "hatch") {
|
|
31749
31841
|
const v = next();
|
|
31750
31842
|
if (!v) return { error: 'hatch needs a name: paigy-harness hatch "Voice bug hunter"' };
|
|
31751
31843
|
args.hatch = v;
|
|
@@ -31755,12 +31847,14 @@ function parseArgs(argv) {
|
|
|
31755
31847
|
args.slot = next() ?? null;
|
|
31756
31848
|
} else if (arg === "--identity") {
|
|
31757
31849
|
return { error: '--identity is gone: start a helper with `paigy-harness launch NAME "brief"`, which gives it its own session' };
|
|
31758
|
-
} else if (arg === "launch") {
|
|
31850
|
+
} else if (command && arg === "launch") {
|
|
31759
31851
|
const v = next();
|
|
31760
31852
|
if (!v) return { error: 'launch needs a name: paigy-harness launch "Deploy check" "brief"' };
|
|
31761
31853
|
args.launch = v;
|
|
31762
31854
|
} else if (arg === "--under") {
|
|
31763
|
-
|
|
31855
|
+
const v = next();
|
|
31856
|
+
if (!v || !external_exports.string().uuid().safeParse(v).success) return { error: "--under needs a Goal UUID" };
|
|
31857
|
+
args.under = v;
|
|
31764
31858
|
} else if (arg?.startsWith("--")) {
|
|
31765
31859
|
return { error: `unknown flag ${arg}` };
|
|
31766
31860
|
} else if (arg) {
|
|
@@ -31769,6 +31863,7 @@ function parseArgs(argv) {
|
|
|
31769
31863
|
}
|
|
31770
31864
|
args.prompt = words2.join(" ");
|
|
31771
31865
|
if (args.help) return args;
|
|
31866
|
+
if (args.under && !args.launch) return { error: "--under requires launch NAME" };
|
|
31772
31867
|
if (args.launch && !args.prompt) return { error: 'launch needs a brief: paigy-harness launch "Deploy check" "brief"' };
|
|
31773
31868
|
if (!args.doctor && !args.handoff && !args.hatch && !args.host && !args.pair && !args.service && !args.setup && !args.enableTools && !args.triage && !args.prompt) return { error: "a prompt is required (or setup / --doctor / pair / host / service / enable-tools / hatch NAME / handoff / triage)" };
|
|
31774
31869
|
if ((args.yesClose || args.yesStale || args.yesAssign) && !args.apply)
|
|
@@ -32192,8 +32287,7 @@ function startSession(opts) {
|
|
|
32192
32287
|
if (!existsSync3(cwd)) {
|
|
32193
32288
|
queueMicrotask(() => opts.onEvent({ kind: "error", message: `workspace does not exist: ${cwd}` }));
|
|
32194
32289
|
}
|
|
32195
|
-
|
|
32196
|
-
if (opts.model && opts.harness === "codex") codexConfig = { ...codexConfig, model: opts.model };
|
|
32290
|
+
const codexConfig = opts.harness === "codex" ? JSON.parse(process.env.CODEX_CONFIG || "{}") : null;
|
|
32197
32291
|
if (codexConfig) {
|
|
32198
32292
|
const key3 = "mcp_servers.paigy.env_vars";
|
|
32199
32293
|
const configPath = join5(process.env.CODEX_HOME || join5(homedir4(), ".codex"), "config.toml");
|
|
@@ -32219,8 +32313,6 @@ function startSession(opts) {
|
|
|
32219
32313
|
PATH: [process.env.PATH, ...probeDirs()].filter(Boolean).join(delimiter2),
|
|
32220
32314
|
...opts.token ? { PAIGY_TOKEN: opts.token } : {},
|
|
32221
32315
|
...opts.instance ? { PAIGY_INSTANCE_ID: opts.instance } : {},
|
|
32222
|
-
...opts.model && opts.harness === "claude" ? { CLAUDE_MODEL_CONFIG: JSON.stringify({ provider: "anthropic", model: opts.model }) } : {},
|
|
32223
|
-
...opts.model && opts.harness === "agy" ? { AGY_MODEL_CONFIG: JSON.stringify({ model: opts.model }) } : {},
|
|
32224
32316
|
...codexConfig ? { CODEX_CONFIG: JSON.stringify(codexConfig) } : {},
|
|
32225
32317
|
...opts.session ?? opts.resume ? { PAIGY_SESSION_ID: opts.session ?? opts.resume } : {},
|
|
32226
32318
|
// A PUMP DRIVES THIS PROCESS (2026-09-22, #2265): every run `runHarness` starts has the
|
|
@@ -32300,6 +32392,8 @@ function startSession(opts) {
|
|
|
32300
32392
|
init_zod();
|
|
32301
32393
|
init_zod();
|
|
32302
32394
|
init_zod();
|
|
32395
|
+
init_zod();
|
|
32396
|
+
init_zod();
|
|
32303
32397
|
|
|
32304
32398
|
// ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Options.js
|
|
32305
32399
|
var ignoreOverride2 = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use");
|
|
@@ -33599,6 +33693,7 @@ init_zod();
|
|
|
33599
33693
|
init_zod();
|
|
33600
33694
|
init_zod();
|
|
33601
33695
|
init_zod();
|
|
33696
|
+
init_zod();
|
|
33602
33697
|
var OPTIONS_MIN2 = 2;
|
|
33603
33698
|
var OPTIONS_MAX2 = 6;
|
|
33604
33699
|
var OPTION_LABEL_MAX2 = 50;
|
|
@@ -33628,35 +33723,6 @@ var OptionInputSchema2 = external_exports.object({
|
|
|
33628
33723
|
imgUrl: OptionSchema2.shape.image
|
|
33629
33724
|
}).strict();
|
|
33630
33725
|
var NIGHT2 = { from: 23, to: 7 };
|
|
33631
|
-
function draft20202(node) {
|
|
33632
|
-
if (Array.isArray(node)) return node.map(draft20202);
|
|
33633
|
-
if (node && typeof node === "object") {
|
|
33634
|
-
const o = node;
|
|
33635
|
-
if (Array.isArray(o.items)) {
|
|
33636
|
-
o.prefixItems = o.items;
|
|
33637
|
-
if ("additionalItems" in o) {
|
|
33638
|
-
o.items = o.additionalItems;
|
|
33639
|
-
delete o.additionalItems;
|
|
33640
|
-
} else delete o.items;
|
|
33641
|
-
}
|
|
33642
|
-
for (const [excl, lim] of [["exclusiveMinimum", "minimum"], ["exclusiveMaximum", "maximum"]]) {
|
|
33643
|
-
if (typeof o[excl] === "boolean") {
|
|
33644
|
-
if (o[excl] === true && typeof o[lim] === "number") {
|
|
33645
|
-
o[excl] = o[lim];
|
|
33646
|
-
delete o[lim];
|
|
33647
|
-
} else delete o[excl];
|
|
33648
|
-
}
|
|
33649
|
-
}
|
|
33650
|
-
for (const k of Object.keys(o)) o[k] = draft20202(o[k]);
|
|
33651
|
-
return o;
|
|
33652
|
-
}
|
|
33653
|
-
return node;
|
|
33654
|
-
}
|
|
33655
|
-
function mcpInputSchema2(s) {
|
|
33656
|
-
const schema = zodToJsonSchema2(s, { target: "jsonSchema2019-09", $refStrategy: "none" });
|
|
33657
|
-
delete schema.$schema;
|
|
33658
|
-
return draft20202(schema);
|
|
33659
|
-
}
|
|
33660
33726
|
var ASK_MAX2 = 250;
|
|
33661
33727
|
var UNITS_MAX2 = 12;
|
|
33662
33728
|
var UNIT_TITLE_MAX2 = 50;
|
|
@@ -33706,6 +33772,60 @@ function capRefusals2(text, units22, field) {
|
|
|
33706
33772
|
function refuseOverCaps2(text, units22, field, ctx) {
|
|
33707
33773
|
for (const { path, message, ...params } of capRefusals2(text, units22, field)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message, params });
|
|
33708
33774
|
}
|
|
33775
|
+
var COMPLETION_MESSAGE_MAX2 = 50;
|
|
33776
|
+
var CompletionSchema2 = external_exports.object({
|
|
33777
|
+
goalId: external_exports.string().uuid().describe("The existing Goal to complete."),
|
|
33778
|
+
message: external_exports.string().trim().min(1).max(COMPLETION_MESSAGE_MAX2, "completion_message_too_long: at most 50 characters; put detail in units.").describe("Completion headline, at most 50 characters."),
|
|
33779
|
+
units: external_exports.array(UnitInputSchema2.extend({
|
|
33780
|
+
title: external_exports.string().trim().min(1).max(UNIT_TITLE_MAX2),
|
|
33781
|
+
body: external_exports.string().trim().min(1).max(UNIT_BODY_MAX2)
|
|
33782
|
+
})).max(UNITS_MAX2).optional().describe("Suggest initial state, changes, current state with verification and limits, and next steps / leftover work with its owner when useful. One idea per unit; omit unnecessary sections."),
|
|
33783
|
+
reply: external_exports.string().trim().min(1).max(OPTION_LABEL_MAX2).describe("One acknowledgement in the person's voice, at most 50 characters. Acknowledgement alone requests no follow-up and does not claim they tested the work."),
|
|
33784
|
+
options: external_exports.array(external_exports.object({
|
|
33785
|
+
label: external_exports.string().trim().min(1).max(OPTION_LABEL_MAX2),
|
|
33786
|
+
hint: external_exports.string().trim().min(1).max(OPTION_HINT_MAX2)
|
|
33787
|
+
}).strict()).max(5).optional().describe("Optional next steps the person may initiate: label (50 characters) and consequence in hint (200). Selecting one acknowledges the report and requests that step from its author; it never reopens this Goal.")
|
|
33788
|
+
}).strict();
|
|
33789
|
+
var CompleteChangeSchema2 = CompletionSchema2.extend({ kind: external_exports.literal("complete") });
|
|
33790
|
+
var ASKING_RULES2 = `HOW TO ASK:
|
|
33791
|
+
1. Write one question per object. Five questions are five objects in \`questions\`, in one contact, so each can be answered on its own; one question with five parts settles nothing until all five are answered.
|
|
33792
|
+
2. Name the Goal you are working on as the question's \`goalId\`: the question goes onto that Goal and its answer comes back there. It must exist: create it with manage_goals first.
|
|
33793
|
+
3. \`workItBlocks\` only for work you are truly blocked on (it asks for a call); leave it out for a question you can keep working around.
|
|
33794
|
+
4. Never hold the process open with while-loops. A wait that comes back \`expired\` is not a finished conversation: wait again with contact({wait:true}) only while you have nothing else to do. While a Call you placed is open, keep receiving window after window until it ends, even when a window is empty: the person may give follow-ups or instructions. With no open Call, STOP WAITING when a window comes back with nothing new \u2014 the question stays open, and its answer reaches you on the Goal. Otherwise, yield only with a working listener or scheduled wakeup, and collect answers with \`contact({wait:false})\` or \`get_goals\` on that wake.
|
|
33795
|
+
5. One question, one row. Never restate a question that is still waiting inside a new contact: keep waiting on the original, or the answer lands on one copy and the other stays open. Take back one that no longer matters with \`withdrawQuestionIds\`.
|
|
33796
|
+
6. Read the Goal's conversation before asking. Never ask again what was answered or already shipped \u2014 and tell its states apart: \`answered\` carries the choice in \`answer\`, while \`replied\` means they said something and chose none of your options, so that question is still yours to settle and their words are the line beside it.
|
|
33797
|
+
7. A question carries its options when you have them; a free-text question may omit them. Each option\u2019s label must stand on its own and name its choice in full -- never \`All three\` or \`Option 2\`. \`pickMode: "many"\` makes the card a checklist (options they may want several of); \`"one"\`, the default, a pick (one rules out the others); \`"rank"\` an ordering. Each question is sent exactly as written, with no reading in between. They can always answer in their own words, so never add an 'Other' option.
|
|
33798
|
+
8. A diagnosis says when, why and how it happens, then proposes one fix. Never options first.
|
|
33799
|
+
9. GIVE A REPORT ITS REPLY: an update takes \`reply\` too, one line the person can tap instead of typing, written in THEIR voice for THAT report ("Sounds good, I'll test later") \u2014 the same care you give a question's options. What they tap is what you read back, which tells you whether they verified your work or only received it. One line, because a report has one way to close, and it may not change what happens next: a reply that would is a question. Requested delivery and completed-Goal reports require a reply (reply_required); ordinary progress may omit it.`;
|
|
33800
|
+
function draft20202(node) {
|
|
33801
|
+
if (Array.isArray(node)) return node.map(draft20202);
|
|
33802
|
+
if (node && typeof node === "object") {
|
|
33803
|
+
const o = node;
|
|
33804
|
+
if (Array.isArray(o.items)) {
|
|
33805
|
+
o.prefixItems = o.items;
|
|
33806
|
+
if ("additionalItems" in o) {
|
|
33807
|
+
o.items = o.additionalItems;
|
|
33808
|
+
delete o.additionalItems;
|
|
33809
|
+
} else delete o.items;
|
|
33810
|
+
}
|
|
33811
|
+
for (const [excl, lim] of [["exclusiveMinimum", "minimum"], ["exclusiveMaximum", "maximum"]]) {
|
|
33812
|
+
if (typeof o[excl] === "boolean") {
|
|
33813
|
+
if (o[excl] === true && typeof o[lim] === "number") {
|
|
33814
|
+
o[excl] = o[lim];
|
|
33815
|
+
delete o[lim];
|
|
33816
|
+
} else delete o[excl];
|
|
33817
|
+
}
|
|
33818
|
+
}
|
|
33819
|
+
for (const k of Object.keys(o)) o[k] = draft20202(o[k]);
|
|
33820
|
+
return o;
|
|
33821
|
+
}
|
|
33822
|
+
return node;
|
|
33823
|
+
}
|
|
33824
|
+
function mcpInputSchema2(s) {
|
|
33825
|
+
const schema = zodToJsonSchema2(s, { target: "jsonSchema2019-09", $refStrategy: "none" });
|
|
33826
|
+
delete schema.$schema;
|
|
33827
|
+
return draft20202(schema);
|
|
33828
|
+
}
|
|
33709
33829
|
var questionHandle2 = external_exports.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/, "a question id, whole or its first eight characters");
|
|
33710
33830
|
var NodeRef2 = external_exports.object({
|
|
33711
33831
|
type: external_exports.enum(["goal", "question"]),
|
|
@@ -33746,10 +33866,18 @@ var UpdateInputSchema2 = external_exports.object({
|
|
|
33746
33866
|
// MECHANISM). It rides the door as the report's single stored option (`storedOption`,
|
|
33747
33867
|
// `apps/api/src/goal/router.ts`), so no reader below the door learns a second shape.
|
|
33748
33868
|
reply: external_exports.string().trim().min(1).describe(
|
|
33749
|
-
`What this report's button says instead of "Got it", at most ${OPTION_LABEL_MAX2} characters, written in THEIR voice for THIS report ("Sounds good, I'll test later") -- the care you give a question's options, so what they tap tells you whether they verified your work or only received it. One tap sends exactly these words back to you. Words that would CHANGE what happens next are a question, not a reply to a report: it still asks nothing and still owes you no answer.
|
|
33869
|
+
`What this report's button says instead of "Got it", at most ${OPTION_LABEL_MAX2} characters, written in THEIR voice for THIS report ("Sounds good, I'll test later") -- the care you give a question's options, so what they tap tells you whether they verified your work or only received it. One tap sends exactly these words back to you. Words that would CHANGE what happens next are a question, not a reply to a report: it still asks nothing and still owes you no answer. The reply is required for requested delivery and completed Goals; ordinary progress may omit it.`
|
|
33750
33870
|
).optional(),
|
|
33751
33871
|
userExplicitlyRequested: userExplicitlyRequested2
|
|
33752
33872
|
}).strict().superRefine((u, ctx) => {
|
|
33873
|
+
if (u.userExplicitlyRequested && !u.reply) {
|
|
33874
|
+
ctx.addIssue({
|
|
33875
|
+
code: external_exports.ZodIssueCode.custom,
|
|
33876
|
+
path: ["reply"],
|
|
33877
|
+
params: { refusal: "reply_required" },
|
|
33878
|
+
message: "reply_required: an update requested for delivery needs one reply, written in the person\u2019s voice for this report."
|
|
33879
|
+
});
|
|
33880
|
+
}
|
|
33753
33881
|
refuseOverCaps2(u.message, u.units, "message", ctx);
|
|
33754
33882
|
if (u.reply && u.reply.length > OPTION_LABEL_MAX2) {
|
|
33755
33883
|
ctx.addIssue({
|
|
@@ -33808,6 +33936,7 @@ var QuestionDependencySchema2 = external_exports.object({
|
|
|
33808
33936
|
reason: reason2.optional()
|
|
33809
33937
|
}).strict().refine(actionFits2, ACTION_FITS2);
|
|
33810
33938
|
var sendFields2 = {
|
|
33939
|
+
completions: external_exports.array(CompletionSchema2).min(1).max(20).optional().describe("Close existing Goals and deliver their completion reports atomically. Ordinary updates never close work."),
|
|
33811
33940
|
updates: external_exports.array(UpdateInputSchema2).min(1).max(20).optional().describe(
|
|
33812
33941
|
"News: progress, findings, a result. An update asks nothing. It reaches the person only when they asked for it (userExplicitlyRequested), when it answers something they said, or once its Goal is done; otherwise it is recorded as the Goal's progress and nobody is notified."
|
|
33813
33942
|
),
|
|
@@ -33830,24 +33959,22 @@ var ContactSchema2 = external_exports.object({
|
|
|
33830
33959
|
"Only controls when this returns. Sending: false (the default) returns at once; true waits up to about 45 seconds for a response to what you sent. Sending nothing: true (the default) waits up to about 45 seconds for anything addressed to you; false returns what is waiting now. It does not mean work is blocked, set urgency, or ask for a call. `waitOutcome` says which happened: `available`, `expired` (nothing arrived; wait again without sending again), or `not_waited`."
|
|
33831
33960
|
)
|
|
33832
33961
|
}).strict();
|
|
33833
|
-
var sendsAnything2 = (c) => !!(c.updates?.length || c.questions?.length || c.answers?.length || c.withdrawQuestionIds?.length || c.questionDependencies?.length);
|
|
33962
|
+
var sendsAnything2 = (c) => !!(c.completions?.length || c.updates?.length || c.questions?.length || c.answers?.length || c.withdrawQuestionIds?.length || c.questionDependencies?.length);
|
|
33834
33963
|
var ContactWireSchema2 = external_exports.object({
|
|
33835
33964
|
operationId: external_exports.string().uuid(),
|
|
33836
33965
|
...sendFields2,
|
|
33837
33966
|
questions: external_exports.array(QuestionWireSchema2).min(1).max(20).optional()
|
|
33838
33967
|
}).strict().refine(sendsAnything2, "a contact sends at least one update, question, answer, withdrawal or question dependency");
|
|
33839
33968
|
var CONTACT_SCHEMA2 = { type: "object", ...mcpInputSchema2(ContactSchema2) };
|
|
33840
|
-
var CONTACT_DESCRIPTION2 =
|
|
33841
|
-
|
|
33842
|
-
QUESTIONS: one question per object; several questions are several objects in one contact, so each can be answered on its own. Each is sent exactly as written, with no reading in between: a bundled question arrives as one card. Give options when you have them. Each option's label must stand on its own \u2014 the person may see only the labels \u2014 so never a label that points into your text ('All three', 'Option 2', '1 and 3 only'). pickMode:'one' (the default) makes a pick, for alternatives; 'many' a checklist, for independent options they may want several of; 'rank' an ordering. The person can always answer in their own words, so never add an 'Other' option. workItBlocks names the work a question holds up, only when you are truly blocked: it asks for a call. Answers land on the question's Goal: collect them by receiving (below) or with get_goal.
|
|
33969
|
+
var CONTACT_DESCRIPTION2 = "Send to and receive from the person: completions, updates, questions, answers to questions addressed to you, withdrawals of your own questions, and what a question holds up. Every Goal you name must already exist: create it with manage_goals first; an unknown Goal is refused and nothing is sent. Every item applies on its own: read `results`, where a failed item names why while the rest still apply.\n\n" + ASKING_RULES2 + `
|
|
33843
33970
|
|
|
33844
33971
|
CUT A LONG MESSAGE BEFORE YOU SEND IT: a question, an update's message or an answer is the one point, in at most ${ASK_MAX2} characters; everything else it needs goes in 'units', up to ${UNITS_MAX2} {title, body} in reading order, one idea each, every title at most ${UNIT_TITLE_MAX2} characters and every body at most ${UNIT_BODY_MAX2}. The person sees the main text and the titles, and opens a unit to read its body. Over a cap is refused by name (ask_too_long, too_many_units, unit_title_too_long, unit_body_too_long) and nothing is sent.
|
|
33845
33972
|
|
|
33846
|
-
|
|
33973
|
+
UPDATES ask nothing, so no answer is owed and none should be awaited. An update reaches the person only when they asked you for it (userExplicitlyRequested), when it answers something they said, or once its Goal is done; any other is recorded as the Goal's progress and nobody is notified. Ordinary updates never close a Goal. On a Goal whose report card is still open, an update that reaches them is added to that card, with no new push. If the person is already on a call, anything that reaches them joins that call, with no ring.
|
|
33847
33974
|
|
|
33848
|
-
|
|
33975
|
+
COMPLETIONS close and report in one operation: completions:[{goalId,message,units?,reply,options?}]. The same contract works in manage_goals changes with kind:complete. message is at most 50 characters; use units when useful for initial state, changes, current state (verification and limits), and next steps / leftover work with its owner. reply is one acknowledgement in the person's voice. Optional options:[{label,hint}] suggest next steps: a tap acknowledges the report and requests that step from its author. It never reopens the completed Goal; follow-up work needs a new Goal. Closure and the report commit together or neither does. Required children and dependencies must already be finished. No Reviewer rejudges your completion; say only what your evidence supports.
|
|
33849
33976
|
|
|
33850
|
-
RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of what is addressed to you (not a history page): \`question\`, a Question you owe (answer it in answers, with its questionId); \`update\`, something new on one of your Goals (a reply, an answer: read it with
|
|
33977
|
+
RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of what is addressed to you (not a history page): \`question\`, a Question you owe (answer it in answers, with its questionId); \`update\`, something new on one of your Goals (a reply, an answer: read it with get_goals); \`instruction\`, a request or note sent to you. \`hasMore\` says more are waiting. Reading acknowledges nothing: once you have handled events, confirm their eventIds with contact({ackEventIds}), and the next batch can come. contact({}) waits up to about 45 seconds for something to arrive; contact({wait:false}) returns at once. It also lists work given to you that nobody has started (\`assigned\`; your first write to it starts it) and your work gone quiet (\`stalled\`).`;
|
|
33851
33978
|
var CreateGoalSchema2 = external_exports.object({
|
|
33852
33979
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
33853
33980
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -33920,6 +34047,7 @@ var DependencyChange2 = external_exports.object({
|
|
|
33920
34047
|
var read2 = { revision: external_exports.number().int().positive().optional() };
|
|
33921
34048
|
var GoalChangeSchema2 = external_exports.discriminatedUnion("kind", [
|
|
33922
34049
|
CreateChange2,
|
|
34050
|
+
CompleteChangeSchema2.extend(read2),
|
|
33923
34051
|
EditChange2.extend(read2),
|
|
33924
34052
|
StateChange2.extend(read2),
|
|
33925
34053
|
AssignChange2.extend(read2),
|
|
@@ -33937,20 +34065,20 @@ var ManageGoalsSchema2 = external_exports.object({
|
|
|
33937
34065
|
changes: external_exports.array(GoalChangeSchema2).min(1).max(50)
|
|
33938
34066
|
}).strict().superRefine((v, ctx) => editNames2(v.changes, ctx));
|
|
33939
34067
|
var ManageGoalsToolSchema2 = external_exports.object({
|
|
33940
|
-
changes: external_exports.array(external_exports.discriminatedUnion("kind", [CreateChange2, EditChange2, StateChange2, AssignChange2, DeferChange2, MoveChange2, DependencyChange2])).min(1).max(50).describe("The changes, applied in order, each on its own.")
|
|
34068
|
+
changes: external_exports.array(external_exports.discriminatedUnion("kind", [CreateChange2, CompleteChangeSchema2, EditChange2, StateChange2, AssignChange2, DeferChange2, MoveChange2, DependencyChange2])).min(1).max(50).describe("The changes, applied in order, each on its own.")
|
|
33941
34069
|
}).strict().superRefine((v, ctx) => editNames2(v.changes, ctx));
|
|
33942
|
-
var MANAGE_GOALS_DESCRIPTION2 = "Create, edit, assign, organize or close work. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (
|
|
33943
|
-
var
|
|
33944
|
-
|
|
34070
|
+
var MANAGE_GOALS_DESCRIPTION2 = "Create, edit, assign, organize or close work. Every change here changes Goals; answering, withdrawing or asking Questions stays in contact. Each change applies independently: a refused one names why (`results[i].error`, e.g. goal_revision_conflict, goal_children_open, goal_not_found) and the rest still apply, so read every result: a partial result is never a complete success. Kinds: complete (goalId, message capped at 50 characters, units, reply, optional next-step options {label, hint}) closes and reports atomically, exactly as contact.completions; create (title, outcome, ownerId, parentGoalId, sourceEntryIds) returns the new Goal's id in `results[i].goalId`, in the order requested, to use in later calls (an outcome is one paragraph, at most 500 characters: split more into child Goals with parentGoalId); edit (title and/or outcome); state (open, completed, canceled); assign (ownerId); defer (until: the Goal waits until then and its owner is woken when it passes; null takes it back); move (parentGoalId, or null for a root: only this Goal moves); dependency (add or remove: goalId waits on dependsOnGoalId to start or to complete). A Goal cannot be completed while its required children or dependencies remain open: finish or move them first. Finishing the children does not prove the parent's own work is done. An edit applies at the version you last read: if someone changed the Goal since, it is refused as goal_revision_conflict, so read it again (get_goals) and reconsider. You may change any Goal of your person; a change puts you on it. Returns `ok` (every change applied), `results` per change (applied or failed), and `goals`, each changed Goal's id, state, title, outcome and revision.";
|
|
34071
|
+
var GetGoalsSchema2 = external_exports.object({
|
|
34072
|
+
goalIds: external_exports.array(external_exports.string().uuid()).min(1).max(10).describe("The Goals to read, in order; one to ten IDs."),
|
|
33945
34073
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
33946
34074
|
* newest entry, and counts what it left out (`apps/api/src/goal/collapse.ts`). */
|
|
33947
|
-
history: external_exports.boolean().optional()
|
|
33948
|
-
|
|
33949
|
-
|
|
33950
|
-
|
|
33951
|
-
diagnose: external_exports.boolean().optional()
|
|
34075
|
+
history: external_exports.boolean().optional()
|
|
34076
|
+
}).strict();
|
|
34077
|
+
var GetDeliverySchema2 = external_exports.object({
|
|
34078
|
+
deliveryId: external_exports.string().uuid().describe("The Delivery id returned by contact or receive.")
|
|
33952
34079
|
}).strict();
|
|
33953
|
-
var
|
|
34080
|
+
var GET_DELIVERY_DESCRIPTION2 = "Read a Delivery in full: its whole ask, units, options and answers, plus current callState and callOutcome for a Call. Any agent of the same account may read it; another account is not disclosed. Reading never acknowledges or resends anything. Use this after a call or when receive names a Call you are waiting on.";
|
|
34081
|
+
var GET_GOALS_DESCRIPTION2 = "Read one to ten Goals by goalIds, returned in the same order under goals (a read never puts you on them). history applies to every requested Goal. A refused read fails the call with its original refusal; no partial result is presented as complete. Each Goal includes its ten most recent other contributors (names, latest entry headlines and times), 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. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. 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. Every Goal of your person is readable, whichever of their agents owns it; another account's Goals are not disclosed.";
|
|
33954
34082
|
var SearchToolSchema2 = external_exports.object({
|
|
33955
34083
|
query: external_exports.string().trim().min(1).max(500),
|
|
33956
34084
|
types: external_exports.array(external_exports.enum(["entry", "goal", "answer"])).min(1).optional(),
|
|
@@ -33958,6 +34086,8 @@ var SearchToolSchema2 = external_exports.object({
|
|
|
33958
34086
|
limit: external_exports.number().int().min(1).max(20).optional()
|
|
33959
34087
|
}).strict();
|
|
33960
34088
|
var SEARCH_DESCRIPTION2 = "Search your person's history across all of their agents: Entries (what anyone said or wrote, typed or spoken on a call), Goals (by title and outcome) and replies -- the person's answers, acknowledgements and deferrals (type answer; found by their summary, by the question or report they reply to, or by the words that gave them). query is words to look for; records sharing more of its words rank first, and exact names work. types narrows it to entry, goal and/or answer (default: all three). goalId searches under one Goal: its Entries, the replies to its items, and it and its immediate children. limit is matches per type, 1 to 20 (default 8). Read-only. Each match carries its whole saved words, its ID and its links (a reply carries its kind -- answered, acknowledged or deferred -- whether it is closed (a deferral is not), its summary in under ten words, its question or report, the choice made and the Entries that support it); `omitted` counts what matched but was left out, so narrow the words or add a goalId to see it. Nothing found is not proof that nothing exists; a refused search says why. Sealed (encrypted) content is never searched or returned.";
|
|
34089
|
+
var ConfigureIdentitySchema2 = external_exports.object({}).strict();
|
|
34090
|
+
var CONFIGURE_IDENTITY_DESCRIPTION2 = "Read this authenticated agent\u2019s current name, voice and token ID. Read-only; no arguments. Pairing, renaming and logout are available only through the local configure driver.";
|
|
33961
34091
|
var CheckActivitySchema2 = external_exports.object({}).strict();
|
|
33962
34092
|
var FEEDBACK_TEXT_MAX2 = 5e4;
|
|
33963
34093
|
var SendFeedbackSchema2 = external_exports.object({
|
|
@@ -33967,11 +34097,13 @@ var SendFeedbackSchema2 = external_exports.object({
|
|
|
33967
34097
|
}).strict();
|
|
33968
34098
|
var SEND_FEEDBACK_DESCRIPTION2 = "Send feedback about Paigy itself (a bug report, an idea) straight to the Paigy team. Send it only when the person asked you to, or agreed when you offered: it goes to Paigy, never to the person, and nobody answers it here. Not for your work or a question for the person: use contact for those. Returns whether it was stored.";
|
|
33969
34099
|
var AGENT_TOOLS2 = [
|
|
34100
|
+
{ name: "configure", description: CONFIGURE_IDENTITY_DESCRIPTION2, inputSchema: mcpInputSchema2(ConfigureIdentitySchema2) },
|
|
33970
34101
|
{ name: "contact", description: CONTACT_DESCRIPTION2, inputSchema: CONTACT_SCHEMA2 },
|
|
33971
34102
|
{ name: "manage_goals", description: MANAGE_GOALS_DESCRIPTION2, inputSchema: mcpInputSchema2(ManageGoalsToolSchema2) },
|
|
33972
|
-
{ name: "
|
|
34103
|
+
{ name: "get_delivery", description: GET_DELIVERY_DESCRIPTION2, inputSchema: mcpInputSchema2(GetDeliverySchema2) },
|
|
34104
|
+
{ name: "get_goals", description: GET_GOALS_DESCRIPTION2, inputSchema: mcpInputSchema2(GetGoalsSchema2) },
|
|
33973
34105
|
{ name: "search", description: SEARCH_DESCRIPTION2, inputSchema: mcpInputSchema2(SearchToolSchema2) },
|
|
33974
|
-
{ name: "check_activity", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use
|
|
34106
|
+
{ name: "check_activity", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goals to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema2(CheckActivitySchema2) },
|
|
33975
34107
|
{ name: "send_feedback", description: SEND_FEEDBACK_DESCRIPTION2, inputSchema: mcpInputSchema2(SendFeedbackSchema2) }
|
|
33976
34108
|
];
|
|
33977
34109
|
var AGENT_TOOL_NAMES2 = AGENT_TOOLS2.map((t) => t.name);
|
|
@@ -34250,8 +34382,45 @@ function entryWords(entry) {
|
|
|
34250
34382
|
}
|
|
34251
34383
|
var LIVE_MS2 = 3 * 6e4;
|
|
34252
34384
|
var WORKING_MS2 = 60 * 6e4;
|
|
34385
|
+
var isWorking = (c, now) => !!c.lastWorkedAt && now - Date.parse(c.lastWorkedAt) < WORKING_MS2;
|
|
34253
34386
|
var WORKING_TIER_MS2 = 2 * 60 * 6e4;
|
|
34254
34387
|
var RECENT_TIER_MS2 = 24 * 60 * 6e4;
|
|
34388
|
+
var json2 = external_exports.lazy(() => external_exports.union([
|
|
34389
|
+
external_exports.string(),
|
|
34390
|
+
external_exports.number().finite(),
|
|
34391
|
+
external_exports.boolean(),
|
|
34392
|
+
external_exports.null(),
|
|
34393
|
+
external_exports.array(json2),
|
|
34394
|
+
external_exports.record(json2)
|
|
34395
|
+
]));
|
|
34396
|
+
var clock3 = external_exports.number().int().nonnegative().safe();
|
|
34397
|
+
var HeardEventSchema2 = external_exports.object({
|
|
34398
|
+
eventId: external_exports.string().uuid(),
|
|
34399
|
+
source: external_exports.enum(["phone", "bot"]),
|
|
34400
|
+
sequence: clock3,
|
|
34401
|
+
kind: external_exports.string().min(1),
|
|
34402
|
+
phoneAtMs: clock3.optional(),
|
|
34403
|
+
botAtMs: clock3.optional(),
|
|
34404
|
+
payload: external_exports.record(json2)
|
|
34405
|
+
}).strict();
|
|
34406
|
+
var HeardCursorSchema2 = HeardEventSchema2.pick({ source: true, sequence: true, eventId: true });
|
|
34407
|
+
var HeardPageSchema2 = external_exports.object({
|
|
34408
|
+
events: external_exports.array(HeardEventSchema2).max(200),
|
|
34409
|
+
next: HeardCursorSchema2.nullable()
|
|
34410
|
+
}).strict();
|
|
34411
|
+
var MAX_EVENTS2 = 50;
|
|
34412
|
+
var MAX_BYTES2 = 8e4;
|
|
34413
|
+
var batchBytes2 = (events) => {
|
|
34414
|
+
let bytes = 0;
|
|
34415
|
+
for (const char of JSON.stringify({ events })) {
|
|
34416
|
+
const point = char.codePointAt(0);
|
|
34417
|
+
bytes += point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
|
|
34418
|
+
}
|
|
34419
|
+
return bytes;
|
|
34420
|
+
};
|
|
34421
|
+
var HeardBatchSchema2 = external_exports.object({
|
|
34422
|
+
events: external_exports.array(HeardEventSchema2).min(1).max(MAX_EVENTS2)
|
|
34423
|
+
}).strict().refine((value) => batchBytes2(value.events) <= MAX_BYTES2, "heard_batch_too_large");
|
|
34255
34424
|
var ContextSchema2 = external_exports.object({
|
|
34256
34425
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
34257
34426
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -34808,8 +34977,8 @@ var MissedCallSchema2 = external_exports.enum([
|
|
|
34808
34977
|
"inbox",
|
|
34809
34978
|
"dismiss"
|
|
34810
34979
|
]);
|
|
34811
|
-
var
|
|
34812
|
-
var QUIET2 = ` Nothing rings from ${
|
|
34980
|
+
var clock22 = (h) => h === 0 ? "midnight" : h === 12 ? "noon" : h < 12 ? `${h} am` : `${h - 12} pm`;
|
|
34981
|
+
var QUIET2 = ` Nothing rings from ${clock22(NIGHT2.from)} to ${clock22(NIGHT2.to)} your time; the count waits for morning.`;
|
|
34813
34982
|
var BrokerTuningSchema2 = external_exports.object({
|
|
34814
34983
|
/** 'none' = skip the spoken ack after a mapped answer (power users find it slow). */
|
|
34815
34984
|
ackVerbosity: external_exports.enum(["normal", "none"]).optional(),
|
|
@@ -35124,7 +35293,13 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
35124
35293
|
/** ITS SUMMARY IN AT MOST TEN WORDS (owner, 2026-10-08: "the same switch to the more summarized sources
|
|
35125
35294
|
* at the work tab top level screen too"): what the talker or filer wrote on the Entry; absent when
|
|
35126
35295
|
* none was. The Work row reads it before `said`. */
|
|
35127
|
-
summary: external_exports.string().optional()
|
|
35296
|
+
summary: external_exports.string().optional(),
|
|
35297
|
+
/** AN AGENT'S UPDATE OR QUESTION AS ITS UNITS' TITLES (owner, 2026-10-08: "send the 50 chars from the title
|
|
35298
|
+
* of each unit"): each at most 50 characters, in order; absent when it has none. */
|
|
35299
|
+
titles: external_exports.array(external_exports.string()).optional(),
|
|
35300
|
+
/** A PROGRESS NOTE'S ONE POINT ("An agent's progress note -> maybe let's cap it at 200 chars"): the
|
|
35301
|
+
* update's own line, before its units; the row caps it at 200. Absent on anything else. */
|
|
35302
|
+
line: external_exports.string().optional()
|
|
35128
35303
|
}).nullable().optional(),
|
|
35129
35304
|
/** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
|
|
35130
35305
|
* the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
|
|
@@ -35452,6 +35627,8 @@ var CallRecapSchema2 = external_exports.object({
|
|
|
35452
35627
|
status: external_exports.string(),
|
|
35453
35628
|
startedAt: external_exports.string(),
|
|
35454
35629
|
durationMs: external_exports.number().nullable(),
|
|
35630
|
+
/** Bot-clock pickup offset; absent before the timestamp release, null before its report lands. */
|
|
35631
|
+
pickupMs: external_exports.number().nullable().optional(),
|
|
35455
35632
|
agents: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string().nullable() }))
|
|
35456
35633
|
}),
|
|
35457
35634
|
topics: external_exports.array(external_exports.object({
|
|
@@ -35496,7 +35673,9 @@ var CallRecapSchema2 = external_exports.object({
|
|
|
35496
35673
|
words: external_exports.string(),
|
|
35497
35674
|
atMs: external_exports.number()
|
|
35498
35675
|
})),
|
|
35499
|
-
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
35676
|
+
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() }),
|
|
35677
|
+
/** Last stored position on this page when another exists; absent before paging shipped. */
|
|
35678
|
+
next: external_exports.number().int().nonnegative().nullable().optional()
|
|
35500
35679
|
});
|
|
35501
35680
|
|
|
35502
35681
|
// src/paigy/activity.ts
|
|
@@ -35570,9 +35749,9 @@ function limitIn(text, now) {
|
|
|
35570
35749
|
if (!line) return null;
|
|
35571
35750
|
const reset = resetOf(line, now);
|
|
35572
35751
|
if (reset !== null && reset > now && reset - now <= FARTHEST_MS) {
|
|
35573
|
-
return { until: reset + MARGIN_MS, resets: /resets\s+(?:at\s+)?(.+?)\s*(?:\(|$)/i.exec(line)?.[1]?.trim() ??
|
|
35752
|
+
return { until: reset + MARGIN_MS, resets: /resets\s+(?:at\s+)?(.+?)\s*(?:\(|$)/i.exec(line)?.[1]?.trim() ?? clock4(reset) };
|
|
35574
35753
|
}
|
|
35575
|
-
return { until: now + RETRY_MS2, resets:
|
|
35754
|
+
return { until: now + RETRY_MS2, resets: clock4(now + RETRY_MS2) };
|
|
35576
35755
|
}
|
|
35577
35756
|
function resetOf(line, now) {
|
|
35578
35757
|
const m = /resets\s+(?:at\s+)?(?:([a-z]{3})[a-z]*\s+(\d{1,2}),?\s+(?:at\s+)?)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)\b(?:\s*\(([^)]+)\))?/i.exec(line);
|
|
@@ -35616,7 +35795,7 @@ function wallToEpoch(year, month, day, hour, minute, zone) {
|
|
|
35616
35795
|
const first = wall - offset(wall);
|
|
35617
35796
|
return wall - offset(first);
|
|
35618
35797
|
}
|
|
35619
|
-
function
|
|
35798
|
+
function clock4(t) {
|
|
35620
35799
|
return new Date(t).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" }).replace(/\s?([AP])M$/i, (_, x) => `${x.toLowerCase()}m`);
|
|
35621
35800
|
}
|
|
35622
35801
|
var pausedLine = (limit) => `Paused by the usage limit until ${limit.resets}`;
|
|
@@ -35660,7 +35839,7 @@ function episodes(opts = {}) {
|
|
|
35660
35839
|
episode.called = true;
|
|
35661
35840
|
const rest = episode.paused.filter((n) => !episode.running.includes(n));
|
|
35662
35841
|
const text = [
|
|
35663
|
-
`Claude's usage limit paused ${names(episode.paused)} from ${
|
|
35842
|
+
`Claude's usage limit paused ${names(episode.paused)} from ${clock4(episode.since)}.`,
|
|
35664
35843
|
rest.length ? `It has reset: ${names(episode.running)} ${episode.running.length > 1 ? "are" : "is"} running again, and ${names(rest)} ${rest.length > 1 ? "haven't" : "hasn't"} started again yet.` : `It has reset, and ${episode.paused.length > 1 ? "they have all" : "it has"} carried on.`
|
|
35665
35844
|
].join(" ");
|
|
35666
35845
|
void call(text).then(
|
|
@@ -35679,9 +35858,9 @@ function railFor(opts) {
|
|
|
35679
35858
|
next: async () => {
|
|
35680
35859
|
const waiting = await receive(opts);
|
|
35681
35860
|
const goalId = waiting.claimable?.goalId ?? waiting.review?.[0]?.goalId;
|
|
35682
|
-
return goalId ?
|
|
35861
|
+
return goalId ? getGoals([goalId], opts).then(([goal]) => goal) : { state: "idle", message: "" };
|
|
35683
35862
|
},
|
|
35684
|
-
read: (goalId) =>
|
|
35863
|
+
read: (goalId) => getGoals([goalId], opts).then(([goal]) => goal),
|
|
35685
35864
|
complete: async (goalId, revision) => {
|
|
35686
35865
|
const done = await manageGoals({ changes: [{ kind: "state", goalId, state: "completed", ...revision !== void 0 ? { revision } : {} }] }, opts);
|
|
35687
35866
|
const result = done.results[0];
|
|
@@ -36107,7 +36286,7 @@ async function look(session, state, rail, opts) {
|
|
|
36107
36286
|
const outcome2 = brief2.outcome?.trim() ?? "";
|
|
36108
36287
|
const said = (pendingWords ?? (brief2.entries ?? []).map((e) => entryWords(e).trim())).filter((w) => w && w.toLowerCase() !== outcome2.toLowerCase());
|
|
36109
36288
|
const left = brief2.earlier ? brief2.earlier.omitted + brief2.earlier.shortened : 0;
|
|
36110
|
-
const earlier = left ? `(${left} earlier entries on this Goal are left out or shortened;
|
|
36289
|
+
const earlier = left ? `(${left} earlier entries on this Goal are left out or shortened; get_goals with history: true reads them in full.)` : "";
|
|
36111
36290
|
const fullText = [outcome2, earlier, ...said].filter(Boolean).join("\n\n");
|
|
36112
36291
|
send2(session, state, [HARNESS_INSTRUCTION, named(brief2.goalId, void 0, brief2), fullText].filter(Boolean).join("\n\n"));
|
|
36113
36292
|
if (ended(brief2.state)) await acknowledge2(brief2.goalId, brief2, [], state, rail, opts?.log);
|
|
@@ -36153,7 +36332,7 @@ function send2(session, state, text) {
|
|
|
36153
36332
|
session.send(text);
|
|
36154
36333
|
}
|
|
36155
36334
|
async function pump(session, state, rail, opts) {
|
|
36156
|
-
const
|
|
36335
|
+
const clock5 = opts.beat ?? beat();
|
|
36157
36336
|
const now = opts.now ?? Date.now;
|
|
36158
36337
|
const counts = { wake: 0, rest: 0, safety: 0, skipped: 0 };
|
|
36159
36338
|
const tally = { noted: 0, seen: 0, announced: 0 };
|
|
@@ -36185,7 +36364,7 @@ async function pump(session, state, rail, opts) {
|
|
|
36185
36364
|
counts.wake = counts.rest = counts.safety = counts.skipped = tally.noted = tally.seen = tally.announced = tally.paused = 0;
|
|
36186
36365
|
since = now();
|
|
36187
36366
|
}
|
|
36188
|
-
cause = await
|
|
36367
|
+
cause = await clock5.next(state.asks?.size ? ASK_BEAT_MS : state.paused ? Math.max(1e3, Math.min(SAFETY_MS, state.paused.until - now())) : SAFETY_MS);
|
|
36189
36368
|
if (!await opts.onIdle()) return;
|
|
36190
36369
|
}
|
|
36191
36370
|
}
|
|
@@ -36195,7 +36374,7 @@ function runHarness(opts) {
|
|
|
36195
36374
|
let running = true;
|
|
36196
36375
|
let tail = [];
|
|
36197
36376
|
const state = { resting: true };
|
|
36198
|
-
const
|
|
36377
|
+
const clock5 = beat();
|
|
36199
36378
|
let session = null;
|
|
36200
36379
|
const speaksAs = opts.session ?? opts.resume ?? sessionId();
|
|
36201
36380
|
const instance = randomUUID4();
|
|
@@ -36256,7 +36435,7 @@ ${event.message}`, state, rail, { log: opts.log });
|
|
|
36256
36435
|
stop2();
|
|
36257
36436
|
return;
|
|
36258
36437
|
} else if (carried) runningAgain();
|
|
36259
|
-
|
|
36438
|
+
clock5.nudge("rest");
|
|
36260
36439
|
}
|
|
36261
36440
|
}
|
|
36262
36441
|
session = startSession({
|
|
@@ -36275,13 +36454,13 @@ ${event.message}`, state, rail, { log: opts.log });
|
|
|
36275
36454
|
},
|
|
36276
36455
|
onEvent: (event) => void handle(event).catch((err) => opts.log(`\u26A0 ${err.message}`))
|
|
36277
36456
|
});
|
|
36278
|
-
void pump(session, state, rail, { onIdle: () => running, beat:
|
|
36457
|
+
void pump(session, state, rail, { onIdle: () => running, beat: clock5, initialPrompt: opts.prompt, log: opts.log, ...inbox ? { inbox } : {} }).catch(
|
|
36279
36458
|
(e) => opts.log(`\u26A0 conversation loop stopped: ${e.message}`)
|
|
36280
36459
|
);
|
|
36281
36460
|
opts.log(`\u25B6 started ${opts.harness} in ${opts.cwd} (${opts.mode} mode)`);
|
|
36282
36461
|
function stop2() {
|
|
36283
36462
|
running = false;
|
|
36284
|
-
|
|
36463
|
+
clock5.nudge("wake");
|
|
36285
36464
|
tail = [];
|
|
36286
36465
|
cancelAsks(state);
|
|
36287
36466
|
session?.stop();
|
|
@@ -36296,7 +36475,7 @@ ${event.message}`, state, rail, { log: opts.log });
|
|
|
36296
36475
|
working: () => running && state.resting !== true,
|
|
36297
36476
|
// Paused by a usage limit, the activity line says so: the quiet place the pause is shown.
|
|
36298
36477
|
tail: () => state.paused ? [pausedLine(state.paused)] : [...tail],
|
|
36299
|
-
nudge: () =>
|
|
36478
|
+
nudge: () => clock5.nudge("wake"),
|
|
36300
36479
|
stop: stop2
|
|
36301
36480
|
};
|
|
36302
36481
|
}
|
|
@@ -36727,7 +36906,7 @@ var claimedRun = (sessionId2) => {
|
|
|
36727
36906
|
const slot = sessionSlot(sessionId2);
|
|
36728
36907
|
return { slot, key: runKey(slot) };
|
|
36729
36908
|
};
|
|
36730
|
-
var launchRun = (spec) => ({ session: spec.sessionId
|
|
36909
|
+
var launchRun = (spec) => ({ session: spec.sessionId });
|
|
36731
36910
|
var BOUND_ELSEWHERE_MS = 10 * 6e4;
|
|
36732
36911
|
var boundElsewhere = /* @__PURE__ */ new Map();
|
|
36733
36912
|
var BOUND_SKIP = "is bound to the session it was paired in, which this host cannot name \u2014 run `paigy-harness handoff` as it, in its folder, to record it";
|
|
@@ -36757,13 +36936,12 @@ function evict(runs, key3, run) {
|
|
|
36757
36936
|
if (runs.get(key3)?.run === run) runs.delete(key3);
|
|
36758
36937
|
}
|
|
36759
36938
|
var PRESENCE_FRESH_MS = 3 * 6e4;
|
|
36760
|
-
var IDLE_HOLDER_MS = 30 * 6e4;
|
|
36761
36939
|
function wakeAction(live, now, serverSeenAt, here = {}) {
|
|
36762
36940
|
if (live) return { action: "skip", why: "run" };
|
|
36763
36941
|
if (here.holder) return { action: "skip", why: "holder" };
|
|
36764
36942
|
if (serverSeenAt !== void 0 && now - serverSeenAt < PRESENCE_FRESH_MS) {
|
|
36765
36943
|
if (here.goneAt !== void 0 && serverSeenAt <= here.goneAt) return { action: "spawn", why: "gone" };
|
|
36766
|
-
if (here.movedAt !== void 0 && now - here.movedAt >=
|
|
36944
|
+
if (here.movedAt !== void 0 && now - here.movedAt >= WORKING_MS2 && !isWorking(here, now)) return { action: "spawn", why: "idle" };
|
|
36767
36945
|
return { action: "skip", why: "presence" };
|
|
36768
36946
|
}
|
|
36769
36947
|
return { action: "spawn", why: "nobody" };
|
|
@@ -36836,7 +37014,7 @@ function startHost(opts) {
|
|
|
36836
37014
|
});
|
|
36837
37015
|
void heartbeat({
|
|
36838
37016
|
version: VERSION,
|
|
36839
|
-
harnesses: detectAll().map((a) => ({ name: a.name, label: a.label, status: a.status
|
|
37017
|
+
harnesses: detectAll().map((a) => ({ name: a.name, label: a.label, status: a.status })),
|
|
36840
37018
|
workspaces: listWorkspaces(opts.wsDeps),
|
|
36841
37019
|
// The repos inside them, so a session started for work on one opens IN it (Goal 26982211).
|
|
36842
37020
|
repos: listRepos(opts.wsDeps)
|
|
@@ -36918,7 +37096,7 @@ function startHost(opts) {
|
|
|
36918
37096
|
const presence = /* @__PURE__ */ new Map();
|
|
36919
37097
|
try {
|
|
36920
37098
|
for (const c of (await listConnections(asHost)).items) {
|
|
36921
|
-
|
|
37099
|
+
presence.set(c.id, c);
|
|
36922
37100
|
}
|
|
36923
37101
|
lastPresence = presence;
|
|
36924
37102
|
} catch {
|
|
@@ -36951,11 +37129,12 @@ function startHost(opts) {
|
|
|
36951
37129
|
}
|
|
36952
37130
|
}
|
|
36953
37131
|
const tokenId = slotIdentity(slot).tokenId;
|
|
36954
|
-
const
|
|
37132
|
+
const connection = tokenId ? presence.get(tokenId) : void 0;
|
|
37133
|
+
const seenByServer = connection?.lastSeenAt ? Date.parse(connection.lastSeenAt) : void 0;
|
|
36955
37134
|
const holder = holders.get(slot);
|
|
36956
37135
|
if (holder) heldHere.add(slot);
|
|
36957
37136
|
else if (heldHere.delete(slot)) goneAt.set(slot, Date.now());
|
|
36958
|
-
const here = { holder, goneAt: goneAt.get(slot) };
|
|
37137
|
+
const here = { holder, goneAt: goneAt.get(slot), lastWorkedAt: connection?.lastWorkedAt };
|
|
36959
37138
|
let decided = wakeAction(!!live, Date.now(), seenByServer, here);
|
|
36960
37139
|
const label = slotName(slot) ?? slot;
|
|
36961
37140
|
if (decided.action === "skip" && decided.why !== "presence") {
|
|
@@ -36998,7 +37177,7 @@ function startHost(opts) {
|
|
|
36998
37177
|
const since = found.since;
|
|
36999
37178
|
decided = wakeAction(false, Date.now(), seenByServer, { ...here, ...since ? { movedAt: Date.parse(since) } : {} });
|
|
37000
37179
|
if (decided.action === "skip") {
|
|
37001
|
-
const said = `is
|
|
37180
|
+
const said = `is connected somewhere this host cannot see (heartbeat) \u2014 ${found.waiting}${since ? ` last moved ${ago(since, Date.now())}; takeover requires ${WORKING_MS2 / 6e4} min without own activity or waiting-work movement` : ", with nothing to say when it last moved"}`;
|
|
37002
37181
|
opts.log(`\u25CC ${label} ${said} \u2014 not spawning`);
|
|
37003
37182
|
decide(slot, "stood_back", found, seenByServer, said);
|
|
37004
37183
|
return;
|
|
@@ -37167,7 +37346,7 @@ function startHost(opts) {
|
|
|
37167
37346
|
// sessions we started ourselves. The server already knows: `last_seen_at` on the
|
|
37168
37347
|
// agent's own token, the same evidence `wakeAction` stands back for. So: a run we
|
|
37169
37348
|
// hold, OR a process the server heard from inside `PRESENCE_FRESH_MS`.
|
|
37170
|
-
running: live.has(name) || !!slot && lastHolders.has(slot) || !!id3.tokenId && Date.now() - (lastPresence.get(id3.tokenId) ??
|
|
37349
|
+
running: live.has(name) || !!slot && lastHolders.has(slot) || !!id3.tokenId && Date.now() - Date.parse(lastPresence.get(id3.tokenId)?.lastSeenAt ?? "") < PRESENCE_FRESH_MS,
|
|
37171
37350
|
// `working` stays ours alone — it means a run WE hold is mid-turn, which is the only
|
|
37172
37351
|
// thing a tail can tell us. A process we did not spawn has no tail here to read.
|
|
37173
37352
|
working: live.get(name)?.working() ?? false
|