@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.
@@ -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 z2 = new Uint8Array(16), x = new Uint8Array(64);
343
+ var z3 = new Uint8Array(16), x = new Uint8Array(64);
340
344
  var u, i;
341
- for (i = 0; i < 16; i++) z2[i] = 0;
342
- for (i = 0; i < 8; i++) z2[i] = n[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, z2, k, sigma);
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 + (z2[i] & 255) | 0;
349
- z2[i] = u & 255;
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, z2, k, sigma);
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 z2 = new Uint8Array(16), x = new Uint8Array(64);
367
+ var z3 = new Uint8Array(16), x = new Uint8Array(64);
364
368
  var u, i;
365
- for (i = 0; i < 16; i++) z2[i] = 0;
366
- for (i = 0; i < 8; i++) z2[i] = n[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, z2, k, sigma);
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 + (z2[i] & 255) | 0;
373
- z2[i] = u & 255;
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, z2, k, sigma);
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 z2 = new Uint8Array(32);
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++) z2[i] = n[i];
1264
- z2[31] = n[31] & 127 | 64;
1265
- z2[0] &= 248;
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 = z2[i >>> 3] >>> (i & 7) & 1;
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 SESSION_ID = process.env.PAIGY_SESSION_ID ?? process.env.CLAUDE_CODE_SESSION_ID ?? INSTANCE_ID;
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 TITLE_MAX = 90;
2287
- var CHUNKS_MAX = 8;
2288
- var CHUNK_MAX = 300;
2289
- var ASK_MAX = 1e4;
2290
- var OPTION_MAX = 80;
2291
- var NEEDS_MAX = 6;
2292
- var UNSPEAKABLE = /```/;
2293
- function normalizeSpeech(req) {
2294
- const flat = (s) => s.replace(/\s+/g, " ").trim();
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
- ...req,
2297
- ...req.ask !== void 0 ? { ask: flat(req.ask) } : {},
2298
- ...req.context ? {
2299
- context: {
2300
- ...req.context,
2301
- title: flat(req.context.title),
2302
- description: req.context.description.map(flat)
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 lintNotify(req) {
2308
- const problems = [];
2309
- if (req.ask !== void 0) {
2310
- if (req.ask.length > ASK_MAX)
2311
- problems.push(`ask is ${req.ask.length} chars \u2014 state the need and why it matters now in \u2264${ASK_MAX}; move detail into a smaller follow-up`);
2312
- if (UNSPEAKABLE.test(req.ask))
2313
- problems.push("ask contains a code fence \u2014 write it as plain prose (it may be read aloud on a call)");
2314
- for (const n of req.needs ?? []) {
2315
- if (n.length > OPTION_MAX) problems.push(`need "${n.slice(0, 40)}\u2026" is too long \u2014 each need is a short phrase (\u2264${OPTION_MAX} chars)`);
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
- if ((req.needs?.length ?? 0) > NEEDS_MAX)
2318
- problems.push(`${req.needs?.length} needs \u2014 cap at ${NEEDS_MAX}; a call can't cover more in one conversation, split the rest into a second ask`);
2319
- return problems;
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
- const spoken = req.urgency === "call" || req.urgency === "banner";
2322
- if (req.context) {
2323
- if (req.context.title.length > TITLE_MAX)
2324
- problems.push(`title is ${req.context.title.length} chars \u2014 shorten to \u2264${TITLE_MAX} (it's what shows on the banner / gets spoken on a ring)`);
2325
- if (UNSPEAKABLE.test(req.context.title))
2326
- problems.push("title contains a code fence \u2014 one plain-prose line");
2327
- if (req.context.description.length > CHUNKS_MAX)
2328
- problems.push(`${req.context.description.length} description chunks \u2014 cap at ${CHUNKS_MAX}; merge or drop the rest`);
2329
- for (const [i, chunk] of req.context.description.entries()) {
2330
- if (chunk.length > CHUNK_MAX)
2331
- problems.push(`description[${i}] is ${chunk.length} chars \u2014 split it into standalone points of \u2264${CHUNK_MAX}`);
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
- if (spoken && UNSPEAKABLE.test(req.context.description.join(" ")))
2334
- problems.push("urgency is 'call'/'banner' but the description has a code fence \u2014 rewrite in spoken register (it will be read aloud)");
3099
+ return {
3100
+ type: [
3101
+ primitiveMappings[def.innerType._def.typeName],
3102
+ "null"
3103
+ ]
3104
+ };
2335
3105
  }
2336
- for (const o of req.options ?? []) {
2337
- if (o.label.length > OPTION_MAX)
2338
- problems.push(`option label "${o.label.slice(0, 40)}\u2026" is ${o.label.length} chars \u2014 labels must read at a glance (\u2264${OPTION_MAX}); move detail into the description`);
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
- return problems;
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
- var ContextSchema = z.object({
2343
- title: z.string().min(1).describe("One-line headline of what you need (required, non-empty)."),
2344
- description: z.array(z.string().min(1)).describe(
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 = z.object({
2349
- kind: z.enum(["human", "agent"]),
2350
- id: z.string()
3606
+ var ParticipantSchema = z2.object({
3607
+ kind: z2.enum(["human", "agent"]),
3608
+ id: z2.string()
2351
3609
  });
2352
- var TransformSchema = z.enum([
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 = z.object({
2369
- id: z.string(),
2370
- label: z.string(),
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: z.string().max(16384).describe(
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: z.string().url().describe(
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 = z.object({
2381
- url: z.string().url(),
2382
- label: z.string().optional()
3638
+ var VisualSchema = z2.object({
3639
+ url: z2.string().url(),
3640
+ label: z2.string().optional()
2383
3641
  });
2384
- var NotifyLevelSchema = z.enum(["inbox", "push", "banner", "call"]);
2385
- var SelectShapeSchema = z.enum(["one", "many", "rank", "confirm", "text"]);
2386
- var ReceiptEventSchema = z.enum([
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 = z.object({
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: z.array(z.string()).nullable(),
3679
+ points: z2.array(z2.string()).nullable(),
2422
3680
  /** Whether the ask blocks the sender — what lets arbitration escalate it on silence. */
2423
- blocking: z.boolean(),
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: z.string().datetime().nullable().optional()
3684
+ deadline: z2.string().datetime().nullable().optional()
2427
3685
  });
2428
- var NotifyRequestSchema = z.object({
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: z.array(OptionSchema.omit({ id: true })).min(1).optional().describe(
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: z.array(z.string().min(1)).optional().describe(
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: z.array(VisualSchema).optional().describe(
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: z.string().optional(),
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: z.string().optional(),
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: z.string().uuid().optional(),
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: z.string().optional(),
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: z.object({
2472
- context: z.lazy(() => EnvelopeSchema).optional(),
2473
- options: z.lazy(() => EnvelopeSchema).optional(),
2474
- visuals: z.lazy(() => EnvelopeSchema).optional()
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: z.string().min(1).max(1e4).optional().describe(
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: z.array(z.string().min(1)).optional().describe(
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: z.enum(["whenever", "soon", "now"]).optional().describe(
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: z.enum(["none", "soft", "hard"]).optional().describe(
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: z.boolean().optional().describe(
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: z.enum(["call", "message"]).optional().describe(
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: z.enum(["yesno", "approve"]).default("yesno").describe(
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: z.boolean().default(false).describe(
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
- }).superRefine((r, ctx) => {
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: z.ZodIssueCode.custom, path: ["envelope", "context"], message: "sealed request must include envelope.context" });
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: z.ZodIssueCode.custom, path: [f], message: `E2EE request must not carry plaintext ${f} \u2014 it's sealed in envelope.${f}` });
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: z.ZodIssueCode.custom, path: ["points"], message: "E2EE request must not carry plaintext points" });
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: z.ZodIssueCode.custom, path: ["ask"], message: "E2EE request must not carry a plaintext ask \u2014 derive the shape agent-side and seal it" });
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: z.ZodIssueCode.custom, path: ["needs"], message: "E2EE request must not carry plaintext needs" });
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: z.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.` });
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: z.ZodIssueCode.custom, path: ["needs"], message: "needs/urgencyHint belong to the simplified `ask` form \u2014 with a shaped request use points/urgency" });
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: z.ZodIssueCode.custom, path: ["context"], message: "context is required (plaintext path)" });
3815
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["context"], message: "context is required (plaintext path)" });
2554
3816
  if (!r.select)
2555
- ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["select"], message: "select is required on the shaped form" });
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: z.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' requires options` });
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: z.ZodIssueCode.custom, path: ["options"], message: `select:'${r.select}' takes no options` });
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 = z.enum(["pending", "answered", "ignored"]);
2618
- var AgentStateSchema = z.enum(["idle", "in_progress", "completed", "needs_input"]);
2619
- var SetTaskStateSchema = z.object({
2620
- state: z.enum(["in_progress", "completed", "needs_input"])
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 TurnSchema = z.object({
2623
- prompt: z.string(),
2624
- reply: z.string()
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 UserAnswerSchema = z.discriminatedUnion("kind", [
2627
- z.object({ kind: z.literal("option"), optionId: z.string(), label: z.string().optional() }),
2628
- z.object({ kind: z.literal("text"), text: z.string() }),
2629
- z.object({ kind: z.literal("ignored") }),
2630
- z.object({ kind: z.literal("multi"), optionIds: z.array(z.string()), labels: z.array(z.string()).optional() }),
2631
- z.object({ kind: z.literal("ranked"), optionIds: z.array(z.string()), labels: z.array(z.string()).optional() }),
2632
- z.object({ kind: z.literal("clarify"), chunks: z.array(z.string()).min(1) }),
2633
- z.object({ kind: z.literal("confirm"), approved: z.boolean() }),
2634
- z.object({ kind: z.literal("turns"), turns: z.array(TurnSchema).min(1) }),
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
- z.object({ kind: z.literal("precedent"), answer: z.string(), derivation: z.string(), sources: z.array(z.string()).min(1) })
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 = z.object({
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: z.enum(["defer", "delegate", "channel", "question", "detail", "feedback", "command", "control"]),
2647
- detail: z.string(),
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: z.number().int().positive().optional(),
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: z.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
3926
+ fault: z2.enum(["loop", "unanswered", "overridden", "misheard", "slow", "other"]).optional()
2658
3927
  });
2659
- var RideAlongSchema = z.object({
3928
+ var RideAlongSchema = z2.object({
2660
3929
  /** The note this came from — assign/clarify/close it through /api/notes/:id. */
2661
- noteId: z.string(),
3930
+ noteId: z2.string(),
2662
3931
  /** What to do, in the owner's own words (the note's headline). Never model-rewritten. */
2663
- text: z.string(),
3932
+ text: z2.string(),
2664
3933
  /** The thread to report back on, when the note was dispatched over the request rail. */
2665
- parentId: z.string().nullable()
3934
+ parentId: z2.string().nullable()
2666
3935
  });
2667
- var AwaitItemSchema = z.discriminatedUnion("type", [
2668
- z.object({
2669
- type: z.literal("reply"),
2670
- parentId: z.string(),
2671
- notificationId: z.string(),
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: z.lazy(() => SealedAnswerSchema).optional(),
2680
- /** Broker extras (#381), present when the answer was mapped from a call: next
2681
- * steps the user attached ("call me after lunch" → defer — act on it via
2682
- * schedule_callback) and the raw words the shaped answer was mapped from. */
2683
- intents: z.array(IntentSchema).optional(),
2684
- transcript: z.string().optional(),
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: z.array(z.string()).optional(),
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: z.array(RideAlongSchema).optional()
3973
+ also: z2.array(RideAlongSchema).optional()
2694
3974
  }),
2695
- z.object({
2696
- type: z.literal("remind"),
2697
- parentId: z.string(),
2698
- notificationId: z.string(),
2699
- remindAt: z.string().datetime({ offset: true }),
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: z.number()
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
- z.object({
2708
- type: z.literal("superseded"),
2709
- parentId: z.string(),
2710
- notificationId: z.string()
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
- z.object({
2718
- type: z.literal("partial"),
2719
- notificationId: z.string(),
2720
- inFlight: z.literal(true),
2721
- turn: z.object({
2722
- idx: z.number(),
2723
- prompt: z.string(),
2724
- reply: z.string(),
2725
- acts: z.array(IntentSchema).nullable().optional()
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
- z.object({ type: z.literal("idle"), also: z.array(RideAlongSchema).optional() })
4008
+ z2.object({ type: z2.literal("idle"), also: z2.array(RideAlongSchema).optional() })
2729
4009
  ]);
2730
- var CallbackTriggerSchema = z.enum(["on_done", "on_blocked", "scheduled"]);
2731
- var ScheduleCallbackSchema = z.object({
2732
- parentId: z.string().describe("The thread to call back on (from a prior contact / reply / request)."),
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: z.number().int().positive().optional().describe("For 'scheduled' only: how many seconds from now to fire."),
2735
- note: z.string().optional().describe("What to tell the user when you follow up.")
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 = z.object({
2738
- replies: z.array(
2739
- z.object({
2740
- parentId: z.string(),
2741
- notificationId: z.string(),
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: z.lazy(() => SealedAnswerSchema).optional(),
2747
- /** Broker extras (#381): next steps the user attached to a call-mapped answer
2748
- * ("call me after lunch" → defer — act on it via schedule_callback) and the
2749
- * raw words the shaped answer was mapped from. */
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: z.array(z.string()).optional()
4033
+ covered: z2.array(z2.string()).optional()
2754
4034
  })
2755
4035
  ),
2756
- pending: z.array(
2757
- z.object({ parentId: z.string(), notificationId: z.string(), createdAt: z.string() })
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: z.object({ name: z.string(), device: z.string().nullable(), tokenId: z.string() }).optional(),
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
- * set_task_state on its notificationId. */
2768
- requests: z.array(
2769
- z.object({
2770
- parentId: z.string(),
2771
- notificationId: z.string(),
2772
- text: z.string(),
2773
- createdAt: z.string(),
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: z.string().optional(),
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 (set_task_state claims it, reply with contact on its parentId), and say
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: z.string().optional()
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: z.array(RideAlongSchema).optional(),
2791
- owedCallbacks: z.array(
2792
- z.object({ parentId: z.string(), trigger: CallbackTriggerSchema, note: z.string() })
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 (set_task_state) or continue the work. */
2797
- stalled: z.array(
2798
- z.object({ parentId: z.string(), notificationId: z.string(), title: z.string().nullable(), startedAt: z.string() })
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: z.array(
2808
- z.object({
2809
- parentId: z.string(),
2810
- busy: z.boolean(),
2811
- items: z.array(
2812
- z.object({
2813
- kind: z.enum(["reply", "request"]),
2814
- notificationId: z.string(),
2815
- at: z.string()
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 = z.object({
2822
- notificationId: z.string(),
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: z.string().datetime(),
4117
+ createdAt: z2.string().datetime(),
2825
4118
  answer: UserAnswerSchema.optional(),
2826
- answeredAt: z.string().datetime().optional(),
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: z.array(RideAlongSchema).optional()
4123
+ also: z2.array(RideAlongSchema).optional()
2831
4124
  });
2832
- var NotifyPlanUnitSchema = z.object({
2833
- notificationId: z.string(),
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: z.string(),
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: z.literal(true).optional(),
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: z.array(z.enum(["options", "visuals"])).optional(),
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: z.object({
4142
+ proposal: z2.object({
2850
4143
  select: SelectShapeSchema,
2851
- options: z.array(z.object({ label: z.string().min(1) })).optional()
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: z.enum(["decision", "approval", "knowledge"]).optional()
4151
+ wants: z2.enum(["decision", "approval", "knowledge"]).optional()
2859
4152
  });
2860
- var NotifyPlanSchema = z.object({
2861
- units: z.array(NotifyPlanUnitSchema),
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: z.string().optional()
4157
+ speaks: z2.string().optional()
2865
4158
  });
2866
- var UserResponseSchema = z.object({
2867
- requestId: z.string(),
4159
+ var UserResponseSchema = z2.object({
4160
+ requestId: z2.string(),
2868
4161
  answer: UserAnswerSchema,
2869
- answeredAt: z.string().datetime(),
2870
- intents: z.array(IntentSchema).optional(),
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: z.lazy(() => SealedAnswerSchema).optional(),
4168
+ sealed: z2.lazy(() => SealedAnswerSchema).optional(),
2877
4169
  /** Coverage report (#396): which of the ask's declared `points` were addressed. */
2878
- covered: z.array(z.string()).optional()
4170
+ covered: z2.array(z2.string()).optional()
2879
4171
  });
2880
- var VoiceKeySchema = z.enum(["rachel", "george", "jessica", "brian", "lily"]);
2881
- var AgendaTurnSchema = z.object({
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: z.array(z.string()).optional(),
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: z.array(z.string().min(1)).max(3).default([]),
2888
- question: z.string().min(1).nullable(),
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: z.boolean().optional(),
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: z.string().optional(),
4186
+ claimId: z2.string().optional(),
2895
4187
  /** The claim's voice key (#462) — the OUTBOUND identity, audible who-is-asking. */
2896
- voice: z.string().optional(),
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: z.string().optional(),
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: z.array(OptionSchema.omit({ id: true })).optional(),
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: z.number().positive().optional(),
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: z.boolean().optional()
4209
+ blocking: z2.boolean().optional()
2917
4210
  });
2918
4211
  var CLAIM_STALE_MS = 30 * 6e4;
2919
- var InboxItemSchema = z.object({
2920
- id: z.string(),
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: z.string().optional(),
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: z.string().optional(),
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: z.number().int().optional(),
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: z.number().int().positive().optional(),
2947
- tokenId: z.string().optional(),
4239
+ units: z2.number().int().positive().optional(),
4240
+ tokenId: z2.string().optional(),
2948
4241
  status: NotifyStatusSchema,
2949
4242
  context: ContextSchema,
2950
- options: z.array(OptionSchema).optional(),
4243
+ options: z2.array(OptionSchema).optional(),
2951
4244
  /** The ask's declared coverage points (#396), when the agent sent them. */
2952
- points: z.array(z.string()).optional(),
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: z.boolean().optional(),
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: z.string().optional(),
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: z.string().datetime().optional(),
2977
- agenda: z.array(AgendaTurnSchema).optional(),
2978
- /** On a replied detail (#397): the next steps the user attached to the answer
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: z.string(),
4274
+ name: z2.string(),
2985
4275
  /** The pairing's assigned voice (#462); absent = the default voice. */
2986
4276
  voice: VoiceKeySchema.optional(),
2987
- repo: z.string().optional(),
2988
- branch: z.string().optional(),
2989
- createdAt: z.string().datetime(),
2990
- snoozedUntil: z.string().datetime().optional(),
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: z.enum(["you", "agent"]).default("you"),
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: z.string().optional(),
3000
- clarifies: z.string().optional(),
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: z.boolean().default(false),
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: z.object({
4302
+ why: z2.object({
3013
4303
  asked: NotifyLevelSchema,
3014
4304
  got: NotifyLevelSchema,
3015
- because: z.enum(["unresponsive", "dismissed", "not_permitted", "silent", "coalesced", "agent_capped", "unplanned", "learned_raise"]).optional(),
3016
- line: z.string()
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: z.enum(["one", "many", "rank", "confirm", "text"]).default("one"),
3019
- confirmStyle: z.enum(["yesno", "approve"]).default("yesno").describe(
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: z.boolean().default(false),
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: z.object({
3035
- context: z.lazy(() => EnvelopeSchema).optional(),
3036
- options: z.lazy(() => EnvelopeSchema).optional(),
3037
- visuals: z.lazy(() => EnvelopeSchema).optional()
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: z.string().optional()
4332
+ agentX25519: z2.string().optional()
3043
4333
  });
3044
- var SnoozeRequestSchema = z.object({
3045
- requestId: z.string(),
3046
- until: z.string().datetime()
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 = z.object({
3050
- voipToken: z.string().min(1).optional(),
3051
- alertToken: z.string().min(1).optional(),
3052
- fcmToken: z.string().min(1).optional(),
3053
- platform: z.enum(["ios", "android"])
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: z.ZodIssueCode.custom,
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 = z.enum([
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 = z.object({
4366
+ var BrokerTuningSchema = z2.object({
3077
4367
  /** 'none' = skip the spoken ack after a mapped answer (power users find it slow). */
3078
- ackVerbosity: z.enum(["normal", "none"]).optional(),
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: z.enum(["low", "normal", "high"]).optional(),
4371
+ clarifyEagerness: z2.enum(["low", "normal", "high"]).optional(),
3082
4372
  /** The user's own shorthand: when they say `say`, they mean `mean`. */
3083
- phrasebook: z.array(z.object({ say: z.string().min(1).max(60), mean: z.string().min(1).max(120) })).max(24).optional(),
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: z.enum(["en", "es"]).optional()
4379
+ language: z2.enum(["en", "es"]).optional()
3090
4380
  });
3091
- var UserSettingsSchema = z.object({
3092
- permissions: z.object({
3093
- call: z.boolean(),
3094
- banner: z.boolean(),
3095
- push: z.boolean()
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: z.enum(["default", "all_calls", "silent"]),
3098
- silentPush: z.boolean(),
3099
- autoCallback: z.boolean(),
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: z.boolean(),
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: z.enum(["hosted", "on_device"]).optional(),
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: z.record(z.string(), z.union([z.number(), z.string()])).optional(),
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: z.boolean().optional(),
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: z.string().min(1).max(64).optional(),
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: z.enum(["off", "on"]).optional(),
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 = z.object({
3142
- id: z.string(),
3143
- parentId: z.string(),
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: z.enum(["user", "agent"]),
3146
- title: z.string(),
4435
+ initiator: z2.enum(["user", "agent"]),
4436
+ title: z2.string(),
3147
4437
  /** The agent on the other end (its name). */
3148
- name: z.string(),
3149
- createdAt: z.string(),
4438
+ name: z2.string(),
4439
+ createdAt: z2.string(),
3150
4440
  /** When the agent fetched your request (user→agent only). */
3151
- agentAckedAt: z.string().nullable(),
4441
+ agentAckedAt: z2.string().nullable(),
3152
4442
  /** When you answered the agent's notification (agent→user only). */
3153
- humanAckedAt: z.string().nullable()
4443
+ humanAckedAt: z2.string().nullable()
3154
4444
  });
3155
4445
  var ACTIVITY_LINES = 2;
3156
4446
  var ACTIVITY_LINE_MAX = 80;
3157
- var AgentActivitySchema = z.object({
4447
+ var AgentActivitySchema = z2.object({
3158
4448
  /** Oldest first, so the newest line is last — the one that replaces in place. */
3159
- lines: z.array(z.string().max(ACTIVITY_LINE_MAX)).max(ACTIVITY_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: z.string().datetime()
4452
+ at: z2.string().datetime()
3163
4453
  });
3164
- var ConnectionSummarySchema = z.object({
4454
+ var ConnectionSummarySchema = z2.object({
3165
4455
  /** The connection = the agent's token id (used to address a request). */
3166
- id: z.string(),
3167
- device: z.string().nullable(),
4456
+ id: z2.string(),
4457
+ device: z2.string().nullable(),
3168
4458
  /** The agent's display name (the single pairing name). */
3169
- name: z.string(),
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: z.string().nullable(),
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: z.string().datetime(),
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: z.string().datetime().nullable(),
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: z.string().datetime().nullable().optional(),
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: z.object({
3194
- harnesses: z.array(z.object({ name: z.string(), label: z.string(), status: z.string() })).optional(),
3195
- workspaces: z.array(z.string()).optional()
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: z.boolean()
4494
+ managed: z2.boolean()
3205
4495
  });
3206
- var MoveRingSchema = z.enum(["home", "travels", "retired", "quarantined"]);
3207
- var MoveSchema = z.object({
3208
- id: z.string(),
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: z.string(),
4500
+ question: z2.string(),
3211
4501
  /** The operative ruling. Editable by the user (PATCH) — which resets the ledger. */
3212
- answer: z.string(),
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: z.string().nullable(),
4505
+ rationale: z2.string().nullable(),
3216
4506
  /** Where the ruling lives: a repo/workspace, or 'global'. */
3217
- scope: z.string(),
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: z.boolean(),
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: z.boolean(),
4513
+ pinBroken: z2.boolean(),
3224
4514
  /** When the ruling was distilled. */
3225
- learnedAt: z.string(),
4515
+ learnedAt: z2.string(),
3226
4516
  /** Last time it answered an ask. Null = never fired. */
3227
- lastUsedAt: z.string().nullable(),
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: z.number(),
4520
+ usedCount: z2.number(),
3231
4521
  /** Ledger: outcomes that said it held up. Saturating — the tenth is worth almost nothing. */
3232
- confirms: z.number(),
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: z.number(),
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: z.object({ id: z.string(), name: z.string() }).nullable()
4528
+ learnedFrom: z2.object({ id: z2.string(), name: z2.string() }).nullable()
3239
4529
  });
3240
- var CreateRequestSchema = z.object({
4530
+ var CreateRequestSchema = z2.object({
3241
4531
  /** The connection (token id) to send to, from GET /api/tokens. */
3242
- tokenId: z.string(),
4532
+ tokenId: z2.string(),
3243
4533
  /** The user's message to the agent. */
3244
- text: z.string().min(1),
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: z.string().optional(),
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: z.string().optional()
4541
+ contextParentId: z2.string().optional()
3252
4542
  });
3253
- var HandoffSchema = z.object({
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: z.string().uuid().optional(),
4547
+ parentId: z2.string().uuid().optional(),
3256
4548
  /** One-line headline of the working context handed off. */
3257
- title: z.string().min(1),
4549
+ title: z2.string().min(1),
3258
4550
  /** The brief — standalone notes the successor reads (what was done, what's left, links). */
3259
- notes: z.array(z.string().min(1)).min(1),
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: z.string().optional(),
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: z.boolean().optional()
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 = z.enum(["app", "call"]);
3270
- var NoteStatusSchema = z.enum(["open", "assigned", "in_progress", "done"]);
3271
- var NoteRepeatSchema = z.enum(["once", "until_done"]);
3272
- var DecisionSchema = z.object({
3273
- id: z.string(),
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: z.string().nullable(),
4571
+ noteId: z2.string().nullable(),
3277
4572
  /** What was ambiguous — the broker's (or the user's own) question. */
3278
- question: z.string(),
4573
+ question: z2.string(),
3279
4574
  /** The user's ruling; null while the question is open. */
3280
- answer: z.string().nullable(),
3281
- decidedAt: z.string().nullable(),
3282
- createdAt: z.string()
4575
+ answer: z2.string().nullable(),
4576
+ decidedAt: z2.string().nullable(),
4577
+ createdAt: z2.string()
3283
4578
  });
3284
- var NoteSchema = z.object({
3285
- id: z.string(),
4579
+ var NoteSchema = z2.object({
4580
+ id: z2.string(),
3286
4581
  /** One-line headline (broker-titled; deterministic floor). */
3287
- title: z.string(),
4582
+ title: z2.string(),
3288
4583
  /** The original intent, verbatim — assignees always see the user's own words. */
3289
- intent: z.string(),
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: z.string().nullable(),
4588
+ assignee: z2.string().nullable(),
3294
4589
  /** The request thread minted at assignment; null until assigned. */
3295
- parentId: z.string().nullable(),
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: z.string().nullable().default(null),
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: z.number().int().default(0),
3308
- lastRideAt: z.string().nullable().default(null),
3309
- createdAt: z.string()
4602
+ rides: z2.number().int().default(0),
4603
+ lastRideAt: z2.string().nullable().default(null),
4604
+ createdAt: z2.string()
3310
4605
  });
3311
- var CreateNoteSchema = z.object({
4606
+ var CreateNoteSchema = z2.object({
3312
4607
  /** The intent, in the user's own words. Stored verbatim; the broker only titles it. */
3313
- text: z.string().min(1).max(4e3),
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: z.boolean().optional(),
4612
+ forMe: z2.boolean().optional(),
3318
4613
  /** The not-before, when the user already said one. Absent = the very next call. */
3319
- dueAt: z.string().datetime().optional(),
4614
+ dueAt: z2.string().datetime().optional(),
3320
4615
  repeat: NoteRepeatSchema.optional()
3321
4616
  });
3322
- var RecordDecisionSchema = z.object({
4617
+ var RecordDecisionSchema = z2.object({
3323
4618
  /** An open decision (from /clarify) to answer. */
3324
- decisionId: z.string().optional(),
4619
+ decisionId: z2.string().optional(),
3325
4620
  /** For an ad-hoc decision: what was ambiguous. Required without `decisionId`. */
3326
- question: z.string().min(1).max(500).optional(),
4621
+ question: z2.string().min(1).max(500).optional(),
3327
4622
  /** The ruling. */
3328
- answer: z.string().min(1).max(2e3)
4623
+ answer: z2.string().min(1).max(2e3)
3329
4624
  }).refine((d) => d.decisionId || d.question, { message: "decisionId or question required" });
3330
- var AssignNoteSchema = z.object({
4625
+ var AssignNoteSchema = z2.object({
3331
4626
  /** An EXISTING agent: token id or nickname. Omit when spawning fresh. */
3332
- target: z.string().min(1).optional(),
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: z.object({
3337
- hostTokenId: z.string().uuid(),
3338
- harness: z.string().min(1),
3339
- workspace: z.string().min(1)
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 = z.enum(["keep", "stale", "close", "assign"]);
3343
- var TriageItemSchema = z.object({
3344
- noteId: z.string(),
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: z.string(),
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: z.string()
4645
+ why: z2.string()
3351
4646
  });
3352
- var TriageAssignmentSchema = z.object({
4647
+ var TriageAssignmentSchema = z2.object({
3353
4648
  /** The agent's token id — what `dispatchNote` resolves and what a request is addressed to. */
3354
- agent: z.string(),
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: z.string(),
3358
- notes: z.array(TriageItemSchema)
4652
+ agentName: z2.string(),
4653
+ notes: z2.array(TriageItemSchema)
3359
4654
  });
3360
- var TriageStatusSchema = z.enum(["open", "superseded", "dismissed"]);
3361
- var SubmitTriageSchema = z.object({
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: z.string().min(1).max(60),
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: z.string().max(200).nullable().optional(),
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: z.number().int().min(0).max(1e4).default(0),
3372
- close: z.array(TriageItemSchema).max(200).default([]),
3373
- stale: z.array(TriageItemSchema).max(200).default([]),
3374
- assign: z.array(TriageAssignmentSchema).max(50).default([])
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: z.string(),
3378
- runAt: z.string(),
4672
+ id: z2.string(),
4673
+ runAt: z2.string(),
3379
4674
  status: TriageStatusSchema,
3380
- model: z.string().nullable().default(null)
4675
+ model: z2.string().nullable().default(null)
3381
4676
  });
3382
- var AcceptTriageSchema = z.discriminatedUnion("group", [
3383
- z.object({ group: z.literal("close"), noteIds: z.array(z.string()).max(200).optional() }),
3384
- z.object({ group: z.literal("stale"), noteIds: z.array(z.string()).max(200).optional() }),
3385
- z.object({
3386
- group: z.literal("assign"),
3387
- agent: z.string().min(1),
3388
- noteIds: z.array(z.string()).max(200).optional()
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 = z.object({
3392
- accepted: z.array(z.string()),
3393
- failed: z.array(z.object({ noteId: z.string(), reason: z.string() }))
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 = z.enum(["poll", "self_hosted"]);
3396
- var RegisterDeliverySchema = z.object({ mode: DeliveryModeSchema });
3397
- var OAuthStartSchema = z.object({
3398
- provider: z.enum(["cma"]),
3399
- returnTo: z.string().min(1)
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 = z.object({
3402
- tokenId: z.string(),
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: z.object({ url: z.string(), anonKey: z.string() }).nullable()
4701
+ realtime: z2.object({ url: z2.string(), anonKey: z2.string() }).nullable()
3407
4702
  });
3408
- var StatusSchema = z.object({
3409
- name: z.string(),
3410
- sessionMode: z.enum(["default", "all_calls", "silent"]),
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: z.boolean()
4707
+ phone: z2.boolean()
3413
4708
  });
3414
- var EnvelopeRecipientSchema = z.object({
3415
- keyId: z.string(),
3416
- epk: z.string(),
3417
- wnonce: z.string(),
3418
- wrap: z.string()
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 = z.object({
3421
- field: z.enum(["context", "options", "visuals", "answer"]),
3422
- kind: z.string(),
3423
- senderRole: z.enum(["agent", "user"]),
3424
- recipientKeyIds: z.array(z.string()),
3425
- seq: z.number().int().nonnegative()
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 = z.object({
3428
- v: z.literal(1),
3429
- alg: z.literal("x25519-xsalsa20poly1305"),
3430
- msgId: z.string(),
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: z.array(EnvelopeRecipientSchema).min(1),
3433
- nonce: z.string(),
3434
- ct: z.string()
4727
+ recipients: z2.array(EnvelopeRecipientSchema).min(1),
4728
+ nonce: z2.string(),
4729
+ ct: z2.string()
3435
4730
  });
3436
- var SealedAnswerSchema = z.object({
3437
- ignored: z.boolean(),
4731
+ var SealedAnswerSchema = z2.object({
4732
+ ignored: z2.boolean(),
3438
4733
  envelope: EnvelopeSchema
3439
4734
  });
3440
- var DeviceCredentialSchema = z.object({
3441
- deviceId: z.string(),
3442
- kind: z.enum(["phone", "web", "agent"]),
3443
- x25519Pub: z.string(),
3444
- ed25519Pub: z.string(),
3445
- sig: z.string()
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 = z.object({
3448
- uikPub: z.string(),
3449
- devices: z.array(DeviceCredentialSchema)
4742
+ var DeviceRosterSchema = z2.object({
4743
+ uikPub: z2.string(),
4744
+ devices: z2.array(DeviceCredentialSchema)
3450
4745
  });
3451
- var WakeNudgeSchema = z.object({
3452
- kind: z.enum(["reply", "request", "callback"]),
3453
- notificationId: z.string().optional(),
3454
- parentId: z.string()
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 = z.enum(["pending", "approved", "denied", "expired"]);
3457
- var PairingRevealSchema = z.object({
3458
- x25519: z.string(),
3459
- ed25519: z.string(),
3460
- nonce: z.string()
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 = z.object({
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: z.string().optional(),
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: z.union([z.string(), z.object({ name: z.string().optional() })]).optional(),
3470
- device: z.string().optional(),
3471
- proto: z.string().optional(),
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: z.string().optional()
4768
+ commitment: z2.string().optional()
3474
4769
  // agent's Ca
3475
4770
  });
3476
- var DeviceCodeSchema = z.object({
3477
- device_code: z.string(),
3478
- user_code: z.string(),
3479
- verification_uri: z.string().url(),
3480
- verification_uri_complete: z.string().url(),
3481
- interval: z.number(),
3482
- expires_in: z.number()
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 = z.object({
3485
- code: z.string(),
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: z.string(),
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: z.string().optional(),
3494
- device: z.string().nullable(),
4788
+ agent: z2.string().optional(),
4789
+ device: z2.string().nullable(),
3495
4790
  status: PairingStatusSchema,
3496
- proto: z.string().nullable().optional(),
3497
- agent_commitment: z.string().nullable().optional(),
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: z.string().nullable().optional()
4802
+ uik_pub: z2.string().nullable().optional()
3508
4803
  });
3509
- var DeviceTokenRequestSchema = z.object({
3510
- device_code: z.string(),
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 = z.object({
3515
- access_token: z.string(),
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: z.string(),
3519
- device: z.string().nullable(),
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: z.string().nullable().optional(),
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: z.string().nullable().optional(),
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: z.string().nullable().optional(),
4825
+ workspace: z2.string().nullable().optional(),
3531
4826
  phone_reveal: PairingRevealSchema.nullable().optional(),
3532
4827
  // present once the phone reveals
3533
- uik_pub: z.string().nullable().optional()
4828
+ uik_pub: z2.string().nullable().optional()
3534
4829
  });
3535
- var DeviceCommitRequestSchema = z.object({
3536
- code: z.string(),
3537
- commitment: z.string()
4830
+ var DeviceCommitRequestSchema = z2.object({
4831
+ code: z2.string(),
4832
+ commitment: z2.string()
3538
4833
  // Cb
3539
4834
  });
3540
- var DevicePeerCommitSchema = z.object({
3541
- commitment: z.string().nullable()
4835
+ var DevicePeerCommitSchema = z2.object({
4836
+ commitment: z2.string().nullable()
3542
4837
  });
3543
- var SupportRequestSchema = z.object({
3544
- email: z.string().email().max(320),
3545
- message: z.string().trim().min(1).max(5e3),
3546
- name: z.string().trim().max(120).optional()
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 = z.enum([
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 = z.object({
3563
- notificationId: z.string(),
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: z.string().trim().max(2e3).optional()
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: z.ZodIssueCode.custom, path: ["note"], message: "note is required for 'other' feedback" });
4864
+ ctx.addIssue({ code: z2.ZodIssueCode.custom, path: ["note"], message: "note is required for 'other' feedback" });
3570
4865
  });
3571
- var FeedbackResolutionSchema = z.enum(["broker_fixed", "sent_to_agent"]);
3572
- var FeedbackOutcomeSchema = z.object({
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: z.string(),
3576
- childIds: z.array(z.string()).optional()
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(sessionId) {
3901
- const id = sessionId ?? process.env.PAIGY_SESSION_ID ?? process.env.CLAUDE_CODE_SESSION_ID ?? randomUUID2();
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
- e2ee ? { suggestedName, proto: e2ee.proto, commitment: e2ee.commitment } : { suggestedName }
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
- function decryptAnswer(reply, deps = {}) {
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 = readToken();
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 landIntents(intents) {
4205
- if (!intents?.length) return intents;
4206
- return intents.map((i) => {
4207
- if (i.kind !== "defer") return i;
4208
- const due = parseDue(i.detail);
4209
- return due !== null ? { ...i, dueInSeconds: due } : i;
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 checkReplies(opts = {}) {
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
- ...opts.replyAck ? { "x-paigy-await-ack": "1" } : {}
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
- const pending = await res.json();
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 = readToken();
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 = readToken();
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 sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
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 ensureAuthed2(res) {
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 = decryptAnswer2(enriched, deps);
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 ?? sleep2;
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 = ensureAuthed2(await reach(`${BACKEND_URL}/api/await/ack`, {
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 = ensureAuthed2(await reach(
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 awaitReply(notificationId, opts = {}) {
4470
- const intervalMs = opts.intervalMs ?? 5e3;
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 checkReplies2(opts = {}) {
4486
- const pending = await checkReplies({ ...opts, replyAck: true });
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 ensureAuthed3(res) {
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 = ensureAuthed3(await reach(
4509
- `${BACKEND_URL}/api/notify/${encodeURIComponent(notificationId)}/state`,
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(`set_task_state failed: ${res.status} ${await res.text()}`);
5790
+ throw new Error(`${operation} failed: ${res.status} ${await res.text()}`);
4522
5791
  }
4523
5792
  return await res.json();
4524
5793
  }
4525
- function overrideToken2(secret) {
4526
- overrideToken(secret);
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
- SetTaskStateSchema,
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
- awaitReply,
4568
- checkReplies2,
5846
+ decodeReply,
5847
+ ackReply,
5848
+ awaitReply2,
5849
+ checkReplies,
4569
5850
  setTaskState,
4570
- overrideToken2
5851
+ setWorkState
4571
5852
  };