@paigy/mcp 0.35.0 → 0.37.0
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-YOGAG4YY.js → chunk-242XN7FD.js} +643 -489
- package/dist/chunk-PN7FDW26.js +315 -0
- package/dist/chunk-UG2FARVV.js +54 -0
- package/dist/chunk-V66NUNSQ.js +143 -0
- package/dist/{chunk-U6SLIORK.js → chunk-VXHTA4PB.js} +1974 -693
- package/dist/enable.js +3 -2
- package/dist/index.js +127 -354
- package/dist/listen.js +31 -34
- package/dist/onboard.js +62 -45
- package/dist/slot.js +7 -0
- package/dist/statusline.js +1 -1
- package/package.json +20 -18
- package/dist/chunk-KNMI3CJ3.js +0 -177
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
// ../../packages/sdk/dist/index.js
|
|
2
2
|
import { createRequire as __sdkCreateRequire } from "module";
|
|
3
|
-
import { randomUUID } from "crypto";
|
|
3
|
+
import { createHash, randomUUID } from "crypto";
|
|
4
|
+
import { z as z2 } from "zod";
|
|
4
5
|
import { z } from "zod";
|
|
6
|
+
import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind3 } from "zod/v3";
|
|
7
|
+
import { ZodFirstPartyTypeKind } from "zod/v3";
|
|
8
|
+
import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind2 } from "zod/v3";
|
|
9
|
+
import { createHash as createHash2 } from "crypto";
|
|
5
10
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
6
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
7
11
|
import { homedir } from "os";
|
|
8
12
|
import { join } from "path";
|
|
9
13
|
var require2 = __sdkCreateRequire(import.meta.url);
|
|
@@ -336,17 +340,17 @@ var require_nacl_fast = __commonJS({
|
|
|
336
340
|
}
|
|
337
341
|
var sigma = new Uint8Array([101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]);
|
|
338
342
|
function crypto_stream_salsa20_xor(c, cpos, m, mpos, b, n, k) {
|
|
339
|
-
var
|
|
343
|
+
var z3 = new Uint8Array(16), x = new Uint8Array(64);
|
|
340
344
|
var u, i;
|
|
341
|
-
for (i = 0; i < 16; i++)
|
|
342
|
-
for (i = 0; i < 8; i++)
|
|
345
|
+
for (i = 0; i < 16; i++) z3[i] = 0;
|
|
346
|
+
for (i = 0; i < 8; i++) z3[i] = n[i];
|
|
343
347
|
while (b >= 64) {
|
|
344
|
-
crypto_core_salsa20(x,
|
|
348
|
+
crypto_core_salsa20(x, z3, k, sigma);
|
|
345
349
|
for (i = 0; i < 64; i++) c[cpos + i] = m[mpos + i] ^ x[i];
|
|
346
350
|
u = 1;
|
|
347
351
|
for (i = 8; i < 16; i++) {
|
|
348
|
-
u = u + (
|
|
349
|
-
|
|
352
|
+
u = u + (z3[i] & 255) | 0;
|
|
353
|
+
z3[i] = u & 255;
|
|
350
354
|
u >>>= 8;
|
|
351
355
|
}
|
|
352
356
|
b -= 64;
|
|
@@ -354,30 +358,30 @@ var require_nacl_fast = __commonJS({
|
|
|
354
358
|
mpos += 64;
|
|
355
359
|
}
|
|
356
360
|
if (b > 0) {
|
|
357
|
-
crypto_core_salsa20(x,
|
|
361
|
+
crypto_core_salsa20(x, z3, k, sigma);
|
|
358
362
|
for (i = 0; i < b; i++) c[cpos + i] = m[mpos + i] ^ x[i];
|
|
359
363
|
}
|
|
360
364
|
return 0;
|
|
361
365
|
}
|
|
362
366
|
function crypto_stream_salsa20(c, cpos, b, n, k) {
|
|
363
|
-
var
|
|
367
|
+
var z3 = new Uint8Array(16), x = new Uint8Array(64);
|
|
364
368
|
var u, i;
|
|
365
|
-
for (i = 0; i < 16; i++)
|
|
366
|
-
for (i = 0; i < 8; i++)
|
|
369
|
+
for (i = 0; i < 16; i++) z3[i] = 0;
|
|
370
|
+
for (i = 0; i < 8; i++) z3[i] = n[i];
|
|
367
371
|
while (b >= 64) {
|
|
368
|
-
crypto_core_salsa20(x,
|
|
372
|
+
crypto_core_salsa20(x, z3, k, sigma);
|
|
369
373
|
for (i = 0; i < 64; i++) c[cpos + i] = x[i];
|
|
370
374
|
u = 1;
|
|
371
375
|
for (i = 8; i < 16; i++) {
|
|
372
|
-
u = u + (
|
|
373
|
-
|
|
376
|
+
u = u + (z3[i] & 255) | 0;
|
|
377
|
+
z3[i] = u & 255;
|
|
374
378
|
u >>>= 8;
|
|
375
379
|
}
|
|
376
380
|
b -= 64;
|
|
377
381
|
cpos += 64;
|
|
378
382
|
}
|
|
379
383
|
if (b > 0) {
|
|
380
|
-
crypto_core_salsa20(x,
|
|
384
|
+
crypto_core_salsa20(x, z3, k, sigma);
|
|
381
385
|
for (i = 0; i < b; i++) c[cpos + i] = x[i];
|
|
382
386
|
}
|
|
383
387
|
return 0;
|
|
@@ -1257,12 +1261,12 @@ var require_nacl_fast = __commonJS({
|
|
|
1257
1261
|
for (a = 0; a < 16; a++) o[a] = c[a];
|
|
1258
1262
|
}
|
|
1259
1263
|
function crypto_scalarmult(q, n, p) {
|
|
1260
|
-
var
|
|
1264
|
+
var z3 = new Uint8Array(32);
|
|
1261
1265
|
var x = new Float64Array(80), r, i;
|
|
1262
1266
|
var a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf();
|
|
1263
|
-
for (i = 0; i < 31; i++)
|
|
1264
|
-
|
|
1265
|
-
|
|
1267
|
+
for (i = 0; i < 31; i++) z3[i] = n[i];
|
|
1268
|
+
z3[31] = n[31] & 127 | 64;
|
|
1269
|
+
z3[0] &= 248;
|
|
1266
1270
|
unpack25519(x, p);
|
|
1267
1271
|
for (i = 0; i < 16; i++) {
|
|
1268
1272
|
b[i] = x[i];
|
|
@@ -1270,7 +1274,7 @@ var require_nacl_fast = __commonJS({
|
|
|
1270
1274
|
}
|
|
1271
1275
|
a[0] = d[0] = 1;
|
|
1272
1276
|
for (i = 254; i >= 0; --i) {
|
|
1273
|
-
r =
|
|
1277
|
+
r = z3[i >>> 3] >>> (i & 7) & 1;
|
|
1274
1278
|
sel25519(a, b, r);
|
|
1275
1279
|
sel25519(c, d, r);
|
|
1276
1280
|
A(e, a, c);
|
|
@@ -2270,7 +2274,24 @@ async function proxy() {
|
|
|
2270
2274
|
return agent ??= new (await import("undici")).EnvHttpProxyAgent();
|
|
2271
2275
|
}
|
|
2272
2276
|
var INSTANCE_ID = randomUUID();
|
|
2273
|
-
var
|
|
2277
|
+
var WORKSPACE_ID = (() => {
|
|
2278
|
+
try {
|
|
2279
|
+
return `cwd-${createHash("sha256").update(process.cwd()).digest("hex").slice(0, 16)}`;
|
|
2280
|
+
} catch {
|
|
2281
|
+
return INSTANCE_ID;
|
|
2282
|
+
}
|
|
2283
|
+
})();
|
|
2284
|
+
var envSession = (key) => {
|
|
2285
|
+
const v = process.env[key];
|
|
2286
|
+
return v && v.trim() ? v : void 0;
|
|
2287
|
+
};
|
|
2288
|
+
var SESSION_ID = envSession("PAIGY_SESSION_ID") ?? envSession("CLAUDE_CODE_SESSION_ID") ?? envSession("CODEX_THREAD_ID") ?? envSession("CODEX_SESSION_ID") ?? WORKSPACE_ID;
|
|
2289
|
+
function sessionId() {
|
|
2290
|
+
return SESSION_ID;
|
|
2291
|
+
}
|
|
2292
|
+
function isNetworkError(e) {
|
|
2293
|
+
return e instanceof Error && e.message.startsWith(NETWORK_MSG);
|
|
2294
|
+
}
|
|
2274
2295
|
async function reach(url, init) {
|
|
2275
2296
|
try {
|
|
2276
2297
|
const headers = {
|
|
@@ -2283,73 +2304,1310 @@ async function reach(url, init) {
|
|
|
2283
2304
|
throw new Error(`${NETWORK_MSG} (${e?.message ?? String(e)})`);
|
|
2284
2305
|
}
|
|
2285
2306
|
}
|
|
2286
|
-
var
|
|
2287
|
-
var
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2307
|
+
var ignoreOverride = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use");
|
|
2308
|
+
var defaultOptions = {
|
|
2309
|
+
name: void 0,
|
|
2310
|
+
$refStrategy: "root",
|
|
2311
|
+
basePath: ["#"],
|
|
2312
|
+
effectStrategy: "input",
|
|
2313
|
+
pipeStrategy: "all",
|
|
2314
|
+
dateStrategy: "format:date-time",
|
|
2315
|
+
mapStrategy: "entries",
|
|
2316
|
+
removeAdditionalStrategy: "passthrough",
|
|
2317
|
+
allowedAdditionalProperties: true,
|
|
2318
|
+
rejectedAdditionalProperties: false,
|
|
2319
|
+
definitionPath: "definitions",
|
|
2320
|
+
target: "jsonSchema7",
|
|
2321
|
+
strictUnions: false,
|
|
2322
|
+
definitions: {},
|
|
2323
|
+
errorMessages: false,
|
|
2324
|
+
markdownDescription: false,
|
|
2325
|
+
patternStrategy: "escape",
|
|
2326
|
+
applyRegexFlags: false,
|
|
2327
|
+
emailStrategy: "format:email",
|
|
2328
|
+
base64Strategy: "contentEncoding:base64",
|
|
2329
|
+
nameStrategy: "ref",
|
|
2330
|
+
openAiAnyTypeName: "OpenAiAnyType"
|
|
2331
|
+
};
|
|
2332
|
+
var getDefaultOptions = (options) => typeof options === "string" ? {
|
|
2333
|
+
...defaultOptions,
|
|
2334
|
+
name: options
|
|
2335
|
+
} : {
|
|
2336
|
+
...defaultOptions,
|
|
2337
|
+
...options
|
|
2338
|
+
};
|
|
2339
|
+
var getRefs = (options) => {
|
|
2340
|
+
const _options = getDefaultOptions(options);
|
|
2341
|
+
const currentPath = _options.name !== void 0 ? [..._options.basePath, _options.definitionPath, _options.name] : _options.basePath;
|
|
2342
|
+
return {
|
|
2343
|
+
..._options,
|
|
2344
|
+
flags: { hasReferencedOpenAiAnyType: false },
|
|
2345
|
+
currentPath,
|
|
2346
|
+
propertyPath: void 0,
|
|
2347
|
+
seen: new Map(Object.entries(_options.definitions).map(([name, def]) => [
|
|
2348
|
+
def._def,
|
|
2349
|
+
{
|
|
2350
|
+
def: def._def,
|
|
2351
|
+
path: [..._options.basePath, _options.definitionPath, name],
|
|
2352
|
+
// Resolution of references will be forced even though seen, so it's ok that the schema is undefined here for now.
|
|
2353
|
+
jsonSchema: void 0
|
|
2354
|
+
}
|
|
2355
|
+
]))
|
|
2356
|
+
};
|
|
2357
|
+
};
|
|
2358
|
+
function addErrorMessage(res, key, errorMessage, refs) {
|
|
2359
|
+
if (!refs?.errorMessages)
|
|
2360
|
+
return;
|
|
2361
|
+
if (errorMessage) {
|
|
2362
|
+
res.errorMessage = {
|
|
2363
|
+
...res.errorMessage,
|
|
2364
|
+
[key]: errorMessage
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
|
|
2369
|
+
res[key] = value;
|
|
2370
|
+
addErrorMessage(res, key, errorMessage, refs);
|
|
2371
|
+
}
|
|
2372
|
+
var getRelativePath = (pathA, pathB) => {
|
|
2373
|
+
let i = 0;
|
|
2374
|
+
for (; i < pathA.length && i < pathB.length; i++) {
|
|
2375
|
+
if (pathA[i] !== pathB[i])
|
|
2376
|
+
break;
|
|
2377
|
+
}
|
|
2378
|
+
return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/");
|
|
2379
|
+
};
|
|
2380
|
+
function parseAnyDef(refs) {
|
|
2381
|
+
if (refs.target !== "openAi") {
|
|
2382
|
+
return {};
|
|
2383
|
+
}
|
|
2384
|
+
const anyDefinitionPath = [
|
|
2385
|
+
...refs.basePath,
|
|
2386
|
+
refs.definitionPath,
|
|
2387
|
+
refs.openAiAnyTypeName
|
|
2388
|
+
];
|
|
2389
|
+
refs.flags.hasReferencedOpenAiAnyType = true;
|
|
2390
|
+
return {
|
|
2391
|
+
$ref: refs.$refStrategy === "relative" ? getRelativePath(anyDefinitionPath, refs.currentPath) : anyDefinitionPath.join("/")
|
|
2392
|
+
};
|
|
2393
|
+
}
|
|
2394
|
+
function parseArrayDef(def, refs) {
|
|
2395
|
+
const res = {
|
|
2396
|
+
type: "array"
|
|
2397
|
+
};
|
|
2398
|
+
if (def.type?._def && def.type?._def?.typeName !== ZodFirstPartyTypeKind.ZodAny) {
|
|
2399
|
+
res.items = parseDef(def.type._def, {
|
|
2400
|
+
...refs,
|
|
2401
|
+
currentPath: [...refs.currentPath, "items"]
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
if (def.minLength) {
|
|
2405
|
+
setResponseValueAndErrors(res, "minItems", def.minLength.value, def.minLength.message, refs);
|
|
2406
|
+
}
|
|
2407
|
+
if (def.maxLength) {
|
|
2408
|
+
setResponseValueAndErrors(res, "maxItems", def.maxLength.value, def.maxLength.message, refs);
|
|
2409
|
+
}
|
|
2410
|
+
if (def.exactLength) {
|
|
2411
|
+
setResponseValueAndErrors(res, "minItems", def.exactLength.value, def.exactLength.message, refs);
|
|
2412
|
+
setResponseValueAndErrors(res, "maxItems", def.exactLength.value, def.exactLength.message, refs);
|
|
2413
|
+
}
|
|
2414
|
+
return res;
|
|
2415
|
+
}
|
|
2416
|
+
function parseBigintDef(def, refs) {
|
|
2417
|
+
const res = {
|
|
2418
|
+
type: "integer",
|
|
2419
|
+
format: "int64"
|
|
2420
|
+
};
|
|
2421
|
+
if (!def.checks)
|
|
2422
|
+
return res;
|
|
2423
|
+
for (const check of def.checks) {
|
|
2424
|
+
switch (check.kind) {
|
|
2425
|
+
case "min":
|
|
2426
|
+
if (refs.target === "jsonSchema7") {
|
|
2427
|
+
if (check.inclusive) {
|
|
2428
|
+
setResponseValueAndErrors(res, "minimum", check.value, check.message, refs);
|
|
2429
|
+
} else {
|
|
2430
|
+
setResponseValueAndErrors(res, "exclusiveMinimum", check.value, check.message, refs);
|
|
2431
|
+
}
|
|
2432
|
+
} else {
|
|
2433
|
+
if (!check.inclusive) {
|
|
2434
|
+
res.exclusiveMinimum = true;
|
|
2435
|
+
}
|
|
2436
|
+
setResponseValueAndErrors(res, "minimum", check.value, check.message, refs);
|
|
2437
|
+
}
|
|
2438
|
+
break;
|
|
2439
|
+
case "max":
|
|
2440
|
+
if (refs.target === "jsonSchema7") {
|
|
2441
|
+
if (check.inclusive) {
|
|
2442
|
+
setResponseValueAndErrors(res, "maximum", check.value, check.message, refs);
|
|
2443
|
+
} else {
|
|
2444
|
+
setResponseValueAndErrors(res, "exclusiveMaximum", check.value, check.message, refs);
|
|
2445
|
+
}
|
|
2446
|
+
} else {
|
|
2447
|
+
if (!check.inclusive) {
|
|
2448
|
+
res.exclusiveMaximum = true;
|
|
2449
|
+
}
|
|
2450
|
+
setResponseValueAndErrors(res, "maximum", check.value, check.message, refs);
|
|
2451
|
+
}
|
|
2452
|
+
break;
|
|
2453
|
+
case "multipleOf":
|
|
2454
|
+
setResponseValueAndErrors(res, "multipleOf", check.value, check.message, refs);
|
|
2455
|
+
break;
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
return res;
|
|
2459
|
+
}
|
|
2460
|
+
function parseBooleanDef() {
|
|
2295
2461
|
return {
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2462
|
+
type: "boolean"
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
function parseBrandedDef(_def, refs) {
|
|
2466
|
+
return parseDef(_def.type._def, refs);
|
|
2467
|
+
}
|
|
2468
|
+
var parseCatchDef = (def, refs) => {
|
|
2469
|
+
return parseDef(def.innerType._def, refs);
|
|
2470
|
+
};
|
|
2471
|
+
function parseDateDef(def, refs, overrideDateStrategy) {
|
|
2472
|
+
const strategy = overrideDateStrategy ?? refs.dateStrategy;
|
|
2473
|
+
if (Array.isArray(strategy)) {
|
|
2474
|
+
return {
|
|
2475
|
+
anyOf: strategy.map((item, i) => parseDateDef(def, refs, item))
|
|
2476
|
+
};
|
|
2477
|
+
}
|
|
2478
|
+
switch (strategy) {
|
|
2479
|
+
case "string":
|
|
2480
|
+
case "format:date-time":
|
|
2481
|
+
return {
|
|
2482
|
+
type: "string",
|
|
2483
|
+
format: "date-time"
|
|
2484
|
+
};
|
|
2485
|
+
case "format:date":
|
|
2486
|
+
return {
|
|
2487
|
+
type: "string",
|
|
2488
|
+
format: "date"
|
|
2489
|
+
};
|
|
2490
|
+
case "integer":
|
|
2491
|
+
return integerDateParser(def, refs);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
var integerDateParser = (def, refs) => {
|
|
2495
|
+
const res = {
|
|
2496
|
+
type: "integer",
|
|
2497
|
+
format: "unix-time"
|
|
2498
|
+
};
|
|
2499
|
+
if (refs.target === "openApi3") {
|
|
2500
|
+
return res;
|
|
2501
|
+
}
|
|
2502
|
+
for (const check of def.checks) {
|
|
2503
|
+
switch (check.kind) {
|
|
2504
|
+
case "min":
|
|
2505
|
+
setResponseValueAndErrors(
|
|
2506
|
+
res,
|
|
2507
|
+
"minimum",
|
|
2508
|
+
check.value,
|
|
2509
|
+
// This is in milliseconds
|
|
2510
|
+
check.message,
|
|
2511
|
+
refs
|
|
2512
|
+
);
|
|
2513
|
+
break;
|
|
2514
|
+
case "max":
|
|
2515
|
+
setResponseValueAndErrors(
|
|
2516
|
+
res,
|
|
2517
|
+
"maximum",
|
|
2518
|
+
check.value,
|
|
2519
|
+
// This is in milliseconds
|
|
2520
|
+
check.message,
|
|
2521
|
+
refs
|
|
2522
|
+
);
|
|
2523
|
+
break;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
return res;
|
|
2527
|
+
};
|
|
2528
|
+
function parseDefaultDef(_def, refs) {
|
|
2529
|
+
return {
|
|
2530
|
+
...parseDef(_def.innerType._def, refs),
|
|
2531
|
+
default: _def.defaultValue()
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
function parseEffectsDef(_def, refs) {
|
|
2535
|
+
return refs.effectStrategy === "input" ? parseDef(_def.schema._def, refs) : parseAnyDef(refs);
|
|
2536
|
+
}
|
|
2537
|
+
function parseEnumDef(def) {
|
|
2538
|
+
return {
|
|
2539
|
+
type: "string",
|
|
2540
|
+
enum: Array.from(def.values)
|
|
2541
|
+
};
|
|
2542
|
+
}
|
|
2543
|
+
var isJsonSchema7AllOfType = (type) => {
|
|
2544
|
+
if ("type" in type && type.type === "string")
|
|
2545
|
+
return false;
|
|
2546
|
+
return "allOf" in type;
|
|
2547
|
+
};
|
|
2548
|
+
function parseIntersectionDef(def, refs) {
|
|
2549
|
+
const allOf = [
|
|
2550
|
+
parseDef(def.left._def, {
|
|
2551
|
+
...refs,
|
|
2552
|
+
currentPath: [...refs.currentPath, "allOf", "0"]
|
|
2553
|
+
}),
|
|
2554
|
+
parseDef(def.right._def, {
|
|
2555
|
+
...refs,
|
|
2556
|
+
currentPath: [...refs.currentPath, "allOf", "1"]
|
|
2557
|
+
})
|
|
2558
|
+
].filter((x) => !!x);
|
|
2559
|
+
let unevaluatedProperties = refs.target === "jsonSchema2019-09" ? { unevaluatedProperties: false } : void 0;
|
|
2560
|
+
const mergedAllOf = [];
|
|
2561
|
+
allOf.forEach((schema) => {
|
|
2562
|
+
if (isJsonSchema7AllOfType(schema)) {
|
|
2563
|
+
mergedAllOf.push(...schema.allOf);
|
|
2564
|
+
if (schema.unevaluatedProperties === void 0) {
|
|
2565
|
+
unevaluatedProperties = void 0;
|
|
2566
|
+
}
|
|
2567
|
+
} else {
|
|
2568
|
+
let nestedSchema = schema;
|
|
2569
|
+
if ("additionalProperties" in schema && schema.additionalProperties === false) {
|
|
2570
|
+
const { additionalProperties, ...rest } = schema;
|
|
2571
|
+
nestedSchema = rest;
|
|
2572
|
+
} else {
|
|
2573
|
+
unevaluatedProperties = void 0;
|
|
2574
|
+
}
|
|
2575
|
+
mergedAllOf.push(nestedSchema);
|
|
2576
|
+
}
|
|
2577
|
+
});
|
|
2578
|
+
return mergedAllOf.length ? {
|
|
2579
|
+
allOf: mergedAllOf,
|
|
2580
|
+
...unevaluatedProperties
|
|
2581
|
+
} : void 0;
|
|
2582
|
+
}
|
|
2583
|
+
function parseLiteralDef(def, refs) {
|
|
2584
|
+
const parsedType = typeof def.value;
|
|
2585
|
+
if (parsedType !== "bigint" && parsedType !== "number" && parsedType !== "boolean" && parsedType !== "string") {
|
|
2586
|
+
return {
|
|
2587
|
+
type: Array.isArray(def.value) ? "array" : "object"
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2590
|
+
if (refs.target === "openApi3") {
|
|
2591
|
+
return {
|
|
2592
|
+
type: parsedType === "bigint" ? "integer" : parsedType,
|
|
2593
|
+
enum: [def.value]
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
return {
|
|
2597
|
+
type: parsedType === "bigint" ? "integer" : parsedType,
|
|
2598
|
+
const: def.value
|
|
2599
|
+
};
|
|
2600
|
+
}
|
|
2601
|
+
var emojiRegex = void 0;
|
|
2602
|
+
var zodPatterns = {
|
|
2603
|
+
/**
|
|
2604
|
+
* `c` was changed to `[cC]` to replicate /i flag
|
|
2605
|
+
*/
|
|
2606
|
+
cuid: /^[cC][^\s-]{8,}$/,
|
|
2607
|
+
cuid2: /^[0-9a-z]+$/,
|
|
2608
|
+
ulid: /^[0-9A-HJKMNP-TV-Z]{26}$/,
|
|
2609
|
+
/**
|
|
2610
|
+
* `a-z` was added to replicate /i flag
|
|
2611
|
+
*/
|
|
2612
|
+
email: /^(?!\.)(?!.*\.\.)([a-zA-Z0-9_'+\-\.]*)[a-zA-Z0-9_+-]@([a-zA-Z0-9][a-zA-Z0-9\-]*\.)+[a-zA-Z]{2,}$/,
|
|
2613
|
+
/**
|
|
2614
|
+
* Constructed a valid Unicode RegExp
|
|
2615
|
+
*
|
|
2616
|
+
* Lazily instantiate since this type of regex isn't supported
|
|
2617
|
+
* in all envs (e.g. React Native).
|
|
2618
|
+
*
|
|
2619
|
+
* See:
|
|
2620
|
+
* https://github.com/colinhacks/zod/issues/2433
|
|
2621
|
+
* Fix in Zod:
|
|
2622
|
+
* https://github.com/colinhacks/zod/commit/9340fd51e48576a75adc919bff65dbc4a5d4c99b
|
|
2623
|
+
*/
|
|
2624
|
+
emoji: () => {
|
|
2625
|
+
if (emojiRegex === void 0) {
|
|
2626
|
+
emojiRegex = RegExp("^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$", "u");
|
|
2627
|
+
}
|
|
2628
|
+
return emojiRegex;
|
|
2629
|
+
},
|
|
2630
|
+
/**
|
|
2631
|
+
* Unused
|
|
2632
|
+
*/
|
|
2633
|
+
uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/,
|
|
2634
|
+
/**
|
|
2635
|
+
* Unused
|
|
2636
|
+
*/
|
|
2637
|
+
ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,
|
|
2638
|
+
ipv4Cidr: /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/,
|
|
2639
|
+
/**
|
|
2640
|
+
* Unused
|
|
2641
|
+
*/
|
|
2642
|
+
ipv6: /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/,
|
|
2643
|
+
ipv6Cidr: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,
|
|
2644
|
+
base64: /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,
|
|
2645
|
+
base64url: /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,
|
|
2646
|
+
nanoid: /^[a-zA-Z0-9_-]{21}$/,
|
|
2647
|
+
jwt: /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/
|
|
2648
|
+
};
|
|
2649
|
+
function parseStringDef(def, refs) {
|
|
2650
|
+
const res = {
|
|
2651
|
+
type: "string"
|
|
2652
|
+
};
|
|
2653
|
+
if (def.checks) {
|
|
2654
|
+
for (const check of def.checks) {
|
|
2655
|
+
switch (check.kind) {
|
|
2656
|
+
case "min":
|
|
2657
|
+
setResponseValueAndErrors(res, "minLength", typeof res.minLength === "number" ? Math.max(res.minLength, check.value) : check.value, check.message, refs);
|
|
2658
|
+
break;
|
|
2659
|
+
case "max":
|
|
2660
|
+
setResponseValueAndErrors(res, "maxLength", typeof res.maxLength === "number" ? Math.min(res.maxLength, check.value) : check.value, check.message, refs);
|
|
2661
|
+
break;
|
|
2662
|
+
case "email":
|
|
2663
|
+
switch (refs.emailStrategy) {
|
|
2664
|
+
case "format:email":
|
|
2665
|
+
addFormat(res, "email", check.message, refs);
|
|
2666
|
+
break;
|
|
2667
|
+
case "format:idn-email":
|
|
2668
|
+
addFormat(res, "idn-email", check.message, refs);
|
|
2669
|
+
break;
|
|
2670
|
+
case "pattern:zod":
|
|
2671
|
+
addPattern(res, zodPatterns.email, check.message, refs);
|
|
2672
|
+
break;
|
|
2673
|
+
}
|
|
2674
|
+
break;
|
|
2675
|
+
case "url":
|
|
2676
|
+
addFormat(res, "uri", check.message, refs);
|
|
2677
|
+
break;
|
|
2678
|
+
case "uuid":
|
|
2679
|
+
addFormat(res, "uuid", check.message, refs);
|
|
2680
|
+
break;
|
|
2681
|
+
case "regex":
|
|
2682
|
+
addPattern(res, check.regex, check.message, refs);
|
|
2683
|
+
break;
|
|
2684
|
+
case "cuid":
|
|
2685
|
+
addPattern(res, zodPatterns.cuid, check.message, refs);
|
|
2686
|
+
break;
|
|
2687
|
+
case "cuid2":
|
|
2688
|
+
addPattern(res, zodPatterns.cuid2, check.message, refs);
|
|
2689
|
+
break;
|
|
2690
|
+
case "startsWith":
|
|
2691
|
+
addPattern(res, RegExp(`^${escapeLiteralCheckValue(check.value, refs)}`), check.message, refs);
|
|
2692
|
+
break;
|
|
2693
|
+
case "endsWith":
|
|
2694
|
+
addPattern(res, RegExp(`${escapeLiteralCheckValue(check.value, refs)}$`), check.message, refs);
|
|
2695
|
+
break;
|
|
2696
|
+
case "datetime":
|
|
2697
|
+
addFormat(res, "date-time", check.message, refs);
|
|
2698
|
+
break;
|
|
2699
|
+
case "date":
|
|
2700
|
+
addFormat(res, "date", check.message, refs);
|
|
2701
|
+
break;
|
|
2702
|
+
case "time":
|
|
2703
|
+
addFormat(res, "time", check.message, refs);
|
|
2704
|
+
break;
|
|
2705
|
+
case "duration":
|
|
2706
|
+
addFormat(res, "duration", check.message, refs);
|
|
2707
|
+
break;
|
|
2708
|
+
case "length":
|
|
2709
|
+
setResponseValueAndErrors(res, "minLength", typeof res.minLength === "number" ? Math.max(res.minLength, check.value) : check.value, check.message, refs);
|
|
2710
|
+
setResponseValueAndErrors(res, "maxLength", typeof res.maxLength === "number" ? Math.min(res.maxLength, check.value) : check.value, check.message, refs);
|
|
2711
|
+
break;
|
|
2712
|
+
case "includes": {
|
|
2713
|
+
addPattern(res, RegExp(escapeLiteralCheckValue(check.value, refs)), check.message, refs);
|
|
2714
|
+
break;
|
|
2715
|
+
}
|
|
2716
|
+
case "ip": {
|
|
2717
|
+
if (check.version !== "v6") {
|
|
2718
|
+
addFormat(res, "ipv4", check.message, refs);
|
|
2719
|
+
}
|
|
2720
|
+
if (check.version !== "v4") {
|
|
2721
|
+
addFormat(res, "ipv6", check.message, refs);
|
|
2722
|
+
}
|
|
2723
|
+
break;
|
|
2724
|
+
}
|
|
2725
|
+
case "base64url":
|
|
2726
|
+
addPattern(res, zodPatterns.base64url, check.message, refs);
|
|
2727
|
+
break;
|
|
2728
|
+
case "jwt":
|
|
2729
|
+
addPattern(res, zodPatterns.jwt, check.message, refs);
|
|
2730
|
+
break;
|
|
2731
|
+
case "cidr": {
|
|
2732
|
+
if (check.version !== "v6") {
|
|
2733
|
+
addPattern(res, zodPatterns.ipv4Cidr, check.message, refs);
|
|
2734
|
+
}
|
|
2735
|
+
if (check.version !== "v4") {
|
|
2736
|
+
addPattern(res, zodPatterns.ipv6Cidr, check.message, refs);
|
|
2737
|
+
}
|
|
2738
|
+
break;
|
|
2739
|
+
}
|
|
2740
|
+
case "emoji":
|
|
2741
|
+
addPattern(res, zodPatterns.emoji(), check.message, refs);
|
|
2742
|
+
break;
|
|
2743
|
+
case "ulid": {
|
|
2744
|
+
addPattern(res, zodPatterns.ulid, check.message, refs);
|
|
2745
|
+
break;
|
|
2746
|
+
}
|
|
2747
|
+
case "base64": {
|
|
2748
|
+
switch (refs.base64Strategy) {
|
|
2749
|
+
case "format:binary": {
|
|
2750
|
+
addFormat(res, "binary", check.message, refs);
|
|
2751
|
+
break;
|
|
2752
|
+
}
|
|
2753
|
+
case "contentEncoding:base64": {
|
|
2754
|
+
setResponseValueAndErrors(res, "contentEncoding", "base64", check.message, refs);
|
|
2755
|
+
break;
|
|
2756
|
+
}
|
|
2757
|
+
case "pattern:zod": {
|
|
2758
|
+
addPattern(res, zodPatterns.base64, check.message, refs);
|
|
2759
|
+
break;
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
break;
|
|
2763
|
+
}
|
|
2764
|
+
case "nanoid": {
|
|
2765
|
+
addPattern(res, zodPatterns.nanoid, check.message, refs);
|
|
2766
|
+
}
|
|
2767
|
+
case "toLowerCase":
|
|
2768
|
+
case "toUpperCase":
|
|
2769
|
+
case "trim":
|
|
2770
|
+
break;
|
|
2771
|
+
default:
|
|
2772
|
+
/* @__PURE__ */ ((_) => {
|
|
2773
|
+
})(check);
|
|
2303
2774
|
}
|
|
2304
|
-
}
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
return res;
|
|
2778
|
+
}
|
|
2779
|
+
function escapeLiteralCheckValue(literal, refs) {
|
|
2780
|
+
return refs.patternStrategy === "escape" ? escapeNonAlphaNumeric(literal) : literal;
|
|
2781
|
+
}
|
|
2782
|
+
var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
|
|
2783
|
+
function escapeNonAlphaNumeric(source) {
|
|
2784
|
+
let result = "";
|
|
2785
|
+
for (let i = 0; i < source.length; i++) {
|
|
2786
|
+
if (!ALPHA_NUMERIC.has(source[i])) {
|
|
2787
|
+
result += "\\";
|
|
2788
|
+
}
|
|
2789
|
+
result += source[i];
|
|
2790
|
+
}
|
|
2791
|
+
return result;
|
|
2792
|
+
}
|
|
2793
|
+
function addFormat(schema, value, message, refs) {
|
|
2794
|
+
if (schema.format || schema.anyOf?.some((x) => x.format)) {
|
|
2795
|
+
if (!schema.anyOf) {
|
|
2796
|
+
schema.anyOf = [];
|
|
2797
|
+
}
|
|
2798
|
+
if (schema.format) {
|
|
2799
|
+
schema.anyOf.push({
|
|
2800
|
+
format: schema.format,
|
|
2801
|
+
...schema.errorMessage && refs.errorMessages && {
|
|
2802
|
+
errorMessage: { format: schema.errorMessage.format }
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
delete schema.format;
|
|
2806
|
+
if (schema.errorMessage) {
|
|
2807
|
+
delete schema.errorMessage.format;
|
|
2808
|
+
if (Object.keys(schema.errorMessage).length === 0) {
|
|
2809
|
+
delete schema.errorMessage;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
schema.anyOf.push({
|
|
2814
|
+
format: value,
|
|
2815
|
+
...message && refs.errorMessages && { errorMessage: { format: message } }
|
|
2816
|
+
});
|
|
2817
|
+
} else {
|
|
2818
|
+
setResponseValueAndErrors(schema, "format", value, message, refs);
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
function addPattern(schema, regex, message, refs) {
|
|
2822
|
+
if (schema.pattern || schema.allOf?.some((x) => x.pattern)) {
|
|
2823
|
+
if (!schema.allOf) {
|
|
2824
|
+
schema.allOf = [];
|
|
2825
|
+
}
|
|
2826
|
+
if (schema.pattern) {
|
|
2827
|
+
schema.allOf.push({
|
|
2828
|
+
pattern: schema.pattern,
|
|
2829
|
+
...schema.errorMessage && refs.errorMessages && {
|
|
2830
|
+
errorMessage: { pattern: schema.errorMessage.pattern }
|
|
2831
|
+
}
|
|
2832
|
+
});
|
|
2833
|
+
delete schema.pattern;
|
|
2834
|
+
if (schema.errorMessage) {
|
|
2835
|
+
delete schema.errorMessage.pattern;
|
|
2836
|
+
if (Object.keys(schema.errorMessage).length === 0) {
|
|
2837
|
+
delete schema.errorMessage;
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
schema.allOf.push({
|
|
2842
|
+
pattern: stringifyRegExpWithFlags(regex, refs),
|
|
2843
|
+
...message && refs.errorMessages && { errorMessage: { pattern: message } }
|
|
2844
|
+
});
|
|
2845
|
+
} else {
|
|
2846
|
+
setResponseValueAndErrors(schema, "pattern", stringifyRegExpWithFlags(regex, refs), message, refs);
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
function stringifyRegExpWithFlags(regex, refs) {
|
|
2850
|
+
if (!refs.applyRegexFlags || !regex.flags) {
|
|
2851
|
+
return regex.source;
|
|
2852
|
+
}
|
|
2853
|
+
const flags = {
|
|
2854
|
+
i: regex.flags.includes("i"),
|
|
2855
|
+
m: regex.flags.includes("m"),
|
|
2856
|
+
s: regex.flags.includes("s")
|
|
2857
|
+
// `.` matches newlines
|
|
2858
|
+
};
|
|
2859
|
+
const source = flags.i ? regex.source.toLowerCase() : regex.source;
|
|
2860
|
+
let pattern = "";
|
|
2861
|
+
let isEscaped = false;
|
|
2862
|
+
let inCharGroup = false;
|
|
2863
|
+
let inCharRange = false;
|
|
2864
|
+
for (let i = 0; i < source.length; i++) {
|
|
2865
|
+
if (isEscaped) {
|
|
2866
|
+
pattern += source[i];
|
|
2867
|
+
isEscaped = false;
|
|
2868
|
+
continue;
|
|
2869
|
+
}
|
|
2870
|
+
if (flags.i) {
|
|
2871
|
+
if (inCharGroup) {
|
|
2872
|
+
if (source[i].match(/[a-z]/)) {
|
|
2873
|
+
if (inCharRange) {
|
|
2874
|
+
pattern += source[i];
|
|
2875
|
+
pattern += `${source[i - 2]}-${source[i]}`.toUpperCase();
|
|
2876
|
+
inCharRange = false;
|
|
2877
|
+
} else if (source[i + 1] === "-" && source[i + 2]?.match(/[a-z]/)) {
|
|
2878
|
+
pattern += source[i];
|
|
2879
|
+
inCharRange = true;
|
|
2880
|
+
} else {
|
|
2881
|
+
pattern += `${source[i]}${source[i].toUpperCase()}`;
|
|
2882
|
+
}
|
|
2883
|
+
continue;
|
|
2884
|
+
}
|
|
2885
|
+
} else if (source[i].match(/[a-z]/)) {
|
|
2886
|
+
pattern += `[${source[i]}${source[i].toUpperCase()}]`;
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
if (flags.m) {
|
|
2891
|
+
if (source[i] === "^") {
|
|
2892
|
+
pattern += `(^|(?<=[\r
|
|
2893
|
+
]))`;
|
|
2894
|
+
continue;
|
|
2895
|
+
} else if (source[i] === "$") {
|
|
2896
|
+
pattern += `($|(?=[\r
|
|
2897
|
+
]))`;
|
|
2898
|
+
continue;
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
if (flags.s && source[i] === ".") {
|
|
2902
|
+
pattern += inCharGroup ? `${source[i]}\r
|
|
2903
|
+
` : `[${source[i]}\r
|
|
2904
|
+
]`;
|
|
2905
|
+
continue;
|
|
2906
|
+
}
|
|
2907
|
+
pattern += source[i];
|
|
2908
|
+
if (source[i] === "\\") {
|
|
2909
|
+
isEscaped = true;
|
|
2910
|
+
} else if (inCharGroup && source[i] === "]") {
|
|
2911
|
+
inCharGroup = false;
|
|
2912
|
+
} else if (!inCharGroup && source[i] === "[") {
|
|
2913
|
+
inCharGroup = true;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
try {
|
|
2917
|
+
new RegExp(pattern);
|
|
2918
|
+
} catch {
|
|
2919
|
+
console.warn(`Could not convert regex pattern at ${refs.currentPath.join("/")} to a flag-independent form! Falling back to the flag-ignorant source`);
|
|
2920
|
+
return regex.source;
|
|
2921
|
+
}
|
|
2922
|
+
return pattern;
|
|
2923
|
+
}
|
|
2924
|
+
function parseRecordDef(def, refs) {
|
|
2925
|
+
if (refs.target === "openAi") {
|
|
2926
|
+
console.warn("Warning: OpenAI may not support records in schemas! Try an array of key-value pairs instead.");
|
|
2927
|
+
}
|
|
2928
|
+
if (refs.target === "openApi3" && def.keyType?._def.typeName === ZodFirstPartyTypeKind2.ZodEnum) {
|
|
2929
|
+
return {
|
|
2930
|
+
type: "object",
|
|
2931
|
+
required: def.keyType._def.values,
|
|
2932
|
+
properties: def.keyType._def.values.reduce((acc, key) => ({
|
|
2933
|
+
...acc,
|
|
2934
|
+
[key]: parseDef(def.valueType._def, {
|
|
2935
|
+
...refs,
|
|
2936
|
+
currentPath: [...refs.currentPath, "properties", key]
|
|
2937
|
+
}) ?? parseAnyDef(refs)
|
|
2938
|
+
}), {}),
|
|
2939
|
+
additionalProperties: refs.rejectedAdditionalProperties
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
const schema = {
|
|
2943
|
+
type: "object",
|
|
2944
|
+
additionalProperties: parseDef(def.valueType._def, {
|
|
2945
|
+
...refs,
|
|
2946
|
+
currentPath: [...refs.currentPath, "additionalProperties"]
|
|
2947
|
+
}) ?? refs.allowedAdditionalProperties
|
|
2948
|
+
};
|
|
2949
|
+
if (refs.target === "openApi3") {
|
|
2950
|
+
return schema;
|
|
2951
|
+
}
|
|
2952
|
+
if (def.keyType?._def.typeName === ZodFirstPartyTypeKind2.ZodString && def.keyType._def.checks?.length) {
|
|
2953
|
+
const { type, ...keyType } = parseStringDef(def.keyType._def, refs);
|
|
2954
|
+
return {
|
|
2955
|
+
...schema,
|
|
2956
|
+
propertyNames: keyType
|
|
2957
|
+
};
|
|
2958
|
+
} else if (def.keyType?._def.typeName === ZodFirstPartyTypeKind2.ZodEnum) {
|
|
2959
|
+
return {
|
|
2960
|
+
...schema,
|
|
2961
|
+
propertyNames: {
|
|
2962
|
+
enum: def.keyType._def.values
|
|
2963
|
+
}
|
|
2964
|
+
};
|
|
2965
|
+
} else if (def.keyType?._def.typeName === ZodFirstPartyTypeKind2.ZodBranded && def.keyType._def.type._def.typeName === ZodFirstPartyTypeKind2.ZodString && def.keyType._def.type._def.checks?.length) {
|
|
2966
|
+
const { type, ...keyType } = parseBrandedDef(def.keyType._def, refs);
|
|
2967
|
+
return {
|
|
2968
|
+
...schema,
|
|
2969
|
+
propertyNames: keyType
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
return schema;
|
|
2973
|
+
}
|
|
2974
|
+
function parseMapDef(def, refs) {
|
|
2975
|
+
if (refs.mapStrategy === "record") {
|
|
2976
|
+
return parseRecordDef(def, refs);
|
|
2977
|
+
}
|
|
2978
|
+
const keys = parseDef(def.keyType._def, {
|
|
2979
|
+
...refs,
|
|
2980
|
+
currentPath: [...refs.currentPath, "items", "items", "0"]
|
|
2981
|
+
}) || parseAnyDef(refs);
|
|
2982
|
+
const values = parseDef(def.valueType._def, {
|
|
2983
|
+
...refs,
|
|
2984
|
+
currentPath: [...refs.currentPath, "items", "items", "1"]
|
|
2985
|
+
}) || parseAnyDef(refs);
|
|
2986
|
+
return {
|
|
2987
|
+
type: "array",
|
|
2988
|
+
maxItems: 125,
|
|
2989
|
+
items: {
|
|
2990
|
+
type: "array",
|
|
2991
|
+
items: [keys, values],
|
|
2992
|
+
minItems: 2,
|
|
2993
|
+
maxItems: 2
|
|
2994
|
+
}
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
function parseNativeEnumDef(def) {
|
|
2998
|
+
const object = def.values;
|
|
2999
|
+
const actualKeys = Object.keys(def.values).filter((key) => {
|
|
3000
|
+
return typeof object[object[key]] !== "number";
|
|
3001
|
+
});
|
|
3002
|
+
const actualValues = actualKeys.map((key) => object[key]);
|
|
3003
|
+
const parsedTypes = Array.from(new Set(actualValues.map((values) => typeof values)));
|
|
3004
|
+
return {
|
|
3005
|
+
type: parsedTypes.length === 1 ? parsedTypes[0] === "string" ? "string" : "number" : ["string", "number"],
|
|
3006
|
+
enum: actualValues
|
|
3007
|
+
};
|
|
3008
|
+
}
|
|
3009
|
+
function parseNeverDef(refs) {
|
|
3010
|
+
return refs.target === "openAi" ? void 0 : {
|
|
3011
|
+
not: parseAnyDef({
|
|
3012
|
+
...refs,
|
|
3013
|
+
currentPath: [...refs.currentPath, "not"]
|
|
3014
|
+
})
|
|
2305
3015
|
};
|
|
2306
3016
|
}
|
|
2307
|
-
function
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
3017
|
+
function parseNullDef(refs) {
|
|
3018
|
+
return refs.target === "openApi3" ? {
|
|
3019
|
+
enum: ["null"],
|
|
3020
|
+
nullable: true
|
|
3021
|
+
} : {
|
|
3022
|
+
type: "null"
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
var primitiveMappings = {
|
|
3026
|
+
ZodString: "string",
|
|
3027
|
+
ZodNumber: "number",
|
|
3028
|
+
ZodBigInt: "integer",
|
|
3029
|
+
ZodBoolean: "boolean",
|
|
3030
|
+
ZodNull: "null"
|
|
3031
|
+
};
|
|
3032
|
+
function parseUnionDef(def, refs) {
|
|
3033
|
+
if (refs.target === "openApi3")
|
|
3034
|
+
return asAnyOf(def, refs);
|
|
3035
|
+
const options = def.options instanceof Map ? Array.from(def.options.values()) : def.options;
|
|
3036
|
+
if (options.every((x) => x._def.typeName in primitiveMappings && (!x._def.checks || !x._def.checks.length))) {
|
|
3037
|
+
const types = options.reduce((types2, x) => {
|
|
3038
|
+
const type = primitiveMappings[x._def.typeName];
|
|
3039
|
+
return type && !types2.includes(type) ? [...types2, type] : types2;
|
|
3040
|
+
}, []);
|
|
3041
|
+
return {
|
|
3042
|
+
type: types.length > 1 ? types : types[0]
|
|
3043
|
+
};
|
|
3044
|
+
} else if (options.every((x) => x._def.typeName === "ZodLiteral" && !x.description)) {
|
|
3045
|
+
const types = options.reduce((acc, x) => {
|
|
3046
|
+
const type = typeof x._def.value;
|
|
3047
|
+
switch (type) {
|
|
3048
|
+
case "string":
|
|
3049
|
+
case "number":
|
|
3050
|
+
case "boolean":
|
|
3051
|
+
return [...acc, type];
|
|
3052
|
+
case "bigint":
|
|
3053
|
+
return [...acc, "integer"];
|
|
3054
|
+
case "object":
|
|
3055
|
+
if (x._def.value === null)
|
|
3056
|
+
return [...acc, "null"];
|
|
3057
|
+
case "symbol":
|
|
3058
|
+
case "undefined":
|
|
3059
|
+
case "function":
|
|
3060
|
+
default:
|
|
3061
|
+
return acc;
|
|
3062
|
+
}
|
|
3063
|
+
}, []);
|
|
3064
|
+
if (types.length === options.length) {
|
|
3065
|
+
const uniqueTypes = types.filter((x, i, a) => a.indexOf(x) === i);
|
|
3066
|
+
return {
|
|
3067
|
+
type: uniqueTypes.length > 1 ? uniqueTypes : uniqueTypes[0],
|
|
3068
|
+
enum: options.reduce((acc, x) => {
|
|
3069
|
+
return acc.includes(x._def.value) ? acc : [...acc, x._def.value];
|
|
3070
|
+
}, [])
|
|
3071
|
+
};
|
|
2316
3072
|
}
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
3073
|
+
} else if (options.every((x) => x._def.typeName === "ZodEnum")) {
|
|
3074
|
+
return {
|
|
3075
|
+
type: "string",
|
|
3076
|
+
enum: options.reduce((acc, x) => [
|
|
3077
|
+
...acc,
|
|
3078
|
+
...x._def.values.filter((x2) => !acc.includes(x2))
|
|
3079
|
+
], [])
|
|
3080
|
+
};
|
|
2320
3081
|
}
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
3082
|
+
return asAnyOf(def, refs);
|
|
3083
|
+
}
|
|
3084
|
+
var asAnyOf = (def, refs) => {
|
|
3085
|
+
const anyOf = (def.options instanceof Map ? Array.from(def.options.values()) : def.options).map((x, i) => parseDef(x._def, {
|
|
3086
|
+
...refs,
|
|
3087
|
+
currentPath: [...refs.currentPath, "anyOf", `${i}`]
|
|
3088
|
+
})).filter((x) => !!x && (!refs.strictUnions || typeof x === "object" && Object.keys(x).length > 0));
|
|
3089
|
+
return anyOf.length ? { anyOf } : void 0;
|
|
3090
|
+
};
|
|
3091
|
+
function parseNullableDef(def, refs) {
|
|
3092
|
+
if (["ZodString", "ZodNumber", "ZodBigInt", "ZodBoolean", "ZodNull"].includes(def.innerType._def.typeName) && (!def.innerType._def.checks || !def.innerType._def.checks.length)) {
|
|
3093
|
+
if (refs.target === "openApi3") {
|
|
3094
|
+
return {
|
|
3095
|
+
type: primitiveMappings[def.innerType._def.typeName],
|
|
3096
|
+
nullable: true
|
|
3097
|
+
};
|
|
2332
3098
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
3099
|
+
return {
|
|
3100
|
+
type: [
|
|
3101
|
+
primitiveMappings[def.innerType._def.typeName],
|
|
3102
|
+
"null"
|
|
3103
|
+
]
|
|
3104
|
+
};
|
|
2335
3105
|
}
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
3106
|
+
if (refs.target === "openApi3") {
|
|
3107
|
+
const base2 = parseDef(def.innerType._def, {
|
|
3108
|
+
...refs,
|
|
3109
|
+
currentPath: [...refs.currentPath]
|
|
3110
|
+
});
|
|
3111
|
+
if (base2 && "$ref" in base2)
|
|
3112
|
+
return { allOf: [base2], nullable: true };
|
|
3113
|
+
return base2 && { ...base2, nullable: true };
|
|
2339
3114
|
}
|
|
2340
|
-
|
|
3115
|
+
const base = parseDef(def.innerType._def, {
|
|
3116
|
+
...refs,
|
|
3117
|
+
currentPath: [...refs.currentPath, "anyOf", "0"]
|
|
3118
|
+
});
|
|
3119
|
+
return base && { anyOf: [base, { type: "null" }] };
|
|
2341
3120
|
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
3121
|
+
function parseNumberDef(def, refs) {
|
|
3122
|
+
const res = {
|
|
3123
|
+
type: "number"
|
|
3124
|
+
};
|
|
3125
|
+
if (!def.checks)
|
|
3126
|
+
return res;
|
|
3127
|
+
for (const check of def.checks) {
|
|
3128
|
+
switch (check.kind) {
|
|
3129
|
+
case "int":
|
|
3130
|
+
res.type = "integer";
|
|
3131
|
+
addErrorMessage(res, "type", check.message, refs);
|
|
3132
|
+
break;
|
|
3133
|
+
case "min":
|
|
3134
|
+
if (refs.target === "jsonSchema7") {
|
|
3135
|
+
if (check.inclusive) {
|
|
3136
|
+
setResponseValueAndErrors(res, "minimum", check.value, check.message, refs);
|
|
3137
|
+
} else {
|
|
3138
|
+
setResponseValueAndErrors(res, "exclusiveMinimum", check.value, check.message, refs);
|
|
3139
|
+
}
|
|
3140
|
+
} else {
|
|
3141
|
+
if (!check.inclusive) {
|
|
3142
|
+
res.exclusiveMinimum = true;
|
|
3143
|
+
}
|
|
3144
|
+
setResponseValueAndErrors(res, "minimum", check.value, check.message, refs);
|
|
3145
|
+
}
|
|
3146
|
+
break;
|
|
3147
|
+
case "max":
|
|
3148
|
+
if (refs.target === "jsonSchema7") {
|
|
3149
|
+
if (check.inclusive) {
|
|
3150
|
+
setResponseValueAndErrors(res, "maximum", check.value, check.message, refs);
|
|
3151
|
+
} else {
|
|
3152
|
+
setResponseValueAndErrors(res, "exclusiveMaximum", check.value, check.message, refs);
|
|
3153
|
+
}
|
|
3154
|
+
} else {
|
|
3155
|
+
if (!check.inclusive) {
|
|
3156
|
+
res.exclusiveMaximum = true;
|
|
3157
|
+
}
|
|
3158
|
+
setResponseValueAndErrors(res, "maximum", check.value, check.message, refs);
|
|
3159
|
+
}
|
|
3160
|
+
break;
|
|
3161
|
+
case "multipleOf":
|
|
3162
|
+
setResponseValueAndErrors(res, "multipleOf", check.value, check.message, refs);
|
|
3163
|
+
break;
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
return res;
|
|
3167
|
+
}
|
|
3168
|
+
function parseObjectDef(def, refs) {
|
|
3169
|
+
const forceOptionalIntoNullable = refs.target === "openAi";
|
|
3170
|
+
const result = {
|
|
3171
|
+
type: "object",
|
|
3172
|
+
properties: {}
|
|
3173
|
+
};
|
|
3174
|
+
const required = [];
|
|
3175
|
+
const shape = def.shape();
|
|
3176
|
+
for (const propName in shape) {
|
|
3177
|
+
let propDef = shape[propName];
|
|
3178
|
+
if (propDef === void 0 || propDef._def === void 0) {
|
|
3179
|
+
continue;
|
|
3180
|
+
}
|
|
3181
|
+
let propOptional = safeIsOptional(propDef);
|
|
3182
|
+
if (propOptional && forceOptionalIntoNullable) {
|
|
3183
|
+
if (propDef._def.typeName === "ZodOptional") {
|
|
3184
|
+
propDef = propDef._def.innerType;
|
|
3185
|
+
}
|
|
3186
|
+
if (!propDef.isNullable()) {
|
|
3187
|
+
propDef = propDef.nullable();
|
|
3188
|
+
}
|
|
3189
|
+
propOptional = false;
|
|
3190
|
+
}
|
|
3191
|
+
const parsedDef = parseDef(propDef._def, {
|
|
3192
|
+
...refs,
|
|
3193
|
+
currentPath: [...refs.currentPath, "properties", propName],
|
|
3194
|
+
propertyPath: [...refs.currentPath, "properties", propName]
|
|
3195
|
+
});
|
|
3196
|
+
if (parsedDef === void 0) {
|
|
3197
|
+
continue;
|
|
3198
|
+
}
|
|
3199
|
+
result.properties[propName] = parsedDef;
|
|
3200
|
+
if (!propOptional) {
|
|
3201
|
+
required.push(propName);
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
if (required.length) {
|
|
3205
|
+
result.required = required;
|
|
3206
|
+
}
|
|
3207
|
+
const additionalProperties = decideAdditionalProperties(def, refs);
|
|
3208
|
+
if (additionalProperties !== void 0) {
|
|
3209
|
+
result.additionalProperties = additionalProperties;
|
|
3210
|
+
}
|
|
3211
|
+
return result;
|
|
3212
|
+
}
|
|
3213
|
+
function decideAdditionalProperties(def, refs) {
|
|
3214
|
+
if (def.catchall._def.typeName !== "ZodNever") {
|
|
3215
|
+
return parseDef(def.catchall._def, {
|
|
3216
|
+
...refs,
|
|
3217
|
+
currentPath: [...refs.currentPath, "additionalProperties"]
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
switch (def.unknownKeys) {
|
|
3221
|
+
case "passthrough":
|
|
3222
|
+
return refs.allowedAdditionalProperties;
|
|
3223
|
+
case "strict":
|
|
3224
|
+
return refs.rejectedAdditionalProperties;
|
|
3225
|
+
case "strip":
|
|
3226
|
+
return refs.removeAdditionalStrategy === "strict" ? refs.allowedAdditionalProperties : refs.rejectedAdditionalProperties;
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
function safeIsOptional(schema) {
|
|
3230
|
+
try {
|
|
3231
|
+
return schema.isOptional();
|
|
3232
|
+
} catch {
|
|
3233
|
+
return true;
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
var parseOptionalDef = (def, refs) => {
|
|
3237
|
+
if (refs.currentPath.toString() === refs.propertyPath?.toString()) {
|
|
3238
|
+
return parseDef(def.innerType._def, refs);
|
|
3239
|
+
}
|
|
3240
|
+
const innerSchema = parseDef(def.innerType._def, {
|
|
3241
|
+
...refs,
|
|
3242
|
+
currentPath: [...refs.currentPath, "anyOf", "1"]
|
|
3243
|
+
});
|
|
3244
|
+
return innerSchema ? {
|
|
3245
|
+
anyOf: [
|
|
3246
|
+
{
|
|
3247
|
+
not: parseAnyDef(refs)
|
|
3248
|
+
},
|
|
3249
|
+
innerSchema
|
|
3250
|
+
]
|
|
3251
|
+
} : parseAnyDef(refs);
|
|
3252
|
+
};
|
|
3253
|
+
var parsePipelineDef = (def, refs) => {
|
|
3254
|
+
if (refs.pipeStrategy === "input") {
|
|
3255
|
+
return parseDef(def.in._def, refs);
|
|
3256
|
+
} else if (refs.pipeStrategy === "output") {
|
|
3257
|
+
return parseDef(def.out._def, refs);
|
|
3258
|
+
}
|
|
3259
|
+
const a = parseDef(def.in._def, {
|
|
3260
|
+
...refs,
|
|
3261
|
+
currentPath: [...refs.currentPath, "allOf", "0"]
|
|
3262
|
+
});
|
|
3263
|
+
const b = parseDef(def.out._def, {
|
|
3264
|
+
...refs,
|
|
3265
|
+
currentPath: [...refs.currentPath, "allOf", a ? "1" : "0"]
|
|
3266
|
+
});
|
|
3267
|
+
return {
|
|
3268
|
+
allOf: [a, b].filter((x) => x !== void 0)
|
|
3269
|
+
};
|
|
3270
|
+
};
|
|
3271
|
+
function parsePromiseDef(def, refs) {
|
|
3272
|
+
return parseDef(def.type._def, refs);
|
|
3273
|
+
}
|
|
3274
|
+
function parseSetDef(def, refs) {
|
|
3275
|
+
const items = parseDef(def.valueType._def, {
|
|
3276
|
+
...refs,
|
|
3277
|
+
currentPath: [...refs.currentPath, "items"]
|
|
3278
|
+
});
|
|
3279
|
+
const schema = {
|
|
3280
|
+
type: "array",
|
|
3281
|
+
uniqueItems: true,
|
|
3282
|
+
items
|
|
3283
|
+
};
|
|
3284
|
+
if (def.minSize) {
|
|
3285
|
+
setResponseValueAndErrors(schema, "minItems", def.minSize.value, def.minSize.message, refs);
|
|
3286
|
+
}
|
|
3287
|
+
if (def.maxSize) {
|
|
3288
|
+
setResponseValueAndErrors(schema, "maxItems", def.maxSize.value, def.maxSize.message, refs);
|
|
3289
|
+
}
|
|
3290
|
+
return schema;
|
|
3291
|
+
}
|
|
3292
|
+
function parseTupleDef(def, refs) {
|
|
3293
|
+
if (def.rest) {
|
|
3294
|
+
return {
|
|
3295
|
+
type: "array",
|
|
3296
|
+
minItems: def.items.length,
|
|
3297
|
+
items: def.items.map((x, i) => parseDef(x._def, {
|
|
3298
|
+
...refs,
|
|
3299
|
+
currentPath: [...refs.currentPath, "items", `${i}`]
|
|
3300
|
+
})).reduce((acc, x) => x === void 0 ? acc : [...acc, x], []),
|
|
3301
|
+
additionalItems: parseDef(def.rest._def, {
|
|
3302
|
+
...refs,
|
|
3303
|
+
currentPath: [...refs.currentPath, "additionalItems"]
|
|
3304
|
+
})
|
|
3305
|
+
};
|
|
3306
|
+
} else {
|
|
3307
|
+
return {
|
|
3308
|
+
type: "array",
|
|
3309
|
+
minItems: def.items.length,
|
|
3310
|
+
maxItems: def.items.length,
|
|
3311
|
+
items: def.items.map((x, i) => parseDef(x._def, {
|
|
3312
|
+
...refs,
|
|
3313
|
+
currentPath: [...refs.currentPath, "items", `${i}`]
|
|
3314
|
+
})).reduce((acc, x) => x === void 0 ? acc : [...acc, x], [])
|
|
3315
|
+
};
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
function parseUndefinedDef(refs) {
|
|
3319
|
+
return {
|
|
3320
|
+
not: parseAnyDef(refs)
|
|
3321
|
+
};
|
|
3322
|
+
}
|
|
3323
|
+
function parseUnknownDef(refs) {
|
|
3324
|
+
return parseAnyDef(refs);
|
|
3325
|
+
}
|
|
3326
|
+
var parseReadonlyDef = (def, refs) => {
|
|
3327
|
+
return parseDef(def.innerType._def, refs);
|
|
3328
|
+
};
|
|
3329
|
+
var selectParser = (def, typeName, refs) => {
|
|
3330
|
+
switch (typeName) {
|
|
3331
|
+
case ZodFirstPartyTypeKind3.ZodString:
|
|
3332
|
+
return parseStringDef(def, refs);
|
|
3333
|
+
case ZodFirstPartyTypeKind3.ZodNumber:
|
|
3334
|
+
return parseNumberDef(def, refs);
|
|
3335
|
+
case ZodFirstPartyTypeKind3.ZodObject:
|
|
3336
|
+
return parseObjectDef(def, refs);
|
|
3337
|
+
case ZodFirstPartyTypeKind3.ZodBigInt:
|
|
3338
|
+
return parseBigintDef(def, refs);
|
|
3339
|
+
case ZodFirstPartyTypeKind3.ZodBoolean:
|
|
3340
|
+
return parseBooleanDef();
|
|
3341
|
+
case ZodFirstPartyTypeKind3.ZodDate:
|
|
3342
|
+
return parseDateDef(def, refs);
|
|
3343
|
+
case ZodFirstPartyTypeKind3.ZodUndefined:
|
|
3344
|
+
return parseUndefinedDef(refs);
|
|
3345
|
+
case ZodFirstPartyTypeKind3.ZodNull:
|
|
3346
|
+
return parseNullDef(refs);
|
|
3347
|
+
case ZodFirstPartyTypeKind3.ZodArray:
|
|
3348
|
+
return parseArrayDef(def, refs);
|
|
3349
|
+
case ZodFirstPartyTypeKind3.ZodUnion:
|
|
3350
|
+
case ZodFirstPartyTypeKind3.ZodDiscriminatedUnion:
|
|
3351
|
+
return parseUnionDef(def, refs);
|
|
3352
|
+
case ZodFirstPartyTypeKind3.ZodIntersection:
|
|
3353
|
+
return parseIntersectionDef(def, refs);
|
|
3354
|
+
case ZodFirstPartyTypeKind3.ZodTuple:
|
|
3355
|
+
return parseTupleDef(def, refs);
|
|
3356
|
+
case ZodFirstPartyTypeKind3.ZodRecord:
|
|
3357
|
+
return parseRecordDef(def, refs);
|
|
3358
|
+
case ZodFirstPartyTypeKind3.ZodLiteral:
|
|
3359
|
+
return parseLiteralDef(def, refs);
|
|
3360
|
+
case ZodFirstPartyTypeKind3.ZodEnum:
|
|
3361
|
+
return parseEnumDef(def);
|
|
3362
|
+
case ZodFirstPartyTypeKind3.ZodNativeEnum:
|
|
3363
|
+
return parseNativeEnumDef(def);
|
|
3364
|
+
case ZodFirstPartyTypeKind3.ZodNullable:
|
|
3365
|
+
return parseNullableDef(def, refs);
|
|
3366
|
+
case ZodFirstPartyTypeKind3.ZodOptional:
|
|
3367
|
+
return parseOptionalDef(def, refs);
|
|
3368
|
+
case ZodFirstPartyTypeKind3.ZodMap:
|
|
3369
|
+
return parseMapDef(def, refs);
|
|
3370
|
+
case ZodFirstPartyTypeKind3.ZodSet:
|
|
3371
|
+
return parseSetDef(def, refs);
|
|
3372
|
+
case ZodFirstPartyTypeKind3.ZodLazy:
|
|
3373
|
+
return () => def.getter()._def;
|
|
3374
|
+
case ZodFirstPartyTypeKind3.ZodPromise:
|
|
3375
|
+
return parsePromiseDef(def, refs);
|
|
3376
|
+
case ZodFirstPartyTypeKind3.ZodNaN:
|
|
3377
|
+
case ZodFirstPartyTypeKind3.ZodNever:
|
|
3378
|
+
return parseNeverDef(refs);
|
|
3379
|
+
case ZodFirstPartyTypeKind3.ZodEffects:
|
|
3380
|
+
return parseEffectsDef(def, refs);
|
|
3381
|
+
case ZodFirstPartyTypeKind3.ZodAny:
|
|
3382
|
+
return parseAnyDef(refs);
|
|
3383
|
+
case ZodFirstPartyTypeKind3.ZodUnknown:
|
|
3384
|
+
return parseUnknownDef(refs);
|
|
3385
|
+
case ZodFirstPartyTypeKind3.ZodDefault:
|
|
3386
|
+
return parseDefaultDef(def, refs);
|
|
3387
|
+
case ZodFirstPartyTypeKind3.ZodBranded:
|
|
3388
|
+
return parseBrandedDef(def, refs);
|
|
3389
|
+
case ZodFirstPartyTypeKind3.ZodReadonly:
|
|
3390
|
+
return parseReadonlyDef(def, refs);
|
|
3391
|
+
case ZodFirstPartyTypeKind3.ZodCatch:
|
|
3392
|
+
return parseCatchDef(def, refs);
|
|
3393
|
+
case ZodFirstPartyTypeKind3.ZodPipeline:
|
|
3394
|
+
return parsePipelineDef(def, refs);
|
|
3395
|
+
case ZodFirstPartyTypeKind3.ZodFunction:
|
|
3396
|
+
case ZodFirstPartyTypeKind3.ZodVoid:
|
|
3397
|
+
case ZodFirstPartyTypeKind3.ZodSymbol:
|
|
3398
|
+
return void 0;
|
|
3399
|
+
default:
|
|
3400
|
+
return /* @__PURE__ */ ((_) => void 0)(typeName);
|
|
3401
|
+
}
|
|
3402
|
+
};
|
|
3403
|
+
function parseDef(def, refs, forceResolution = false) {
|
|
3404
|
+
const seenItem = refs.seen.get(def);
|
|
3405
|
+
if (refs.override) {
|
|
3406
|
+
const overrideResult = refs.override?.(def, refs, seenItem, forceResolution);
|
|
3407
|
+
if (overrideResult !== ignoreOverride) {
|
|
3408
|
+
return overrideResult;
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
if (seenItem && !forceResolution) {
|
|
3412
|
+
const seenSchema = get$ref(seenItem, refs);
|
|
3413
|
+
if (seenSchema !== void 0) {
|
|
3414
|
+
return seenSchema;
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
const newItem = { def, path: refs.currentPath, jsonSchema: void 0 };
|
|
3418
|
+
refs.seen.set(def, newItem);
|
|
3419
|
+
const jsonSchemaOrGetter = selectParser(def, def.typeName, refs);
|
|
3420
|
+
const jsonSchema = typeof jsonSchemaOrGetter === "function" ? parseDef(jsonSchemaOrGetter(), refs) : jsonSchemaOrGetter;
|
|
3421
|
+
if (jsonSchema) {
|
|
3422
|
+
addMeta(def, refs, jsonSchema);
|
|
3423
|
+
}
|
|
3424
|
+
if (refs.postProcess) {
|
|
3425
|
+
const postProcessResult = refs.postProcess(jsonSchema, def, refs);
|
|
3426
|
+
newItem.jsonSchema = jsonSchema;
|
|
3427
|
+
return postProcessResult;
|
|
3428
|
+
}
|
|
3429
|
+
newItem.jsonSchema = jsonSchema;
|
|
3430
|
+
return jsonSchema;
|
|
3431
|
+
}
|
|
3432
|
+
var get$ref = (item, refs) => {
|
|
3433
|
+
switch (refs.$refStrategy) {
|
|
3434
|
+
case "root":
|
|
3435
|
+
return { $ref: item.path.join("/") };
|
|
3436
|
+
case "relative":
|
|
3437
|
+
return { $ref: getRelativePath(refs.currentPath, item.path) };
|
|
3438
|
+
case "none":
|
|
3439
|
+
case "seen": {
|
|
3440
|
+
if (item.path.length < refs.currentPath.length && item.path.every((value, index) => refs.currentPath[index] === value)) {
|
|
3441
|
+
console.warn(`Recursive reference detected at ${refs.currentPath.join("/")}! Defaulting to any`);
|
|
3442
|
+
return parseAnyDef(refs);
|
|
3443
|
+
}
|
|
3444
|
+
return refs.$refStrategy === "seen" ? parseAnyDef(refs) : void 0;
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
};
|
|
3448
|
+
var addMeta = (def, refs, jsonSchema) => {
|
|
3449
|
+
if (def.description) {
|
|
3450
|
+
jsonSchema.description = def.description;
|
|
3451
|
+
if (refs.markdownDescription) {
|
|
3452
|
+
jsonSchema.markdownDescription = def.description;
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
return jsonSchema;
|
|
3456
|
+
};
|
|
3457
|
+
var zodToJsonSchema = (schema, options) => {
|
|
3458
|
+
const refs = getRefs(options);
|
|
3459
|
+
let definitions = typeof options === "object" && options.definitions ? Object.entries(options.definitions).reduce((acc, [name2, schema2]) => ({
|
|
3460
|
+
...acc,
|
|
3461
|
+
[name2]: parseDef(schema2._def, {
|
|
3462
|
+
...refs,
|
|
3463
|
+
currentPath: [...refs.basePath, refs.definitionPath, name2]
|
|
3464
|
+
}, true) ?? parseAnyDef(refs)
|
|
3465
|
+
}), {}) : void 0;
|
|
3466
|
+
const name = typeof options === "string" ? options : options?.nameStrategy === "title" ? void 0 : options?.name;
|
|
3467
|
+
const main = parseDef(schema._def, name === void 0 ? refs : {
|
|
3468
|
+
...refs,
|
|
3469
|
+
currentPath: [...refs.basePath, refs.definitionPath, name]
|
|
3470
|
+
}, false) ?? parseAnyDef(refs);
|
|
3471
|
+
const title = typeof options === "object" && options.name !== void 0 && options.nameStrategy === "title" ? options.name : void 0;
|
|
3472
|
+
if (title !== void 0) {
|
|
3473
|
+
main.title = title;
|
|
3474
|
+
}
|
|
3475
|
+
if (refs.flags.hasReferencedOpenAiAnyType) {
|
|
3476
|
+
if (!definitions) {
|
|
3477
|
+
definitions = {};
|
|
3478
|
+
}
|
|
3479
|
+
if (!definitions[refs.openAiAnyTypeName]) {
|
|
3480
|
+
definitions[refs.openAiAnyTypeName] = {
|
|
3481
|
+
// Skipping "object" as no properties can be defined and additionalProperties must be "false"
|
|
3482
|
+
type: ["string", "number", "integer", "boolean", "array", "null"],
|
|
3483
|
+
items: {
|
|
3484
|
+
$ref: refs.$refStrategy === "relative" ? "1" : [
|
|
3485
|
+
...refs.basePath,
|
|
3486
|
+
refs.definitionPath,
|
|
3487
|
+
refs.openAiAnyTypeName
|
|
3488
|
+
].join("/")
|
|
3489
|
+
}
|
|
3490
|
+
};
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
const combined = name === void 0 ? definitions ? {
|
|
3494
|
+
...main,
|
|
3495
|
+
[refs.definitionPath]: definitions
|
|
3496
|
+
} : main : {
|
|
3497
|
+
$ref: [
|
|
3498
|
+
...refs.$refStrategy === "relative" ? [] : refs.basePath,
|
|
3499
|
+
refs.definitionPath,
|
|
3500
|
+
name
|
|
3501
|
+
].join("/"),
|
|
3502
|
+
[refs.definitionPath]: {
|
|
3503
|
+
...definitions,
|
|
3504
|
+
[name]: main
|
|
3505
|
+
}
|
|
3506
|
+
};
|
|
3507
|
+
if (refs.target === "jsonSchema7") {
|
|
3508
|
+
combined.$schema = "http://json-schema.org/draft-07/schema#";
|
|
3509
|
+
} else if (refs.target === "jsonSchema2019-09" || refs.target === "openAi") {
|
|
3510
|
+
combined.$schema = "https://json-schema.org/draft/2019-09/schema#";
|
|
3511
|
+
}
|
|
3512
|
+
if (refs.target === "openAi" && ("anyOf" in combined || "oneOf" in combined || "allOf" in combined || "type" in combined && Array.isArray(combined.type))) {
|
|
3513
|
+
console.warn("Warning: OpenAI may not support schemas with unions as roots! Try wrapping it in an object property.");
|
|
3514
|
+
}
|
|
3515
|
+
return combined;
|
|
3516
|
+
};
|
|
3517
|
+
var OPTIONS_MIN = 2;
|
|
3518
|
+
var OPTIONS_MAX = 6;
|
|
3519
|
+
var MISSED_CALL_PLAN = {
|
|
3520
|
+
retry_10m: { kind: "every", minutes: 10 },
|
|
3521
|
+
retry_30m: { kind: "every", minutes: 30 },
|
|
3522
|
+
retry_60m: { kind: "every", minutes: 60 },
|
|
3523
|
+
backoff_gentle: { kind: "at", minutes: [30, 120, 360] },
|
|
3524
|
+
backoff_standard: { kind: "at", minutes: [10, 30, 120] },
|
|
3525
|
+
backoff_aggressive: { kind: "at", minutes: [5, 15, 45] },
|
|
3526
|
+
inbox: { kind: "once" },
|
|
3527
|
+
dismiss: { kind: "grace", minutes: 2 }
|
|
3528
|
+
};
|
|
3529
|
+
function draft2020(node) {
|
|
3530
|
+
if (Array.isArray(node)) return node.map(draft2020);
|
|
3531
|
+
if (node && typeof node === "object") {
|
|
3532
|
+
const o = node;
|
|
3533
|
+
for (const [excl, lim] of [["exclusiveMinimum", "minimum"], ["exclusiveMaximum", "maximum"]]) {
|
|
3534
|
+
if (typeof o[excl] === "boolean") {
|
|
3535
|
+
if (o[excl] === true && typeof o[lim] === "number") {
|
|
3536
|
+
o[excl] = o[lim];
|
|
3537
|
+
delete o[lim];
|
|
3538
|
+
} else delete o[excl];
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
for (const k of Object.keys(o)) o[k] = draft2020(o[k]);
|
|
3542
|
+
return o;
|
|
3543
|
+
}
|
|
3544
|
+
return node;
|
|
3545
|
+
}
|
|
3546
|
+
function mcpInputSchema(s) {
|
|
3547
|
+
const schema = zodToJsonSchema(s, { target: "jsonSchema2019-09", $refStrategy: "none" });
|
|
3548
|
+
delete schema.$schema;
|
|
3549
|
+
return draft2020(schema);
|
|
3550
|
+
}
|
|
3551
|
+
var CreateGoalSchema = z.object({
|
|
3552
|
+
outcome: z.string().trim().min(1).max(1e4),
|
|
3553
|
+
ownerParticipant: z.string().trim().min(1).optional(),
|
|
3554
|
+
idempotencyKey: z.string().trim().min(1).max(200)
|
|
3555
|
+
});
|
|
3556
|
+
var UpdateGoalSchema = z.object({
|
|
3557
|
+
revision: z.number().int().positive(),
|
|
3558
|
+
changes: z.object({
|
|
3559
|
+
outcome: z.string().trim().min(1).max(1e4).optional(),
|
|
3560
|
+
state: z.enum(["active", "waiting", "done", "cancelled"]).optional(),
|
|
3561
|
+
progress: z.string().trim().min(1).max(1e4).optional(),
|
|
3562
|
+
reviewed: z.literal(true).optional()
|
|
3563
|
+
}).refine((v) => Object.keys(v).length > 0),
|
|
3564
|
+
reason: z.string().trim().min(1).max(2e3)
|
|
3565
|
+
});
|
|
3566
|
+
var fmtMin = (m) => m >= 60 ? `${m / 60} hr` : `${m} min`;
|
|
3567
|
+
var STANDARD_MEANS = (() => {
|
|
3568
|
+
const plan = MISSED_CALL_PLAN.backoff_standard;
|
|
3569
|
+
const mins = plan.kind === "at" ? plan.minutes : [];
|
|
3570
|
+
const parts = mins.map(fmtMin);
|
|
3571
|
+
const list = parts.length > 1 ? `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}` : parts[0] ?? "";
|
|
3572
|
+
return `Rings again ${list} after the missed call, then leaves it in your inbox`;
|
|
3573
|
+
})();
|
|
3574
|
+
function contactSchemaFrom(fields) {
|
|
3575
|
+
const surface = z.object({
|
|
3576
|
+
ask: fields.ask.describe(
|
|
3577
|
+
`What to tell the user, or what you need to find out from them. Plain prose \u2014 as long as it needs to be (up to 10k characters); Paigy splits it into topics and reads back a few sentences at a time, so do NOT compress a briefing into one line. May be spoken aloud on a call, so write natural speech and name things (not IDs). Contact at exactly two moments: BLOCKED on a decision only they can make, or DONE (one short report \u2014 what shipped, how you verified it, what you flagged). DONE IS SAID ONCE: "all set", "nothing open on my end", "that thread is complete" are the same report in new words, and each one reaches them separately (live 2026-08-12: three of them in three minutes). After the first, you are finished speaking; if they acknowledge it, stop rather than confirming the acknowledgement. Progress is never a contact: set_work_state carries it, and working narration stays in your own terminal \u2014 the user sees you're working without being interrupted by it.`
|
|
3578
|
+
),
|
|
3579
|
+
waiting: fields.waiting.describe(
|
|
3580
|
+
"What happens to your work while you wait. 'none': you're just informing them. 'soft': you'd like an answer but can keep working. 'hard': you are STOPPED until they answer \u2014 reaches them urgently and escalates to a real phone call if unanswered."
|
|
3581
|
+
),
|
|
3582
|
+
options: fields.options.describe(
|
|
3583
|
+
`The choices the user picks from, when you have them \u2014 ${OPTIONS_MIN} to ${OPTIONS_MAX}, drawn from your own sentence.`
|
|
3584
|
+
),
|
|
3585
|
+
channel: fields.channel.describe(
|
|
3586
|
+
"Relay how the user explicitly said to reach them ('call me' \u2192 'call', 'just message/text me' \u2192 'message'), or use 'call' when promoting the same quiet ask after it becomes a substantial blocker. Omit otherwise; Paigy picks."
|
|
3587
|
+
),
|
|
3588
|
+
parentId: fields.parentId.describe(
|
|
3589
|
+
"To continue an earlier conversation, pass the parentId a previous contact or reply returned. Omit to start a new one."
|
|
3590
|
+
),
|
|
3591
|
+
workId: fields.workId.describe(
|
|
3592
|
+
"The durable Work this contact advances. Pass the workId from check_replies or a prior reply when asking for a decision that blocks that work."
|
|
3593
|
+
)
|
|
3594
|
+
});
|
|
3595
|
+
const out = mcpInputSchema(surface);
|
|
3596
|
+
out.required = ["ask"];
|
|
3597
|
+
return out;
|
|
3598
|
+
}
|
|
3599
|
+
var CONTACT_DESCRIPTION = `Reach the user through Paigy \u2014 tell them something, or ask and get their answer. State what you need in \`ask\`, say what happens to your work while you wait in \`waiting\`, and Paigy handles the rest (channel, phrasing, answer format). If the user explicitly asks you to CALL them, send waiting:'hard' and say so in the ask. Returns { notificationId, parentId, workId?, decisionId? } \u2014 pass notificationId to the reply path named by the returned \`message\`, and parentId to a later contact to continue the conversation. When it rang, the reply also carries { ifMissed: { mode, means } }: what the user's own policy does with a call they don't take ("${STANDARD_MEANS}"), so a no-answer tells you how long to wait before coming back. THREADING REPLACES: a threaded follow-up SUPERSEDES your earlier pending items on that thread \u2014 right for updates to one ask, WRONG for a checklist (send independent to-dos un-threaded). A threaded re-send with IDENTICAL content escalates the pending ask in place. If a reply comes back as {kind:'clarify', chunks:[...]}, the user wants more detail \u2014 contact again on the SAME parentId with an expanded ask. BLOCKED ON A DECISION for existing work? Pass that work's \`workId\`; the reply returns the same workId plus a decisionId, so the answer resumes the right outcome. ONE ASK, ONE ROW: never restate a still-pending ask's question inside a NEW contact (e.g. weaving it into a briefing) \u2014 the whole answer settles on the new row and the original can never receive it. Keep waiting on the original (a live call reads every pending ask out separately, each answer routes to its own row), and use \`needs\` for a genuinely multi-part NEW ask. ANSWERABLE, NOT JUST ASKED: when the reply comes back carrying \`plan.units[].needs\`, that unit asked for something it gave the user no way to answer \u2014 'options' means it posed a choice with nothing to choose from, 'visuals' means it asked about something to look at with nothing to look at. Send it again on the SAME parentId with ${OPTIONS_MIN}-${OPTIONS_MAX} options (or the image), drawn from your own sentence. Paigy will not add them for you: a shape it guessed wrong cannot be undone, and you are the one who knows what the real alternatives are. \`units\` reports WHAT BECAME OF YOUR PROSE \u2014 { kept, raw, why }: how many topics Paigy compressed for delivery, how many kept your exact words, and the reason when it kept them (e.g. 'no_output' = compression produced nothing usable, so the user got your raw sentence). It needs no action and is not an error \u2014 read it only when the delivered wording matters to you; a high \`raw\` count means the user is hearing you verbatim.`;
|
|
3600
|
+
var ContextSchema = z2.object({
|
|
3601
|
+
title: z2.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
|
|
3602
|
+
description: z2.array(z2.string().min(1)).describe(
|
|
2345
3603
|
"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."
|
|
2346
3604
|
)
|
|
2347
3605
|
});
|
|
2348
|
-
var ParticipantSchema =
|
|
2349
|
-
kind:
|
|
2350
|
-
id:
|
|
3606
|
+
var ParticipantSchema = z2.object({
|
|
3607
|
+
kind: z2.enum(["human", "agent"]),
|
|
3608
|
+
id: z2.string()
|
|
2351
3609
|
});
|
|
2352
|
-
var TransformSchema =
|
|
3610
|
+
var TransformSchema = z2.enum([
|
|
2353
3611
|
"structure",
|
|
2354
3612
|
// shape intent into an answer contract; pick channel/urgency — broker `ask`, `select` shapes, `points`
|
|
2355
3613
|
"request_more",
|
|
@@ -2365,25 +3623,25 @@ var TransformSchema = z.enum([
|
|
|
2365
3623
|
"summarize"
|
|
2366
3624
|
// reduce volume, keep decision value — 30-turn cap, spoken briefing
|
|
2367
3625
|
]);
|
|
2368
|
-
var OptionSchema =
|
|
2369
|
-
id:
|
|
2370
|
-
label:
|
|
3626
|
+
var OptionSchema = z2.object({
|
|
3627
|
+
id: z2.string(),
|
|
3628
|
+
label: z2.string(),
|
|
2371
3629
|
// .describe() flows into the MCP contact JSON schema (zodToJsonSchema), so
|
|
2372
3630
|
// the constraints below are what an agent reads when deciding to use these.
|
|
2373
|
-
html:
|
|
3631
|
+
html: z2.string().max(16384).describe(
|
|
2374
3632
|
"Optional sandboxed HTML/CSS preview for a visual 'pick one' (shown in the option card). Untrusted-sandboxed: NO JavaScript, NO external network or images \u2014 inline CSS and data: URIs only; <=16KB. Use for layout/CSS mockups, tables, diffs. For a hosted image use `image` instead."
|
|
2375
3633
|
).optional(),
|
|
2376
|
-
image:
|
|
3634
|
+
image: z2.string().url().describe(
|
|
2377
3635
|
"Optional image URL rendered as the option's preview (plain image, not sandboxed). For agent-generated HTML/CSS mockups, use `html` instead."
|
|
2378
3636
|
).optional()
|
|
2379
3637
|
});
|
|
2380
|
-
var VisualSchema =
|
|
2381
|
-
url:
|
|
2382
|
-
label:
|
|
3638
|
+
var VisualSchema = z2.object({
|
|
3639
|
+
url: z2.string().url(),
|
|
3640
|
+
label: z2.string().optional()
|
|
2383
3641
|
});
|
|
2384
|
-
var NotifyLevelSchema =
|
|
2385
|
-
var SelectShapeSchema =
|
|
2386
|
-
var ReceiptEventSchema =
|
|
3642
|
+
var NotifyLevelSchema = z2.enum(["inbox", "push", "banner", "call"]);
|
|
3643
|
+
var SelectShapeSchema = z2.enum(["one", "many", "rank", "confirm", "text"]);
|
|
3644
|
+
var ReceiptEventSchema = z2.enum([
|
|
2387
3645
|
"delivered",
|
|
2388
3646
|
// the bundle reached the recipient at some level
|
|
2389
3647
|
"seen",
|
|
@@ -2413,41 +3671,44 @@ var ReceiptEventSchema = z.enum([
|
|
|
2413
3671
|
// be rewound by a writer that forgot to advance it.
|
|
2414
3672
|
"restarted"
|
|
2415
3673
|
]);
|
|
2416
|
-
var AttentionSchema =
|
|
3674
|
+
var AttentionSchema = z2.object({
|
|
2417
3675
|
urgency: NotifyLevelSchema,
|
|
2418
3676
|
/** The required answer shape, or null for a plain notify that asks nothing back. */
|
|
2419
3677
|
select: SelectShapeSchema.nullable(),
|
|
2420
3678
|
/** Coverage contract (#396) — points the answer must address; null = none declared. */
|
|
2421
|
-
points:
|
|
3679
|
+
points: z2.array(z2.string()).nullable(),
|
|
2422
3680
|
/** Whether the ask blocks the sender — what lets arbitration escalate it on silence. */
|
|
2423
|
-
blocking:
|
|
3681
|
+
blocking: z2.boolean(),
|
|
2424
3682
|
/** Reserved (MODEL.md lists it): a response deadline. No row column yet — a later Phase 2
|
|
2425
3683
|
* slice wires it; optional so today's rows/callers project cleanly. */
|
|
2426
|
-
deadline:
|
|
3684
|
+
deadline: z2.string().datetime().nullable().optional()
|
|
2427
3685
|
});
|
|
2428
|
-
var
|
|
3686
|
+
var NotifyRequestFields = z2.object({
|
|
2429
3687
|
/** Plaintext message content. Present on the plaintext path (today's shape);
|
|
2430
3688
|
* ABSENT on the E2EE path, where the sealed `envelope` below carries it. The
|
|
2431
3689
|
* superRefine at the bottom enforces exactly one of the two. */
|
|
2432
3690
|
context: ContextSchema.optional(),
|
|
2433
|
-
options:
|
|
3691
|
+
options: z2.array(OptionSchema.omit({ id: true })).min(OPTIONS_MIN).max(OPTIONS_MAX).optional().describe(
|
|
2434
3692
|
"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)."
|
|
2435
3693
|
),
|
|
2436
|
-
points:
|
|
3694
|
+
points: z2.array(z2.string().min(1)).optional().describe(
|
|
2437
3695
|
"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."
|
|
2438
3696
|
),
|
|
2439
|
-
visuals:
|
|
3697
|
+
visuals: z2.array(VisualSchema).optional().describe(
|
|
2440
3698
|
"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."
|
|
2441
3699
|
),
|
|
2442
3700
|
/** Git repo the agent is working in ("owner/name"). Local MCP fills this from the checkout — omit unless overriding. */
|
|
2443
|
-
repo:
|
|
3701
|
+
repo: z2.string().optional(),
|
|
2444
3702
|
/** Git branch the agent is on. Local MCP fills this from the checkout — omit unless overriding. */
|
|
2445
|
-
branch:
|
|
3703
|
+
branch: z2.string().optional(),
|
|
2446
3704
|
/** Continue an existing conversation — the id of any notification in it (its root
|
|
2447
3705
|
* is the conversation's identity). Omitted = start a new conversation. Renamed
|
|
2448
3706
|
* from `parentId` (2026-08-03): one linkage system, the parent; the API edge
|
|
2449
3707
|
* still accepts the old name from older clients. */
|
|
2450
|
-
parentId:
|
|
3708
|
+
parentId: z2.string().uuid().optional(),
|
|
3709
|
+
/** The durable outcome this contact advances. Optional during the notification-to-Work
|
|
3710
|
+
* migration; when present, a blocking ask creates a DecisionNeed for this Work. */
|
|
3711
|
+
workId: z2.string().uuid().optional(),
|
|
2451
3712
|
urgency: NotifyLevelSchema.default("inbox").describe(
|
|
2452
3713
|
"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."
|
|
2453
3714
|
),
|
|
@@ -2455,7 +3716,7 @@ var NotifyRequestSchema = z.object({
|
|
|
2455
3716
|
* visible and marks it needs_input. Renamed from the old `parentId` (2026-08-03)
|
|
2456
3717
|
* when `parentId` became the conversation handle: `parentId` says WHERE, this
|
|
2457
3718
|
* says HOW. */
|
|
2458
|
-
clarifies:
|
|
3719
|
+
clarifies: z2.string().optional(),
|
|
2459
3720
|
/** E2EE (text lane): when the pairing is E2EE, the sealed replacements for the
|
|
2460
3721
|
* plaintext content fields, keyed by field name. FINALIZED wire shape (was
|
|
2461
3722
|
* provisional in the storage PR): a per-field map `{ context?, options?,
|
|
@@ -2468,10 +3729,10 @@ var NotifyRequestSchema = z.object({
|
|
|
2468
3729
|
* notifications.envelope and relays it blindly; it never decrypts. Absent =
|
|
2469
3730
|
* today's plaintext path (context/options/visuals carry the cleartext).
|
|
2470
3731
|
* z.lazy because EnvelopeSchema is declared further down (E2EE section). */
|
|
2471
|
-
envelope:
|
|
2472
|
-
context:
|
|
2473
|
-
options:
|
|
2474
|
-
visuals:
|
|
3732
|
+
envelope: z2.object({
|
|
3733
|
+
context: z2.lazy(() => EnvelopeSchema).optional(),
|
|
3734
|
+
options: z2.lazy(() => EnvelopeSchema).optional(),
|
|
3735
|
+
visuals: z2.lazy(() => EnvelopeSchema).optional()
|
|
2475
3736
|
}).optional(),
|
|
2476
3737
|
select: SelectShapeSchema.optional().describe(
|
|
2477
3738
|
"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."
|
|
@@ -2486,20 +3747,20 @@ var NotifyRequestSchema = z.object({
|
|
|
2486
3747
|
// (broker/agenda-design.md) — not by a wire cap the agent has to pre-summarize under.
|
|
2487
3748
|
// Owner, 2026-07-28: "our actual limitation on how long something is to the user should
|
|
2488
3749
|
// come from the broker splitting and summarizing." The cap that remains is a size guard.
|
|
2489
|
-
ask:
|
|
3750
|
+
ask: z2.string().min(1).max(1e4).optional().describe(
|
|
2490
3751
|
'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.'
|
|
2491
3752
|
),
|
|
2492
|
-
needs:
|
|
3753
|
+
needs: z2.array(z2.string().min(1)).optional().describe(
|
|
2493
3754
|
"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."
|
|
2494
3755
|
),
|
|
2495
|
-
urgencyHint:
|
|
3756
|
+
urgencyHint: z2.enum(["whenever", "soon", "now"]).optional().describe(
|
|
2496
3757
|
"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."
|
|
2497
3758
|
),
|
|
2498
3759
|
/** #575: the ONE self-report that replaces urgencyHint + blocking — what happens
|
|
2499
3760
|
* to the agent's work while it waits. Normalized server-side into those two
|
|
2500
3761
|
* fields (normalizeWaiting) so everything downstream is untouched; explicit
|
|
2501
3762
|
* urgencyHint/blocking win when both are sent. */
|
|
2502
|
-
waiting:
|
|
3763
|
+
waiting: z2.enum(["none", "soft", "hard"]).optional().describe(
|
|
2503
3764
|
"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."
|
|
2504
3765
|
),
|
|
2505
3766
|
/** Δ9b (#895): HOLD this claim so the sender can correct the plan before anyone is
|
|
@@ -2507,57 +3768,58 @@ var NotifyRequestSchema = z.object({
|
|
|
2507
3768
|
* holding by default would charge every quiet claim that minute before any agent could
|
|
2508
3769
|
* correct anything. Ignored for `waiting: 'hard'`: a blocking ask rings on what we have,
|
|
2509
3770
|
* and the enrichment can still land mid-call (#781 re-plans the unspoken tail). */
|
|
2510
|
-
confirm:
|
|
3771
|
+
confirm: z2.boolean().optional().describe(
|
|
2511
3772
|
"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'."
|
|
2512
3773
|
),
|
|
2513
3774
|
/** #575: a RELAY of the user's explicitly stated preference, never the agent's
|
|
2514
3775
|
* choice. Outranks waiting in both directions: 'call' rings even for a
|
|
2515
3776
|
* waiting:'none' "call me when it's done"; 'message' never rings even for
|
|
2516
3777
|
* waiting:'hard'. */
|
|
2517
|
-
channel:
|
|
3778
|
+
channel: z2.enum(["call", "message"]).optional().describe(
|
|
2518
3779
|
"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."
|
|
2519
3780
|
),
|
|
2520
|
-
confirmStyle:
|
|
3781
|
+
confirmStyle: z2.enum(["yesno", "approve"]).default("yesno").describe(
|
|
2521
3782
|
"Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
|
|
2522
3783
|
),
|
|
2523
|
-
blocking:
|
|
3784
|
+
blocking: z2.boolean().default(false).describe(
|
|
2524
3785
|
"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."
|
|
2525
3786
|
)
|
|
2526
|
-
})
|
|
3787
|
+
});
|
|
3788
|
+
var NotifyRequestSchema = NotifyRequestFields.superRefine((r, ctx) => {
|
|
2527
3789
|
const sealed = !!r.envelope;
|
|
2528
3790
|
if (sealed) {
|
|
2529
3791
|
if (!r.envelope?.context)
|
|
2530
|
-
ctx.addIssue({ code:
|
|
3792
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["envelope", "context"], message: "sealed request must include envelope.context" });
|
|
2531
3793
|
for (const f of ["context", "options", "visuals"]) {
|
|
2532
3794
|
if (r[f] !== void 0)
|
|
2533
|
-
ctx.addIssue({ code:
|
|
3795
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: [f], message: `E2EE request must not carry plaintext ${f} \u2014 it's sealed in envelope.${f}` });
|
|
2534
3796
|
}
|
|
2535
3797
|
if (r.points !== void 0)
|
|
2536
|
-
ctx.addIssue({ code:
|
|
3798
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["points"], message: "E2EE request must not carry plaintext points" });
|
|
2537
3799
|
if (r.ask !== void 0)
|
|
2538
|
-
ctx.addIssue({ code:
|
|
3800
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["ask"], message: "E2EE request must not carry a plaintext ask \u2014 derive the shape agent-side and seal it" });
|
|
2539
3801
|
if (r.needs !== void 0)
|
|
2540
|
-
ctx.addIssue({ code:
|
|
3802
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["needs"], message: "E2EE request must not carry plaintext needs" });
|
|
2541
3803
|
return;
|
|
2542
3804
|
}
|
|
2543
3805
|
if (r.ask !== void 0) {
|
|
2544
3806
|
for (const f of ["context", "select", "points"]) {
|
|
2545
3807
|
if (r[f] !== void 0)
|
|
2546
|
-
ctx.addIssue({ code:
|
|
3808
|
+
ctx.addIssue({ code: z2.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.` });
|
|
2547
3809
|
}
|
|
2548
3810
|
return;
|
|
2549
3811
|
}
|
|
2550
3812
|
if (r.needs !== void 0 || r.urgencyHint !== void 0)
|
|
2551
|
-
ctx.addIssue({ code:
|
|
3813
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["needs"], message: "needs/urgencyHint belong to the simplified `ask` form \u2014 with a shaped request use points/urgency" });
|
|
2552
3814
|
if (!r.context)
|
|
2553
|
-
ctx.addIssue({ code:
|
|
3815
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["context"], message: "context is required (plaintext path)" });
|
|
2554
3816
|
if (!r.select)
|
|
2555
|
-
ctx.addIssue({ code:
|
|
3817
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["select"], message: "select is required on the shaped form" });
|
|
2556
3818
|
const needsOptions = r.select === "one" || r.select === "many" || r.select === "rank";
|
|
2557
3819
|
if (needsOptions && !r.options?.length)
|
|
2558
|
-
ctx.addIssue({ code:
|
|
3820
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' requires options` });
|
|
2559
3821
|
if (!needsOptions && r.options?.length)
|
|
2560
|
-
ctx.addIssue({ code:
|
|
3822
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' takes no options` });
|
|
2561
3823
|
});
|
|
2562
3824
|
function normalizeWaiting(req) {
|
|
2563
3825
|
if (!req.waiting) return req;
|
|
@@ -2614,61 +3876,70 @@ function deriveAsk(req) {
|
|
|
2614
3876
|
...needs?.length ? { points: needs } : {}
|
|
2615
3877
|
};
|
|
2616
3878
|
}
|
|
2617
|
-
var NotifyStatusSchema =
|
|
2618
|
-
var AgentStateSchema =
|
|
2619
|
-
var SetTaskStateSchema =
|
|
2620
|
-
state:
|
|
3879
|
+
var NotifyStatusSchema = z2.enum(["pending", "answered", "ignored"]);
|
|
3880
|
+
var AgentStateSchema = z2.enum(["idle", "in_progress", "completed", "needs_input"]);
|
|
3881
|
+
var SetTaskStateSchema = z2.object({
|
|
3882
|
+
state: z2.enum(["in_progress", "completed", "needs_input"])
|
|
2621
3883
|
});
|
|
2622
|
-
var
|
|
2623
|
-
|
|
2624
|
-
|
|
3884
|
+
var SetWorkStateSchema = z2.object({
|
|
3885
|
+
workId: z2.string().uuid().optional(),
|
|
3886
|
+
notificationId: z2.string().optional(),
|
|
3887
|
+
state: SetTaskStateSchema.shape.state
|
|
3888
|
+
}).refine((value) => Number(Boolean(value.workId)) + Number(Boolean(value.notificationId)) === 1, {
|
|
3889
|
+
message: "exactly one of workId or notificationId is required"
|
|
2625
3890
|
});
|
|
2626
|
-
var
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
3891
|
+
var TurnSchema = z2.object({
|
|
3892
|
+
prompt: z2.string(),
|
|
3893
|
+
reply: z2.string()
|
|
3894
|
+
});
|
|
3895
|
+
var UserAnswerSchema = z2.discriminatedUnion("kind", [
|
|
3896
|
+
z2.object({ kind: z2.literal("option"), optionId: z2.string(), label: z2.string().optional() }),
|
|
3897
|
+
z2.object({ kind: z2.literal("text"), text: z2.string() }),
|
|
3898
|
+
z2.object({ kind: z2.literal("ignored") }),
|
|
3899
|
+
z2.object({ kind: z2.literal("multi"), optionIds: z2.array(z2.string()), labels: z2.array(z2.string()).optional() }),
|
|
3900
|
+
z2.object({ kind: z2.literal("ranked"), optionIds: z2.array(z2.string()), labels: z2.array(z2.string()).optional() }),
|
|
3901
|
+
z2.object({ kind: z2.literal("clarify"), chunks: z2.array(z2.string()).min(1) }),
|
|
3902
|
+
z2.object({ kind: z2.literal("confirm"), approved: z2.boolean() }),
|
|
3903
|
+
z2.object({ kind: z2.literal("turns"), turns: z2.array(TurnSchema).min(1) }),
|
|
2635
3904
|
/** An auto-answer derived from the user's PAST decisions (broker/precedent-design.md §2):
|
|
2636
3905
|
* delivered through the same settle/await path as a human answer, carrying the judge's
|
|
2637
3906
|
* derivation and the precedent ids it grew from. Always paired with a visible trail
|
|
2638
3907
|
* card the user can reply to — the broker never overrides the user. */
|
|
2639
|
-
|
|
3908
|
+
z2.object({ kind: z2.literal("precedent"), answer: z2.string(), derivation: z2.string(), sources: z2.array(z2.string()).min(1) })
|
|
2640
3909
|
]);
|
|
2641
|
-
var IntentSchema =
|
|
3910
|
+
var IntentSchema = z2.object({
|
|
2642
3911
|
// The full vocabulary the bot's mapper emits (mapper.INTENT_KINDS) — the schema lagged
|
|
2643
3912
|
// it by two ("detail", "feedback"), and because the settle handler parsed the array
|
|
2644
3913
|
// all-or-nothing, ONE feedback act silently dropped EVERY intent on the call,
|
|
2645
3914
|
// questions included. Found auditing five calls' stored feedback, 2026-08-01.
|
|
2646
|
-
kind:
|
|
2647
|
-
detail:
|
|
3915
|
+
kind: z2.enum(["defer", "delegate", "channel", "question", "detail", "feedback", "command", "control"]),
|
|
3916
|
+
detail: z2.string(),
|
|
2648
3917
|
/** Defer only: seconds until the callback the caller asked for, when something upstream
|
|
2649
3918
|
* already read the time. Nothing sets it today (#397 documented an MCP parser that was
|
|
2650
3919
|
* never written) — the API reads the defer's `detail` itself with `notes/when.ts`
|
|
2651
3920
|
* (`parseDelay`, #1292), and a value here simply wins over that reading. */
|
|
2652
|
-
dueInSeconds:
|
|
3921
|
+
dueInSeconds: z2.number().int().positive().optional(),
|
|
2653
3922
|
/** Feedback only (#812): WHICH failure the complaint names — typed by the mapper that
|
|
2654
3923
|
* already read the utterance, so `feedback_from_call.kind` stops defaulting to
|
|
2655
3924
|
* 'other' on every row. A table that records that something was wrong and nothing
|
|
2656
3925
|
* about what cannot answer "is the bot looping less this week?". */
|
|
2657
|
-
fault:
|
|
3926
|
+
fault: z2.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
|
|
2658
3927
|
});
|
|
2659
|
-
var RideAlongSchema =
|
|
3928
|
+
var RideAlongSchema = z2.object({
|
|
2660
3929
|
/** The note this came from — assign/clarify/close it through /api/notes/:id. */
|
|
2661
|
-
noteId:
|
|
3930
|
+
noteId: z2.string(),
|
|
2662
3931
|
/** What to do, in the owner's own words (the note's headline). Never model-rewritten. */
|
|
2663
|
-
text:
|
|
3932
|
+
text: z2.string(),
|
|
2664
3933
|
/** The thread to report back on, when the note was dispatched over the request rail. */
|
|
2665
|
-
parentId:
|
|
3934
|
+
parentId: z2.string().nullable()
|
|
2666
3935
|
});
|
|
2667
|
-
var AwaitItemSchema =
|
|
2668
|
-
|
|
2669
|
-
type:
|
|
2670
|
-
parentId:
|
|
2671
|
-
notificationId:
|
|
3936
|
+
var AwaitItemSchema = z2.discriminatedUnion("type", [
|
|
3937
|
+
z2.object({
|
|
3938
|
+
type: z2.literal("reply"),
|
|
3939
|
+
parentId: z2.string(),
|
|
3940
|
+
notificationId: z2.string(),
|
|
3941
|
+
workId: z2.string().uuid().optional(),
|
|
3942
|
+
decisionId: z2.string().uuid().optional(),
|
|
2672
3943
|
answer: UserAnswerSchema,
|
|
2673
3944
|
/** E2EE: present when the answer is sealed. The server relays the opaque answer
|
|
2674
3945
|
* envelope + the plaintext `ignored` status hint; the receiving agent OPENS it
|
|
@@ -2676,126 +3947,146 @@ var AwaitItemSchema = z.discriminatedUnion("type", [
|
|
|
2676
3947
|
* against `ignored` as tampering. Absent = today's plaintext answer (in `answer`).
|
|
2677
3948
|
* On a sealed reply the plaintext `answer` is a placeholder (kind reflects only
|
|
2678
3949
|
* the `ignored` bit) — never the real content, which stays sealed. */
|
|
2679
|
-
sealed:
|
|
2680
|
-
/**
|
|
2681
|
-
*
|
|
2682
|
-
*
|
|
2683
|
-
|
|
2684
|
-
|
|
3950
|
+
sealed: z2.lazy(() => SealedAnswerSchema).optional(),
|
|
3951
|
+
/** WHAT THE AGENT CANNOT KNOW FROM THE FIELDS BESIDE IT (owner, 2026-09-04, issue
|
|
3952
|
+
* #1537). One line, built from the record: the ask and the caller's reply VERBATIM,
|
|
3953
|
+
* the notification they belong to, and the `contact` call that reaches the person
|
|
3954
|
+
* again, spelled with its real arguments. It restates nothing — deleting the brain's
|
|
3955
|
+
* acts deleted the paraphrase, and this is not it coming back: an agent that reads
|
|
3956
|
+
* "call me back after you merge" in their own words decides for itself what to do,
|
|
3957
|
+
* and now knows exactly which call to make. Absent when either half is missing —
|
|
3958
|
+
* a sentence with a hole in it is worse than no sentence. */
|
|
3959
|
+
note: z2.string().optional(),
|
|
3960
|
+
/** The call record rendered for THIS agent (`voice/record-design.md`): the words the
|
|
3961
|
+
* shaped answer was mapped from, filtered to its own claims. There is no second list
|
|
3962
|
+
* of labels beside it — the acts went 2026-09-04 and `intents` went with them (owner,
|
|
3963
|
+
* 2026-09-04): the agent reads the sentence and decides. */
|
|
3964
|
+
transcript: z2.string().optional(),
|
|
2685
3965
|
/** Coverage report (#396), when the ask declared `points`: which of them this
|
|
2686
3966
|
* answer addressed. Missing points = re-ask or proceed knowingly partial. */
|
|
2687
|
-
covered:
|
|
3967
|
+
covered: z2.array(z2.string()).optional(),
|
|
2688
3968
|
/** Ride-alongs (RideAlongSchema) — pending work for you, attached to the moment you
|
|
2689
3969
|
* became free. Only `reply` and `idle` carry it: those are the two outcomes that
|
|
2690
3970
|
* END a wait. `remind`, `superseded` and `turn` are mid-flight, and handing an
|
|
2691
3971
|
* agent a side-quest while it is still holding the line is how the main thing gets
|
|
2692
3972
|
* dropped. Absent/empty = nothing owed. */
|
|
2693
|
-
also:
|
|
3973
|
+
also: z2.array(RideAlongSchema).optional()
|
|
2694
3974
|
}),
|
|
2695
|
-
|
|
2696
|
-
type:
|
|
2697
|
-
parentId:
|
|
2698
|
-
notificationId:
|
|
2699
|
-
remindAt:
|
|
3975
|
+
z2.object({
|
|
3976
|
+
type: z2.literal("remind"),
|
|
3977
|
+
parentId: z2.string(),
|
|
3978
|
+
notificationId: z2.string(),
|
|
3979
|
+
remindAt: z2.string().datetime({ offset: true }),
|
|
2700
3980
|
/** Seconds until remindAt, server-computed — pass straight to ScheduleWakeup. */
|
|
2701
|
-
remindInSeconds:
|
|
3981
|
+
remindInSeconds: z2.number()
|
|
2702
3982
|
}),
|
|
2703
3983
|
/** The awaited ask was REPLACED by a newer notification on its thread (e.g. a
|
|
2704
3984
|
* post-feedback revision, #633) — the user will never answer this id. Stop
|
|
2705
3985
|
* awaiting it; the live ask is the thread's newest turn (await that one, or
|
|
2706
3986
|
* re-orient via get_thread / check_replies). */
|
|
2707
|
-
|
|
2708
|
-
type:
|
|
2709
|
-
parentId:
|
|
2710
|
-
notificationId:
|
|
3987
|
+
z2.object({
|
|
3988
|
+
type: z2.literal("superseded"),
|
|
3989
|
+
parentId: z2.string(),
|
|
3990
|
+
notificationId: z2.string()
|
|
2711
3991
|
}),
|
|
2712
3992
|
/** A LIVE call's turn, streamed as it lands (#783). PROVISIONAL: the user can still
|
|
2713
3993
|
* revise any of these until the final reply arrives — partial = intelligence,
|
|
2714
3994
|
* settled = authorization. Use it to PREPARE (fetch, draft, warm), never to act
|
|
2715
3995
|
* irreversibly. If `acts` carries a question aimed at you and you know the answer,
|
|
2716
3996
|
* contact on the same thread right away — the caller hears it on the same call. */
|
|
2717
|
-
|
|
2718
|
-
type:
|
|
2719
|
-
notificationId:
|
|
2720
|
-
inFlight:
|
|
2721
|
-
turn:
|
|
2722
|
-
idx:
|
|
2723
|
-
prompt:
|
|
2724
|
-
reply:
|
|
2725
|
-
acts:
|
|
3997
|
+
z2.object({
|
|
3998
|
+
type: z2.literal("partial"),
|
|
3999
|
+
notificationId: z2.string(),
|
|
4000
|
+
inFlight: z2.literal(true),
|
|
4001
|
+
turn: z2.object({
|
|
4002
|
+
idx: z2.number(),
|
|
4003
|
+
prompt: z2.string(),
|
|
4004
|
+
reply: z2.string(),
|
|
4005
|
+
acts: z2.array(IntentSchema).nullable().optional()
|
|
2726
4006
|
})
|
|
2727
4007
|
}),
|
|
2728
|
-
|
|
4008
|
+
z2.object({ type: z2.literal("idle"), also: z2.array(RideAlongSchema).optional() })
|
|
2729
4009
|
]);
|
|
2730
|
-
var CallbackTriggerSchema =
|
|
2731
|
-
var ScheduleCallbackSchema =
|
|
2732
|
-
parentId:
|
|
4010
|
+
var CallbackTriggerSchema = z2.enum(["on_done", "on_blocked", "scheduled"]);
|
|
4011
|
+
var ScheduleCallbackSchema = z2.object({
|
|
4012
|
+
parentId: z2.string().describe("The thread to call back on (from a prior contact / reply / request)."),
|
|
2733
4013
|
trigger: CallbackTriggerSchema,
|
|
2734
|
-
dueInSeconds:
|
|
2735
|
-
note:
|
|
4014
|
+
dueInSeconds: z2.number().int().positive().optional().describe("For 'scheduled' only: how many seconds from now to fire."),
|
|
4015
|
+
note: z2.string().optional().describe("What to tell the user when you follow up.")
|
|
2736
4016
|
});
|
|
2737
|
-
var PendingRepliesSchema =
|
|
2738
|
-
replies:
|
|
2739
|
-
|
|
2740
|
-
parentId:
|
|
2741
|
-
notificationId:
|
|
4017
|
+
var PendingRepliesSchema = z2.object({
|
|
4018
|
+
replies: z2.array(
|
|
4019
|
+
z2.object({
|
|
4020
|
+
parentId: z2.string(),
|
|
4021
|
+
notificationId: z2.string(),
|
|
4022
|
+
workId: z2.string().uuid().optional(),
|
|
4023
|
+
decisionId: z2.string().uuid().optional(),
|
|
2742
4024
|
answer: UserAnswerSchema,
|
|
2743
4025
|
/** E2EE: the sealed answer (opaque envelope + plaintext `ignored` hint) when the
|
|
2744
4026
|
* pairing is E2EE — the agent opens it and re-derives the real answer. Absent =
|
|
2745
4027
|
* plaintext answer (in `answer`). See AwaitItemSchema's reply variant. */
|
|
2746
|
-
sealed:
|
|
2747
|
-
/**
|
|
2748
|
-
*
|
|
2749
|
-
|
|
2750
|
-
intents: z.array(IntentSchema).optional(),
|
|
2751
|
-
transcript: z.string().optional(),
|
|
4028
|
+
sealed: z2.lazy(() => SealedAnswerSchema).optional(),
|
|
4029
|
+
/** The call record rendered for THIS agent: the raw words the shaped answer was
|
|
4030
|
+
* mapped from, filtered to its own claims. See AwaitItemSchema's reply variant. */
|
|
4031
|
+
transcript: z2.string().optional(),
|
|
2752
4032
|
/** Coverage report (#396): which declared `points` this answer addressed. */
|
|
2753
|
-
covered:
|
|
4033
|
+
covered: z2.array(z2.string()).optional()
|
|
2754
4034
|
})
|
|
2755
4035
|
),
|
|
2756
|
-
pending:
|
|
2757
|
-
|
|
4036
|
+
pending: z2.array(
|
|
4037
|
+
z2.object({ parentId: z2.string(), notificationId: z2.string(), createdAt: z2.string() })
|
|
2758
4038
|
),
|
|
2759
4039
|
/** WHO YOU ARE on this account (field report 2026-08-28): the name and device the user
|
|
2760
4040
|
* sees for this session's identity. From inside a session there was no way to find out —
|
|
2761
4041
|
* `pair` with no arguments can HATCH a fresh identity, so it is not a safe probe — and an
|
|
2762
4042
|
* agent that cannot tell which agent it is cannot tell whether work addressed to
|
|
2763
4043
|
* "Reta" was addressed to it. Absent only for a token with no pairing behind it. */
|
|
2764
|
-
you:
|
|
4044
|
+
you: z2.object({ name: z2.string(), device: z2.string().nullable(), tokenId: z2.string() }).optional(),
|
|
2765
4045
|
/** User-initiated requests addressed to this agent; act on them and reply via
|
|
2766
4046
|
* contact on the same parentId. Keeps reappearing until you call
|
|
2767
|
-
*
|
|
2768
|
-
requests:
|
|
2769
|
-
|
|
2770
|
-
parentId:
|
|
2771
|
-
notificationId:
|
|
2772
|
-
|
|
2773
|
-
|
|
4047
|
+
* set_work_state on its workId (or notificationId once while bootstrapping). */
|
|
4048
|
+
requests: z2.array(
|
|
4049
|
+
z2.object({
|
|
4050
|
+
parentId: z2.string(),
|
|
4051
|
+
notificationId: z2.string(),
|
|
4052
|
+
workId: z2.string().uuid().optional(),
|
|
4053
|
+
text: z2.string(),
|
|
4054
|
+
createdAt: z2.string(),
|
|
2774
4055
|
/** The user seeded this request with a past conversation — call get_thread on it
|
|
2775
4056
|
* FIRST and treat the transcript as prior context (#57/#251). */
|
|
2776
|
-
contextParentId:
|
|
4057
|
+
contextParentId: z2.string().optional(),
|
|
2777
4058
|
/** STRANDED (field report 2026-08-28): this request was addressed to ANOTHER agent on
|
|
2778
4059
|
* the account — the name here — which has not been seen since it landed, so nobody
|
|
2779
4060
|
* came for it. Handed to you because you are the session that is here. Take it like
|
|
2780
|
-
* any request (
|
|
4061
|
+
* any request (set_work_state claims it, reply with contact on its parentId), and say
|
|
2781
4062
|
* whose it was, because the user chose that agent on purpose. */
|
|
2782
|
-
stranded:
|
|
4063
|
+
stranded: z2.string().optional()
|
|
2783
4064
|
})
|
|
2784
4065
|
),
|
|
4066
|
+
/** Durable outcomes currently owned by this agent. This is the Work-native queue; flat
|
|
4067
|
+
* notification lists remain during migration so old clients keep their existing view. */
|
|
4068
|
+
work: z2.array(z2.object({
|
|
4069
|
+
workId: z2.string().uuid(),
|
|
4070
|
+
parentId: z2.string().optional(),
|
|
4071
|
+
objective: z2.string().nullable(),
|
|
4072
|
+
state: z2.enum(["active", "blocked", "waiting_external"]),
|
|
4073
|
+
blockedOn: z2.array(z2.string().uuid()),
|
|
4074
|
+
updatedAt: z2.string()
|
|
4075
|
+
})).optional(),
|
|
2785
4076
|
/** Callbacks you owe the user that are now DUE (you said you'd follow up when done,
|
|
2786
4077
|
* if blocked, or at a time that has passed). Re-surfaced every sweep until you
|
|
2787
4078
|
* fulfill one by calling contact on its parentId. */
|
|
2788
4079
|
/** Ride-alongs (RideAlongSchema): notes assigned to this agent that no wake could
|
|
2789
4080
|
* reach. Same array the contact/await replies carry — one queue, every carrier. */
|
|
2790
|
-
also:
|
|
2791
|
-
owedCallbacks:
|
|
2792
|
-
|
|
4081
|
+
also: z2.array(RideAlongSchema).optional(),
|
|
4082
|
+
owedCallbacks: z2.array(
|
|
4083
|
+
z2.object({ parentId: z2.string(), trigger: CallbackTriggerSchema, note: z2.string() })
|
|
2793
4084
|
),
|
|
2794
4085
|
/** Work (either direction) you reported in_progress a while ago and never reported
|
|
2795
4086
|
* completed — likely left half-done by this session or a prior one that crashed or
|
|
2796
|
-
* went idle. Report a real state (
|
|
2797
|
-
stalled:
|
|
2798
|
-
|
|
4087
|
+
* went idle. Report a real state (set_work_state) or continue the work. */
|
|
4088
|
+
stalled: z2.array(
|
|
4089
|
+
z2.object({ parentId: z2.string(), notificationId: z2.string(), title: z2.string().nullable(), startedAt: z2.string() })
|
|
2799
4090
|
),
|
|
2800
4091
|
/** The queue rail (#614, pending/design.md): the same replies + requests, grouped by
|
|
2801
4092
|
* thread and ordered oldest-thread-first, so you work ONE thread at a time — fold all of
|
|
@@ -2804,123 +4095,125 @@ var PendingRepliesSchema = z.object({
|
|
|
2804
4095
|
* ride the next turn. `items` are that thread's replies/requests in arrival order; the
|
|
2805
4096
|
* full payload for each is in the flat `replies`/`requests` arrays (matched by
|
|
2806
4097
|
* notificationId). Derived, never stored — a crashed agent recomputes it exactly. */
|
|
2807
|
-
threads:
|
|
2808
|
-
|
|
2809
|
-
parentId:
|
|
2810
|
-
busy:
|
|
2811
|
-
items:
|
|
2812
|
-
|
|
2813
|
-
kind:
|
|
2814
|
-
notificationId:
|
|
2815
|
-
at:
|
|
4098
|
+
threads: z2.array(
|
|
4099
|
+
z2.object({
|
|
4100
|
+
parentId: z2.string(),
|
|
4101
|
+
busy: z2.boolean(),
|
|
4102
|
+
items: z2.array(
|
|
4103
|
+
z2.object({
|
|
4104
|
+
kind: z2.enum(["reply", "request"]),
|
|
4105
|
+
notificationId: z2.string(),
|
|
4106
|
+
at: z2.string()
|
|
2816
4107
|
})
|
|
2817
4108
|
)
|
|
2818
4109
|
})
|
|
2819
4110
|
)
|
|
2820
4111
|
});
|
|
2821
|
-
var NotifyResponseSchema =
|
|
2822
|
-
notificationId:
|
|
4112
|
+
var NotifyResponseSchema = z2.object({
|
|
4113
|
+
notificationId: z2.string(),
|
|
4114
|
+
workId: z2.string().uuid().optional(),
|
|
4115
|
+
decisionId: z2.string().uuid().optional(),
|
|
2823
4116
|
status: NotifyStatusSchema,
|
|
2824
|
-
createdAt:
|
|
4117
|
+
createdAt: z2.string().datetime(),
|
|
2825
4118
|
answer: UserAnswerSchema.optional(),
|
|
2826
|
-
answeredAt:
|
|
4119
|
+
answeredAt: z2.string().datetime().optional(),
|
|
2827
4120
|
/** Ride-alongs for THIS agent — pending work it should pick up when it's done with
|
|
2828
4121
|
* what it came for. Present on any reply, because an unwakeable agent's only
|
|
2829
4122
|
* reliable moment is one it initiated. Absent/empty = nothing owed. */
|
|
2830
|
-
also:
|
|
4123
|
+
also: z2.array(RideAlongSchema).optional()
|
|
2831
4124
|
});
|
|
2832
|
-
var NotifyPlanUnitSchema =
|
|
2833
|
-
notificationId:
|
|
4125
|
+
var NotifyPlanUnitSchema = z2.object({
|
|
4126
|
+
notificationId: z2.string(),
|
|
2834
4127
|
/** The unit's own heading, so the agent can tell which of its paragraphs this became. */
|
|
2835
|
-
title:
|
|
4128
|
+
title: z2.string(),
|
|
2836
4129
|
/** How loudly this unit was arbitrated to arrive — per unit, which is the point of units. */
|
|
2837
4130
|
level: NotifyLevelSchema,
|
|
2838
4131
|
/** Answered from something the user already decided: nobody is interrupted, and a trail card
|
|
2839
4132
|
* says so. The agent should not wait on this one. */
|
|
2840
|
-
settled:
|
|
4133
|
+
settled: z2.literal(true).optional(),
|
|
2841
4134
|
/** What this unit would need to be answerable and does not carry (#894). A PROPOSAL to the
|
|
2842
4135
|
* agent — nothing here changed the ask, and ignoring it costs nothing. */
|
|
2843
|
-
needs:
|
|
4136
|
+
needs: z2.array(z2.enum(["options", "visuals"])).optional(),
|
|
2844
4137
|
/** The SHAPE the broker would give this unit, for the agent to ratify (#886/#894). The
|
|
2845
4138
|
* split layer reads prose and can see that a paragraph is a yes/no or a pick-one — but a
|
|
2846
4139
|
* broker that DECIDES that destroys the only fact separating a statement from a real ask
|
|
2847
4140
|
* (#731), so it is offered, never applied: the unit is stored `text` until the agent
|
|
2848
4141
|
* confirms the shape (POST /notify/:id/confirm). Ignoring it costs nothing. */
|
|
2849
|
-
proposal:
|
|
4142
|
+
proposal: z2.object({
|
|
2850
4143
|
select: SelectShapeSchema,
|
|
2851
|
-
options:
|
|
4144
|
+
options: z2.array(z2.object({ label: z2.string().min(1) })).optional()
|
|
2852
4145
|
}).optional(),
|
|
2853
4146
|
/** What the broker READ this unit as wanting from the human (#952 layer 2): a `decision`
|
|
2854
4147
|
* between alternatives, an `approval` the agent is blocked on, or `knowledge` it just
|
|
2855
4148
|
* needs to know. Reported so the agent can correct a misread the same way it ratifies a
|
|
2856
4149
|
* shape — the read RAISES (a decision always asks) and never silences a question the
|
|
2857
4150
|
* agent declared (#731, #923). */
|
|
2858
|
-
wants:
|
|
4151
|
+
wants: z2.enum(["decision", "approval", "knowledge"]).optional()
|
|
2859
4152
|
});
|
|
2860
|
-
var NotifyPlanSchema =
|
|
2861
|
-
units:
|
|
4153
|
+
var NotifyPlanSchema = z2.object({
|
|
4154
|
+
units: z2.array(NotifyPlanUnitSchema),
|
|
2862
4155
|
/** Which unit is this arrival's ONE interruption (units-design.md D23). Absent means nobody
|
|
2863
4156
|
* was interrupted — every unit was either settled or quiet enough to sit in the inbox. */
|
|
2864
|
-
speaks:
|
|
4157
|
+
speaks: z2.string().optional()
|
|
2865
4158
|
});
|
|
2866
|
-
var UserResponseSchema =
|
|
2867
|
-
requestId:
|
|
4159
|
+
var UserResponseSchema = z2.object({
|
|
4160
|
+
requestId: z2.string(),
|
|
2868
4161
|
answer: UserAnswerSchema,
|
|
2869
|
-
answeredAt:
|
|
2870
|
-
|
|
2871
|
-
transcript: z.string().optional(),
|
|
4162
|
+
answeredAt: z2.string().datetime(),
|
|
4163
|
+
transcript: z2.string().optional(),
|
|
2872
4164
|
/** E2EE: the sealed answer (opaque envelope + plaintext `ignored` hint) when the item was
|
|
2873
4165
|
* E2EE-sealed. Present → the server persists it opaquely and branches status on `ignored`;
|
|
2874
4166
|
* the plaintext `answer` is a placeholder (`{ kind: "ignored" }`) the server ignores for a
|
|
2875
4167
|
* sealed row. Absent = today's plaintext answer, unchanged. */
|
|
2876
|
-
sealed:
|
|
4168
|
+
sealed: z2.lazy(() => SealedAnswerSchema).optional(),
|
|
2877
4169
|
/** Coverage report (#396): which of the ask's declared `points` were addressed. */
|
|
2878
|
-
covered:
|
|
4170
|
+
covered: z2.array(z2.string()).optional()
|
|
2879
4171
|
});
|
|
2880
|
-
var VoiceKeySchema =
|
|
2881
|
-
var AgendaTurnSchema =
|
|
4172
|
+
var VoiceKeySchema = z2.enum(["rachel", "george", "jessica", "brian", "lily"]);
|
|
4173
|
+
var AgendaTurnSchema = z2.object({
|
|
2882
4174
|
/** Twin coverage (#1089): sibling claim ids this asking turn's answer ALSO settles —
|
|
2883
4175
|
* the planner declares duplicates instead of asking them twice. */
|
|
2884
|
-
coveredIds:
|
|
4176
|
+
coveredIds: z2.array(z2.string()).optional(),
|
|
2885
4177
|
/** At most three short spoken sentences. Capped because a turn is a breath: a 1031-char
|
|
2886
4178
|
* line went out on 2026-07-28 and the caller could not answer it at all. */
|
|
2887
|
-
info:
|
|
2888
|
-
question:
|
|
4179
|
+
info: z2.array(z2.string().min(1)).max(3).default([]),
|
|
4180
|
+
question: z2.string().min(1).nullable(),
|
|
2889
4181
|
/** True on the one turn carrying the agent's own declared question. */
|
|
2890
|
-
asks:
|
|
4182
|
+
asks: z2.boolean().optional(),
|
|
2891
4183
|
/** The claim this turn belongs to (#781) — the RETURN identity: answers route by it.
|
|
2892
4184
|
* Absent on a single-claim plan (the session's own claim) and on shared context turns,
|
|
2893
4185
|
* which route nothing. */
|
|
2894
|
-
claimId:
|
|
4186
|
+
claimId: z2.string().optional(),
|
|
2895
4187
|
/** The claim's voice key (#462) — the OUTBOUND identity, audible who-is-asking. */
|
|
2896
|
-
voice:
|
|
4188
|
+
voice: z2.string().optional(),
|
|
2897
4189
|
/** The claim's AGENT NAME (#838) — the spoken identity. A voice alone doesn't say
|
|
2898
4190
|
* whose request this is: an item that folded in from another agent arrived as a bare
|
|
2899
4191
|
* non-sequitur ("First real production sign-in is yours to make whenever you want.")
|
|
2900
4192
|
* and the owner answered "What?". The bot names the agent before its first turn. */
|
|
2901
|
-
agent:
|
|
4193
|
+
agent: z2.string().optional(),
|
|
4194
|
+
/** The claim's agent by ID — the pairing's connection id (`notifications.token_id`), the
|
|
4195
|
+
* same id a face is minted from. A name is not an identity: two pairings may be called
|
|
4196
|
+
* "Claude", and a name cannot be joined on. The record's entries carry it (`agent_id`)
|
|
4197
|
+
* so "who said that" survives the call, and it rides PER TURN because a coalesced call
|
|
4198
|
+
* speaks for several agents — the turn is the only place that knows which. */
|
|
4199
|
+
agentId: z2.string().optional(),
|
|
2902
4200
|
select: SelectShapeSchema.optional(),
|
|
2903
|
-
options:
|
|
2904
|
-
/** The seat was satisfied by the call's own ACCOUNT (replan-design.md, user_info): the
|
|
2905
|
-
* caller already answered this claim in an earlier utterance, quoted here VERBATIM —
|
|
2906
|
-
* the bot speaks the turn's short confirmation, posts these words as the claim's
|
|
2907
|
-
* answer, and never re-asks. Grounded at parse time: an invented settle is #796. */
|
|
2908
|
-
settle: z.string().optional(),
|
|
4201
|
+
options: z2.array(OptionSchema.omit({ id: true })).optional(),
|
|
2909
4202
|
/** Pacing (#826, owner 2026-08-03: "how fast we move through them ... are parameters"):
|
|
2910
4203
|
* seconds the floor stays open after this turn speaks. Absent = the bot's defaults
|
|
2911
4204
|
* (the beat for context, the answer window for asks). Clamped bot-side. */
|
|
2912
|
-
pace:
|
|
4205
|
+
pace: z2.number().positive().optional(),
|
|
2913
4206
|
/** Whether the walk WAITS for an answer before moving on. Absent = derived as today
|
|
2914
4207
|
* (a question blocks, context flows). blocking:false on a question = ask and move
|
|
2915
4208
|
* on, the claim stays pending; blocking:true on context = hold for a reply. */
|
|
2916
|
-
blocking:
|
|
4209
|
+
blocking: z2.boolean().optional()
|
|
2917
4210
|
});
|
|
2918
4211
|
var CLAIM_STALE_MS = 30 * 6e4;
|
|
2919
|
-
var InboxItemSchema =
|
|
2920
|
-
id:
|
|
4212
|
+
var InboxItemSchema = z2.object({
|
|
4213
|
+
id: z2.string(),
|
|
2921
4214
|
/** The conversation thread + connection this item lives on. Present on the replied
|
|
2922
4215
|
* detail — they power History's "Continue" / "New session from this" (#57/#251). */
|
|
2923
|
-
parentId:
|
|
4216
|
+
parentId: z2.string().optional(),
|
|
2924
4217
|
/** THE ARRIVAL this row is one unit of (`notifications.ask_id` → `asks`). A claim is one
|
|
2925
4218
|
* arrival and its units are N rows of it, so this — not `parentId` — is what makes a
|
|
2926
4219
|
* multi-part notification one thing on screen. The thread is the whole CONVERSATION: it
|
|
@@ -2928,13 +4221,13 @@ var InboxItemSchema = z.object({
|
|
|
2928
4221
|
* unrelated updates as a single "12-part request". Absent on rows written before the
|
|
2929
4222
|
* `asks` table, and on anything that never went through `notify` — both fall back to the
|
|
2930
4223
|
* thread, which is what the client did for all rows until now. */
|
|
2931
|
-
askId:
|
|
4224
|
+
askId: z2.string().optional(),
|
|
2932
4225
|
/** WHERE this unit sat in the message it was cut from (`notifications.seq`). The batch
|
|
2933
4226
|
* shares one `created_at` to the microsecond, so without it the author's order is
|
|
2934
4227
|
* unrecoverable client-side — a four-paragraph briefing rendered opening-paragraph-last
|
|
2935
4228
|
* (live 2026-08-10, D35). The API already orders by it; this lets a reader that
|
|
2936
4229
|
* re-sorts (grouping, filtering) put an arrival back in the order it was written. */
|
|
2937
|
-
seq:
|
|
4230
|
+
seq: z2.number().int().optional(),
|
|
2938
4231
|
/** HOW MANY units the arrival was cut into. A device reads a LENS, never the arrival —
|
|
2939
4232
|
* `/api/inbox` serves `open`, so the units already settled are gone from it — and a client
|
|
2940
4233
|
* counting what it can see is counting what is LEFT. Walking a three-unit ask on the answer
|
|
@@ -2943,13 +4236,13 @@ var InboxItemSchema = z.object({
|
|
|
2943
4236
|
* server that can still see every row states it. Absent on any row with no `askId`: a
|
|
2944
4237
|
* unit knows WHICH ask it came from and WHERE it sat in it, and how many there were is
|
|
2945
4238
|
* the one part of its own arrival a single row cannot answer. */
|
|
2946
|
-
units:
|
|
2947
|
-
tokenId:
|
|
4239
|
+
units: z2.number().int().positive().optional(),
|
|
4240
|
+
tokenId: z2.string().optional(),
|
|
2948
4241
|
status: NotifyStatusSchema,
|
|
2949
4242
|
context: ContextSchema,
|
|
2950
|
-
options:
|
|
4243
|
+
options: z2.array(OptionSchema).optional(),
|
|
2951
4244
|
/** The ask's declared coverage points (#396), when the agent sent them. */
|
|
2952
|
-
points:
|
|
4245
|
+
points: z2.array(z2.string()).optional(),
|
|
2953
4246
|
/** The call's AGENDA (broker/agenda-design.md): the ordered turns it is made of, built at
|
|
2954
4247
|
* ring/enqueue time. Replaces the condensed line + index-aligned phrased points, which
|
|
2955
4248
|
* between them could not express a call as a sequence. `question: null` is a real turn —
|
|
@@ -2958,12 +4251,12 @@ var InboxItemSchema = z.object({
|
|
|
2958
4251
|
* `requestAsks` — the agent's own declaration, not a guess. `false` is what earns a card
|
|
2959
4252
|
* its acknowledge affordance: without it a status update offers a text box and a dismiss,
|
|
2960
4253
|
* and neither of those is "got it" (owner, 2026-08-10). */
|
|
2961
|
-
asks:
|
|
4254
|
+
asks: z2.boolean().optional(),
|
|
2962
4255
|
/** When a live process last pulsed for this row's agent — the liveness input for
|
|
2963
4256
|
* "working requires a pulse" (#928): the list said "Working…" from agent_state alone
|
|
2964
4257
|
* while the party called the same dead claim stalled. Absent = no token/no data,
|
|
2965
4258
|
* which must never CLAIM stalled. */
|
|
2966
|
-
lastSeenAt:
|
|
4259
|
+
lastSeenAt: z2.string().optional(),
|
|
2967
4260
|
/** WHEN THE AGENT LAST SAID ANYTHING ABOUT THIS CLAIM — the newest `agent_state` row in
|
|
2968
4261
|
* the `notification_events` ledger (trigger-written since 20260621010000, so every row a
|
|
2969
4262
|
* user can see has one). The age input for `CLAIM_STALE_MS`, and it has to be this rather
|
|
@@ -2973,56 +4266,53 @@ var InboxItemSchema = z.object({
|
|
|
2973
4266
|
* work. Reading the row's birth as the claim's age brands that "No update in 8h" the
|
|
2974
4267
|
* instant the agent picks it up (#997). Absent = pre-trigger row; fall back to
|
|
2975
4268
|
* `createdAt`. */
|
|
2976
|
-
agentStateAt:
|
|
2977
|
-
agenda:
|
|
2978
|
-
|
|
2979
|
-
* ("call back after lunch") — shown so they can see the commitment was captured. */
|
|
2980
|
-
intents: z.array(IntentSchema).optional(),
|
|
2981
|
-
visuals: z.array(VisualSchema).optional(),
|
|
4269
|
+
agentStateAt: z2.string().datetime().optional(),
|
|
4270
|
+
agenda: z2.array(AgendaTurnSchema).optional(),
|
|
4271
|
+
visuals: z2.array(VisualSchema).optional(),
|
|
2982
4272
|
/** The connected agent's name (the single pairing name — user-typed, or the
|
|
2983
4273
|
* agent's suggestion, or a default silly name). */
|
|
2984
|
-
name:
|
|
4274
|
+
name: z2.string(),
|
|
2985
4275
|
/** The pairing's assigned voice (#462); absent = the default voice. */
|
|
2986
4276
|
voice: VoiceKeySchema.optional(),
|
|
2987
|
-
repo:
|
|
2988
|
-
branch:
|
|
2989
|
-
createdAt:
|
|
2990
|
-
snoozedUntil:
|
|
4277
|
+
repo: z2.string().optional(),
|
|
4278
|
+
branch: z2.string().optional(),
|
|
4279
|
+
createdAt: z2.string().datetime(),
|
|
4280
|
+
snoozedUntil: z2.string().datetime().optional(),
|
|
2991
4281
|
agentState: AgentStateSchema.default("idle"),
|
|
2992
4282
|
/** Whose action the item is waiting on: "you" = an agent asked you (the default,
|
|
2993
4283
|
* every agent→user notification); "agent" = you sent a request and it's awaiting the
|
|
2994
4284
|
* agent (held in the inbox until the agent replies on the thread). */
|
|
2995
|
-
turn:
|
|
4285
|
+
turn: z2.enum(["you", "agent"]).default("you"),
|
|
2996
4286
|
/** Hard error reason on an awaiting request (turn="agent") — the wake failed to reach
|
|
2997
4287
|
* the agent (provider-agnostic; set server-side). Absent = no hard error, though the
|
|
2998
4288
|
* client may still flag a stall by age. Drives the inbox error badge + Retry. */
|
|
2999
|
-
error:
|
|
3000
|
-
clarifies:
|
|
4289
|
+
error: z2.string().optional(),
|
|
4290
|
+
clarifies: z2.string().optional(),
|
|
3001
4291
|
/** The ring ladder ran out while this was still pending — we tried to reach you and
|
|
3002
4292
|
* STOPPED trying (`arbitration/arbitrate.ts` `nextRing` → `stop`). Distinct from an
|
|
3003
4293
|
* agent with nothing to say, which the roster drew identically until now: "nothing to
|
|
3004
4294
|
* say" and "gave up saying it" are opposite situations wearing the same face
|
|
3005
4295
|
* (navigation-design.md, gap 1). False for anything that never rang. */
|
|
3006
|
-
gaveUp:
|
|
4296
|
+
gaveUp: z2.boolean().default(false),
|
|
3007
4297
|
/** Why this arrived the way it did, read back off the delivery receipt (`notify/why.ts`).
|
|
3008
4298
|
* Absent for anything never delivered through a push, and for older rows written before
|
|
3009
4299
|
* the reason was recorded. Deliberately a debug affordance, shown small (owner,
|
|
3010
4300
|
* 2026-08-07) — its real job is to give "this didn't need a call" something to be
|
|
3011
4301
|
* feedback ABOUT. */
|
|
3012
|
-
why:
|
|
4302
|
+
why: z2.object({
|
|
3013
4303
|
asked: NotifyLevelSchema,
|
|
3014
4304
|
got: NotifyLevelSchema,
|
|
3015
|
-
because:
|
|
3016
|
-
line:
|
|
4305
|
+
because: z2.enum(["unresponsive", "dismissed", "not_permitted", "silent", "coalesced", "agent_capped", "unplanned", "learned_raise"]).optional(),
|
|
4306
|
+
line: z2.string()
|
|
3017
4307
|
}).optional(),
|
|
3018
|
-
select:
|
|
3019
|
-
confirmStyle:
|
|
4308
|
+
select: z2.enum(["one", "many", "rank", "confirm", "text"]).default("one"),
|
|
4309
|
+
confirmStyle: z2.enum(["yesno", "approve"]).default("yesno").describe(
|
|
3020
4310
|
"Labels for a select:'confirm' paige \u2014 'yesno' (Yes/No) or 'approve' (Approve/Deny). Ignored unless select is 'confirm'."
|
|
3021
4311
|
),
|
|
3022
4312
|
/** Real downstream work is stuck behind this one — set by the agent, independent of
|
|
3023
4313
|
* urgency (see the main README's "premier use case" + notify/states.md). Drives the
|
|
3024
4314
|
* inbox's blocking badge and the extra confirm step before dismissing it. */
|
|
3025
|
-
blocking:
|
|
4315
|
+
blocking: z2.boolean().default(false),
|
|
3026
4316
|
/** The user's locked-in answer; present only for replied items (GET /api/replied/:id). */
|
|
3027
4317
|
answer: UserAnswerSchema.optional(),
|
|
3028
4318
|
/** E2EE (text lane): the sealed content this item carries when the pairing is E2EE, in
|
|
@@ -3031,39 +4321,39 @@ var InboxItemSchema = z.object({
|
|
|
3031
4321
|
* NotifyRequest.envelope. The native app decrypts it locally via openNotification;
|
|
3032
4322
|
* web / an un-enrolled device can't and renders a locked placeholder. Absent = today's
|
|
3033
4323
|
* plaintext item (context carries the cleartext), so plaintext items are unchanged. */
|
|
3034
|
-
envelope:
|
|
3035
|
-
context:
|
|
3036
|
-
options:
|
|
3037
|
-
visuals:
|
|
4324
|
+
envelope: z2.object({
|
|
4325
|
+
context: z2.lazy(() => EnvelopeSchema).optional(),
|
|
4326
|
+
options: z2.lazy(() => EnvelopeSchema).optional(),
|
|
4327
|
+
visuals: z2.lazy(() => EnvelopeSchema).optional()
|
|
3038
4328
|
}).optional(),
|
|
3039
4329
|
/** E2EE: the agent's device X25519 public key to seal the user's answer BACK to (the
|
|
3040
4330
|
* sender the phone replies to). Sourced server-side from this item's pairing credential.
|
|
3041
4331
|
* Present only alongside `envelope`; the phone seals via sealAnswer(answer, this, id). */
|
|
3042
|
-
agentX25519:
|
|
4332
|
+
agentX25519: z2.string().optional()
|
|
3043
4333
|
});
|
|
3044
|
-
var SnoozeRequestSchema =
|
|
3045
|
-
requestId:
|
|
3046
|
-
until:
|
|
4334
|
+
var SnoozeRequestSchema = z2.object({
|
|
4335
|
+
requestId: z2.string(),
|
|
4336
|
+
until: z2.string().datetime()
|
|
3047
4337
|
});
|
|
3048
4338
|
var APNS_TOKEN_RE = /^[0-9a-fA-F]{64}$/;
|
|
3049
|
-
var PushTokenSchema =
|
|
3050
|
-
voipToken:
|
|
3051
|
-
alertToken:
|
|
3052
|
-
fcmToken:
|
|
3053
|
-
platform:
|
|
4339
|
+
var PushTokenSchema = z2.object({
|
|
4340
|
+
voipToken: z2.string().min(1).optional(),
|
|
4341
|
+
alertToken: z2.string().min(1).optional(),
|
|
4342
|
+
fcmToken: z2.string().min(1).optional(),
|
|
4343
|
+
platform: z2.enum(["ios", "android"])
|
|
3054
4344
|
}).superRefine((v, ctx) => {
|
|
3055
4345
|
if (v.platform !== "ios") return;
|
|
3056
4346
|
for (const field of ["voipToken", "alertToken"]) {
|
|
3057
4347
|
const token = v[field];
|
|
3058
4348
|
if (token === void 0 || APNS_TOKEN_RE.test(token)) continue;
|
|
3059
4349
|
ctx.addIssue({
|
|
3060
|
-
code:
|
|
4350
|
+
code: z2.ZodIssueCode.custom,
|
|
3061
4351
|
path: [field],
|
|
3062
4352
|
message: `not an APNs device token (want 64 hex chars, got ${token.length})`
|
|
3063
4353
|
});
|
|
3064
4354
|
}
|
|
3065
4355
|
});
|
|
3066
|
-
var MissedCallSchema =
|
|
4356
|
+
var MissedCallSchema = z2.enum([
|
|
3067
4357
|
"retry_10m",
|
|
3068
4358
|
"retry_30m",
|
|
3069
4359
|
"retry_60m",
|
|
@@ -3073,32 +4363,32 @@ var MissedCallSchema = z.enum([
|
|
|
3073
4363
|
"inbox",
|
|
3074
4364
|
"dismiss"
|
|
3075
4365
|
]);
|
|
3076
|
-
var BrokerTuningSchema =
|
|
4366
|
+
var BrokerTuningSchema = z2.object({
|
|
3077
4367
|
/** 'none' = skip the spoken ack after a mapped answer (power users find it slow). */
|
|
3078
|
-
ackVerbosity:
|
|
4368
|
+
ackVerbosity: z2.enum(["normal", "none"]).optional(),
|
|
3079
4369
|
/** How readily the mapper asks its one clarification: 'low' = only when truly
|
|
3080
4370
|
* uninterpretable, 'high' = whenever not fully certain. */
|
|
3081
|
-
clarifyEagerness:
|
|
4371
|
+
clarifyEagerness: z2.enum(["low", "normal", "high"]).optional(),
|
|
3082
4372
|
/** The user's own shorthand: when they say `say`, they mean `mean`. */
|
|
3083
|
-
phrasebook:
|
|
4373
|
+
phrasebook: z2.array(z2.object({ say: z2.string().min(1).max(60), mean: z2.string().min(1).max(120) })).max(24).optional(),
|
|
3084
4374
|
/** The language calls are PLANNED in, when the account has chosen one (#1272). Absent —
|
|
3085
4375
|
* which is every account today — means the agent's own words decide, per ask: a call
|
|
3086
4376
|
* about an English ask opens in English. This is the only thing that overrides that,
|
|
3087
4377
|
* and a live caller who switches language mid-call still outranks it (broker/lang.ts).
|
|
3088
4378
|
* Set per user (no UI yet), like `voiceTuning`. */
|
|
3089
|
-
language:
|
|
4379
|
+
language: z2.enum(["en", "es"]).optional()
|
|
3090
4380
|
});
|
|
3091
|
-
var UserSettingsSchema =
|
|
3092
|
-
permissions:
|
|
3093
|
-
call:
|
|
3094
|
-
banner:
|
|
3095
|
-
push:
|
|
4381
|
+
var UserSettingsSchema = z2.object({
|
|
4382
|
+
permissions: z2.object({
|
|
4383
|
+
call: z2.boolean(),
|
|
4384
|
+
banner: z2.boolean(),
|
|
4385
|
+
push: z2.boolean()
|
|
3096
4386
|
}),
|
|
3097
|
-
sessionMode:
|
|
3098
|
-
silentPush:
|
|
3099
|
-
autoCallback:
|
|
4387
|
+
sessionMode: z2.enum(["default", "all_calls", "silent"]),
|
|
4388
|
+
silentPush: z2.boolean(),
|
|
4389
|
+
autoCallback: z2.boolean(),
|
|
3100
4390
|
/** Opt-in (default false) to using your content to improve Paigy and train models. */
|
|
3101
|
-
improveConsent:
|
|
4391
|
+
improveConsent: z2.boolean(),
|
|
3102
4392
|
missedCall: MissedCallSchema.default("backoff_standard"),
|
|
3103
4393
|
/** Where voice audio is processed. 'hosted' (default) = Paigy's voice services
|
|
3104
4394
|
* (ElevenLabs TTS, faster-whisper STT, the call bot); 'on_device' = the phone
|
|
@@ -3106,7 +4396,7 @@ var UserSettingsSchema = z.object({
|
|
|
3106
4396
|
* Optional, NOT defaulted: a stale client PATCHing the full settings object
|
|
3107
4397
|
* must not silently reset this privacy choice. Absent = leave unchanged on
|
|
3108
4398
|
* write, 'hosted' on read (see store.ts). */
|
|
3109
|
-
voiceMode:
|
|
4399
|
+
voiceMode: z2.enum(["hosted", "on_device"]).optional(),
|
|
3110
4400
|
/** Per-user ring budget (#603): calls per rolling day before further calls
|
|
3111
4401
|
* degrade to banner. Absent = the global default (25). A number, never a
|
|
3112
4402
|
* bypass — every account keeps a ceiling. No UI; set per user for testing. */
|
|
@@ -3114,10 +4404,10 @@ var UserSettingsSchema = z.object({
|
|
|
3114
4404
|
* payload['tuning'] (e.g. { silence_s: 3.5 } — a longer pause window for a
|
|
3115
4405
|
* slower speaker). No API-side semantics; the bot resolves each key with its
|
|
3116
4406
|
* own defaults. Set per user (no UI yet); absent = bot defaults. */
|
|
3117
|
-
voiceTuning:
|
|
4407
|
+
voiceTuning: z2.record(z2.string(), z2.union([z2.number(), z2.string()])).optional(),
|
|
3118
4408
|
/** Opt-in to real-phone (PSTN) calls when the app can't ring. Optional, not
|
|
3119
4409
|
* defaulted — an older client PATCHing the full object must not clobber it. */
|
|
3120
|
-
pstnCalls:
|
|
4410
|
+
pstnCalls: z2.boolean().optional(),
|
|
3121
4411
|
/** The user's IANA timezone (e.g. "America/Bogota"), recorded by the app — it is the
|
|
3122
4412
|
* only party that knows it. REMINDERS are why it exists: "remind me at ten" becomes
|
|
3123
4413
|
* an absolute `due_at` only if we know whose ten. Optional and never defaulted, for
|
|
@@ -3126,50 +4416,50 @@ var UserSettingsSchema = z.object({
|
|
|
3126
4416
|
* that failure reads as the reminder rail being unreliable rather than as a missing
|
|
3127
4417
|
* setting. Absent = a spoken time can't be landed, so the reminder rides the next
|
|
3128
4418
|
* call — honest about what we know. */
|
|
3129
|
-
timezone:
|
|
4419
|
+
timezone: z2.string().min(1).max(64).optional(),
|
|
3130
4420
|
/** Account E2EE state (text lane): 'off' (default) = today's plaintext; 'on' =
|
|
3131
4421
|
* content is sealed end-to-end between the local agent and the phone. Like
|
|
3132
4422
|
* voiceMode, OPTIONAL and NOT defaulted so a stale client PATCHing the full
|
|
3133
4423
|
* settings object without it can't silently flip the account's E2EE state.
|
|
3134
4424
|
* Absent = leave unchanged on write, 'off' on read (see store.ts). The demo
|
|
3135
4425
|
* account is plaintext by construction and refuses any non-'off' value. */
|
|
3136
|
-
e2eeMode:
|
|
4426
|
+
e2eeMode: z2.enum(["off", "on"]).optional(),
|
|
3137
4427
|
/** Rung-2 broker tuning (#381). Optional and NOT defaulted, same stale-client
|
|
3138
4428
|
* clobber guard as voiceMode: absent = leave unchanged on write. */
|
|
3139
4429
|
broker: BrokerTuningSchema.optional()
|
|
3140
4430
|
});
|
|
3141
|
-
var HistoryItemSchema =
|
|
3142
|
-
id:
|
|
3143
|
-
parentId:
|
|
4431
|
+
var HistoryItemSchema = z2.object({
|
|
4432
|
+
id: z2.string(),
|
|
4433
|
+
parentId: z2.string(),
|
|
3144
4434
|
/** 'user' = a request you sent; 'agent' = a notification an agent sent you. */
|
|
3145
|
-
initiator:
|
|
3146
|
-
title:
|
|
4435
|
+
initiator: z2.enum(["user", "agent"]),
|
|
4436
|
+
title: z2.string(),
|
|
3147
4437
|
/** The agent on the other end (its name). */
|
|
3148
|
-
name:
|
|
3149
|
-
createdAt:
|
|
4438
|
+
name: z2.string(),
|
|
4439
|
+
createdAt: z2.string(),
|
|
3150
4440
|
/** When the agent fetched your request (user→agent only). */
|
|
3151
|
-
agentAckedAt:
|
|
4441
|
+
agentAckedAt: z2.string().nullable(),
|
|
3152
4442
|
/** When you answered the agent's notification (agent→user only). */
|
|
3153
|
-
humanAckedAt:
|
|
4443
|
+
humanAckedAt: z2.string().nullable()
|
|
3154
4444
|
});
|
|
3155
4445
|
var ACTIVITY_LINES = 2;
|
|
3156
4446
|
var ACTIVITY_LINE_MAX = 80;
|
|
3157
|
-
var AgentActivitySchema =
|
|
4447
|
+
var AgentActivitySchema = z2.object({
|
|
3158
4448
|
/** Oldest first, so the newest line is last — the one that replaces in place. */
|
|
3159
|
-
lines:
|
|
4449
|
+
lines: z2.array(z2.string().max(ACTIVITY_LINE_MAX)).max(ACTIVITY_LINES),
|
|
3160
4450
|
/** When the harness observed this tail. Its own timestamp, not the heartbeat's: a beat
|
|
3161
4451
|
* that carries an UNCHANGED tail must not make a stalled agent look like it just moved. */
|
|
3162
|
-
at:
|
|
4452
|
+
at: z2.string().datetime()
|
|
3163
4453
|
});
|
|
3164
|
-
var ConnectionSummarySchema =
|
|
4454
|
+
var ConnectionSummarySchema = z2.object({
|
|
3165
4455
|
/** The connection = the agent's token id (used to address a request). */
|
|
3166
|
-
id:
|
|
3167
|
-
device:
|
|
4456
|
+
id: z2.string(),
|
|
4457
|
+
device: z2.string().nullable(),
|
|
3168
4458
|
/** The agent's display name (the single pairing name). */
|
|
3169
|
-
name:
|
|
4459
|
+
name: z2.string(),
|
|
3170
4460
|
/** For a managed connection, the provider key (e.g. "cma") that agentOrigin maps to a
|
|
3171
4461
|
* label; null for a local connection. Sourced from the token's provider, not the name. */
|
|
3172
|
-
provider:
|
|
4462
|
+
provider: z2.string().nullable(),
|
|
3173
4463
|
/** The pairing's assigned voice (#462); null = the default voice. */
|
|
3174
4464
|
voice: VoiceKeySchema.nullable(),
|
|
3175
4465
|
/** The LOUDEST this agent may ever reach you — a ceiling on `NOTIFY_LADDER`, set by the
|
|
@@ -3180,19 +4470,19 @@ var ConnectionSummarySchema = z.object({
|
|
|
3180
4470
|
* every surface at once and outranks even `sessionMode: all_calls` — a mode the user
|
|
3181
4471
|
* set once must not overrule a rule they set about one agent. */
|
|
3182
4472
|
reach: NotifyLevelSchema.nullable().optional(),
|
|
3183
|
-
createdAt:
|
|
4473
|
+
createdAt: z2.string().datetime(),
|
|
3184
4474
|
/** Most recent notification on this connection, either direction. Null = no contact yet.
|
|
3185
4475
|
* Drives the agents-page recency grouping (Today / This week / …). */
|
|
3186
|
-
lastContactAt:
|
|
4476
|
+
lastContactAt: z2.string().datetime().nullable(),
|
|
3187
4477
|
/** Last presence heartbeat from a running agent process (POST /api/presence) — the
|
|
3188
4478
|
* desktop app while open. Null = never seen; stale = offline. */
|
|
3189
|
-
lastSeenAt:
|
|
4479
|
+
lastSeenAt: z2.string().datetime().nullable().optional(),
|
|
3190
4480
|
/** What a live desktop can run (companion.md §2.2), advertised on its heartbeat:
|
|
3191
4481
|
* harness availabilities + granted workspaces — the option set the phone's
|
|
3192
4482
|
* "new session" sheet offers. Absent for ordinary MCP agents. */
|
|
3193
|
-
runtime:
|
|
3194
|
-
harnesses:
|
|
3195
|
-
workspaces:
|
|
4483
|
+
runtime: z2.object({
|
|
4484
|
+
harnesses: z2.array(z2.object({ name: z2.string(), label: z2.string(), status: z2.string() })).optional(),
|
|
4485
|
+
workspaces: z2.array(z2.string()).optional()
|
|
3196
4486
|
}).optional(),
|
|
3197
4487
|
/** The tail of this agent's working log, when a harness is driving it — the agent page's
|
|
3198
4488
|
* live strip. Absent for anything the desktop harness isn't running (a hatched identity
|
|
@@ -3201,98 +4491,103 @@ var ConnectionSummarySchema = z.object({
|
|
|
3201
4491
|
activity: AgentActivitySchema.optional(),
|
|
3202
4492
|
/** True = a provider-managed agent running in the provider's cloud (e.g. Anthropic CMA);
|
|
3203
4493
|
* false = a local MCP connection running on the user's computer (Claude Code/Codex/…). */
|
|
3204
|
-
managed:
|
|
4494
|
+
managed: z2.boolean()
|
|
3205
4495
|
});
|
|
3206
|
-
var MoveRingSchema =
|
|
3207
|
-
var MoveSchema =
|
|
3208
|
-
id:
|
|
4496
|
+
var MoveRingSchema = z2.enum(["home", "travels", "retired", "quarantined"]);
|
|
4497
|
+
var MoveSchema = z2.object({
|
|
4498
|
+
id: z2.string(),
|
|
3209
4499
|
/** The reusable question, as distill normalized it. */
|
|
3210
|
-
question:
|
|
4500
|
+
question: z2.string(),
|
|
3211
4501
|
/** The operative ruling. Editable by the user (PATCH) — which resets the ledger. */
|
|
3212
|
-
answer:
|
|
4502
|
+
answer: z2.string(),
|
|
3213
4503
|
/** The user's stated reason, when they gave one. Null = inherently narrow: the judge is
|
|
3214
4504
|
* told so, and the ruling only derives essentially the same question in the same scope. */
|
|
3215
|
-
rationale:
|
|
4505
|
+
rationale: z2.string().nullable(),
|
|
3216
4506
|
/** Where the ruling lives: a repo/workspace, or 'global'. */
|
|
3217
|
-
scope:
|
|
4507
|
+
scope: z2.string(),
|
|
3218
4508
|
ring: MoveRingSchema,
|
|
3219
4509
|
/** True = the user pinned it with `always` (travel granted by hand, not by evidence). */
|
|
3220
|
-
pinned:
|
|
4510
|
+
pinned: z2.boolean(),
|
|
3221
4511
|
/** True = a pin the user placed was BROKEN by later counter-evidence. Surfaced so the
|
|
3222
4512
|
* break is visible instead of a pin silently disappearing. */
|
|
3223
|
-
pinBroken:
|
|
4513
|
+
pinBroken: z2.boolean(),
|
|
3224
4514
|
/** When the ruling was distilled. */
|
|
3225
|
-
learnedAt:
|
|
4515
|
+
learnedAt: z2.string(),
|
|
3226
4516
|
/** Last time it answered an ask. Null = never fired. */
|
|
3227
|
-
lastUsedAt:
|
|
4517
|
+
lastUsedAt: z2.string().nullable(),
|
|
3228
4518
|
/** How many asks it has answered. Instrumentation — deliberately NOT an input to the
|
|
3229
4519
|
* evidence curve: firing says the question keeps arising, not that the ruling is right. */
|
|
3230
|
-
usedCount:
|
|
4520
|
+
usedCount: z2.number(),
|
|
3231
4521
|
/** Ledger: outcomes that said it held up. Saturating — the tenth is worth almost nothing. */
|
|
3232
|
-
confirms:
|
|
4522
|
+
confirms: z2.number(),
|
|
3233
4523
|
/** Ledger: contradictions, in signal units (a full override = 1, weaker signals less).
|
|
3234
4524
|
* Linear and priced above the entire confirmation budget, so any full counter wins. */
|
|
3235
|
-
counters:
|
|
4525
|
+
counters: z2.number(),
|
|
3236
4526
|
/** The agent that asked the question this move came from, when known. Null for a move
|
|
3237
4527
|
* distilled from a clarify ruling (those carry no agent) or one whose source rows are gone. */
|
|
3238
|
-
learnedFrom:
|
|
4528
|
+
learnedFrom: z2.object({ id: z2.string(), name: z2.string() }).nullable()
|
|
3239
4529
|
});
|
|
3240
|
-
var CreateRequestSchema =
|
|
4530
|
+
var CreateRequestSchema = z2.object({
|
|
3241
4531
|
/** The connection (token id) to send to, from GET /api/tokens. */
|
|
3242
|
-
tokenId:
|
|
4532
|
+
tokenId: z2.string(),
|
|
3243
4533
|
/** The user's message to the agent. */
|
|
3244
|
-
text:
|
|
4534
|
+
text: z2.string().min(1),
|
|
3245
4535
|
/** Land the request on an existing conversation thread (History → "Continue")
|
|
3246
4536
|
* instead of minting a fresh one. Must belong to the requesting user. */
|
|
3247
|
-
parentId:
|
|
4537
|
+
parentId: z2.string().optional(),
|
|
3248
4538
|
/** Point the agent at a past conversation (possibly with a different agent) as
|
|
3249
4539
|
* starting context (History → "New session from this"). A reference, not a copy —
|
|
3250
4540
|
* the agent reads it via get_thread. Must belong to the requesting user. */
|
|
3251
|
-
contextParentId:
|
|
4541
|
+
contextParentId: z2.string().optional()
|
|
3252
4542
|
});
|
|
3253
|
-
var HandoffSchema =
|
|
4543
|
+
var HandoffSchema = z2.object({
|
|
4544
|
+
/** Move this existing outcome to `target` without reminting it. Requires `target`. */
|
|
4545
|
+
workId: z2.string().uuid().optional(),
|
|
3254
4546
|
/** Land the note on an existing thread; omitted mints a fresh one. */
|
|
3255
|
-
parentId:
|
|
4547
|
+
parentId: z2.string().uuid().optional(),
|
|
3256
4548
|
/** One-line headline of the working context handed off. */
|
|
3257
|
-
title:
|
|
4549
|
+
title: z2.string().min(1),
|
|
3258
4550
|
/** The brief — standalone notes the successor reads (what was done, what's left, links). */
|
|
3259
|
-
notes:
|
|
4551
|
+
notes: z2.array(z2.string().min(1)).min(1),
|
|
3260
4552
|
/** A sibling connection to dispatch directly to (token id or agent nickname). Same-account
|
|
3261
4553
|
* only; omit to leave the thread for the user to hand off in the app. */
|
|
3262
|
-
target:
|
|
4554
|
+
target: z2.string().optional(),
|
|
3263
4555
|
/** Write the note as a RECAP (kind:'recap', #617): a summary turn that supersedes the
|
|
3264
4556
|
* thread's earlier turns for rehydration — get_thread returns the latest recap + only
|
|
3265
4557
|
* the turns after it. Handoff-to-a-successor and handoff-to-yourself-later are the
|
|
3266
4558
|
* same primitive; a recap is one whose audience includes you. */
|
|
3267
|
-
recap:
|
|
4559
|
+
recap: z2.boolean().optional()
|
|
4560
|
+
}).refine((value) => !value.workId || Boolean(value.target), {
|
|
4561
|
+
message: "target is required when handing off Work",
|
|
4562
|
+
path: ["target"]
|
|
3268
4563
|
});
|
|
3269
|
-
var NoteSourceSchema =
|
|
3270
|
-
var NoteStatusSchema =
|
|
3271
|
-
var NoteRepeatSchema =
|
|
3272
|
-
var DecisionSchema =
|
|
3273
|
-
id:
|
|
4564
|
+
var NoteSourceSchema = z2.enum(["app", "call"]);
|
|
4565
|
+
var NoteStatusSchema = z2.enum(["open", "assigned", "in_progress", "done"]);
|
|
4566
|
+
var NoteRepeatSchema = z2.enum(["once", "until_done"]);
|
|
4567
|
+
var DecisionSchema = z2.object({
|
|
4568
|
+
id: z2.string(),
|
|
3274
4569
|
/** The note this decision refines; null = recorded on a bare thread (the
|
|
3275
4570
|
* extensibility seam — any conversation can accrue decisions). */
|
|
3276
|
-
noteId:
|
|
4571
|
+
noteId: z2.string().nullable(),
|
|
3277
4572
|
/** What was ambiguous — the broker's (or the user's own) question. */
|
|
3278
|
-
question:
|
|
4573
|
+
question: z2.string(),
|
|
3279
4574
|
/** The user's ruling; null while the question is open. */
|
|
3280
|
-
answer:
|
|
3281
|
-
decidedAt:
|
|
3282
|
-
createdAt:
|
|
4575
|
+
answer: z2.string().nullable(),
|
|
4576
|
+
decidedAt: z2.string().nullable(),
|
|
4577
|
+
createdAt: z2.string()
|
|
3283
4578
|
});
|
|
3284
|
-
var NoteSchema =
|
|
3285
|
-
id:
|
|
4579
|
+
var NoteSchema = z2.object({
|
|
4580
|
+
id: z2.string(),
|
|
3286
4581
|
/** One-line headline (broker-titled; deterministic floor). */
|
|
3287
|
-
title:
|
|
4582
|
+
title: z2.string(),
|
|
3288
4583
|
/** The original intent, verbatim — assignees always see the user's own words. */
|
|
3289
|
-
intent:
|
|
4584
|
+
intent: z2.string(),
|
|
3290
4585
|
source: NoteSourceSchema,
|
|
3291
4586
|
status: NoteStatusSchema,
|
|
3292
4587
|
/** Who it was assigned to (a participant ref, 'agent:<tokenId>'); null = unassigned. */
|
|
3293
|
-
assignee:
|
|
4588
|
+
assignee: z2.string().nullable(),
|
|
3294
4589
|
/** The request thread minted at assignment; null until assigned. */
|
|
3295
|
-
parentId:
|
|
4590
|
+
parentId: z2.string().nullable(),
|
|
3296
4591
|
/** REMINDERS (reminders-design.md): the NOT-BEFORE this becomes eligible to ride a
|
|
3297
4592
|
* call — never a deadline. It only ever comes from the user's own words, so when it
|
|
3298
4593
|
* passes Paigy rings ONCE (#1293, owner 2026-08-26: a time said out loud is consent to
|
|
@@ -3301,200 +4596,200 @@ var NoteSchema = z.object({
|
|
|
3301
4596
|
// Defaulted, not required: a Note from an API deploy older than the reminders
|
|
3302
4597
|
// migration has none of these, and the defaults ARE what it means — no not-before,
|
|
3303
4598
|
// one ride, never ridden. Parsing must not fail across a rolling deploy.
|
|
3304
|
-
dueAt:
|
|
4599
|
+
dueAt: z2.string().nullable().default(null),
|
|
3305
4600
|
repeat: NoteRepeatSchema.default("once"),
|
|
3306
4601
|
/** How many calls have already carried it — the fatigue cap counts rides, not days. */
|
|
3307
|
-
rides:
|
|
3308
|
-
lastRideAt:
|
|
3309
|
-
createdAt:
|
|
4602
|
+
rides: z2.number().int().default(0),
|
|
4603
|
+
lastRideAt: z2.string().nullable().default(null),
|
|
4604
|
+
createdAt: z2.string()
|
|
3310
4605
|
});
|
|
3311
|
-
var CreateNoteSchema =
|
|
4606
|
+
var CreateNoteSchema = z2.object({
|
|
3312
4607
|
/** The intent, in the user's own words. Stored verbatim; the broker only titles it. */
|
|
3313
|
-
text:
|
|
4608
|
+
text: z2.string().min(1).max(4e3),
|
|
3314
4609
|
/** Capture it as a REMINDER — a note assigned to the user themselves, which rides
|
|
3315
4610
|
* their next call instead of being handed to an agent. Everything else about the
|
|
3316
4611
|
* note is identical; this is the one parameter that separates the two. */
|
|
3317
|
-
forMe:
|
|
4612
|
+
forMe: z2.boolean().optional(),
|
|
3318
4613
|
/** The not-before, when the user already said one. Absent = the very next call. */
|
|
3319
|
-
dueAt:
|
|
4614
|
+
dueAt: z2.string().datetime().optional(),
|
|
3320
4615
|
repeat: NoteRepeatSchema.optional()
|
|
3321
4616
|
});
|
|
3322
|
-
var RecordDecisionSchema =
|
|
4617
|
+
var RecordDecisionSchema = z2.object({
|
|
3323
4618
|
/** An open decision (from /clarify) to answer. */
|
|
3324
|
-
decisionId:
|
|
4619
|
+
decisionId: z2.string().optional(),
|
|
3325
4620
|
/** For an ad-hoc decision: what was ambiguous. Required without `decisionId`. */
|
|
3326
|
-
question:
|
|
4621
|
+
question: z2.string().min(1).max(500).optional(),
|
|
3327
4622
|
/** The ruling. */
|
|
3328
|
-
answer:
|
|
4623
|
+
answer: z2.string().min(1).max(2e3)
|
|
3329
4624
|
}).refine((d) => d.decisionId || d.question, { message: "decisionId or question required" });
|
|
3330
|
-
var AssignNoteSchema =
|
|
4625
|
+
var AssignNoteSchema = z2.object({
|
|
3331
4626
|
/** An EXISTING agent: token id or nickname. Omit when spawning fresh. */
|
|
3332
|
-
target:
|
|
4627
|
+
target: z2.string().min(1).optional(),
|
|
3333
4628
|
/** Spawn a NEW session for this note (companion.md §2.2): the assignee doesn't
|
|
3334
4629
|
* exist yet — mint it on a live desktop that advertises the harness+workspace,
|
|
3335
4630
|
* named after the note. The brief arrives as its opening request. */
|
|
3336
|
-
spawn:
|
|
3337
|
-
hostTokenId:
|
|
3338
|
-
harness:
|
|
3339
|
-
workspace:
|
|
4631
|
+
spawn: z2.object({
|
|
4632
|
+
hostTokenId: z2.string().uuid(),
|
|
4633
|
+
harness: z2.string().min(1),
|
|
4634
|
+
workspace: z2.string().min(1)
|
|
3340
4635
|
}).optional()
|
|
3341
4636
|
}).refine((a) => !!a.target !== !!a.spawn, { message: "exactly one of target or spawn" });
|
|
3342
|
-
var TriageVerdictSchema =
|
|
3343
|
-
var TriageItemSchema =
|
|
3344
|
-
noteId:
|
|
4637
|
+
var TriageVerdictSchema = z2.enum(["keep", "stale", "close", "assign"]);
|
|
4638
|
+
var TriageItemSchema = z2.object({
|
|
4639
|
+
noteId: z2.string(),
|
|
3345
4640
|
/** The note's headline at run time. */
|
|
3346
|
-
title:
|
|
4641
|
+
title: z2.string(),
|
|
3347
4642
|
/** WHY, in one short human line, evidence first — this is read on a phone underneath
|
|
3348
4643
|
* the note's title: "no movement in 34 days", "worked 3 notes in this repo this week".
|
|
3349
4644
|
* Never a model's reasoning transcript, never an id. */
|
|
3350
|
-
why:
|
|
4645
|
+
why: z2.string()
|
|
3351
4646
|
});
|
|
3352
|
-
var TriageAssignmentSchema =
|
|
4647
|
+
var TriageAssignmentSchema = z2.object({
|
|
3353
4648
|
/** The agent's token id — what `dispatchNote` resolves and what a request is addressed to. */
|
|
3354
|
-
agent:
|
|
4649
|
+
agent: z2.string(),
|
|
3355
4650
|
/** Its display name at run time (the name on the hatchling's card). Denormalized for the
|
|
3356
4651
|
* same reason as `title`: the card must render from the proposal alone. */
|
|
3357
|
-
agentName:
|
|
3358
|
-
notes:
|
|
4652
|
+
agentName: z2.string(),
|
|
4653
|
+
notes: z2.array(TriageItemSchema)
|
|
3359
4654
|
});
|
|
3360
|
-
var TriageStatusSchema =
|
|
3361
|
-
var SubmitTriageSchema =
|
|
4655
|
+
var TriageStatusSchema = z2.enum(["open", "superseded", "dismissed"]);
|
|
4656
|
+
var SubmitTriageSchema = z2.object({
|
|
3362
4657
|
/** Which runtime judged: "ollama" (inference never left the machine) or a harness the
|
|
3363
4658
|
* user already runs under their own credentials ("claude" / "codex" / "agy"). Recorded
|
|
3364
4659
|
* so the phone can say where the content went — an unattributed privacy claim is worth
|
|
3365
4660
|
* nothing, and #1106's promise is precisely "Paigy's servers never see this". */
|
|
3366
|
-
provider:
|
|
4661
|
+
provider: z2.string().min(1).max(60),
|
|
3367
4662
|
/** The concrete model when the provider names one (an ollama tag); null otherwise. */
|
|
3368
|
-
model:
|
|
4663
|
+
model: z2.string().max(200).nullable().optional(),
|
|
3369
4664
|
/** How many open notes the run actually looked at — the denominator on the phone
|
|
3370
4665
|
* ("6 of 50"), and the honest answer to "did it read the whole queue?". */
|
|
3371
|
-
reviewed:
|
|
3372
|
-
close:
|
|
3373
|
-
stale:
|
|
3374
|
-
assign:
|
|
4666
|
+
reviewed: z2.number().int().min(0).max(1e4).default(0),
|
|
4667
|
+
close: z2.array(TriageItemSchema).max(200).default([]),
|
|
4668
|
+
stale: z2.array(TriageItemSchema).max(200).default([]),
|
|
4669
|
+
assign: z2.array(TriageAssignmentSchema).max(50).default([])
|
|
3375
4670
|
});
|
|
3376
4671
|
var TriageProposalSchema = SubmitTriageSchema.extend({
|
|
3377
|
-
id:
|
|
3378
|
-
runAt:
|
|
4672
|
+
id: z2.string(),
|
|
4673
|
+
runAt: z2.string(),
|
|
3379
4674
|
status: TriageStatusSchema,
|
|
3380
|
-
model:
|
|
4675
|
+
model: z2.string().nullable().default(null)
|
|
3381
4676
|
});
|
|
3382
|
-
var AcceptTriageSchema =
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
group:
|
|
3387
|
-
agent:
|
|
3388
|
-
noteIds:
|
|
4677
|
+
var AcceptTriageSchema = z2.discriminatedUnion("group", [
|
|
4678
|
+
z2.object({ group: z2.literal("close"), noteIds: z2.array(z2.string()).max(200).optional() }),
|
|
4679
|
+
z2.object({ group: z2.literal("stale"), noteIds: z2.array(z2.string()).max(200).optional() }),
|
|
4680
|
+
z2.object({
|
|
4681
|
+
group: z2.literal("assign"),
|
|
4682
|
+
agent: z2.string().min(1),
|
|
4683
|
+
noteIds: z2.array(z2.string()).max(200).optional()
|
|
3389
4684
|
})
|
|
3390
4685
|
]);
|
|
3391
|
-
var AcceptTriageResultSchema =
|
|
3392
|
-
accepted:
|
|
3393
|
-
failed:
|
|
4686
|
+
var AcceptTriageResultSchema = z2.object({
|
|
4687
|
+
accepted: z2.array(z2.string()),
|
|
4688
|
+
failed: z2.array(z2.object({ noteId: z2.string(), reason: z2.string() }))
|
|
3394
4689
|
});
|
|
3395
|
-
var DeliveryModeSchema =
|
|
3396
|
-
var RegisterDeliverySchema =
|
|
3397
|
-
var OAuthStartSchema =
|
|
3398
|
-
provider:
|
|
3399
|
-
returnTo:
|
|
4690
|
+
var DeliveryModeSchema = z2.enum(["poll", "self_hosted"]);
|
|
4691
|
+
var RegisterDeliverySchema = z2.object({ mode: DeliveryModeSchema });
|
|
4692
|
+
var OAuthStartSchema = z2.object({
|
|
4693
|
+
provider: z2.enum(["cma"]),
|
|
4694
|
+
returnTo: z2.string().min(1)
|
|
3400
4695
|
});
|
|
3401
|
-
var DeliveryConfigSchema =
|
|
3402
|
-
tokenId:
|
|
4696
|
+
var DeliveryConfigSchema = z2.object({
|
|
4697
|
+
tokenId: z2.string(),
|
|
3403
4698
|
mode: DeliveryModeSchema,
|
|
3404
4699
|
/** null when the server has no SUPABASE_ANON_KEY set — the listener then falls
|
|
3405
4700
|
* back to its own PAIGY_SUPABASE_URL / PAIGY_SUPABASE_ANON_KEY env. */
|
|
3406
|
-
realtime:
|
|
4701
|
+
realtime: z2.object({ url: z2.string(), anonKey: z2.string() }).nullable()
|
|
3407
4702
|
});
|
|
3408
|
-
var StatusSchema =
|
|
3409
|
-
name:
|
|
3410
|
-
sessionMode:
|
|
4703
|
+
var StatusSchema = z2.object({
|
|
4704
|
+
name: z2.string(),
|
|
4705
|
+
sessionMode: z2.enum(["default", "all_calls", "silent"]),
|
|
3411
4706
|
/** A phone is registered for push/ring (any push token on the account). */
|
|
3412
|
-
phone:
|
|
4707
|
+
phone: z2.boolean()
|
|
3413
4708
|
});
|
|
3414
|
-
var EnvelopeRecipientSchema =
|
|
3415
|
-
keyId:
|
|
3416
|
-
epk:
|
|
3417
|
-
wnonce:
|
|
3418
|
-
wrap:
|
|
4709
|
+
var EnvelopeRecipientSchema = z2.object({
|
|
4710
|
+
keyId: z2.string(),
|
|
4711
|
+
epk: z2.string(),
|
|
4712
|
+
wnonce: z2.string(),
|
|
4713
|
+
wrap: z2.string()
|
|
3419
4714
|
});
|
|
3420
|
-
var EnvelopeHeaderSchema =
|
|
3421
|
-
field:
|
|
3422
|
-
kind:
|
|
3423
|
-
senderRole:
|
|
3424
|
-
recipientKeyIds:
|
|
3425
|
-
seq:
|
|
4715
|
+
var EnvelopeHeaderSchema = z2.object({
|
|
4716
|
+
field: z2.enum(["context", "options", "visuals", "answer"]),
|
|
4717
|
+
kind: z2.string(),
|
|
4718
|
+
senderRole: z2.enum(["agent", "user"]),
|
|
4719
|
+
recipientKeyIds: z2.array(z2.string()),
|
|
4720
|
+
seq: z2.number().int().nonnegative()
|
|
3426
4721
|
});
|
|
3427
|
-
var EnvelopeSchema =
|
|
3428
|
-
v:
|
|
3429
|
-
alg:
|
|
3430
|
-
msgId:
|
|
4722
|
+
var EnvelopeSchema = z2.object({
|
|
4723
|
+
v: z2.literal(1),
|
|
4724
|
+
alg: z2.literal("x25519-xsalsa20poly1305"),
|
|
4725
|
+
msgId: z2.string(),
|
|
3431
4726
|
hdr: EnvelopeHeaderSchema,
|
|
3432
|
-
recipients:
|
|
3433
|
-
nonce:
|
|
3434
|
-
ct:
|
|
4727
|
+
recipients: z2.array(EnvelopeRecipientSchema).min(1),
|
|
4728
|
+
nonce: z2.string(),
|
|
4729
|
+
ct: z2.string()
|
|
3435
4730
|
});
|
|
3436
|
-
var SealedAnswerSchema =
|
|
3437
|
-
ignored:
|
|
4731
|
+
var SealedAnswerSchema = z2.object({
|
|
4732
|
+
ignored: z2.boolean(),
|
|
3438
4733
|
envelope: EnvelopeSchema
|
|
3439
4734
|
});
|
|
3440
|
-
var DeviceCredentialSchema =
|
|
3441
|
-
deviceId:
|
|
3442
|
-
kind:
|
|
3443
|
-
x25519Pub:
|
|
3444
|
-
ed25519Pub:
|
|
3445
|
-
sig:
|
|
4735
|
+
var DeviceCredentialSchema = z2.object({
|
|
4736
|
+
deviceId: z2.string(),
|
|
4737
|
+
kind: z2.enum(["phone", "web", "agent"]),
|
|
4738
|
+
x25519Pub: z2.string(),
|
|
4739
|
+
ed25519Pub: z2.string(),
|
|
4740
|
+
sig: z2.string()
|
|
3446
4741
|
});
|
|
3447
|
-
var DeviceRosterSchema =
|
|
3448
|
-
uikPub:
|
|
3449
|
-
devices:
|
|
4742
|
+
var DeviceRosterSchema = z2.object({
|
|
4743
|
+
uikPub: z2.string(),
|
|
4744
|
+
devices: z2.array(DeviceCredentialSchema)
|
|
3450
4745
|
});
|
|
3451
|
-
var WakeNudgeSchema =
|
|
3452
|
-
kind:
|
|
3453
|
-
notificationId:
|
|
3454
|
-
parentId:
|
|
4746
|
+
var WakeNudgeSchema = z2.object({
|
|
4747
|
+
kind: z2.enum(["reply", "request", "callback"]),
|
|
4748
|
+
notificationId: z2.string().optional(),
|
|
4749
|
+
parentId: z2.string()
|
|
3455
4750
|
});
|
|
3456
|
-
var PairingStatusSchema =
|
|
3457
|
-
var PairingRevealSchema =
|
|
3458
|
-
x25519:
|
|
3459
|
-
ed25519:
|
|
3460
|
-
nonce:
|
|
4751
|
+
var PairingStatusSchema = z2.enum(["pending", "approved", "denied", "expired"]);
|
|
4752
|
+
var PairingRevealSchema = z2.object({
|
|
4753
|
+
x25519: z2.string(),
|
|
4754
|
+
ed25519: z2.string(),
|
|
4755
|
+
nonce: z2.string()
|
|
3461
4756
|
});
|
|
3462
|
-
var DeviceCodeRequestSchema =
|
|
4757
|
+
var DeviceCodeRequestSchema = z2.object({
|
|
3463
4758
|
/** The agent's suggested name for the pairing — the human sees it pre-filled at
|
|
3464
4759
|
* approval and can override. Optional; blank → a default silly name server-side. */
|
|
3465
|
-
suggestedName:
|
|
4760
|
+
suggestedName: z2.string().optional(),
|
|
3466
4761
|
/** Legacy alias for suggestedName (older MCPs sent `agent`). Accepted for
|
|
3467
4762
|
* back-compat; `suggestedName` wins when both are present. ponytail: drop once no
|
|
3468
4763
|
* pre-`name` MCP is in the wild. */
|
|
3469
|
-
agent:
|
|
3470
|
-
device:
|
|
3471
|
-
proto:
|
|
4764
|
+
agent: z2.union([z2.string(), z2.object({ name: z2.string().optional() })]).optional(),
|
|
4765
|
+
device: z2.string().optional(),
|
|
4766
|
+
proto: z2.string().optional(),
|
|
3472
4767
|
// compiled-in tag, e.g. "paigy-pair-v2|sas=24"
|
|
3473
|
-
commitment:
|
|
4768
|
+
commitment: z2.string().optional()
|
|
3474
4769
|
// agent's Ca
|
|
3475
4770
|
});
|
|
3476
|
-
var DeviceCodeSchema =
|
|
3477
|
-
device_code:
|
|
3478
|
-
user_code:
|
|
3479
|
-
verification_uri:
|
|
3480
|
-
verification_uri_complete:
|
|
3481
|
-
interval:
|
|
3482
|
-
expires_in:
|
|
4771
|
+
var DeviceCodeSchema = z2.object({
|
|
4772
|
+
device_code: z2.string(),
|
|
4773
|
+
user_code: z2.string(),
|
|
4774
|
+
verification_uri: z2.string().url(),
|
|
4775
|
+
verification_uri_complete: z2.string().url(),
|
|
4776
|
+
interval: z2.number(),
|
|
4777
|
+
expires_in: z2.number()
|
|
3483
4778
|
});
|
|
3484
|
-
var DeviceInfoSchema =
|
|
3485
|
-
code:
|
|
4779
|
+
var DeviceInfoSchema = z2.object({
|
|
4780
|
+
code: z2.string(),
|
|
3486
4781
|
/** The agent's suggested name (from /device/code) — shown on the approval screen,
|
|
3487
4782
|
* pre-filling the name field the human can edit. */
|
|
3488
|
-
name:
|
|
4783
|
+
name: z2.string(),
|
|
3489
4784
|
/** @deprecated Legacy alias of `name` for the pre-#531 embedded bundle in App Store
|
|
3490
4785
|
* build 35, whose DeviceFlow renders `info.agent.slice(0, 2)` — without this a FRESH
|
|
3491
4786
|
* install crashes on the pairing screen on first launch, before the OTA lands
|
|
3492
4787
|
* (seen live: PAIGY-5T, 2026-07-21). Remove once a newer binary is the floor. */
|
|
3493
|
-
agent:
|
|
3494
|
-
device:
|
|
4788
|
+
agent: z2.string().optional(),
|
|
4789
|
+
device: z2.string().nullable(),
|
|
3495
4790
|
status: PairingStatusSchema,
|
|
3496
|
-
proto:
|
|
3497
|
-
agent_commitment:
|
|
4791
|
+
proto: z2.string().nullable().optional(),
|
|
4792
|
+
agent_commitment: z2.string().nullable().optional(),
|
|
3498
4793
|
// Ca
|
|
3499
4794
|
agent_reveal: PairingRevealSchema.nullable().optional(),
|
|
3500
4795
|
// present once the agent reveals
|
|
@@ -3504,48 +4799,48 @@ var DeviceInfoSchema = z.object({
|
|
|
3504
4799
|
// withheld until e2ee=true) can compute the SAS WITHOUT a token. Public keys — same safety
|
|
3505
4800
|
// class as agent_reveal above. Present only once the phone reveals; null otherwise.
|
|
3506
4801
|
phone_reveal: PairingRevealSchema.nullable().optional(),
|
|
3507
|
-
uik_pub:
|
|
4802
|
+
uik_pub: z2.string().nullable().optional()
|
|
3508
4803
|
});
|
|
3509
|
-
var DeviceTokenRequestSchema =
|
|
3510
|
-
device_code:
|
|
4804
|
+
var DeviceTokenRequestSchema = z2.object({
|
|
4805
|
+
device_code: z2.string(),
|
|
3511
4806
|
reveal: PairingRevealSchema.optional()
|
|
3512
4807
|
// agent's reveal {x25519, ed25519, nonce}
|
|
3513
4808
|
});
|
|
3514
|
-
var DeviceTokenSchema =
|
|
3515
|
-
access_token:
|
|
4809
|
+
var DeviceTokenSchema = z2.object({
|
|
4810
|
+
access_token: z2.string(),
|
|
3516
4811
|
/** The pairing's single name (user-typed at approval, the agent's suggestion, or
|
|
3517
4812
|
* a default silly name). */
|
|
3518
|
-
name:
|
|
3519
|
-
device:
|
|
4813
|
+
name: z2.string(),
|
|
4814
|
+
device: z2.string().nullable(),
|
|
3520
4815
|
/** The pairing's assigned voice, cached so the desktop can seed the SAME face the phone
|
|
3521
4816
|
* draws — voice is the third ingredient of a hatchling's build (party/traits.ts). */
|
|
3522
|
-
voice:
|
|
4817
|
+
voice: z2.string().nullable().optional(),
|
|
3523
4818
|
/** The token's server-side id — the face's COLOUR anchor, and the only seed ingredient
|
|
3524
4819
|
* that survives a rename. Cached by the host's identity beat. */
|
|
3525
|
-
token_id:
|
|
4820
|
+
token_id: z2.string().nullable().optional(),
|
|
3526
4821
|
/** WHERE this identity works — the folder a wake should land it in. Written by the host
|
|
3527
4822
|
* at spawn and by `paigy-harness handoff` from a live terminal. Without it every wake
|
|
3528
4823
|
* landed in the FIRST granted workspace and the agent rediscovered its own repo from
|
|
3529
4824
|
* the thread each time (host.ts, live catch 2026-08-06 — prompt-papered until now). */
|
|
3530
|
-
workspace:
|
|
4825
|
+
workspace: z2.string().nullable().optional(),
|
|
3531
4826
|
phone_reveal: PairingRevealSchema.nullable().optional(),
|
|
3532
4827
|
// present once the phone reveals
|
|
3533
|
-
uik_pub:
|
|
4828
|
+
uik_pub: z2.string().nullable().optional()
|
|
3534
4829
|
});
|
|
3535
|
-
var DeviceCommitRequestSchema =
|
|
3536
|
-
code:
|
|
3537
|
-
commitment:
|
|
4830
|
+
var DeviceCommitRequestSchema = z2.object({
|
|
4831
|
+
code: z2.string(),
|
|
4832
|
+
commitment: z2.string()
|
|
3538
4833
|
// Cb
|
|
3539
4834
|
});
|
|
3540
|
-
var DevicePeerCommitSchema =
|
|
3541
|
-
commitment:
|
|
4835
|
+
var DevicePeerCommitSchema = z2.object({
|
|
4836
|
+
commitment: z2.string().nullable()
|
|
3542
4837
|
});
|
|
3543
|
-
var SupportRequestSchema =
|
|
3544
|
-
email:
|
|
3545
|
-
message:
|
|
3546
|
-
name:
|
|
4838
|
+
var SupportRequestSchema = z2.object({
|
|
4839
|
+
email: z2.string().email().max(320),
|
|
4840
|
+
message: z2.string().trim().min(1).max(5e3),
|
|
4841
|
+
name: z2.string().trim().max(120).optional()
|
|
3547
4842
|
});
|
|
3548
|
-
var NotificationFeedbackKindSchema =
|
|
4843
|
+
var NotificationFeedbackKindSchema = z2.enum([
|
|
3549
4844
|
"break_down",
|
|
3550
4845
|
// "This should be more than one ask — break it down."
|
|
3551
4846
|
"regenerate_options",
|
|
@@ -3559,76 +4854,30 @@ var NotificationFeedbackKindSchema = z.enum([
|
|
|
3559
4854
|
"other"
|
|
3560
4855
|
// anything else — the note carries it.
|
|
3561
4856
|
]);
|
|
3562
|
-
var NotificationFeedbackSchema =
|
|
3563
|
-
notificationId:
|
|
4857
|
+
var NotificationFeedbackSchema = z2.object({
|
|
4858
|
+
notificationId: z2.string(),
|
|
3564
4859
|
kind: NotificationFeedbackKindSchema,
|
|
3565
4860
|
/** Optional free-text elaboration for a preset; required (non-empty) for 'other'. */
|
|
3566
|
-
note:
|
|
4861
|
+
note: z2.string().trim().max(2e3).optional()
|
|
3567
4862
|
}).superRefine((r, ctx) => {
|
|
3568
4863
|
if (r.kind === "other" && !r.note)
|
|
3569
|
-
ctx.addIssue({ code:
|
|
4864
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["note"], message: "note is required for 'other' feedback" });
|
|
3570
4865
|
});
|
|
3571
|
-
var FeedbackResolutionSchema =
|
|
3572
|
-
var FeedbackOutcomeSchema =
|
|
4866
|
+
var FeedbackResolutionSchema = z2.enum(["broker_fixed", "sent_to_agent"]);
|
|
4867
|
+
var FeedbackOutcomeSchema = z2.object({
|
|
3573
4868
|
resolution: FeedbackResolutionSchema,
|
|
3574
4869
|
transform: TransformSchema,
|
|
3575
|
-
message:
|
|
3576
|
-
childIds:
|
|
4870
|
+
message: z2.string(),
|
|
4871
|
+
childIds: z2.array(z2.string()).optional()
|
|
4872
|
+
});
|
|
4873
|
+
var CONTACT_SCHEMA = contactSchemaFrom({
|
|
4874
|
+
ask: NotifyRequestFields.shape.ask,
|
|
4875
|
+
waiting: NotifyRequestFields.shape.waiting,
|
|
4876
|
+
options: NotifyRequestFields.shape.options,
|
|
4877
|
+
channel: NotifyRequestFields.shape.channel,
|
|
4878
|
+
parentId: NotifyRequestFields.shape.parentId,
|
|
4879
|
+
workId: NotifyRequestFields.shape.workId
|
|
3577
4880
|
});
|
|
3578
|
-
var WORD_NUMS = {
|
|
3579
|
-
a: 1,
|
|
3580
|
-
an: 1,
|
|
3581
|
-
one: 1,
|
|
3582
|
-
two: 2,
|
|
3583
|
-
three: 3,
|
|
3584
|
-
four: 4,
|
|
3585
|
-
five: 5,
|
|
3586
|
-
six: 6,
|
|
3587
|
-
seven: 7,
|
|
3588
|
-
eight: 8,
|
|
3589
|
-
nine: 9,
|
|
3590
|
-
ten: 10,
|
|
3591
|
-
fifteen: 15,
|
|
3592
|
-
twenty: 20,
|
|
3593
|
-
thirty: 30,
|
|
3594
|
-
forty: 40,
|
|
3595
|
-
"forty-five": 45,
|
|
3596
|
-
fifty: 50,
|
|
3597
|
-
sixty: 60,
|
|
3598
|
-
"half an": 0.5,
|
|
3599
|
-
"a couple of": 2,
|
|
3600
|
-
"a few": 3
|
|
3601
|
-
};
|
|
3602
|
-
function untilHour(hour, now) {
|
|
3603
|
-
const due = new Date(now);
|
|
3604
|
-
due.setHours(hour, 0, 0, 0);
|
|
3605
|
-
if (due.getTime() <= now.getTime()) due.setDate(due.getDate() + 1);
|
|
3606
|
-
return Math.round((due.getTime() - now.getTime()) / 1e3);
|
|
3607
|
-
}
|
|
3608
|
-
function parseDue(detail, now = /* @__PURE__ */ new Date()) {
|
|
3609
|
-
const t = detail.toLowerCase().trim();
|
|
3610
|
-
const rel = /(?:^|\b)in\s+(half an|a couple of|a few|an?|[\w-]+|\d+)\s*(minutes?|mins?|hours?|hrs?)\b/.exec(t);
|
|
3611
|
-
if (rel) {
|
|
3612
|
-
const n = /^\d+$/.test(rel[1]) ? Number(rel[1]) : WORD_NUMS[rel[1]];
|
|
3613
|
-
if (n !== void 0) {
|
|
3614
|
-
const unit = rel[2].startsWith("h") ? 3600 : 60;
|
|
3615
|
-
return Math.round(n * unit);
|
|
3616
|
-
}
|
|
3617
|
-
}
|
|
3618
|
-
if (/after lunch|lunchtime/.test(t)) return untilHour(13, now);
|
|
3619
|
-
if (/tomorrow/.test(t)) {
|
|
3620
|
-
const due = new Date(now);
|
|
3621
|
-
due.setDate(due.getDate() + 1);
|
|
3622
|
-
due.setHours(9, 0, 0, 0);
|
|
3623
|
-
return Math.round((due.getTime() - now.getTime()) / 1e3);
|
|
3624
|
-
}
|
|
3625
|
-
if (/tonight|this evening/.test(t)) {
|
|
3626
|
-
return now.getHours() < 20 ? untilHour(20, now) : 3600;
|
|
3627
|
-
}
|
|
3628
|
-
if (/end of (the )?day|eod/.test(t)) return untilHour(17, now);
|
|
3629
|
-
if (/later|in a (bit|while)/.test(t)) return 1800;
|
|
3630
|
-
return null;
|
|
3631
|
-
}
|
|
3632
4881
|
var import_tweetnacl = __toESM(require_nacl_fast(), 1);
|
|
3633
4882
|
var import_tweetnacl2 = __toESM(require_nacl_fast(), 1);
|
|
3634
4883
|
var import_tweetnacl3 = __toESM(require_nacl_fast(), 1);
|
|
@@ -3897,10 +5146,32 @@ function verifyPairingCommit(commitment, proto, role, x25519Pub, ed25519Pub, non
|
|
|
3897
5146
|
return false;
|
|
3898
5147
|
}
|
|
3899
5148
|
}
|
|
3900
|
-
function sessionSlot(
|
|
3901
|
-
const id =
|
|
5149
|
+
function sessionSlot(sessionId2) {
|
|
5150
|
+
const id = sessionId2 ?? sessionId();
|
|
3902
5151
|
return `session:${id.slice(0, 8)}`;
|
|
3903
5152
|
}
|
|
5153
|
+
function subagentId(env = process.env) {
|
|
5154
|
+
if (!env.CLAUDE_CODE_CHILD_SESSION) return null;
|
|
5155
|
+
const pid = env.CLAUDE_PID ?? env.CLAUDE_CODE_MESSAGING_SOCKET?.match(/(\d+)\.sock$/)?.[1];
|
|
5156
|
+
return pid ?? null;
|
|
5157
|
+
}
|
|
5158
|
+
function subagentThread(env = process.env, sessionId2) {
|
|
5159
|
+
const base = sessionId2 ?? sessionId();
|
|
5160
|
+
const sub = subagentId(env);
|
|
5161
|
+
return sub ? `${base}:sub:${sub}` : base;
|
|
5162
|
+
}
|
|
5163
|
+
function uuidFromKey(key) {
|
|
5164
|
+
const h = createHash2("sha1").update(`paigy-thread:${key}`).digest();
|
|
5165
|
+
h[6] = h[6] & 15 | 80;
|
|
5166
|
+
h[8] = h[8] & 63 | 128;
|
|
5167
|
+
const hex = h.subarray(0, 16).toString("hex");
|
|
5168
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
5169
|
+
}
|
|
5170
|
+
function contactParentId(explicit, env = process.env) {
|
|
5171
|
+
if (explicit) return explicit;
|
|
5172
|
+
if (!subagentId(env)) return void 0;
|
|
5173
|
+
return uuidFromKey(subagentThread(env));
|
|
5174
|
+
}
|
|
3904
5175
|
var AGENT_NAME = process.env.PAIGY_AGENT || sessionSlot();
|
|
3905
5176
|
var PROTO = "paigy-pair-v2|sas=24";
|
|
3906
5177
|
var SAS_BITS = 24;
|
|
@@ -3972,13 +5243,15 @@ async function revokeToken(token) {
|
|
|
3972
5243
|
});
|
|
3973
5244
|
return res.ok;
|
|
3974
5245
|
}
|
|
3975
|
-
async function requestCode(suggestedName = AGENT_NAME, e2ee) {
|
|
5246
|
+
async function requestCode(suggestedName = AGENT_NAME, e2ee, device) {
|
|
3976
5247
|
const res = await reach(`${BACKEND_URL}/api/device/code`, {
|
|
3977
5248
|
method: "POST",
|
|
3978
5249
|
headers: { "content-type": "application/json" },
|
|
3979
|
-
body: JSON.stringify(
|
|
3980
|
-
|
|
3981
|
-
|
|
5250
|
+
body: JSON.stringify({
|
|
5251
|
+
suggestedName,
|
|
5252
|
+
...device ? { device } : {},
|
|
5253
|
+
...e2ee ? { proto: e2ee.proto, commitment: e2ee.commitment } : {}
|
|
5254
|
+
})
|
|
3982
5255
|
});
|
|
3983
5256
|
if (!res.ok) throw new Error(`/api/device/code failed: ${res.status} ${await res.text()}`);
|
|
3984
5257
|
return await res.json();
|
|
@@ -4162,6 +5435,28 @@ async function sealForE2ee(req, token, deps = {}) {
|
|
|
4162
5435
|
const { context: _c, options: _o, visuals: _v, repo: _r, branch: _b, ...meta } = req;
|
|
4163
5436
|
return { ...meta, envelope };
|
|
4164
5437
|
}
|
|
5438
|
+
async function createGoal(input, opts = {}) {
|
|
5439
|
+
const token = authToken(opts.token) ?? "";
|
|
5440
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/goals`, {
|
|
5441
|
+
method: "POST",
|
|
5442
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
5443
|
+
body: JSON.stringify(input)
|
|
5444
|
+
}));
|
|
5445
|
+
if (!res.ok) throw new Error(`create_goal failed: ${res.status} ${await res.text()}`);
|
|
5446
|
+
return await res.json();
|
|
5447
|
+
}
|
|
5448
|
+
async function claimGoal(goalId, opts = {}) {
|
|
5449
|
+
const token = authToken(opts.token) ?? "";
|
|
5450
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/goals/${encodeURIComponent(goalId)}/claim`, { method: "POST", headers: { authorization: `Bearer ${token}` } }));
|
|
5451
|
+
if (!res.ok) throw new Error(`claim_goal failed: ${res.status} ${await res.text()}`);
|
|
5452
|
+
return await res.json();
|
|
5453
|
+
}
|
|
5454
|
+
async function updateGoal(goalId, input, opts = {}) {
|
|
5455
|
+
const token = authToken(opts.token) ?? "";
|
|
5456
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/goals/${encodeURIComponent(goalId)}`, { method: "PATCH", headers: { "content-type": "application/json", authorization: `Bearer ${token}` }, body: JSON.stringify(input) }));
|
|
5457
|
+
if (!res.ok) throw new Error(`update_goal failed: ${res.status} ${await res.text()}`);
|
|
5458
|
+
return await res.json();
|
|
5459
|
+
}
|
|
4165
5460
|
async function submitNotification(req, opts = {}) {
|
|
4166
5461
|
const token = authToken(opts.token) ?? "";
|
|
4167
5462
|
const body = await sealForE2ee(req, token);
|
|
@@ -4173,18 +5468,11 @@ async function submitNotification(req, opts = {}) {
|
|
|
4173
5468
|
if (!res.ok) throw new Error(`notify failed: ${res.status} ${await res.text()}`);
|
|
4174
5469
|
return await res.json();
|
|
4175
5470
|
}
|
|
4176
|
-
|
|
4177
|
-
if (!reply.sealed) return reply.answer;
|
|
4178
|
-
const keyFile = (deps.readKeyFile ?? readKeyFile)();
|
|
4179
|
-
if (!keyFile?.keypair) {
|
|
4180
|
-
throw new Error("received a sealed answer but this agent has no device key to open it \u2014 re-pair.");
|
|
4181
|
-
}
|
|
4182
|
-
return openSealedAnswer(reply.sealed, keyFile.keypair);
|
|
4183
|
-
}
|
|
5471
|
+
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
4184
5472
|
var AWAIT_WINDOW_MS = 45e3;
|
|
4185
5473
|
var _partialSeen = /* @__PURE__ */ new Map();
|
|
4186
5474
|
async function pollPartials(notificationId) {
|
|
4187
|
-
const token =
|
|
5475
|
+
const token = authToken();
|
|
4188
5476
|
const after = _partialSeen.get(notificationId) ?? 0;
|
|
4189
5477
|
try {
|
|
4190
5478
|
const res = await reach(
|
|
@@ -4201,13 +5489,26 @@ async function pollPartials(notificationId) {
|
|
|
4201
5489
|
return null;
|
|
4202
5490
|
}
|
|
4203
5491
|
}
|
|
4204
|
-
function
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
5492
|
+
async function awaitReply(notificationId, opts) {
|
|
5493
|
+
const intervalMs = opts.intervalMs ?? 5e3;
|
|
5494
|
+
const windowMs = opts.windowMs ?? AWAIT_WINDOW_MS;
|
|
5495
|
+
const doSleep = opts.sleep ?? sleep2;
|
|
5496
|
+
const now = opts.now ?? Date.now;
|
|
5497
|
+
const read = opts.read;
|
|
5498
|
+
const start = now();
|
|
5499
|
+
while (true) {
|
|
5500
|
+
if (opts.signal?.aborted) return { type: "idle" };
|
|
5501
|
+
try {
|
|
5502
|
+
const item = await read(notificationId);
|
|
5503
|
+
if (item.type !== "idle") return item;
|
|
5504
|
+
const partial = await pollPartials(notificationId);
|
|
5505
|
+
if (partial) return partial;
|
|
5506
|
+
} catch (e) {
|
|
5507
|
+
if (!isNetworkError(e)) throw e;
|
|
5508
|
+
}
|
|
5509
|
+
if (now() - start >= windowMs) return { type: "idle" };
|
|
5510
|
+
await doSleep(intervalMs);
|
|
5511
|
+
}
|
|
4211
5512
|
}
|
|
4212
5513
|
async function getThread(parentId) {
|
|
4213
5514
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/thread/${encodeURIComponent(parentId)}`, {
|
|
@@ -4223,26 +5524,16 @@ async function searchThreads(q) {
|
|
|
4223
5524
|
if (!res.ok) throw new Error(`search_threads failed: ${res.status} ${await res.text()}`);
|
|
4224
5525
|
return await res.json();
|
|
4225
5526
|
}
|
|
4226
|
-
async function
|
|
5527
|
+
async function sweepLeased(opts = {}) {
|
|
4227
5528
|
const token = authToken(opts.token);
|
|
4228
5529
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/pending`, {
|
|
4229
5530
|
headers: {
|
|
4230
5531
|
authorization: `Bearer ${token}`,
|
|
4231
|
-
|
|
5532
|
+
"x-paigy-await-ack": "1"
|
|
4232
5533
|
}
|
|
4233
5534
|
}));
|
|
4234
5535
|
if (!res.ok) throw new Error(`check_replies failed: ${res.status} ${await res.text()}`);
|
|
4235
|
-
|
|
4236
|
-
if (opts.replyAck) return pending;
|
|
4237
|
-
return {
|
|
4238
|
-
...pending,
|
|
4239
|
-
replies: pending.replies.map((raw) => {
|
|
4240
|
-
const r = { ...raw, intents: landIntents(raw.intents) };
|
|
4241
|
-
if (!r.sealed) return r;
|
|
4242
|
-
const { sealed: _s, ...rest } = r;
|
|
4243
|
-
return { ...rest, answer: decryptAnswer(r) };
|
|
4244
|
-
})
|
|
4245
|
-
};
|
|
5536
|
+
return await res.json();
|
|
4246
5537
|
}
|
|
4247
5538
|
async function answerCallerQuestion(notificationId, answer) {
|
|
4248
5539
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/voice/session-answer`, {
|
|
@@ -4308,7 +5599,7 @@ async function registerDelivery(mode) {
|
|
|
4308
5599
|
return await res.json();
|
|
4309
5600
|
}
|
|
4310
5601
|
async function scheduleCallback(req) {
|
|
4311
|
-
const token =
|
|
5602
|
+
const token = authToken();
|
|
4312
5603
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/callback`, {
|
|
4313
5604
|
method: "POST",
|
|
4314
5605
|
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
@@ -4318,7 +5609,7 @@ async function scheduleCallback(req) {
|
|
|
4318
5609
|
return await res.json();
|
|
4319
5610
|
}
|
|
4320
5611
|
async function handoff(req) {
|
|
4321
|
-
const token =
|
|
5612
|
+
const token = authToken();
|
|
4322
5613
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/handoff`, {
|
|
4323
5614
|
method: "POST",
|
|
4324
5615
|
headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
|
|
@@ -4327,7 +5618,7 @@ async function handoff(req) {
|
|
|
4327
5618
|
if (!res.ok) throw new Error(`handoff failed: ${res.status} ${await res.text()}`);
|
|
4328
5619
|
return await res.json();
|
|
4329
5620
|
}
|
|
4330
|
-
var
|
|
5621
|
+
var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4331
5622
|
var ReplyLeaseExpiredError = class extends Error {
|
|
4332
5623
|
constructor() {
|
|
4333
5624
|
super(
|
|
@@ -4344,11 +5635,7 @@ var ReplyAckError = class extends Error {
|
|
|
4344
5635
|
}
|
|
4345
5636
|
retryable;
|
|
4346
5637
|
};
|
|
4347
|
-
function
|
|
4348
|
-
if (res.status === 401) throw new UnpairedError();
|
|
4349
|
-
return res;
|
|
4350
|
-
}
|
|
4351
|
-
function decryptAnswer2(reply, deps = {}) {
|
|
5638
|
+
function decryptAnswer(reply, deps = {}) {
|
|
4352
5639
|
if (!reply.sealed) return reply.answer;
|
|
4353
5640
|
const keyFile = (deps.readKeyFile ?? readKeyFile)();
|
|
4354
5641
|
if (!keyFile?.keypair) {
|
|
@@ -4358,22 +5645,11 @@ function decryptAnswer2(reply, deps = {}) {
|
|
|
4358
5645
|
}
|
|
4359
5646
|
return openSealedAnswer(reply.sealed, keyFile.keypair);
|
|
4360
5647
|
}
|
|
4361
|
-
function landIntents2(intents) {
|
|
4362
|
-
if (!intents?.length) return intents;
|
|
4363
|
-
return intents.map((intent) => {
|
|
4364
|
-
if (intent.kind !== "defer") return intent;
|
|
4365
|
-
const due = parseDue(intent.detail);
|
|
4366
|
-
return due !== null ? { ...intent, dueInSeconds: due } : intent;
|
|
4367
|
-
});
|
|
4368
|
-
}
|
|
4369
5648
|
function decodeReply(item, deps = {}) {
|
|
4370
5649
|
const { delivery: _delivery, ...withoutDelivery } = item;
|
|
4371
|
-
const enriched = {
|
|
4372
|
-
...withoutDelivery,
|
|
4373
|
-
intents: landIntents2(withoutDelivery.intents)
|
|
4374
|
-
};
|
|
5650
|
+
const enriched = { ...withoutDelivery };
|
|
4375
5651
|
if (!enriched.sealed) return enriched;
|
|
4376
|
-
const answer =
|
|
5652
|
+
const answer = decryptAnswer(enriched, deps);
|
|
4377
5653
|
const { sealed: _sealed, ...plaintext } = enriched;
|
|
4378
5654
|
return { ...plaintext, answer };
|
|
4379
5655
|
}
|
|
@@ -4399,11 +5675,11 @@ async function responseDetail(res) {
|
|
|
4399
5675
|
async function acknowledge(token, leaseId, opts = {}) {
|
|
4400
5676
|
const attempts = Math.max(1, Math.min(opts.attempts ?? 3, 5));
|
|
4401
5677
|
const backoffMs = Math.max(0, opts.backoffMs ?? 100);
|
|
4402
|
-
const doSleep = opts.doSleep ??
|
|
5678
|
+
const doSleep = opts.doSleep ?? sleep3;
|
|
4403
5679
|
let lastError;
|
|
4404
5680
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
4405
5681
|
try {
|
|
4406
|
-
const res =
|
|
5682
|
+
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/await/ack`, {
|
|
4407
5683
|
method: "POST",
|
|
4408
5684
|
headers: {
|
|
4409
5685
|
authorization: `Bearer ${token}`,
|
|
@@ -4434,6 +5710,15 @@ async function acknowledge(token, leaseId, opts = {}) {
|
|
|
4434
5710
|
`Could not acknowledge the Paigy reply after ${attempts} attempts (${detail}). The answer was not returned to the agent and remains recoverable after the lease expires.`
|
|
4435
5711
|
);
|
|
4436
5712
|
}
|
|
5713
|
+
async function ackReply(item, token, opts = {}) {
|
|
5714
|
+
const delivery = deliveryOf(item);
|
|
5715
|
+
if (!delivery) throw new Error("Paigy returned a reply without a delivery lease; nothing to acknowledge.");
|
|
5716
|
+
await acknowledge(token, delivery.leaseId, {
|
|
5717
|
+
attempts: opts.ackAttempts,
|
|
5718
|
+
backoffMs: opts.ackBackoffMs,
|
|
5719
|
+
doSleep: opts.ackSleep
|
|
5720
|
+
});
|
|
5721
|
+
}
|
|
4437
5722
|
async function receiveReply(item, token, opts = {}) {
|
|
4438
5723
|
const decoded = decodeReply(item, opts);
|
|
4439
5724
|
const delivery = deliveryOf(item);
|
|
@@ -4452,7 +5737,7 @@ async function receiveReply(item, token, opts = {}) {
|
|
|
4452
5737
|
}
|
|
4453
5738
|
async function pollAnswer(notificationId, opts = {}) {
|
|
4454
5739
|
const token = authToken() ?? "";
|
|
4455
|
-
const res =
|
|
5740
|
+
const res = ensureAuthed(await reach(
|
|
4456
5741
|
`${BACKEND_URL}/api/await?notificationId=${encodeURIComponent(notificationId)}`,
|
|
4457
5742
|
{
|
|
4458
5743
|
headers: {
|
|
@@ -4466,24 +5751,11 @@ async function pollAnswer(notificationId, opts = {}) {
|
|
|
4466
5751
|
if (wire.type !== "reply") return wire;
|
|
4467
5752
|
return receiveReply(wire, token, opts);
|
|
4468
5753
|
}
|
|
4469
|
-
async function
|
|
4470
|
-
|
|
4471
|
-
const windowMs = opts.windowMs ?? AWAIT_WINDOW_MS;
|
|
4472
|
-
const doSleep = opts.sleep ?? sleep2;
|
|
4473
|
-
const now = opts.now ?? Date.now;
|
|
4474
|
-
const start = now();
|
|
4475
|
-
while (true) {
|
|
4476
|
-
if (opts.signal?.aborted) return { type: "idle" };
|
|
4477
|
-
const item = await pollAnswer(notificationId);
|
|
4478
|
-
if (item.type !== "idle") return item;
|
|
4479
|
-
const partial = await pollPartials(notificationId);
|
|
4480
|
-
if (partial) return partial;
|
|
4481
|
-
if (now() - start >= windowMs) return { type: "idle" };
|
|
4482
|
-
await doSleep(intervalMs);
|
|
4483
|
-
}
|
|
5754
|
+
async function awaitReply2(notificationId, opts = {}) {
|
|
5755
|
+
return awaitReply(notificationId, { ...opts, read: pollAnswer });
|
|
4484
5756
|
}
|
|
4485
|
-
async function
|
|
4486
|
-
const pending = await
|
|
5757
|
+
async function checkReplies(opts = {}) {
|
|
5758
|
+
const pending = await sweepLeased(opts);
|
|
4487
5759
|
const token = authToken(opts.token) ?? "";
|
|
4488
5760
|
const replies = await Promise.all(pending.replies.map(async (reply) => {
|
|
4489
5761
|
const received = await receiveReply(
|
|
@@ -4496,17 +5768,14 @@ async function checkReplies2(opts = {}) {
|
|
|
4496
5768
|
}));
|
|
4497
5769
|
return {
|
|
4498
5770
|
...pending,
|
|
4499
|
-
replies
|
|
5771
|
+
replies,
|
|
5772
|
+
work: pending.work ?? []
|
|
4500
5773
|
};
|
|
4501
5774
|
}
|
|
4502
|
-
function
|
|
4503
|
-
if (res.status === 401) throw new UnpairedError();
|
|
4504
|
-
return res;
|
|
4505
|
-
}
|
|
4506
|
-
async function setTaskState(notificationId, state, opts = {}) {
|
|
5775
|
+
async function patchState(path, state, opts, operation) {
|
|
4507
5776
|
const token = authToken(opts.token) ?? "";
|
|
4508
|
-
const res =
|
|
4509
|
-
`${BACKEND_URL}
|
|
5777
|
+
const res = ensureAuthed(await reach(
|
|
5778
|
+
`${BACKEND_URL}${path}`,
|
|
4510
5779
|
{
|
|
4511
5780
|
method: "PATCH",
|
|
4512
5781
|
headers: {
|
|
@@ -4518,22 +5787,27 @@ async function setTaskState(notificationId, state, opts = {}) {
|
|
|
4518
5787
|
}
|
|
4519
5788
|
));
|
|
4520
5789
|
if (!res.ok) {
|
|
4521
|
-
throw new Error(
|
|
5790
|
+
throw new Error(`${operation} failed: ${res.status} ${await res.text()}`);
|
|
4522
5791
|
}
|
|
4523
5792
|
return await res.json();
|
|
4524
5793
|
}
|
|
4525
|
-
function
|
|
4526
|
-
|
|
5794
|
+
function setTaskState(notificationId, state, opts = {}) {
|
|
5795
|
+
return patchState(`/api/notify/${encodeURIComponent(notificationId)}/state`, state, opts, "set_task_state");
|
|
5796
|
+
}
|
|
5797
|
+
function setWorkState(input, opts = {}) {
|
|
5798
|
+
const parsed = SetWorkStateSchema.parse(input);
|
|
5799
|
+
const path = parsed.workId ? `/api/work/${encodeURIComponent(parsed.workId)}/state` : `/api/notify/${encodeURIComponent(parsed.notificationId)}/state`;
|
|
5800
|
+
return patchState(path, parsed.state, opts, "set_work_state");
|
|
4527
5801
|
}
|
|
4528
5802
|
|
|
4529
5803
|
export {
|
|
4530
5804
|
BACKEND_URL,
|
|
5805
|
+
isNetworkError,
|
|
4531
5806
|
reach,
|
|
4532
|
-
normalizeSpeech,
|
|
4533
|
-
lintNotify,
|
|
4534
5807
|
NotifyRequestSchema,
|
|
4535
|
-
|
|
5808
|
+
SetWorkStateSchema,
|
|
4536
5809
|
ScheduleCallbackSchema,
|
|
5810
|
+
contactParentId,
|
|
4537
5811
|
AGENT_NAME,
|
|
4538
5812
|
TOKEN_PATH,
|
|
4539
5813
|
saveToken,
|
|
@@ -4544,7 +5818,6 @@ export {
|
|
|
4544
5818
|
deleteToken,
|
|
4545
5819
|
revokeToken,
|
|
4546
5820
|
requestCode,
|
|
4547
|
-
pollToken,
|
|
4548
5821
|
saveKeyFile,
|
|
4549
5822
|
readKeyFile,
|
|
4550
5823
|
deleteKeyFile,
|
|
@@ -4553,10 +5826,16 @@ export {
|
|
|
4553
5826
|
fetchCredential,
|
|
4554
5827
|
pairStep,
|
|
4555
5828
|
UnpairedError,
|
|
5829
|
+
createGoal,
|
|
5830
|
+
claimGoal,
|
|
5831
|
+
updateGoal,
|
|
4556
5832
|
submitNotification,
|
|
4557
5833
|
getThread,
|
|
4558
5834
|
searchThreads,
|
|
5835
|
+
sweepLeased,
|
|
4559
5836
|
answerCallerQuestion,
|
|
5837
|
+
overrideToken,
|
|
5838
|
+
authToken,
|
|
4560
5839
|
hatch,
|
|
4561
5840
|
whoAmI,
|
|
4562
5841
|
setIdentity,
|
|
@@ -4564,8 +5843,10 @@ export {
|
|
|
4564
5843
|
registerDelivery,
|
|
4565
5844
|
scheduleCallback,
|
|
4566
5845
|
handoff,
|
|
4567
|
-
|
|
4568
|
-
|
|
5846
|
+
decodeReply,
|
|
5847
|
+
ackReply,
|
|
5848
|
+
awaitReply2,
|
|
5849
|
+
checkReplies,
|
|
4569
5850
|
setTaskState,
|
|
4570
|
-
|
|
5851
|
+
setWorkState
|
|
4571
5852
|
};
|