@paigy/harness 0.3.35 → 0.3.37
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 +923 -409
- package/dist/main.js +845 -340
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -901,14 +901,14 @@ var require_regex = __commonJS({
|
|
|
901
901
|
var TEST_KANJI = new RegExp("^" + kanji + "$");
|
|
902
902
|
var TEST_NUMERIC = new RegExp("^" + numeric + "$");
|
|
903
903
|
var TEST_ALPHANUMERIC = new RegExp("^[A-Z0-9 $%*+\\-./:]+$");
|
|
904
|
-
exports.testKanji = function testKanji(
|
|
905
|
-
return TEST_KANJI.test(
|
|
904
|
+
exports.testKanji = function testKanji(str5) {
|
|
905
|
+
return TEST_KANJI.test(str5);
|
|
906
906
|
};
|
|
907
|
-
exports.testNumeric = function testNumeric(
|
|
908
|
-
return TEST_NUMERIC.test(
|
|
907
|
+
exports.testNumeric = function testNumeric(str5) {
|
|
908
|
+
return TEST_NUMERIC.test(str5);
|
|
909
909
|
};
|
|
910
|
-
exports.testAlphanumeric = function testAlphanumeric(
|
|
911
|
-
return TEST_ALPHANUMERIC.test(
|
|
910
|
+
exports.testAlphanumeric = function testAlphanumeric(str5) {
|
|
911
|
+
return TEST_ALPHANUMERIC.test(str5);
|
|
912
912
|
};
|
|
913
913
|
}
|
|
914
914
|
});
|
|
@@ -1410,13 +1410,13 @@ var require_segments = __commonJS({
|
|
|
1410
1410
|
var Regex = require_regex();
|
|
1411
1411
|
var Utils = require_utils();
|
|
1412
1412
|
var dijkstra = require_dijkstra();
|
|
1413
|
-
function getStringByteLength(
|
|
1414
|
-
return unescape(encodeURIComponent(
|
|
1413
|
+
function getStringByteLength(str5) {
|
|
1414
|
+
return unescape(encodeURIComponent(str5)).length;
|
|
1415
1415
|
}
|
|
1416
|
-
function getSegments(regex, mode,
|
|
1416
|
+
function getSegments(regex, mode, str5) {
|
|
1417
1417
|
const segments = [];
|
|
1418
1418
|
let result;
|
|
1419
|
-
while ((result = regex.exec(
|
|
1419
|
+
while ((result = regex.exec(str5)) !== null) {
|
|
1420
1420
|
segments.push({
|
|
1421
1421
|
data: result[0],
|
|
1422
1422
|
index: result.index,
|
|
@@ -4250,13 +4250,13 @@ var require_svg_tag = __commonJS({
|
|
|
4250
4250
|
var Utils = require_utils2();
|
|
4251
4251
|
function getColorAttrib(color, attrib) {
|
|
4252
4252
|
const alpha = color.a / 255;
|
|
4253
|
-
const
|
|
4254
|
-
return alpha < 1 ?
|
|
4253
|
+
const str5 = attrib + '="' + color.hex + '"';
|
|
4254
|
+
return alpha < 1 ? str5 + " " + attrib + '-opacity="' + alpha.toFixed(2).slice(1) + '"' : str5;
|
|
4255
4255
|
}
|
|
4256
4256
|
function svgCmd(cmd, x, y) {
|
|
4257
|
-
let
|
|
4258
|
-
if (typeof y !== "undefined")
|
|
4259
|
-
return
|
|
4257
|
+
let str5 = cmd + x;
|
|
4258
|
+
if (typeof y !== "undefined") str5 += " " + y;
|
|
4259
|
+
return str5;
|
|
4260
4260
|
}
|
|
4261
4261
|
function qrToPath(data, size, margin) {
|
|
4262
4262
|
let path = "";
|
|
@@ -4589,7 +4589,7 @@ import { createHash, randomUUID } from "crypto";
|
|
|
4589
4589
|
import { readFileSync } from "fs";
|
|
4590
4590
|
import { homedir } from "os";
|
|
4591
4591
|
import { join } from "path";
|
|
4592
|
-
import { randomUUID as
|
|
4592
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
4593
4593
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
4594
4594
|
|
|
4595
4595
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
@@ -8634,11 +8634,9 @@ var coerce = {
|
|
|
8634
8634
|
var NEVER = INVALID;
|
|
8635
8635
|
|
|
8636
8636
|
// ../../packages/sdk/dist/index.js
|
|
8637
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
8638
8637
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync as readFileSync2, rmSync, statSync, writeFileSync } from "fs";
|
|
8639
8638
|
import { homedir as homedir2 } from "os";
|
|
8640
8639
|
import { join as join2 } from "path";
|
|
8641
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
8642
8640
|
var import_undici = __toESM(require_undici(), 1);
|
|
8643
8641
|
var require2 = __sdkCreateRequire(import.meta.url);
|
|
8644
8642
|
var CODEX_ENV = ["CODEX_THREAD_ID", "CODEX_SESSION_ID", "CODEX_HOME", "PAIGY_TOKEN", "PAIGY_SESSION_ID", "PAIGY_HARNESS", "PAIGY_INSTANCE_ID", "PAIGY_ON_WAKE"];
|
|
@@ -9925,21 +9923,31 @@ var zodToJsonSchema = (schema, options) => {
|
|
|
9925
9923
|
};
|
|
9926
9924
|
var OPTIONS_MIN = 2;
|
|
9927
9925
|
var OPTIONS_MAX = 6;
|
|
9926
|
+
var OPTION_LABEL_MAX = 50;
|
|
9927
|
+
var OPTION_HINT_MAX = 200;
|
|
9928
9928
|
var OptionSchema = external_exports.object({
|
|
9929
9929
|
id: external_exports.string(),
|
|
9930
9930
|
label: external_exports.string(),
|
|
9931
|
-
hint: external_exports.string().max(500).describe(
|
|
9931
|
+
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(),
|
|
9932
9932
|
// .describe() flows into the MCP contact JSON schema (zodToJsonSchema), so
|
|
9933
9933
|
// the constraints below are what an agent reads when deciding to use these.
|
|
9934
9934
|
html: external_exports.string().max(16384).describe(
|
|
9935
|
-
"Optional sandboxed HTML/CSS preview for a visual 'pick one' (shown in the option card). Untrusted-sandboxed: NO JavaScript, NO external network or images \u2014 inline CSS and data: URIs only; <=16KB. Rendered edge-to-edge in a responsive card that is 200pt tall (about 320pt wide on a phone, with the next option peeking beside it); make your HTML fit that viewport. Use for layout/CSS mockups, tables, diffs. For a hosted image use
|
|
9935
|
+
"Optional sandboxed HTML/CSS preview for a visual 'pick one' (shown in the option card). Untrusted-sandboxed: NO JavaScript, NO external network or images \u2014 inline CSS and data: URIs only; <=16KB. Rendered edge-to-edge in a responsive card that is 200pt tall (about 320pt wide on a phone, with the next option peeking beside it); make your HTML fit that viewport. Use for layout/CSS mockups, tables, diffs. For a hosted image use the image URL instead."
|
|
9936
9936
|
).optional(),
|
|
9937
9937
|
image: external_exports.string().url().describe(
|
|
9938
|
-
"Optional image URL rendered as the option's preview (plain image, not sandboxed). For agent-generated HTML/CSS mockups, use
|
|
9938
|
+
"Optional image URL rendered as the option's preview (plain image, not sandboxed). For agent-generated HTML/CSS mockups, use the HTML preview instead."
|
|
9939
9939
|
).optional()
|
|
9940
9940
|
});
|
|
9941
|
-
var OptionInputSchema =
|
|
9942
|
-
|
|
9941
|
+
var OptionInputSchema = external_exports.object({
|
|
9942
|
+
id: external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9_.:-]+$/).optional().describe(
|
|
9943
|
+
`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.`
|
|
9944
|
+
),
|
|
9945
|
+
// SHORT CHOICES (owner, 2026-10-07: "options label should be maybe max 50 characters and the hint 200"):
|
|
9946
|
+
// a label is one choice the person reads, or hears on a call; a hint says its consequence in a line.
|
|
9947
|
+
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.`),
|
|
9948
|
+
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(),
|
|
9949
|
+
htmlPreview: OptionSchema.shape.html,
|
|
9950
|
+
imgUrl: OptionSchema.shape.image
|
|
9943
9951
|
}).strict();
|
|
9944
9952
|
var NIGHT = { from: 23, to: 7 };
|
|
9945
9953
|
function draft2020(node) {
|
|
@@ -9971,41 +9979,162 @@ function mcpInputSchema(s) {
|
|
|
9971
9979
|
delete schema.$schema;
|
|
9972
9980
|
return draft2020(schema);
|
|
9973
9981
|
}
|
|
9974
|
-
var
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9982
|
+
var ASK_MAX = 250;
|
|
9983
|
+
var UNITS_MAX = 12;
|
|
9984
|
+
var UNIT_TITLE_MAX = 50;
|
|
9985
|
+
var UNIT_BODY_MAX = 200;
|
|
9986
|
+
var UnitInputSchema = external_exports.object({
|
|
9987
|
+
title: external_exports.string().trim().min(1).describe(`What this unit is about, at most ${UNIT_TITLE_MAX} characters. The app lists the titles; the person taps one to read its body.`),
|
|
9988
|
+
body: external_exports.string().trim().min(1).describe(`The unit itself, at most ${UNIT_BODY_MAX} characters.`)
|
|
9989
|
+
}).strict();
|
|
9990
|
+
function capRefusals(text, units22, field) {
|
|
9991
|
+
const out = [];
|
|
9992
|
+
const name = field === "text" ? "answer" : field;
|
|
9993
|
+
if (text.length > ASK_MAX) out.push({
|
|
9994
|
+
refusal: "ask_too_long",
|
|
9995
|
+
length: text.length,
|
|
9996
|
+
cap: ASK_MAX,
|
|
9997
|
+
path: field === "text" ? ["answer", "text"] : [field],
|
|
9998
|
+
message: `ask_too_long: the ${name} is ${text.length} characters; the cap is ${ASK_MAX}. Keep the one point in the ${name} and move the context into units.`
|
|
9999
|
+
});
|
|
10000
|
+
const all = units22 ?? [];
|
|
10001
|
+
if (all.length > UNITS_MAX) out.push({
|
|
10002
|
+
refusal: "too_many_units",
|
|
10003
|
+
length: all.length,
|
|
10004
|
+
cap: UNITS_MAX,
|
|
10005
|
+
path: ["units"],
|
|
10006
|
+
message: `too_many_units: ${all.length} units; the cap is ${UNITS_MAX}. Keep only the context this ${name} needs.`
|
|
10007
|
+
});
|
|
10008
|
+
all.forEach((unit, index) => {
|
|
10009
|
+
if (unit.title.length > UNIT_TITLE_MAX) out.push({
|
|
10010
|
+
refusal: "unit_title_too_long",
|
|
10011
|
+
index,
|
|
10012
|
+
length: unit.title.length,
|
|
10013
|
+
cap: UNIT_TITLE_MAX,
|
|
10014
|
+
path: ["units", index, "title"],
|
|
10015
|
+
message: `unit_title_too_long: units[${index}]'s title is ${unit.title.length} characters; the cap is ${UNIT_TITLE_MAX}.`
|
|
10016
|
+
});
|
|
10017
|
+
if (unit.body.length > UNIT_BODY_MAX) out.push({
|
|
10018
|
+
refusal: "unit_body_too_long",
|
|
10019
|
+
index,
|
|
10020
|
+
length: unit.body.length,
|
|
10021
|
+
cap: UNIT_BODY_MAX,
|
|
10022
|
+
path: ["units", index, "body"],
|
|
10023
|
+
message: `unit_body_too_long: units[${index}]'s body is ${unit.body.length} characters; the cap is ${UNIT_BODY_MAX}. Cut it into two units.`
|
|
10024
|
+
});
|
|
10025
|
+
});
|
|
10026
|
+
return out;
|
|
10027
|
+
}
|
|
10028
|
+
function refuseOverCaps(text, units22, field, ctx) {
|
|
10029
|
+
for (const { path, message, ...params } of capRefusals(text, units22, field)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message, params });
|
|
10030
|
+
}
|
|
10031
|
+
var questionHandle = external_exports.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/, "a question id, whole or its first eight characters");
|
|
10032
|
+
var NodeRef = external_exports.object({
|
|
10033
|
+
type: external_exports.enum(["goal", "question"]),
|
|
10034
|
+
id: external_exports.string().uuid()
|
|
10035
|
+
}).strict().describe("A Goal or a Question, by its id.");
|
|
10036
|
+
var BlockedAction = external_exports.enum(["start", "complete", "answer"]).describe("What waits: starting or completing a Goal, or answering a Question.");
|
|
10037
|
+
var reason = external_exports.string().trim().min(1).max(500).describe("Why, as an explanation, never as policy.");
|
|
10038
|
+
var actionFits = (b) => b.blocked.type === "goal" === (b.action !== "answer");
|
|
10039
|
+
var ACTION_FITS = "a goal is blocked from start or complete; a question from answer";
|
|
10040
|
+
var userExplicitlyRequested = external_exports.enum(["any", "call"]).optional().describe(
|
|
10041
|
+
`Only what the person said about THIS message, in so many words: "any" when they asked you to send it to them ("send it to my phone", "message me the result"), "call" when they asked to be called about it. It is then delivered (a call rings for the whole contact, and carries everything waiting on them); omitted, an update is only recorded as the Goal's progress unless the person needs it. Never set it to make a message louder.`
|
|
10042
|
+
);
|
|
10043
|
+
var units = external_exports.array(UnitInputSchema).optional().describe(
|
|
10044
|
+
`The context this message needs, as up to ${UNITS_MAX} units in reading order, each {title, body}: a title of at most ${UNIT_TITLE_MAX} characters and a body of at most ${UNIT_BODY_MAX}. One idea per unit. Leave it out when the main text says it all.`
|
|
10045
|
+
);
|
|
10046
|
+
var UpdateInputSchema = external_exports.object({
|
|
10047
|
+
message: external_exports.string().trim().min(1).describe(`The one point, at most ${ASK_MAX} characters; the context goes in units.`),
|
|
10048
|
+
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."),
|
|
10049
|
+
units,
|
|
10050
|
+
userExplicitlyRequested
|
|
10051
|
+
}).strict().superRefine((u, ctx) => refuseOverCaps(u.message, u.units, "message", ctx));
|
|
10052
|
+
var questionFields = {
|
|
10053
|
+
question: external_exports.string().trim().min(1).describe(
|
|
10054
|
+
`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.`
|
|
9980
10055
|
),
|
|
9981
|
-
|
|
10056
|
+
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."),
|
|
10057
|
+
units,
|
|
10058
|
+
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."),
|
|
9982
10059
|
// HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
|
|
9983
|
-
// brain_prompts.md §1: structured agent questions need no model).
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
select: external_exports.enum(["one", "many"]).optional().describe(
|
|
9987
|
-
'How the options are answered: "many" makes a checklist (they may want several, e.g. independent fixes), "one" (the default) a pick (choosing one rules out the others). Send "many" whenever more than one option could be wanted at once. The person can always answer in their own words as well. Ignored without options.'
|
|
10060
|
+
// brain_prompts.md §1: structured agent questions need no model). The app draws all three.
|
|
10061
|
+
pickMode: external_exports.enum(["one", "many", "rank"]).optional().describe(
|
|
10062
|
+
'How the options are answered: "one" (the default) a pick, for alternatives; "many" a checklist, for independent options they may want several of; "rank" an order of all of them. Only with options.'
|
|
9988
10063
|
),
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
)
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10064
|
+
// WHAT THE QUESTION HOLDS UP (brain_prompts.md §2.1): replaces `waiting: 'hard'`. A question that
|
|
10065
|
+
// blocks work asks for a call (the person's settings and ring cooldowns still decide).
|
|
10066
|
+
workItBlocks: external_exports.array(external_exports.object({ blocked: NodeRef, action: BlockedAction, reason: reason.optional() }).strict().refine(actionFits, ACTION_FITS)).min(1).max(10).optional().describe(
|
|
10067
|
+
"The work that cannot go on until this is answered: a Goal's start or completion, or another Question's answer. Only for work truly blocked; it asks for a call. Leave it out for a question you can keep working around."
|
|
10068
|
+
),
|
|
10069
|
+
userExplicitlyRequested
|
|
10070
|
+
};
|
|
10071
|
+
var refineQuestion = (q, ctx) => {
|
|
10072
|
+
refuseOverCaps(q.question, q.units, "question", ctx);
|
|
10073
|
+
if (q.pickMode && !q.options) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["pickMode"], message: "pickMode comes only with options" });
|
|
10074
|
+
const ids = (q.options ?? []).map((o) => o.id);
|
|
10075
|
+
if (ids.some((id22) => id22) && (ids.some((id22) => !id22) || new Set(ids).size !== ids.length)) {
|
|
10076
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["options"], message: "give every option a distinct id, or none" });
|
|
10077
|
+
}
|
|
10078
|
+
};
|
|
10079
|
+
var QuestionInputSchema = external_exports.object({
|
|
10080
|
+
questionId: external_exports.string().uuid().optional().describe("Optional: your own stable id for this question (a UUID), so a resend is the same question. Minted for you when omitted; the result returns it."),
|
|
10081
|
+
...questionFields
|
|
10082
|
+
}).strict().superRefine(refineQuestion);
|
|
10083
|
+
var QuestionWireSchema = external_exports.object({ questionId: external_exports.string().uuid(), ...questionFields }).strict().superRefine(refineQuestion);
|
|
10084
|
+
var AnswerInputSchema = external_exports.object({
|
|
10085
|
+
questionId: questionHandle.describe("The Question you answer: its id as the event or the conversation shows it (8 characters or whole)."),
|
|
10086
|
+
answer: external_exports.object({
|
|
10087
|
+
text: external_exports.string().trim().min(1).describe(`Your answer in your own words, at most ${ASK_MAX} characters; the context goes in units.`),
|
|
10088
|
+
selectedOptionIds: external_exports.array(external_exports.string().trim().min(1)).min(1).max(6).optional().describe("The ids of the options you choose, when the Question offered options (in order, for a ranking).")
|
|
10089
|
+
}).strict(),
|
|
10090
|
+
units
|
|
10091
|
+
}).strict().superRefine((a, ctx) => refuseOverCaps(a.answer.text, a.units, "text", ctx));
|
|
10092
|
+
var QuestionDependencySchema = external_exports.object({
|
|
10093
|
+
questionId: questionHandle.describe("A Question you asked."),
|
|
10094
|
+
change: external_exports.enum(["add", "remove"]),
|
|
10095
|
+
blocked: NodeRef,
|
|
10096
|
+
action: BlockedAction,
|
|
10097
|
+
reason: reason.optional()
|
|
10098
|
+
}).strict().refine(actionFits, ACTION_FITS);
|
|
10099
|
+
var sendFields = {
|
|
10100
|
+
updates: external_exports.array(UpdateInputSchema).min(1).max(20).optional().describe(
|
|
10101
|
+
"News: progress, findings, a result. An update asks nothing. It reaches the person only when they asked for it (userExplicitlyRequested), when it answers something they said, or once its Goal is done; otherwise it is recorded as the Goal's progress and nobody is notified."
|
|
10001
10102
|
),
|
|
10103
|
+
answers: external_exports.array(AnswerInputSchema).min(1).max(20).optional().describe("Answers to Questions addressed to you."),
|
|
10104
|
+
withdrawQuestionIds: external_exports.array(questionHandle).min(1).max(10).optional().describe(
|
|
10105
|
+
"Questions you asked that no longer need an answer: you acted on them yourself, they no longer matter, or you asked wrongly. Each is cancelled, not answered: its card closes and nothing rings for it."
|
|
10106
|
+
),
|
|
10107
|
+
questionDependencies: external_exports.array(QuestionDependencySchema).min(1).max(20).optional().describe(
|
|
10108
|
+
"Add or remove what a Question you asked holds up, after asking it."
|
|
10109
|
+
)
|
|
10110
|
+
};
|
|
10111
|
+
var ContactSchema = external_exports.object({
|
|
10112
|
+
...sendFields,
|
|
10113
|
+
questions: external_exports.array(QuestionInputSchema).min(1).max(20).optional().describe("Questions for the person, one per object."),
|
|
10114
|
+
/** RECEIVING (2I, I2; brain_prompts.md §2.2): the events this agent handled. */
|
|
10002
10115
|
ackEventIds: external_exports.array(external_exports.string().uuid()).min(1).max(100).optional().describe(
|
|
10003
|
-
"The exact eventIds of the events you have handled: an acknowledgment (ACK). Reading alone acknowledges nothing, so an event comes back until you acknowledge it; after that the next batch can come. Each recipient acknowledges separately, for itself only. A Question you owe is answered (
|
|
10116
|
+
"The exact eventIds of the events you have handled: an acknowledgment (ACK). Reading alone acknowledges nothing, so an event comes back until you acknowledge it; after that the next batch can come. Each recipient acknowledges separately, for itself only. A Question you owe is answered (answers), never acknowledged away."
|
|
10117
|
+
),
|
|
10118
|
+
wait: external_exports.boolean().optional().describe(
|
|
10119
|
+
"Only controls when this returns. Sending: false (the default) returns at once; true waits up to about 45 seconds for a response to what you sent. Sending nothing: true (the default) waits up to about 45 seconds for anything addressed to you; false returns what is waiting now. It does not mean work is blocked, set urgency, or ask for a call. `waitOutcome` says which happened: `available`, `expired` (nothing arrived; wait again without sending again), or `not_waited`."
|
|
10004
10120
|
)
|
|
10005
10121
|
}).strict();
|
|
10006
|
-
var
|
|
10122
|
+
var sendsAnything = (c) => !!(c.updates?.length || c.questions?.length || c.answers?.length || c.withdrawQuestionIds?.length || c.questionDependencies?.length);
|
|
10123
|
+
var ContactWireSchema = external_exports.object({
|
|
10124
|
+
operationId: external_exports.string().uuid(),
|
|
10125
|
+
...sendFields,
|
|
10126
|
+
questions: external_exports.array(QuestionWireSchema).min(1).max(20).optional()
|
|
10127
|
+
}).strict().refine(sendsAnything, "a contact sends at least one update, question, answer, withdrawal or question dependency");
|
|
10007
10128
|
var CONTACT_SCHEMA = { type: "object", ...mcpInputSchema(ContactSchema) };
|
|
10008
|
-
var CONTACT_DESCRIPTION =
|
|
10129
|
+
var CONTACT_DESCRIPTION = `Send to and receive from the person: updates, questions, answers to questions addressed to you, withdrawals of your own questions, and what a question holds up. Every Goal you name must already exist: create it with manage_goals first; an unknown Goal is refused and nothing is sent. Every item applies on its own: read \`results\`, where a failed item names why while the rest still apply.
|
|
10130
|
+
|
|
10131
|
+
QUESTIONS: one question per object; several questions are several objects in one contact, so each can be answered on its own. Each is sent exactly as written, with no reading in between: a bundled question arrives as one card. Give options when you have them. Each option's label must stand on its own \u2014 the person may see only the labels \u2014 so never a label that points into your text ('All three', 'Option 2', '1 and 3 only'). pickMode:'one' (the default) makes a pick, for alternatives; 'many' a checklist, for independent options they may want several of; 'rank' an ordering. The person can always answer in their own words, so never add an 'Other' option. workItBlocks names the work a question holds up, only when you are truly blocked: it asks for a call. Answers land on the question's Goal: collect them by receiving (below) or with get_goal.
|
|
10132
|
+
|
|
10133
|
+
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.
|
|
10134
|
+
|
|
10135
|
+
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.
|
|
10136
|
+
|
|
10137
|
+
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\`).`;
|
|
10009
10138
|
var CreateGoalSchema = external_exports.object({
|
|
10010
10139
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
10011
10140
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -10039,29 +10168,20 @@ var UpdateGoalSchema = external_exports.object({
|
|
|
10039
10168
|
state: external_exports.enum(["active", "done", "cancelled"]).optional(),
|
|
10040
10169
|
progress: external_exports.string().trim().min(1).max(1e4).optional(),
|
|
10041
10170
|
reviewed: external_exports.literal(true).optional(),
|
|
10042
|
-
dueAt: external_exports.string().datetime({ offset: true }).nullable().optional()
|
|
10043
|
-
/** WITHDRAW YOUR OWN QUESTION (#2777, owner 2026-09-30). The question's id as every read shows it
|
|
10044
|
-
* (the conversation's `id`, eight characters, or the whole request Entry id), on THIS Goal, asked
|
|
10045
|
-
* by you and still open. It is cancelled, not answered: its cards close and nothing rings for it. */
|
|
10046
|
-
withdraw: external_exports.array(external_exports.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/)).min(1).max(10).optional()
|
|
10171
|
+
dueAt: external_exports.string().datetime({ offset: true }).nullable().optional()
|
|
10047
10172
|
}).strict().refine((v) => Object.keys(v).length > 0),
|
|
10048
10173
|
reason: external_exports.string().trim().min(1).max(2e3),
|
|
10049
10174
|
operationId: external_exports.string().uuid().optional()
|
|
10050
10175
|
}).strict();
|
|
10051
|
-
var UpdateGoalToolSchema = external_exports.object({
|
|
10052
|
-
goalId: external_exports.string().uuid(),
|
|
10053
|
-
revision: UpdateGoalSchema.shape.revision,
|
|
10054
|
-
changes: UpdateGoalSchema.shape.changes.innerType().pick({ progress: true, reviewed: true, dueAt: true, withdraw: true }).strict().refine((v) => Object.keys(v).length > 0),
|
|
10055
|
-
reason: UpdateGoalSchema.shape.reason
|
|
10056
|
-
}).strict();
|
|
10057
10176
|
var changedGoal = external_exports.string().uuid().describe("The Goal to change, as a read shows it.");
|
|
10058
10177
|
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.");
|
|
10059
|
-
var
|
|
10178
|
+
var GOAL_OUTCOME_MAX = 500;
|
|
10179
|
+
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.`);
|
|
10060
10180
|
var CreateChange = external_exports.object({
|
|
10061
10181
|
kind: external_exports.literal("create"),
|
|
10062
10182
|
title: goalTitle,
|
|
10063
10183
|
outcome: goalOutcome,
|
|
10064
|
-
ownerId: external_exports.string().trim().min(1).optional().describe("Who owns the work: an agent's participant (as
|
|
10184
|
+
ownerId: external_exports.string().trim().min(1).optional().describe("Who owns the work: an agent's participant (as check_activity shows it) or the person's. Omitted: you."),
|
|
10065
10185
|
parentGoalId: external_exports.string().uuid().optional().describe("The Goal it belongs under. Omitted: a root."),
|
|
10066
10186
|
sourceEntryIds: external_exports.array(external_exports.string().uuid()).max(20).optional().describe("The whole Entries the work came from, ones you can read.")
|
|
10067
10187
|
}).strict();
|
|
@@ -10071,7 +10191,7 @@ var StateChange = external_exports.object({ kind: external_exports.literal("stat
|
|
|
10071
10191
|
) }).strict();
|
|
10072
10192
|
var AssignChange = external_exports.object({ kind: external_exports.literal("assign"), goalId: changedGoal, ownerId: external_exports.string().trim().min(1).describe("The new owner's participant.") }).strict();
|
|
10073
10193
|
var DeferChange = external_exports.object({ kind: external_exports.literal("defer"), goalId: changedGoal, until: external_exports.string().datetime({ offset: true }).nullable().describe(
|
|
10074
|
-
"Postpone it until this instant
|
|
10194
|
+
"Postpone it until this instant: it waits until then, and its owner is woken when it passes (a promise to follow up later). null takes the postponement back."
|
|
10075
10195
|
) }).strict();
|
|
10076
10196
|
var MoveChange = external_exports.object({ kind: external_exports.literal("move"), goalId: changedGoal, parentGoalId: external_exports.string().uuid().nullable().describe(
|
|
10077
10197
|
"Its new parent, or null for a root. Only this Goal moves; its new parent's other children stay."
|
|
@@ -10082,7 +10202,7 @@ var DependencyChange = external_exports.object({
|
|
|
10082
10202
|
goalId: changedGoal,
|
|
10083
10203
|
dependsOnGoalId: external_exports.string().uuid().describe("The Goal it waits on."),
|
|
10084
10204
|
action: external_exports.enum(["start", "complete"]).describe("What waits: starting goalId, or completing it."),
|
|
10085
|
-
reason: external_exports.string().trim().min(1).max(
|
|
10205
|
+
reason: external_exports.string().trim().min(1).max(250).optional().describe("Why, at most 250 characters, as an explanation, never as policy.")
|
|
10086
10206
|
}).strict();
|
|
10087
10207
|
var read = { revision: external_exports.number().int().positive().optional() };
|
|
10088
10208
|
var GoalChangeSchema = external_exports.discriminatedUnion("kind", [
|
|
@@ -10106,8 +10226,7 @@ var ManageGoalsSchema = external_exports.object({
|
|
|
10106
10226
|
var ManageGoalsToolSchema = external_exports.object({
|
|
10107
10227
|
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.")
|
|
10108
10228
|
}).strict().superRefine((v, ctx) => editNames(v.changes, ctx));
|
|
10109
|
-
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,
|
|
10110
|
-
var ClaimGoalSchema = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
|
|
10229
|
+
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.";
|
|
10111
10230
|
var GetGoalSchema = external_exports.object({
|
|
10112
10231
|
goalId: external_exports.string().uuid(),
|
|
10113
10232
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
@@ -10118,25 +10237,29 @@ var GetGoalSchema = external_exports.object({
|
|
|
10118
10237
|
* between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
|
|
10119
10238
|
diagnose: external_exports.boolean().optional()
|
|
10120
10239
|
}).strict();
|
|
10121
|
-
var GET_GOAL_DESCRIPTION = "Read one Goal
|
|
10122
|
-
var UPDATE_GOAL_DESCRIPTION = "Report work on a Goal you are on, at an exact revision: progress, review acknowledgement, when to wake for it, and withdrawing a question of yours. What the Goal is (its title, outcome, state, owner, parent, dependencies) changes with manage_goals. You are on a Goal you own, or one you have written on (an update with progress, or a contact on it); any of your person's agents may write on any of their Goals, and one that never touched a Goal is refused (409 goal_not_joined). Stale revisions are rejected. A CONTACT ON THIS GOAL MOVES ITS REVISION: a question filed on a Goal is a change to it, so an update prepared before a contact and sent after it is refused as stale (409 goal_revision_conflict) \u2014 re-read the Goal, then write. progress says where the work stands. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision (contact({ackEventIds}) does the same for an `update` event). dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. Returns the Goal as get_goal reads it, at its new revision.";
|
|
10123
|
-
var CLAIM_GOAL_DESCRIPTION = "Claim a pending answer to your question or the oldest runnable or review-pending Goal you own. Pass goalId to join any Goal of your person; its assignment stays unchanged. Read others before overlapping another agent\u2019s work. Joining lets you contribute and change it; use manage_goals (assign) when the assignment itself should change. Returns the Goal as get_goal reads it, and marks you as on it, which never shuts another agent out: other agents of your person may write on it and change it too, and two changes at once are told apart by revision (409 goal_revision_conflict).";
|
|
10240
|
+
var GET_GOAL_DESCRIPTION = "Read one Goal (a read never puts you on it), including others (the ten most recent other contributors, with names, latest entry headlines and times): its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full. Every Goal of your person is readable, whichever of their agents owns it; another account's Goals are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
|
|
10124
10241
|
var SearchToolSchema = external_exports.object({
|
|
10125
10242
|
query: external_exports.string().trim().min(1).max(500),
|
|
10126
10243
|
types: external_exports.array(external_exports.enum(["entry", "goal", "answer"])).min(1).optional(),
|
|
10127
10244
|
goalId: external_exports.string().uuid().optional(),
|
|
10128
10245
|
limit: external_exports.number().int().min(1).max(20).optional()
|
|
10129
10246
|
}).strict();
|
|
10130
|
-
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
|
|
10131
|
-
var
|
|
10247
|
+
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.";
|
|
10248
|
+
var CheckActivitySchema = external_exports.object({}).strict();
|
|
10249
|
+
var FEEDBACK_TEXT_MAX = 5e4;
|
|
10250
|
+
var SendFeedbackSchema = external_exports.object({
|
|
10251
|
+
text: external_exports.string().trim().min(1).max(FEEDBACK_TEXT_MAX).describe(`The report, in markdown: what happened, what was expected, how to reproduce it, versions, evidence. At most ${FEEDBACK_TEXT_MAX} characters.`),
|
|
10252
|
+
title: external_exports.string().trim().min(1).max(200).optional().describe("One line naming it."),
|
|
10253
|
+
kind: external_exports.enum(["bug", "idea", "other"]).default("other").describe("bug: something Paigy does wrong. idea: something it should do. other: anything else.")
|
|
10254
|
+
}).strict();
|
|
10255
|
+
var SEND_FEEDBACK_DESCRIPTION = "Send feedback about Paigy itself (a bug report, an idea) straight to the Paigy team. Send it only when the person asked you to, or agreed when you offered: it goes to Paigy, never to the person, and nobody answers it here. Not for your work or a question for the person: use contact for those. Returns whether it was stored.";
|
|
10132
10256
|
var AGENT_TOOLS = [
|
|
10133
|
-
{ name: "who_is_working", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goal to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema(WhoIsWorkingSchema) },
|
|
10134
10257
|
{ name: "contact", description: CONTACT_DESCRIPTION, inputSchema: CONTACT_SCHEMA },
|
|
10135
10258
|
{ name: "manage_goals", description: MANAGE_GOALS_DESCRIPTION, inputSchema: mcpInputSchema(ManageGoalsToolSchema) },
|
|
10136
|
-
{ name: "claim_goal", description: CLAIM_GOAL_DESCRIPTION, inputSchema: mcpInputSchema(ClaimGoalSchema) },
|
|
10137
10259
|
{ name: "get_goal", description: GET_GOAL_DESCRIPTION, inputSchema: mcpInputSchema(GetGoalSchema) },
|
|
10138
|
-
{ name: "
|
|
10139
|
-
{ name: "
|
|
10260
|
+
{ name: "search", description: SEARCH_DESCRIPTION, inputSchema: mcpInputSchema(SearchToolSchema) },
|
|
10261
|
+
{ name: "check_activity", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goal to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema(CheckActivitySchema) },
|
|
10262
|
+
{ name: "send_feedback", description: SEND_FEEDBACK_DESCRIPTION, inputSchema: mcpInputSchema(SendFeedbackSchema) }
|
|
10140
10263
|
];
|
|
10141
10264
|
var AGENT_TOOL_NAMES = AGENT_TOOLS.map((t) => t.name);
|
|
10142
10265
|
var id = external_exports.string().uuid();
|
|
@@ -10184,6 +10307,7 @@ var QuestionChangeSchema = external_exports.union([
|
|
|
10184
10307
|
external_exports.object({
|
|
10185
10308
|
kind: external_exports.literal("create"),
|
|
10186
10309
|
text: external_exports.string().min(1),
|
|
10310
|
+
summary: external_exports.string().optional(),
|
|
10187
10311
|
answererId: participant,
|
|
10188
10312
|
goal: ResultRefSchema.optional(),
|
|
10189
10313
|
blocks: external_exports.array(external_exports.object({ question: ResultRefSchema }).strict()),
|
|
@@ -10234,12 +10358,12 @@ var BrainSearchSchema = external_exports.object({
|
|
|
10234
10358
|
}).strict();
|
|
10235
10359
|
var BrainResultSchema = external_exports.object({
|
|
10236
10360
|
messages: external_exports.array(BrainMessageSchema),
|
|
10237
|
-
entries: external_exports.array(external_exports.object({ key, sources: external_exports.array(SourceSchema).min(1), goals: external_exports.array(ResultRefSchema) }).strict()),
|
|
10361
|
+
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()),
|
|
10238
10362
|
questions: external_exports.array(external_exports.object({ key, entryKeys: external_exports.array(key), change: QuestionChangeSchema }).strict()),
|
|
10239
10363
|
answers: external_exports.array(external_exports.object({
|
|
10240
10364
|
question: ResultRefSchema,
|
|
10241
10365
|
entryKeys: external_exports.array(key).min(1),
|
|
10242
|
-
summary: external_exports.string()
|
|
10366
|
+
summary: external_exports.string(),
|
|
10243
10367
|
selectedOptionIds: external_exports.array(external_exports.string().min(1)).optional()
|
|
10244
10368
|
}).strict()),
|
|
10245
10369
|
goals: external_exports.array(external_exports.object({ key, entryKeys: external_exports.array(key), change: BrainGoalChangeSchema }).strict()),
|
|
@@ -10275,13 +10399,140 @@ var CompactResultSchema = external_exports.object({
|
|
|
10275
10399
|
var COMPACT_RESULT_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10276
10400
|
zodToJsonSchema(CompactResultSchema, { $refStrategy: "none" })
|
|
10277
10401
|
);
|
|
10402
|
+
var str2 = external_exports.string();
|
|
10403
|
+
var strs2 = external_exports.array(external_exports.string());
|
|
10404
|
+
var TalkerReplySchema = external_exports.object({
|
|
10405
|
+
/** What to say and send, messages to the person first: spoken in the order written. `to` is "user"
|
|
10406
|
+
* or an agent's handle; `about` names handles; `blocks` is the item a question to an agent must be
|
|
10407
|
+
* answered before ("" for none). */
|
|
10408
|
+
messages: external_exports.array(external_exports.object({ key: str2, to: str2, text: str2, about: strs2, blocks: str2 }).strict()),
|
|
10409
|
+
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
10410
|
+
then: external_exports.enum(["listen", "hold", "end"]),
|
|
10411
|
+
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
10412
|
+
* IDs, the line handles, how they replied in under ten words (keeping any condition), and `defer`:
|
|
10413
|
+
* "" when their words settle the item, else what a reply that puts it off waits for -- "call" (the
|
|
10414
|
+
* end of this call), a question's handle (that question's answer) or an ISO time. */
|
|
10415
|
+
replies: external_exports.array(external_exports.object({ item: str2, options: strs2, lines: strs2, summary: str2, defer: str2 }).strict()),
|
|
10416
|
+
/** The line handles that tell the talker how to run this call; they last until it ends and reach no agent. */
|
|
10417
|
+
instruction: strs2,
|
|
10418
|
+
/** The line handles the filer acts on, and the only ones it reads (3e): new work, a change to existing
|
|
10419
|
+
* work, feedback on Paigy, anything said to an agent, and any rule meant to outlast the call. */
|
|
10420
|
+
file: strs2,
|
|
10421
|
+
/** Evidence to fetch for a second round: a search, an item's full text, or an agent by name. */
|
|
10422
|
+
need: external_exports.object({ kind: external_exports.enum(["search", "item", "agent", ""]), text: str2 }).strict(),
|
|
10423
|
+
/** The handle of the item to explain ("" for none). */
|
|
10424
|
+
explain: str2
|
|
10425
|
+
}).strict();
|
|
10426
|
+
var TALKER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10427
|
+
zodToJsonSchema(TalkerReplySchema, { $refStrategy: "none" })
|
|
10428
|
+
);
|
|
10429
|
+
var str3 = external_exports.string();
|
|
10430
|
+
var strs3 = external_exports.array(external_exports.string());
|
|
10431
|
+
var FilerReplySchema = external_exports.object({
|
|
10432
|
+
/** Which Goals each new line goes on: a line's handle, Goal handles (or keys of new Goals), and what
|
|
10433
|
+
* the line says in at most ten words. */
|
|
10434
|
+
filed: external_exports.array(external_exports.object({ line: str3, goals: strs3, summary: str3 }).strict()),
|
|
10435
|
+
goals: external_exports.array(external_exports.object({
|
|
10436
|
+
op: external_exports.enum(["create", "edit", "complete", "cancel", "reopen", "assign", "defer", "move", "block", "unblock"]),
|
|
10437
|
+
key: str3,
|
|
10438
|
+
goal: str3,
|
|
10439
|
+
title: str3,
|
|
10440
|
+
outcome: str3,
|
|
10441
|
+
owner: str3,
|
|
10442
|
+
under: str3,
|
|
10443
|
+
until: str3,
|
|
10444
|
+
on: str3,
|
|
10445
|
+
gate: external_exports.enum(["start", "complete", "answer", ""]),
|
|
10446
|
+
why: str3
|
|
10447
|
+
}).strict()),
|
|
10448
|
+
questions: external_exports.array(external_exports.object({
|
|
10449
|
+
op: external_exports.enum(["create", "edit", "assign", "withdraw"]),
|
|
10450
|
+
key: str3,
|
|
10451
|
+
question: str3,
|
|
10452
|
+
text: str3,
|
|
10453
|
+
summary: str3,
|
|
10454
|
+
to: str3,
|
|
10455
|
+
goal: str3,
|
|
10456
|
+
options: external_exports.array(external_exports.object({ id: str3, label: str3 }).strict()),
|
|
10457
|
+
pick: external_exports.enum(["one", "many", "rank", "words", ""]),
|
|
10458
|
+
blocks: str3
|
|
10459
|
+
}).strict()),
|
|
10460
|
+
lessons: external_exports.array(external_exports.object({ op: external_exports.enum(["remember", "revise", "withdraw"]), lesson: str3, text: str3, scope: str3 }).strict()),
|
|
10461
|
+
/** Line handles whose words comment on Paigy itself. */
|
|
10462
|
+
feedback: strs3
|
|
10463
|
+
}).strict();
|
|
10464
|
+
var FILER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10465
|
+
zodToJsonSchema(FilerReplySchema, { $refStrategy: "none" })
|
|
10466
|
+
);
|
|
10467
|
+
var ExplainerReplySchema = external_exports.object({
|
|
10468
|
+
/** The explanation, as sentences, spoken in order. */
|
|
10469
|
+
say: external_exports.array(external_exports.string()),
|
|
10470
|
+
/** Questions to an agent about gaps in what it read: the agent's handle and the question. */
|
|
10471
|
+
followUps: external_exports.array(external_exports.object({ to: external_exports.string(), text: external_exports.string() }).strict())
|
|
10472
|
+
}).strict();
|
|
10473
|
+
var EXPLAINER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10474
|
+
zodToJsonSchema(ExplainerReplySchema, { $refStrategy: "none" })
|
|
10475
|
+
);
|
|
10476
|
+
var DISTILLER_DEFECTS = [
|
|
10477
|
+
"reasked",
|
|
10478
|
+
// a question put to the person again after they answered it
|
|
10479
|
+
"unsupported",
|
|
10480
|
+
// a fact stated that nothing in the input supports
|
|
10481
|
+
"unsaid",
|
|
10482
|
+
// an agent's answer arrived during the call and was never said
|
|
10483
|
+
"skipped",
|
|
10484
|
+
// an agenda item never put to the person
|
|
10485
|
+
"unsaved",
|
|
10486
|
+
// something the person answered or asked for did not save, was refused or dropped
|
|
10487
|
+
"ignored",
|
|
10488
|
+
// the person asked or said something and was never answered (the critic's ignored_a_question)
|
|
10489
|
+
"promised"
|
|
10490
|
+
// said it did or would do something, and nothing shows it done (the critic's broken_promise)
|
|
10491
|
+
];
|
|
10492
|
+
var DistillerReplySchema = external_exports.object({
|
|
10493
|
+
defects: external_exports.array(external_exports.object({
|
|
10494
|
+
kind: external_exports.enum(DISTILLER_DEFECTS),
|
|
10495
|
+
/** The handles of the lines it shows in. */
|
|
10496
|
+
lines: external_exports.array(external_exports.string()),
|
|
10497
|
+
/** The handle of the record it concerns, or "". */
|
|
10498
|
+
about: external_exports.string(),
|
|
10499
|
+
/** One plain sentence, in words, never handles: it is what the feedback row keeps. */
|
|
10500
|
+
why: external_exports.string()
|
|
10501
|
+
}).strict())
|
|
10502
|
+
}).strict();
|
|
10503
|
+
var DISTILLER_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10504
|
+
zodToJsonSchema(DistillerReplySchema, { $refStrategy: "none" })
|
|
10505
|
+
);
|
|
10506
|
+
var TIDY_AUDIENCES = ["talker", "filer", "agents"];
|
|
10507
|
+
var TidyReplySchema = external_exports.object({
|
|
10508
|
+
lessons: external_exports.array(external_exports.object({
|
|
10509
|
+
lesson: external_exports.string(),
|
|
10510
|
+
op: external_exports.enum(["keep", "revise", "withdraw"]),
|
|
10511
|
+
text: external_exports.string(),
|
|
10512
|
+
for: external_exports.enum([...TIDY_AUDIENCES, ""]),
|
|
10513
|
+
into: external_exports.string()
|
|
10514
|
+
}).strict())
|
|
10515
|
+
}).strict();
|
|
10516
|
+
var TIDY_REPLY_JSON_SCHEMA = (({ $schema: _, ...rest }) => rest)(
|
|
10517
|
+
zodToJsonSchema(TidyReplySchema, { $refStrategy: "none" })
|
|
10518
|
+
);
|
|
10519
|
+
function entryUnit(entry) {
|
|
10520
|
+
if (entry.kind !== "contribution" || !entry.aboutId) return null;
|
|
10521
|
+
const plain = entry.content && "plain" in entry.content ? entry.content.plain : null;
|
|
10522
|
+
if (!plain || typeof plain !== "object") return null;
|
|
10523
|
+
const { title, text } = plain;
|
|
10524
|
+
return typeof title === "string" && typeof text === "string" ? { title, body: text } : null;
|
|
10525
|
+
}
|
|
10278
10526
|
var LIVE_MS = 3 * 6e4;
|
|
10279
10527
|
var WORKING_MS = 60 * 6e4;
|
|
10280
10528
|
var ContextSchema = external_exports.object({
|
|
10281
10529
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
10282
10530
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
10283
10531
|
"Semantic chunks of detail (each a standalone, non-empty piece). The user can select chunks to ask you to expand. MAY BE EMPTY: a claim whose whole content is its heading \u2014 a single sentence \u2014 has no body, and saying so beats repeating the heading underneath itself. That repeat is what `min(1)` used to force, at 2x the storage, with every reader subtracting it back out at render time."
|
|
10284
|
-
)
|
|
10532
|
+
),
|
|
10533
|
+
/** THE ASK'S UNITS (3c): its context as titled units, in order. The app lists the titles and opens
|
|
10534
|
+
* a body on a tap. Absent on an ask sent without units, which renders as before. */
|
|
10535
|
+
units: external_exports.array(external_exports.object({ title: external_exports.string(), body: external_exports.string() })).optional()
|
|
10285
10536
|
});
|
|
10286
10537
|
var ParticipantSchema = external_exports.object({
|
|
10287
10538
|
kind: external_exports.enum(["human", "agent"]),
|
|
@@ -10356,7 +10607,7 @@ var NotifyRequestFields = external_exports.object({
|
|
|
10356
10607
|
* ABSENT on the E2EE path, where the sealed `envelope` below carries it. The
|
|
10357
10608
|
* superRefine at the bottom enforces exactly one of the two. */
|
|
10358
10609
|
context: ContextSchema.optional(),
|
|
10359
|
-
options: external_exports.array(
|
|
10610
|
+
options: external_exports.array(OptionSchema.omit({ id: true }).extend({ label: external_exports.string().trim().min(1).max(1e3) }).strict()).min(OPTIONS_MIN).max(OPTIONS_MAX).optional().describe(
|
|
10360
10611
|
"The choices, in order \u2014 required when select is 'one'/'many'/'rank', omitted otherwise. Ids are assigned automatically by position ('1', '2', \u2026); the user's answer references them as optionId(s)."
|
|
10361
10612
|
),
|
|
10362
10613
|
points: external_exports.array(external_exports.string().min(1)).optional().describe(
|
|
@@ -10954,7 +11205,7 @@ var ConnectionSummarySchema = external_exports.object({
|
|
|
10954
11205
|
* desktop app while open. Null = never seen; stale = offline. */
|
|
10955
11206
|
lastSeenAt: external_exports.string().datetime().nullable().optional(),
|
|
10956
11207
|
/** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
|
|
10957
|
-
* its Goals —
|
|
11208
|
+
* its Goals — wrote on one or recorded an operation (`tokens.last_worked_at`). Within
|
|
10958
11209
|
* `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
|
|
10959
11210
|
lastWorkedAt: external_exports.string().datetime().nullable().optional(),
|
|
10960
11211
|
/** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
|
|
@@ -11117,8 +11368,8 @@ var QueueItemSchema = external_exports.object({
|
|
|
11117
11368
|
repo: external_exports.string().nullable().optional(),
|
|
11118
11369
|
createdAt: external_exports.string(),
|
|
11119
11370
|
updatedAt: external_exports.string().nullable().default(null),
|
|
11120
|
-
/** When its owner last SAID something about it (`
|
|
11121
|
-
*
|
|
11371
|
+
/** When its owner last SAID something about it (the newest `progress` Entry: a contact update kept
|
|
11372
|
+
* as progress). `updatedAt` moves for reasons nobody chose — a
|
|
11122
11373
|
* state recomputed, a review flag — so it cannot tell work in hand from work gone quiet. */
|
|
11123
11374
|
lastProgressAt: external_exports.string().nullable().optional(),
|
|
11124
11375
|
/** THE GOAL'S NEWEST WORD, FROM EITHER SIDE (owner, 2026-09-27): the newest Entry on it, of any
|
|
@@ -11152,7 +11403,7 @@ var QueueItemSchema = external_exports.object({
|
|
|
11152
11403
|
*
|
|
11153
11404
|
* IT IS THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK. Nothing here reasons about which
|
|
11154
11405
|
* writes change which field — the comparison is over the bytes the phone is holding, so a fact
|
|
11155
|
-
* the row shows that no `updated_at` moves for (
|
|
11406
|
+
* the row shows that no `updated_at` moves for (`active` lapsing, a dependency's state, a
|
|
11156
11407
|
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
11157
11408
|
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
11158
11409
|
rev: external_exports.string().optional()
|
|
@@ -11276,7 +11527,7 @@ var HostDecisionSchema = external_exports.object({
|
|
|
11276
11527
|
/** The agent's token id: the row's `recipient`. */
|
|
11277
11528
|
agent: external_exports.string().uuid(),
|
|
11278
11529
|
decision: external_exports.enum(["stood_back", "took_over"]),
|
|
11279
|
-
/** The work it was about: the Goal
|
|
11530
|
+
/** The work it was about: the Goal waiting on that agent next (`claimable` on its `contact({})` read). */
|
|
11280
11531
|
goalId: external_exports.string().uuid().nullable().optional(),
|
|
11281
11532
|
/** When the server last heard from the agent, as the host read it: the presence it stood back for. */
|
|
11282
11533
|
seenAt: external_exports.string().datetime().nullable().optional(),
|
|
@@ -11469,14 +11720,13 @@ var CallRecapSchema = external_exports.object({
|
|
|
11469
11720
|
owner: external_exports.string(),
|
|
11470
11721
|
state: external_exports.string(),
|
|
11471
11722
|
questions: external_exports.array(external_exports.object({ id: external_exports.string().uuid(), state: external_exports.string(), title: external_exports.string() })),
|
|
11472
|
-
/** `words` is always what they SAID, verbatim — the record, never replaced. `
|
|
11473
|
-
*
|
|
11474
|
-
*
|
|
11475
|
-
*
|
|
11476
|
-
* anything that is not an answer. */
|
|
11723
|
+
/** `words` is always what they SAID, verbatim — the record, never replaced. `summary` is what
|
|
11724
|
+
* the line says in ten words (owner, 2026-10-07): the filer's, or the talker's for the answer it
|
|
11725
|
+
* gave, so the row scans like a chosen option and their own words stay under it. Absent when
|
|
11726
|
+
* neither wrote one. */
|
|
11477
11727
|
/** `about` is the request the line answered (its question), null for words that answered none —
|
|
11478
11728
|
* the key the screen groups on, so one question is one row however many times it was answered. */
|
|
11479
|
-
lines: external_exports.array(external_exports.object({ entryId: external_exports.string().uuid(), words: external_exports.string(),
|
|
11729
|
+
lines: external_exports.array(external_exports.object({ entryId: external_exports.string().uuid(), words: external_exports.string(), summary: external_exports.string().optional(), about: external_exports.string().nullable().optional() }))
|
|
11480
11730
|
})),
|
|
11481
11731
|
unfiled: external_exports.array(external_exports.object({ lineId: external_exports.string().uuid(), words: external_exports.string(), atMs: external_exports.number() })),
|
|
11482
11732
|
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
@@ -11633,12 +11883,6 @@ async function manageGoals(input, opts = {}) {
|
|
|
11633
11883
|
if (!res.ok) await fail("manage_goals", res);
|
|
11634
11884
|
return await res.json();
|
|
11635
11885
|
}
|
|
11636
|
-
async function claimGoal(goalId, opts = {}) {
|
|
11637
|
-
const token = authToken(opts.token) ?? "";
|
|
11638
|
-
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals/claim`, { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify(goalId ? { goalId } : {}) }));
|
|
11639
|
-
if (!res.ok) await fail("claim_goal", res);
|
|
11640
|
-
return await res.json();
|
|
11641
|
-
}
|
|
11642
11886
|
async function getGoal(goalId, opts = {}, read3 = {}) {
|
|
11643
11887
|
const token = authToken(opts.token) ?? "";
|
|
11644
11888
|
const query = [read3.history ? "history=1" : "", read3.diagnose ? "diagnose=1" : ""].filter(Boolean).join("&");
|
|
@@ -11649,13 +11893,6 @@ async function getGoal(goalId, opts = {}, read3 = {}) {
|
|
|
11649
11893
|
if (!res.ok) await fail("get_goal", res);
|
|
11650
11894
|
return await res.json();
|
|
11651
11895
|
}
|
|
11652
|
-
async function updateGoal(goalId, input, opts = {}) {
|
|
11653
|
-
const token = authToken(opts.token) ?? "";
|
|
11654
|
-
const operationId = input.operationId ?? randomUUID2();
|
|
11655
|
-
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/goals/${encodeURIComponent(goalId)}`, { method: "PATCH", headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify({ ...input, operationId }) }));
|
|
11656
|
-
if (!res.ok) await fail("update_goal", res);
|
|
11657
|
-
return await res.json();
|
|
11658
|
-
}
|
|
11659
11896
|
var AWAIT_WINDOW_MS = 45e3;
|
|
11660
11897
|
async function whoAmI(opts = {}) {
|
|
11661
11898
|
try {
|
|
@@ -11769,99 +12006,67 @@ async function dismissTriage(proposalId, opts = {}) {
|
|
|
11769
12006
|
if (!res.ok) throw new Error(`dismiss_triage failed: ${res.status} ${await res.text()}`);
|
|
11770
12007
|
return await res.json();
|
|
11771
12008
|
}
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
|
|
11776
|
-
const
|
|
11777
|
-
|
|
11778
|
-
|
|
11779
|
-
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
var asked;
|
|
11783
|
-
function currentRepo(cwd = process.cwd()) {
|
|
11784
|
-
if (asked !== void 0) return asked;
|
|
11785
|
-
try {
|
|
11786
|
-
const url = execFileSync2("git", ["remote", "get-url", "origin"], {
|
|
11787
|
-
cwd,
|
|
11788
|
-
encoding: "utf8",
|
|
11789
|
-
timeout: 2e3,
|
|
11790
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
11791
|
-
});
|
|
11792
|
-
asked = repoFromRemote(url);
|
|
11793
|
-
} catch {
|
|
11794
|
-
asked = null;
|
|
11795
|
-
}
|
|
11796
|
-
return asked;
|
|
12009
|
+
var NOT_SENT = `Recorded as the Goal's progress, not sent: nobody was notified. The person is told only a question, an answer to one they asked you, and what you say once the Goal is done. To close the work, mark the Goal done first, then send the one update saying what is done and anything they need to do or check. If the person asked you, in so many words, to send them this, send it again with userExplicitlyRequested: "any" on that update.`;
|
|
12010
|
+
var APPLIED = "Applied; nothing was sent to the person.";
|
|
12011
|
+
var DEMOTED = "Delivered as a card, not a call: the person's settings do not allow a call right now (silent, quiet hours, or a mode). It has reached them; do not send it again.";
|
|
12012
|
+
async function readDelivery(deliveryId, opts = {}) {
|
|
12013
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/deliveries/${encodeURIComponent(deliveryId)}`, {
|
|
12014
|
+
headers: { authorization: `Bearer ${authToken(opts.token) ?? ""}` },
|
|
12015
|
+
signal: opts.signal
|
|
12016
|
+
}));
|
|
12017
|
+
if (!res.ok) await fail("read_delivery", res);
|
|
12018
|
+
return await res.json();
|
|
11797
12019
|
}
|
|
11798
|
-
var NOT_SENT = "Recorded as the Goal's progress, not sent: nobody was notified. The person is told only a question, an answer to one they asked you, and what you say once the Goal is done. To close the work, mark the Goal done first, then send the one message saying what is done and anything they need to do or check.";
|
|
11799
|
-
var DEMOTED = "Delivered as a card, not a call: the person's settings do not allow a call right now (silent, quiet hours, or a mode). It has reached them; do not send it again, with or without a Goal.";
|
|
11800
12020
|
async function contact(input, opts = {}) {
|
|
11801
12021
|
const parsed = ContactSchema.parse(input);
|
|
11802
|
-
if (!(
|
|
12022
|
+
if (!sendsAnything(parsed)) throw new Error("contact with nothing to send receives: call receiveContact");
|
|
11803
12023
|
opts.signal?.throwIfAborted();
|
|
11804
12024
|
const send22 = opts.reach ?? reach;
|
|
11805
|
-
const
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
waiting: parsed.waiting,
|
|
11834
|
-
channel: parsed.channel === "call" ? "call" : "message"
|
|
11835
|
-
})
|
|
11836
|
-
}));
|
|
11837
|
-
if (!res.ok) await fail("batch contact", res);
|
|
11838
|
-
const receipt = await res.json();
|
|
11839
|
-
deliveries = receipt.deliveries ?? [];
|
|
11840
|
-
if (parsed.channel === "call" && receipt.channel && receipt.channel !== "call") demoted = DEMOTED;
|
|
11841
|
-
recorded = receipt.recorded ?? [];
|
|
11842
|
-
if (deliveries.length === 0 && recorded.length) return { sent: false, recorded, message: NOT_SENT };
|
|
11843
|
-
if (deliveries.length === 0) throw new Error("Goal contact returned no Delivery identities");
|
|
11844
|
-
deliveryId = deliveries[0]?.deliveryId ?? "";
|
|
11845
|
-
if (!deliveryId) throw new Error("Goal contact returned no valid Delivery identity");
|
|
11846
|
-
}
|
|
11847
|
-
const all = deliveries.filter((d) => !!d.deliveryId && !!d.goalId).map(({ id: id22, deliveryId: deliveryId2, goalId }) => ({ ...id22 ? { id: id22 } : {}, deliveryId: deliveryId2, goalId }));
|
|
11848
|
-
const joinedCard = deliveries[0]?.joinedCard === true;
|
|
11849
|
-
const joinedCall = deliveries[0]?.joinedCall === true;
|
|
12025
|
+
const acknowledged = parsed.ackEventIds?.length ? await acknowledge(parsed.ackEventIds, opts) : void 0;
|
|
12026
|
+
const questions = parsed.questions?.map((q) => ({ ...q, questionId: q.questionId ?? randomUUID2() }));
|
|
12027
|
+
const res = await ensureAuthed(await send22(`${BACKEND_URL}/api/goals/contact`, {
|
|
12028
|
+
method: "POST",
|
|
12029
|
+
signal: opts.signal,
|
|
12030
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token) ?? ""}` },
|
|
12031
|
+
body: JSON.stringify({
|
|
12032
|
+
operationId: opts.operationId ?? randomUUID2(),
|
|
12033
|
+
...parsed.updates ? { updates: parsed.updates } : {},
|
|
12034
|
+
...questions ? { questions } : {},
|
|
12035
|
+
...parsed.answers ? { answers: parsed.answers } : {},
|
|
12036
|
+
...parsed.withdrawQuestionIds ? { withdrawQuestionIds: parsed.withdrawQuestionIds } : {},
|
|
12037
|
+
...parsed.questionDependencies ? { questionDependencies: parsed.questionDependencies } : {}
|
|
12038
|
+
})
|
|
12039
|
+
}));
|
|
12040
|
+
if (!res.ok) await fail("contact", res);
|
|
12041
|
+
const receipt = await res.json();
|
|
12042
|
+
const results = receipt.results ?? [];
|
|
12043
|
+
const ok = receipt.ok ?? results.every((r) => r.status !== "failed");
|
|
12044
|
+
const deliveries = receipt.deliveries ?? [];
|
|
12045
|
+
const recorded = results.filter((r) => r.status === "recorded" && r.goalId).map((r) => ({ goalId: r.goalId }));
|
|
12046
|
+
const askedCall = [...parsed.updates ?? [], ...questions ?? []].some((m) => m.userExplicitlyRequested === "call") || !!questions?.some((q) => q.workItBlocks?.length);
|
|
12047
|
+
const demoted = askedCall && receipt.channel && receipt.channel !== "call" ? DEMOTED : void 0;
|
|
12048
|
+
const acked = acknowledged ? { acknowledged } : {};
|
|
12049
|
+
if (!deliveries.length) {
|
|
12050
|
+
return { sent: false, ok, results, ...recorded.length ? { recorded } : {}, message: recorded.length ? NOT_SENT : APPLIED, waitOutcome: "not_waited", ...acked };
|
|
12051
|
+
}
|
|
12052
|
+
const lead = deliveries.find((d) => d.kind === "question") ?? deliveries[0];
|
|
11850
12053
|
const read3 = async (signal2) => {
|
|
11851
|
-
const
|
|
11852
|
-
if (!res.ok) await fail("read_delivery", res);
|
|
11853
|
-
const one = await res.json();
|
|
12054
|
+
const one = await readDelivery(lead.deliveryId, { ...opts, signal: signal2 });
|
|
11854
12055
|
return {
|
|
11855
12056
|
...one,
|
|
11856
|
-
|
|
12057
|
+
ok,
|
|
12058
|
+
results,
|
|
12059
|
+
...deliveries.length > 1 ? { deliveries: deliveries.map(({ deliveryId, goalId, kind, questionId }) => ({ deliveryId, goalId, kind, ...questionId ? { questionId } : {} })) } : {},
|
|
11857
12060
|
...recorded.length ? { recorded, notSent: NOT_SENT } : {},
|
|
11858
|
-
...joinedCard ? { joinedCard: true } : {},
|
|
11859
|
-
|
|
11860
|
-
...
|
|
12061
|
+
...lead.joinedCard ? { joinedCard: true } : {},
|
|
12062
|
+
// IT WENT ONTO A CALL ALREADY HAPPENING (owner, 2026-09-28): the view words it as that call.
|
|
12063
|
+
...lead.joinedCall ? { joinedCall: true } : {},
|
|
12064
|
+
...demoted ? { demoted } : {},
|
|
12065
|
+
...acked
|
|
11861
12066
|
};
|
|
11862
12067
|
};
|
|
11863
|
-
const settled = (d) => d.
|
|
11864
|
-
if (opts.waits === false) return read3(opts.signal);
|
|
12068
|
+
const settled = (d) => d.state === "closed" || d.answers.length > 0 || d.entries.some((e) => e.kind === "contribution" && !entryUnit(e));
|
|
12069
|
+
if (parsed.wait !== true || opts.waits === false) return { ...await read3(opts.signal), waitOutcome: "not_waited" };
|
|
11865
12070
|
const window = AbortSignal.timeout(AWAIT_WINDOW_MS);
|
|
11866
12071
|
const signal = opts.signal ? AbortSignal.any([opts.signal, window]) : window;
|
|
11867
12072
|
let latest;
|
|
@@ -11869,12 +12074,12 @@ async function contact(input, opts = {}) {
|
|
|
11869
12074
|
while (true) {
|
|
11870
12075
|
signal.throwIfAborted();
|
|
11871
12076
|
latest = await read3(signal);
|
|
11872
|
-
if (settled(latest)) return latest;
|
|
12077
|
+
if (settled(latest)) return { ...latest, waitOutcome: "available" };
|
|
11873
12078
|
await sleep2(5e3, void 0, { signal });
|
|
11874
12079
|
}
|
|
11875
12080
|
} catch (error) {
|
|
11876
12081
|
opts.signal?.throwIfAborted();
|
|
11877
|
-
if (window.aborted && latest) return latest;
|
|
12082
|
+
if (window.aborted && latest) return { ...latest, waitOutcome: "expired" };
|
|
11878
12083
|
throw error;
|
|
11879
12084
|
}
|
|
11880
12085
|
}
|
|
@@ -11885,6 +12090,15 @@ async function receive(opts = {}) {
|
|
|
11885
12090
|
if (!res.ok) await fail("receive", res);
|
|
11886
12091
|
return await res.json();
|
|
11887
12092
|
}
|
|
12093
|
+
async function acknowledge(eventIds, opts = {}) {
|
|
12094
|
+
const res = await ensureAuthed(await (opts.reach ?? reach)(`${BACKEND_URL}/api/deliveries/ack`, {
|
|
12095
|
+
method: "POST",
|
|
12096
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token) ?? ""}` },
|
|
12097
|
+
body: JSON.stringify({ eventIds })
|
|
12098
|
+
}));
|
|
12099
|
+
if (!res.ok) await fail("acknowledge", res);
|
|
12100
|
+
return (await res.json()).results ?? [];
|
|
12101
|
+
}
|
|
11888
12102
|
var HEARTBEAT_MS = 25e3;
|
|
11889
12103
|
var RETRY_MS = [1e3, 2e3, 5e3, 1e4, 3e4];
|
|
11890
12104
|
var JOIN = { config: { broadcast: { self: false } } };
|
|
@@ -12365,9 +12579,9 @@ async function available(deps = {}) {
|
|
|
12365
12579
|
});
|
|
12366
12580
|
return [ollama, ...harnesses];
|
|
12367
12581
|
}
|
|
12368
|
-
function refusal(options,
|
|
12582
|
+
function refusal(options, asked) {
|
|
12369
12583
|
const lines2 = [
|
|
12370
|
-
|
|
12584
|
+
asked ? `Can't judge with --via ${asked}: ${options.find((o) => o.provider === asked)?.hint ?? "it isn't available on this machine"}` : "No local judge available \u2014 triage refuses rather than sending your queue to a cloud model.",
|
|
12371
12585
|
"",
|
|
12372
12586
|
"Triage runs the model where your queue already lives (#1106). Your options:"
|
|
12373
12587
|
];
|
|
@@ -12391,10 +12605,10 @@ var RUNNERS = {
|
|
|
12391
12605
|
agy: { cli: "agy", argv: () => ["-p"] }
|
|
12392
12606
|
};
|
|
12393
12607
|
var RUN_TIMEOUT_MS = 5 * 6e4;
|
|
12394
|
-
async function judgeFor(
|
|
12608
|
+
async function judgeFor(asked, deps = {}) {
|
|
12395
12609
|
const options = await available(deps);
|
|
12396
|
-
const chosen =
|
|
12397
|
-
if (!chosen?.ready) return { error: refusal(options,
|
|
12610
|
+
const chosen = asked ? options.find((o) => o.provider === asked) : options.find((o) => o.ready);
|
|
12611
|
+
if (!chosen?.ready) return { error: refusal(options, asked) };
|
|
12398
12612
|
if (chosen.provider === "ollama") {
|
|
12399
12613
|
const model = chosen.model;
|
|
12400
12614
|
return {
|
|
@@ -12415,9 +12629,9 @@ async function judgeFor(asked2, deps = {}) {
|
|
|
12415
12629
|
};
|
|
12416
12630
|
}
|
|
12417
12631
|
const runner = RUNNERS[chosen.provider];
|
|
12418
|
-
if (!runner) return { error: refusal(options,
|
|
12632
|
+
if (!runner) return { error: refusal(options, asked) };
|
|
12419
12633
|
const bin = which(runner.cli);
|
|
12420
|
-
if (!bin) return { error: refusal(options,
|
|
12634
|
+
if (!bin) return { error: refusal(options, asked) };
|
|
12421
12635
|
return {
|
|
12422
12636
|
provider: chosen.provider,
|
|
12423
12637
|
model: null,
|
|
@@ -12686,10 +12900,10 @@ function ago(iso, now) {
|
|
|
12686
12900
|
return `${plural2(Math.floor(hours / 24), "day")} ago`;
|
|
12687
12901
|
}
|
|
12688
12902
|
function standing(queue, proposal, now) {
|
|
12689
|
-
const
|
|
12690
|
-
if (!proposal) return `${
|
|
12903
|
+
const head2 = queue.length === 0 ? "Nothing open \u2014 the queue is clear" : `Holding ${plural2(queue.length, "open note")}`;
|
|
12904
|
+
if (!proposal) return `${head2} \xB7 no sweep waiting`;
|
|
12691
12905
|
const n = suggested(proposal);
|
|
12692
|
-
return n === 0 ? `${
|
|
12906
|
+
return n === 0 ? `${head2} \xB7 swept ${ago(proposal.runAt, now)}, nothing to suggest` : `${head2} \xB7 ${plural2(n, "suggestion")} waiting on you, swept ${ago(proposal.runAt, now)}`;
|
|
12693
12907
|
}
|
|
12694
12908
|
async function brokerView(deps) {
|
|
12695
12909
|
const now = (deps.now ?? Date.now)();
|
|
@@ -12789,7 +13003,7 @@ function listRepos(deps) {
|
|
|
12789
13003
|
}
|
|
12790
13004
|
|
|
12791
13005
|
// src/holder.ts
|
|
12792
|
-
import { execFileSync as
|
|
13006
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
12793
13007
|
import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
|
|
12794
13008
|
import { homedir as homedir6 } from "os";
|
|
12795
13009
|
import { join as join6 } from "path";
|
|
@@ -12804,7 +13018,7 @@ function isAlive(pid) {
|
|
|
12804
13018
|
function psStart(pid, zone) {
|
|
12805
13019
|
try {
|
|
12806
13020
|
const env = zone === "UTC" ? { ...process.env, TZ: "UTC", LC_ALL: "C" } : { ...process.env, LC_ALL: "C" };
|
|
12807
|
-
return
|
|
13021
|
+
return execFileSync2("ps", ["-o", "lstart=", "-p", String(pid)], { env, encoding: "utf8", timeout: 2e3 }).trim() || null;
|
|
12808
13022
|
} catch {
|
|
12809
13023
|
return null;
|
|
12810
13024
|
}
|
|
@@ -12859,7 +13073,7 @@ function localHolderPids(deps = {}) {
|
|
|
12859
13073
|
}
|
|
12860
13074
|
function psParent(pid) {
|
|
12861
13075
|
try {
|
|
12862
|
-
const out =
|
|
13076
|
+
const out = execFileSync2("ps", ["-o", "ppid=", "-p", String(pid)], { encoding: "utf8", timeout: 2e3 }).trim();
|
|
12863
13077
|
const n = Number(out);
|
|
12864
13078
|
return Number.isInteger(n) && n > 0 ? n : null;
|
|
12865
13079
|
} catch {
|
|
@@ -12983,7 +13197,7 @@ function ownerSession(slot, deps = {}) {
|
|
|
12983
13197
|
const prefix = m[1];
|
|
12984
13198
|
const root = deps.projectsDir ?? join7(homedir7(), ".claude", "projects");
|
|
12985
13199
|
const list = deps.list ?? ((dir) => readdirSync3(dir));
|
|
12986
|
-
const
|
|
13200
|
+
const head2 = deps.head ?? readHead;
|
|
12987
13201
|
let folders;
|
|
12988
13202
|
try {
|
|
12989
13203
|
folders = list(root);
|
|
@@ -13001,7 +13215,7 @@ function ownerSession(slot, deps = {}) {
|
|
|
13001
13215
|
if (!file) continue;
|
|
13002
13216
|
let text;
|
|
13003
13217
|
try {
|
|
13004
|
-
text =
|
|
13218
|
+
text = head2(join7(root, folder, file));
|
|
13005
13219
|
} catch {
|
|
13006
13220
|
continue;
|
|
13007
13221
|
}
|
|
@@ -14305,21 +14519,31 @@ var zodToJsonSchema2 = (schema, options) => {
|
|
|
14305
14519
|
// ../../packages/schema/dist/index.js
|
|
14306
14520
|
var OPTIONS_MIN2 = 2;
|
|
14307
14521
|
var OPTIONS_MAX2 = 6;
|
|
14522
|
+
var OPTION_LABEL_MAX2 = 50;
|
|
14523
|
+
var OPTION_HINT_MAX2 = 200;
|
|
14308
14524
|
var OptionSchema2 = external_exports.object({
|
|
14309
14525
|
id: external_exports.string(),
|
|
14310
14526
|
label: external_exports.string(),
|
|
14311
|
-
hint: external_exports.string().max(500).describe(
|
|
14527
|
+
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(),
|
|
14312
14528
|
// .describe() flows into the MCP contact JSON schema (zodToJsonSchema), so
|
|
14313
14529
|
// the constraints below are what an agent reads when deciding to use these.
|
|
14314
14530
|
html: external_exports.string().max(16384).describe(
|
|
14315
|
-
"Optional sandboxed HTML/CSS preview for a visual 'pick one' (shown in the option card). Untrusted-sandboxed: NO JavaScript, NO external network or images \u2014 inline CSS and data: URIs only; <=16KB. Rendered edge-to-edge in a responsive card that is 200pt tall (about 320pt wide on a phone, with the next option peeking beside it); make your HTML fit that viewport. Use for layout/CSS mockups, tables, diffs. For a hosted image use
|
|
14531
|
+
"Optional sandboxed HTML/CSS preview for a visual 'pick one' (shown in the option card). Untrusted-sandboxed: NO JavaScript, NO external network or images \u2014 inline CSS and data: URIs only; <=16KB. Rendered edge-to-edge in a responsive card that is 200pt tall (about 320pt wide on a phone, with the next option peeking beside it); make your HTML fit that viewport. Use for layout/CSS mockups, tables, diffs. For a hosted image use the image URL instead."
|
|
14316
14532
|
).optional(),
|
|
14317
14533
|
image: external_exports.string().url().describe(
|
|
14318
|
-
"Optional image URL rendered as the option's preview (plain image, not sandboxed). For agent-generated HTML/CSS mockups, use
|
|
14534
|
+
"Optional image URL rendered as the option's preview (plain image, not sandboxed). For agent-generated HTML/CSS mockups, use the HTML preview instead."
|
|
14319
14535
|
).optional()
|
|
14320
14536
|
});
|
|
14321
|
-
var OptionInputSchema2 =
|
|
14322
|
-
|
|
14537
|
+
var OptionInputSchema2 = external_exports.object({
|
|
14538
|
+
id: external_exports.string().trim().min(1).max(64).regex(/^[A-Za-z0-9_.:-]+$/).optional().describe(
|
|
14539
|
+
`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.`
|
|
14540
|
+
),
|
|
14541
|
+
// SHORT CHOICES (owner, 2026-10-07: "options label should be maybe max 50 characters and the hint 200"):
|
|
14542
|
+
// a label is one choice the person reads, or hears on a call; a hint says its consequence in a line.
|
|
14543
|
+
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.`),
|
|
14544
|
+
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(),
|
|
14545
|
+
htmlPreview: OptionSchema2.shape.html,
|
|
14546
|
+
imgUrl: OptionSchema2.shape.image
|
|
14323
14547
|
}).strict();
|
|
14324
14548
|
var NIGHT2 = { from: 23, to: 7 };
|
|
14325
14549
|
function draft20202(node) {
|
|
@@ -14351,41 +14575,162 @@ function mcpInputSchema2(s) {
|
|
|
14351
14575
|
delete schema.$schema;
|
|
14352
14576
|
return draft20202(schema);
|
|
14353
14577
|
}
|
|
14354
|
-
var
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
|
|
14359
|
-
|
|
14578
|
+
var ASK_MAX2 = 250;
|
|
14579
|
+
var UNITS_MAX2 = 12;
|
|
14580
|
+
var UNIT_TITLE_MAX2 = 50;
|
|
14581
|
+
var UNIT_BODY_MAX2 = 200;
|
|
14582
|
+
var UnitInputSchema2 = external_exports.object({
|
|
14583
|
+
title: external_exports.string().trim().min(1).describe(`What this unit is about, at most ${UNIT_TITLE_MAX2} characters. The app lists the titles; the person taps one to read its body.`),
|
|
14584
|
+
body: external_exports.string().trim().min(1).describe(`The unit itself, at most ${UNIT_BODY_MAX2} characters.`)
|
|
14585
|
+
}).strict();
|
|
14586
|
+
function capRefusals2(text, units22, field) {
|
|
14587
|
+
const out = [];
|
|
14588
|
+
const name = field === "text" ? "answer" : field;
|
|
14589
|
+
if (text.length > ASK_MAX2) out.push({
|
|
14590
|
+
refusal: "ask_too_long",
|
|
14591
|
+
length: text.length,
|
|
14592
|
+
cap: ASK_MAX2,
|
|
14593
|
+
path: field === "text" ? ["answer", "text"] : [field],
|
|
14594
|
+
message: `ask_too_long: the ${name} is ${text.length} characters; the cap is ${ASK_MAX2}. Keep the one point in the ${name} and move the context into units.`
|
|
14595
|
+
});
|
|
14596
|
+
const all = units22 ?? [];
|
|
14597
|
+
if (all.length > UNITS_MAX2) out.push({
|
|
14598
|
+
refusal: "too_many_units",
|
|
14599
|
+
length: all.length,
|
|
14600
|
+
cap: UNITS_MAX2,
|
|
14601
|
+
path: ["units"],
|
|
14602
|
+
message: `too_many_units: ${all.length} units; the cap is ${UNITS_MAX2}. Keep only the context this ${name} needs.`
|
|
14603
|
+
});
|
|
14604
|
+
all.forEach((unit, index) => {
|
|
14605
|
+
if (unit.title.length > UNIT_TITLE_MAX2) out.push({
|
|
14606
|
+
refusal: "unit_title_too_long",
|
|
14607
|
+
index,
|
|
14608
|
+
length: unit.title.length,
|
|
14609
|
+
cap: UNIT_TITLE_MAX2,
|
|
14610
|
+
path: ["units", index, "title"],
|
|
14611
|
+
message: `unit_title_too_long: units[${index}]'s title is ${unit.title.length} characters; the cap is ${UNIT_TITLE_MAX2}.`
|
|
14612
|
+
});
|
|
14613
|
+
if (unit.body.length > UNIT_BODY_MAX2) out.push({
|
|
14614
|
+
refusal: "unit_body_too_long",
|
|
14615
|
+
index,
|
|
14616
|
+
length: unit.body.length,
|
|
14617
|
+
cap: UNIT_BODY_MAX2,
|
|
14618
|
+
path: ["units", index, "body"],
|
|
14619
|
+
message: `unit_body_too_long: units[${index}]'s body is ${unit.body.length} characters; the cap is ${UNIT_BODY_MAX2}. Cut it into two units.`
|
|
14620
|
+
});
|
|
14621
|
+
});
|
|
14622
|
+
return out;
|
|
14623
|
+
}
|
|
14624
|
+
function refuseOverCaps2(text, units22, field, ctx) {
|
|
14625
|
+
for (const { path, message, ...params } of capRefusals2(text, units22, field)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message, params });
|
|
14626
|
+
}
|
|
14627
|
+
var questionHandle2 = external_exports.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/, "a question id, whole or its first eight characters");
|
|
14628
|
+
var NodeRef2 = external_exports.object({
|
|
14629
|
+
type: external_exports.enum(["goal", "question"]),
|
|
14630
|
+
id: external_exports.string().uuid()
|
|
14631
|
+
}).strict().describe("A Goal or a Question, by its id.");
|
|
14632
|
+
var BlockedAction2 = external_exports.enum(["start", "complete", "answer"]).describe("What waits: starting or completing a Goal, or answering a Question.");
|
|
14633
|
+
var reason2 = external_exports.string().trim().min(1).max(500).describe("Why, as an explanation, never as policy.");
|
|
14634
|
+
var actionFits2 = (b) => b.blocked.type === "goal" === (b.action !== "answer");
|
|
14635
|
+
var ACTION_FITS2 = "a goal is blocked from start or complete; a question from answer";
|
|
14636
|
+
var userExplicitlyRequested2 = external_exports.enum(["any", "call"]).optional().describe(
|
|
14637
|
+
`Only what the person said about THIS message, in so many words: "any" when they asked you to send it to them ("send it to my phone", "message me the result"), "call" when they asked to be called about it. It is then delivered (a call rings for the whole contact, and carries everything waiting on them); omitted, an update is only recorded as the Goal's progress unless the person needs it. Never set it to make a message louder.`
|
|
14638
|
+
);
|
|
14639
|
+
var units2 = external_exports.array(UnitInputSchema2).optional().describe(
|
|
14640
|
+
`The context this message needs, as up to ${UNITS_MAX2} units in reading order, each {title, body}: a title of at most ${UNIT_TITLE_MAX2} characters and a body of at most ${UNIT_BODY_MAX2}. One idea per unit. Leave it out when the main text says it all.`
|
|
14641
|
+
);
|
|
14642
|
+
var UpdateInputSchema2 = external_exports.object({
|
|
14643
|
+
message: external_exports.string().trim().min(1).describe(`The one point, at most ${ASK_MAX2} characters; the context goes in units.`),
|
|
14644
|
+
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."),
|
|
14645
|
+
units: units2,
|
|
14646
|
+
userExplicitlyRequested: userExplicitlyRequested2
|
|
14647
|
+
}).strict().superRefine((u, ctx) => refuseOverCaps2(u.message, u.units, "message", ctx));
|
|
14648
|
+
var questionFields2 = {
|
|
14649
|
+
question: external_exports.string().trim().min(1).describe(
|
|
14650
|
+
`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.`
|
|
14360
14651
|
),
|
|
14361
|
-
|
|
14652
|
+
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."),
|
|
14653
|
+
units: units2,
|
|
14654
|
+
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."),
|
|
14362
14655
|
// HOW THE OPTIONS ARE ANSWERED, SAID BY THE AGENT (owner, 2026-10-02: "one and many makes sense";
|
|
14363
|
-
// brain_prompts.md §1: structured agent questions need no model).
|
|
14364
|
-
|
|
14365
|
-
|
|
14366
|
-
select: external_exports.enum(["one", "many"]).optional().describe(
|
|
14367
|
-
'How the options are answered: "many" makes a checklist (they may want several, e.g. independent fixes), "one" (the default) a pick (choosing one rules out the others). Send "many" whenever more than one option could be wanted at once. The person can always answer in their own words as well. Ignored without options.'
|
|
14656
|
+
// brain_prompts.md §1: structured agent questions need no model). The app draws all three.
|
|
14657
|
+
pickMode: external_exports.enum(["one", "many", "rank"]).optional().describe(
|
|
14658
|
+
'How the options are answered: "one" (the default) a pick, for alternatives; "many" a checklist, for independent options they may want several of; "rank" an order of all of them. Only with options.'
|
|
14368
14659
|
),
|
|
14369
|
-
|
|
14370
|
-
|
|
14371
|
-
)
|
|
14372
|
-
|
|
14373
|
-
|
|
14374
|
-
|
|
14375
|
-
|
|
14376
|
-
|
|
14377
|
-
|
|
14378
|
-
|
|
14379
|
-
|
|
14380
|
-
|
|
14660
|
+
// WHAT THE QUESTION HOLDS UP (brain_prompts.md §2.1): replaces `waiting: 'hard'`. A question that
|
|
14661
|
+
// blocks work asks for a call (the person's settings and ring cooldowns still decide).
|
|
14662
|
+
workItBlocks: external_exports.array(external_exports.object({ blocked: NodeRef2, action: BlockedAction2, reason: reason2.optional() }).strict().refine(actionFits2, ACTION_FITS2)).min(1).max(10).optional().describe(
|
|
14663
|
+
"The work that cannot go on until this is answered: a Goal's start or completion, or another Question's answer. Only for work truly blocked; it asks for a call. Leave it out for a question you can keep working around."
|
|
14664
|
+
),
|
|
14665
|
+
userExplicitlyRequested: userExplicitlyRequested2
|
|
14666
|
+
};
|
|
14667
|
+
var refineQuestion2 = (q, ctx) => {
|
|
14668
|
+
refuseOverCaps2(q.question, q.units, "question", ctx);
|
|
14669
|
+
if (q.pickMode && !q.options) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["pickMode"], message: "pickMode comes only with options" });
|
|
14670
|
+
const ids = (q.options ?? []).map((o) => o.id);
|
|
14671
|
+
if (ids.some((id22) => id22) && (ids.some((id22) => !id22) || new Set(ids).size !== ids.length)) {
|
|
14672
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["options"], message: "give every option a distinct id, or none" });
|
|
14673
|
+
}
|
|
14674
|
+
};
|
|
14675
|
+
var QuestionInputSchema2 = external_exports.object({
|
|
14676
|
+
questionId: external_exports.string().uuid().optional().describe("Optional: your own stable id for this question (a UUID), so a resend is the same question. Minted for you when omitted; the result returns it."),
|
|
14677
|
+
...questionFields2
|
|
14678
|
+
}).strict().superRefine(refineQuestion2);
|
|
14679
|
+
var QuestionWireSchema2 = external_exports.object({ questionId: external_exports.string().uuid(), ...questionFields2 }).strict().superRefine(refineQuestion2);
|
|
14680
|
+
var AnswerInputSchema2 = external_exports.object({
|
|
14681
|
+
questionId: questionHandle2.describe("The Question you answer: its id as the event or the conversation shows it (8 characters or whole)."),
|
|
14682
|
+
answer: external_exports.object({
|
|
14683
|
+
text: external_exports.string().trim().min(1).describe(`Your answer in your own words, at most ${ASK_MAX2} characters; the context goes in units.`),
|
|
14684
|
+
selectedOptionIds: external_exports.array(external_exports.string().trim().min(1)).min(1).max(6).optional().describe("The ids of the options you choose, when the Question offered options (in order, for a ranking).")
|
|
14685
|
+
}).strict(),
|
|
14686
|
+
units: units2
|
|
14687
|
+
}).strict().superRefine((a, ctx) => refuseOverCaps2(a.answer.text, a.units, "text", ctx));
|
|
14688
|
+
var QuestionDependencySchema2 = external_exports.object({
|
|
14689
|
+
questionId: questionHandle2.describe("A Question you asked."),
|
|
14690
|
+
change: external_exports.enum(["add", "remove"]),
|
|
14691
|
+
blocked: NodeRef2,
|
|
14692
|
+
action: BlockedAction2,
|
|
14693
|
+
reason: reason2.optional()
|
|
14694
|
+
}).strict().refine(actionFits2, ACTION_FITS2);
|
|
14695
|
+
var sendFields2 = {
|
|
14696
|
+
updates: external_exports.array(UpdateInputSchema2).min(1).max(20).optional().describe(
|
|
14697
|
+
"News: progress, findings, a result. An update asks nothing. It reaches the person only when they asked for it (userExplicitlyRequested), when it answers something they said, or once its Goal is done; otherwise it is recorded as the Goal's progress and nobody is notified."
|
|
14698
|
+
),
|
|
14699
|
+
answers: external_exports.array(AnswerInputSchema2).min(1).max(20).optional().describe("Answers to Questions addressed to you."),
|
|
14700
|
+
withdrawQuestionIds: external_exports.array(questionHandle2).min(1).max(10).optional().describe(
|
|
14701
|
+
"Questions you asked that no longer need an answer: you acted on them yourself, they no longer matter, or you asked wrongly. Each is cancelled, not answered: its card closes and nothing rings for it."
|
|
14381
14702
|
),
|
|
14703
|
+
questionDependencies: external_exports.array(QuestionDependencySchema2).min(1).max(20).optional().describe(
|
|
14704
|
+
"Add or remove what a Question you asked holds up, after asking it."
|
|
14705
|
+
)
|
|
14706
|
+
};
|
|
14707
|
+
var ContactSchema2 = external_exports.object({
|
|
14708
|
+
...sendFields2,
|
|
14709
|
+
questions: external_exports.array(QuestionInputSchema2).min(1).max(20).optional().describe("Questions for the person, one per object."),
|
|
14710
|
+
/** RECEIVING (2I, I2; brain_prompts.md §2.2): the events this agent handled. */
|
|
14382
14711
|
ackEventIds: external_exports.array(external_exports.string().uuid()).min(1).max(100).optional().describe(
|
|
14383
|
-
"The exact eventIds of the events you have handled: an acknowledgment (ACK). Reading alone acknowledges nothing, so an event comes back until you acknowledge it; after that the next batch can come. Each recipient acknowledges separately, for itself only. A Question you owe is answered (
|
|
14712
|
+
"The exact eventIds of the events you have handled: an acknowledgment (ACK). Reading alone acknowledges nothing, so an event comes back until you acknowledge it; after that the next batch can come. Each recipient acknowledges separately, for itself only. A Question you owe is answered (answers), never acknowledged away."
|
|
14713
|
+
),
|
|
14714
|
+
wait: external_exports.boolean().optional().describe(
|
|
14715
|
+
"Only controls when this returns. Sending: false (the default) returns at once; true waits up to about 45 seconds for a response to what you sent. Sending nothing: true (the default) waits up to about 45 seconds for anything addressed to you; false returns what is waiting now. It does not mean work is blocked, set urgency, or ask for a call. `waitOutcome` says which happened: `available`, `expired` (nothing arrived; wait again without sending again), or `not_waited`."
|
|
14384
14716
|
)
|
|
14385
14717
|
}).strict();
|
|
14386
|
-
var
|
|
14718
|
+
var sendsAnything2 = (c) => !!(c.updates?.length || c.questions?.length || c.answers?.length || c.withdrawQuestionIds?.length || c.questionDependencies?.length);
|
|
14719
|
+
var ContactWireSchema2 = external_exports.object({
|
|
14720
|
+
operationId: external_exports.string().uuid(),
|
|
14721
|
+
...sendFields2,
|
|
14722
|
+
questions: external_exports.array(QuestionWireSchema2).min(1).max(20).optional()
|
|
14723
|
+
}).strict().refine(sendsAnything2, "a contact sends at least one update, question, answer, withdrawal or question dependency");
|
|
14387
14724
|
var CONTACT_SCHEMA2 = { type: "object", ...mcpInputSchema2(ContactSchema2) };
|
|
14388
|
-
var CONTACT_DESCRIPTION2 =
|
|
14725
|
+
var CONTACT_DESCRIPTION2 = `Send to and receive from the person: updates, questions, answers to questions addressed to you, withdrawals of your own questions, and what a question holds up. Every Goal you name must already exist: create it with manage_goals first; an unknown Goal is refused and nothing is sent. Every item applies on its own: read \`results\`, where a failed item names why while the rest still apply.
|
|
14726
|
+
|
|
14727
|
+
QUESTIONS: one question per object; several questions are several objects in one contact, so each can be answered on its own. Each is sent exactly as written, with no reading in between: a bundled question arrives as one card. Give options when you have them. Each option's label must stand on its own \u2014 the person may see only the labels \u2014 so never a label that points into your text ('All three', 'Option 2', '1 and 3 only'). pickMode:'one' (the default) makes a pick, for alternatives; 'many' a checklist, for independent options they may want several of; 'rank' an ordering. The person can always answer in their own words, so never add an 'Other' option. workItBlocks names the work a question holds up, only when you are truly blocked: it asks for a call. Answers land on the question's Goal: collect them by receiving (below) or with get_goal.
|
|
14728
|
+
|
|
14729
|
+
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.
|
|
14730
|
+
|
|
14731
|
+
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.
|
|
14732
|
+
|
|
14733
|
+
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\`).`;
|
|
14389
14734
|
var CreateGoalSchema2 = external_exports.object({
|
|
14390
14735
|
outcome: external_exports.string().trim().min(1).max(1e4),
|
|
14391
14736
|
/** The work's NAME (#2115) — one to five words, how a person refers to it out loud ("the night
|
|
@@ -14419,29 +14764,20 @@ var UpdateGoalSchema2 = external_exports.object({
|
|
|
14419
14764
|
state: external_exports.enum(["active", "done", "cancelled"]).optional(),
|
|
14420
14765
|
progress: external_exports.string().trim().min(1).max(1e4).optional(),
|
|
14421
14766
|
reviewed: external_exports.literal(true).optional(),
|
|
14422
|
-
dueAt: external_exports.string().datetime({ offset: true }).nullable().optional()
|
|
14423
|
-
/** WITHDRAW YOUR OWN QUESTION (#2777, owner 2026-09-30). The question's id as every read shows it
|
|
14424
|
-
* (the conversation's `id`, eight characters, or the whole request Entry id), on THIS Goal, asked
|
|
14425
|
-
* by you and still open. It is cancelled, not answered: its cards close and nothing rings for it. */
|
|
14426
|
-
withdraw: external_exports.array(external_exports.string().trim().regex(/^[0-9a-fA-F-]{8,36}$/)).min(1).max(10).optional()
|
|
14767
|
+
dueAt: external_exports.string().datetime({ offset: true }).nullable().optional()
|
|
14427
14768
|
}).strict().refine((v) => Object.keys(v).length > 0),
|
|
14428
14769
|
reason: external_exports.string().trim().min(1).max(2e3),
|
|
14429
14770
|
operationId: external_exports.string().uuid().optional()
|
|
14430
14771
|
}).strict();
|
|
14431
|
-
var UpdateGoalToolSchema2 = external_exports.object({
|
|
14432
|
-
goalId: external_exports.string().uuid(),
|
|
14433
|
-
revision: UpdateGoalSchema2.shape.revision,
|
|
14434
|
-
changes: UpdateGoalSchema2.shape.changes.innerType().pick({ progress: true, reviewed: true, dueAt: true, withdraw: true }).strict().refine((v) => Object.keys(v).length > 0),
|
|
14435
|
-
reason: UpdateGoalSchema2.shape.reason
|
|
14436
|
-
}).strict();
|
|
14437
14772
|
var changedGoal2 = external_exports.string().uuid().describe("The Goal to change, as a read shows it.");
|
|
14438
14773
|
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.");
|
|
14439
|
-
var
|
|
14774
|
+
var GOAL_OUTCOME_MAX2 = 500;
|
|
14775
|
+
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.`);
|
|
14440
14776
|
var CreateChange2 = external_exports.object({
|
|
14441
14777
|
kind: external_exports.literal("create"),
|
|
14442
14778
|
title: goalTitle2,
|
|
14443
14779
|
outcome: goalOutcome2,
|
|
14444
|
-
ownerId: external_exports.string().trim().min(1).optional().describe("Who owns the work: an agent's participant (as
|
|
14780
|
+
ownerId: external_exports.string().trim().min(1).optional().describe("Who owns the work: an agent's participant (as check_activity shows it) or the person's. Omitted: you."),
|
|
14445
14781
|
parentGoalId: external_exports.string().uuid().optional().describe("The Goal it belongs under. Omitted: a root."),
|
|
14446
14782
|
sourceEntryIds: external_exports.array(external_exports.string().uuid()).max(20).optional().describe("The whole Entries the work came from, ones you can read.")
|
|
14447
14783
|
}).strict();
|
|
@@ -14451,7 +14787,7 @@ var StateChange2 = external_exports.object({ kind: external_exports.literal("sta
|
|
|
14451
14787
|
) }).strict();
|
|
14452
14788
|
var AssignChange2 = external_exports.object({ kind: external_exports.literal("assign"), goalId: changedGoal2, ownerId: external_exports.string().trim().min(1).describe("The new owner's participant.") }).strict();
|
|
14453
14789
|
var DeferChange2 = external_exports.object({ kind: external_exports.literal("defer"), goalId: changedGoal2, until: external_exports.string().datetime({ offset: true }).nullable().describe(
|
|
14454
|
-
"Postpone it until this instant
|
|
14790
|
+
"Postpone it until this instant: it waits until then, and its owner is woken when it passes (a promise to follow up later). null takes the postponement back."
|
|
14455
14791
|
) }).strict();
|
|
14456
14792
|
var MoveChange2 = external_exports.object({ kind: external_exports.literal("move"), goalId: changedGoal2, parentGoalId: external_exports.string().uuid().nullable().describe(
|
|
14457
14793
|
"Its new parent, or null for a root. Only this Goal moves; its new parent's other children stay."
|
|
@@ -14462,7 +14798,7 @@ var DependencyChange2 = external_exports.object({
|
|
|
14462
14798
|
goalId: changedGoal2,
|
|
14463
14799
|
dependsOnGoalId: external_exports.string().uuid().describe("The Goal it waits on."),
|
|
14464
14800
|
action: external_exports.enum(["start", "complete"]).describe("What waits: starting goalId, or completing it."),
|
|
14465
|
-
reason: external_exports.string().trim().min(1).max(
|
|
14801
|
+
reason: external_exports.string().trim().min(1).max(250).optional().describe("Why, at most 250 characters, as an explanation, never as policy.")
|
|
14466
14802
|
}).strict();
|
|
14467
14803
|
var read2 = { revision: external_exports.number().int().positive().optional() };
|
|
14468
14804
|
var GoalChangeSchema2 = external_exports.discriminatedUnion("kind", [
|
|
@@ -14486,8 +14822,7 @@ var ManageGoalsSchema2 = external_exports.object({
|
|
|
14486
14822
|
var ManageGoalsToolSchema2 = external_exports.object({
|
|
14487
14823
|
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.")
|
|
14488
14824
|
}).strict().superRefine((v, ctx) => editNames2(v.changes, ctx));
|
|
14489
|
-
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,
|
|
14490
|
-
var ClaimGoalSchema2 = external_exports.object({ goalId: external_exports.string().uuid().optional() }).strict();
|
|
14825
|
+
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.";
|
|
14491
14826
|
var GetGoalSchema2 = external_exports.object({
|
|
14492
14827
|
goalId: external_exports.string().uuid(),
|
|
14493
14828
|
/** Every entry in full. Without it the read carries the person's words, open questions and your
|
|
@@ -14498,25 +14833,29 @@ var GetGoalSchema2 = external_exports.object({
|
|
|
14498
14833
|
* between two of them named. Off by default; `docs/model/goal/diagnose-design.md`. */
|
|
14499
14834
|
diagnose: external_exports.boolean().optional()
|
|
14500
14835
|
}).strict();
|
|
14501
|
-
var GET_GOAL_DESCRIPTION2 = "Read one Goal
|
|
14502
|
-
var UPDATE_GOAL_DESCRIPTION2 = "Report work on a Goal you are on, at an exact revision: progress, review acknowledgement, when to wake for it, and withdrawing a question of yours. What the Goal is (its title, outcome, state, owner, parent, dependencies) changes with manage_goals. You are on a Goal you own, or one you have written on (an update with progress, or a contact on it); any of your person's agents may write on any of their Goals, and one that never touched a Goal is refused (409 goal_not_joined). Stale revisions are rejected. A CONTACT ON THIS GOAL MOVES ITS REVISION: a question filed on a Goal is a change to it, so an update prepared before a contact and sent after it is refused as stale (409 goal_revision_conflict) \u2014 re-read the Goal, then write. progress says where the work stands. reviewed: true acknowledges new evidence and closes the Deliveries addressed to you on that Goal, never over an open decision (contact({ackEventIds}) does the same for an `update` event). dueAt (an ISO instant, or null) makes the Goal wait until then; when it passes you are woken for it \u2014 use it for a promise to follow up later. withdraw: [questionId] takes back a question YOU asked on this Goal that is still open \u2014 because you acted on it yourself, it no longer matters, or you asked it wrongly (e.g. waiting: hard when nothing was blocked): it is cancelled, not answered, its card closes and it stops ringing; the id is the one the conversation shows. Returns the Goal as get_goal reads it, at its new revision.";
|
|
14503
|
-
var CLAIM_GOAL_DESCRIPTION2 = "Claim a pending answer to your question or the oldest runnable or review-pending Goal you own. Pass goalId to join any Goal of your person; its assignment stays unchanged. Read others before overlapping another agent\u2019s work. Joining lets you contribute and change it; use manage_goals (assign) when the assignment itself should change. Returns the Goal as get_goal reads it, and marks you as on it, which never shuts another agent out: other agents of your person may write on it and change it too, and two changes at once are told apart by revision (409 goal_revision_conflict).";
|
|
14836
|
+
var GET_GOAL_DESCRIPTION2 = "Read one Goal (a read never puts you on it), including others (the ten most recent other contributors, with names, latest entry headlines and times): its outcome, state, revision, progress, blockers, the conversation on it (each question with its options and what was decided), `next`, the one step to take, and `more`, where to read further. A question in that conversation reads `open` (nothing yet), `answered` (a choice was made, and `answer` carries it), `replied` (they said something and the read settled the question on their words \u2014 NO option of yours was chosen, and the words are the reply line beside it, so read that before you act), or `closed`. The conversation carries everything the person said, every open question and your newest entry; your older entries are left out and counted \u2014 history: true reads every entry in full. Every Goal of your person is readable, whichever of their agents owns it; another account's Goals are not disclosed.\n\ndiagnose: true is for when a read has CONFUSED you \u2014 not for the working loop. It adds `diagnosis`, which asks every reader that already answers a question about your work and NAMES the reader behind every value, so you never guess which of two places to look: what state your work is really in (the stored `goals.state` beside the derived `goal_execution_state`, which can differ); whether anything is armed to ring and when (`ladder_candidates`, with the ladder's own last decision); whether a wake fired for you and what it did (durable `wake.*` events, beside when your process was last heard from); and what has reached you (`list_waiting` \u2014 review flags, unstarted work, open questions, the person's newest words). Read `diagnosis.disagreements` FIRST: each one is two readers giving different values for the same fact, with both values and both sources, and it never chooses between them \u2014 that is yours to do, from the Goal's own history. `diagnosis.looked` names every reader asked, including any that could not answer, so an empty answer is never confused with a broken one.";
|
|
14504
14837
|
var SearchToolSchema2 = external_exports.object({
|
|
14505
14838
|
query: external_exports.string().trim().min(1).max(500),
|
|
14506
14839
|
types: external_exports.array(external_exports.enum(["entry", "goal", "answer"])).min(1).optional(),
|
|
14507
14840
|
goalId: external_exports.string().uuid().optional(),
|
|
14508
14841
|
limit: external_exports.number().int().min(1).max(20).optional()
|
|
14509
14842
|
}).strict();
|
|
14510
|
-
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
|
|
14511
|
-
var
|
|
14843
|
+
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.";
|
|
14844
|
+
var CheckActivitySchema2 = external_exports.object({}).strict();
|
|
14845
|
+
var FEEDBACK_TEXT_MAX2 = 5e4;
|
|
14846
|
+
var SendFeedbackSchema2 = external_exports.object({
|
|
14847
|
+
text: external_exports.string().trim().min(1).max(FEEDBACK_TEXT_MAX2).describe(`The report, in markdown: what happened, what was expected, how to reproduce it, versions, evidence. At most ${FEEDBACK_TEXT_MAX2} characters.`),
|
|
14848
|
+
title: external_exports.string().trim().min(1).max(200).optional().describe("One line naming it."),
|
|
14849
|
+
kind: external_exports.enum(["bug", "idea", "other"]).default("other").describe("bug: something Paigy does wrong. idea: something it should do. other: anything else.")
|
|
14850
|
+
}).strict();
|
|
14851
|
+
var SEND_FEEDBACK_DESCRIPTION2 = "Send feedback about Paigy itself (a bug report, an idea) straight to the Paigy team. Send it only when the person asked you to, or agreed when you offered: it goes to Paigy, never to the person, and nobody answers it here. Not for your work or a question for the person: use contact for those. Returns whether it was stored.";
|
|
14512
14852
|
var AGENT_TOOLS2 = [
|
|
14513
|
-
{ name: "who_is_working", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goal to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema2(WhoIsWorkingSchema2) },
|
|
14514
14853
|
{ name: "contact", description: CONTACT_DESCRIPTION2, inputSchema: CONTACT_SCHEMA2 },
|
|
14515
14854
|
{ name: "manage_goals", description: MANAGE_GOALS_DESCRIPTION2, inputSchema: mcpInputSchema2(ManageGoalsToolSchema2) },
|
|
14516
|
-
{ name: "claim_goal", description: CLAIM_GOAL_DESCRIPTION2, inputSchema: mcpInputSchema2(ClaimGoalSchema2) },
|
|
14517
14855
|
{ name: "get_goal", description: GET_GOAL_DESCRIPTION2, inputSchema: mcpInputSchema2(GetGoalSchema2) },
|
|
14518
|
-
{ name: "
|
|
14519
|
-
{ name: "
|
|
14856
|
+
{ name: "search", description: SEARCH_DESCRIPTION2, inputSchema: mcpInputSchema2(SearchToolSchema2) },
|
|
14857
|
+
{ name: "check_activity", description: "Read your person's agents that contributed in the last 24 hours, newest first: at most 30 agents and their five most recently touched Goals, with their latest entry headline and time. Use get_goal to read a Goal and its other contributors before starting overlapping work. Read-only; no arguments.", inputSchema: mcpInputSchema2(CheckActivitySchema2) },
|
|
14858
|
+
{ name: "send_feedback", description: SEND_FEEDBACK_DESCRIPTION2, inputSchema: mcpInputSchema2(SendFeedbackSchema2) }
|
|
14520
14859
|
];
|
|
14521
14860
|
var AGENT_TOOL_NAMES2 = AGENT_TOOLS2.map((t) => t.name);
|
|
14522
14861
|
var id2 = external_exports.string().uuid();
|
|
@@ -14564,6 +14903,7 @@ var QuestionChangeSchema2 = external_exports.union([
|
|
|
14564
14903
|
external_exports.object({
|
|
14565
14904
|
kind: external_exports.literal("create"),
|
|
14566
14905
|
text: external_exports.string().min(1),
|
|
14906
|
+
summary: external_exports.string().optional(),
|
|
14567
14907
|
answererId: participant2,
|
|
14568
14908
|
goal: ResultRefSchema2.optional(),
|
|
14569
14909
|
blocks: external_exports.array(external_exports.object({ question: ResultRefSchema2 }).strict()),
|
|
@@ -14614,12 +14954,12 @@ var BrainSearchSchema2 = external_exports.object({
|
|
|
14614
14954
|
}).strict();
|
|
14615
14955
|
var BrainResultSchema2 = external_exports.object({
|
|
14616
14956
|
messages: external_exports.array(BrainMessageSchema2),
|
|
14617
|
-
entries: external_exports.array(external_exports.object({ key: key2, sources: external_exports.array(SourceSchema2).min(1), goals: external_exports.array(ResultRefSchema2) }).strict()),
|
|
14957
|
+
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()),
|
|
14618
14958
|
questions: external_exports.array(external_exports.object({ key: key2, entryKeys: external_exports.array(key2), change: QuestionChangeSchema2 }).strict()),
|
|
14619
14959
|
answers: external_exports.array(external_exports.object({
|
|
14620
14960
|
question: ResultRefSchema2,
|
|
14621
14961
|
entryKeys: external_exports.array(key2).min(1),
|
|
14622
|
-
summary: external_exports.string()
|
|
14962
|
+
summary: external_exports.string(),
|
|
14623
14963
|
selectedOptionIds: external_exports.array(external_exports.string().min(1)).optional()
|
|
14624
14964
|
}).strict()),
|
|
14625
14965
|
goals: external_exports.array(external_exports.object({ key: key2, entryKeys: external_exports.array(key2), change: BrainGoalChangeSchema2 }).strict()),
|
|
@@ -14628,33 +14968,150 @@ var BrainResultSchema2 = external_exports.object({
|
|
|
14628
14968
|
next: BrainNextSchema2,
|
|
14629
14969
|
search: BrainSearchSchema2.optional()
|
|
14630
14970
|
}).strict();
|
|
14631
|
-
var
|
|
14632
|
-
var
|
|
14971
|
+
var str4 = external_exports.string();
|
|
14972
|
+
var strs4 = external_exports.array(external_exports.string());
|
|
14633
14973
|
var CompactResultSchema2 = external_exports.object({
|
|
14634
|
-
next: external_exports.object({ say:
|
|
14635
|
-
messages: external_exports.array(external_exports.object({ key:
|
|
14636
|
-
entries: external_exports.array(external_exports.object({ key:
|
|
14637
|
-
answers: external_exports.array(external_exports.object({ question:
|
|
14974
|
+
next: external_exports.object({ say: strs4, then: external_exports.enum(["listen", "hold", "end", "none"]), waitFor: strs4, reason: str4, instructions: strs4 }).strict(),
|
|
14975
|
+
messages: external_exports.array(external_exports.object({ key: str4, to: str4, text: str4, about: strs4, entries: strs4, owed: strs4, inviteToCall: external_exports.boolean() }).strict()),
|
|
14976
|
+
entries: external_exports.array(external_exports.object({ key: str4, from: str4, on: strs4 }).strict()),
|
|
14977
|
+
answers: external_exports.array(external_exports.object({ question: str4, entries: strs4, summary: str4, picked: strs4 }).strict()),
|
|
14638
14978
|
changes: external_exports.array(external_exports.object({
|
|
14639
|
-
key:
|
|
14979
|
+
key: str4,
|
|
14640
14980
|
what: external_exports.enum(["goal", "question", "lesson"]),
|
|
14641
14981
|
op: external_exports.enum(["create", "edit", "assign", "open", "complete", "cancel", "defer", "move", "block", "unblock", "withdraw", "remember", "revise"]),
|
|
14642
|
-
id:
|
|
14643
|
-
text:
|
|
14644
|
-
title:
|
|
14645
|
-
who:
|
|
14646
|
-
under:
|
|
14982
|
+
id: str4,
|
|
14983
|
+
text: str4,
|
|
14984
|
+
title: str4,
|
|
14985
|
+
who: str4,
|
|
14986
|
+
under: str4,
|
|
14647
14987
|
gate: external_exports.enum(["start", "complete", "answer", "none"]),
|
|
14648
|
-
options: external_exports.array(external_exports.object({ id:
|
|
14988
|
+
options: external_exports.array(external_exports.object({ id: str4, label: str4 }).strict()),
|
|
14649
14989
|
pick: external_exports.enum(["one", "many", "rank", "words"]),
|
|
14650
|
-
entries:
|
|
14990
|
+
entries: strs4
|
|
14651
14991
|
}).strict()),
|
|
14652
|
-
feedback: external_exports.array(
|
|
14653
|
-
search: external_exports.object({ query:
|
|
14992
|
+
feedback: external_exports.array(strs4),
|
|
14993
|
+
search: external_exports.object({ query: str4, within: strs4, goal: str4 }).strict()
|
|
14654
14994
|
}).strict();
|
|
14655
14995
|
var COMPACT_RESULT_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
14656
14996
|
zodToJsonSchema2(CompactResultSchema2, { $refStrategy: "none" })
|
|
14657
14997
|
);
|
|
14998
|
+
var str22 = external_exports.string();
|
|
14999
|
+
var strs22 = external_exports.array(external_exports.string());
|
|
15000
|
+
var TalkerReplySchema2 = external_exports.object({
|
|
15001
|
+
/** What to say and send, messages to the person first: spoken in the order written. `to` is "user"
|
|
15002
|
+
* or an agent's handle; `about` names handles; `blocks` is the item a question to an agent must be
|
|
15003
|
+
* answered before ("" for none). */
|
|
15004
|
+
messages: external_exports.array(external_exports.object({ key: str22, to: str22, text: str22, about: strs22, blocks: str22 }).strict()),
|
|
15005
|
+
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
15006
|
+
then: external_exports.enum(["listen", "hold", "end"]),
|
|
15007
|
+
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
15008
|
+
* IDs, the line handles, how they replied in under ten words (keeping any condition), and `defer`:
|
|
15009
|
+
* "" when their words settle the item, else what a reply that puts it off waits for -- "call" (the
|
|
15010
|
+
* end of this call), a question's handle (that question's answer) or an ISO time. */
|
|
15011
|
+
replies: external_exports.array(external_exports.object({ item: str22, options: strs22, lines: strs22, summary: str22, defer: str22 }).strict()),
|
|
15012
|
+
/** The line handles that tell the talker how to run this call; they last until it ends and reach no agent. */
|
|
15013
|
+
instruction: strs22,
|
|
15014
|
+
/** The line handles the filer acts on, and the only ones it reads (3e): new work, a change to existing
|
|
15015
|
+
* work, feedback on Paigy, anything said to an agent, and any rule meant to outlast the call. */
|
|
15016
|
+
file: strs22,
|
|
15017
|
+
/** Evidence to fetch for a second round: a search, an item's full text, or an agent by name. */
|
|
15018
|
+
need: external_exports.object({ kind: external_exports.enum(["search", "item", "agent", ""]), text: str22 }).strict(),
|
|
15019
|
+
/** The handle of the item to explain ("" for none). */
|
|
15020
|
+
explain: str22
|
|
15021
|
+
}).strict();
|
|
15022
|
+
var TALKER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
15023
|
+
zodToJsonSchema2(TalkerReplySchema2, { $refStrategy: "none" })
|
|
15024
|
+
);
|
|
15025
|
+
var str32 = external_exports.string();
|
|
15026
|
+
var strs32 = external_exports.array(external_exports.string());
|
|
15027
|
+
var FilerReplySchema2 = external_exports.object({
|
|
15028
|
+
/** Which Goals each new line goes on: a line's handle, Goal handles (or keys of new Goals), and what
|
|
15029
|
+
* the line says in at most ten words. */
|
|
15030
|
+
filed: external_exports.array(external_exports.object({ line: str32, goals: strs32, summary: str32 }).strict()),
|
|
15031
|
+
goals: external_exports.array(external_exports.object({
|
|
15032
|
+
op: external_exports.enum(["create", "edit", "complete", "cancel", "reopen", "assign", "defer", "move", "block", "unblock"]),
|
|
15033
|
+
key: str32,
|
|
15034
|
+
goal: str32,
|
|
15035
|
+
title: str32,
|
|
15036
|
+
outcome: str32,
|
|
15037
|
+
owner: str32,
|
|
15038
|
+
under: str32,
|
|
15039
|
+
until: str32,
|
|
15040
|
+
on: str32,
|
|
15041
|
+
gate: external_exports.enum(["start", "complete", "answer", ""]),
|
|
15042
|
+
why: str32
|
|
15043
|
+
}).strict()),
|
|
15044
|
+
questions: external_exports.array(external_exports.object({
|
|
15045
|
+
op: external_exports.enum(["create", "edit", "assign", "withdraw"]),
|
|
15046
|
+
key: str32,
|
|
15047
|
+
question: str32,
|
|
15048
|
+
text: str32,
|
|
15049
|
+
summary: str32,
|
|
15050
|
+
to: str32,
|
|
15051
|
+
goal: str32,
|
|
15052
|
+
options: external_exports.array(external_exports.object({ id: str32, label: str32 }).strict()),
|
|
15053
|
+
pick: external_exports.enum(["one", "many", "rank", "words", ""]),
|
|
15054
|
+
blocks: str32
|
|
15055
|
+
}).strict()),
|
|
15056
|
+
lessons: external_exports.array(external_exports.object({ op: external_exports.enum(["remember", "revise", "withdraw"]), lesson: str32, text: str32, scope: str32 }).strict()),
|
|
15057
|
+
/** Line handles whose words comment on Paigy itself. */
|
|
15058
|
+
feedback: strs32
|
|
15059
|
+
}).strict();
|
|
15060
|
+
var FILER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
15061
|
+
zodToJsonSchema2(FilerReplySchema2, { $refStrategy: "none" })
|
|
15062
|
+
);
|
|
15063
|
+
var ExplainerReplySchema2 = external_exports.object({
|
|
15064
|
+
/** The explanation, as sentences, spoken in order. */
|
|
15065
|
+
say: external_exports.array(external_exports.string()),
|
|
15066
|
+
/** Questions to an agent about gaps in what it read: the agent's handle and the question. */
|
|
15067
|
+
followUps: external_exports.array(external_exports.object({ to: external_exports.string(), text: external_exports.string() }).strict())
|
|
15068
|
+
}).strict();
|
|
15069
|
+
var EXPLAINER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
15070
|
+
zodToJsonSchema2(ExplainerReplySchema2, { $refStrategy: "none" })
|
|
15071
|
+
);
|
|
15072
|
+
var DISTILLER_DEFECTS2 = [
|
|
15073
|
+
"reasked",
|
|
15074
|
+
// a question put to the person again after they answered it
|
|
15075
|
+
"unsupported",
|
|
15076
|
+
// a fact stated that nothing in the input supports
|
|
15077
|
+
"unsaid",
|
|
15078
|
+
// an agent's answer arrived during the call and was never said
|
|
15079
|
+
"skipped",
|
|
15080
|
+
// an agenda item never put to the person
|
|
15081
|
+
"unsaved",
|
|
15082
|
+
// something the person answered or asked for did not save, was refused or dropped
|
|
15083
|
+
"ignored",
|
|
15084
|
+
// the person asked or said something and was never answered (the critic's ignored_a_question)
|
|
15085
|
+
"promised"
|
|
15086
|
+
// said it did or would do something, and nothing shows it done (the critic's broken_promise)
|
|
15087
|
+
];
|
|
15088
|
+
var DistillerReplySchema2 = external_exports.object({
|
|
15089
|
+
defects: external_exports.array(external_exports.object({
|
|
15090
|
+
kind: external_exports.enum(DISTILLER_DEFECTS2),
|
|
15091
|
+
/** The handles of the lines it shows in. */
|
|
15092
|
+
lines: external_exports.array(external_exports.string()),
|
|
15093
|
+
/** The handle of the record it concerns, or "". */
|
|
15094
|
+
about: external_exports.string(),
|
|
15095
|
+
/** One plain sentence, in words, never handles: it is what the feedback row keeps. */
|
|
15096
|
+
why: external_exports.string()
|
|
15097
|
+
}).strict())
|
|
15098
|
+
}).strict();
|
|
15099
|
+
var DISTILLER_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
15100
|
+
zodToJsonSchema2(DistillerReplySchema2, { $refStrategy: "none" })
|
|
15101
|
+
);
|
|
15102
|
+
var TIDY_AUDIENCES2 = ["talker", "filer", "agents"];
|
|
15103
|
+
var TidyReplySchema2 = external_exports.object({
|
|
15104
|
+
lessons: external_exports.array(external_exports.object({
|
|
15105
|
+
lesson: external_exports.string(),
|
|
15106
|
+
op: external_exports.enum(["keep", "revise", "withdraw"]),
|
|
15107
|
+
text: external_exports.string(),
|
|
15108
|
+
for: external_exports.enum([...TIDY_AUDIENCES2, ""]),
|
|
15109
|
+
into: external_exports.string()
|
|
15110
|
+
}).strict())
|
|
15111
|
+
}).strict();
|
|
15112
|
+
var TIDY_REPLY_JSON_SCHEMA2 = (({ $schema: _, ...rest }) => rest)(
|
|
15113
|
+
zodToJsonSchema2(TidyReplySchema2, { $refStrategy: "none" })
|
|
15114
|
+
);
|
|
14658
15115
|
function entryWords(entry) {
|
|
14659
15116
|
const content = entry.content;
|
|
14660
15117
|
if (content && "sealed" in content) return "";
|
|
@@ -14678,7 +15135,10 @@ var ContextSchema2 = external_exports.object({
|
|
|
14678
15135
|
title: external_exports.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
14679
15136
|
description: external_exports.array(external_exports.string().min(1)).describe(
|
|
14680
15137
|
"Semantic chunks of detail (each a standalone, non-empty piece). The user can select chunks to ask you to expand. MAY BE EMPTY: a claim whose whole content is its heading \u2014 a single sentence \u2014 has no body, and saying so beats repeating the heading underneath itself. That repeat is what `min(1)` used to force, at 2x the storage, with every reader subtracting it back out at render time."
|
|
14681
|
-
)
|
|
15138
|
+
),
|
|
15139
|
+
/** THE ASK'S UNITS (3c): its context as titled units, in order. The app lists the titles and opens
|
|
15140
|
+
* a body on a tap. Absent on an ask sent without units, which renders as before. */
|
|
15141
|
+
units: external_exports.array(external_exports.object({ title: external_exports.string(), body: external_exports.string() })).optional()
|
|
14682
15142
|
});
|
|
14683
15143
|
var ParticipantSchema2 = external_exports.object({
|
|
14684
15144
|
kind: external_exports.enum(["human", "agent"]),
|
|
@@ -14753,7 +15213,7 @@ var NotifyRequestFields2 = external_exports.object({
|
|
|
14753
15213
|
* ABSENT on the E2EE path, where the sealed `envelope` below carries it. The
|
|
14754
15214
|
* superRefine at the bottom enforces exactly one of the two. */
|
|
14755
15215
|
context: ContextSchema2.optional(),
|
|
14756
|
-
options: external_exports.array(
|
|
15216
|
+
options: external_exports.array(OptionSchema2.omit({ id: true }).extend({ label: external_exports.string().trim().min(1).max(1e3) }).strict()).min(OPTIONS_MIN2).max(OPTIONS_MAX2).optional().describe(
|
|
14757
15217
|
"The choices, in order \u2014 required when select is 'one'/'many'/'rank', omitted otherwise. Ids are assigned automatically by position ('1', '2', \u2026); the user's answer references them as optionId(s)."
|
|
14758
15218
|
),
|
|
14759
15219
|
points: external_exports.array(external_exports.string().min(1)).optional().describe(
|
|
@@ -15351,7 +15811,7 @@ var ConnectionSummarySchema2 = external_exports.object({
|
|
|
15351
15811
|
* desktop app while open. Null = never seen; stale = offline. */
|
|
15352
15812
|
lastSeenAt: external_exports.string().datetime().nullable().optional(),
|
|
15353
15813
|
/** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
|
|
15354
|
-
* its Goals —
|
|
15814
|
+
* its Goals — wrote on one or recorded an operation (`tokens.last_worked_at`). Within
|
|
15355
15815
|
* `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
|
|
15356
15816
|
lastWorkedAt: external_exports.string().datetime().nullable().optional(),
|
|
15357
15817
|
/** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
|
|
@@ -15514,8 +15974,8 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
15514
15974
|
repo: external_exports.string().nullable().optional(),
|
|
15515
15975
|
createdAt: external_exports.string(),
|
|
15516
15976
|
updatedAt: external_exports.string().nullable().default(null),
|
|
15517
|
-
/** When its owner last SAID something about it (`
|
|
15518
|
-
*
|
|
15977
|
+
/** When its owner last SAID something about it (the newest `progress` Entry: a contact update kept
|
|
15978
|
+
* as progress). `updatedAt` moves for reasons nobody chose — a
|
|
15519
15979
|
* state recomputed, a review flag — so it cannot tell work in hand from work gone quiet. */
|
|
15520
15980
|
lastProgressAt: external_exports.string().nullable().optional(),
|
|
15521
15981
|
/** THE GOAL'S NEWEST WORD, FROM EITHER SIDE (owner, 2026-09-27): the newest Entry on it, of any
|
|
@@ -15549,7 +16009,7 @@ var QueueItemSchema2 = external_exports.object({
|
|
|
15549
16009
|
*
|
|
15550
16010
|
* IT IS THE PAYLOAD'S OWN HASH, NEVER A STAMP ON THE WORK. Nothing here reasons about which
|
|
15551
16011
|
* writes change which field — the comparison is over the bytes the phone is holding, so a fact
|
|
15552
|
-
* the row shows that no `updated_at` moves for (
|
|
16012
|
+
* the row shows that no `updated_at` moves for (`active` lapsing, a dependency's state, a
|
|
15553
16013
|
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
15554
16014
|
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
15555
16015
|
rev: external_exports.string().optional()
|
|
@@ -15671,7 +16131,7 @@ var HostDecisionSchema2 = external_exports.object({
|
|
|
15671
16131
|
/** The agent's token id: the row's `recipient`. */
|
|
15672
16132
|
agent: external_exports.string().uuid(),
|
|
15673
16133
|
decision: external_exports.enum(["stood_back", "took_over"]),
|
|
15674
|
-
/** The work it was about: the Goal
|
|
16134
|
+
/** The work it was about: the Goal waiting on that agent next (`claimable` on its `contact({})` read). */
|
|
15675
16135
|
goalId: external_exports.string().uuid().nullable().optional(),
|
|
15676
16136
|
/** When the server last heard from the agent, as the host read it: the presence it stood back for. */
|
|
15677
16137
|
seenAt: external_exports.string().datetime().nullable().optional(),
|
|
@@ -15864,14 +16324,13 @@ var CallRecapSchema2 = external_exports.object({
|
|
|
15864
16324
|
owner: external_exports.string(),
|
|
15865
16325
|
state: external_exports.string(),
|
|
15866
16326
|
questions: external_exports.array(external_exports.object({ id: external_exports.string().uuid(), state: external_exports.string(), title: external_exports.string() })),
|
|
15867
|
-
/** `words` is always what they SAID, verbatim — the record, never replaced. `
|
|
15868
|
-
*
|
|
15869
|
-
*
|
|
15870
|
-
*
|
|
15871
|
-
* anything that is not an answer. */
|
|
16327
|
+
/** `words` is always what they SAID, verbatim — the record, never replaced. `summary` is what
|
|
16328
|
+
* the line says in ten words (owner, 2026-10-07): the filer's, or the talker's for the answer it
|
|
16329
|
+
* gave, so the row scans like a chosen option and their own words stay under it. Absent when
|
|
16330
|
+
* neither wrote one. */
|
|
15872
16331
|
/** `about` is the request the line answered (its question), null for words that answered none —
|
|
15873
16332
|
* the key the screen groups on, so one question is one row however many times it was answered. */
|
|
15874
|
-
lines: external_exports.array(external_exports.object({ entryId: external_exports.string().uuid(), words: external_exports.string(),
|
|
16333
|
+
lines: external_exports.array(external_exports.object({ entryId: external_exports.string().uuid(), words: external_exports.string(), summary: external_exports.string().optional(), about: external_exports.string().nullable().optional() }))
|
|
15875
16334
|
})),
|
|
15876
16335
|
unfiled: external_exports.array(external_exports.object({ lineId: external_exports.string().uuid(), words: external_exports.string(), atMs: external_exports.number() })),
|
|
15877
16336
|
more: external_exports.object({ lines: external_exports.number(), entries: external_exports.number(), topics: external_exports.number() })
|
|
@@ -15898,7 +16357,7 @@ function sameTail(a, b) {
|
|
|
15898
16357
|
}
|
|
15899
16358
|
|
|
15900
16359
|
// src/run.ts
|
|
15901
|
-
import { randomUUID as
|
|
16360
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
15902
16361
|
|
|
15903
16362
|
// src/harness/session.ts
|
|
15904
16363
|
import { spawn } from "child_process";
|
|
@@ -16182,7 +16641,7 @@ var frame2 = (body) => JSON.stringify({ jsonrpc: "2.0", ...body });
|
|
|
16182
16641
|
import { mkdirSync as mkdirSync3, readdirSync as readdirSync4, renameSync, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
16183
16642
|
import { join as join8 } from "path";
|
|
16184
16643
|
import { homedir as homedir8 } from "os";
|
|
16185
|
-
import { randomUUID as
|
|
16644
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
16186
16645
|
var MIDTURN_HOOK = `d="$PAIGY_INBOX"; [ -n "$d" ] || { cat >/dev/null; exit 0; }; set -- "$d"/*.note; [ -e "$1" ] || { cat >/dev/null; exit 0; }; grep '"agent_id"' >/dev/null && exit 0; mkdir -p "$d/taken"; o=""; for f in "$@"; do n=\${f##*/}; mv "$f" "$d/taken/$n" 2>/dev/null || continue; touch "$d/taken/$n"; o="$o\${o:+\\n\\n}$(cat "$d/taken/$n")"; done; [ -n "$o" ] || exit 0; printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"%s"}}' "$o"`;
|
|
16187
16646
|
var missing = (error) => error.code === "ENOENT";
|
|
16188
16647
|
function openInbox(dir) {
|
|
@@ -16242,7 +16701,7 @@ function inboxFor(root = INBOX_ROOT) {
|
|
|
16242
16701
|
} catch (error) {
|
|
16243
16702
|
if (!missing(error)) throw error;
|
|
16244
16703
|
}
|
|
16245
|
-
return openInbox(join8(root, `${process.pid}-${
|
|
16704
|
+
return openInbox(join8(root, `${process.pid}-${randomUUID3().slice(0, 8)}`));
|
|
16246
16705
|
}
|
|
16247
16706
|
|
|
16248
16707
|
// src/harness/session.ts
|
|
@@ -16376,6 +16835,32 @@ function startSession(opts) {
|
|
|
16376
16835
|
};
|
|
16377
16836
|
}
|
|
16378
16837
|
|
|
16838
|
+
// src/paigy/cut.ts
|
|
16839
|
+
function cut(text, title = "Details") {
|
|
16840
|
+
const all = text.trim();
|
|
16841
|
+
if (all.length <= ASK_MAX2) return { main: all };
|
|
16842
|
+
const main = head(all, ASK_MAX2);
|
|
16843
|
+
const units3 = [];
|
|
16844
|
+
let rest = all.slice(main.length).trim();
|
|
16845
|
+
while (rest && units3.length < UNITS_MAX2) {
|
|
16846
|
+
const last = units3.length === UNITS_MAX2 - 1;
|
|
16847
|
+
const body = last && rest.length > UNIT_BODY_MAX2 ? `${head(rest, UNIT_BODY_MAX2 - 1).trimEnd()}\u2026` : head(rest, UNIT_BODY_MAX2);
|
|
16848
|
+
units3.push({ title: units3.length ? `${title} (${units3.length + 1})` : title, body: body.trim() });
|
|
16849
|
+
rest = last ? "" : rest.slice(body.length).trim();
|
|
16850
|
+
}
|
|
16851
|
+
return { main: main.trim(), units: units3 };
|
|
16852
|
+
}
|
|
16853
|
+
function head(text, max) {
|
|
16854
|
+
if (text.length <= max) return text;
|
|
16855
|
+
const window = text.slice(0, max + 1);
|
|
16856
|
+
for (const boundary of [/\n\s*\n/g, /[.!?](\s)/g, /\s/g]) {
|
|
16857
|
+
let at = -1;
|
|
16858
|
+
for (const m of window.matchAll(boundary)) if (m.index > max / 3 && m.index < max) at = m.index + (boundary.source.startsWith("[") ? 1 : 0);
|
|
16859
|
+
if (at > 0) return text.slice(0, at);
|
|
16860
|
+
}
|
|
16861
|
+
return text.slice(0, max);
|
|
16862
|
+
}
|
|
16863
|
+
|
|
16379
16864
|
// src/paigy/limit.ts
|
|
16380
16865
|
var LIMIT_PREFIXES = [
|
|
16381
16866
|
"You've hit your",
|
|
@@ -16504,10 +16989,19 @@ var LIMITS = episodes();
|
|
|
16504
16989
|
// src/paigy/bridge.ts
|
|
16505
16990
|
function railFor(opts) {
|
|
16506
16991
|
return {
|
|
16507
|
-
|
|
16992
|
+
next: async () => {
|
|
16993
|
+
const waiting = await receive(opts);
|
|
16994
|
+
const goalId = waiting.claimable?.goalId ?? waiting.review?.[0]?.goalId;
|
|
16995
|
+
return goalId ? getGoal(goalId, opts) : { state: "idle", message: "" };
|
|
16996
|
+
},
|
|
16508
16997
|
read: (goalId) => getGoal(goalId, opts),
|
|
16509
|
-
|
|
16510
|
-
|
|
16998
|
+
complete: async (goalId, revision) => {
|
|
16999
|
+
const done = await manageGoals({ changes: [{ kind: "state", goalId, state: "completed", ...revision !== void 0 ? { revision } : {} }] }, opts);
|
|
17000
|
+
const result = done.results[0];
|
|
17001
|
+
if (result?.status !== "applied") throw new Error(`manage_goals failed: ${result?.error ?? "no result"}`);
|
|
17002
|
+
},
|
|
17003
|
+
ack: (eventIds) => acknowledge(eventIds, opts),
|
|
17004
|
+
// One read and back: the pump owns the waiting, so no wait is held here.
|
|
16511
17005
|
contact: (input) => contact(input, { ...opts, waits: false }),
|
|
16512
17006
|
create: async (input) => {
|
|
16513
17007
|
const made = await manageGoals({ changes: [{ kind: "create", ...input }] }, opts);
|
|
@@ -16536,13 +17030,18 @@ function decisionFrom(result) {
|
|
|
16536
17030
|
}
|
|
16537
17031
|
async function ask(state, rail, input) {
|
|
16538
17032
|
if (!state.goal) return null;
|
|
17033
|
+
const { main, units: units3 } = cut(input.ask);
|
|
16539
17034
|
try {
|
|
16540
17035
|
const sent = await rail.contact({
|
|
16541
|
-
|
|
16542
|
-
|
|
16543
|
-
|
|
17036
|
+
questions: [{
|
|
17037
|
+
question: main,
|
|
17038
|
+
...units3 ? { units: units3 } : {},
|
|
17039
|
+
goalId: state.goal.id,
|
|
17040
|
+
options: input.options,
|
|
17041
|
+
workItBlocks: [{ blocked: { type: "goal", id: state.goal.id }, action: "complete", reason: "The agent cannot go on without this approval." }]
|
|
17042
|
+
}]
|
|
16544
17043
|
});
|
|
16545
|
-
if ("
|
|
17044
|
+
if ("sent" in sent) return null;
|
|
16546
17045
|
const entryId = sent.requestEntryIds[0];
|
|
16547
17046
|
if (!entryId) return null;
|
|
16548
17047
|
(state.seen ??= /* @__PURE__ */ new Set()).add(entryId);
|
|
@@ -16560,57 +17059,54 @@ async function askPermission(event, state, rail) {
|
|
|
16560
17059
|
]
|
|
16561
17060
|
}));
|
|
16562
17061
|
}
|
|
16563
|
-
async function finish(
|
|
17062
|
+
async function finish(state, rail, opts) {
|
|
16564
17063
|
const goal = state.goal;
|
|
16565
17064
|
if (!goal) return;
|
|
16566
|
-
const progress = result?.trim().slice(0, 1e4) || void 0;
|
|
16567
|
-
let revision = goal.revision;
|
|
16568
17065
|
try {
|
|
16569
17066
|
const held = await rail.read(goal.id);
|
|
16570
|
-
revision = held.revision ?? revision;
|
|
16571
17067
|
const owed = state.noted?.some((b) => b.goals.includes(goal.id));
|
|
16572
17068
|
if (held.state === "done" || held.state === "cancelled") {
|
|
16573
17069
|
if (!owed) state.goal = void 0;
|
|
16574
17070
|
return;
|
|
16575
17071
|
}
|
|
16576
17072
|
if (!held.openDecisionNeedIds?.length && !owed) {
|
|
16577
|
-
await rail.
|
|
17073
|
+
await rail.complete(goal.id, held.revision ?? goal.revision);
|
|
16578
17074
|
state.goal = void 0;
|
|
16579
|
-
return;
|
|
16580
17075
|
}
|
|
16581
17076
|
} catch (error) {
|
|
16582
17077
|
opts?.log?.(`\u26A0 the Goal did not close, still holding it: ${error.message}`);
|
|
16583
17078
|
}
|
|
16584
|
-
if (!progress) return;
|
|
16585
|
-
try {
|
|
16586
|
-
await rail.update(goal.id, { revision, changes: { progress }, reason: "The agent's turn ended; its Goal is not closed yet." });
|
|
16587
|
-
} catch (error) {
|
|
16588
|
-
opts?.log?.(`\u26A0 could not keep the turn as progress: ${error.message}`);
|
|
16589
|
-
}
|
|
16590
17079
|
}
|
|
16591
17080
|
async function cannotWork(report, state, rail, opts) {
|
|
16592
17081
|
state.resting = true;
|
|
16593
|
-
const
|
|
17082
|
+
const { main, units: units3 } = cut(report);
|
|
17083
|
+
const say = (goalId) => rail.contact({ updates: [{ message: main, ...units3 ? { units: units3 } : {}, goalIds: [goalId] }] });
|
|
17084
|
+
const own = async () => (await rail.create({ title: "Agent cannot work", outcome: main })).goalId;
|
|
16594
17085
|
try {
|
|
16595
17086
|
if (!state.goal) {
|
|
16596
|
-
const
|
|
16597
|
-
if (
|
|
17087
|
+
const next = await rail.next().catch(() => void 0);
|
|
17088
|
+
if (next?.goalId && next.revision !== void 0) state.goal = { id: next.goalId, revision: next.revision };
|
|
16598
17089
|
}
|
|
16599
17090
|
if (!state.goal) {
|
|
16600
|
-
await say();
|
|
17091
|
+
await say(await own());
|
|
16601
17092
|
return;
|
|
16602
17093
|
}
|
|
16603
17094
|
try {
|
|
16604
17095
|
await say(state.goal.id);
|
|
16605
17096
|
} catch (error) {
|
|
16606
|
-
if (!/
|
|
16607
|
-
opts?.log?.(`\u26A0 the held Goal ${state.goal.id.slice(0, 8)}
|
|
16608
|
-
await say();
|
|
17097
|
+
if (!/goal_not_found/.test(error.message)) throw error;
|
|
17098
|
+
opts?.log?.(`\u26A0 the held Goal ${state.goal.id.slice(0, 8)} is gone (goal_not_found) \u2014 saying it on a Goal of its own`);
|
|
17099
|
+
await say(await own());
|
|
16609
17100
|
}
|
|
16610
17101
|
} catch (error) {
|
|
16611
17102
|
opts?.log?.(`\u26A0 could not say the agent cannot work: ${error.message}`);
|
|
16612
17103
|
}
|
|
16613
17104
|
}
|
|
17105
|
+
async function callForResume(text, state, rail) {
|
|
17106
|
+
const { main, units: units3 } = cut(text);
|
|
17107
|
+
const goalId = state.goal?.id ?? (await rail.create({ title: "Usage limit reset", outcome: main })).goalId;
|
|
17108
|
+
return rail.contact({ updates: [{ message: main, ...units3 ? { units: units3 } : {}, goalIds: [goalId], userExplicitlyRequested: "call" }] });
|
|
17109
|
+
}
|
|
16614
17110
|
function conversationFull(result) {
|
|
16615
17111
|
return /prompt is too long/i.test(result ?? "");
|
|
16616
17112
|
}
|
|
@@ -16637,14 +17133,14 @@ async function afterTurn(event, state, rail, opts) {
|
|
|
16637
17133
|
${event.result?.trim() || "No result reported."}`, state, rail, opts);
|
|
16638
17134
|
return void 0;
|
|
16639
17135
|
}
|
|
16640
|
-
await finish(
|
|
17136
|
+
await finish(state, rail, opts);
|
|
16641
17137
|
state.typed = void 0;
|
|
16642
17138
|
state.resting = true;
|
|
16643
17139
|
return void 0;
|
|
16644
17140
|
}
|
|
16645
17141
|
|
|
16646
17142
|
// src/paigy/conversation.ts
|
|
16647
|
-
var HARNESS_INSTRUCTION = "You are running as an unattended background harness agent; your user reaches you through Paigy and is not at this terminal. Nothing wakes you after your turn ends but the person, so wait on CI or a deploy inside the turn. The person never sees your turn's final message
|
|
17143
|
+
var HARNESS_INSTRUCTION = "You are running as an unattended background harness agent; your user reaches you through Paigy and is not at this terminal. Nothing wakes you after your turn ends but the person, so wait on CI or a deploy inside the turn. The person never sees your turn's final message, and it is not kept: report where the work stands with a contact update on this Goal (one the person does not need yet is kept as its progress). Anything the person needs \u2014 a question, a result, a blocker \u2014 you send through Paigy's contact tool, once; the harness sends nothing on your behalf. What the person says later arrives with the Goal it was said on named in front of it; answer them on that Goal (its id as the contact's goalId).";
|
|
16648
17144
|
var SAFETY_MS = 6e4;
|
|
16649
17145
|
var ASK_BEAT_MS = 5e3;
|
|
16650
17146
|
function beat() {
|
|
@@ -16744,18 +17240,23 @@ async function gather(state, rail, held) {
|
|
|
16744
17240
|
if (!looks.has(goalId)) looks.set(goalId, { via, ...title ? { title } : {}, ...since !== void 0 ? { since } : {} });
|
|
16745
17241
|
};
|
|
16746
17242
|
const waiting = await rail.waiting().catch(() => null);
|
|
16747
|
-
const listed = /* @__PURE__ */ new
|
|
17243
|
+
const listed = /* @__PURE__ */ new Map();
|
|
17244
|
+
const list = (goalId, eventId) => {
|
|
17245
|
+
const ids = listed.get(goalId) ?? [];
|
|
17246
|
+
if (eventId && !ids.includes(eventId)) ids.push(eventId);
|
|
17247
|
+
listed.set(goalId, ids);
|
|
17248
|
+
};
|
|
16748
17249
|
const announced = state.announced ??= /* @__PURE__ */ new Set();
|
|
16749
17250
|
if (waiting) {
|
|
16750
17251
|
for (const id3 of announced) if (!(waiting.assigned ?? []).some((g) => g.goalId === id3)) announced.delete(id3);
|
|
16751
17252
|
}
|
|
16752
17253
|
for (const g of waiting?.assigned ?? []) if (g.goalId !== state.goal.id && !announced.has(g.goalId)) look2(g.goalId, "assigned", g.title);
|
|
16753
17254
|
for (const g of waiting?.review ?? []) {
|
|
16754
|
-
|
|
17255
|
+
list(g.goalId, g.eventId);
|
|
16755
17256
|
look2(g.goalId, "waiting work", g.title);
|
|
16756
17257
|
}
|
|
16757
17258
|
for (const d of waiting?.deliveries ?? []) for (const id3 of d.goalIds) {
|
|
16758
|
-
|
|
17259
|
+
list(id3, d.deliveryId);
|
|
16759
17260
|
look2(id3, "waiting work");
|
|
16760
17261
|
}
|
|
16761
17262
|
const moved = movedBelow(held, state);
|
|
@@ -16780,7 +17281,7 @@ async function gather(state, rail, held) {
|
|
|
16780
17281
|
for (const { goal, since } of moved) if (read3.has(goal.goalId)) state.below.goals.set(goal.goalId, { since, at: goal.updatedAt ?? null });
|
|
16781
17282
|
return { heard, listed };
|
|
16782
17283
|
}
|
|
16783
|
-
var GIVEN = "This Goal was given to you, and nobody has started it. Carry on with what you're doing unless it changes that;
|
|
17284
|
+
var GIVEN = "This Goal was given to you, and nobody has started it. Carry on with what you're doing unless it changes that; your first write to it (a contact update naming it) starts it when you take it up.";
|
|
16784
17285
|
var says = (h) => h.words.length > 0 || h.via === "assigned";
|
|
16785
17286
|
function whatItIs(h) {
|
|
16786
17287
|
const outcome2 = h.brief.outcome?.trim() ?? "";
|
|
@@ -16810,7 +17311,10 @@ async function hear(session, state, rail, held, opts) {
|
|
|
16810
17311
|
if (opts?.tally) opts.tally.announced += goals.length;
|
|
16811
17312
|
}
|
|
16812
17313
|
}
|
|
16813
|
-
for (const h of heard)
|
|
17314
|
+
for (const h of heard) {
|
|
17315
|
+
const events = listed.get(h.goalId);
|
|
17316
|
+
if (events) await acknowledge2(h.goalId, h.brief, events, state, rail, log2);
|
|
17317
|
+
}
|
|
16814
17318
|
}
|
|
16815
17319
|
var NOTED = "New from the person while you work. Carry on unless it changes what you're doing: it comes back to you as your next turn when this one ends.";
|
|
16816
17320
|
var SEEN_MID_TURN = "While you worked, the person said this, and you saw it mid-task. Act on it now, unless you already have.";
|
|
@@ -16856,15 +17360,16 @@ function settle(session, state, inbox, log2) {
|
|
|
16856
17360
|
send2(session, state, [...seen.length ? [SEEN_MID_TURN, ...seen.map((b) => b.words)] : [], ...unseen.map((b) => b.words)].join("\n\n"));
|
|
16857
17361
|
log2?.(`\u25B8 typed the person's words from ${short([...new Set(batches.flatMap((b) => b.goals))])} (noted mid-turn: ${seen.length} seen by the hook, ${unseen.length} not)`);
|
|
16858
17362
|
}
|
|
16859
|
-
async function
|
|
17363
|
+
async function acknowledge2(goalId, brief, events, state, rail, log2) {
|
|
17364
|
+
const ids = [.../* @__PURE__ */ new Set([...brief.reviewEventId ? [brief.reviewEventId] : [], ...events])];
|
|
17365
|
+
if (!ids.length) return;
|
|
16860
17366
|
const acked = state.acked ??= /* @__PURE__ */ new Map();
|
|
16861
|
-
|
|
16862
|
-
acked.
|
|
17367
|
+
const key3 = ids.join(",");
|
|
17368
|
+
if (acked.get(goalId) === key3) return;
|
|
17369
|
+
acked.set(goalId, key3);
|
|
16863
17370
|
try {
|
|
16864
|
-
|
|
16865
|
-
|
|
16866
|
-
acked.set(goalId, done.revision);
|
|
16867
|
-
if (state.goal?.id === goalId && done.revision !== void 0) state.goal.revision = done.revision;
|
|
17371
|
+
const refused = (await rail.ack(ids)).filter((a) => a.refused);
|
|
17372
|
+
if (refused.length) log2?.(`\u26A0 could not acknowledge ${goalId.slice(0, 8)}: ${refused.map((a) => `${a.eventId.slice(0, 8)} ${a.refused}`).join(", ")} \u2014 its words are typed in; it stays listed until acknowledged`);
|
|
16868
17373
|
} catch (error) {
|
|
16869
17374
|
log2?.(`\u26A0 could not acknowledge ${goalId.slice(0, 8)}: ${error.message} \u2014 its words are typed in; it stays listed until acknowledged`);
|
|
16870
17375
|
}
|
|
@@ -16890,13 +17395,13 @@ async function look(session, state, rail, opts) {
|
|
|
16890
17395
|
if (state.resting && state.noted?.length && !state.asks?.size) return settle(session, state, opts?.inbox, opts?.log);
|
|
16891
17396
|
if (!state.goal) {
|
|
16892
17397
|
if (!state.resting) return;
|
|
16893
|
-
let brief2 = await rail.
|
|
17398
|
+
let brief2 = await rail.next();
|
|
16894
17399
|
if (!brief2.goalId || brief2.revision === void 0) {
|
|
16895
17400
|
if (opts?.initialPrompt?.trim()) {
|
|
16896
17401
|
const prompt = opts.initialPrompt.trim();
|
|
16897
17402
|
opts.initialPrompt = void 0;
|
|
16898
17403
|
const admitted = await rail.create({ title: prompt.split(/\s+/).slice(0, 5).join(" ").slice(0, 80), outcome: prompt });
|
|
16899
|
-
brief2 = await rail.
|
|
17404
|
+
brief2 = await rail.next();
|
|
16900
17405
|
if (!brief2.goalId || brief2.revision === void 0) {
|
|
16901
17406
|
state.goal = { id: admitted.goalId, revision: admitted.revision };
|
|
16902
17407
|
send2(session, state, [HARNESS_INSTRUCTION, named(admitted.goalId, void 0, { outcome: prompt }), prompt].join("\n\n"));
|
|
@@ -16918,7 +17423,7 @@ async function look(session, state, rail, opts) {
|
|
|
16918
17423
|
const earlier = left ? `(${left} earlier entries on this Goal are left out or shortened; get_goal with history: true reads them in full.)` : "";
|
|
16919
17424
|
const fullText = [outcome2, earlier, ...said].filter(Boolean).join("\n\n");
|
|
16920
17425
|
send2(session, state, [HARNESS_INSTRUCTION, named(brief2.goalId, void 0, brief2), fullText].filter(Boolean).join("\n\n"));
|
|
16921
|
-
if (ended(brief2.state)) await
|
|
17426
|
+
if (ended(brief2.state)) await acknowledge2(brief2.goalId, brief2, [], state, rail, opts?.log);
|
|
16922
17427
|
return;
|
|
16923
17428
|
}
|
|
16924
17429
|
const brief = await rail.read(state.goal.id);
|
|
@@ -17006,7 +17511,7 @@ function runHarness(opts) {
|
|
|
17006
17511
|
const clock4 = beat();
|
|
17007
17512
|
let session = null;
|
|
17008
17513
|
const speaksAs = opts.session ?? opts.resume ?? sessionId();
|
|
17009
|
-
const instance =
|
|
17514
|
+
const instance = randomUUID4();
|
|
17010
17515
|
const rail = railFor({ token: opts.token, reach: reachAs(speaksAs, instance) });
|
|
17011
17516
|
let inbox;
|
|
17012
17517
|
if (opts.harness === "claude") {
|
|
@@ -17020,7 +17525,7 @@ function runHarness(opts) {
|
|
|
17020
17525
|
function runningAgain() {
|
|
17021
17526
|
state.carriedOn = false;
|
|
17022
17527
|
opts.log("\u25B6 running again after the limit");
|
|
17023
|
-
LIMITS.running(name, (text) =>
|
|
17528
|
+
LIMITS.running(name, (text) => callForResume(text, state, rail), opts.log);
|
|
17024
17529
|
}
|
|
17025
17530
|
async function handle(event) {
|
|
17026
17531
|
if (event.kind === "error") {
|