@paigy/harness 0.3.36 → 0.3.38
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 +419 -73
- package/dist/main.js +414 -72
- 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, models: entry.models };
|
|
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,7 +109,8 @@ 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"
|
|
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"]
|
|
113
114
|
},
|
|
114
115
|
{
|
|
115
116
|
name: "codex",
|
|
@@ -119,7 +120,8 @@ var init_catalog = __esm({
|
|
|
119
120
|
authProbe: ["codex", "login", "status"],
|
|
120
121
|
loginHint: "Run `codex login` to authenticate.",
|
|
121
122
|
installCli: "curl -fsSL https://chatgpt.com/codex/install.sh | sh",
|
|
122
|
-
installAdapter: "npm install -g @agentclientprotocol/codex-acp"
|
|
123
|
+
installAdapter: "npm install -g @agentclientprotocol/codex-acp",
|
|
124
|
+
models: ["gpt-4o", "gpt-4o-mini", "o1", "o1-mini"]
|
|
123
125
|
},
|
|
124
126
|
{
|
|
125
127
|
name: "agy",
|
|
@@ -129,7 +131,8 @@ var init_catalog = __esm({
|
|
|
129
131
|
adapter: "paigy-agy-acp",
|
|
130
132
|
authProbe: ["agy", "help"],
|
|
131
133
|
loginHint: "Install and set up Antigravity (`agy`).",
|
|
132
|
-
installCli: "curl -fsSL https://antigravity.dev/install.sh | bash"
|
|
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"]
|
|
133
136
|
}
|
|
134
137
|
];
|
|
135
138
|
}
|
|
@@ -22777,10 +22780,47 @@ function claudeConversation() {
|
|
|
22777
22780
|
if (!envSession("CLAUDE_CODE_SESSION_ID") || process.env.CLAUDE_CODE_CHILD_SESSION) return void 0;
|
|
22778
22781
|
const dir = envSession("CLAUDE_CONFIG_DIR") ?? join2(homedir2(), ".claude");
|
|
22779
22782
|
for (const pid of ancestors()) {
|
|
22783
|
+
let text;
|
|
22780
22784
|
try {
|
|
22781
|
-
|
|
22782
|
-
return typeof sessionId2 === "string" && sessionId2.trim() ? sessionId2 : void 0;
|
|
22785
|
+
text = readFileSync(join2(dir, "sessions", `${pid}.json`), "utf8");
|
|
22783
22786
|
} catch {
|
|
22787
|
+
continue;
|
|
22788
|
+
}
|
|
22789
|
+
const record = parseSessionRecord(text);
|
|
22790
|
+
if (!record) continue;
|
|
22791
|
+
return typeof record.sessionId === "string" && record.sessionId.trim() ? record.sessionId : void 0;
|
|
22792
|
+
}
|
|
22793
|
+
return void 0;
|
|
22794
|
+
}
|
|
22795
|
+
function parseSessionRecord(text) {
|
|
22796
|
+
const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
22797
|
+
try {
|
|
22798
|
+
return object(JSON.parse(text));
|
|
22799
|
+
} catch {
|
|
22800
|
+
}
|
|
22801
|
+
let depth = 0;
|
|
22802
|
+
let start = -1;
|
|
22803
|
+
let quoted = false;
|
|
22804
|
+
let escaped = false;
|
|
22805
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
22806
|
+
const c = text[i];
|
|
22807
|
+
if (quoted) {
|
|
22808
|
+
if (escaped) escaped = false;
|
|
22809
|
+
else if (c === "\\") escaped = true;
|
|
22810
|
+
else if (c === '"') quoted = false;
|
|
22811
|
+
} else if (c === '"') quoted = true;
|
|
22812
|
+
else if (c === "{") {
|
|
22813
|
+
if (depth === 0) start = i;
|
|
22814
|
+
depth += 1;
|
|
22815
|
+
} else if (c === "}" && depth > 0) {
|
|
22816
|
+
depth -= 1;
|
|
22817
|
+
if (depth === 0) {
|
|
22818
|
+
try {
|
|
22819
|
+
return object(JSON.parse(text.slice(start, i + 1)));
|
|
22820
|
+
} catch {
|
|
22821
|
+
return void 0;
|
|
22822
|
+
}
|
|
22823
|
+
}
|
|
22784
22824
|
}
|
|
22785
22825
|
}
|
|
22786
22826
|
return void 0;
|
|
@@ -24254,7 +24294,7 @@ ${block2}`;
|
|
|
24254
24294
|
}
|
|
24255
24295
|
return text.slice(0, start) + updated + text.slice(end);
|
|
24256
24296
|
}
|
|
24257
|
-
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, 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, goalOutcome, CreateChange, EditChange, StateChange, AssignChange, DeferChange, MoveChange, DependencyChange, read, GoalChangeSchema, editNames, ManageGoalsSchema, ManageGoalsToolSchema, MANAGE_GOALS_DESCRIPTION, GetGoalSchema, GET_GOAL_DESCRIPTION, SearchToolSchema, SEARCH_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, LIVE_MS, WORKING_MS, ContextSchema, ParticipantSchema, TransformSchema, VisualSchema, NotifyLevelSchema, SelectShapeSchema, ReceiptEventSchema, AttentionSchema, NotifyRequestFields, NotifyRequestSchema, NotifyStatusSchema, AgentStateSchema, TurnSchema, UserAnswerSchema, IntentSchema, RideAlongSchema, AwaitItemSchema, VoiceKeySchema, AgendaTurnSchema, CLAIM_STALE_MS, InboxItemSchema, APNS_TOKEN_RE, PushTokenSchema, MissedCallSchema, clock, QUIET, BrokerTuningSchema, UserSettingsSchema, 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;
|
|
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, GetGoalSchema, GET_GOAL_DESCRIPTION, SearchToolSchema, SEARCH_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, ContextSchema, ParticipantSchema, TransformSchema, VisualSchema, NotifyLevelSchema, SelectShapeSchema, ReceiptEventSchema, AttentionSchema, NotifyRequestFields, NotifyRequestSchema, NotifyStatusSchema, AgentStateSchema, TurnSchema, UserAnswerSchema, IntentSchema, RideAlongSchema, AwaitItemSchema, VoiceKeySchema, AgendaTurnSchema, CLAIM_STALE_MS, InboxItemSchema, APNS_TOKEN_RE, PushTokenSchema, MissedCallSchema, clock, QUIET, BrokerTuningSchema, UserSettingsSchema, 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;
|
|
24258
24298
|
var init_dist = __esm({
|
|
24259
24299
|
"../../packages/sdk/dist/index.js"() {
|
|
24260
24300
|
"use strict";
|
|
@@ -24268,6 +24308,8 @@ var init_dist = __esm({
|
|
|
24268
24308
|
init_zod();
|
|
24269
24309
|
init_zod();
|
|
24270
24310
|
init_zod();
|
|
24311
|
+
init_zod();
|
|
24312
|
+
init_zod();
|
|
24271
24313
|
import_undici = __toESM(require_undici(), 1);
|
|
24272
24314
|
require2 = __sdkCreateRequire(import.meta.url);
|
|
24273
24315
|
CODEX_ENV = ["CODEX_THREAD_ID", "CODEX_SESSION_ID", "CODEX_HOME", "PAIGY_TOKEN", "PAIGY_SESSION_ID", "PAIGY_HARNESS", "PAIGY_INSTANCE_ID", "PAIGY_ON_WAKE"];
|
|
@@ -24649,10 +24691,12 @@ var init_dist = __esm({
|
|
|
24649
24691
|
};
|
|
24650
24692
|
OPTIONS_MIN = 2;
|
|
24651
24693
|
OPTIONS_MAX = 6;
|
|
24694
|
+
OPTION_LABEL_MAX = 50;
|
|
24695
|
+
OPTION_HINT_MAX = 200;
|
|
24652
24696
|
OptionSchema = external_exports.object({
|
|
24653
24697
|
id: external_exports.string(),
|
|
24654
24698
|
label: external_exports.string(),
|
|
24655
|
-
hint: external_exports.string().max(500).describe(
|
|
24699
|
+
hint: external_exports.string().max(500).describe(`Optional: what choosing it does, at most ${OPTION_HINT_MAX} characters (e.g. 'Reruns test suite', 'Merges to main').`).optional(),
|
|
24656
24700
|
// .describe() flows into the MCP contact JSON schema (zodToJsonSchema), so
|
|
24657
24701
|
// the constraints below are what an agent reads when deciding to use these.
|
|
24658
24702
|
html: external_exports.string().max(16384).describe(
|
|
@@ -24666,8 +24710,10 @@ var init_dist = __esm({
|
|
|
24666
24710
|
id: external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9_.:-]+$/).optional().describe(
|
|
24667
24711
|
`Optional: this option's own id, which answers name it by (selectedOptionIds). Give every option one, or none: without them the options are numbered "1", "2", \u2026 in order.`
|
|
24668
24712
|
),
|
|
24669
|
-
|
|
24670
|
-
hint
|
|
24713
|
+
// SHORT CHOICES (owner, 2026-10-07: "options label should be maybe max 50 characters and the hint 200"):
|
|
24714
|
+
// a label is one choice the person reads, or hears on a call; a hint says its consequence in a line.
|
|
24715
|
+
label: external_exports.string().trim().min(1).max(OPTION_LABEL_MAX, `option_label_too_long: an option's label is at most ${OPTION_LABEL_MAX} characters; say the choice, and put its consequence in the hint.`).describe(`The choice, at most ${OPTION_LABEL_MAX} characters, standing on its own.`),
|
|
24716
|
+
hint: external_exports.string().max(OPTION_HINT_MAX, `option_hint_too_long: an option's hint is at most ${OPTION_HINT_MAX} characters.`).describe(`Optional: what choosing it does, at most ${OPTION_HINT_MAX} characters (e.g. 'Reruns test suite', 'Merges to main').`).optional(),
|
|
24671
24717
|
htmlPreview: OptionSchema.shape.html,
|
|
24672
24718
|
imgUrl: OptionSchema.shape.image
|
|
24673
24719
|
}).strict();
|
|
@@ -24699,15 +24745,48 @@ var init_dist = __esm({
|
|
|
24699
24745
|
message: external_exports.string().trim().min(1).describe(`The one point, at most ${ASK_MAX} characters; the context goes in units.`),
|
|
24700
24746
|
goalIds: external_exports.array(external_exports.string().uuid()).min(1).max(10).describe("The Goals it is about, each one that exists: the one it is mainly about first."),
|
|
24701
24747
|
units,
|
|
24748
|
+
// A REPORT KEEPS ITS TAP (#2799, #3260; owner, 2026-10-08, settling #2776: "keep the reports open
|
|
24749
|
+
// until the user acknowledges them"). A report asks nothing, so it opens no Question -- but it
|
|
24750
|
+
// still waits to be acknowledged, and it carries the ONE tap that closes it. Never a decision: the
|
|
24751
|
+
// tap lands as a contribution, the person's own words on the Goal, because there is no Question
|
|
24752
|
+
// for it to answer.
|
|
24753
|
+
//
|
|
24754
|
+
// AND ITS WORDS ARE WRITTEN FOR THIS REPORT (owner, 2026-10-08, Goal 3c12d3c6: "Agent
|
|
24755
|
+
// acknowledgments vary (e.g. 'sounds good, I will test later') instead of always 'got it'"). `Got
|
|
24756
|
+
// it` and `Noted` say only that a card was cleared, where "Sounds good, I'll test later" says the
|
|
24757
|
+
// work was RECEIVED and not verified -- the distinction a session spent an hour recovering from a
|
|
24758
|
+
// call transcript on 2026-10-07, having read "not quite" as a defect report when it meant *not
|
|
24759
|
+
// tested yet*. The words are worth writing because the agent reads them back.
|
|
24760
|
+
//
|
|
24761
|
+
// ONE STRING, NOT A LIST (owner, 2026-10-08 15:47, Goal 31a1e2ac, asked whether an agent should
|
|
24762
|
+
// write up to four of them: "just on the cards, but it should be more like just they should only
|
|
24763
|
+
// get one basically string value that they can enter that replaces got it"). A report has exactly
|
|
24764
|
+
// one way to close, so the only thing an agent writes is what that button SAYS. #3260's 1..6
|
|
24765
|
+
// `options` and this field are one job: the list is deleted, not deprecated (ONE JOB, ONE
|
|
24766
|
+
// MECHANISM). It rides the door as the report's single stored option (`storedOption`,
|
|
24767
|
+
// `apps/api/src/goal/router.ts`), so no reader below the door learns a second shape.
|
|
24768
|
+
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. Left out, the button says "Got it", as every card did.`
|
|
24770
|
+
).optional(),
|
|
24702
24771
|
userExplicitlyRequested
|
|
24703
|
-
}).strict().superRefine((u, ctx) =>
|
|
24772
|
+
}).strict().superRefine((u, ctx) => {
|
|
24773
|
+
refuseOverCaps(u.message, u.units, "message", ctx);
|
|
24774
|
+
if (u.reply && u.reply.length > OPTION_LABEL_MAX) {
|
|
24775
|
+
ctx.addIssue({
|
|
24776
|
+
code: external_exports.ZodIssueCode.custom,
|
|
24777
|
+
path: ["reply"],
|
|
24778
|
+
params: { refusal: "reply_too_long", length: u.reply.length, cap: OPTION_LABEL_MAX },
|
|
24779
|
+
message: `reply_too_long: the reply is ${u.reply.length} characters; the cap is ${OPTION_LABEL_MAX}. It is a button's words, so say it in a breath.`
|
|
24780
|
+
});
|
|
24781
|
+
}
|
|
24782
|
+
});
|
|
24704
24783
|
questionFields = {
|
|
24705
24784
|
question: external_exports.string().trim().min(1).describe(
|
|
24706
24785
|
`ONE question, in at most ${ASK_MAX} characters, and only what is needed to answer it; the context goes in units. Several questions are several objects, one each: an answer settles the one question it was given, so a person who answers the part of a bundled question that interested them settles nothing and is asked again. Separate questions cost nothing: ANY contact for a person already on a call JOINS that call, so several arrive as one call.`
|
|
24707
24786
|
),
|
|
24708
24787
|
goalId: external_exports.string().uuid().describe("The Goal the question is about, which must exist (create it with manage_goals first). Its answer comes back there."),
|
|
24709
24788
|
units,
|
|
24710
|
-
options: external_exports.array(OptionInputSchema).min(1).max(6).optional().describe("The choices, when you have them. Each label stands on its own."),
|
|
24789
|
+
options: external_exports.array(OptionInputSchema).min(1).max(6).optional().describe("The choices, when you have them. Each label stands on its own, at most 50 characters; a hint, at most 200."),
|
|
24711
24790
|
// HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
|
|
24712
24791
|
// brain_prompts.md §1: structured agent questions need no model). The app draws all three.
|
|
24713
24792
|
pickMode: external_exports.enum(["one", "many", "rank"]).optional().describe(
|
|
@@ -24784,6 +24863,8 @@ QUESTIONS: one question per object; several questions are several objects in one
|
|
|
24784
24863
|
|
|
24785
24864
|
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.
|
|
24786
24865
|
|
|
24866
|
+
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. It is what their button says instead of "Got it", and 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. Nothing is owed on it either way, and a report that writes none offers "Got it".
|
|
24867
|
+
|
|
24787
24868
|
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. So when the work is finished, mark the Goal done first (manage_goals), then send one update saying what is done and anything they need to do or check. 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.
|
|
24788
24869
|
|
|
24789
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 get_goal); \`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\`).`;
|
|
@@ -24827,7 +24908,8 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24827
24908
|
}).strict();
|
|
24828
24909
|
changedGoal = external_exports.string().uuid().describe("The Goal to change, as a read shows it.");
|
|
24829
24910
|
goalTitle = external_exports.string().trim().min(1).max(80).describe("The Goal's short display name: one to five words, how a person refers to it out loud.");
|
|
24830
|
-
|
|
24911
|
+
GOAL_OUTCOME_MAX = 500;
|
|
24912
|
+
goalOutcome = external_exports.string().trim().min(1).max(GOAL_OUTCOME_MAX, `outcome_too_long: a Goal's outcome is one paragraph, at most ${GOAL_OUTCOME_MAX} characters; split the rest into child Goals (create with parentGoalId).`).describe(`The desired result, one paragraph of at most ${GOAL_OUTCOME_MAX} characters. More than that is more work: create child Goals under it (parentGoalId). There is no third description field.`);
|
|
24831
24913
|
CreateChange = external_exports.object({
|
|
24832
24914
|
kind: external_exports.literal("create"),
|
|
24833
24915
|
title: goalTitle,
|
|
@@ -24853,7 +24935,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24853
24935
|
goalId: changedGoal,
|
|
24854
24936
|
dependsOnGoalId: external_exports.string().uuid().describe("The Goal it waits on."),
|
|
24855
24937
|
action: external_exports.enum(["start", "complete"]).describe("What waits: starting goalId, or completing it."),
|
|
24856
|
-
reason: external_exports.string().trim().min(1).max(
|
|
24938
|
+
reason: external_exports.string().trim().min(1).max(250).optional().describe("Why, at most 250 characters, as an explanation, never as policy.")
|
|
24857
24939
|
}).strict();
|
|
24858
24940
|
read = { revision: external_exports.number().int().positive().optional() };
|
|
24859
24941
|
GoalChangeSchema = external_exports.discriminatedUnion("kind", [
|
|
@@ -24877,7 +24959,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24877
24959
|
ManageGoalsToolSchema = external_exports.object({
|
|
24878
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.")
|
|
24879
24961
|
}).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
|
|
24880
|
-
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; 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_goal) 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.";
|
|
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 (get_goal) 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.";
|
|
24881
24963
|
GetGoalSchema = external_exports.object({
|
|
24882
24964
|
goalId: external_exports.string().uuid(),
|
|
24883
24965
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
@@ -24895,7 +24977,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24895
24977
|
goalId: external_exports.string().uuid().optional(),
|
|
24896
24978
|
limit: external_exports.number().int().min(1).max(20).optional()
|
|
24897
24979
|
}).strict();
|
|
24898
|
-
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
|
|
24980
|
+
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.";
|
|
24899
24981
|
CheckActivitySchema = external_exports.object({}).strict();
|
|
24900
24982
|
FEEDBACK_TEXT_MAX = 5e4;
|
|
24901
24983
|
SendFeedbackSchema = external_exports.object({
|
|
@@ -24958,6 +25040,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
24958
25040
|
external_exports.object({
|
|
24959
25041
|
kind: external_exports.literal("create"),
|
|
24960
25042
|
text: external_exports.string().min(1),
|
|
25043
|
+
summary: external_exports.string().optional(),
|
|
24961
25044
|
answererId: participant,
|
|
24962
25045
|
goal: ResultRefSchema.optional(),
|
|
24963
25046
|
blocks: external_exports.array(external_exports.object({ question: ResultRefSchema }).strict()),
|
|
@@ -25008,12 +25091,12 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25008
25091
|
}).strict();
|
|
25009
25092
|
BrainResultSchema = external_exports.object({
|
|
25010
25093
|
messages: external_exports.array(BrainMessageSchema),
|
|
25011
|
-
entries: external_exports.array(external_exports.object({ key, sources: external_exports.array(SourceSchema).min(1), goals: external_exports.array(ResultRefSchema) }).strict()),
|
|
25094
|
+
entries: external_exports.array(external_exports.object({ key, sources: external_exports.array(SourceSchema).min(1), goals: external_exports.array(ResultRefSchema), summary: external_exports.string().optional() }).strict()),
|
|
25012
25095
|
questions: external_exports.array(external_exports.object({ key, entryKeys: external_exports.array(key), change: QuestionChangeSchema }).strict()),
|
|
25013
25096
|
answers: external_exports.array(external_exports.object({
|
|
25014
25097
|
question: ResultRefSchema,
|
|
25015
25098
|
entryKeys: external_exports.array(key).min(1),
|
|
25016
|
-
summary: external_exports.string()
|
|
25099
|
+
summary: external_exports.string(),
|
|
25017
25100
|
selectedOptionIds: external_exports.array(external_exports.string().min(1)).optional()
|
|
25018
25101
|
}).strict()),
|
|
25019
25102
|
goals: external_exports.array(external_exports.object({ key, entryKeys: external_exports.array(key), change: BrainGoalChangeSchema }).strict()),
|
|
@@ -25058,12 +25141,15 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25058
25141
|
messages: external_exports.array(external_exports.object({ key: str2, to: str2, text: str2, about: strs2, blocks: str2 }).strict()),
|
|
25059
25142
|
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
25060
25143
|
then: external_exports.enum(["listen", "hold", "end"]),
|
|
25061
|
-
/**
|
|
25062
|
-
|
|
25063
|
-
|
|
25144
|
+
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
25145
|
+
* IDs, the line handles, how they replied in under ten words (keeping any condition), and `defer`:
|
|
25146
|
+
* "" when their words settle the item, else what a reply that puts it off waits for -- "call" (the
|
|
25147
|
+
* end of this call), a question's handle (that question's answer) or an ISO time. */
|
|
25148
|
+
replies: external_exports.array(external_exports.object({ item: str2, options: strs2, lines: strs2, summary: str2, defer: str2 }).strict()),
|
|
25149
|
+
/** The line handles that tell the talker how to run this call; they last until it ends and reach no agent. */
|
|
25064
25150
|
instruction: strs2,
|
|
25065
|
-
/** The line handles
|
|
25066
|
-
*
|
|
25151
|
+
/** The line handles the filer acts on, and the only ones it reads (3e): new work, a change to existing
|
|
25152
|
+
* work, feedback on Paigy, anything said to an agent, and any rule meant to outlast the call. */
|
|
25067
25153
|
file: strs2,
|
|
25068
25154
|
/** Evidence to fetch for a second round: a search, an item's full text, or an agent by name. */
|
|
25069
25155
|
need: external_exports.object({ kind: external_exports.enum(["search", "item", "agent", ""]), text: str2 }).strict(),
|
|
@@ -25076,8 +25162,9 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25076
25162
|
str3 = external_exports.string();
|
|
25077
25163
|
strs3 = external_exports.array(external_exports.string());
|
|
25078
25164
|
FilerReplySchema = external_exports.object({
|
|
25079
|
-
/** Which Goals each new line goes on: a line's handle
|
|
25080
|
-
|
|
25165
|
+
/** Which Goals each new line goes on: a line's handle, Goal handles (or keys of new Goals), and what
|
|
25166
|
+
* the line says in at most ten words. */
|
|
25167
|
+
filed: external_exports.array(external_exports.object({ line: str3, goals: strs3, summary: str3 }).strict()),
|
|
25081
25168
|
goals: external_exports.array(external_exports.object({
|
|
25082
25169
|
op: external_exports.enum(["create", "edit", "complete", "cancel", "reopen", "assign", "defer", "move", "block", "unblock"]),
|
|
25083
25170
|
key: str3,
|
|
@@ -25096,6 +25183,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25096
25183
|
key: str3,
|
|
25097
25184
|
question: str3,
|
|
25098
25185
|
text: str3,
|
|
25186
|
+
summary: str3,
|
|
25099
25187
|
to: str3,
|
|
25100
25188
|
goal: str3,
|
|
25101
25189
|
options: external_exports.array(external_exports.object({ id: str3, label: str3 }).strict()),
|
|
@@ -25109,8 +25197,64 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25109
25197
|
FILER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
25110
25198
|
zodToJsonSchema(FilerReplySchema, { $refStrategy: "none" })
|
|
25111
25199
|
);
|
|
25200
|
+
ExplainerReplySchema = external_exports.object({
|
|
25201
|
+
/** The explanation, as sentences, spoken in order. */
|
|
25202
|
+
say: external_exports.array(external_exports.string()),
|
|
25203
|
+
/** Questions to an agent about gaps in what it read: the agent's handle and the question. */
|
|
25204
|
+
followUps: external_exports.array(external_exports.object({ to: external_exports.string(), text: external_exports.string() }).strict())
|
|
25205
|
+
}).strict();
|
|
25206
|
+
EXPLAINER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
25207
|
+
zodToJsonSchema(ExplainerReplySchema, { $refStrategy: "none" })
|
|
25208
|
+
);
|
|
25209
|
+
DISTILLER_DEFECTS = [
|
|
25210
|
+
"reasked",
|
|
25211
|
+
// a question put to the person again after they answered it
|
|
25212
|
+
"unsupported",
|
|
25213
|
+
// a fact stated that nothing in the input supports
|
|
25214
|
+
"unsaid",
|
|
25215
|
+
// an agent's answer arrived during the call and was never said
|
|
25216
|
+
"skipped",
|
|
25217
|
+
// an agenda item never put to the person
|
|
25218
|
+
"unsaved",
|
|
25219
|
+
// something the person answered or asked for did not save, was refused or dropped
|
|
25220
|
+
"ignored",
|
|
25221
|
+
// the person asked or said something and was never answered (the critic's ignored_a_question)
|
|
25222
|
+
"promised",
|
|
25223
|
+
// said it did or would do something, and nothing shows it done (the critic's broken_promise)
|
|
25224
|
+
"dropped"
|
|
25225
|
+
// something it had to say never played and was never said later (owner, 2026-10-08, call 65433a9d)
|
|
25226
|
+
];
|
|
25227
|
+
DistillerReplySchema = external_exports.object({
|
|
25228
|
+
defects: external_exports.array(external_exports.object({
|
|
25229
|
+
kind: external_exports.enum(DISTILLER_DEFECTS),
|
|
25230
|
+
/** The handles of the lines it shows in. */
|
|
25231
|
+
lines: external_exports.array(external_exports.string()),
|
|
25232
|
+
/** The handle of the record it concerns, or "". */
|
|
25233
|
+
about: external_exports.string(),
|
|
25234
|
+
/** One plain sentence, in words, never handles: it is what the feedback row keeps. */
|
|
25235
|
+
why: external_exports.string()
|
|
25236
|
+
}).strict())
|
|
25237
|
+
}).strict();
|
|
25238
|
+
DISTILLER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
25239
|
+
zodToJsonSchema(DistillerReplySchema, { $refStrategy: "none" })
|
|
25240
|
+
);
|
|
25241
|
+
TIDY_AUDIENCES = ["talker", "filer", "agents"];
|
|
25242
|
+
TidyReplySchema = external_exports.object({
|
|
25243
|
+
lessons: external_exports.array(external_exports.object({
|
|
25244
|
+
lesson: external_exports.string(),
|
|
25245
|
+
op: external_exports.enum(["keep", "revise", "withdraw"]),
|
|
25246
|
+
text: external_exports.string(),
|
|
25247
|
+
for: external_exports.enum([...TIDY_AUDIENCES, ""]),
|
|
25248
|
+
into: external_exports.string()
|
|
25249
|
+
}).strict())
|
|
25250
|
+
}).strict();
|
|
25251
|
+
TIDY_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
25252
|
+
zodToJsonSchema(TidyReplySchema, { $refStrategy: "none" })
|
|
25253
|
+
);
|
|
25112
25254
|
LIVE_MS = 3 * 6e4;
|
|
25113
25255
|
WORKING_MS = 60 * 6e4;
|
|
25256
|
+
WORKING_TIER_MS = 2 * 60 * 6e4;
|
|
25257
|
+
RECENT_TIER_MS = 24 * 60 * 6e4;
|
|
25114
25258
|
ContextSchema = external_exports.object({
|
|
25115
25259
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
25116
25260
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -25455,6 +25599,10 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25455
25599
|
* so "who said that" survives the call, and it rides PER TURN because a coalesced call
|
|
25456
25600
|
* speaks for several agents — the turn is the only place that knows which. */
|
|
25457
25601
|
agentId: external_exports.string().optional(),
|
|
25602
|
+
/** ITS QUESTION IS ON THE TABLE (#3292): a talker line about a question the person owes. The walk tells
|
|
25603
|
+
* the phone (`asking`, so the agenda goes gold and its options show) without treating the line as a
|
|
25604
|
+
* question turn — the talker owns re-asking and the waits, so `question` stays null. */
|
|
25605
|
+
onTable: external_exports.boolean().optional(),
|
|
25458
25606
|
select: SelectShapeSchema.optional(),
|
|
25459
25607
|
options: external_exports.array(OptionSchema.omit({ id: true })).optional(),
|
|
25460
25608
|
/* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
|
|
@@ -25529,9 +25677,16 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25529
25677
|
/** ITS OPTIONS, WHEN THERE IS SOMETHING TO SEE (owner, 2026-09-25: "Yes, add it"): the options
|
|
25530
25678
|
* its need offers, exactly as its own card carries them, present only when one of them has a
|
|
25531
25679
|
* preview (`html` or `image`). The call screen opens them from the agenda row, so a preview is
|
|
25532
|
-
* never re-sent as a second card to be seen mid-call. Words-only options are
|
|
25533
|
-
|
|
25534
|
-
|
|
25680
|
+
* never re-sent as a second card to be seen mid-call. Words-only options are `choices`. */
|
|
25681
|
+
options: external_exports.array(OptionSchema).optional(),
|
|
25682
|
+
/** ITS OPTIONS WHEN THEY ARE WORDS (owner, 2026-10-02, on a call: "whenever there are pre-made
|
|
25683
|
+
* options … now for a checklist, we should also show them on the screen"; design C, 2026-10-08):
|
|
25684
|
+
* numbered chips above the call's controls, so a checklist is never only something read aloud. A
|
|
25685
|
+
* field of its own so `options` keeps meaning "previews": a phone on an older bundle opens the
|
|
25686
|
+
* preview layout for any `options`, and must see exactly what it did. */
|
|
25687
|
+
choices: external_exports.array(OptionSchema).optional(),
|
|
25688
|
+
/** HOW MANY MAY BE PICKED, beside its `choices`: one, any (`many`) or an order (`rank`). */
|
|
25689
|
+
select: external_exports.enum(["one", "many", "rank"]).optional()
|
|
25535
25690
|
})).optional(),
|
|
25536
25691
|
visuals: external_exports.array(VisualSchema).optional(),
|
|
25537
25692
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
@@ -25791,7 +25946,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25791
25946
|
* desktop app while open. Null = never seen; stale = offline. */
|
|
25792
25947
|
lastSeenAt: external_exports.string().datetime().nullable().optional(),
|
|
25793
25948
|
/** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
|
|
25794
|
-
* its Goals —
|
|
25949
|
+
* its Goals — wrote on one or recorded an operation (`tokens.last_worked_at`). Within
|
|
25795
25950
|
* `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
|
|
25796
25951
|
lastWorkedAt: external_exports.string().datetime().nullable().optional(),
|
|
25797
25952
|
/** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
|
|
@@ -25968,7 +26123,11 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25968
26123
|
from: external_exports.enum(["person", "agent"]),
|
|
25969
26124
|
said: external_exports.string(),
|
|
25970
26125
|
at: external_exports.string(),
|
|
25971
|
-
entryId: external_exports.string()
|
|
26126
|
+
entryId: external_exports.string(),
|
|
26127
|
+
/** ITS SUMMARY IN AT MOST TEN WORDS (owner, 2026-10-08: "the same switch to the more summarized sources
|
|
26128
|
+
* at the work tab top level screen too"): what the talker or filer wrote on the Entry; absent when
|
|
26129
|
+
* none was. The Work row reads it before `said`. */
|
|
26130
|
+
summary: external_exports.string().optional()
|
|
25972
26131
|
}).nullable().optional(),
|
|
25973
26132
|
/** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
|
|
25974
26133
|
* the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
|
|
@@ -25989,7 +26148,7 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
25989
26148
|
*
|
|
25990
26149
|
* IT IS THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK. Nothing here reasons about which
|
|
25991
26150
|
* writes change which field — the comparison is over the bytes the phone is holding, so a fact
|
|
25992
|
-
* the row shows that no `updated_at` moves for (
|
|
26151
|
+
* the row shows that no `updated_at` moves for (`active` lapsing, a dependency's state, a
|
|
25993
26152
|
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
25994
26153
|
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
25995
26154
|
rev: external_exports.string().optional()
|
|
@@ -26305,17 +26464,43 @@ RECEIVING: a contact that sends nothing returns \`events\`, a limited batch of w
|
|
|
26305
26464
|
title: external_exports.string(),
|
|
26306
26465
|
owner: external_exports.string(),
|
|
26307
26466
|
state: external_exports.string(),
|
|
26467
|
+
/** Whether the call put it to the person. An item it carried and never said has no row on the
|
|
26468
|
+
* review (owner, 2026-10-08: "why do we get the third one when it didn't happen in the call?"). */
|
|
26469
|
+
presented: external_exports.boolean(),
|
|
26308
26470
|
questions: external_exports.array(external_exports.object({ id: external_exports.string().uuid(), state: external_exports.string(), title: external_exports.string() })),
|
|
26309
|
-
/** `words` is always what they SAID, verbatim — the record, never replaced. `
|
|
26310
|
-
*
|
|
26311
|
-
*
|
|
26312
|
-
*
|
|
26313
|
-
* anything that is not an answer. */
|
|
26471
|
+
/** `words` is always what they SAID, verbatim — the record, never replaced. `summary` is what
|
|
26472
|
+
* the line says in ten words (owner, 2026-10-07): the filer's, or the talker's for the answer it
|
|
26473
|
+
* gave, so the row scans like a chosen option and their own words stay under it. Absent when
|
|
26474
|
+
* neither wrote one. */
|
|
26314
26475
|
/** `about` is the request the line answered (its question), null for words that answered none —
|
|
26315
26476
|
* the key the screen groups on, so one question is one row however many times it was answered. */
|
|
26316
|
-
|
|
26477
|
+
/** `reply` is the reply the line makes up (an answer, an ok, a deferral), so one reply is one row
|
|
26478
|
+
* however many lines it cites; `at` is the call lines it came from, so a filed line an answer already
|
|
26479
|
+
* said is not drawn again (owner, 2026-10-07: "fix them all"); `replyKind` says which replies are answers. */
|
|
26480
|
+
lines: external_exports.array(external_exports.object({
|
|
26481
|
+
entryId: external_exports.string().uuid(),
|
|
26482
|
+
words: external_exports.string(),
|
|
26483
|
+
summary: external_exports.string().optional(),
|
|
26484
|
+
about: external_exports.string().nullable().optional(),
|
|
26485
|
+
reply: external_exports.string().nullable().optional(),
|
|
26486
|
+
replyKind: external_exports.string().nullable().optional(),
|
|
26487
|
+
at: external_exports.array(external_exports.number()).optional()
|
|
26488
|
+
}))
|
|
26317
26489
|
})),
|
|
26318
26490
|
unfiled: external_exports.array(external_exports.object({ lineId: external_exports.string().uuid(), words: external_exports.string(), atMs: external_exports.number() })),
|
|
26491
|
+
/** THE PUBLIC TRANSCRIPT — every line said on the call, in the order it was said, and WHO SAID IT
|
|
26492
|
+
* (owner, 2026-10-08: "the public view should only have the lines and who said them, and that's
|
|
26493
|
+
* it"; `docs/brain/phase-three/modular.md` build step 3). `speaker` is the role: "user" is the
|
|
26494
|
+
* person, "agent" an agent speaking, named in `agent`. The timeline's action lines are ours, not
|
|
26495
|
+
* theirs, and are not here; nor is a sealed line, which the server has no words for. It stands
|
|
26496
|
+
* BESIDE `topics` and `unfiled` — what the call did with the words — never instead of them. */
|
|
26497
|
+
said: external_exports.array(external_exports.object({
|
|
26498
|
+
lineId: external_exports.string().uuid(),
|
|
26499
|
+
speaker: external_exports.enum(["user", "agent"]),
|
|
26500
|
+
agent: external_exports.object({ id: external_exports.string().nullable(), name: external_exports.string().nullable() }).nullable(),
|
|
26501
|
+
words: external_exports.string(),
|
|
26502
|
+
atMs: external_exports.number()
|
|
26503
|
+
})),
|
|
26319
26504
|
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
26320
26505
|
});
|
|
26321
26506
|
TOKEN_PATH = join22(homedir22(), ".paigy", "token.json");
|
|
@@ -32007,7 +32192,8 @@ function startSession(opts) {
|
|
|
32007
32192
|
if (!existsSync3(cwd)) {
|
|
32008
32193
|
queueMicrotask(() => opts.onEvent({ kind: "error", message: `workspace does not exist: ${cwd}` }));
|
|
32009
32194
|
}
|
|
32010
|
-
|
|
32195
|
+
let codexConfig = opts.harness === "codex" ? JSON.parse(process.env.CODEX_CONFIG || "{}") : null;
|
|
32196
|
+
if (opts.model && opts.harness === "codex") codexConfig = { ...codexConfig, model: opts.model };
|
|
32011
32197
|
if (codexConfig) {
|
|
32012
32198
|
const key3 = "mcp_servers.paigy.env_vars";
|
|
32013
32199
|
const configPath = join5(process.env.CODEX_HOME || join5(homedir4(), ".codex"), "config.toml");
|
|
@@ -32033,6 +32219,8 @@ function startSession(opts) {
|
|
|
32033
32219
|
PATH: [process.env.PATH, ...probeDirs()].filter(Boolean).join(delimiter2),
|
|
32034
32220
|
...opts.token ? { PAIGY_TOKEN: opts.token } : {},
|
|
32035
32221
|
...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 }) } : {},
|
|
32036
32224
|
...codexConfig ? { CODEX_CONFIG: JSON.stringify(codexConfig) } : {},
|
|
32037
32225
|
...opts.session ?? opts.resume ? { PAIGY_SESSION_ID: opts.session ?? opts.resume } : {},
|
|
32038
32226
|
// A PUMP DRIVES THIS PROCESS (2026-09-22, #2265): every run `runHarness` starts has the
|
|
@@ -33409,12 +33597,16 @@ init_zod();
|
|
|
33409
33597
|
init_zod();
|
|
33410
33598
|
init_zod();
|
|
33411
33599
|
init_zod();
|
|
33600
|
+
init_zod();
|
|
33601
|
+
init_zod();
|
|
33412
33602
|
var OPTIONS_MIN2 = 2;
|
|
33413
33603
|
var OPTIONS_MAX2 = 6;
|
|
33604
|
+
var OPTION_LABEL_MAX2 = 50;
|
|
33605
|
+
var OPTION_HINT_MAX2 = 200;
|
|
33414
33606
|
var OptionSchema2 = external_exports.object({
|
|
33415
33607
|
id: external_exports.string(),
|
|
33416
33608
|
label: external_exports.string(),
|
|
33417
|
-
hint: external_exports.string().max(500).describe(
|
|
33609
|
+
hint: external_exports.string().max(500).describe(`Optional: what choosing it does, at most ${OPTION_HINT_MAX2} characters (e.g. 'Reruns test suite', 'Merges to main').`).optional(),
|
|
33418
33610
|
// .describe() flows into the MCP contact JSON schema (zodToJsonSchema), so
|
|
33419
33611
|
// the constraints below are what an agent reads when deciding to use these.
|
|
33420
33612
|
html: external_exports.string().max(16384).describe(
|
|
@@ -33428,8 +33620,10 @@ var OptionInputSchema2 = external_exports.object({
|
|
|
33428
33620
|
id: external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9_.:-]+$/).optional().describe(
|
|
33429
33621
|
`Optional: this option's own id, which answers name it by (selectedOptionIds). Give every option one, or none: without them the options are numbered "1", "2", \u2026 in order.`
|
|
33430
33622
|
),
|
|
33431
|
-
|
|
33432
|
-
hint
|
|
33623
|
+
// SHORT CHOICES (owner, 2026-10-07: "options label should be maybe max 50 characters and the hint 200"):
|
|
33624
|
+
// a label is one choice the person reads, or hears on a call; a hint says its consequence in a line.
|
|
33625
|
+
label: external_exports.string().trim().min(1).max(OPTION_LABEL_MAX2, `option_label_too_long: an option's label is at most ${OPTION_LABEL_MAX2} characters; say the choice, and put its consequence in the hint.`).describe(`The choice, at most ${OPTION_LABEL_MAX2} characters, standing on its own.`),
|
|
33626
|
+
hint: external_exports.string().max(OPTION_HINT_MAX2, `option_hint_too_long: an option's hint is at most ${OPTION_HINT_MAX2} characters.`).describe(`Optional: what choosing it does, at most ${OPTION_HINT_MAX2} characters (e.g. 'Reruns test suite', 'Merges to main').`).optional(),
|
|
33433
33627
|
htmlPreview: OptionSchema2.shape.html,
|
|
33434
33628
|
imgUrl: OptionSchema2.shape.image
|
|
33435
33629
|
}).strict();
|
|
@@ -33531,15 +33725,48 @@ var UpdateInputSchema2 = external_exports.object({
|
|
|
33531
33725
|
message: external_exports.string().trim().min(1).describe(`The one point, at most ${ASK_MAX2} characters; the context goes in units.`),
|
|
33532
33726
|
goalIds: external_exports.array(external_exports.string().uuid()).min(1).max(10).describe("The Goals it is about, each one that exists: the one it is mainly about first."),
|
|
33533
33727
|
units: units2,
|
|
33728
|
+
// A REPORT KEEPS ITS TAP (#2799, #3260; owner, 2026-10-08, settling #2776: "keep the reports open
|
|
33729
|
+
// until the user acknowledges them"). A report asks nothing, so it opens no Question -- but it
|
|
33730
|
+
// still waits to be acknowledged, and it carries the ONE tap that closes it. Never a decision: the
|
|
33731
|
+
// tap lands as a contribution, the person's own words on the Goal, because there is no Question
|
|
33732
|
+
// for it to answer.
|
|
33733
|
+
//
|
|
33734
|
+
// AND ITS WORDS ARE WRITTEN FOR THIS REPORT (owner, 2026-10-08, Goal 3c12d3c6: "Agent
|
|
33735
|
+
// acknowledgments vary (e.g. 'sounds good, I will test later') instead of always 'got it'"). `Got
|
|
33736
|
+
// it` and `Noted` say only that a card was cleared, where "Sounds good, I'll test later" says the
|
|
33737
|
+
// work was RECEIVED and not verified -- the distinction a session spent an hour recovering from a
|
|
33738
|
+
// call transcript on 2026-10-07, having read "not quite" as a defect report when it meant *not
|
|
33739
|
+
// tested yet*. The words are worth writing because the agent reads them back.
|
|
33740
|
+
//
|
|
33741
|
+
// ONE STRING, NOT A LIST (owner, 2026-10-08 15:47, Goal 31a1e2ac, asked whether an agent should
|
|
33742
|
+
// write up to four of them: "just on the cards, but it should be more like just they should only
|
|
33743
|
+
// get one basically string value that they can enter that replaces got it"). A report has exactly
|
|
33744
|
+
// one way to close, so the only thing an agent writes is what that button SAYS. #3260's 1..6
|
|
33745
|
+
// `options` and this field are one job: the list is deleted, not deprecated (ONE JOB, ONE
|
|
33746
|
+
// MECHANISM). It rides the door as the report's single stored option (`storedOption`,
|
|
33747
|
+
// `apps/api/src/goal/router.ts`), so no reader below the door learns a second shape.
|
|
33748
|
+
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. Left out, the button says "Got it", as every card did.`
|
|
33750
|
+
).optional(),
|
|
33534
33751
|
userExplicitlyRequested: userExplicitlyRequested2
|
|
33535
|
-
}).strict().superRefine((u, ctx) =>
|
|
33752
|
+
}).strict().superRefine((u, ctx) => {
|
|
33753
|
+
refuseOverCaps2(u.message, u.units, "message", ctx);
|
|
33754
|
+
if (u.reply && u.reply.length > OPTION_LABEL_MAX2) {
|
|
33755
|
+
ctx.addIssue({
|
|
33756
|
+
code: external_exports.ZodIssueCode.custom,
|
|
33757
|
+
path: ["reply"],
|
|
33758
|
+
params: { refusal: "reply_too_long", length: u.reply.length, cap: OPTION_LABEL_MAX2 },
|
|
33759
|
+
message: `reply_too_long: the reply is ${u.reply.length} characters; the cap is ${OPTION_LABEL_MAX2}. It is a button's words, so say it in a breath.`
|
|
33760
|
+
});
|
|
33761
|
+
}
|
|
33762
|
+
});
|
|
33536
33763
|
var questionFields2 = {
|
|
33537
33764
|
question: external_exports.string().trim().min(1).describe(
|
|
33538
33765
|
`ONE question, in at most ${ASK_MAX2} characters, and only what is needed to answer it; the context goes in units. Several questions are several objects, one each: an answer settles the one question it was given, so a person who answers the part of a bundled question that interested them settles nothing and is asked again. Separate questions cost nothing: ANY contact for a person already on a call JOINS that call, so several arrive as one call.`
|
|
33539
33766
|
),
|
|
33540
33767
|
goalId: external_exports.string().uuid().describe("The Goal the question is about, which must exist (create it with manage_goals first). Its answer comes back there."),
|
|
33541
33768
|
units: units2,
|
|
33542
|
-
options: external_exports.array(OptionInputSchema2).min(1).max(6).optional().describe("The choices, when you have them. Each label stands on its own."),
|
|
33769
|
+
options: external_exports.array(OptionInputSchema2).min(1).max(6).optional().describe("The choices, when you have them. Each label stands on its own, at most 50 characters; a hint, at most 200."),
|
|
33543
33770
|
// HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
|
|
33544
33771
|
// brain_prompts.md §1: structured agent questions need no model). The app draws all three.
|
|
33545
33772
|
pickMode: external_exports.enum(["one", "many", "rank"]).optional().describe(
|
|
@@ -33616,6 +33843,8 @@ QUESTIONS: one question per object; several questions are several objects in one
|
|
|
33616
33843
|
|
|
33617
33844
|
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.
|
|
33618
33845
|
|
|
33846
|
+
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. It is what their button says instead of "Got it", and 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. Nothing is owed on it either way, and a report that writes none offers "Got it".
|
|
33847
|
+
|
|
33619
33848
|
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. So when the work is finished, mark the Goal done first (manage_goals), then send one update saying what is done and anything they need to do or check. 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.
|
|
33620
33849
|
|
|
33621
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 get_goal); \`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\`).`;
|
|
@@ -33659,7 +33888,8 @@ var UpdateGoalSchema2 = external_exports.object({
|
|
|
33659
33888
|
}).strict();
|
|
33660
33889
|
var changedGoal2 = external_exports.string().uuid().describe("The Goal to change, as a read shows it.");
|
|
33661
33890
|
var goalTitle2 = external_exports.string().trim().min(1).max(80).describe("The Goal's short display name: one to five words, how a person refers to it out loud.");
|
|
33662
|
-
var
|
|
33891
|
+
var GOAL_OUTCOME_MAX2 = 500;
|
|
33892
|
+
var goalOutcome2 = external_exports.string().trim().min(1).max(GOAL_OUTCOME_MAX2, `outcome_too_long: a Goal's outcome is one paragraph, at most ${GOAL_OUTCOME_MAX2} characters; split the rest into child Goals (create with parentGoalId).`).describe(`The desired result, one paragraph of at most ${GOAL_OUTCOME_MAX2} characters. More than that is more work: create child Goals under it (parentGoalId). There is no third description field.`);
|
|
33663
33893
|
var CreateChange2 = external_exports.object({
|
|
33664
33894
|
kind: external_exports.literal("create"),
|
|
33665
33895
|
title: goalTitle2,
|
|
@@ -33685,7 +33915,7 @@ var DependencyChange2 = external_exports.object({
|
|
|
33685
33915
|
goalId: changedGoal2,
|
|
33686
33916
|
dependsOnGoalId: external_exports.string().uuid().describe("The Goal it waits on."),
|
|
33687
33917
|
action: external_exports.enum(["start", "complete"]).describe("What waits: starting goalId, or completing it."),
|
|
33688
|
-
reason: external_exports.string().trim().min(1).max(
|
|
33918
|
+
reason: external_exports.string().trim().min(1).max(250).optional().describe("Why, at most 250 characters, as an explanation, never as policy.")
|
|
33689
33919
|
}).strict();
|
|
33690
33920
|
var read2 = { revision: external_exports.number().int().positive().optional() };
|
|
33691
33921
|
var GoalChangeSchema2 = external_exports.discriminatedUnion("kind", [
|
|
@@ -33709,7 +33939,7 @@ var ManageGoalsSchema2 = external_exports.object({
|
|
|
33709
33939
|
var ManageGoalsToolSchema2 = external_exports.object({
|
|
33710
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.")
|
|
33711
33941
|
}).strict().superRefine((v, ctx) => editNames2(v.changes, ctx));
|
|
33712
|
-
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; 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_goal) 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.";
|
|
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 (get_goal) 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.";
|
|
33713
33943
|
var GetGoalSchema2 = external_exports.object({
|
|
33714
33944
|
goalId: external_exports.string().uuid(),
|
|
33715
33945
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
@@ -33727,7 +33957,7 @@ var SearchToolSchema2 = external_exports.object({
|
|
|
33727
33957
|
goalId: external_exports.string().uuid().optional(),
|
|
33728
33958
|
limit: external_exports.number().int().min(1).max(20).optional()
|
|
33729
33959
|
}).strict();
|
|
33730
|
-
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
|
|
33960
|
+
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.";
|
|
33731
33961
|
var CheckActivitySchema2 = external_exports.object({}).strict();
|
|
33732
33962
|
var FEEDBACK_TEXT_MAX2 = 5e4;
|
|
33733
33963
|
var SendFeedbackSchema2 = external_exports.object({
|
|
@@ -33790,6 +34020,7 @@ var QuestionChangeSchema2 = external_exports.union([
|
|
|
33790
34020
|
external_exports.object({
|
|
33791
34021
|
kind: external_exports.literal("create"),
|
|
33792
34022
|
text: external_exports.string().min(1),
|
|
34023
|
+
summary: external_exports.string().optional(),
|
|
33793
34024
|
answererId: participant2,
|
|
33794
34025
|
goal: ResultRefSchema2.optional(),
|
|
33795
34026
|
blocks: external_exports.array(external_exports.object({ question: ResultRefSchema2 }).strict()),
|
|
@@ -33840,12 +34071,12 @@ var BrainSearchSchema2 = external_exports.object({
|
|
|
33840
34071
|
}).strict();
|
|
33841
34072
|
var BrainResultSchema2 = external_exports.object({
|
|
33842
34073
|
messages: external_exports.array(BrainMessageSchema2),
|
|
33843
|
-
entries: external_exports.array(external_exports.object({ key: key2, sources: external_exports.array(SourceSchema2).min(1), goals: external_exports.array(ResultRefSchema2) }).strict()),
|
|
34074
|
+
entries: external_exports.array(external_exports.object({ key: key2, sources: external_exports.array(SourceSchema2).min(1), goals: external_exports.array(ResultRefSchema2), summary: external_exports.string().optional() }).strict()),
|
|
33844
34075
|
questions: external_exports.array(external_exports.object({ key: key2, entryKeys: external_exports.array(key2), change: QuestionChangeSchema2 }).strict()),
|
|
33845
34076
|
answers: external_exports.array(external_exports.object({
|
|
33846
34077
|
question: ResultRefSchema2,
|
|
33847
34078
|
entryKeys: external_exports.array(key2).min(1),
|
|
33848
|
-
summary: external_exports.string()
|
|
34079
|
+
summary: external_exports.string(),
|
|
33849
34080
|
selectedOptionIds: external_exports.array(external_exports.string().min(1)).optional()
|
|
33850
34081
|
}).strict()),
|
|
33851
34082
|
goals: external_exports.array(external_exports.object({ key: key2, entryKeys: external_exports.array(key2), change: BrainGoalChangeSchema2 }).strict()),
|
|
@@ -33890,12 +34121,15 @@ var TalkerReplySchema2 = external_exports.object({
|
|
|
33890
34121
|
messages: external_exports.array(external_exports.object({ key: str22, to: str22, text: str22, about: strs22, blocks: str22 }).strict()),
|
|
33891
34122
|
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
33892
34123
|
then: external_exports.enum(["listen", "hold", "end"]),
|
|
33893
|
-
/**
|
|
33894
|
-
|
|
33895
|
-
|
|
34124
|
+
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
34125
|
+
* IDs, the line handles, how they replied in under ten words (keeping any condition), and `defer`:
|
|
34126
|
+
* "" when their words settle the item, else what a reply that puts it off waits for -- "call" (the
|
|
34127
|
+
* end of this call), a question's handle (that question's answer) or an ISO time. */
|
|
34128
|
+
replies: external_exports.array(external_exports.object({ item: str22, options: strs22, lines: strs22, summary: str22, defer: str22 }).strict()),
|
|
34129
|
+
/** The line handles that tell the talker how to run this call; they last until it ends and reach no agent. */
|
|
33896
34130
|
instruction: strs22,
|
|
33897
|
-
/** The line handles
|
|
33898
|
-
*
|
|
34131
|
+
/** The line handles the filer acts on, and the only ones it reads (3e): new work, a change to existing
|
|
34132
|
+
* work, feedback on Paigy, anything said to an agent, and any rule meant to outlast the call. */
|
|
33899
34133
|
file: strs22,
|
|
33900
34134
|
/** Evidence to fetch for a second round: a search, an item's full text, or an agent by name. */
|
|
33901
34135
|
need: external_exports.object({ kind: external_exports.enum(["search", "item", "agent", ""]), text: str22 }).strict(),
|
|
@@ -33908,8 +34142,9 @@ var TALKER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
|
33908
34142
|
var str32 = external_exports.string();
|
|
33909
34143
|
var strs32 = external_exports.array(external_exports.string());
|
|
33910
34144
|
var FilerReplySchema2 = external_exports.object({
|
|
33911
|
-
/** Which Goals each new line goes on: a line's handle
|
|
33912
|
-
|
|
34145
|
+
/** Which Goals each new line goes on: a line's handle, Goal handles (or keys of new Goals), and what
|
|
34146
|
+
* the line says in at most ten words. */
|
|
34147
|
+
filed: external_exports.array(external_exports.object({ line: str32, goals: strs32, summary: str32 }).strict()),
|
|
33913
34148
|
goals: external_exports.array(external_exports.object({
|
|
33914
34149
|
op: external_exports.enum(["create", "edit", "complete", "cancel", "reopen", "assign", "defer", "move", "block", "unblock"]),
|
|
33915
34150
|
key: str32,
|
|
@@ -33928,6 +34163,7 @@ var FilerReplySchema2 = external_exports.object({
|
|
|
33928
34163
|
key: str32,
|
|
33929
34164
|
question: str32,
|
|
33930
34165
|
text: str32,
|
|
34166
|
+
summary: str32,
|
|
33931
34167
|
to: str32,
|
|
33932
34168
|
goal: str32,
|
|
33933
34169
|
options: external_exports.array(external_exports.object({ id: str32, label: str32 }).strict()),
|
|
@@ -33941,6 +34177,60 @@ var FilerReplySchema2 = external_exports.object({
|
|
|
33941
34177
|
var FILER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
33942
34178
|
zodToJsonSchema2(FilerReplySchema2, { $refStrategy: "none" })
|
|
33943
34179
|
);
|
|
34180
|
+
var ExplainerReplySchema2 = external_exports.object({
|
|
34181
|
+
/** The explanation, as sentences, spoken in order. */
|
|
34182
|
+
say: external_exports.array(external_exports.string()),
|
|
34183
|
+
/** Questions to an agent about gaps in what it read: the agent's handle and the question. */
|
|
34184
|
+
followUps: external_exports.array(external_exports.object({ to: external_exports.string(), text: external_exports.string() }).strict())
|
|
34185
|
+
}).strict();
|
|
34186
|
+
var EXPLAINER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
34187
|
+
zodToJsonSchema2(ExplainerReplySchema2, { $refStrategy: "none" })
|
|
34188
|
+
);
|
|
34189
|
+
var DISTILLER_DEFECTS2 = [
|
|
34190
|
+
"reasked",
|
|
34191
|
+
// a question put to the person again after they answered it
|
|
34192
|
+
"unsupported",
|
|
34193
|
+
// a fact stated that nothing in the input supports
|
|
34194
|
+
"unsaid",
|
|
34195
|
+
// an agent's answer arrived during the call and was never said
|
|
34196
|
+
"skipped",
|
|
34197
|
+
// an agenda item never put to the person
|
|
34198
|
+
"unsaved",
|
|
34199
|
+
// something the person answered or asked for did not save, was refused or dropped
|
|
34200
|
+
"ignored",
|
|
34201
|
+
// the person asked or said something and was never answered (the critic's ignored_a_question)
|
|
34202
|
+
"promised",
|
|
34203
|
+
// said it did or would do something, and nothing shows it done (the critic's broken_promise)
|
|
34204
|
+
"dropped"
|
|
34205
|
+
// something it had to say never played and was never said later (owner, 2026-10-08, call 65433a9d)
|
|
34206
|
+
];
|
|
34207
|
+
var DistillerReplySchema2 = external_exports.object({
|
|
34208
|
+
defects: external_exports.array(external_exports.object({
|
|
34209
|
+
kind: external_exports.enum(DISTILLER_DEFECTS2),
|
|
34210
|
+
/** The handles of the lines it shows in. */
|
|
34211
|
+
lines: external_exports.array(external_exports.string()),
|
|
34212
|
+
/** The handle of the record it concerns, or "". */
|
|
34213
|
+
about: external_exports.string(),
|
|
34214
|
+
/** One plain sentence, in words, never handles: it is what the feedback row keeps. */
|
|
34215
|
+
why: external_exports.string()
|
|
34216
|
+
}).strict())
|
|
34217
|
+
}).strict();
|
|
34218
|
+
var DISTILLER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
34219
|
+
zodToJsonSchema2(DistillerReplySchema2, { $refStrategy: "none" })
|
|
34220
|
+
);
|
|
34221
|
+
var TIDY_AUDIENCES2 = ["talker", "filer", "agents"];
|
|
34222
|
+
var TidyReplySchema2 = external_exports.object({
|
|
34223
|
+
lessons: external_exports.array(external_exports.object({
|
|
34224
|
+
lesson: external_exports.string(),
|
|
34225
|
+
op: external_exports.enum(["keep", "revise", "withdraw"]),
|
|
34226
|
+
text: external_exports.string(),
|
|
34227
|
+
for: external_exports.enum([...TIDY_AUDIENCES2, ""]),
|
|
34228
|
+
into: external_exports.string()
|
|
34229
|
+
}).strict())
|
|
34230
|
+
}).strict();
|
|
34231
|
+
var TIDY_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
34232
|
+
zodToJsonSchema2(TidyReplySchema2, { $refStrategy: "none" })
|
|
34233
|
+
);
|
|
33944
34234
|
function entryWords(entry) {
|
|
33945
34235
|
const content = entry.content;
|
|
33946
34236
|
if (content && "sealed" in content) return "";
|
|
@@ -33960,6 +34250,8 @@ function entryWords(entry) {
|
|
|
33960
34250
|
}
|
|
33961
34251
|
var LIVE_MS2 = 3 * 6e4;
|
|
33962
34252
|
var WORKING_MS2 = 60 * 6e4;
|
|
34253
|
+
var WORKING_TIER_MS2 = 2 * 60 * 6e4;
|
|
34254
|
+
var RECENT_TIER_MS2 = 24 * 60 * 6e4;
|
|
33963
34255
|
var ContextSchema2 = external_exports.object({
|
|
33964
34256
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
33965
34257
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -34304,6 +34596,10 @@ var AgendaTurnSchema2 = external_exports.object({
|
|
|
34304
34596
|
* so "who said that" survives the call, and it rides PER TURN because a coalesced call
|
|
34305
34597
|
* speaks for several agents — the turn is the only place that knows which. */
|
|
34306
34598
|
agentId: external_exports.string().optional(),
|
|
34599
|
+
/** ITS QUESTION IS ON THE TABLE (#3292): a talker line about a question the person owes. The walk tells
|
|
34600
|
+
* the phone (`asking`, so the agenda goes gold and its options show) without treating the line as a
|
|
34601
|
+
* question turn — the talker owns re-asking and the waits, so `question` stays null. */
|
|
34602
|
+
onTable: external_exports.boolean().optional(),
|
|
34307
34603
|
select: SelectShapeSchema2.optional(),
|
|
34308
34604
|
options: external_exports.array(OptionSchema2.omit({ id: true })).optional(),
|
|
34309
34605
|
/* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
|
|
@@ -34378,9 +34674,16 @@ var InboxItemSchema2 = external_exports.object({
|
|
|
34378
34674
|
/** ITS OPTIONS, WHEN THERE IS SOMETHING TO SEE (owner, 2026-09-25: "Yes, add it"): the options
|
|
34379
34675
|
* its need offers, exactly as its own card carries them, present only when one of them has a
|
|
34380
34676
|
* preview (`html` or `image`). The call screen opens them from the agenda row, so a preview is
|
|
34381
|
-
* never re-sent as a second card to be seen mid-call. Words-only options are
|
|
34382
|
-
|
|
34383
|
-
|
|
34677
|
+
* never re-sent as a second card to be seen mid-call. Words-only options are `choices`. */
|
|
34678
|
+
options: external_exports.array(OptionSchema2).optional(),
|
|
34679
|
+
/** ITS OPTIONS WHEN THEY ARE WORDS (owner, 2026-10-02, on a call: "whenever there are pre-made
|
|
34680
|
+
* options … now for a checklist, we should also show them on the screen"; design C, 2026-10-08):
|
|
34681
|
+
* numbered chips above the call's controls, so a checklist is never only something read aloud. A
|
|
34682
|
+
* field of its own so `options` keeps meaning "previews": a phone on an older bundle opens the
|
|
34683
|
+
* preview layout for any `options`, and must see exactly what it did. */
|
|
34684
|
+
choices: external_exports.array(OptionSchema2).optional(),
|
|
34685
|
+
/** HOW MANY MAY BE PICKED, beside its `choices`: one, any (`many`) or an order (`rank`). */
|
|
34686
|
+
select: external_exports.enum(["one", "many", "rank"]).optional()
|
|
34384
34687
|
})).optional(),
|
|
34385
34688
|
visuals: external_exports.array(VisualSchema2).optional(),
|
|
34386
34689
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
@@ -34640,7 +34943,7 @@ var ConnectionSummarySchema2 = external_exports.object({
|
|
|
34640
34943
|
* desktop app while open. Null = never seen; stale = offline. */
|
|
34641
34944
|
lastSeenAt: external_exports.string().datetime().nullable().optional(),
|
|
34642
34945
|
/** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
|
|
34643
|
-
* its Goals —
|
|
34946
|
+
* its Goals — wrote on one or recorded an operation (`tokens.last_worked_at`). Within
|
|
34644
34947
|
* `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
|
|
34645
34948
|
lastWorkedAt: external_exports.string().datetime().nullable().optional(),
|
|
34646
34949
|
/** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
|
|
@@ -34817,7 +35120,11 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
34817
35120
|
from: external_exports.enum(["person", "agent"]),
|
|
34818
35121
|
said: external_exports.string(),
|
|
34819
35122
|
at: external_exports.string(),
|
|
34820
|
-
entryId: external_exports.string()
|
|
35123
|
+
entryId: external_exports.string(),
|
|
35124
|
+
/** ITS SUMMARY IN AT MOST TEN WORDS (owner, 2026-10-08: "the same switch to the more summarized sources
|
|
35125
|
+
* at the work tab top level screen too"): what the talker or filer wrote on the Entry; absent when
|
|
35126
|
+
* none was. The Work row reads it before `said`. */
|
|
35127
|
+
summary: external_exports.string().optional()
|
|
34821
35128
|
}).nullable().optional(),
|
|
34822
35129
|
/** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
|
|
34823
35130
|
* the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
|
|
@@ -34838,7 +35145,7 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
34838
35145
|
*
|
|
34839
35146
|
* IT IS THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK. Nothing here reasons about which
|
|
34840
35147
|
* writes change which field — the comparison is over the bytes the phone is holding, so a fact
|
|
34841
|
-
* the row shows that no `updated_at` moves for (
|
|
35148
|
+
* the row shows that no `updated_at` moves for (`active` lapsing, a dependency's state, a
|
|
34842
35149
|
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
34843
35150
|
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
34844
35151
|
rev: external_exports.string().optional()
|
|
@@ -35152,17 +35459,43 @@ var CallRecapSchema2 = external_exports.object({
|
|
|
35152
35459
|
title: external_exports.string(),
|
|
35153
35460
|
owner: external_exports.string(),
|
|
35154
35461
|
state: external_exports.string(),
|
|
35462
|
+
/** Whether the call put it to the person. An item it carried and never said has no row on the
|
|
35463
|
+
* review (owner, 2026-10-08: "why do we get the third one when it didn't happen in the call?"). */
|
|
35464
|
+
presented: external_exports.boolean(),
|
|
35155
35465
|
questions: external_exports.array(external_exports.object({ id: external_exports.string().uuid(), state: external_exports.string(), title: external_exports.string() })),
|
|
35156
|
-
/** `words` is always what they SAID, verbatim — the record, never replaced. `
|
|
35157
|
-
*
|
|
35158
|
-
*
|
|
35159
|
-
*
|
|
35160
|
-
* anything that is not an answer. */
|
|
35466
|
+
/** `words` is always what they SAID, verbatim — the record, never replaced. `summary` is what
|
|
35467
|
+
* the line says in ten words (owner, 2026-10-07): the filer's, or the talker's for the answer it
|
|
35468
|
+
* gave, so the row scans like a chosen option and their own words stay under it. Absent when
|
|
35469
|
+
* neither wrote one. */
|
|
35161
35470
|
/** `about` is the request the line answered (its question), null for words that answered none —
|
|
35162
35471
|
* the key the screen groups on, so one question is one row however many times it was answered. */
|
|
35163
|
-
|
|
35472
|
+
/** `reply` is the reply the line makes up (an answer, an ok, a deferral), so one reply is one row
|
|
35473
|
+
* however many lines it cites; `at` is the call lines it came from, so a filed line an answer already
|
|
35474
|
+
* said is not drawn again (owner, 2026-10-07: "fix them all"); `replyKind` says which replies are answers. */
|
|
35475
|
+
lines: external_exports.array(external_exports.object({
|
|
35476
|
+
entryId: external_exports.string().uuid(),
|
|
35477
|
+
words: external_exports.string(),
|
|
35478
|
+
summary: external_exports.string().optional(),
|
|
35479
|
+
about: external_exports.string().nullable().optional(),
|
|
35480
|
+
reply: external_exports.string().nullable().optional(),
|
|
35481
|
+
replyKind: external_exports.string().nullable().optional(),
|
|
35482
|
+
at: external_exports.array(external_exports.number()).optional()
|
|
35483
|
+
}))
|
|
35164
35484
|
})),
|
|
35165
35485
|
unfiled: external_exports.array(external_exports.object({ lineId: external_exports.string().uuid(), words: external_exports.string(), atMs: external_exports.number() })),
|
|
35486
|
+
/** THE PUBLIC TRANSCRIPT — every line said on the call, in the order it was said, and WHO SAID IT
|
|
35487
|
+
* (owner, 2026-10-08: "the public view should only have the lines and who said them, and that's
|
|
35488
|
+
* it"; `docs/brain/phase-three/modular.md` build step 3). `speaker` is the role: "user" is the
|
|
35489
|
+
* person, "agent" an agent speaking, named in `agent`. The timeline's action lines are ours, not
|
|
35490
|
+
* theirs, and are not here; nor is a sealed line, which the server has no words for. It stands
|
|
35491
|
+
* BESIDE `topics` and `unfiled` — what the call did with the words — never instead of them. */
|
|
35492
|
+
said: external_exports.array(external_exports.object({
|
|
35493
|
+
lineId: external_exports.string().uuid(),
|
|
35494
|
+
speaker: external_exports.enum(["user", "agent"]),
|
|
35495
|
+
agent: external_exports.object({ id: external_exports.string().nullable(), name: external_exports.string().nullable() }).nullable(),
|
|
35496
|
+
words: external_exports.string(),
|
|
35497
|
+
atMs: external_exports.number()
|
|
35498
|
+
})),
|
|
35166
35499
|
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
35167
35500
|
});
|
|
35168
35501
|
|
|
@@ -36117,10 +36450,14 @@ function localHolderPids(deps = {}) {
|
|
|
36117
36450
|
if (!/^\d+\.json$/.test(file)) continue;
|
|
36118
36451
|
let row;
|
|
36119
36452
|
try {
|
|
36120
|
-
row =
|
|
36453
|
+
row = parseSessionRecord(read3(join7(dir, file)));
|
|
36121
36454
|
} catch {
|
|
36122
36455
|
continue;
|
|
36123
36456
|
}
|
|
36457
|
+
if (!row) {
|
|
36458
|
+
deps.unreadable?.(file);
|
|
36459
|
+
continue;
|
|
36460
|
+
}
|
|
36124
36461
|
const pid = typeof row.pid === "number" ? row.pid : Number(file.slice(0, -5));
|
|
36125
36462
|
if (typeof row.sessionId !== "string" || !row.sessionId.trim() || !Number.isInteger(pid) || pid <= 0) continue;
|
|
36126
36463
|
if (!holds(pid, row.procStart, check)) continue;
|
|
@@ -36162,10 +36499,14 @@ function localHolders(deps = {}) {
|
|
|
36162
36499
|
if (!/^\d+\.json$/.test(file)) continue;
|
|
36163
36500
|
let row;
|
|
36164
36501
|
try {
|
|
36165
|
-
row =
|
|
36502
|
+
row = parseSessionRecord(read3(join7(dir, file)));
|
|
36166
36503
|
} catch {
|
|
36167
36504
|
continue;
|
|
36168
36505
|
}
|
|
36506
|
+
if (!row) {
|
|
36507
|
+
deps.unreadable?.(file);
|
|
36508
|
+
continue;
|
|
36509
|
+
}
|
|
36169
36510
|
const pid = typeof row.pid === "number" ? row.pid : Number(file.slice(0, -5));
|
|
36170
36511
|
if (typeof row.sessionId !== "string" || !row.sessionId.trim() || !Number.isInteger(pid) || pid <= 0) continue;
|
|
36171
36512
|
const slot = sessionSlot(row.sessionId);
|
|
@@ -36386,7 +36727,7 @@ var claimedRun = (sessionId2) => {
|
|
|
36386
36727
|
const slot = sessionSlot(sessionId2);
|
|
36387
36728
|
return { slot, key: runKey(slot) };
|
|
36388
36729
|
};
|
|
36389
|
-
var launchRun = (spec) => ({ session: spec.sessionId });
|
|
36730
|
+
var launchRun = (spec) => ({ session: spec.sessionId, model: spec.model });
|
|
36390
36731
|
var BOUND_ELSEWHERE_MS = 10 * 6e4;
|
|
36391
36732
|
var boundElsewhere = /* @__PURE__ */ new Map();
|
|
36392
36733
|
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";
|
|
@@ -36495,7 +36836,7 @@ function startHost(opts) {
|
|
|
36495
36836
|
});
|
|
36496
36837
|
void heartbeat({
|
|
36497
36838
|
version: VERSION,
|
|
36498
|
-
harnesses: detectAll().map((a) => ({ name: a.name, label: a.label, status: a.status })),
|
|
36839
|
+
harnesses: detectAll().map((a) => ({ name: a.name, label: a.label, status: a.status, models: a.models })),
|
|
36499
36840
|
workspaces: listWorkspaces(opts.wsDeps),
|
|
36500
36841
|
// The repos inside them, so a session started for work on one opens IN it (Goal 26982211).
|
|
36501
36842
|
repos: listRepos(opts.wsDeps)
|
|
@@ -36587,9 +36928,14 @@ function startHost(opts) {
|
|
|
36587
36928
|
let lastPresence = /* @__PURE__ */ new Map();
|
|
36588
36929
|
async function evidence() {
|
|
36589
36930
|
const presence = await presenceMap();
|
|
36590
|
-
lastHolders = localHolders()
|
|
36931
|
+
lastHolders = localHolders({ unreadable: (file) => {
|
|
36932
|
+
if (unreadable.has(file)) return;
|
|
36933
|
+
unreadable.add(file);
|
|
36934
|
+
opts.log(`\u25CC session record ${file} has no object in it \u2014 the process it names reads as not running here`);
|
|
36935
|
+
} });
|
|
36591
36936
|
return { presence, holders: lastHolders };
|
|
36592
36937
|
}
|
|
36938
|
+
const unreadable = /* @__PURE__ */ new Set();
|
|
36593
36939
|
let lastHolders = /* @__PURE__ */ new Map();
|
|
36594
36940
|
async function wakeSlot(slot, { presence, holders }, cause = "sweep") {
|
|
36595
36941
|
{
|