@paigy/mcp 0.40.37 → 0.40.39
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/{chunk-3ECJXVIL.js → chunk-4IAK4ATB.js} +2 -2
- package/dist/{chunk-YEW373XZ.js → chunk-PMZBYLLJ.js} +1 -1
- package/dist/{chunk-4UCAUMA3.js → chunk-QE33A5XK.js} +498 -505
- package/dist/{chunk-X5ICLVW3.js → chunk-X4UIGUEX.js} +523 -511
- package/dist/{dist-WIKMO2VH.js → dist-DOFR2565.js} +1 -1
- package/dist/enable.js +3 -3
- package/dist/index.js +4 -4
- package/dist/listen.js +2 -2
- package/dist/onboard.js +4 -4
- package/dist/slot.js +1 -1
- package/dist/stalled.js +1 -1
- package/dist/statusline.js +1 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import { homedir } from "os";
|
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
9
9
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
10
|
-
import { z as
|
|
10
|
+
import { z as z11 } from "zod";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import { z as z3 } from "zod";
|
|
13
13
|
import { z as z2 } from "zod";
|
|
@@ -21,7 +21,6 @@ import { z as z7 } from "zod";
|
|
|
21
21
|
import { z as z8 } from "zod";
|
|
22
22
|
import { z as z9 } from "zod";
|
|
23
23
|
import { z as z10 } from "zod";
|
|
24
|
-
import { z as z11 } from "zod";
|
|
25
24
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync as readFileSync2, rmSync, statSync, writeFileSync } from "fs";
|
|
26
25
|
import { homedir as homedir2 } from "os";
|
|
27
26
|
import { join as join2 } from "path";
|
|
@@ -59,6 +58,24 @@ function parentOf(pid) {
|
|
|
59
58
|
return void 0;
|
|
60
59
|
}
|
|
61
60
|
}
|
|
61
|
+
var CODEX_PARENT = /(^|\/)\.codex\/|CodexCLI\.app|(^|[\/\s])codex(\s|$)/;
|
|
62
|
+
var codexParent;
|
|
63
|
+
function underCodex() {
|
|
64
|
+
if (codexParent !== void 0) return codexParent;
|
|
65
|
+
codexParent = false;
|
|
66
|
+
if (process.platform === "win32") return codexParent;
|
|
67
|
+
for (const pid of ancestors()) {
|
|
68
|
+
try {
|
|
69
|
+
const command = execFileSync("ps", ["-o", "command=", "-p", String(pid)], { encoding: "utf8", timeout: 1e3 });
|
|
70
|
+
if (CODEX_PARENT.test(command)) {
|
|
71
|
+
codexParent = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return codexParent;
|
|
78
|
+
}
|
|
62
79
|
var lineage;
|
|
63
80
|
function ancestors() {
|
|
64
81
|
if (lineage) return lineage;
|
|
@@ -116,7 +133,8 @@ function parseSessionRecord(text) {
|
|
|
116
133
|
return void 0;
|
|
117
134
|
}
|
|
118
135
|
function sessionId() {
|
|
119
|
-
return envSession("PAIGY_SESSION_ID") ?? claudeConversation() ?? envSession("CLAUDE_CODE_SESSION_ID") ?? envSession("CODEX_THREAD_ID") ?? envSession("CODEX_SESSION_ID") ??
|
|
136
|
+
return envSession("PAIGY_SESSION_ID") ?? claudeConversation() ?? envSession("CLAUDE_CODE_SESSION_ID") ?? envSession("CODEX_THREAD_ID") ?? envSession("CODEX_SESSION_ID") ?? // A Codex thread that was handed no id is its own agent, not the folder's (`underCodex`).
|
|
137
|
+
(underCodex() ? INSTANCE_ID : WORKSPACE_ID);
|
|
120
138
|
}
|
|
121
139
|
function isNetworkError(e) {
|
|
122
140
|
return e instanceof Error && e.message.startsWith(NETWORK_MSG);
|
|
@@ -1498,16 +1516,16 @@ function parseWhen(said2, tz, nowMs, read22 = "earliest") {
|
|
|
1498
1516
|
let hour = null;
|
|
1499
1517
|
let minute = 0;
|
|
1500
1518
|
const spoken = new RegExp(`\\bat\\s+(${Object.keys(SPOKEN_HOUR).join("|")})\\b`, "i").exec(text);
|
|
1501
|
-
const
|
|
1519
|
+
const clock2 = CLOCK.exec(text);
|
|
1502
1520
|
if (spoken) {
|
|
1503
1521
|
hour = SPOKEN_HOUR[spoken[1].toLowerCase()];
|
|
1504
|
-
} else if (
|
|
1505
|
-
hour = Number(
|
|
1506
|
-
minute =
|
|
1522
|
+
} else if (clock2 && (clock2[3] || /\bat\s/i.test(clock2[0]))) {
|
|
1523
|
+
hour = Number(clock2[1]);
|
|
1524
|
+
minute = clock2[2] ? Number(clock2[2]) : 0;
|
|
1507
1525
|
if (hour > 23 || minute > 59) return null;
|
|
1508
1526
|
}
|
|
1509
1527
|
if (hour !== null) {
|
|
1510
|
-
const mer =
|
|
1528
|
+
const mer = clock2?.[3]?.toLowerCase().replace(/\./g, "") ?? "";
|
|
1511
1529
|
const pmWord = /\b(p\.?m\.?|afternoon|evening|tonight|at night)\b/i.test(text);
|
|
1512
1530
|
const amWord = /\b(a\.?m\.?|morning)\b/i.test(text);
|
|
1513
1531
|
if (mer.startsWith("p") || pmWord && !amWord) {
|
|
@@ -2017,11 +2035,17 @@ var messageBase = {
|
|
|
2017
2035
|
obligationIds: z6.array(id)
|
|
2018
2036
|
};
|
|
2019
2037
|
var BrainMessageSchema = z6.union([
|
|
2020
|
-
|
|
2038
|
+
// A MESSAGE TO THE PERSON KEEPS ITS SENTENCES (turns.md): the talker writes them one by one, and each is
|
|
2039
|
+
// its own unit of speech (`<cycle>:<key>:<n>`). `text` is the same words joined, for every reader of a
|
|
2040
|
+
// message's words. Absent on results written before sentences.
|
|
2041
|
+
z6.object({ ...messageBase, to: z6.object({ kind: z6.literal("user") }).strict(), sentences: z6.array(z6.string().min(1)).min(1).optional() }).strict(),
|
|
2021
2042
|
z6.object({
|
|
2022
2043
|
...messageBase,
|
|
2023
2044
|
to: z6.object({ kind: z6.literal("agent"), agentId: z6.string().regex(/^agent:(?!paigy$)\S+$/) }).strict(),
|
|
2024
|
-
call: z6.object({ callId: id, listen: z6.literal(true) }).strict().optional()
|
|
2045
|
+
call: z6.object({ callId: id, listen: z6.literal(true) }).strict().optional(),
|
|
2046
|
+
// THE PERSON ASKED TO BE CALLED BACK ONCE THIS AGENT'S WORK IS DONE (turns.md, step 0): the agent owes
|
|
2047
|
+
// the call, and its next contact about it rings them (`public.callbacks`).
|
|
2048
|
+
callback: z6.literal(true).optional()
|
|
2025
2049
|
}).strict()
|
|
2026
2050
|
]);
|
|
2027
2051
|
var BrainSearchSchema = z6.object({
|
|
@@ -2076,9 +2100,12 @@ var str2 = z7.string();
|
|
|
2076
2100
|
var strs2 = z7.array(z7.string());
|
|
2077
2101
|
var TalkerReplySchema = z7.object({
|
|
2078
2102
|
/** What to say and send, messages to the person first: spoken in the order written. `to` is "user"
|
|
2079
|
-
* or an agent's handle; `
|
|
2080
|
-
*
|
|
2081
|
-
|
|
2103
|
+
* or an agent's handle; `sentences` is what a message to the person says, one short sentence each
|
|
2104
|
+
* ([] to an agent); `text` is what a message to an agent says ("" to the person); `about` names
|
|
2105
|
+
* handles; `blocks` is the item a question to an agent must be answered before ("" for none);
|
|
2106
|
+
* `callback` is true on a message to the agent doing work the person asked to be called back about
|
|
2107
|
+
* once it is done (false otherwise): that agent then owes the call (turns.md, step 0). */
|
|
2108
|
+
messages: z7.array(z7.object({ key: str2, to: str2, sentences: strs2, text: str2, about: strs2, blocks: str2, callback: z7.boolean() }).strict()),
|
|
2082
2109
|
/** What the call does next: listen, hold (the person asked for a moment) or end (they asked to). */
|
|
2083
2110
|
then: z7.enum(["listen", "hold", "end"]),
|
|
2084
2111
|
/** The person's replies to items (replies, owner 2026-10-07): the item's handle, the chosen option
|
|
@@ -2226,56 +2253,20 @@ var LIVE_MS = 3 * 6e4;
|
|
|
2226
2253
|
var WORKING_MS = 60 * 6e4;
|
|
2227
2254
|
var WORKING_TIER_MS = 2 * 60 * 6e4;
|
|
2228
2255
|
var RECENT_TIER_MS = 24 * 60 * 6e4;
|
|
2229
|
-
var
|
|
2230
|
-
z11.string(),
|
|
2231
|
-
z11.
|
|
2232
|
-
z11.boolean(),
|
|
2233
|
-
z11.null(),
|
|
2234
|
-
z11.array(json),
|
|
2235
|
-
z11.record(json)
|
|
2236
|
-
]));
|
|
2237
|
-
var clock = z11.number().int().nonnegative().safe();
|
|
2238
|
-
var HeardEventSchema = z11.object({
|
|
2239
|
-
eventId: z11.string().uuid(),
|
|
2240
|
-
source: z11.enum(["phone", "bot"]),
|
|
2241
|
-
sequence: clock,
|
|
2242
|
-
kind: z11.string().min(1),
|
|
2243
|
-
phoneAtMs: clock.optional(),
|
|
2244
|
-
botAtMs: clock.optional(),
|
|
2245
|
-
payload: z11.record(json)
|
|
2246
|
-
}).strict();
|
|
2247
|
-
var HeardCursorSchema = HeardEventSchema.pick({ source: true, sequence: true, eventId: true });
|
|
2248
|
-
var HeardPageSchema = z11.object({
|
|
2249
|
-
events: z11.array(HeardEventSchema).max(200),
|
|
2250
|
-
next: HeardCursorSchema.nullable()
|
|
2251
|
-
}).strict();
|
|
2252
|
-
var MAX_EVENTS = 50;
|
|
2253
|
-
var MAX_BYTES = 8e4;
|
|
2254
|
-
var batchBytes = (events) => {
|
|
2255
|
-
let bytes = 0;
|
|
2256
|
-
for (const char of JSON.stringify({ events })) {
|
|
2257
|
-
const point = char.codePointAt(0);
|
|
2258
|
-
bytes += point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
|
|
2259
|
-
}
|
|
2260
|
-
return bytes;
|
|
2261
|
-
};
|
|
2262
|
-
var HeardBatchSchema = z11.object({
|
|
2263
|
-
events: z11.array(HeardEventSchema).min(1).max(MAX_EVENTS)
|
|
2264
|
-
}).strict().refine((value) => batchBytes(value.events) <= MAX_BYTES, "heard_batch_too_large");
|
|
2265
|
-
var ContextSchema = z12.object({
|
|
2266
|
-
title: z12.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
2267
|
-
description: z12.array(z12.string().min(1)).describe(
|
|
2256
|
+
var ContextSchema = z11.object({
|
|
2257
|
+
title: z11.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
2258
|
+
description: z11.array(z11.string().min(1)).describe(
|
|
2268
2259
|
"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."
|
|
2269
2260
|
),
|
|
2270
2261
|
/** THE ASK'S UNITS (3c): its context as titled units, in order. The app lists the titles and opens
|
|
2271
2262
|
* a body on a tap. Absent on an ask sent without units, which renders as before. */
|
|
2272
|
-
units:
|
|
2263
|
+
units: z11.array(z11.object({ title: z11.string(), body: z11.string() })).optional()
|
|
2273
2264
|
});
|
|
2274
|
-
var ParticipantSchema =
|
|
2275
|
-
kind:
|
|
2276
|
-
id:
|
|
2265
|
+
var ParticipantSchema = z11.object({
|
|
2266
|
+
kind: z11.enum(["human", "agent"]),
|
|
2267
|
+
id: z11.string()
|
|
2277
2268
|
});
|
|
2278
|
-
var TransformSchema =
|
|
2269
|
+
var TransformSchema = z11.enum([
|
|
2279
2270
|
"structure",
|
|
2280
2271
|
// shape intent into an answer contract; pick channel/urgency — broker `ask`, `select` shapes, `points`
|
|
2281
2272
|
"request_more",
|
|
@@ -2298,13 +2289,13 @@ var PAIGY_SELF = { kind: "agent", id: "paigy" };
|
|
|
2298
2289
|
function isPaigy(ref) {
|
|
2299
2290
|
return ref === participantRef(PAIGY_SELF);
|
|
2300
2291
|
}
|
|
2301
|
-
var VisualSchema =
|
|
2302
|
-
url:
|
|
2303
|
-
label:
|
|
2292
|
+
var VisualSchema = z11.object({
|
|
2293
|
+
url: z11.string().url(),
|
|
2294
|
+
label: z11.string().optional()
|
|
2304
2295
|
});
|
|
2305
|
-
var NotifyLevelSchema =
|
|
2306
|
-
var SelectShapeSchema =
|
|
2307
|
-
var ReceiptEventSchema =
|
|
2296
|
+
var NotifyLevelSchema = z11.enum(["inbox", "push", "banner", "call"]);
|
|
2297
|
+
var SelectShapeSchema = z11.enum(["one", "many", "rank", "confirm", "text"]);
|
|
2298
|
+
var ReceiptEventSchema = z11.enum([
|
|
2308
2299
|
"delivered",
|
|
2309
2300
|
// the bundle reached the recipient at some level
|
|
2310
2301
|
"seen",
|
|
@@ -2334,47 +2325,47 @@ var ReceiptEventSchema = z12.enum([
|
|
|
2334
2325
|
// be rewound by a writer that forgot to advance it.
|
|
2335
2326
|
"restarted"
|
|
2336
2327
|
]);
|
|
2337
|
-
var AttentionSchema =
|
|
2328
|
+
var AttentionSchema = z11.object({
|
|
2338
2329
|
urgency: NotifyLevelSchema,
|
|
2339
2330
|
/** The required answer shape, or null for a plain notify that asks nothing back. */
|
|
2340
2331
|
select: SelectShapeSchema.nullable(),
|
|
2341
2332
|
/** Coverage contract (#396) — points the answer must address; null = none declared. */
|
|
2342
|
-
points:
|
|
2333
|
+
points: z11.array(z11.string()).nullable(),
|
|
2343
2334
|
/** Whether the ask blocks the sender — what lets arbitration escalate it on silence. */
|
|
2344
|
-
blocking:
|
|
2335
|
+
blocking: z11.boolean(),
|
|
2345
2336
|
/** Reserved (docs/model/model.md lists it): a response deadline. No row column yet — a later Phase 2
|
|
2346
2337
|
* slice wires it; optional so today's rows/callers project cleanly. */
|
|
2347
|
-
deadline:
|
|
2338
|
+
deadline: z11.string().datetime().nullable().optional()
|
|
2348
2339
|
});
|
|
2349
|
-
var NotifyRequestFields =
|
|
2340
|
+
var NotifyRequestFields = z11.object({
|
|
2350
2341
|
/** Plaintext message content. Present on the plaintext path (today's shape);
|
|
2351
2342
|
* ABSENT on the E2EE path, where the sealed `envelope` below carries it. The
|
|
2352
2343
|
* superRefine at the bottom enforces exactly one of the two. */
|
|
2353
2344
|
context: ContextSchema.optional(),
|
|
2354
|
-
options:
|
|
2345
|
+
options: z11.array(OptionSchema.omit({ id: true }).extend({ label: z11.string().trim().min(1).max(1e3) }).strict()).min(OPTIONS_MIN).max(OPTIONS_MAX).optional().describe(
|
|
2355
2346
|
"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)."
|
|
2356
2347
|
),
|
|
2357
|
-
points:
|
|
2348
|
+
points: z11.array(z11.string().min(1)).optional().describe(
|
|
2358
2349
|
"The distinct things you need answered, each a short phrase \u2014 on a call the broker keeps the conversation going until each is addressed, and the reply reports which were covered, so a half-answer is never silently returned as final. Omit for single-part asks."
|
|
2359
2350
|
),
|
|
2360
|
-
visuals:
|
|
2351
|
+
visuals: z11.array(VisualSchema).optional().describe(
|
|
2361
2352
|
"Images attached to the message itself \u2014 context for the whole question (a screenshot, a chart). For a preview on one selectable choice, use that option's `html`/`image` instead."
|
|
2362
2353
|
),
|
|
2363
2354
|
/** Git repo the agent is working in ("owner/name"). Local MCP fills this from the checkout — omit unless overriding. */
|
|
2364
|
-
repo:
|
|
2355
|
+
repo: z11.string().optional(),
|
|
2365
2356
|
/** Git branch the agent is on. Local MCP fills this from the checkout — omit unless overriding. */
|
|
2366
|
-
branch:
|
|
2357
|
+
branch: z11.string().optional(),
|
|
2367
2358
|
/** Continue an existing conversation — the id of any notification in it (its root
|
|
2368
2359
|
* is the conversation's identity). Omitted = start a new conversation. Renamed
|
|
2369
2360
|
* from `parentId` (2026-08-03): one linkage system, the parent; the API edge
|
|
2370
2361
|
* still accepts the old name from older clients. */
|
|
2371
|
-
parentId:
|
|
2362
|
+
parentId: z11.string().uuid().optional(),
|
|
2372
2363
|
/** The durable outcome this contact advances. Optional during the notification-to-Work
|
|
2373
2364
|
* migration; when present, a blocking ask creates a DecisionNeed for this Work. */
|
|
2374
|
-
workId:
|
|
2365
|
+
workId: z11.string().uuid().optional(),
|
|
2375
2366
|
/** Target Goal scope. During staged migration this is accepted by the shared contract but
|
|
2376
2367
|
* target delivery activation remains model-gated; workId and goalId are mutually exclusive. */
|
|
2377
|
-
goalId:
|
|
2368
|
+
goalId: z11.string().uuid().optional(),
|
|
2378
2369
|
urgency: NotifyLevelSchema.default("inbox").describe(
|
|
2379
2370
|
"The level you're requesting \u2014 the user's account permissions + session mode can lower it. 'inbox' (default) = sits silently in the inbox for the user to get to. 'push' = a quiet passive push (lands in Notification Center, no sound) \u2014 a gentle heads-up. 'banner' = a time-sensitive banner/lock-screen push with sound (a 'paige') they tap to open \u2014 use when you need them soon-ish but it's not worth ringing them. 'call' = rings the user's phone now (a CallKit voice call) \u2014 use only when you genuinely need them in the moment (blocked and waiting, time-sensitive). context.title is what they see on the banner/ring, so make it specific."
|
|
2380
2371
|
),
|
|
@@ -2382,7 +2373,7 @@ var NotifyRequestFields = z12.object({
|
|
|
2382
2373
|
* visible and marks it needs_input. Renamed from the old `parentId` (2026-08-03)
|
|
2383
2374
|
* when `parentId` became the conversation handle: `parentId` says WHERE, this
|
|
2384
2375
|
* says HOW. */
|
|
2385
|
-
clarifies:
|
|
2376
|
+
clarifies: z11.string().optional(),
|
|
2386
2377
|
select: SelectShapeSchema.optional().describe(
|
|
2387
2378
|
"How the user answers \u2014 required on the fully-shaped form, pick the shape that fits the question: 'one' = pick one option, 'many' = pick several, 'rank' = pick & order (each needs `options`); 'confirm' = yes/no or approve/deny; 'text' = free-form reply only (status updates, open questions). 'confirm' and 'text' take no options. Omit only when sending the simplified `ask` form \u2014 the broker picks the shape."
|
|
2388
2379
|
),
|
|
@@ -2396,20 +2387,20 @@ var NotifyRequestFields = z12.object({
|
|
|
2396
2387
|
// (docs/brain/broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
|
|
2397
2388
|
// Owner, 2026-07-28: "our actual limitation on how long something is to the user should
|
|
2398
2389
|
// come from the broker splitting and summarizing." The cap that remains is a size guard.
|
|
2399
|
-
ask:
|
|
2390
|
+
ask: z11.string().min(1).max(1e4).optional().describe(
|
|
2400
2391
|
'SIMPLIFIED FORM \u2014 state in plain prose what you need to learn from the user and why it matters now (e.g. "I need to know whether to deploy the auth fix \u2014 tests are green, staging verified"). Write as much as the situation needs (up to 10k characters) \u2014 Paigy breaks it into topics and reads it back a few sentences at a time; do NOT pre-summarize it into one line. Paigy derives the title, answer shape, options, and delivery channel for you. Mutually exclusive with context/select/options \u2014 send one form or the other.'
|
|
2401
2392
|
),
|
|
2402
|
-
needs:
|
|
2393
|
+
needs: z11.array(z11.string().min(1)).optional().describe(
|
|
2403
2394
|
"With `ask` only: the distinct things you need answered when the ask is multi-part \u2014 becomes the coverage contract (`points`), so a half-answer is never silently final."
|
|
2404
2395
|
),
|
|
2405
|
-
urgencyHint:
|
|
2396
|
+
urgencyHint: z11.enum(["whenever", "soon", "now"]).optional().describe(
|
|
2406
2397
|
"With `ask` only: how urgently you need the answer \u2014 'whenever' (inbox), 'soon' (worth a heads-up), 'now' (you're blocked this minute). A hint, not a command: the user's settings still have the final word."
|
|
2407
2398
|
),
|
|
2408
2399
|
/** #575: the ONE self-report that replaces urgencyHint + blocking — what happens
|
|
2409
2400
|
* to the agent's work while it waits. Normalized server-side into those two
|
|
2410
2401
|
* fields (normalizeWaiting) so everything downstream is untouched; explicit
|
|
2411
2402
|
* urgencyHint/blocking win when both are sent. */
|
|
2412
|
-
waiting:
|
|
2403
|
+
waiting: z11.enum(["none", "soft", "hard"]).optional().describe(
|
|
2413
2404
|
"With `ask`: what happens to your work while you wait. 'none' = you're just informing the user. 'soft' = you'd like an answer but can keep working. 'hard' = you are stopped until they answer (reaches them urgently and escalates to a real phone call if unanswered). Replaces urgencyHint + blocking \u2014 send this one field."
|
|
2414
2405
|
),
|
|
2415
2406
|
/** Δ9b (#895): HOLD this claim so the sender can correct the plan before anyone is
|
|
@@ -2417,98 +2408,98 @@ var NotifyRequestFields = z12.object({
|
|
|
2417
2408
|
* holding by default would charge every quiet claim that minute before any agent could
|
|
2418
2409
|
* correct anything. Ignored for `waiting: 'hard'`: a blocking ask rings on what we have,
|
|
2419
2410
|
* and the enrichment can still land mid-call (#781 re-plans the unspoken tail). */
|
|
2420
|
-
confirm:
|
|
2411
|
+
confirm: z11.boolean().optional().describe(
|
|
2421
2412
|
"Hold this one so you can correct the plan before the user is interrupted. The response comes back with `held: true` and the plan; POST the confirm route to release it (with options/visuals/urgency corrections, or nothing at all). If you never do, it is announced anyway a couple of minutes later. Ignored when waiting is 'hard'."
|
|
2422
2413
|
),
|
|
2423
2414
|
/** #575: a RELAY of the user's explicitly stated preference, never the agent's
|
|
2424
2415
|
* choice. Outranks waiting in both directions: 'call' rings even for a
|
|
2425
2416
|
* waiting:'none' "call me when it's done"; 'message' never rings even for
|
|
2426
2417
|
* waiting:'hard'. */
|
|
2427
|
-
channel:
|
|
2418
|
+
channel: z11.enum(["call", "message"]).optional().describe(
|
|
2428
2419
|
"Only if the user explicitly said how to reach them \u2014 'call me' \u2192 'call', 'just message/text me' \u2192 'message'. Omit otherwise; Paigy picks."
|
|
2429
2420
|
),
|
|
2430
|
-
confirmStyle:
|
|
2421
|
+
confirmStyle: z11.enum(["yesno", "approve"]).default("yesno").describe(
|
|
2431
2422
|
"Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
|
|
2432
2423
|
),
|
|
2433
|
-
blocking:
|
|
2424
|
+
blocking: z11.boolean().default(false).describe(
|
|
2434
2425
|
"Set true when real downstream work is stuck behind this specific decision \u2014 you can't make meaningful progress until it's answered. This is the real signal for how urgently the user should be reached; it's what the premier use case (an agent that stays unblocked instead of going idle) depends on. Independent of `urgency`: a `banner`-level question can still be `blocking` (something IS stuck, just not time-critical enough to ring for immediately) \u2014 if it goes unanswered a while, Paigy escalates it to a real call using this flag rather than guessing from how many other things happen to be pending. Leave false for anything you could work around, defer, or where other useful work exists meanwhile."
|
|
2435
2426
|
)
|
|
2436
2427
|
});
|
|
2437
2428
|
var NotifyRequestSchema = NotifyRequestFields.superRefine((r, ctx) => {
|
|
2438
|
-
if (r.workId && r.goalId) ctx.addIssue({ code:
|
|
2429
|
+
if (r.workId && r.goalId) ctx.addIssue({ code: z11.ZodIssueCode.custom, path: ["goalId"], message: "pass goalId or workId, not both" });
|
|
2439
2430
|
if (r.ask !== void 0) {
|
|
2440
2431
|
for (const f of ["context", "select", "points"]) {
|
|
2441
2432
|
if (r[f] !== void 0)
|
|
2442
|
-
ctx.addIssue({ code:
|
|
2433
|
+
ctx.addIssue({ code: z11.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives the answer shape from your prose. Drop ${f} and say it in \`ask\` instead ("should I\u2026" for approve/deny, "which of these\u2026" for a pick), passing \`options\` when you're offering concrete alternatives.` });
|
|
2443
2434
|
}
|
|
2444
2435
|
return;
|
|
2445
2436
|
}
|
|
2446
2437
|
if (r.needs !== void 0 || r.urgencyHint !== void 0)
|
|
2447
|
-
ctx.addIssue({ code:
|
|
2438
|
+
ctx.addIssue({ code: z11.ZodIssueCode.custom, path: ["needs"], message: "needs/urgencyHint belong to the simplified `ask` form \u2014 with a shaped request use points/urgency" });
|
|
2448
2439
|
if (!r.context)
|
|
2449
|
-
ctx.addIssue({ code:
|
|
2440
|
+
ctx.addIssue({ code: z11.ZodIssueCode.custom, path: ["context"], message: "context is required (plaintext path)" });
|
|
2450
2441
|
if (!r.select)
|
|
2451
|
-
ctx.addIssue({ code:
|
|
2442
|
+
ctx.addIssue({ code: z11.ZodIssueCode.custom, path: ["select"], message: "select is required on the shaped form" });
|
|
2452
2443
|
const needsOptions = r.select === "one" || r.select === "many" || r.select === "rank";
|
|
2453
2444
|
if (needsOptions && !r.options?.length)
|
|
2454
|
-
ctx.addIssue({ code:
|
|
2445
|
+
ctx.addIssue({ code: z11.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' requires options` });
|
|
2455
2446
|
if (!needsOptions && r.options?.length)
|
|
2456
|
-
ctx.addIssue({ code:
|
|
2447
|
+
ctx.addIssue({ code: z11.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' takes no options` });
|
|
2457
2448
|
});
|
|
2458
|
-
var NotifyStatusSchema =
|
|
2459
|
-
var AgentStateSchema =
|
|
2460
|
-
var TurnSchema =
|
|
2461
|
-
prompt:
|
|
2462
|
-
reply:
|
|
2449
|
+
var NotifyStatusSchema = z11.enum(["pending", "answered", "ignored"]);
|
|
2450
|
+
var AgentStateSchema = z11.enum(["idle", "in_progress", "completed", "needs_input"]);
|
|
2451
|
+
var TurnSchema = z11.object({
|
|
2452
|
+
prompt: z11.string(),
|
|
2453
|
+
reply: z11.string()
|
|
2463
2454
|
});
|
|
2464
|
-
var UserAnswerSchema =
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2455
|
+
var UserAnswerSchema = z11.discriminatedUnion("kind", [
|
|
2456
|
+
z11.object({ kind: z11.literal("option"), optionId: z11.string(), label: z11.string().optional() }),
|
|
2457
|
+
z11.object({ kind: z11.literal("text"), text: z11.string() }),
|
|
2458
|
+
z11.object({ kind: z11.literal("ignored") }),
|
|
2459
|
+
z11.object({ kind: z11.literal("multi"), optionIds: z11.array(z11.string()), labels: z11.array(z11.string()).optional() }),
|
|
2460
|
+
z11.object({ kind: z11.literal("ranked"), optionIds: z11.array(z11.string()), labels: z11.array(z11.string()).optional() }),
|
|
2461
|
+
z11.object({ kind: z11.literal("clarify"), chunks: z11.array(z11.string()).min(1) }),
|
|
2462
|
+
z11.object({ kind: z11.literal("confirm"), approved: z11.boolean() }),
|
|
2463
|
+
z11.object({ kind: z11.literal("turns"), turns: z11.array(TurnSchema).min(1) }),
|
|
2473
2464
|
/** An auto-answer derived from the user's PAST decisions (docs/brain/broker/precedent-design.md §2):
|
|
2474
2465
|
* delivered through the same settle/await path as a human answer, carrying the judge's
|
|
2475
2466
|
* derivation and the precedent ids it grew from. Always paired with a visible trail
|
|
2476
2467
|
* card the user can reply to — the broker never overrides the user. */
|
|
2477
|
-
|
|
2468
|
+
z11.object({ kind: z11.literal("precedent"), answer: z11.string(), derivation: z11.string(), sources: z11.array(z11.string()).min(1) })
|
|
2478
2469
|
]);
|
|
2479
|
-
var IntentSchema =
|
|
2470
|
+
var IntentSchema = z11.object({
|
|
2480
2471
|
// The full vocabulary the bot's mapper emits (mapper.INTENT_KINDS) — the schema lagged
|
|
2481
2472
|
// it by two ("detail", "feedback"), and because the settle handler parsed the array
|
|
2482
2473
|
// all-or-nothing, ONE feedback act silently dropped EVERY intent on the call,
|
|
2483
2474
|
// questions included. Found auditing five calls' stored feedback, 2026-08-01.
|
|
2484
|
-
kind:
|
|
2485
|
-
detail:
|
|
2475
|
+
kind: z11.enum(["defer", "delegate", "channel", "question", "detail", "feedback", "command", "control"]),
|
|
2476
|
+
detail: z11.string(),
|
|
2486
2477
|
/** Defer only: seconds until the callback the caller asked for, when something upstream
|
|
2487
2478
|
* already read the time. Nothing sets it today (#397 documented an MCP parser that was
|
|
2488
2479
|
* never written) — the API reads the defer's `detail` itself with `notes/when.ts`
|
|
2489
2480
|
* (`parseDelay`, #1292), and a value here simply wins over that reading. */
|
|
2490
|
-
dueInSeconds:
|
|
2481
|
+
dueInSeconds: z11.number().int().positive().optional(),
|
|
2491
2482
|
/** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
|
|
2492
2483
|
* already read the utterance, so `signals.kind` stops defaulting to
|
|
2493
2484
|
* 'other' on every row. A table that records that something was wrong and nothing
|
|
2494
2485
|
* about what cannot answer "is the bot looping less this week?". */
|
|
2495
|
-
fault:
|
|
2486
|
+
fault: z11.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
|
|
2496
2487
|
});
|
|
2497
|
-
var RideAlongSchema =
|
|
2488
|
+
var RideAlongSchema = z11.object({
|
|
2498
2489
|
/** The note this came from — assign/clarify/close it through /api/notes/:id. */
|
|
2499
|
-
noteId:
|
|
2490
|
+
noteId: z11.string(),
|
|
2500
2491
|
/** What to do, in the owner's own words (the note's headline). Never model-rewritten. */
|
|
2501
|
-
text:
|
|
2492
|
+
text: z11.string(),
|
|
2502
2493
|
/** The thread to report back on, when the note was dispatched over the request rail. */
|
|
2503
|
-
parentId:
|
|
2494
|
+
parentId: z11.string().nullable()
|
|
2504
2495
|
});
|
|
2505
|
-
var AwaitItemSchema =
|
|
2506
|
-
|
|
2507
|
-
type:
|
|
2508
|
-
parentId:
|
|
2509
|
-
notificationId:
|
|
2510
|
-
workId:
|
|
2511
|
-
decisionId:
|
|
2496
|
+
var AwaitItemSchema = z11.discriminatedUnion("type", [
|
|
2497
|
+
z11.object({
|
|
2498
|
+
type: z11.literal("reply"),
|
|
2499
|
+
parentId: z11.string(),
|
|
2500
|
+
notificationId: z11.string(),
|
|
2501
|
+
workId: z11.string().uuid().optional(),
|
|
2502
|
+
decisionId: z11.string().uuid().optional(),
|
|
2512
2503
|
answer: UserAnswerSchema,
|
|
2513
2504
|
/** WHAT THE AGENT CANNOT KNOW FROM THE FIELDS BESIDE IT (owner, 2026-09-04, issue
|
|
2514
2505
|
* #1537). One line, built from the record: the ask and the caller's reply VERBATIM,
|
|
@@ -2518,107 +2509,111 @@ var AwaitItemSchema = z12.discriminatedUnion("type", [
|
|
|
2518
2509
|
* "call me back after you merge" in their own words decides for itself what to do,
|
|
2519
2510
|
* and now knows exactly which call to make. Absent when either half is missing —
|
|
2520
2511
|
* a sentence with a hole in it is worse than no sentence. */
|
|
2521
|
-
note:
|
|
2512
|
+
note: z11.string().optional(),
|
|
2522
2513
|
/** The call record rendered for THIS agent (`docs/brain/voice/record-design.md`): the words the
|
|
2523
2514
|
* shaped answer was mapped from, filtered to its own claims. There is no second list
|
|
2524
2515
|
* of labels beside it — the acts went 2026-09-04 and `intents` went with them (owner,
|
|
2525
2516
|
* 2026-09-04): the agent reads the sentence and decides. */
|
|
2526
|
-
transcript:
|
|
2517
|
+
transcript: z11.string().optional(),
|
|
2527
2518
|
/** Coverage report (#396), when the ask declared `points`: which of them this
|
|
2528
2519
|
* answer addressed. Missing points = re-ask or proceed knowingly partial. */
|
|
2529
|
-
covered:
|
|
2520
|
+
covered: z11.array(z11.string()).optional(),
|
|
2530
2521
|
/** Ride-alongs (RideAlongSchema) — pending work for you, attached to the moment you
|
|
2531
2522
|
* became free. Only `reply` and `idle` carry it: those are the two outcomes that
|
|
2532
2523
|
* END a wait. `remind`, `superseded` and `turn` are mid-flight, and handing an
|
|
2533
2524
|
* agent a side-quest while it is still holding the line is how the main thing gets
|
|
2534
2525
|
* dropped. Absent/empty = nothing owed. */
|
|
2535
|
-
also:
|
|
2526
|
+
also: z11.array(RideAlongSchema).optional()
|
|
2536
2527
|
}),
|
|
2537
|
-
|
|
2538
|
-
type:
|
|
2539
|
-
parentId:
|
|
2540
|
-
notificationId:
|
|
2541
|
-
remindAt:
|
|
2528
|
+
z11.object({
|
|
2529
|
+
type: z11.literal("remind"),
|
|
2530
|
+
parentId: z11.string(),
|
|
2531
|
+
notificationId: z11.string(),
|
|
2532
|
+
remindAt: z11.string().datetime({ offset: true }),
|
|
2542
2533
|
/** Seconds until remindAt, server-computed — pass straight to ScheduleWakeup. */
|
|
2543
|
-
remindInSeconds:
|
|
2534
|
+
remindInSeconds: z11.number()
|
|
2544
2535
|
}),
|
|
2545
2536
|
/** The awaited ask was REPLACED by a newer notification on its thread (e.g. a
|
|
2546
2537
|
* post-feedback revision, #633) — the user will never answer this id. Stop
|
|
2547
2538
|
* awaiting it; the live ask is the thread's newest turn (await that one, or
|
|
2548
2539
|
* re-orient via contact({})). */
|
|
2549
|
-
|
|
2550
|
-
type:
|
|
2551
|
-
parentId:
|
|
2552
|
-
notificationId:
|
|
2540
|
+
z11.object({
|
|
2541
|
+
type: z11.literal("superseded"),
|
|
2542
|
+
parentId: z11.string(),
|
|
2543
|
+
notificationId: z11.string()
|
|
2553
2544
|
}),
|
|
2554
2545
|
/** A LIVE call's turn, streamed as it lands (#783). PROVISIONAL: the user can still
|
|
2555
2546
|
* revise any of these until the final reply arrives — partial = intelligence,
|
|
2556
2547
|
* settled = authorization. Use it to PREPARE (fetch, draft, warm), never to act
|
|
2557
2548
|
* irreversibly. If `acts` carries a question aimed at you and you know the answer,
|
|
2558
2549
|
* contact on the same thread right away — the caller hears it on the same call. */
|
|
2559
|
-
|
|
2560
|
-
type:
|
|
2561
|
-
notificationId:
|
|
2562
|
-
inFlight:
|
|
2563
|
-
turn:
|
|
2564
|
-
idx:
|
|
2565
|
-
prompt:
|
|
2566
|
-
reply:
|
|
2567
|
-
acts:
|
|
2550
|
+
z11.object({
|
|
2551
|
+
type: z11.literal("partial"),
|
|
2552
|
+
notificationId: z11.string(),
|
|
2553
|
+
inFlight: z11.literal(true),
|
|
2554
|
+
turn: z11.object({
|
|
2555
|
+
idx: z11.number(),
|
|
2556
|
+
prompt: z11.string(),
|
|
2557
|
+
reply: z11.string(),
|
|
2558
|
+
acts: z11.array(IntentSchema).nullable().optional()
|
|
2568
2559
|
})
|
|
2569
2560
|
}),
|
|
2570
|
-
|
|
2571
|
-
type:
|
|
2572
|
-
also:
|
|
2561
|
+
z11.object({
|
|
2562
|
+
type: z11.literal("idle"),
|
|
2563
|
+
also: z11.array(RideAlongSchema).optional(),
|
|
2573
2564
|
/** Is a call live for this agent's user right now? The SDK polls the partial stream
|
|
2574
2565
|
* (#783) between idle ticks ONLY while this is not `false` — a partial can only exist
|
|
2575
2566
|
* during a live call, and polling for one on a banner/message was a wasted HTTP call +
|
|
2576
2567
|
* 3 queries on every idle tick of every waiting agent (~80% of all traffic at scale).
|
|
2577
2568
|
* Absent = an older API → the SDK keeps polling, exactly as before. */
|
|
2578
|
-
inFlight:
|
|
2569
|
+
inFlight: z11.boolean().optional()
|
|
2579
2570
|
})
|
|
2580
2571
|
]);
|
|
2581
|
-
var VoiceKeySchema =
|
|
2582
|
-
var AgendaTurnSchema =
|
|
2572
|
+
var VoiceKeySchema = z11.enum(["rachel", "george", "jessica", "brian", "lily"]);
|
|
2573
|
+
var AgendaTurnSchema = z11.object({
|
|
2583
2574
|
/** THE TURN'S IDENTITY (the first-sentence stream, 2026-09-09): the brain call that wrote
|
|
2584
2575
|
* it and its place in that reply — `<brainCallId>:<index>`, with `:p` on the first
|
|
2585
2576
|
* sentence a re-plan publishes ahead of the rest. A turn is spoken once, by this id: the
|
|
2586
2577
|
* completion of a streamed re-plan carries the published sentence again, and the walk
|
|
2587
2578
|
* drops what it already said by identity, never by the API's guess of what was polled.
|
|
2588
2579
|
* Absent on plans nothing streams (a ring plan, a floor). */
|
|
2589
|
-
id:
|
|
2580
|
+
id: z11.string().optional(),
|
|
2590
2581
|
/** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
|
|
2591
2582
|
* the planner declares duplicates instead of asking them twice. */
|
|
2592
|
-
coveredIds:
|
|
2583
|
+
coveredIds: z11.array(z11.string()).optional(),
|
|
2593
2584
|
/** The spoken sentences of the turn, in order. No count: how long a turn is is the brain's call
|
|
2594
2585
|
* (owner, 2026-09-25), and a count here refused whole plans. */
|
|
2595
|
-
info:
|
|
2596
|
-
|
|
2586
|
+
info: z11.array(z11.string().min(1)).default([]),
|
|
2587
|
+
/** THE ID OF EACH SENTENCE, when the plan names them (turns.md): a talker message's sentences are its
|
|
2588
|
+
* `info`, one piece each, and `units[i]` is the id the bot's line receipt carries for `info[i]`
|
|
2589
|
+
* (`<cycle>:<key>:<n>`), so what was heard is known per sentence. Absent on every other turn. */
|
|
2590
|
+
units: z11.array(z11.string().min(1)).optional(),
|
|
2591
|
+
question: z11.string().min(1).nullable(),
|
|
2597
2592
|
/** True on the one turn carrying the agent's own declared question. */
|
|
2598
|
-
asks:
|
|
2593
|
+
asks: z11.boolean().optional(),
|
|
2599
2594
|
/** The claim this turn belongs to (#781) — the RETURN identity: answers route by it.
|
|
2600
2595
|
* Absent on a single-claim plan (the session's own claim) and on shared context turns,
|
|
2601
2596
|
* which route nothing. */
|
|
2602
|
-
claimId:
|
|
2597
|
+
claimId: z11.string().optional(),
|
|
2603
2598
|
/** The claim's voice key (#462) — the OUTBOUND identity, audible who-is-asking. */
|
|
2604
|
-
voice:
|
|
2599
|
+
voice: z11.string().optional(),
|
|
2605
2600
|
/** The claim's AGENT NAME (#838) — the spoken identity. A voice alone doesn't say
|
|
2606
2601
|
* whose request this is: an item that folded in from another agent arrived as a bare
|
|
2607
2602
|
* non-sequitur ("First real production sign-in is yours to make whenever you want.")
|
|
2608
2603
|
* and the owner answered "What?". The bot names the agent before its first turn. */
|
|
2609
|
-
agent:
|
|
2604
|
+
agent: z11.string().optional(),
|
|
2610
2605
|
/** The claim's agent by ID — the pairing's connection id (`notifications.token_id`), the
|
|
2611
2606
|
* same id a face is minted from. A name is not an identity: two pairings may be called
|
|
2612
2607
|
* "Claude", and a name cannot be joined on. The record's entries carry it (`agent_id`)
|
|
2613
2608
|
* so "who said that" survives the call, and it rides PER TURN because a coalesced call
|
|
2614
2609
|
* speaks for several agents — the turn is the only place that knows which. */
|
|
2615
|
-
agentId:
|
|
2610
|
+
agentId: z11.string().optional(),
|
|
2616
2611
|
/** ITS QUESTION IS ON THE TABLE (#3292): a talker line about a question the person owes. The walk tells
|
|
2617
2612
|
* the phone (`asking`, so the agenda goes gold and its options show) without treating the line as a
|
|
2618
2613
|
* question turn — the talker owns re-asking and the waits, so `question` stays null. */
|
|
2619
|
-
onTable:
|
|
2614
|
+
onTable: z11.boolean().optional(),
|
|
2620
2615
|
select: SelectShapeSchema.optional(),
|
|
2621
|
-
options:
|
|
2616
|
+
options: z11.array(OptionSchema.omit({ id: true })).optional(),
|
|
2622
2617
|
/* `pace` STOOD HERE (#826). A turn could carry seconds and the model chose them. The walk
|
|
2623
2618
|
paces itself now — a short beat between the sentences of a turn, the longer one at its end
|
|
2624
2619
|
(owner, 2026-09-30: "remove the bot deciding pace") — and it does that where the words are
|
|
@@ -2627,58 +2622,58 @@ var AgendaTurnSchema = z12.object({
|
|
|
2627
2622
|
/** Whether the walk WAITS for an answer before moving on. Absent = derived as today
|
|
2628
2623
|
* (a question blocks, context flows). blocking:false on a question = ask and move
|
|
2629
2624
|
* on, the claim stays pending; blocking:true on context = hold for a reply. */
|
|
2630
|
-
blocking:
|
|
2625
|
+
blocking: z11.boolean().optional(),
|
|
2631
2626
|
/** SPOKEN ONLY IF THEY SAY NOTHING (owner, 2026-10-01, call 812de935: "you're gonna re-ask, but it
|
|
2632
2627
|
* shouldn't be the same words … more like, hey, are you still there, or are you able to answer, or
|
|
2633
2628
|
* would you need more information"). The walk holds this turn out of its queue; at the queue's end it
|
|
2634
2629
|
* listens for the last word, and only if that listen is silent is this turn said and asked. If they
|
|
2635
2630
|
* speak, it is dropped and their words are taken like any reply. */
|
|
2636
|
-
ifSilent:
|
|
2631
|
+
ifSilent: z11.boolean().optional()
|
|
2637
2632
|
});
|
|
2638
2633
|
var CLAIM_STALE_MS = 30 * 6e4;
|
|
2639
|
-
var CallQuestionSchema =
|
|
2640
|
-
id:
|
|
2641
|
-
entryId:
|
|
2642
|
-
title:
|
|
2643
|
-
state:
|
|
2644
|
-
answer:
|
|
2634
|
+
var CallQuestionSchema = z11.object({
|
|
2635
|
+
id: z11.string(),
|
|
2636
|
+
entryId: z11.string(),
|
|
2637
|
+
title: z11.string(),
|
|
2638
|
+
state: z11.enum(["open", "answered"]),
|
|
2639
|
+
answer: z11.string().nullable(),
|
|
2645
2640
|
/** WHO ASKED IT (owner, 2026-09-23, Goal a345e906: each agenda row wears its agent's face) — the
|
|
2646
2641
|
* request Entry's author, as the same three facts the item's own `tokenId`/`name`/`voice`
|
|
2647
2642
|
* carry for the call's one agent, so the phone draws it with the same seed. Absent when the
|
|
2648
2643
|
* author is not an agent this account holds (unpaired since, or a person). */
|
|
2649
|
-
agent:
|
|
2644
|
+
agent: z11.object({ tokenId: z11.string(), name: z11.string(), voice: VoiceKeySchema.optional() }).optional(),
|
|
2650
2645
|
/** ITS OPTIONS, WHEN THERE IS SOMETHING TO SEE (owner, 2026-09-25: "Yes, add it"): the options
|
|
2651
2646
|
* its need offers, exactly as its own card carries them, present only when one of them has a
|
|
2652
2647
|
* preview (`html` or `image`). The call screen opens them from the agenda row, so a preview is
|
|
2653
2648
|
* never re-sent as a second card to be seen mid-call. Words-only options are `choices`. */
|
|
2654
|
-
options:
|
|
2649
|
+
options: z11.array(OptionSchema).optional(),
|
|
2655
2650
|
/** ITS OPTIONS WHEN THEY ARE WORDS (owner, 2026-10-02, on a call: "whenever there are pre-made
|
|
2656
2651
|
* options … now for a checklist, we should also show them on the screen"; design C, 2026-10-08):
|
|
2657
2652
|
* numbered chips above the call's controls, so a checklist is never only something read aloud. A
|
|
2658
2653
|
* field of its own so `options` keeps meaning "previews": a phone on an older bundle opens the
|
|
2659
2654
|
* preview layout for any `options`, and must see exactly what it did. */
|
|
2660
|
-
choices:
|
|
2655
|
+
choices: z11.array(OptionSchema).optional(),
|
|
2661
2656
|
/** HOW MANY MAY BE PICKED, beside its `choices`: one, any (`many`) or an order (`rank`). */
|
|
2662
|
-
select:
|
|
2657
|
+
select: z11.enum(["one", "many", "rank"]).optional()
|
|
2663
2658
|
});
|
|
2664
|
-
var InboxItemSchema =
|
|
2665
|
-
id:
|
|
2666
|
-
tokenId:
|
|
2659
|
+
var InboxItemSchema = z11.object({
|
|
2660
|
+
id: z11.string(),
|
|
2661
|
+
tokenId: z11.string().optional(),
|
|
2667
2662
|
status: NotifyStatusSchema,
|
|
2668
2663
|
context: ContextSchema,
|
|
2669
|
-
options:
|
|
2664
|
+
options: z11.array(OptionSchema).optional(),
|
|
2670
2665
|
/** The ask's declared coverage points (#396), when the agent sent them. */
|
|
2671
|
-
points:
|
|
2666
|
+
points: z11.array(z11.string()).optional(),
|
|
2672
2667
|
/** Does this claim want an ANSWER, or is it telling you something? Written per row from
|
|
2673
2668
|
* `requestAsks` — the agent's own declaration, not a guess. `false` is what earns a card
|
|
2674
2669
|
* its acknowledge affordance: without it a status update offers a text box and a dismiss,
|
|
2675
2670
|
* and neither of those is "got it" (owner, 2026-08-10). */
|
|
2676
|
-
asks:
|
|
2671
|
+
asks: z11.boolean().optional(),
|
|
2677
2672
|
/** When a live process last pulsed for this row's agent — the liveness input for
|
|
2678
2673
|
* "working requires a pulse" (#928): the list said "Working…" from agent_state alone
|
|
2679
2674
|
* while the party called the same dead claim stalled. Absent = no token/no data,
|
|
2680
2675
|
* which must never CLAIM stalled. */
|
|
2681
|
-
lastSeenAt:
|
|
2676
|
+
lastSeenAt: z11.string().optional(),
|
|
2682
2677
|
/** WHEN THE AGENT LAST SAID ANYTHING ABOUT THIS CLAIM — the newest `agent_state` row in
|
|
2683
2678
|
* the `notification_events` ledger (trigger-written since 20260621010000, so every row a
|
|
2684
2679
|
* user can see has one). The age input for `CLAIM_STALE_MS`, and it has to be this rather
|
|
@@ -2688,7 +2683,7 @@ var InboxItemSchema = z12.object({
|
|
|
2688
2683
|
* work. Reading the row's birth as the claim's age brands that "No update in 8h" the
|
|
2689
2684
|
* instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
|
|
2690
2685
|
* `createdAt`. */
|
|
2691
|
-
agentStateAt:
|
|
2686
|
+
agentStateAt: z11.string().datetime().optional(),
|
|
2692
2687
|
/** THE QUESTIONS A CALL CARRIES — the call screen's agenda spine (docs/clients/app/walk/design.md §11, owner
|
|
2693
2688
|
* 2026-09-22). One per DecisionNeed on the Call, in the Call's order, answered or open (a
|
|
2694
2689
|
* superseded or cancelled need is no longer a question anyone is asked). Present only on a
|
|
@@ -2702,40 +2697,40 @@ var InboxItemSchema = z12.object({
|
|
|
2702
2697
|
* `turn` topic (`asking`, `settled`), because the bot never sees a DecisionNeed id. `title` is
|
|
2703
2698
|
* the card's own concise heading; `answer` the accepted answer in words, null while open. It
|
|
2704
2699
|
* REPLACED `agenda` (turns), which nothing ever filled. */
|
|
2705
|
-
questions:
|
|
2700
|
+
questions: z11.array(CallQuestionSchema).optional(),
|
|
2706
2701
|
/** The same call carrying order, including request Entries with no DecisionNeed as reports.
|
|
2707
2702
|
* Additive: questions stays decision-only for every other surface and older bundles. */
|
|
2708
|
-
callItems:
|
|
2709
|
-
visuals:
|
|
2703
|
+
callItems: z11.array(CallQuestionSchema.extend({ state: z11.enum(["open", "answered", "report"]) })).optional(),
|
|
2704
|
+
visuals: z11.array(VisualSchema).optional(),
|
|
2710
2705
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
2711
2706
|
* agent's suggestion, or a default silly name). */
|
|
2712
|
-
name:
|
|
2707
|
+
name: z11.string(),
|
|
2713
2708
|
/** The pairing's assigned voice (#462); absent = the default voice. */
|
|
2714
2709
|
voice: VoiceKeySchema.optional(),
|
|
2715
|
-
repo:
|
|
2716
|
-
branch:
|
|
2717
|
-
createdAt:
|
|
2718
|
-
snoozedUntil:
|
|
2710
|
+
repo: z11.string().optional(),
|
|
2711
|
+
branch: z11.string().optional(),
|
|
2712
|
+
createdAt: z11.string().datetime(),
|
|
2713
|
+
snoozedUntil: z11.string().datetime().optional(),
|
|
2719
2714
|
agentState: AgentStateSchema.default("idle"),
|
|
2720
2715
|
/** Whose action the item is waiting on: "you" = an agent asked you (the default,
|
|
2721
2716
|
* every agent→user notification); "agent" = you sent a request and it's awaiting the
|
|
2722
2717
|
* agent (held in the inbox until the agent replies on the thread). */
|
|
2723
|
-
turn:
|
|
2718
|
+
turn: z11.enum(["you", "agent"]).default("you"),
|
|
2724
2719
|
/** Hard error reason on an awaiting request (turn="agent") — the wake failed to reach
|
|
2725
2720
|
* the agent (provider-agnostic; set server-side). Absent = no hard error. Drives the inbox
|
|
2726
2721
|
* error badge + Retry. */
|
|
2727
|
-
error:
|
|
2722
|
+
error: z11.string().optional(),
|
|
2728
2723
|
/** WHEN THIS AGENT WORK WENT QUIET (turn="agent"), by the one rule (`coldSince`: three days
|
|
2729
2724
|
* with nothing said), or absent while it is not stalled. The inbox's stalled badge reads
|
|
2730
2725
|
* this and nothing else (2026-09-23: a 3-minute age rule badged every live Goal stalled,
|
|
2731
2726
|
* and "dismiss the stalled ones" cancelled 37 pieces of live work). */
|
|
2732
|
-
cold:
|
|
2733
|
-
clarifies:
|
|
2727
|
+
cold: z11.string().datetime().optional(),
|
|
2728
|
+
clarifies: z11.string().optional(),
|
|
2734
2729
|
/** THIS CARD'S QUESTION IS ON A LIVE CALL (owner, 2026-09-24: "Mark it while the call is
|
|
2735
2730
|
* live"). Present only while an open Call Delivery carries the card's request Entry — read
|
|
2736
2731
|
* off the same open list the card came from, so it clears when the Call does. A card is the
|
|
2737
2732
|
* backup for a call not taken; while the call has it, the call is where it is answered. */
|
|
2738
|
-
onCall:
|
|
2733
|
+
onCall: z11.literal(true).optional(),
|
|
2739
2734
|
/** THE RING, ON THE ITEM (docs/clients/app/walk/design.md §12 §17, #2251): the last ring on this card was
|
|
2740
2735
|
* declined, and what the ladder will do next — read off the cron's own row, never computed
|
|
2741
2736
|
* on the phone. Present only while a `declined` receipt stands on the card's last Call.
|
|
@@ -2745,31 +2740,31 @@ var InboxItemSchema = z12.object({
|
|
|
2745
2740
|
* It replaced `gaveUp` (deleted 2026-09-22): "the ladder spent" was a boolean the projection
|
|
2746
2741
|
* never set, and it is `nextRingAt === null` here — the party's *Missed you* (`party/dress.ts`)
|
|
2747
2742
|
* and the roster's `unreached` read `declinedAt`, and stand while it does. */
|
|
2748
|
-
ring:
|
|
2749
|
-
declinedAt:
|
|
2750
|
-
anchorAt:
|
|
2751
|
-
nextRingAt:
|
|
2752
|
-
step:
|
|
2743
|
+
ring: z11.object({
|
|
2744
|
+
declinedAt: z11.string().datetime(),
|
|
2745
|
+
anchorAt: z11.string().datetime(),
|
|
2746
|
+
nextRingAt: z11.string().datetime().nullable(),
|
|
2747
|
+
step: z11.number().int()
|
|
2753
2748
|
}).optional(),
|
|
2754
2749
|
/** Why this arrived the way it did, read back off the delivery receipt (`notify/why.ts`).
|
|
2755
2750
|
* Absent for anything never delivered through a push, and for older rows written before
|
|
2756
2751
|
* the reason was recorded. Deliberately a debug affordance, shown small (owner,
|
|
2757
2752
|
* 2026-08-07) — its real job is to give "this didn't need a call" something to be
|
|
2758
2753
|
* feedback ABOUT. */
|
|
2759
|
-
why:
|
|
2754
|
+
why: z11.object({
|
|
2760
2755
|
asked: NotifyLevelSchema,
|
|
2761
2756
|
got: NotifyLevelSchema,
|
|
2762
|
-
because:
|
|
2763
|
-
line:
|
|
2757
|
+
because: z11.enum(["unresponsive", "dismissed", "not_permitted", "silent", "coalesced", "agent_capped", "unplanned", "learned_raise"]).optional(),
|
|
2758
|
+
line: z11.string()
|
|
2764
2759
|
}).optional(),
|
|
2765
|
-
select:
|
|
2766
|
-
confirmStyle:
|
|
2760
|
+
select: z11.enum(["one", "many", "rank", "confirm", "text"]).default("one"),
|
|
2761
|
+
confirmStyle: z11.enum(["yesno", "approve"]).default("yesno").describe(
|
|
2767
2762
|
"Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
|
|
2768
2763
|
),
|
|
2769
2764
|
/** Real downstream work is stuck behind this one — set by the agent, independent of
|
|
2770
2765
|
* urgency (see the main README's "premier use case" + docs/delivery/notify/states.md). Drives the
|
|
2771
2766
|
* inbox's blocking badge and the extra confirm step before dismissing it. */
|
|
2772
|
-
blocking:
|
|
2767
|
+
blocking: z11.boolean().default(false),
|
|
2773
2768
|
/** The user's locked-in answer; present only for replied items (GET /api/replied/:id). */
|
|
2774
2769
|
answer: UserAnswerSchema.optional(),
|
|
2775
2770
|
/** THE TARGET FACTS A CARD RENDERS (#1796 point 5, 2026-09-11): the Delivery it is a view of,
|
|
@@ -2777,17 +2772,17 @@ var InboxItemSchema = z12.object({
|
|
|
2777
2772
|
* for a request that asks nothing), whether its content is sealed, and that Goal's state. The
|
|
2778
2773
|
* answer writer (`POST /api/entries`) and the disposition (`close_delivery`) take their ids from
|
|
2779
2774
|
* here. The server projects it (`apps/api/src/inbox/project.ts`); a client never builds it. */
|
|
2780
|
-
communication:
|
|
2781
|
-
deliveryId:
|
|
2782
|
-
kind:
|
|
2783
|
-
entryId:
|
|
2784
|
-
goalIds:
|
|
2785
|
-
decisionNeedId:
|
|
2786
|
-
sealed:
|
|
2787
|
-
goalState:
|
|
2775
|
+
communication: z11.object({
|
|
2776
|
+
deliveryId: z11.string(),
|
|
2777
|
+
kind: z11.enum(["notification", "call"]),
|
|
2778
|
+
entryId: z11.string(),
|
|
2779
|
+
goalIds: z11.array(z11.string()),
|
|
2780
|
+
decisionNeedId: z11.string().optional(),
|
|
2781
|
+
sealed: z11.boolean(),
|
|
2782
|
+
goalState: z11.string().optional(),
|
|
2788
2783
|
/** THAT GOAL'S NAME (#2416) — what Activity's row is headed by, since a row there is one Goal
|
|
2789
2784
|
* and the cards it holds sit behind it. Stamped by the same read as `goalState`. */
|
|
2790
|
-
goalTitle:
|
|
2785
|
+
goalTitle: z11.string().optional()
|
|
2791
2786
|
}).optional(),
|
|
2792
2787
|
/** WHAT THIS CARD IS, IN TWELVE CHARACTERS (#3019) — the hash of every other field on it, stamped
|
|
2793
2788
|
* by the one read that serves the open list (`apps/api/src/inbox/project.ts` `inboxFor`). It is
|
|
@@ -2799,27 +2794,27 @@ var InboxItemSchema = z12.object({
|
|
|
2799
2794
|
* own moves (its Goal's state and name, how cold the work behind it has gone, whether a ring is
|
|
2800
2795
|
* live). Optional, so a fixture, the demo and the archive lens need not spell one, and a card
|
|
2801
2796
|
* with no rev is simply always re-sent. */
|
|
2802
|
-
rev:
|
|
2797
|
+
rev: z11.string().optional()
|
|
2803
2798
|
});
|
|
2804
2799
|
var APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
|
|
2805
|
-
var PushTokenSchema =
|
|
2806
|
-
voipToken:
|
|
2807
|
-
alertToken:
|
|
2808
|
-
fcmToken:
|
|
2809
|
-
platform:
|
|
2800
|
+
var PushTokenSchema = z11.object({
|
|
2801
|
+
voipToken: z11.string().min(1).optional(),
|
|
2802
|
+
alertToken: z11.string().min(1).optional(),
|
|
2803
|
+
fcmToken: z11.string().min(1).optional(),
|
|
2804
|
+
platform: z11.enum(["ios", "android"])
|
|
2810
2805
|
}).superRefine((v, ctx) => {
|
|
2811
2806
|
if (v.platform !== "ios") return;
|
|
2812
2807
|
for (const field of ["voipToken", "alertToken"]) {
|
|
2813
2808
|
const token = v[field];
|
|
2814
2809
|
if (token === void 0 || APNS_TOKEN_RE.test(token)) continue;
|
|
2815
2810
|
ctx.addIssue({
|
|
2816
|
-
code:
|
|
2811
|
+
code: z11.ZodIssueCode.custom,
|
|
2817
2812
|
path: [field],
|
|
2818
2813
|
message: `not an APNs device token (want 64 hex chars, got ${token.length})`
|
|
2819
2814
|
});
|
|
2820
2815
|
}
|
|
2821
2816
|
});
|
|
2822
|
-
var MissedCallSchema =
|
|
2817
|
+
var MissedCallSchema = z11.enum([
|
|
2823
2818
|
"retry_10m",
|
|
2824
2819
|
"retry_30m",
|
|
2825
2820
|
"retry_60m",
|
|
@@ -2829,33 +2824,33 @@ var MissedCallSchema = z12.enum([
|
|
|
2829
2824
|
"inbox",
|
|
2830
2825
|
"dismiss"
|
|
2831
2826
|
]);
|
|
2832
|
-
var
|
|
2833
|
-
var QUIET = ` Nothing rings from ${
|
|
2834
|
-
var BrokerTuningSchema =
|
|
2827
|
+
var clock = (h) => h === 0 ? "midnight" : h === 12 ? "noon" : h < 12 ? `${h} am` : `${h - 12} pm`;
|
|
2828
|
+
var QUIET = ` Nothing rings from ${clock(NIGHT.from)} to ${clock(NIGHT.to)} your time; the count waits for morning.`;
|
|
2829
|
+
var BrokerTuningSchema = z11.object({
|
|
2835
2830
|
/** 'none' = skip the spoken ack after a mapped answer (power users find it slow). */
|
|
2836
|
-
ackVerbosity:
|
|
2831
|
+
ackVerbosity: z11.enum(["normal", "none"]).optional(),
|
|
2837
2832
|
/** How readily the mapper asks its one clarification: 'low' = only when truly
|
|
2838
2833
|
* uninterpretable, 'high' = whenever not fully certain. */
|
|
2839
|
-
clarifyEagerness:
|
|
2834
|
+
clarifyEagerness: z11.enum(["low", "normal", "high"]).optional(),
|
|
2840
2835
|
/** The user's own shorthand: when they say `say`, they mean `mean`. */
|
|
2841
|
-
phrasebook:
|
|
2836
|
+
phrasebook: z11.array(z11.object({ say: z11.string().min(1).max(60), mean: z11.string().min(1).max(120) })).max(24).optional(),
|
|
2842
2837
|
/** The language calls are PLANNED in, when the account has chosen one (#1272). Absent —
|
|
2843
2838
|
* which is every account today — means the agent's own words decide, per ask: a call
|
|
2844
2839
|
* about an English ask opens in English. This is the only thing that overrides that,
|
|
2845
2840
|
* and a live caller who switches language mid-call still outranks it (broker/lang.ts).
|
|
2846
2841
|
* Set per user (no UI yet), like `voiceTuning`. */
|
|
2847
|
-
language:
|
|
2842
|
+
language: z11.enum(["en", "es"]).optional()
|
|
2848
2843
|
});
|
|
2849
|
-
var UserSettingsSchema =
|
|
2850
|
-
permissions:
|
|
2851
|
-
call:
|
|
2852
|
-
banner:
|
|
2853
|
-
push:
|
|
2844
|
+
var UserSettingsSchema = z11.object({
|
|
2845
|
+
permissions: z11.object({
|
|
2846
|
+
call: z11.boolean(),
|
|
2847
|
+
banner: z11.boolean(),
|
|
2848
|
+
push: z11.boolean()
|
|
2854
2849
|
}),
|
|
2855
2850
|
/** LockedIn / Default / DateNight on screen; the stored words are unchanged on purpose —
|
|
2856
2851
|
* they are an enum on a live column across every account, and the rename is a rename of
|
|
2857
2852
|
* what people read (owner, 2026-09-30). */
|
|
2858
|
-
sessionMode:
|
|
2853
|
+
sessionMode: z11.enum(["default", "all_calls", "silent"]),
|
|
2859
2854
|
/** `silentPush` lived here until #2813 and is now GONE, field and column both. It was kept as an
|
|
2860
2855
|
* optional long after DateNight stopped reading it, on the theory that a phone on an older
|
|
2861
2856
|
* bundle PATCHing the whole settings object would be REFUSED for sending a key we had stopped
|
|
@@ -2864,7 +2859,7 @@ var UserSettingsSchema = z12.object({
|
|
|
2864
2859
|
* an old bundle's `silentPush` is accepted and ignored. Worth remembering before keeping the
|
|
2865
2860
|
* next dead field for the same reason. */
|
|
2866
2861
|
/** Opt-in (default false) to using your content to improve Paigy and train models. */
|
|
2867
|
-
improveConsent:
|
|
2862
|
+
improveConsent: z11.boolean(),
|
|
2868
2863
|
missedCall: MissedCallSchema.default("backoff_standard"),
|
|
2869
2864
|
/** Where voice audio is processed. 'hosted' (default) = Paigy's voice services
|
|
2870
2865
|
* (ElevenLabs TTS, faster-whisper STT, the call bot); 'on_device' = the phone
|
|
@@ -2872,17 +2867,17 @@ var UserSettingsSchema = z12.object({
|
|
|
2872
2867
|
* Optional, NOT defaulted: a stale client PATCHing the full settings object
|
|
2873
2868
|
* must not silently reset this privacy choice. Absent = leave unchanged on
|
|
2874
2869
|
* write, 'hosted' on read (see store.ts). */
|
|
2875
|
-
voiceMode:
|
|
2870
|
+
voiceMode: z11.enum(["hosted", "on_device"]).optional(),
|
|
2876
2871
|
/** Talk — after you answer, the next step is read aloud (docs/clients/app/walk/design.md §6). ALWAYS ON until
|
|
2877
2872
|
* turned off (owner, 2026-09-18, #2249): a setting, not a per-walk toggle. Optional, NOT
|
|
2878
2873
|
* defaulted, for the same reason `voiceMode` is: a stale client PATCHing the full settings
|
|
2879
2874
|
* object must not silently turn it back on. Absent = leave unchanged on write, true on
|
|
2880
2875
|
* read (see store.ts). */
|
|
2881
|
-
talk:
|
|
2876
|
+
talk: z11.boolean().optional(),
|
|
2882
2877
|
/** CALL DIAGNOSTICS (owner, 2026-10-01): the call report carries each listen and the bot's own
|
|
2883
2878
|
* load timings. SERVER-SET, no UI — on for every account that existed on 2026-10-01, off for
|
|
2884
2879
|
* newer ones (migration 20261001132859). Read-only here: the settings PATCH never writes it. */
|
|
2885
|
-
callDiagnostics:
|
|
2880
|
+
callDiagnostics: z11.boolean().optional(),
|
|
2886
2881
|
/** Per-user ring budget (#603): calls per rolling day before further calls
|
|
2887
2882
|
* degrade to banner. Absent = the global default (25). A number, never a
|
|
2888
2883
|
* bypass — every account keeps a ceiling. No UI; set per user for testing. */
|
|
@@ -2890,7 +2885,7 @@ var UserSettingsSchema = z12.object({
|
|
|
2890
2885
|
* payload['tuning'] (e.g. { silence_s: 3.5 } — a longer pause window for a
|
|
2891
2886
|
* slower speaker). No API-side semantics; the bot resolves each key with its
|
|
2892
2887
|
* own defaults. Set per user (no UI yet); absent = bot defaults. */
|
|
2893
|
-
voiceTuning:
|
|
2888
|
+
voiceTuning: z11.record(z11.string(), z11.union([z11.number(), z11.string()])).optional(),
|
|
2894
2889
|
/** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
|
|
2895
2890
|
* only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
|
|
2896
2891
|
* an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
|
|
@@ -2899,70 +2894,70 @@ var UserSettingsSchema = z12.object({
|
|
|
2899
2894
|
* that failure reads as the reminder rail being unreliable rather than as a missing
|
|
2900
2895
|
* setting. Absent = a spoken time can't be landed, so the reminder rides the next
|
|
2901
2896
|
* call — honest about what we know. */
|
|
2902
|
-
timezone:
|
|
2897
|
+
timezone: z11.string().min(1).max(64).optional(),
|
|
2903
2898
|
/** Rung-2 broker tuning (#381). Optional and NOT defaulted, same stale-client
|
|
2904
2899
|
* clobber guard as voiceMode: absent = leave unchanged on write. */
|
|
2905
2900
|
broker: BrokerTuningSchema.optional()
|
|
2906
2901
|
});
|
|
2907
|
-
var GoalLatestSchema =
|
|
2908
|
-
from:
|
|
2909
|
-
said:
|
|
2910
|
-
at:
|
|
2911
|
-
entryId:
|
|
2902
|
+
var GoalLatestSchema = z11.object({
|
|
2903
|
+
from: z11.enum(["person", "agent"]),
|
|
2904
|
+
said: z11.string(),
|
|
2905
|
+
at: z11.string(),
|
|
2906
|
+
entryId: z11.string(),
|
|
2912
2907
|
/** ITS SUMMARY IN AT MOST TEN WORDS (owner, 2026-10-08: "the same switch to the more summarized sources
|
|
2913
2908
|
* at the work tab top level screen too"): what the talker or filer wrote on the Entry; absent when
|
|
2914
2909
|
* none was. The Work row reads it before `said`. */
|
|
2915
|
-
summary:
|
|
2910
|
+
summary: z11.string().optional(),
|
|
2916
2911
|
/** AN AGENT'S UPDATE OR QUESTION AS ITS UNITS' TITLES (owner, 2026-10-08: "send the 50 chars from the title
|
|
2917
2912
|
* of each unit"): each at most 50 characters, in order; absent when it has none. */
|
|
2918
|
-
titles:
|
|
2913
|
+
titles: z11.array(z11.string()).optional(),
|
|
2919
2914
|
/** A PROGRESS NOTE'S ONE POINT ("An agent's progress note -> maybe let's cap it at 200 chars"): the
|
|
2920
2915
|
* update's own line, before its units; the row caps it at 200. Absent on anything else. */
|
|
2921
|
-
line:
|
|
2916
|
+
line: z11.string().optional()
|
|
2922
2917
|
});
|
|
2923
|
-
var HistoryWorkSchema =
|
|
2924
|
-
id:
|
|
2925
|
-
title:
|
|
2926
|
-
state:
|
|
2918
|
+
var HistoryWorkSchema = z11.object({
|
|
2919
|
+
id: z11.string(),
|
|
2920
|
+
title: z11.string(),
|
|
2921
|
+
state: z11.enum(["done", "cancelled"]),
|
|
2927
2922
|
/** Who held it (`agent:<tokenId>` or `human:<userId>`). */
|
|
2928
|
-
assignee:
|
|
2923
|
+
assignee: z11.string(),
|
|
2929
2924
|
latest: GoalLatestSchema.nullable().optional()
|
|
2930
2925
|
});
|
|
2931
|
-
var HistoryEntrySchema =
|
|
2932
|
-
|
|
2933
|
-
|
|
2926
|
+
var HistoryEntrySchema = z11.union([
|
|
2927
|
+
z11.object({ at: z11.string(), card: InboxItemSchema }),
|
|
2928
|
+
z11.object({ at: z11.string(), work: HistoryWorkSchema })
|
|
2934
2929
|
]);
|
|
2935
|
-
var HistoryPageSchema =
|
|
2936
|
-
entries:
|
|
2937
|
-
next:
|
|
2930
|
+
var HistoryPageSchema = z11.object({
|
|
2931
|
+
entries: z11.array(HistoryEntrySchema),
|
|
2932
|
+
next: z11.string().nullable()
|
|
2938
2933
|
});
|
|
2939
2934
|
var ACTIVITY_LINES = 2;
|
|
2940
2935
|
var ACTIVITY_LINE_MAX = 80;
|
|
2941
|
-
var AgentActivitySchema =
|
|
2936
|
+
var AgentActivitySchema = z11.object({
|
|
2942
2937
|
/** Oldest first, so the newest line is last — the one that replaces in place. */
|
|
2943
|
-
lines:
|
|
2938
|
+
lines: z11.array(z11.string().max(ACTIVITY_LINE_MAX)).max(ACTIVITY_LINES),
|
|
2944
2939
|
/** When the harness observed this tail. Its own timestamp, not the heartbeat's: a beat
|
|
2945
2940
|
* that carries an UNCHANGED tail must not make a stalled agent look like it just moved. */
|
|
2946
|
-
at:
|
|
2941
|
+
at: z11.string().datetime()
|
|
2947
2942
|
});
|
|
2948
|
-
var ConnectionSummarySchema =
|
|
2943
|
+
var ConnectionSummarySchema = z11.object({
|
|
2949
2944
|
/** The connection = the agent's token id (used to address a request). */
|
|
2950
|
-
id:
|
|
2945
|
+
id: z11.string(),
|
|
2951
2946
|
/** The credential kind: "device" = a paired machine (mint-only — it hosts and mints, it
|
|
2952
2947
|
* never talks); "agent" = an identity that sends. The roster and devices surfaces split
|
|
2953
2948
|
* on this. Optional/absent reads as "agent" (a row predating the kind column). See
|
|
2954
2949
|
* docs/server/tokens/devices-vs-agents-design.md. */
|
|
2955
|
-
kind:
|
|
2950
|
+
kind: z11.enum(["device", "agent"]).optional(),
|
|
2956
2951
|
/** For an agent, the token id of the DEVICE that minted it — so agents group under their
|
|
2957
2952
|
* machine, and revoking a device cascades to them. Null on devices, and on unlinked
|
|
2958
2953
|
* agents (phone-launched, provider-managed, or minted before the link existed). */
|
|
2959
|
-
mintedByDevice:
|
|
2960
|
-
device:
|
|
2954
|
+
mintedByDevice: z11.string().nullable().optional(),
|
|
2955
|
+
device: z11.string().nullable(),
|
|
2961
2956
|
/** The agent's display name (the single pairing name). */
|
|
2962
|
-
name:
|
|
2957
|
+
name: z11.string(),
|
|
2963
2958
|
/** For a managed connection, the provider key (e.g. "cma") that agentOrigin maps to a
|
|
2964
2959
|
* label; null for a local connection. Sourced from the token's provider, not the name. */
|
|
2965
|
-
provider:
|
|
2960
|
+
provider: z11.string().nullable(),
|
|
2966
2961
|
/** The pairing's assigned voice (#462); null = the default voice. */
|
|
2967
2962
|
voice: VoiceKeySchema.nullable(),
|
|
2968
2963
|
/** The LOUDEST this agent may ever reach you — a ceiling on `NOTIFY_LADDER`, set by the
|
|
@@ -2973,34 +2968,34 @@ var ConnectionSummarySchema = z12.object({
|
|
|
2973
2968
|
* every surface at once and outranks even `sessionMode: all_calls` — a mode the user
|
|
2974
2969
|
* set once must not overrule a rule they set about one agent. */
|
|
2975
2970
|
reach: NotifyLevelSchema.nullable().optional(),
|
|
2976
|
-
createdAt:
|
|
2971
|
+
createdAt: z11.string().datetime(),
|
|
2977
2972
|
/** Most recent notification on this connection, either direction. Null = no contact yet.
|
|
2978
2973
|
* Drives the agents-page recency grouping (Today / This week / …). */
|
|
2979
|
-
lastContactAt:
|
|
2974
|
+
lastContactAt: z11.string().datetime().nullable(),
|
|
2980
2975
|
/** Last presence heartbeat from a running agent process (POST /api/presence) — the
|
|
2981
2976
|
* desktop app while open. Null = never seen; stale = offline. */
|
|
2982
|
-
lastSeenAt:
|
|
2977
|
+
lastSeenAt: z11.string().datetime().nullable().optional(),
|
|
2983
2978
|
/** WORKING, NOT JUST CONNECTED (owner, 2026-09-30): the last time the agent itself acted on one of
|
|
2984
2979
|
* its Goals — wrote on one or recorded an operation (`tokens.last_worked_at`). Within
|
|
2985
2980
|
* `WORKING_MS` it is working; otherwise it is connected but idle. Null = not seen working yet. */
|
|
2986
|
-
lastWorkedAt:
|
|
2981
|
+
lastWorkedAt: z11.string().datetime().nullable().optional(),
|
|
2987
2982
|
/** The oldest of its Goals that is `ready` for it — work handed to it that nobody has started.
|
|
2988
2983
|
* With no work of its own for `WORKING_MS`, an agent sitting on this is not taking its work. */
|
|
2989
|
-
oldestReadyAt:
|
|
2984
|
+
oldestReadyAt: z11.string().datetime().nullable().optional(),
|
|
2990
2985
|
/** What a live desktop can run (docs/clients/desktop/companion.md §2.2), advertised on its heartbeat:
|
|
2991
2986
|
* harness availabilities + granted workspaces — the option set the phone's
|
|
2992
2987
|
* "new session" sheet offers. Absent for ordinary MCP agents. */
|
|
2993
|
-
runtime:
|
|
2988
|
+
runtime: z11.object({
|
|
2994
2989
|
/** The @paigy/harness this host is running — a machine the self-update has not reached
|
|
2995
2990
|
* shows its age here (`apps/desktop/src/update.ts`). */
|
|
2996
|
-
version:
|
|
2997
|
-
harnesses:
|
|
2998
|
-
workspaces:
|
|
2991
|
+
version: z11.string().optional(),
|
|
2992
|
+
harnesses: z11.array(z11.object({ name: z11.string(), label: z11.string(), status: z11.string(), models: z11.array(z11.string()).optional() })).optional(),
|
|
2993
|
+
workspaces: z11.array(z11.string()).optional(),
|
|
2999
2994
|
/** THE GIT REPOS IN THOSE FOLDERS (2026-10-01, Goal 26982211): each granted folder that is a
|
|
3000
2995
|
* repo, and each repo directly inside one, with its `origin` remote. A session started for
|
|
3001
2996
|
* work on `mauurda/paigy` opens in that repo rather than the folder above it, where the repo's
|
|
3002
2997
|
* own AGENTS.md is never read (`workspaceForRepo`). Absent on hosts that predate it. */
|
|
3003
|
-
repos:
|
|
2998
|
+
repos: z11.array(z11.object({ path: z11.string(), remote: z11.string() })).optional()
|
|
3004
2999
|
}).optional(),
|
|
3005
3000
|
/** The tail of this agent's working log, when a harness is driving it — the agent page's
|
|
3006
3001
|
* live strip. Absent for anything the desktop harness isn't running (a hatched identity
|
|
@@ -3009,145 +3004,145 @@ var ConnectionSummarySchema = z12.object({
|
|
|
3009
3004
|
activity: AgentActivitySchema.optional(),
|
|
3010
3005
|
/** True = a provider-managed agent running in the provider's cloud (e.g. Anthropic CMA);
|
|
3011
3006
|
* false = a local MCP connection running on the user's computer (Claude Code/Codex/…). */
|
|
3012
|
-
managed:
|
|
3007
|
+
managed: z11.boolean()
|
|
3013
3008
|
});
|
|
3014
|
-
var LedgerItemSchema =
|
|
3015
|
-
var AgentLedgerSchema =
|
|
3009
|
+
var LedgerItemSchema = z11.object({ id: z11.string(), parentId: z11.string(), title: z11.string(), createdAt: z11.string() });
|
|
3010
|
+
var AgentLedgerSchema = z11.object({
|
|
3016
3011
|
/** Null when the agent has not named itself yet — never a placeholder (owner, 2026-10-01). */
|
|
3017
|
-
agent:
|
|
3012
|
+
agent: z11.object({ id: z11.string(), name: z11.string().nullable(), revokedAt: z11.string().nullable() }),
|
|
3018
3013
|
/** Its own questions you have not answered. */
|
|
3019
|
-
asks:
|
|
3014
|
+
asks: z11.array(LedgerItemSchema),
|
|
3020
3015
|
/** Its questions you answered that nobody acted on — still owed to somebody. */
|
|
3021
|
-
answered:
|
|
3016
|
+
answered: z11.array(LedgerItemSchema),
|
|
3022
3017
|
/** Requests you sent it that it never took. */
|
|
3023
|
-
requests:
|
|
3024
|
-
goals:
|
|
3025
|
-
callbacks:
|
|
3018
|
+
requests: z11.array(LedgerItemSchema),
|
|
3019
|
+
goals: z11.array(z11.object({ id: z11.string(), outcome: z11.string(), state: z11.string() })),
|
|
3020
|
+
callbacks: z11.array(z11.object({ id: z11.string(), parentId: z11.string(), trigger: z11.string(), note: z11.string(), dueAt: z11.string().nullable() }))
|
|
3026
3021
|
});
|
|
3027
|
-
var ReassignResultSchema =
|
|
3028
|
-
moved:
|
|
3029
|
-
parentId:
|
|
3022
|
+
var ReassignResultSchema = z11.object({
|
|
3023
|
+
moved: z11.object({ asks: z11.number(), answered: z11.number(), requests: z11.number(), goals: z11.number(), callbacks: z11.number() }),
|
|
3024
|
+
parentId: z11.string().nullable()
|
|
3030
3025
|
});
|
|
3031
|
-
var LessonStateSchema =
|
|
3032
|
-
var LessonViewSchema =
|
|
3033
|
-
id:
|
|
3034
|
-
text:
|
|
3026
|
+
var LessonStateSchema = z11.enum(["active", "proposed", "retired"]);
|
|
3027
|
+
var LessonViewSchema = z11.object({
|
|
3028
|
+
id: z11.string(),
|
|
3029
|
+
text: z11.string(),
|
|
3035
3030
|
state: LessonStateSchema,
|
|
3036
3031
|
/** The Goal it is scoped to; null = the whole account. */
|
|
3037
|
-
scopeGoalId:
|
|
3038
|
-
goalTitle:
|
|
3039
|
-
version:
|
|
3040
|
-
pinned:
|
|
3032
|
+
scopeGoalId: z11.string().nullable(),
|
|
3033
|
+
goalTitle: z11.string().nullable(),
|
|
3034
|
+
version: z11.number(),
|
|
3035
|
+
pinned: z11.boolean(),
|
|
3041
3036
|
/** When the person last wrote its text themselves. */
|
|
3042
|
-
editedAt:
|
|
3043
|
-
createdAt:
|
|
3044
|
-
updatedAt:
|
|
3037
|
+
editedAt: z11.string().nullable(),
|
|
3038
|
+
createdAt: z11.string(),
|
|
3039
|
+
updatedAt: z11.string(),
|
|
3045
3040
|
/** The Entries it came from, oldest first; `words` is null when an Entry has none to show (sealed). */
|
|
3046
|
-
sources:
|
|
3041
|
+
sources: z11.array(z11.object({ entryId: z11.string(), words: z11.string().nullable(), at: z11.string() }))
|
|
3047
3042
|
});
|
|
3048
|
-
var QueueQuestionSchema =
|
|
3043
|
+
var QueueQuestionSchema = z11.object({
|
|
3049
3044
|
/** The decision need's id — what an answer is accepted against. */
|
|
3050
|
-
id:
|
|
3045
|
+
id: z11.string(),
|
|
3051
3046
|
/** The words that were asked, from the request Entry that asked them. */
|
|
3052
|
-
question:
|
|
3047
|
+
question: z11.string(),
|
|
3053
3048
|
/** Where it was asked — which is where the ruling goes (`POST /api/entries`). Null only
|
|
3054
3049
|
* for a need whose request Entry is carried by no interactive Delivery, which nothing
|
|
3055
3050
|
* can answer. */
|
|
3056
|
-
deliveryId:
|
|
3051
|
+
deliveryId: z11.string().nullable().default(null),
|
|
3057
3052
|
/** The Entry the ruling is about. */
|
|
3058
|
-
aboutId:
|
|
3053
|
+
aboutId: z11.string().nullable().default(null),
|
|
3059
3054
|
/** Empty for a free-text question. */
|
|
3060
|
-
options:
|
|
3061
|
-
select:
|
|
3062
|
-
askedAt:
|
|
3055
|
+
options: z11.array(OptionSchema).default([]),
|
|
3056
|
+
select: z11.enum(["one", "many", "rank", "confirm", "text"]).default("text"),
|
|
3057
|
+
askedAt: z11.string(),
|
|
3063
3058
|
/** Null while the question is open — which is how the page tells the two apart. */
|
|
3064
|
-
answeredAt:
|
|
3059
|
+
answeredAt: z11.string().nullable().default(null),
|
|
3065
3060
|
/** The ruling in the person's own words, from the contribution that replied — not the
|
|
3066
3061
|
* option id, which is not something anyone reads back. Null while it is open, and null
|
|
3067
3062
|
* for a settled question whose reply carried nothing readable. */
|
|
3068
|
-
answer:
|
|
3063
|
+
answer: z11.string().nullable().default(null),
|
|
3069
3064
|
/** The Goal this question belongs to — a step knows its Goal on its own, not only through
|
|
3070
3065
|
* an `InboxItem`'s `communication.goalIds[0]` (docs/clients/app/walk/design.md §12 item 3).
|
|
3071
3066
|
* READ BY `apps/client/src/walk/order.ts`, which stamps it onto every `WalkStep`: the walk's
|
|
3072
3067
|
* order, its route, home's trees and the list of steps all take a step's Goal from here, so
|
|
3073
3068
|
* this is the field they agree through rather than each re-deriving it from the row it
|
|
3074
3069
|
* arrived under. Required because the API projects it on every need it sends. */
|
|
3075
|
-
goalId:
|
|
3070
|
+
goalId: z11.string(),
|
|
3076
3071
|
/** True only while an unmet START gate holds the Goal — a Goal that merely waits to
|
|
3077
3072
|
* *finish* does not stop a person from answering (owner, 2026-09-16: "per need gate from
|
|
3078
3073
|
* the API"; §4's dashed node). Not the same fact as `QueueItem.blocked`, which counts any
|
|
3079
3074
|
* gate at all. */
|
|
3080
|
-
blocked:
|
|
3075
|
+
blocked: z11.boolean().default(false)
|
|
3081
3076
|
});
|
|
3082
|
-
var QueueReplySchema =
|
|
3077
|
+
var QueueReplySchema = z11.object({
|
|
3083
3078
|
/** The card this note was (`deliveryId:requestEntryId`, minted by the server like every card
|
|
3084
3079
|
* id) — so the phone can tell a reply it just sent from one the queue already carries, and the
|
|
3085
3080
|
* walk can name it in its zoom. */
|
|
3086
|
-
id:
|
|
3081
|
+
id: z11.string(),
|
|
3087
3082
|
/** The Goal the note is on. */
|
|
3088
|
-
goalId:
|
|
3083
|
+
goalId: z11.string(),
|
|
3089
3084
|
/** What the note said. */
|
|
3090
|
-
note:
|
|
3085
|
+
note: z11.string(),
|
|
3091
3086
|
/** Where it was carried — where a second reply goes (`POST /api/entries`, #2252). */
|
|
3092
|
-
deliveryId:
|
|
3093
|
-
requestEntryId:
|
|
3094
|
-
askedAt:
|
|
3087
|
+
deliveryId: z11.string(),
|
|
3088
|
+
requestEntryId: z11.string(),
|
|
3089
|
+
askedAt: z11.string(),
|
|
3095
3090
|
/** When the person last replied — the window's start. */
|
|
3096
|
-
repliedAt:
|
|
3091
|
+
repliedAt: z11.string(),
|
|
3097
3092
|
/** The person's latest words about it; null when there is nothing readable in them. */
|
|
3098
|
-
reply:
|
|
3093
|
+
reply: z11.string().nullable()
|
|
3099
3094
|
});
|
|
3100
|
-
var QueueItemSchema =
|
|
3101
|
-
id:
|
|
3095
|
+
var QueueItemSchema = z11.object({
|
|
3096
|
+
id: z11.string(),
|
|
3102
3097
|
/** One-line headline — the first sentence of the outcome. */
|
|
3103
|
-
title:
|
|
3098
|
+
title: z11.string(),
|
|
3104
3099
|
/** The outcome in full, verbatim: the person's own words are what an assignee sees. */
|
|
3105
|
-
intent:
|
|
3100
|
+
intent: z11.string(),
|
|
3106
3101
|
/** `ready` | `active` | `waiting` | `done` | `cancelled`, straight off the Goal. */
|
|
3107
|
-
state:
|
|
3102
|
+
state: z11.string(),
|
|
3108
3103
|
/** Who holds it (a participant ref); null when nobody does yet. */
|
|
3109
|
-
assignee:
|
|
3104
|
+
assignee: z11.string().nullable().default(null),
|
|
3110
3105
|
/** What the agent last said it was doing; null if it has said nothing. */
|
|
3111
|
-
progress:
|
|
3106
|
+
progress: z11.string().nullable().default(null),
|
|
3112
3107
|
/** HOME'S LINE FOR THAT NOTE (owner, 2026-09-23): a few plain words one read wrote from `progress`,
|
|
3113
3108
|
* served only while it was written for the current note. Null means show the Goal's name. */
|
|
3114
|
-
progressLine:
|
|
3115
|
-
reviewPending:
|
|
3116
|
-
dueAt:
|
|
3109
|
+
progressLine: z11.string().nullable().optional(),
|
|
3110
|
+
reviewPending: z11.boolean().default(false),
|
|
3111
|
+
dueAt: z11.string().nullable().default(null),
|
|
3117
3112
|
/** WHEN ITS OWNER SAID DONE WHILE CHILDREN WERE OPEN (#2704): its own work is finished and it closes
|
|
3118
3113
|
* with its last open child. Null otherwise; optional, so hand-built queues need not spell it. */
|
|
3119
|
-
finishedAt:
|
|
3114
|
+
finishedAt: z11.string().nullable().optional(),
|
|
3120
3115
|
/** The Goal this one was opened under; null at the root. */
|
|
3121
|
-
parentGoalId:
|
|
3116
|
+
parentGoalId: z11.string().nullable().default(null),
|
|
3122
3117
|
/** Goals opened under this one — only those the same list holds. */
|
|
3123
|
-
childGoalIds:
|
|
3118
|
+
childGoalIds: z11.array(z11.string()).default([]),
|
|
3124
3119
|
/** Goals this one waits on (start or finish gates). */
|
|
3125
|
-
dependencyGoalIds:
|
|
3120
|
+
dependencyGoalIds: z11.array(z11.string()).default([]),
|
|
3126
3121
|
/** True while any gate is on a Goal that is not done — the walk draws it dashed. */
|
|
3127
|
-
blocked:
|
|
3122
|
+
blocked: z11.boolean().default(false),
|
|
3128
3123
|
/** Its questions: every OPEN one, and at most ten settled, newest settled first
|
|
3129
3124
|
* (20260929133308) — the page decides which of them to show. NOT the whole set: `asked` and
|
|
3130
3125
|
* `answered` are, and a settled one's words are a line (280 characters), its body read when the
|
|
3131
3126
|
* question is opened. */
|
|
3132
|
-
questions:
|
|
3127
|
+
questions: z11.array(QueueQuestionSchema).default([]),
|
|
3133
3128
|
/** HOW MANY QUESTIONS THIS WORK HAS ASKED, and how many are answered — the Goal's own totals,
|
|
3134
3129
|
* bounded at 100 server-side. A tally counted off `questions` is a wrong number that looks
|
|
3135
3130
|
* right once the cap bites (`walk/trees.ts` `tallyOf`). Optional, and defaulted from the array
|
|
3136
3131
|
* by the projection, so hand-built queues (fixtures, the demo) need not spell them. */
|
|
3137
|
-
asked:
|
|
3138
|
-
answered:
|
|
3132
|
+
asked: z11.number().optional(),
|
|
3133
|
+
answered: z11.number().optional(),
|
|
3139
3134
|
/** Every note on it the person replied to (`QueueReplySchema`) — the page decides which to show.
|
|
3140
3135
|
* Optional, not defaulted: absent is none, and every hand-built queue (fixtures, the demo) need
|
|
3141
3136
|
* not spell an empty list. */
|
|
3142
|
-
replies:
|
|
3137
|
+
replies: z11.array(QueueReplySchema).optional(),
|
|
3143
3138
|
/** The repository or project identifier this Goal belongs to (#2280), null if untracked. */
|
|
3144
|
-
repo:
|
|
3145
|
-
createdAt:
|
|
3146
|
-
updatedAt:
|
|
3139
|
+
repo: z11.string().nullable().optional(),
|
|
3140
|
+
createdAt: z11.string(),
|
|
3141
|
+
updatedAt: z11.string().nullable().default(null),
|
|
3147
3142
|
/** When its owner last SAID something about it (the newest `progress` Entry: a contact update kept
|
|
3148
3143
|
* as progress). `updatedAt` moves for reasons nobody chose — a
|
|
3149
3144
|
* state recomputed, a review flag — so it cannot tell work in hand from work gone quiet. */
|
|
3150
|
-
lastProgressAt:
|
|
3145
|
+
lastProgressAt: z11.string().nullable().optional(),
|
|
3151
3146
|
/** THE GOAL'S NEWEST WORD, FROM EITHER SIDE (owner, 2026-09-27): the newest Entry on it, of any
|
|
3152
3147
|
* kind — what the person added ("Add to this"), their reply, the agent's ask or its progress
|
|
3153
3148
|
* note. A progress note is an Entry, so this is already the newer of the two: the person's note
|
|
@@ -3166,7 +3161,14 @@ var QueueItemSchema = z12.object({
|
|
|
3166
3161
|
* minute after the person speaks erases their instant from it, and the durable traces the client
|
|
3167
3162
|
* can see (`replies`, `questions[].answeredAt`) miss a spontaneous note entirely — a `request`
|
|
3168
3163
|
* Entry with no `about_id` is in neither. */
|
|
3169
|
-
lastPersonAt:
|
|
3164
|
+
lastPersonAt: z11.string().nullable().optional(),
|
|
3165
|
+
/** EACH SIDE'S NEWEST WORD IN THE LAST DAY (owner, 2026-10-13, the Work tab's order): the person's
|
|
3166
|
+
* (`yours`) and the agents' (`theirs`), each null when that side wrote nothing on it in 24 hours.
|
|
3167
|
+
* `latest` holds one Entry, so whichever side wrote first is hidden there; Work orders a Goal by
|
|
3168
|
+
* these two and draws the side it was placed by under its title. Optional, so hand-built queues
|
|
3169
|
+
* and a door older than 20261013045542 need not spell them. */
|
|
3170
|
+
yours: GoalLatestSchema.nullable().optional(),
|
|
3171
|
+
theirs: GoalLatestSchema.nullable().optional(),
|
|
3170
3172
|
/** WHAT THIS ROW IS, IN TWELVE CHARACTERS (#2928) — the hash of every other field on it, stamped
|
|
3171
3173
|
* by the one projection that builds the row (`apps/api/src/goal/queue.ts`). It is how the
|
|
3172
3174
|
* incremental read knows a row has not moved: the phone echoes back the revs it holds
|
|
@@ -3177,40 +3179,40 @@ var QueueItemSchema = z12.object({
|
|
|
3177
3179
|
* the row shows that no `updated_at` moves for (`active` lapsing, a dependency's state, a
|
|
3178
3180
|
* sibling appearing in `childGoalIds`) cannot go unnoticed. Optional because a hand-built
|
|
3179
3181
|
* queue (a fixture, the demo) spells none, and a row with no rev is simply always re-sent. */
|
|
3180
|
-
rev:
|
|
3182
|
+
rev: z11.string().optional()
|
|
3181
3183
|
});
|
|
3182
|
-
var QueueDeltaSchema =
|
|
3183
|
-
ids:
|
|
3184
|
-
items:
|
|
3184
|
+
var QueueDeltaSchema = z11.object({
|
|
3185
|
+
ids: z11.array(z11.string()),
|
|
3186
|
+
items: z11.array(QueueItemSchema)
|
|
3185
3187
|
});
|
|
3186
3188
|
var COLD_AFTER_MS = 3 * 24 * 60 * 60 * 1e3;
|
|
3187
|
-
var NoteSourceSchema =
|
|
3188
|
-
var NoteStatusSchema =
|
|
3189
|
-
var NoteRepeatSchema =
|
|
3190
|
-
var DecisionSchema =
|
|
3191
|
-
id:
|
|
3189
|
+
var NoteSourceSchema = z11.enum(["app", "call"]);
|
|
3190
|
+
var NoteStatusSchema = z11.enum(["open", "assigned", "in_progress", "done"]);
|
|
3191
|
+
var NoteRepeatSchema = z11.enum(["once", "until_done"]);
|
|
3192
|
+
var DecisionSchema = z11.object({
|
|
3193
|
+
id: z11.string(),
|
|
3192
3194
|
/** The note this decision refines; null = recorded on a bare thread (the
|
|
3193
3195
|
* extensibility seam — any conversation can accrue decisions). */
|
|
3194
|
-
noteId:
|
|
3196
|
+
noteId: z11.string().nullable(),
|
|
3195
3197
|
/** What was ambiguous — the broker's (or the user's own) question. */
|
|
3196
|
-
question:
|
|
3198
|
+
question: z11.string(),
|
|
3197
3199
|
/** The user's ruling; null while the question is open. */
|
|
3198
|
-
answer:
|
|
3199
|
-
decidedAt:
|
|
3200
|
-
createdAt:
|
|
3200
|
+
answer: z11.string().nullable(),
|
|
3201
|
+
decidedAt: z11.string().nullable(),
|
|
3202
|
+
createdAt: z11.string()
|
|
3201
3203
|
});
|
|
3202
|
-
var NoteSchema =
|
|
3203
|
-
id:
|
|
3204
|
+
var NoteSchema = z11.object({
|
|
3205
|
+
id: z11.string(),
|
|
3204
3206
|
/** One-line headline (broker-titled; deterministic floor). */
|
|
3205
|
-
title:
|
|
3207
|
+
title: z11.string(),
|
|
3206
3208
|
/** The original intent, verbatim — assignees always see the user's own words. */
|
|
3207
|
-
intent:
|
|
3209
|
+
intent: z11.string(),
|
|
3208
3210
|
source: NoteSourceSchema,
|
|
3209
3211
|
status: NoteStatusSchema,
|
|
3210
3212
|
/** Who it was assigned to (a participant ref, 'agent:<tokenId>'); null = unassigned. */
|
|
3211
|
-
assignee:
|
|
3213
|
+
assignee: z11.string().nullable(),
|
|
3212
3214
|
/** The request thread minted at assignment; null until assigned. */
|
|
3213
|
-
parentId:
|
|
3215
|
+
parentId: z11.string().nullable(),
|
|
3214
3216
|
/** REMINDERS (docs/model/notes/reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
|
|
3215
3217
|
* call — never a deadline. It only ever comes from the user's own words, so when it
|
|
3216
3218
|
* passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
|
|
@@ -3219,102 +3221,102 @@ var NoteSchema = z12.object({
|
|
|
3219
3221
|
// Defaulted, not required: a Note from an API deploy older than the reminders
|
|
3220
3222
|
// migration has none of these, and the defaults ARE what it means — no not-before,
|
|
3221
3223
|
// one ride, never ridden. Parsing must not fail across a rolling deploy.
|
|
3222
|
-
dueAt:
|
|
3224
|
+
dueAt: z11.string().nullable().default(null),
|
|
3223
3225
|
repeat: NoteRepeatSchema.default("once"),
|
|
3224
3226
|
/** How many calls have already carried it — the fatigue cap counts rides, not days. */
|
|
3225
|
-
rides:
|
|
3226
|
-
lastRideAt:
|
|
3227
|
-
createdAt:
|
|
3227
|
+
rides: z11.number().int().default(0),
|
|
3228
|
+
lastRideAt: z11.string().nullable().default(null),
|
|
3229
|
+
createdAt: z11.string()
|
|
3228
3230
|
});
|
|
3229
|
-
var DeliveryModeSchema =
|
|
3231
|
+
var DeliveryModeSchema = z11.enum(["poll", "self_hosted"]);
|
|
3230
3232
|
var WAKE_EVENT = "wake";
|
|
3231
3233
|
var wakeChannel = (tokenId) => `wake:${tokenId}`;
|
|
3232
|
-
var RegisterDeliverySchema =
|
|
3233
|
-
var OAuthStartSchema =
|
|
3234
|
-
provider:
|
|
3235
|
-
returnTo:
|
|
3234
|
+
var RegisterDeliverySchema = z11.object({ mode: DeliveryModeSchema });
|
|
3235
|
+
var OAuthStartSchema = z11.object({
|
|
3236
|
+
provider: z11.enum(["cma"]),
|
|
3237
|
+
returnTo: z11.string().min(1)
|
|
3236
3238
|
});
|
|
3237
|
-
var DeliveryConfigSchema =
|
|
3238
|
-
tokenId:
|
|
3239
|
+
var DeliveryConfigSchema = z11.object({
|
|
3240
|
+
tokenId: z11.string(),
|
|
3239
3241
|
mode: DeliveryModeSchema,
|
|
3240
3242
|
/** null when the deployment has no anon key configured. `self_hosted` is then REFUSED
|
|
3241
3243
|
* (503 `self_hosted_unavailable`) rather than registered, so a self_hosted config always
|
|
3242
3244
|
* carries credentials; only a `poll` registration can come back with null here. */
|
|
3243
|
-
realtime:
|
|
3245
|
+
realtime: z11.object({ url: z11.string(), anonKey: z11.string() }).nullable()
|
|
3244
3246
|
});
|
|
3245
|
-
var HostDecisionSchema =
|
|
3247
|
+
var HostDecisionSchema = z11.object({
|
|
3246
3248
|
/** The agent's token id: the row's `recipient`. */
|
|
3247
|
-
agent:
|
|
3248
|
-
decision:
|
|
3249
|
+
agent: z11.string().uuid(),
|
|
3250
|
+
decision: z11.enum(["stood_back", "took_over"]),
|
|
3249
3251
|
/** The work it was about: the Goal waiting on that agent next (`claimable` on its `contact({})` read). */
|
|
3250
|
-
goalId:
|
|
3252
|
+
goalId: z11.string().uuid().nullable().optional(),
|
|
3251
3253
|
/** When the server last heard from the agent, as the host read it: the presence it stood back for. */
|
|
3252
|
-
seenAt:
|
|
3254
|
+
seenAt: z11.string().datetime().nullable().optional(),
|
|
3253
3255
|
/** When that work last moved (`claimable.since` on an agent's `contact({})` read), the fact the bound is judged on. */
|
|
3254
|
-
since:
|
|
3256
|
+
since: z11.string().datetime().nullable().optional(),
|
|
3255
3257
|
/** What the host said, in its log's own words: why it stood back, or what the take-over did. */
|
|
3256
|
-
said:
|
|
3258
|
+
said: z11.string().max(300).optional()
|
|
3257
3259
|
});
|
|
3258
|
-
var WakeNudgeSchema =
|
|
3259
|
-
kind:
|
|
3260
|
-
notificationId:
|
|
3261
|
-
parentId:
|
|
3260
|
+
var WakeNudgeSchema = z11.object({
|
|
3261
|
+
kind: z11.enum(["reply", "request", "callback"]),
|
|
3262
|
+
notificationId: z11.string().optional(),
|
|
3263
|
+
parentId: z11.string()
|
|
3262
3264
|
});
|
|
3263
|
-
var PairingStatusSchema =
|
|
3264
|
-
var DeviceCodeSchema =
|
|
3265
|
-
device_code:
|
|
3266
|
-
user_code:
|
|
3267
|
-
verification_uri:
|
|
3268
|
-
verification_uri_complete:
|
|
3269
|
-
interval:
|
|
3270
|
-
expires_in:
|
|
3265
|
+
var PairingStatusSchema = z11.enum(["pending", "approved", "denied", "expired"]);
|
|
3266
|
+
var DeviceCodeSchema = z11.object({
|
|
3267
|
+
device_code: z11.string(),
|
|
3268
|
+
user_code: z11.string(),
|
|
3269
|
+
verification_uri: z11.string().url(),
|
|
3270
|
+
verification_uri_complete: z11.string().url(),
|
|
3271
|
+
interval: z11.number(),
|
|
3272
|
+
expires_in: z11.number()
|
|
3271
3273
|
});
|
|
3272
|
-
var DeviceInfoSchema =
|
|
3273
|
-
code:
|
|
3274
|
+
var DeviceInfoSchema = z11.object({
|
|
3275
|
+
code: z11.string(),
|
|
3274
3276
|
/** The agent's suggested name (from /device/code) — shown on the approval screen,
|
|
3275
3277
|
* pre-filling the name field the human can edit. */
|
|
3276
|
-
name:
|
|
3278
|
+
name: z11.string(),
|
|
3277
3279
|
/** @deprecated Legacy alias of `name` for the pre-#531 embedded bundle in App Store
|
|
3278
3280
|
* build 35, whose DeviceFlow renders `info.agent.slice(0, 2)` — without this a FRESH
|
|
3279
3281
|
* install crashes on the pairing screen on first launch, before the OTA lands
|
|
3280
3282
|
* (seen live: PAIGY-5T, 2026-07-21). Remove once a newer binary is the floor. */
|
|
3281
|
-
agent:
|
|
3282
|
-
device:
|
|
3283
|
+
agent: z11.string().optional(),
|
|
3284
|
+
device: z11.string().nullable(),
|
|
3283
3285
|
status: PairingStatusSchema
|
|
3284
3286
|
});
|
|
3285
|
-
var DeviceTokenSchema =
|
|
3286
|
-
access_token:
|
|
3287
|
+
var DeviceTokenSchema = z11.object({
|
|
3288
|
+
access_token: z11.string(),
|
|
3287
3289
|
/** The pairing's single name (user-typed at approval, the agent's suggestion, or
|
|
3288
3290
|
* a default silly name). */
|
|
3289
|
-
name:
|
|
3290
|
-
device:
|
|
3291
|
+
name: z11.string(),
|
|
3292
|
+
device: z11.string().nullable(),
|
|
3291
3293
|
/** The pairing's assigned voice, cached so the desktop can seed the SAME face the phone
|
|
3292
3294
|
* draws — voice is the third ingredient of a hatchling's build (party/traits.ts). */
|
|
3293
|
-
voice:
|
|
3295
|
+
voice: z11.string().nullable().optional(),
|
|
3294
3296
|
/** The token's server-side id — the face's COLOUR anchor, and the only seed ingredient
|
|
3295
3297
|
* that survives a rename. Cached by the host's identity beat. */
|
|
3296
|
-
token_id:
|
|
3298
|
+
token_id: z11.string().nullable().optional(),
|
|
3297
3299
|
/** WHERE this identity works — the folder a wake should land it in. Written by the host
|
|
3298
3300
|
* at spawn and by `paigy-harness handoff` from a live terminal. Without it every wake
|
|
3299
3301
|
* landed in the FIRST granted workspace and the agent rediscovered its own repo from
|
|
3300
3302
|
* the thread each time (host.ts, live catch 2026-08-06 — prompt-papered until now). */
|
|
3301
|
-
workspace:
|
|
3303
|
+
workspace: z11.string().nullable().optional(),
|
|
3302
3304
|
/** Local host recovery must preserve the launch's runtime and Paigy identity. */
|
|
3303
|
-
harness:
|
|
3304
|
-
session_id:
|
|
3305
|
+
harness: z11.enum(["claude", "codex", "agy"]).optional(),
|
|
3306
|
+
session_id: z11.string().uuid().optional(),
|
|
3305
3307
|
/** A conversation the host must not resume: its context is full, so every turn fails
|
|
3306
3308
|
* ("Prompt is too long"). Written when a run hits it (`run.ts` `onFull`); the host skips a slot
|
|
3307
3309
|
* whose resumable session is this one, so its Goals reach the dead-agent handoff instead of a
|
|
3308
3310
|
* copy that types the person's words into a turn that cannot run (Calls, 2026-10-06). */
|
|
3309
|
-
full_session:
|
|
3310
|
-
uik_pub:
|
|
3311
|
+
full_session: z11.string().optional(),
|
|
3312
|
+
uik_pub: z11.string().nullable().optional()
|
|
3311
3313
|
});
|
|
3312
|
-
var SupportRequestSchema =
|
|
3313
|
-
email:
|
|
3314
|
-
message:
|
|
3315
|
-
name:
|
|
3314
|
+
var SupportRequestSchema = z11.object({
|
|
3315
|
+
email: z11.string().email().max(320),
|
|
3316
|
+
message: z11.string().trim().min(1).max(5e3),
|
|
3317
|
+
name: z11.string().trim().max(120).optional()
|
|
3316
3318
|
});
|
|
3317
|
-
var NotificationFeedbackKindSchema =
|
|
3319
|
+
var NotificationFeedbackKindSchema = z11.enum([
|
|
3318
3320
|
"break_down",
|
|
3319
3321
|
// "This should be more than one ask — break it down."
|
|
3320
3322
|
"regenerate_options",
|
|
@@ -3329,121 +3331,131 @@ var NotificationFeedbackKindSchema = z12.enum([
|
|
|
3329
3331
|
// anything else — the note carries it.
|
|
3330
3332
|
]);
|
|
3331
3333
|
var SlimOptionSchema = OptionSchema.omit({ html: true });
|
|
3332
|
-
var QuestionRowSchema =
|
|
3334
|
+
var QuestionRowSchema = z11.object({
|
|
3333
3335
|
/** The card's id (`deliveryId:needId`, or `deliveryId:entryId` for an update), as the inbox mints it. */
|
|
3334
|
-
id:
|
|
3335
|
-
deliveryId:
|
|
3336
|
-
entryId:
|
|
3336
|
+
id: z11.string(),
|
|
3337
|
+
deliveryId: z11.string(),
|
|
3338
|
+
entryId: z11.string(),
|
|
3337
3339
|
/** The decision it waits on; null for an update, which asks nothing. */
|
|
3338
|
-
needId:
|
|
3339
|
-
goalIds:
|
|
3340
|
+
needId: z11.string().nullable(),
|
|
3341
|
+
goalIds: z11.array(z11.string()),
|
|
3340
3342
|
/** The name of the work it is about, when the read could word it. */
|
|
3341
|
-
goalTitle:
|
|
3342
|
-
tokenId:
|
|
3343
|
-
name:
|
|
3344
|
-
title:
|
|
3345
|
-
body:
|
|
3346
|
-
select:
|
|
3347
|
-
options:
|
|
3348
|
-
hasPreview:
|
|
3349
|
-
blocking:
|
|
3350
|
-
askedAt:
|
|
3343
|
+
goalTitle: z11.string().optional(),
|
|
3344
|
+
tokenId: z11.string().optional(),
|
|
3345
|
+
name: z11.string(),
|
|
3346
|
+
title: z11.string(),
|
|
3347
|
+
body: z11.string(),
|
|
3348
|
+
select: z11.enum(["one", "many", "rank", "confirm", "text"]),
|
|
3349
|
+
options: z11.array(SlimOptionSchema),
|
|
3350
|
+
hasPreview: z11.boolean(),
|
|
3351
|
+
blocking: z11.boolean(),
|
|
3352
|
+
askedAt: z11.string().datetime(),
|
|
3351
3353
|
ring: InboxItemSchema.shape.ring,
|
|
3352
|
-
onCall:
|
|
3353
|
-
sealed:
|
|
3354
|
+
onCall: z11.literal(true).optional(),
|
|
3355
|
+
sealed: z11.boolean()
|
|
3354
3356
|
});
|
|
3355
|
-
var WorkStateSchema =
|
|
3356
|
-
var WorkRowSchema =
|
|
3357
|
-
id:
|
|
3358
|
-
parentId:
|
|
3359
|
-
title:
|
|
3357
|
+
var WorkStateSchema = z11.enum(["ready", "active", "waiting", "done", "cancelled"]);
|
|
3358
|
+
var WorkRowSchema = z11.object({
|
|
3359
|
+
id: z11.string(),
|
|
3360
|
+
parentId: z11.string().nullable(),
|
|
3361
|
+
title: z11.string(),
|
|
3360
3362
|
/** Straight off the Goal. */
|
|
3361
3363
|
state: WorkStateSchema,
|
|
3362
|
-
owner:
|
|
3363
|
-
revision:
|
|
3364
|
+
owner: z11.string().nullable(),
|
|
3365
|
+
revision: z11.number().int(),
|
|
3364
3366
|
/** Open questions on it, counted to 100. */
|
|
3365
|
-
waiting:
|
|
3367
|
+
waiting: z11.number().int(),
|
|
3366
3368
|
/** Held by a gate on work that is not done. */
|
|
3367
|
-
blocked:
|
|
3368
|
-
lastProgressAt:
|
|
3369
|
+
blocked: z11.boolean(),
|
|
3370
|
+
lastProgressAt: z11.string().datetime().nullable(),
|
|
3369
3371
|
/** The line written for its newest progress note, else that note's first words. */
|
|
3370
|
-
line:
|
|
3372
|
+
line: z11.string().nullable(),
|
|
3371
3373
|
/** Work directly under it, counted to 100; the list carries up to 12 of them. */
|
|
3372
|
-
children:
|
|
3373
|
-
createdAt:
|
|
3374
|
-
updatedAt:
|
|
3374
|
+
children: z11.number().int(),
|
|
3375
|
+
createdAt: z11.string().datetime(),
|
|
3376
|
+
updatedAt: z11.string().datetime(),
|
|
3375
3377
|
/** When anything at or under it last moved — the order the list is in. */
|
|
3376
|
-
activeAt:
|
|
3378
|
+
activeAt: z11.string().datetime(),
|
|
3377
3379
|
/** A sealed outcome has no title here; the work's page opens it. */
|
|
3378
|
-
sealed:
|
|
3380
|
+
sealed: z11.boolean()
|
|
3379
3381
|
});
|
|
3380
3382
|
var ComputerRowSchema = ConnectionSummarySchema.omit({ activity: true });
|
|
3381
3383
|
var AgentRowSchema = ComputerRowSchema.extend({
|
|
3382
3384
|
/** Open questions it is asking the person, over every open card; null when that read failed. */
|
|
3383
|
-
asking:
|
|
3384
|
-
oldestAskAt:
|
|
3385
|
+
asking: z11.number().int().nullable(),
|
|
3386
|
+
oldestAskAt: z11.string().datetime().nullable(),
|
|
3385
3387
|
/** Up to three of the live Goals it holds, oldest first (the order it picks them up), and how
|
|
3386
3388
|
* many in all among the account's 200 most recently active agent-held live Goals
|
|
3387
3389
|
* (`agent_holds`); null when that read failed. */
|
|
3388
|
-
holds:
|
|
3389
|
-
held:
|
|
3390
|
+
holds: z11.array(z11.object({ id: z11.string(), title: z11.string() })).nullable(),
|
|
3391
|
+
held: z11.number().int().nullable(),
|
|
3390
3392
|
/** The earliest instant any Goal it holds went quiet, by the one rule (`coldSince`); null
|
|
3391
3393
|
* while none has, or when that read failed. */
|
|
3392
|
-
cold:
|
|
3394
|
+
cold: z11.string().datetime().nullable(),
|
|
3393
3395
|
/** The newest line of its working log, and when the harness saw it. */
|
|
3394
|
-
line:
|
|
3395
|
-
lineAt:
|
|
3396
|
+
line: z11.string().nullable(),
|
|
3397
|
+
lineAt: z11.string().datetime().nullable()
|
|
3396
3398
|
});
|
|
3397
|
-
var SnapshotSchema =
|
|
3399
|
+
var SnapshotSchema = z11.object({
|
|
3398
3400
|
/** The API's clock, taken before the first read: what a later delta will start from. */
|
|
3399
|
-
at:
|
|
3400
|
-
questions:
|
|
3401
|
+
at: z11.string().datetime(),
|
|
3402
|
+
questions: z11.object({
|
|
3401
3403
|
/** The newest 30 open cards, questions before updates. */
|
|
3402
|
-
items:
|
|
3404
|
+
items: z11.array(QuestionRowSchema),
|
|
3403
3405
|
/** Every open question, and apart from them every update, and what was put off. */
|
|
3404
|
-
total:
|
|
3405
|
-
updates:
|
|
3406
|
-
putOff:
|
|
3406
|
+
total: z11.number().int(),
|
|
3407
|
+
updates: z11.number().int(),
|
|
3408
|
+
putOff: z11.number().int()
|
|
3407
3409
|
}).nullable(),
|
|
3408
|
-
agents:
|
|
3410
|
+
agents: z11.object({
|
|
3409
3411
|
/** Up to 60, most recently seen first. */
|
|
3410
|
-
items:
|
|
3411
|
-
more:
|
|
3412
|
+
items: z11.array(AgentRowSchema),
|
|
3413
|
+
more: z11.boolean()
|
|
3412
3414
|
}).nullable(),
|
|
3413
|
-
work:
|
|
3415
|
+
work: z11.object({
|
|
3414
3416
|
/** The 60 most recently active roots, each followed by up to 12 children; 240 rows at most. */
|
|
3415
|
-
items:
|
|
3417
|
+
items: z11.array(WorkRowSchema),
|
|
3416
3418
|
/** How much work is behind each of the Work tab's four filters, each counted to 100, read with
|
|
3417
3419
|
* the rows. `work_list` (20260928023533) owns the predicates: Live is `ready`, `active` or
|
|
3418
3420
|
* `waiting`; Waiting on you is live work with an open question or an unmet gate; Not started
|
|
3419
3421
|
* is `ready`; Done is `done` or `cancelled`. */
|
|
3420
|
-
counts:
|
|
3422
|
+
counts: z11.object({ live: z11.number().int(), waiting: z11.number().int(), notStarted: z11.number().int(), done: z11.number().int() })
|
|
3421
3423
|
}).nullable(),
|
|
3422
|
-
you:
|
|
3424
|
+
you: z11.object({
|
|
3423
3425
|
settings: UserSettingsSchema,
|
|
3424
|
-
callable:
|
|
3426
|
+
callable: z11.boolean(),
|
|
3425
3427
|
/** Up to 20 paired computers; null when the roster read failed. */
|
|
3426
|
-
computers:
|
|
3428
|
+
computers: z11.array(ComputerRowSchema).nullable()
|
|
3427
3429
|
}).nullable()
|
|
3428
3430
|
});
|
|
3429
|
-
var CallRecapSchema =
|
|
3430
|
-
call:
|
|
3431
|
-
status:
|
|
3432
|
-
startedAt:
|
|
3433
|
-
durationMs:
|
|
3431
|
+
var CallRecapSchema = z11.object({
|
|
3432
|
+
call: z11.object({
|
|
3433
|
+
status: z11.string(),
|
|
3434
|
+
startedAt: z11.string(),
|
|
3435
|
+
durationMs: z11.number().nullable(),
|
|
3434
3436
|
/** Bot-clock pickup offset; absent before the timestamp release, null before its report lands. */
|
|
3435
|
-
pickupMs:
|
|
3436
|
-
agents:
|
|
3437
|
+
pickupMs: z11.number().nullable().optional(),
|
|
3438
|
+
agents: z11.array(z11.object({ id: z11.string(), name: z11.string().nullable() }))
|
|
3437
3439
|
}),
|
|
3438
|
-
topics:
|
|
3439
|
-
goalId:
|
|
3440
|
-
title:
|
|
3441
|
-
owner:
|
|
3442
|
-
state:
|
|
3440
|
+
topics: z11.array(z11.object({
|
|
3441
|
+
goalId: z11.string().uuid(),
|
|
3442
|
+
title: z11.string(),
|
|
3443
|
+
owner: z11.string(),
|
|
3444
|
+
state: z11.string(),
|
|
3445
|
+
/** Presented Entry content and its author, separately from the current Goal owner. */
|
|
3446
|
+
items: z11.array(z11.object({ entryId: z11.string(), author: z11.string(), text: z11.string(), summary: z11.string().nullable() })).optional(),
|
|
3443
3447
|
/** Whether the call put it to the person. An item it carried and never said has no row on the
|
|
3444
3448
|
* review (owner, 2026-10-08: "why do we get the third one when it didn't happen in the call?"). */
|
|
3445
|
-
presented:
|
|
3446
|
-
|
|
3449
|
+
presented: z11.boolean(),
|
|
3450
|
+
/** Whether THIS call created it — the third way a call touches work, beside carrying a request
|
|
3451
|
+
* there and filing words there (owner, 2026-10-08, Goal `5b4b1a0a`: "Show the new work, keep my
|
|
3452
|
+
* words under it"). Created work is never carried, so it is never `presented`; without this the
|
|
3453
|
+
* row is dropped by the "only what happened on the call" filter. False on a database before
|
|
3454
|
+
* 20261013080133. */
|
|
3455
|
+
created: z11.boolean(),
|
|
3456
|
+
/** `request` is the Entry that asked it — what a line's `about` names when the line answered or put
|
|
3457
|
+
* it off. Optional: an older API sent none. */
|
|
3458
|
+
questions: z11.array(z11.object({ id: z11.string().uuid(), state: z11.string(), title: z11.string(), request: z11.string().optional() })),
|
|
3447
3459
|
/** `words` is always what they SAID, verbatim — the record, never replaced. `summary` is what
|
|
3448
3460
|
* the line says in ten words (owner, 2026-10-07): the filer's, or the talker's for the answer it
|
|
3449
3461
|
* gave, so the row scans like a chosen option and their own words stay under it. Absent when
|
|
@@ -3453,33 +3465,33 @@ var CallRecapSchema = z12.object({
|
|
|
3453
3465
|
/** `reply` is the reply the line makes up (an answer, an ok, a deferral), so one reply is one row
|
|
3454
3466
|
* however many lines it cites; `at` is the call lines it came from, so a filed line an answer already
|
|
3455
3467
|
* said is not drawn again (owner, 2026-10-07: "fix them all"); `replyKind` says which replies are answers. */
|
|
3456
|
-
lines:
|
|
3457
|
-
entryId:
|
|
3458
|
-
words:
|
|
3459
|
-
summary:
|
|
3460
|
-
about:
|
|
3461
|
-
reply:
|
|
3462
|
-
replyKind:
|
|
3463
|
-
at:
|
|
3468
|
+
lines: z11.array(z11.object({
|
|
3469
|
+
entryId: z11.string().uuid(),
|
|
3470
|
+
words: z11.string(),
|
|
3471
|
+
summary: z11.string().optional(),
|
|
3472
|
+
about: z11.string().nullable().optional(),
|
|
3473
|
+
reply: z11.string().nullable().optional(),
|
|
3474
|
+
replyKind: z11.string().nullable().optional(),
|
|
3475
|
+
at: z11.array(z11.number()).optional()
|
|
3464
3476
|
}))
|
|
3465
3477
|
})),
|
|
3466
|
-
unfiled:
|
|
3478
|
+
unfiled: z11.array(z11.object({ lineId: z11.string().uuid(), words: z11.string(), atMs: z11.number() })),
|
|
3467
3479
|
/** THE PUBLIC TRANSCRIPT — every line said on the call, in the order it was said, and WHO SAID IT
|
|
3468
3480
|
* (owner, 2026-10-08: "the public view should only have the lines and who said them, and that's
|
|
3469
3481
|
* it"; `docs/brain/phase-three/modular.md` build step 3). `speaker` is the role: "user" is the
|
|
3470
3482
|
* person, "agent" an agent speaking, named in `agent`. The timeline's action lines are ours, not
|
|
3471
3483
|
* theirs, and are not here; nor is a sealed line, which the server has no words for. It stands
|
|
3472
3484
|
* BESIDE `topics` and `unfiled` — what the call did with the words — never instead of them. */
|
|
3473
|
-
said:
|
|
3474
|
-
lineId:
|
|
3475
|
-
speaker:
|
|
3476
|
-
agent:
|
|
3477
|
-
words:
|
|
3478
|
-
atMs:
|
|
3485
|
+
said: z11.array(z11.object({
|
|
3486
|
+
lineId: z11.string().uuid(),
|
|
3487
|
+
speaker: z11.enum(["user", "agent"]),
|
|
3488
|
+
agent: z11.object({ id: z11.string().nullable(), name: z11.string().nullable() }).nullable(),
|
|
3489
|
+
words: z11.string(),
|
|
3490
|
+
atMs: z11.number()
|
|
3479
3491
|
})),
|
|
3480
|
-
more:
|
|
3492
|
+
more: z11.object({ lines: z11.number(), entries: z11.number(), topics: z11.number() }),
|
|
3481
3493
|
/** Last stored position on this page when another exists; absent before paging shipped. */
|
|
3482
|
-
next:
|
|
3494
|
+
next: z11.number().int().nonnegative().nullable().optional()
|
|
3483
3495
|
});
|
|
3484
3496
|
function sessionSlot(sessionId2) {
|
|
3485
3497
|
const id2 = sessionId2 ?? sessionId();
|