@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/main.js
CHANGED
|
@@ -8680,10 +8680,47 @@ function claudeConversation() {
|
|
|
8680
8680
|
if (!envSession("CLAUDE_CODE_SESSION_ID") || process.env.CLAUDE_CODE_CHILD_SESSION) return void 0;
|
|
8681
8681
|
const dir = envSession("CLAUDE_CONFIG_DIR") ?? join(homedir(), ".claude");
|
|
8682
8682
|
for (const pid of ancestors()) {
|
|
8683
|
+
let text;
|
|
8683
8684
|
try {
|
|
8684
|
-
|
|
8685
|
-
return typeof sessionId2 === "string" && sessionId2.trim() ? sessionId2 : void 0;
|
|
8685
|
+
text = readFileSync(join(dir, "sessions", `${pid}.json`), "utf8");
|
|
8686
8686
|
} catch {
|
|
8687
|
+
continue;
|
|
8688
|
+
}
|
|
8689
|
+
const record = parseSessionRecord(text);
|
|
8690
|
+
if (!record) continue;
|
|
8691
|
+
return typeof record.sessionId === "string" && record.sessionId.trim() ? record.sessionId : void 0;
|
|
8692
|
+
}
|
|
8693
|
+
return void 0;
|
|
8694
|
+
}
|
|
8695
|
+
function parseSessionRecord(text) {
|
|
8696
|
+
const object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
8697
|
+
try {
|
|
8698
|
+
return object(JSON.parse(text));
|
|
8699
|
+
} catch {
|
|
8700
|
+
}
|
|
8701
|
+
let depth = 0;
|
|
8702
|
+
let start = -1;
|
|
8703
|
+
let quoted = false;
|
|
8704
|
+
let escaped = false;
|
|
8705
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
8706
|
+
const c = text[i];
|
|
8707
|
+
if (quoted) {
|
|
8708
|
+
if (escaped) escaped = false;
|
|
8709
|
+
else if (c === "\\") escaped = true;
|
|
8710
|
+
else if (c === '"') quoted = false;
|
|
8711
|
+
} else if (c === '"') quoted = true;
|
|
8712
|
+
else if (c === "{") {
|
|
8713
|
+
if (depth === 0) start = i;
|
|
8714
|
+
depth += 1;
|
|
8715
|
+
} else if (c === "}" && depth > 0) {
|
|
8716
|
+
depth -= 1;
|
|
8717
|
+
if (depth === 0) {
|
|
8718
|
+
try {
|
|
8719
|
+
return object(JSON.parse(text.slice(start, i + 1)));
|
|
8720
|
+
} catch {
|
|
8721
|
+
return void 0;
|
|
8722
|
+
}
|
|
8723
|
+
}
|
|
8687
8724
|
}
|
|
8688
8725
|
}
|
|
8689
8726
|
return void 0;
|
|
@@ -9923,10 +9960,12 @@ var zodToJsonSchema = (schema, options) => {
|
|
|
9923
9960
|
};
|
|
9924
9961
|
var OPTIONS_MIN = 2;
|
|
9925
9962
|
var OPTIONS_MAX = 6;
|
|
9963
|
+
var OPTION_LABEL_MAX = 50;
|
|
9964
|
+
var OPTION_HINT_MAX = 200;
|
|
9926
9965
|
var OptionSchema = external_exports.object({
|
|
9927
9966
|
id: external_exports.string(),
|
|
9928
9967
|
label: external_exports.string(),
|
|
9929
|
-
hint: external_exports.string().max(500).describe(
|
|
9968
|
+
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(),
|
|
9930
9969
|
// .describe() flows into the MCP contact JSON schema (zodToJsonSchema), so
|
|
9931
9970
|
// the constraints below are what an agent reads when deciding to use these.
|
|
9932
9971
|
html: external_exports.string().max(16384).describe(
|
|
@@ -9940,8 +9979,10 @@ var OptionInputSchema = external_exports.object({
|
|
|
9940
9979
|
id: external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9_.:-]+$/).optional().describe(
|
|
9941
9980
|
`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.`
|
|
9942
9981
|
),
|
|
9943
|
-
|
|
9944
|
-
hint
|
|
9982
|
+
// SHORT CHOICES (owner, 2026-10-07: "options label should be maybe max 50 characters and the hint 200"):
|
|
9983
|
+
// a label is one choice the person reads, or hears on a call; a hint says its consequence in a line.
|
|
9984
|
+
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.`),
|
|
9985
|
+
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(),
|
|
9945
9986
|
htmlPreview: OptionSchema.shape.html,
|
|
9946
9987
|
imgUrl: OptionSchema.shape.image
|
|
9947
9988
|
}).strict();
|
|
@@ -10043,15 +10084,48 @@ var UpdateInputSchema = external_exports.object({
|
|
|
10043
10084
|
message: external_exports.string().trim().min(1).describe(`The one point, at most ${ASK_MAX} characters; the context goes in units.`),
|
|
10044
10085
|
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."),
|
|
10045
10086
|
units,
|
|
10087
|
+
// A REPORT KEEPS ITS TAP (#2799, #3260; owner, 2026-10-08, settling #2776: "keep the reports open
|
|
10088
|
+
// until the user acknowledges them"). A report asks nothing, so it opens no Question -- but it
|
|
10089
|
+
// still waits to be acknowledged, and it carries the ONE tap that closes it. Never a decision: the
|
|
10090
|
+
// tap lands as a contribution, the person's own words on the Goal, because there is no Question
|
|
10091
|
+
// for it to answer.
|
|
10092
|
+
//
|
|
10093
|
+
// AND ITS WORDS ARE WRITTEN FOR THIS REPORT (owner, 2026-10-08, Goal 3c12d3c6: "Agent
|
|
10094
|
+
// acknowledgments vary (e.g. 'sounds good, I will test later') instead of always 'got it'"). `Got
|
|
10095
|
+
// it` and `Noted` say only that a card was cleared, where "Sounds good, I'll test later" says the
|
|
10096
|
+
// work was RECEIVED and not verified -- the distinction a session spent an hour recovering from a
|
|
10097
|
+
// call transcript on 2026-10-07, having read "not quite" as a defect report when it meant *not
|
|
10098
|
+
// tested yet*. The words are worth writing because the agent reads them back.
|
|
10099
|
+
//
|
|
10100
|
+
// ONE STRING, NOT A LIST (owner, 2026-10-08 15:47, Goal 31a1e2ac, asked whether an agent should
|
|
10101
|
+
// write up to four of them: "just on the cards, but it should be more like just they should only
|
|
10102
|
+
// get one basically string value that they can enter that replaces got it"). A report has exactly
|
|
10103
|
+
// one way to close, so the only thing an agent writes is what that button SAYS. #3260's 1..6
|
|
10104
|
+
// `options` and this field are one job: the list is deleted, not deprecated (ONE JOB, ONE
|
|
10105
|
+
// MECHANISM). It rides the door as the report's single stored option (`storedOption`,
|
|
10106
|
+
// `apps/api/src/goal/router.ts`), so no reader below the door learns a second shape.
|
|
10107
|
+
reply: external_exports.string().trim().min(1).describe(
|
|
10108
|
+
`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.`
|
|
10109
|
+
).optional(),
|
|
10046
10110
|
userExplicitlyRequested
|
|
10047
|
-
}).strict().superRefine((u, ctx) =>
|
|
10111
|
+
}).strict().superRefine((u, ctx) => {
|
|
10112
|
+
refuseOverCaps(u.message, u.units, "message", ctx);
|
|
10113
|
+
if (u.reply && u.reply.length > OPTION_LABEL_MAX) {
|
|
10114
|
+
ctx.addIssue({
|
|
10115
|
+
code: external_exports.ZodIssueCode.custom,
|
|
10116
|
+
path: ["reply"],
|
|
10117
|
+
params: { refusal: "reply_too_long", length: u.reply.length, cap: OPTION_LABEL_MAX },
|
|
10118
|
+
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.`
|
|
10119
|
+
});
|
|
10120
|
+
}
|
|
10121
|
+
});
|
|
10048
10122
|
var questionFields = {
|
|
10049
10123
|
question: external_exports.string().trim().min(1).describe(
|
|
10050
10124
|
`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.`
|
|
10051
10125
|
),
|
|
10052
10126
|
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."),
|
|
10053
10127
|
units,
|
|
10054
|
-
options: external_exports.array(OptionInputSchema).min(1).max(6).optional().describe("The choices, when you have them. Each label stands on its own."),
|
|
10128
|
+
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."),
|
|
10055
10129
|
// HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
|
|
10056
10130
|
// brain_prompts.md §1: structured agent questions need no model). The app draws all three.
|
|
10057
10131
|
pickMode: external_exports.enum(["one", "many", "rank"]).optional().describe(
|
|
@@ -10128,6 +10202,8 @@ QUESTIONS: one question per object; several questions are several objects in one
|
|
|
10128
10202
|
|
|
10129
10203
|
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.
|
|
10130
10204
|
|
|
10205
|
+
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".
|
|
10206
|
+
|
|
10131
10207
|
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.
|
|
10132
10208
|
|
|
10133
10209
|
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\`).`;
|
|
@@ -10171,7 +10247,8 @@ var UpdateGoalSchema = external_exports.object({
|
|
|
10171
10247
|
}).strict();
|
|
10172
10248
|
var changedGoal = external_exports.string().uuid().describe("The Goal to change, as a read shows it.");
|
|
10173
10249
|
var 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.");
|
|
10174
|
-
var
|
|
10250
|
+
var GOAL_OUTCOME_MAX = 500;
|
|
10251
|
+
var 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.`);
|
|
10175
10252
|
var CreateChange = external_exports.object({
|
|
10176
10253
|
kind: external_exports.literal("create"),
|
|
10177
10254
|
title: goalTitle,
|
|
@@ -10197,7 +10274,7 @@ var DependencyChange = external_exports.object({
|
|
|
10197
10274
|
goalId: changedGoal,
|
|
10198
10275
|
dependsOnGoalId: external_exports.string().uuid().describe("The Goal it waits on."),
|
|
10199
10276
|
action: external_exports.enum(["start", "complete"]).describe("What waits: starting goalId, or completing it."),
|
|
10200
|
-
reason: external_exports.string().trim().min(1).max(
|
|
10277
|
+
reason: external_exports.string().trim().min(1).max(250).optional().describe("Why, at most 250 characters, as an explanation, never as policy.")
|
|
10201
10278
|
}).strict();
|
|
10202
10279
|
var read = { revision: external_exports.number().int().positive().optional() };
|
|
10203
10280
|
var GoalChangeSchema = external_exports.discriminatedUnion("kind", [
|
|
@@ -10221,7 +10298,7 @@ var ManageGoalsSchema = external_exports.object({
|
|
|
10221
10298
|
var ManageGoalsToolSchema = external_exports.object({
|
|
10222
10299
|
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.")
|
|
10223
10300
|
}).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
|
|
10224
|
-
var 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.";
|
|
10301
|
+
var 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.";
|
|
10225
10302
|
var GetGoalSchema = external_exports.object({
|
|
10226
10303
|
goalId: external_exports.string().uuid(),
|
|
10227
10304
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
@@ -10239,7 +10316,7 @@ var SearchToolSchema = external_exports.object({
|
|
|
10239
10316
|
goalId: external_exports.string().uuid().optional(),
|
|
10240
10317
|
limit: external_exports.number().int().min(1).max(20).optional()
|
|
10241
10318
|
}).strict();
|
|
10242
|
-
var 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
|
|
10319
|
+
var 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.";
|
|
10243
10320
|
var CheckActivitySchema = external_exports.object({}).strict();
|
|
10244
10321
|
var FEEDBACK_TEXT_MAX = 5e4;
|
|
10245
10322
|
var SendFeedbackSchema = external_exports.object({
|
|
@@ -10302,6 +10379,7 @@ var QuestionChangeSchema = external_exports.union([
|
|
|
10302
10379
|
external_exports.object({
|
|
10303
10380
|
kind: external_exports.literal("create"),
|
|
10304
10381
|
text: external_exports.string().min(1),
|
|
10382
|
+
summary: external_exports.string().optional(),
|
|
10305
10383
|
answererId: participant,
|
|
10306
10384
|
goal: ResultRefSchema.optional(),
|
|
10307
10385
|
blocks: external_exports.array(external_exports.object({ question: ResultRefSchema }).strict()),
|
|
@@ -10352,12 +10430,12 @@ var BrainSearchSchema = external_exports.object({
|
|
|
10352
10430
|
}).strict();
|
|
10353
10431
|
var BrainResultSchema = external_exports.object({
|
|
10354
10432
|
messages: external_exports.array(BrainMessageSchema),
|
|
10355
|
-
entries: external_exports.array(external_exports.object({ key, sources: external_exports.array(SourceSchema).min(1), goals: external_exports.array(ResultRefSchema) }).strict()),
|
|
10433
|
+
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()),
|
|
10356
10434
|
questions: external_exports.array(external_exports.object({ key, entryKeys: external_exports.array(key), change: QuestionChangeSchema }).strict()),
|
|
10357
10435
|
answers: external_exports.array(external_exports.object({
|
|
10358
10436
|
question: ResultRefSchema,
|
|
10359
10437
|
entryKeys: external_exports.array(key).min(1),
|
|
10360
|
-
summary: external_exports.string()
|
|
10438
|
+
summary: external_exports.string(),
|
|
10361
10439
|
selectedOptionIds: external_exports.array(external_exports.string().min(1)).optional()
|
|
10362
10440
|
}).strict()),
|
|
10363
10441
|
goals: external_exports.array(external_exports.object({ key, entryKeys: external_exports.array(key), change: BrainGoalChangeSchema }).strict()),
|
|
@@ -10402,12 +10480,15 @@ var TalkerReplySchema = external_exports.object({
|
|
|
10402
10480
|
messages: external_exports.array(external_exports.object({ key: str2, to: str2, text: str2, about: strs2, blocks: str2 }).strict()),
|
|
10403
10481
|
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
10404
10482
|
then: external_exports.enum(["listen", "hold", "end"]),
|
|
10405
|
-
/**
|
|
10406
|
-
|
|
10407
|
-
|
|
10483
|
+
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
10484
|
+
* IDs, the line handles, how they replied in under ten words (keeping any condition), and `defer`:
|
|
10485
|
+
* "" when their words settle the item, else what a reply that puts it off waits for -- "call" (the
|
|
10486
|
+
* end of this call), a question's handle (that question's answer) or an ISO time. */
|
|
10487
|
+
replies: external_exports.array(external_exports.object({ item: str2, options: strs2, lines: strs2, summary: str2, defer: str2 }).strict()),
|
|
10488
|
+
/** The line handles that tell the talker how to run this call; they last until it ends and reach no agent. */
|
|
10408
10489
|
instruction: strs2,
|
|
10409
|
-
/** The line handles
|
|
10410
|
-
*
|
|
10490
|
+
/** The line handles the filer acts on, and the only ones it reads (3e): new work, a change to existing
|
|
10491
|
+
* work, feedback on Paigy, anything said to an agent, and any rule meant to outlast the call. */
|
|
10411
10492
|
file: strs2,
|
|
10412
10493
|
/** Evidence to fetch for a second round: a search, an item's full text, or an agent by name. */
|
|
10413
10494
|
need: external_exports.object({ kind: external_exports.enum(["search", "item", "agent", ""]), text: str2 }).strict(),
|
|
@@ -10420,8 +10501,9 @@ var TALKER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
|
10420
10501
|
var str3 = external_exports.string();
|
|
10421
10502
|
var strs3 = external_exports.array(external_exports.string());
|
|
10422
10503
|
var FilerReplySchema = external_exports.object({
|
|
10423
|
-
/** Which Goals each new line goes on: a line's handle
|
|
10424
|
-
|
|
10504
|
+
/** Which Goals each new line goes on: a line's handle, Goal handles (or keys of new Goals), and what
|
|
10505
|
+
* the line says in at most ten words. */
|
|
10506
|
+
filed: external_exports.array(external_exports.object({ line: str3, goals: strs3, summary: str3 }).strict()),
|
|
10425
10507
|
goals: external_exports.array(external_exports.object({
|
|
10426
10508
|
op: external_exports.enum(["create", "edit", "complete", "cancel", "reopen", "assign", "defer", "move", "block", "unblock"]),
|
|
10427
10509
|
key: str3,
|
|
@@ -10440,6 +10522,7 @@ var FilerReplySchema = external_exports.object({
|
|
|
10440
10522
|
key: str3,
|
|
10441
10523
|
question: str3,
|
|
10442
10524
|
text: str3,
|
|
10525
|
+
summary: str3,
|
|
10443
10526
|
to: str3,
|
|
10444
10527
|
goal: str3,
|
|
10445
10528
|
options: external_exports.array(external_exports.object({ id: str3, label: str3 }).strict()),
|
|
@@ -10453,6 +10536,60 @@ var FilerReplySchema = external_exports.object({
|
|
|
10453
10536
|
var FILER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10454
10537
|
zodToJsonSchema(FilerReplySchema, { $refStrategy: "none" })
|
|
10455
10538
|
);
|
|
10539
|
+
var ExplainerReplySchema = external_exports.object({
|
|
10540
|
+
/** The explanation, as sentences, spoken in order. */
|
|
10541
|
+
say: external_exports.array(external_exports.string()),
|
|
10542
|
+
/** Questions to an agent about gaps in what it read: the agent's handle and the question. */
|
|
10543
|
+
followUps: external_exports.array(external_exports.object({ to: external_exports.string(), text: external_exports.string() }).strict())
|
|
10544
|
+
}).strict();
|
|
10545
|
+
var EXPLAINER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10546
|
+
zodToJsonSchema(ExplainerReplySchema, { $refStrategy: "none" })
|
|
10547
|
+
);
|
|
10548
|
+
var DISTILLER_DEFECTS = [
|
|
10549
|
+
"reasked",
|
|
10550
|
+
// a question put to the person again after they answered it
|
|
10551
|
+
"unsupported",
|
|
10552
|
+
// a fact stated that nothing in the input supports
|
|
10553
|
+
"unsaid",
|
|
10554
|
+
// an agent's answer arrived during the call and was never said
|
|
10555
|
+
"skipped",
|
|
10556
|
+
// an agenda item never put to the person
|
|
10557
|
+
"unsaved",
|
|
10558
|
+
// something the person answered or asked for did not save, was refused or dropped
|
|
10559
|
+
"ignored",
|
|
10560
|
+
// the person asked or said something and was never answered (the critic's ignored_a_question)
|
|
10561
|
+
"promised",
|
|
10562
|
+
// said it did or would do something, and nothing shows it done (the critic's broken_promise)
|
|
10563
|
+
"dropped"
|
|
10564
|
+
// something it had to say never played and was never said later (owner, 2026-10-08, call 65433a9d)
|
|
10565
|
+
];
|
|
10566
|
+
var DistillerReplySchema = external_exports.object({
|
|
10567
|
+
defects: external_exports.array(external_exports.object({
|
|
10568
|
+
kind: external_exports.enum(DISTILLER_DEFECTS),
|
|
10569
|
+
/** The handles of the lines it shows in. */
|
|
10570
|
+
lines: external_exports.array(external_exports.string()),
|
|
10571
|
+
/** The handle of the record it concerns, or "". */
|
|
10572
|
+
about: external_exports.string(),
|
|
10573
|
+
/** One plain sentence, in words, never handles: it is what the feedback row keeps. */
|
|
10574
|
+
why: external_exports.string()
|
|
10575
|
+
}).strict())
|
|
10576
|
+
}).strict();
|
|
10577
|
+
var DISTILLER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10578
|
+
zodToJsonSchema(DistillerReplySchema, { $refStrategy: "none" })
|
|
10579
|
+
);
|
|
10580
|
+
var TIDY_AUDIENCES = ["talker", "filer", "agents"];
|
|
10581
|
+
var TidyReplySchema = external_exports.object({
|
|
10582
|
+
lessons: external_exports.array(external_exports.object({
|
|
10583
|
+
lesson: external_exports.string(),
|
|
10584
|
+
op: external_exports.enum(["keep", "revise", "withdraw"]),
|
|
10585
|
+
text: external_exports.string(),
|
|
10586
|
+
for: external_exports.enum([...TIDY_AUDIENCES, ""]),
|
|
10587
|
+
into: external_exports.string()
|
|
10588
|
+
}).strict())
|
|
10589
|
+
}).strict();
|
|
10590
|
+
var TIDY_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10591
|
+
zodToJsonSchema(TidyReplySchema, { $refStrategy: "none" })
|
|
10592
|
+
);
|
|
10456
10593
|
function entryUnit(entry) {
|
|
10457
10594
|
if (entry.kind !== "contribution" || !entry.aboutId) return null;
|
|
10458
10595
|
const plain = entry.content && "plain" in entry.content ? entry.content.plain : null;
|
|
@@ -10462,6 +10599,8 @@ function entryUnit(entry) {
|
|
|
10462
10599
|
}
|
|
10463
10600
|
var LIVE_MS = 3 * 6e4;
|
|
10464
10601
|
var WORKING_MS = 60 * 6e4;
|
|
10602
|
+
var WORKING_TIER_MS = 2 * 60 * 6e4;
|
|
10603
|
+
var RECENT_TIER_MS = 24 * 60 * 6e4;
|
|
10465
10604
|
var ContextSchema = external_exports.object({
|
|
10466
10605
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
10467
10606
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -10806,6 +10945,10 @@ var AgendaTurnSchema = external_exports.object({
|
|
|
10806
10945
|
* so "who said that" survives the call, and it rides PER TURN because a coalesced call
|
|
10807
10946
|
* speaks for several agents — the turn is the only place that knows which. */
|
|
10808
10947
|
agentId: external_exports.string().optional(),
|
|
10948
|
+
/** ITS QUESTION IS ON THE TABLE (#3292): a talker line about a question the person owes. The walk tells
|
|
10949
|
+
* the phone (`asking`, so the agenda goes gold and its options show) without treating the line as a
|
|
10950
|
+
* question turn — the talker owns re-asking and the waits, so `question` stays null. */
|
|
10951
|
+
onTable: external_exports.boolean().optional(),
|
|
10809
10952
|
select: SelectShapeSchema.optional(),
|
|
10810
10953
|
options: external_exports.array(OptionSchema.omit({ id: true })).optional(),
|
|
10811
10954
|
/* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
|
|
@@ -10880,9 +11023,16 @@ var InboxItemSchema = external_exports.object({
|
|
|
10880
11023
|
/** ITS OPTIONS, WHEN THERE IS SOMETHING TO SEE (owner, 2026-09-25: "Yes, add it"): the options
|
|
10881
11024
|
* its need offers, exactly as its own card carries them, present only when one of them has a
|
|
10882
11025
|
* preview (`html` or `image`). The call screen opens them from the agenda row, so a preview is
|
|
10883
|
-
* never re-sent as a second card to be seen mid-call. Words-only options are
|
|
10884
|
-
|
|
10885
|
-
|
|
11026
|
+
* never re-sent as a second card to be seen mid-call. Words-only options are `choices`. */
|
|
11027
|
+
options: external_exports.array(OptionSchema).optional(),
|
|
11028
|
+
/** ITS OPTIONS WHEN THEY ARE WORDS (owner, 2026-10-02, on a call: "whenever there are pre-made
|
|
11029
|
+
* options … now for a checklist, we should also show them on the screen"; design C, 2026-10-08):
|
|
11030
|
+
* numbered chips above the call's controls, so a checklist is never only something read aloud. A
|
|
11031
|
+
* field of its own so `options` keeps meaning "previews": a phone on an older bundle opens the
|
|
11032
|
+
* preview layout for any `options`, and must see exactly what it did. */
|
|
11033
|
+
choices: external_exports.array(OptionSchema).optional(),
|
|
11034
|
+
/** HOW MANY MAY BE PICKED, beside its `choices`: one, any (`many`) or an order (`rank`). */
|
|
11035
|
+
select: external_exports.enum(["one", "many", "rank"]).optional()
|
|
10886
11036
|
})).optional(),
|
|
10887
11037
|
visuals: external_exports.array(VisualSchema).optional(),
|
|
10888
11038
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
@@ -11142,7 +11292,7 @@ var ConnectionSummarySchema = external_exports.object({
|
|
|
11142
11292
|
* desktop app while open. Null = never seen; stale = offline. */
|
|
11143
11293
|
lastSeenAt: external_exports.string().datetime().nullable().optional(),
|
|
11144
11294
|
/** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
|
|
11145
|
-
* its Goals —
|
|
11295
|
+
* its Goals — wrote on one or recorded an operation (`tokens.last_worked_at`). Within
|
|
11146
11296
|
* `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
|
|
11147
11297
|
lastWorkedAt: external_exports.string().datetime().nullable().optional(),
|
|
11148
11298
|
/** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
|
|
@@ -11319,7 +11469,11 @@ var QueueItemSchema = external_exports.object({
|
|
|
11319
11469
|
from: external_exports.enum(["person", "agent"]),
|
|
11320
11470
|
said: external_exports.string(),
|
|
11321
11471
|
at: external_exports.string(),
|
|
11322
|
-
entryId: external_exports.string()
|
|
11472
|
+
entryId: external_exports.string(),
|
|
11473
|
+
/** ITS SUMMARY IN AT MOST TEN WORDS (owner, 2026-10-08: "the same switch to the more summarized sources
|
|
11474
|
+
* at the work tab top level screen too"): what the talker or filer wrote on the Entry; absent when
|
|
11475
|
+
* none was. The Work row reads it before `said`. */
|
|
11476
|
+
summary: external_exports.string().optional()
|
|
11323
11477
|
}).nullable().optional(),
|
|
11324
11478
|
/** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
|
|
11325
11479
|
* the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
|
|
@@ -11340,7 +11494,7 @@ var QueueItemSchema = external_exports.object({
|
|
|
11340
11494
|
*
|
|
11341
11495
|
* IT IS THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK. Nothing here reasons about which
|
|
11342
11496
|
* writes change which field — the comparison is over the bytes the phone is holding, so a fact
|
|
11343
|
-
* the row shows that no `updated_at` moves for (
|
|
11497
|
+
* the row shows that no `updated_at` moves for (`active` lapsing, a dependency's state, a
|
|
11344
11498
|
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
11345
11499
|
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
11346
11500
|
rev: external_exports.string().optional()
|
|
@@ -11656,17 +11810,43 @@ var CallRecapSchema = external_exports.object({
|
|
|
11656
11810
|
title: external_exports.string(),
|
|
11657
11811
|
owner: external_exports.string(),
|
|
11658
11812
|
state: external_exports.string(),
|
|
11813
|
+
/** Whether the call put it to the person. An item it carried and never said has no row on the
|
|
11814
|
+
* review (owner, 2026-10-08: "why do we get the third one when it didn't happen in the call?"). */
|
|
11815
|
+
presented: external_exports.boolean(),
|
|
11659
11816
|
questions: external_exports.array(external_exports.object({ id: external_exports.string().uuid(), state: external_exports.string(), title: external_exports.string() })),
|
|
11660
|
-
/** `words` is always what they SAID, verbatim — the record, never replaced. `
|
|
11661
|
-
*
|
|
11662
|
-
*
|
|
11663
|
-
*
|
|
11664
|
-
* anything that is not an answer. */
|
|
11817
|
+
/** `words` is always what they SAID, verbatim — the record, never replaced. `summary` is what
|
|
11818
|
+
* the line says in ten words (owner, 2026-10-07): the filer's, or the talker's for the answer it
|
|
11819
|
+
* gave, so the row scans like a chosen option and their own words stay under it. Absent when
|
|
11820
|
+
* neither wrote one. */
|
|
11665
11821
|
/** `about` is the request the line answered (its question), null for words that answered none —
|
|
11666
11822
|
* the key the screen groups on, so one question is one row however many times it was answered. */
|
|
11667
|
-
|
|
11823
|
+
/** `reply` is the reply the line makes up (an answer, an ok, a deferral), so one reply is one row
|
|
11824
|
+
* however many lines it cites; `at` is the call lines it came from, so a filed line an answer already
|
|
11825
|
+
* said is not drawn again (owner, 2026-10-07: "fix them all"); `replyKind` says which replies are answers. */
|
|
11826
|
+
lines: external_exports.array(external_exports.object({
|
|
11827
|
+
entryId: external_exports.string().uuid(),
|
|
11828
|
+
words: external_exports.string(),
|
|
11829
|
+
summary: external_exports.string().optional(),
|
|
11830
|
+
about: external_exports.string().nullable().optional(),
|
|
11831
|
+
reply: external_exports.string().nullable().optional(),
|
|
11832
|
+
replyKind: external_exports.string().nullable().optional(),
|
|
11833
|
+
at: external_exports.array(external_exports.number()).optional()
|
|
11834
|
+
}))
|
|
11668
11835
|
})),
|
|
11669
11836
|
unfiled: external_exports.array(external_exports.object({ lineId: external_exports.string().uuid(), words: external_exports.string(), atMs: external_exports.number() })),
|
|
11837
|
+
/** THE PUBLIC TRANSCRIPT — every line said on the call, in the order it was said, and WHO SAID IT
|
|
11838
|
+
* (owner, 2026-10-08: "the public view should only have the lines and who said them, and that's
|
|
11839
|
+
* it"; `docs/brain/phase-three/modular.md` build step 3). `speaker` is the role: "user" is the
|
|
11840
|
+
* person, "agent" an agent speaking, named in `agent`. The timeline's action lines are ours, not
|
|
11841
|
+
* theirs, and are not here; nor is a sealed line, which the server has no words for. It stands
|
|
11842
|
+
* BESIDE `topics` and `unfiled` — what the call did with the words — never instead of them. */
|
|
11843
|
+
said: external_exports.array(external_exports.object({
|
|
11844
|
+
lineId: external_exports.string().uuid(),
|
|
11845
|
+
speaker: external_exports.enum(["user", "agent"]),
|
|
11846
|
+
agent: external_exports.object({ id: external_exports.string().nullable(), name: external_exports.string().nullable() }).nullable(),
|
|
11847
|
+
words: external_exports.string(),
|
|
11848
|
+
atMs: external_exports.number()
|
|
11849
|
+
})),
|
|
11670
11850
|
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
11671
11851
|
});
|
|
11672
11852
|
function sessionSlot(sessionId2) {
|
|
@@ -12221,7 +12401,8 @@ var CATALOG = [
|
|
|
12221
12401
|
authProbe: ["claude", "auth", "status"],
|
|
12222
12402
|
loginHint: "Run `claude` once in a terminal and sign in.",
|
|
12223
12403
|
installCli: "curl -fsSL https://claude.ai/install.sh | bash",
|
|
12224
|
-
installAdapter: "npm install -g @agentclientprotocol/claude-agent-acp"
|
|
12404
|
+
installAdapter: "npm install -g @agentclientprotocol/claude-agent-acp",
|
|
12405
|
+
models: ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", "claude-3-opus-20240229"]
|
|
12225
12406
|
},
|
|
12226
12407
|
{
|
|
12227
12408
|
name: "codex",
|
|
@@ -12231,7 +12412,8 @@ var CATALOG = [
|
|
|
12231
12412
|
authProbe: ["codex", "login", "status"],
|
|
12232
12413
|
loginHint: "Run `codex login` to authenticate.",
|
|
12233
12414
|
installCli: "curl -fsSL https://chatgpt.com/codex/install.sh | sh",
|
|
12234
|
-
installAdapter: "npm install -g @agentclientprotocol/codex-acp"
|
|
12415
|
+
installAdapter: "npm install -g @agentclientprotocol/codex-acp",
|
|
12416
|
+
models: ["gpt-4o", "gpt-4o-mini", "o1", "o1-mini"]
|
|
12235
12417
|
},
|
|
12236
12418
|
{
|
|
12237
12419
|
name: "agy",
|
|
@@ -12241,7 +12423,8 @@ var CATALOG = [
|
|
|
12241
12423
|
adapter: "paigy-agy-acp",
|
|
12242
12424
|
authProbe: ["agy", "help"],
|
|
12243
12425
|
loginHint: "Install and set up Antigravity (`agy`).",
|
|
12244
|
-
installCli: "curl -fsSL https://antigravity.dev/install.sh | bash"
|
|
12426
|
+
installCli: "curl -fsSL https://antigravity.dev/install.sh | bash",
|
|
12427
|
+
models: ["gemini-1.5-pro", "gemini-1.5-flash", "gemini-2.0-pro-exp"]
|
|
12245
12428
|
}
|
|
12246
12429
|
];
|
|
12247
12430
|
function probeDirs(home = homedir3()) {
|
|
@@ -12277,7 +12460,7 @@ function runProbe(argv) {
|
|
|
12277
12460
|
function detect(entry, deps = {}) {
|
|
12278
12461
|
const find = deps.which ?? which;
|
|
12279
12462
|
const probe = deps.probe ?? runProbe;
|
|
12280
|
-
const base = { name: entry.name, label: entry.label };
|
|
12463
|
+
const base = { name: entry.name, label: entry.label, models: entry.models };
|
|
12281
12464
|
const cliPath = find(entry.cli);
|
|
12282
12465
|
if (!cliPath) return { ...base, status: "missing", hint: `${entry.label} is not installed.`, install: entry.installCli };
|
|
12283
12466
|
const adapterPath = entry.adapter ? find(entry.adapter) : null;
|
|
@@ -12997,10 +13180,14 @@ function localHolderPids(deps = {}) {
|
|
|
12997
13180
|
if (!/^\d+\.json$/.test(file)) continue;
|
|
12998
13181
|
let row;
|
|
12999
13182
|
try {
|
|
13000
|
-
row =
|
|
13183
|
+
row = parseSessionRecord(read3(join6(dir, file)));
|
|
13001
13184
|
} catch {
|
|
13002
13185
|
continue;
|
|
13003
13186
|
}
|
|
13187
|
+
if (!row) {
|
|
13188
|
+
deps.unreadable?.(file);
|
|
13189
|
+
continue;
|
|
13190
|
+
}
|
|
13004
13191
|
const pid = typeof row.pid === "number" ? row.pid : Number(file.slice(0, -5));
|
|
13005
13192
|
if (typeof row.sessionId !== "string" || !row.sessionId.trim() || !Number.isInteger(pid) || pid <= 0) continue;
|
|
13006
13193
|
if (!holds(pid, row.procStart, check)) continue;
|
|
@@ -13042,10 +13229,14 @@ function localHolders(deps = {}) {
|
|
|
13042
13229
|
if (!/^\d+\.json$/.test(file)) continue;
|
|
13043
13230
|
let row;
|
|
13044
13231
|
try {
|
|
13045
|
-
row =
|
|
13232
|
+
row = parseSessionRecord(read3(join6(dir, file)));
|
|
13046
13233
|
} catch {
|
|
13047
13234
|
continue;
|
|
13048
13235
|
}
|
|
13236
|
+
if (!row) {
|
|
13237
|
+
deps.unreadable?.(file);
|
|
13238
|
+
continue;
|
|
13239
|
+
}
|
|
13049
13240
|
const pid = typeof row.pid === "number" ? row.pid : Number(file.slice(0, -5));
|
|
13050
13241
|
if (typeof row.sessionId !== "string" || !row.sessionId.trim() || !Number.isInteger(pid) || pid <= 0) continue;
|
|
13051
13242
|
const slot = sessionSlot(row.sessionId);
|
|
@@ -14457,10 +14648,12 @@ var zodToJsonSchema2 = (schema, options) => {
|
|
|
14457
14648
|
// ../../packages/schema/dist/index.js
|
|
14458
14649
|
var OPTIONS_MIN2 = 2;
|
|
14459
14650
|
var OPTIONS_MAX2 = 6;
|
|
14651
|
+
var OPTION_LABEL_MAX2 = 50;
|
|
14652
|
+
var OPTION_HINT_MAX2 = 200;
|
|
14460
14653
|
var OptionSchema2 = external_exports.object({
|
|
14461
14654
|
id: external_exports.string(),
|
|
14462
14655
|
label: external_exports.string(),
|
|
14463
|
-
hint: external_exports.string().max(500).describe(
|
|
14656
|
+
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(),
|
|
14464
14657
|
// .describe() flows into the MCP contact JSON schema (zodToJsonSchema), so
|
|
14465
14658
|
// the constraints below are what an agent reads when deciding to use these.
|
|
14466
14659
|
html: external_exports.string().max(16384).describe(
|
|
@@ -14474,8 +14667,10 @@ var OptionInputSchema2 = external_exports.object({
|
|
|
14474
14667
|
id: external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9_.:-]+$/).optional().describe(
|
|
14475
14668
|
`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.`
|
|
14476
14669
|
),
|
|
14477
|
-
|
|
14478
|
-
hint
|
|
14670
|
+
// SHORT CHOICES (owner, 2026-10-07: "options label should be maybe max 50 characters and the hint 200"):
|
|
14671
|
+
// a label is one choice the person reads, or hears on a call; a hint says its consequence in a line.
|
|
14672
|
+
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.`),
|
|
14673
|
+
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(),
|
|
14479
14674
|
htmlPreview: OptionSchema2.shape.html,
|
|
14480
14675
|
imgUrl: OptionSchema2.shape.image
|
|
14481
14676
|
}).strict();
|
|
@@ -14577,15 +14772,48 @@ var UpdateInputSchema2 = external_exports.object({
|
|
|
14577
14772
|
message: external_exports.string().trim().min(1).describe(`The one point, at most ${ASK_MAX2} characters; the context goes in units.`),
|
|
14578
14773
|
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."),
|
|
14579
14774
|
units: units2,
|
|
14775
|
+
// A REPORT KEEPS ITS TAP (#2799, #3260; owner, 2026-10-08, settling #2776: "keep the reports open
|
|
14776
|
+
// until the user acknowledges them"). A report asks nothing, so it opens no Question -- but it
|
|
14777
|
+
// still waits to be acknowledged, and it carries the ONE tap that closes it. Never a decision: the
|
|
14778
|
+
// tap lands as a contribution, the person's own words on the Goal, because there is no Question
|
|
14779
|
+
// for it to answer.
|
|
14780
|
+
//
|
|
14781
|
+
// AND ITS WORDS ARE WRITTEN FOR THIS REPORT (owner, 2026-10-08, Goal 3c12d3c6: "Agent
|
|
14782
|
+
// acknowledgments vary (e.g. 'sounds good, I will test later') instead of always 'got it'"). `Got
|
|
14783
|
+
// it` and `Noted` say only that a card was cleared, where "Sounds good, I'll test later" says the
|
|
14784
|
+
// work was RECEIVED and not verified -- the distinction a session spent an hour recovering from a
|
|
14785
|
+
// call transcript on 2026-10-07, having read "not quite" as a defect report when it meant *not
|
|
14786
|
+
// tested yet*. The words are worth writing because the agent reads them back.
|
|
14787
|
+
//
|
|
14788
|
+
// ONE STRING, NOT A LIST (owner, 2026-10-08 15:47, Goal 31a1e2ac, asked whether an agent should
|
|
14789
|
+
// write up to four of them: "just on the cards, but it should be more like just they should only
|
|
14790
|
+
// get one basically string value that they can enter that replaces got it"). A report has exactly
|
|
14791
|
+
// one way to close, so the only thing an agent writes is what that button SAYS. #3260's 1..6
|
|
14792
|
+
// `options` and this field are one job: the list is deleted, not deprecated (ONE JOB, ONE
|
|
14793
|
+
// MECHANISM). It rides the door as the report's single stored option (`storedOption`,
|
|
14794
|
+
// `apps/api/src/goal/router.ts`), so no reader below the door learns a second shape.
|
|
14795
|
+
reply: external_exports.string().trim().min(1).describe(
|
|
14796
|
+
`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.`
|
|
14797
|
+
).optional(),
|
|
14580
14798
|
userExplicitlyRequested: userExplicitlyRequested2
|
|
14581
|
-
}).strict().superRefine((u, ctx) =>
|
|
14799
|
+
}).strict().superRefine((u, ctx) => {
|
|
14800
|
+
refuseOverCaps2(u.message, u.units, "message", ctx);
|
|
14801
|
+
if (u.reply && u.reply.length > OPTION_LABEL_MAX2) {
|
|
14802
|
+
ctx.addIssue({
|
|
14803
|
+
code: external_exports.ZodIssueCode.custom,
|
|
14804
|
+
path: ["reply"],
|
|
14805
|
+
params: { refusal: "reply_too_long", length: u.reply.length, cap: OPTION_LABEL_MAX2 },
|
|
14806
|
+
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.`
|
|
14807
|
+
});
|
|
14808
|
+
}
|
|
14809
|
+
});
|
|
14582
14810
|
var questionFields2 = {
|
|
14583
14811
|
question: external_exports.string().trim().min(1).describe(
|
|
14584
14812
|
`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.`
|
|
14585
14813
|
),
|
|
14586
14814
|
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."),
|
|
14587
14815
|
units: units2,
|
|
14588
|
-
options: external_exports.array(OptionInputSchema2).min(1).max(6).optional().describe("The choices, when you have them. Each label stands on its own."),
|
|
14816
|
+
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."),
|
|
14589
14817
|
// HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
|
|
14590
14818
|
// brain_prompts.md §1: structured agent questions need no model). The app draws all three.
|
|
14591
14819
|
pickMode: external_exports.enum(["one", "many", "rank"]).optional().describe(
|
|
@@ -14662,6 +14890,8 @@ QUESTIONS: one question per object; several questions are several objects in one
|
|
|
14662
14890
|
|
|
14663
14891
|
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.
|
|
14664
14892
|
|
|
14893
|
+
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".
|
|
14894
|
+
|
|
14665
14895
|
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.
|
|
14666
14896
|
|
|
14667
14897
|
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\`).`;
|
|
@@ -14705,7 +14935,8 @@ var UpdateGoalSchema2 = external_exports.object({
|
|
|
14705
14935
|
}).strict();
|
|
14706
14936
|
var changedGoal2 = external_exports.string().uuid().describe("The Goal to change, as a read shows it.");
|
|
14707
14937
|
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.");
|
|
14708
|
-
var
|
|
14938
|
+
var GOAL_OUTCOME_MAX2 = 500;
|
|
14939
|
+
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.`);
|
|
14709
14940
|
var CreateChange2 = external_exports.object({
|
|
14710
14941
|
kind: external_exports.literal("create"),
|
|
14711
14942
|
title: goalTitle2,
|
|
@@ -14731,7 +14962,7 @@ var DependencyChange2 = external_exports.object({
|
|
|
14731
14962
|
goalId: changedGoal2,
|
|
14732
14963
|
dependsOnGoalId: external_exports.string().uuid().describe("The Goal it waits on."),
|
|
14733
14964
|
action: external_exports.enum(["start", "complete"]).describe("What waits: starting goalId, or completing it."),
|
|
14734
|
-
reason: external_exports.string().trim().min(1).max(
|
|
14965
|
+
reason: external_exports.string().trim().min(1).max(250).optional().describe("Why, at most 250 characters, as an explanation, never as policy.")
|
|
14735
14966
|
}).strict();
|
|
14736
14967
|
var read2 = { revision: external_exports.number().int().positive().optional() };
|
|
14737
14968
|
var GoalChangeSchema2 = external_exports.discriminatedUnion("kind", [
|
|
@@ -14755,7 +14986,7 @@ var ManageGoalsSchema2 = external_exports.object({
|
|
|
14755
14986
|
var ManageGoalsToolSchema2 = external_exports.object({
|
|
14756
14987
|
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.")
|
|
14757
14988
|
}).strict().superRefine((v, ctx) => editNames2(v.changes, ctx));
|
|
14758
|
-
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.";
|
|
14989
|
+
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.";
|
|
14759
14990
|
var GetGoalSchema2 = external_exports.object({
|
|
14760
14991
|
goalId: external_exports.string().uuid(),
|
|
14761
14992
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
@@ -14773,7 +15004,7 @@ var SearchToolSchema2 = external_exports.object({
|
|
|
14773
15004
|
goalId: external_exports.string().uuid().optional(),
|
|
14774
15005
|
limit: external_exports.number().int().min(1).max(20).optional()
|
|
14775
15006
|
}).strict();
|
|
14776
|
-
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
|
|
15007
|
+
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.";
|
|
14777
15008
|
var CheckActivitySchema2 = external_exports.object({}).strict();
|
|
14778
15009
|
var FEEDBACK_TEXT_MAX2 = 5e4;
|
|
14779
15010
|
var SendFeedbackSchema2 = external_exports.object({
|
|
@@ -14836,6 +15067,7 @@ var QuestionChangeSchema2 = external_exports.union([
|
|
|
14836
15067
|
external_exports.object({
|
|
14837
15068
|
kind: external_exports.literal("create"),
|
|
14838
15069
|
text: external_exports.string().min(1),
|
|
15070
|
+
summary: external_exports.string().optional(),
|
|
14839
15071
|
answererId: participant2,
|
|
14840
15072
|
goal: ResultRefSchema2.optional(),
|
|
14841
15073
|
blocks: external_exports.array(external_exports.object({ question: ResultRefSchema2 }).strict()),
|
|
@@ -14886,12 +15118,12 @@ var BrainSearchSchema2 = external_exports.object({
|
|
|
14886
15118
|
}).strict();
|
|
14887
15119
|
var BrainResultSchema2 = external_exports.object({
|
|
14888
15120
|
messages: external_exports.array(BrainMessageSchema2),
|
|
14889
|
-
entries: external_exports.array(external_exports.object({ key: key2, sources: external_exports.array(SourceSchema2).min(1), goals: external_exports.array(ResultRefSchema2) }).strict()),
|
|
15121
|
+
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()),
|
|
14890
15122
|
questions: external_exports.array(external_exports.object({ key: key2, entryKeys: external_exports.array(key2), change: QuestionChangeSchema2 }).strict()),
|
|
14891
15123
|
answers: external_exports.array(external_exports.object({
|
|
14892
15124
|
question: ResultRefSchema2,
|
|
14893
15125
|
entryKeys: external_exports.array(key2).min(1),
|
|
14894
|
-
summary: external_exports.string()
|
|
15126
|
+
summary: external_exports.string(),
|
|
14895
15127
|
selectedOptionIds: external_exports.array(external_exports.string().min(1)).optional()
|
|
14896
15128
|
}).strict()),
|
|
14897
15129
|
goals: external_exports.array(external_exports.object({ key: key2, entryKeys: external_exports.array(key2), change: BrainGoalChangeSchema2 }).strict()),
|
|
@@ -14936,12 +15168,15 @@ var TalkerReplySchema2 = external_exports.object({
|
|
|
14936
15168
|
messages: external_exports.array(external_exports.object({ key: str22, to: str22, text: str22, about: strs22, blocks: str22 }).strict()),
|
|
14937
15169
|
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
14938
15170
|
then: external_exports.enum(["listen", "hold", "end"]),
|
|
14939
|
-
/**
|
|
14940
|
-
|
|
14941
|
-
|
|
15171
|
+
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
15172
|
+
* IDs, the line handles, how they replied in under ten words (keeping any condition), and `defer`:
|
|
15173
|
+
* "" when their words settle the item, else what a reply that puts it off waits for -- "call" (the
|
|
15174
|
+
* end of this call), a question's handle (that question's answer) or an ISO time. */
|
|
15175
|
+
replies: external_exports.array(external_exports.object({ item: str22, options: strs22, lines: strs22, summary: str22, defer: str22 }).strict()),
|
|
15176
|
+
/** The line handles that tell the talker how to run this call; they last until it ends and reach no agent. */
|
|
14942
15177
|
instruction: strs22,
|
|
14943
|
-
/** The line handles
|
|
14944
|
-
*
|
|
15178
|
+
/** The line handles the filer acts on, and the only ones it reads (3e): new work, a change to existing
|
|
15179
|
+
* work, feedback on Paigy, anything said to an agent, and any rule meant to outlast the call. */
|
|
14945
15180
|
file: strs22,
|
|
14946
15181
|
/** Evidence to fetch for a second round: a search, an item's full text, or an agent by name. */
|
|
14947
15182
|
need: external_exports.object({ kind: external_exports.enum(["search", "item", "agent", ""]), text: str22 }).strict(),
|
|
@@ -14954,8 +15189,9 @@ var TALKER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
|
14954
15189
|
var str32 = external_exports.string();
|
|
14955
15190
|
var strs32 = external_exports.array(external_exports.string());
|
|
14956
15191
|
var FilerReplySchema2 = external_exports.object({
|
|
14957
|
-
/** Which Goals each new line goes on: a line's handle
|
|
14958
|
-
|
|
15192
|
+
/** Which Goals each new line goes on: a line's handle, Goal handles (or keys of new Goals), and what
|
|
15193
|
+
* the line says in at most ten words. */
|
|
15194
|
+
filed: external_exports.array(external_exports.object({ line: str32, goals: strs32, summary: str32 }).strict()),
|
|
14959
15195
|
goals: external_exports.array(external_exports.object({
|
|
14960
15196
|
op: external_exports.enum(["create", "edit", "complete", "cancel", "reopen", "assign", "defer", "move", "block", "unblock"]),
|
|
14961
15197
|
key: str32,
|
|
@@ -14974,6 +15210,7 @@ var FilerReplySchema2 = external_exports.object({
|
|
|
14974
15210
|
key: str32,
|
|
14975
15211
|
question: str32,
|
|
14976
15212
|
text: str32,
|
|
15213
|
+
summary: str32,
|
|
14977
15214
|
to: str32,
|
|
14978
15215
|
goal: str32,
|
|
14979
15216
|
options: external_exports.array(external_exports.object({ id: str32, label: str32 }).strict()),
|
|
@@ -14987,6 +15224,60 @@ var FilerReplySchema2 = external_exports.object({
|
|
|
14987
15224
|
var FILER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
14988
15225
|
zodToJsonSchema2(FilerReplySchema2, { $refStrategy: "none" })
|
|
14989
15226
|
);
|
|
15227
|
+
var ExplainerReplySchema2 = external_exports.object({
|
|
15228
|
+
/** The explanation, as sentences, spoken in order. */
|
|
15229
|
+
say: external_exports.array(external_exports.string()),
|
|
15230
|
+
/** Questions to an agent about gaps in what it read: the agent's handle and the question. */
|
|
15231
|
+
followUps: external_exports.array(external_exports.object({ to: external_exports.string(), text: external_exports.string() }).strict())
|
|
15232
|
+
}).strict();
|
|
15233
|
+
var EXPLAINER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
15234
|
+
zodToJsonSchema2(ExplainerReplySchema2, { $refStrategy: "none" })
|
|
15235
|
+
);
|
|
15236
|
+
var DISTILLER_DEFECTS2 = [
|
|
15237
|
+
"reasked",
|
|
15238
|
+
// a question put to the person again after they answered it
|
|
15239
|
+
"unsupported",
|
|
15240
|
+
// a fact stated that nothing in the input supports
|
|
15241
|
+
"unsaid",
|
|
15242
|
+
// an agent's answer arrived during the call and was never said
|
|
15243
|
+
"skipped",
|
|
15244
|
+
// an agenda item never put to the person
|
|
15245
|
+
"unsaved",
|
|
15246
|
+
// something the person answered or asked for did not save, was refused or dropped
|
|
15247
|
+
"ignored",
|
|
15248
|
+
// the person asked or said something and was never answered (the critic's ignored_a_question)
|
|
15249
|
+
"promised",
|
|
15250
|
+
// said it did or would do something, and nothing shows it done (the critic's broken_promise)
|
|
15251
|
+
"dropped"
|
|
15252
|
+
// something it had to say never played and was never said later (owner, 2026-10-08, call 65433a9d)
|
|
15253
|
+
];
|
|
15254
|
+
var DistillerReplySchema2 = external_exports.object({
|
|
15255
|
+
defects: external_exports.array(external_exports.object({
|
|
15256
|
+
kind: external_exports.enum(DISTILLER_DEFECTS2),
|
|
15257
|
+
/** The handles of the lines it shows in. */
|
|
15258
|
+
lines: external_exports.array(external_exports.string()),
|
|
15259
|
+
/** The handle of the record it concerns, or "". */
|
|
15260
|
+
about: external_exports.string(),
|
|
15261
|
+
/** One plain sentence, in words, never handles: it is what the feedback row keeps. */
|
|
15262
|
+
why: external_exports.string()
|
|
15263
|
+
}).strict())
|
|
15264
|
+
}).strict();
|
|
15265
|
+
var DISTILLER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
15266
|
+
zodToJsonSchema2(DistillerReplySchema2, { $refStrategy: "none" })
|
|
15267
|
+
);
|
|
15268
|
+
var TIDY_AUDIENCES2 = ["talker", "filer", "agents"];
|
|
15269
|
+
var TidyReplySchema2 = external_exports.object({
|
|
15270
|
+
lessons: external_exports.array(external_exports.object({
|
|
15271
|
+
lesson: external_exports.string(),
|
|
15272
|
+
op: external_exports.enum(["keep", "revise", "withdraw"]),
|
|
15273
|
+
text: external_exports.string(),
|
|
15274
|
+
for: external_exports.enum([...TIDY_AUDIENCES2, ""]),
|
|
15275
|
+
into: external_exports.string()
|
|
15276
|
+
}).strict())
|
|
15277
|
+
}).strict();
|
|
15278
|
+
var TIDY_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
15279
|
+
zodToJsonSchema2(TidyReplySchema2, { $refStrategy: "none" })
|
|
15280
|
+
);
|
|
14990
15281
|
function entryWords(entry) {
|
|
14991
15282
|
const content = entry.content;
|
|
14992
15283
|
if (content && "sealed" in content) return "";
|
|
@@ -15006,6 +15297,8 @@ function entryWords(entry) {
|
|
|
15006
15297
|
}
|
|
15007
15298
|
var LIVE_MS2 = 3 * 6e4;
|
|
15008
15299
|
var WORKING_MS2 = 60 * 6e4;
|
|
15300
|
+
var WORKING_TIER_MS2 = 2 * 60 * 6e4;
|
|
15301
|
+
var RECENT_TIER_MS2 = 24 * 60 * 6e4;
|
|
15009
15302
|
var ContextSchema2 = external_exports.object({
|
|
15010
15303
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
15011
15304
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
@@ -15350,6 +15643,10 @@ var AgendaTurnSchema2 = external_exports.object({
|
|
|
15350
15643
|
* so "who said that" survives the call, and it rides PER TURN because a coalesced call
|
|
15351
15644
|
* speaks for several agents — the turn is the only place that knows which. */
|
|
15352
15645
|
agentId: external_exports.string().optional(),
|
|
15646
|
+
/** ITS QUESTION IS ON THE TABLE (#3292): a talker line about a question the person owes. The walk tells
|
|
15647
|
+
* the phone (`asking`, so the agenda goes gold and its options show) without treating the line as a
|
|
15648
|
+
* question turn — the talker owns re-asking and the waits, so `question` stays null. */
|
|
15649
|
+
onTable: external_exports.boolean().optional(),
|
|
15353
15650
|
select: SelectShapeSchema2.optional(),
|
|
15354
15651
|
options: external_exports.array(OptionSchema2.omit({ id: true })).optional(),
|
|
15355
15652
|
/* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
|
|
@@ -15424,9 +15721,16 @@ var InboxItemSchema2 = external_exports.object({
|
|
|
15424
15721
|
/** ITS OPTIONS, WHEN THERE IS SOMETHING TO SEE (owner, 2026-09-25: "Yes, add it"): the options
|
|
15425
15722
|
* its need offers, exactly as its own card carries them, present only when one of them has a
|
|
15426
15723
|
* preview (`html` or `image`). The call screen opens them from the agenda row, so a preview is
|
|
15427
|
-
* never re-sent as a second card to be seen mid-call. Words-only options are
|
|
15428
|
-
|
|
15429
|
-
|
|
15724
|
+
* never re-sent as a second card to be seen mid-call. Words-only options are `choices`. */
|
|
15725
|
+
options: external_exports.array(OptionSchema2).optional(),
|
|
15726
|
+
/** ITS OPTIONS WHEN THEY ARE WORDS (owner, 2026-10-02, on a call: "whenever there are pre-made
|
|
15727
|
+
* options … now for a checklist, we should also show them on the screen"; design C, 2026-10-08):
|
|
15728
|
+
* numbered chips above the call's controls, so a checklist is never only something read aloud. A
|
|
15729
|
+
* field of its own so `options` keeps meaning "previews": a phone on an older bundle opens the
|
|
15730
|
+
* preview layout for any `options`, and must see exactly what it did. */
|
|
15731
|
+
choices: external_exports.array(OptionSchema2).optional(),
|
|
15732
|
+
/** HOW MANY MAY BE PICKED, beside its `choices`: one, any (`many`) or an order (`rank`). */
|
|
15733
|
+
select: external_exports.enum(["one", "many", "rank"]).optional()
|
|
15430
15734
|
})).optional(),
|
|
15431
15735
|
visuals: external_exports.array(VisualSchema2).optional(),
|
|
15432
15736
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
@@ -15686,7 +15990,7 @@ var ConnectionSummarySchema2 = external_exports.object({
|
|
|
15686
15990
|
* desktop app while open. Null = never seen; stale = offline. */
|
|
15687
15991
|
lastSeenAt: external_exports.string().datetime().nullable().optional(),
|
|
15688
15992
|
/** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
|
|
15689
|
-
* its Goals —
|
|
15993
|
+
* its Goals — wrote on one or recorded an operation (`tokens.last_worked_at`). Within
|
|
15690
15994
|
* `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
|
|
15691
15995
|
lastWorkedAt: external_exports.string().datetime().nullable().optional(),
|
|
15692
15996
|
/** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
|
|
@@ -15863,7 +16167,11 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
15863
16167
|
from: external_exports.enum(["person", "agent"]),
|
|
15864
16168
|
said: external_exports.string(),
|
|
15865
16169
|
at: external_exports.string(),
|
|
15866
|
-
entryId: external_exports.string()
|
|
16170
|
+
entryId: external_exports.string(),
|
|
16171
|
+
/** ITS SUMMARY IN AT MOST TEN WORDS (owner, 2026-10-08: "the same switch to the more summarized sources
|
|
16172
|
+
* at the work tab top level screen too"): what the talker or filer wrote on the Entry; absent when
|
|
16173
|
+
* none was. The Work row reads it before `said`. */
|
|
16174
|
+
summary: external_exports.string().optional()
|
|
15867
16175
|
}).nullable().optional(),
|
|
15868
16176
|
/** WHEN THIS PERSON LAST PUT A HAND ON IT THEMSELVES (owner, Paigy Goal 16d18f51, 2026-09-30):
|
|
15869
16177
|
* the newest Entry on the Goal they wrote, of any kind — a line they added, a reply to a note, an
|
|
@@ -15884,7 +16192,7 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
15884
16192
|
*
|
|
15885
16193
|
* IT IS THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK. Nothing here reasons about which
|
|
15886
16194
|
* writes change which field — the comparison is over the bytes the phone is holding, so a fact
|
|
15887
|
-
* the row shows that no `updated_at` moves for (
|
|
16195
|
+
* the row shows that no `updated_at` moves for (`active` lapsing, a dependency's state, a
|
|
15888
16196
|
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
15889
16197
|
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
15890
16198
|
rev: external_exports.string().optional()
|
|
@@ -16198,17 +16506,43 @@ var CallRecapSchema2 = external_exports.object({
|
|
|
16198
16506
|
title: external_exports.string(),
|
|
16199
16507
|
owner: external_exports.string(),
|
|
16200
16508
|
state: external_exports.string(),
|
|
16509
|
+
/** Whether the call put it to the person. An item it carried and never said has no row on the
|
|
16510
|
+
* review (owner, 2026-10-08: "why do we get the third one when it didn't happen in the call?"). */
|
|
16511
|
+
presented: external_exports.boolean(),
|
|
16201
16512
|
questions: external_exports.array(external_exports.object({ id: external_exports.string().uuid(), state: external_exports.string(), title: external_exports.string() })),
|
|
16202
|
-
/** `words` is always what they SAID, verbatim — the record, never replaced. `
|
|
16203
|
-
*
|
|
16204
|
-
*
|
|
16205
|
-
*
|
|
16206
|
-
* anything that is not an answer. */
|
|
16513
|
+
/** `words` is always what they SAID, verbatim — the record, never replaced. `summary` is what
|
|
16514
|
+
* the line says in ten words (owner, 2026-10-07): the filer's, or the talker's for the answer it
|
|
16515
|
+
* gave, so the row scans like a chosen option and their own words stay under it. Absent when
|
|
16516
|
+
* neither wrote one. */
|
|
16207
16517
|
/** `about` is the request the line answered (its question), null for words that answered none —
|
|
16208
16518
|
* the key the screen groups on, so one question is one row however many times it was answered. */
|
|
16209
|
-
|
|
16519
|
+
/** `reply` is the reply the line makes up (an answer, an ok, a deferral), so one reply is one row
|
|
16520
|
+
* however many lines it cites; `at` is the call lines it came from, so a filed line an answer already
|
|
16521
|
+
* said is not drawn again (owner, 2026-10-07: "fix them all"); `replyKind` says which replies are answers. */
|
|
16522
|
+
lines: external_exports.array(external_exports.object({
|
|
16523
|
+
entryId: external_exports.string().uuid(),
|
|
16524
|
+
words: external_exports.string(),
|
|
16525
|
+
summary: external_exports.string().optional(),
|
|
16526
|
+
about: external_exports.string().nullable().optional(),
|
|
16527
|
+
reply: external_exports.string().nullable().optional(),
|
|
16528
|
+
replyKind: external_exports.string().nullable().optional(),
|
|
16529
|
+
at: external_exports.array(external_exports.number()).optional()
|
|
16530
|
+
}))
|
|
16210
16531
|
})),
|
|
16211
16532
|
unfiled: external_exports.array(external_exports.object({ lineId: external_exports.string().uuid(), words: external_exports.string(), atMs: external_exports.number() })),
|
|
16533
|
+
/** THE PUBLIC TRANSCRIPT — every line said on the call, in the order it was said, and WHO SAID IT
|
|
16534
|
+
* (owner, 2026-10-08: "the public view should only have the lines and who said them, and that's
|
|
16535
|
+
* it"; `docs/brain/phase-three/modular.md` build step 3). `speaker` is the role: "user" is the
|
|
16536
|
+
* person, "agent" an agent speaking, named in `agent`. The timeline's action lines are ours, not
|
|
16537
|
+
* theirs, and are not here; nor is a sealed line, which the server has no words for. It stands
|
|
16538
|
+
* BESIDE `topics` and `unfiled` — what the call did with the words — never instead of them. */
|
|
16539
|
+
said: external_exports.array(external_exports.object({
|
|
16540
|
+
lineId: external_exports.string().uuid(),
|
|
16541
|
+
speaker: external_exports.enum(["user", "agent"]),
|
|
16542
|
+
agent: external_exports.object({ id: external_exports.string().nullable(), name: external_exports.string().nullable() }).nullable(),
|
|
16543
|
+
words: external_exports.string(),
|
|
16544
|
+
atMs: external_exports.number()
|
|
16545
|
+
})),
|
|
16212
16546
|
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
16213
16547
|
});
|
|
16214
16548
|
|
|
@@ -16610,7 +16944,8 @@ function startSession(opts) {
|
|
|
16610
16944
|
if (!existsSync4(cwd)) {
|
|
16611
16945
|
queueMicrotask(() => opts.onEvent({ kind: "error", message: `workspace does not exist: ${cwd}` }));
|
|
16612
16946
|
}
|
|
16613
|
-
|
|
16947
|
+
let codexConfig = opts.harness === "codex" ? JSON.parse(process.env.CODEX_CONFIG || "{}") : null;
|
|
16948
|
+
if (opts.model && opts.harness === "codex") codexConfig = { ...codexConfig, model: opts.model };
|
|
16614
16949
|
if (codexConfig) {
|
|
16615
16950
|
const key3 = "mcp_servers.paigy.env_vars";
|
|
16616
16951
|
const configPath = join9(process.env.CODEX_HOME || join9(homedir9(), ".codex"), "config.toml");
|
|
@@ -16636,6 +16971,8 @@ function startSession(opts) {
|
|
|
16636
16971
|
PATH: [process.env.PATH, ...probeDirs()].filter(Boolean).join(delimiter3),
|
|
16637
16972
|
...opts.token ? { PAIGY_TOKEN: opts.token } : {},
|
|
16638
16973
|
...opts.instance ? { PAIGY_INSTANCE_ID: opts.instance } : {},
|
|
16974
|
+
...opts.model && opts.harness === "claude" ? { CLAUDE_MODEL_CONFIG: JSON.stringify({ provider: "anthropic", model: opts.model }) } : {},
|
|
16975
|
+
...opts.model && opts.harness === "agy" ? { AGY_MODEL_CONFIG: JSON.stringify({ model: opts.model }) } : {},
|
|
16639
16976
|
...codexConfig ? { CODEX_CONFIG: JSON.stringify(codexConfig) } : {},
|
|
16640
16977
|
...opts.session ?? opts.resume ? { PAIGY_SESSION_ID: opts.session ?? opts.resume } : {},
|
|
16641
16978
|
// A PUMP DRIVES THIS PROCESS (2026-09-22, #2265): every run `runHarness` starts has the
|
|
@@ -17604,7 +17941,7 @@ var claimedRun = (sessionId2) => {
|
|
|
17604
17941
|
const slot = sessionSlot(sessionId2);
|
|
17605
17942
|
return { slot, key: runKey(slot) };
|
|
17606
17943
|
};
|
|
17607
|
-
var launchRun = (spec) => ({ session: spec.sessionId });
|
|
17944
|
+
var launchRun = (spec) => ({ session: spec.sessionId, model: spec.model });
|
|
17608
17945
|
var BOUND_ELSEWHERE_MS = 10 * 6e4;
|
|
17609
17946
|
var boundElsewhere = /* @__PURE__ */ new Map();
|
|
17610
17947
|
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";
|
|
@@ -17713,7 +18050,7 @@ function startHost(opts) {
|
|
|
17713
18050
|
});
|
|
17714
18051
|
void heartbeat({
|
|
17715
18052
|
version: VERSION,
|
|
17716
|
-
harnesses: detectAll().map((a) => ({ name: a.name, label: a.label, status: a.status })),
|
|
18053
|
+
harnesses: detectAll().map((a) => ({ name: a.name, label: a.label, status: a.status, models: a.models })),
|
|
17717
18054
|
workspaces: listWorkspaces(opts.wsDeps),
|
|
17718
18055
|
// The repos inside them, so a session started for work on one opens IN it (Goal 26982211).
|
|
17719
18056
|
repos: listRepos(opts.wsDeps)
|
|
@@ -17805,9 +18142,14 @@ function startHost(opts) {
|
|
|
17805
18142
|
let lastPresence = /* @__PURE__ */ new Map();
|
|
17806
18143
|
async function evidence() {
|
|
17807
18144
|
const presence = await presenceMap();
|
|
17808
|
-
lastHolders = localHolders()
|
|
18145
|
+
lastHolders = localHolders({ unreadable: (file) => {
|
|
18146
|
+
if (unreadable.has(file)) return;
|
|
18147
|
+
unreadable.add(file);
|
|
18148
|
+
opts.log(`\u25CC session record ${file} has no object in it \u2014 the process it names reads as not running here`);
|
|
18149
|
+
} });
|
|
17809
18150
|
return { presence, holders: lastHolders };
|
|
17810
18151
|
}
|
|
18152
|
+
const unreadable = /* @__PURE__ */ new Set();
|
|
17811
18153
|
let lastHolders = /* @__PURE__ */ new Map();
|
|
17812
18154
|
async function wakeSlot(slot, { presence, holders }, cause = "sweep") {
|
|
17813
18155
|
{
|