@mnemom/mnemom 0.17.34 → 0.17.35

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.
Files changed (87) hide show
  1. package/dist/commands/agent-config.js +3 -0
  2. package/dist/commands/agent-goal.d.ts +8 -3
  3. package/dist/commands/agent-goal.js +22 -8
  4. package/dist/commands/agent-run.d.ts +10 -2
  5. package/dist/commands/agent-run.js +19 -6
  6. package/dist/commands/agent-slack.js +32 -0
  7. package/dist/commands/durable-inbox.d.ts +51 -0
  8. package/dist/commands/durable-inbox.js +134 -0
  9. package/dist/commands/durable-questions.d.ts +64 -0
  10. package/dist/commands/durable-questions.js +161 -0
  11. package/dist/durable/seat-proxy.d.ts +12 -1
  12. package/dist/durable/seat-proxy.js +55 -9
  13. package/dist/durable/seat-socket.d.ts +20 -2
  14. package/dist/durable/seat-socket.js +58 -0
  15. package/dist/durable/turn-chain.d.ts +8 -0
  16. package/dist/durable/turn-chain.js +25 -4
  17. package/dist/host/client.d.ts +41 -0
  18. package/dist/host/client.js +7 -0
  19. package/dist/host/control.d.ts +7 -0
  20. package/dist/host/control.js +15 -0
  21. package/dist/host/daemon.d.ts +20 -0
  22. package/dist/host/daemon.js +74 -1
  23. package/dist/host/inbox-delivery.d.ts +174 -0
  24. package/dist/host/inbox-delivery.js +453 -0
  25. package/dist/host/run.d.ts +18 -0
  26. package/dist/host/run.js +61 -0
  27. package/dist/host/s1-names.d.ts +2 -0
  28. package/dist/host/s1-names.js +2 -0
  29. package/dist/host/tmux.d.ts +7 -0
  30. package/dist/host/tmux.js +19 -0
  31. package/dist/index.js +59 -1
  32. package/dist/lib/api.d.ts +5 -0
  33. package/dist/lib/api.js +16 -0
  34. package/dist/lib/durable/api.d.ts +11 -0
  35. package/dist/lib/durable/api.js +28 -3
  36. package/dist/lib/durable/gate.d.ts +1 -1
  37. package/dist/lib/durable/gate.js +8 -1
  38. package/dist/lib/durable/live.d.ts +5 -0
  39. package/dist/lib/durable/live.js +19 -0
  40. package/dist/lib/experimental.js +3 -0
  41. package/dist/lib/goal-api.d.ts +11 -4
  42. package/dist/lib/goal-api.js +4 -3
  43. package/dist/lib/launcher-config.d.ts +5 -0
  44. package/dist/lib/launcher-config.js +17 -2
  45. package/dist/lib/prompt.d.ts +4 -2
  46. package/dist/lib/prompt.js +15 -5
  47. package/dist/lib/runner/job.d.ts +2 -2
  48. package/dist/lib/runner/job.js +16 -3
  49. package/dist/lib/runner/pr-body.d.ts +17 -0
  50. package/dist/lib/runner/pr-body.js +42 -0
  51. package/dist/lib/runner/proxy.d.ts +18 -3
  52. package/dist/lib/runner/proxy.js +24 -5
  53. package/dist/lib/scan/record.d.ts +60 -9
  54. package/dist/lib/scan/record.js +157 -39
  55. package/dist/lib/scan/report.d.ts +6 -8
  56. package/dist/lib/scan/report.js +21 -7
  57. package/dist/lib/scan/share.d.ts +5 -1
  58. package/dist/lib/scan/share.js +15 -5
  59. package/dist/lib/slack-durable.d.ts +123 -0
  60. package/dist/lib/slack-durable.js +237 -0
  61. package/dist/lib/slack-router.d.ts +7 -0
  62. package/dist/lib/slack-router.js +50 -4
  63. package/dist/vendor/durable-contract/index.js +992 -34
  64. package/dist/vendor/shared/durable-contract/admission.d.ts +81 -2
  65. package/dist/vendor/shared/durable-contract/admission.js +319 -2
  66. package/dist/vendor/shared/durable-contract/cli-surface.js +21 -0
  67. package/dist/vendor/shared/durable-contract/codes.d.ts +60 -0
  68. package/dist/vendor/shared/durable-contract/codes.js +22 -0
  69. package/dist/vendor/shared/durable-contract/events.d.ts +4 -2
  70. package/dist/vendor/shared/durable-contract/events.js +55 -1
  71. package/dist/vendor/shared/durable-contract/freeze-record.d.ts +14 -0
  72. package/dist/vendor/shared/durable-contract/inbox.d.ts +299 -0
  73. package/dist/vendor/shared/durable-contract/inbox.js +462 -0
  74. package/dist/vendor/shared/durable-contract/index.d.ts +2 -0
  75. package/dist/vendor/shared/durable-contract/index.js +2 -0
  76. package/dist/vendor/shared/durable-contract/question.d.ts +218 -0
  77. package/dist/vendor/shared/durable-contract/question.js +373 -0
  78. package/dist/vendor/shared/durable-contract/routes.d.ts +44 -2
  79. package/dist/vendor/shared/durable-contract/routes.js +43 -1
  80. package/dist/vendor/shared/durable-contract/turn.d.ts +15 -1
  81. package/dist/vendor/shared/durable-contract/turn.js +33 -1
  82. package/dist/vendor/shared/durable-contract/vectors.d.ts +45 -0
  83. package/dist/vendor/shared/durable-contract/vectors.js +49 -0
  84. package/package.json +1 -1
  85. package/plugins/integrity-manifest.json +6 -2
  86. package/plugins/mnemom/hooks/hooks.json +12 -0
  87. package/plugins/mnemom/hooks/seat-ask.mjs +221 -0
@@ -36,8 +36,8 @@ function bytesToHex(bytes) {
36
36
  for (const b of bytes) out += HEX[b];
37
37
  return out;
38
38
  }
39
- function utf8(text) {
40
- return new TextEncoder().encode(text);
39
+ function utf8(text2) {
40
+ return new TextEncoder().encode(text2);
41
41
  }
42
42
  async function sha256Hex(input) {
43
43
  const bytes = typeof input === "string" ? utf8(input) : input;
@@ -61,13 +61,13 @@ function base64urlEncode(bytes) {
61
61
  return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
62
62
  }
63
63
  var B64URL_RE = /^[A-Za-z0-9_-]*$/;
64
- function base64urlDecode(text) {
65
- if (!B64URL_RE.test(text) || text.length % 4 === 1) return null;
66
- const padded = text.replace(/-/g, "+").replace(/_/g, "/") + "===".slice(0, (4 - text.length % 4) % 4);
64
+ function base64urlDecode(text2) {
65
+ if (!B64URL_RE.test(text2) || text2.length % 4 === 1) return null;
66
+ const padded = text2.replace(/-/g, "+").replace(/_/g, "/") + "===".slice(0, (4 - text2.length % 4) % 4);
67
67
  const bin = atob(padded);
68
68
  const out = new Uint8Array(bin.length);
69
69
  for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
70
- return base64urlEncode(out) === text ? out : null;
70
+ return base64urlEncode(out) === text2 ? out : null;
71
71
  }
72
72
  var HEX64_RE = /^[0-9a-f]{64}$/;
73
73
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
@@ -367,7 +367,8 @@ var RESOLVER_SCHEMA = {
367
367
  },
368
368
  epoch: { type: "integer", nullable: false },
369
369
  fenced: { type: "boolean", nullable: false },
370
- agent_id: { type: "uuid", nullable: false },
370
+ // The opaque agents.id text, e.g. "mnm-<hex>" (ruling 33.29), never a uuid.
371
+ agent_id: { type: "string", nullable: false },
371
372
  home_cell: { type: "enum", nullable: false, values: CANONICAL_CELL_IDS },
372
373
  posture: {
373
374
  type: "enum",
@@ -434,6 +435,15 @@ var GUARD_STATE_OK = Object.freeze({ spend_usd: 0 });
434
435
  var GUARD_STATE_FAULT = Object.freeze({
435
436
  spend_usd: "unparseable"
436
437
  });
438
+ function guardStateV1Ok(guard) {
439
+ if (guard === null || guard.schema_version !== 1) return false;
440
+ return [guard.turns, guard.spend_usd].every(
441
+ (v) => typeof v === "number" && Number.isFinite(v) && v >= 0
442
+ );
443
+ }
444
+ function unattendedPoolEligible(d) {
445
+ return d.durable_mode === "locked" && d.pool === true && !d.fenced && d.lease_executor_id !== null && (d.effective_state === "running" || d.effective_state === "resuming") && d.sealed && Number.isSafeInteger(d.charter_version) && d.charter_version >= 1;
446
+ }
437
447
  function nofenceHonoured(durableAdmit) {
438
448
  return durableAdmit === NOFENCE_ADMIT_VALUE;
439
449
  }
@@ -480,6 +490,12 @@ function isUnchainedCountTokens(r) {
480
490
  return r.method === "POST" && isCountTokensPath(r.path) && !r.chain;
481
491
  }
482
492
  var RESOLVER_FAULT = "fault";
493
+ var INBOX_OPEN_MAX = 64;
494
+ var INBOX_SHA_RE = /^[0-9a-f]{64}$/;
495
+ function unattendedHomedInboxEligible(d, inboxSha2) {
496
+ const open = d.inbox_open;
497
+ return d.durable_mode === "locked" && d.pool !== true && !d.fenced && d.lease_executor_id !== null && (d.effective_state === "running" || d.effective_state === "resuming") && d.sealed && Number.isSafeInteger(d.charter_version) && d.charter_version >= 1 && d.host_unattended === true && typeof inboxSha2 === "string" && INBOX_SHA_RE.test(inboxSha2) && Array.isArray(open) && open.length <= INBOX_OPEN_MAX && open.includes(inboxSha2);
498
+ }
483
499
  function admit(input) {
484
500
  const fenceSource = nofenceHonoured(input.durableAdmit) ? "nofence" : "fresh";
485
501
  const posture = input.posture ?? "unattended";
@@ -519,6 +535,24 @@ function admit(input) {
519
535
  writes: false
520
536
  };
521
537
  if (posture === "unattended") {
538
+ if (unattendedPoolEligible(d) || unattendedHomedInboxEligible(d, input.inboxSha)) {
539
+ if (guardStateV1Ok(d.guard_state))
540
+ return {
541
+ kind: "admit",
542
+ step: "admit",
543
+ posture: "unattended",
544
+ guardFault: false,
545
+ fenceSource,
546
+ helloTransition: false
547
+ };
548
+ return {
549
+ kind: "refuse",
550
+ step: "posture",
551
+ code: "durable_guard_unavailable",
552
+ posture,
553
+ fenceSource
554
+ };
555
+ }
522
556
  return {
523
557
  kind: "refuse",
524
558
  step: "posture",
@@ -670,6 +704,120 @@ function admissionTruthTable() {
670
704
  }
671
705
  return out;
672
706
  }
707
+ var GUARD_STATE_V1_OK = Object.freeze({
708
+ schema_version: 1,
709
+ turns: 0,
710
+ spend_usd: 0
711
+ });
712
+ var UNATTENDED_POOL_VECTORS = Object.freeze([
713
+ { name: "all hold: admitted unattended", over: {}, nofence: false, outcome: "admit" },
714
+ {
715
+ name: "all hold, resuming: admitted, no hello",
716
+ over: { effective_state: "resuming", state: "resuming" },
717
+ nofence: false,
718
+ outcome: "admit"
719
+ },
720
+ {
721
+ name: "(b) pool absent",
722
+ over: { pool: void 0 },
723
+ nofence: false,
724
+ outcome: "durable_unattended_unavailable"
725
+ },
726
+ {
727
+ name: "(b) pool false",
728
+ over: { pool: false },
729
+ nofence: false,
730
+ outcome: "durable_unattended_unavailable"
731
+ },
732
+ {
733
+ name: "(c) fenced key under nofence",
734
+ over: { fenced: true },
735
+ nofence: true,
736
+ outcome: "durable_unattended_unavailable"
737
+ },
738
+ {
739
+ name: "(c) no lease executor",
740
+ over: { lease_executor_id: null },
741
+ nofence: false,
742
+ outcome: "durable_unattended_unavailable"
743
+ },
744
+ {
745
+ name: "(c) lease lapsed (detached)",
746
+ over: { effective_state: "detached" },
747
+ nofence: false,
748
+ outcome: "durable_unattended_unavailable"
749
+ },
750
+ {
751
+ name: "(d) not sealed",
752
+ over: { sealed: false },
753
+ nofence: false,
754
+ outcome: "durable_unattended_unavailable"
755
+ },
756
+ {
757
+ name: "(d) no charter version",
758
+ over: { charter_version: null },
759
+ nofence: false,
760
+ outcome: "durable_unattended_unavailable"
761
+ },
762
+ {
763
+ name: "(d) charter version 0",
764
+ over: { charter_version: 0 },
765
+ nofence: false,
766
+ outcome: "durable_unattended_unavailable"
767
+ },
768
+ {
769
+ name: "(d) charter version not an integer",
770
+ over: { charter_version: 1.5 },
771
+ nofence: false,
772
+ outcome: "durable_unattended_unavailable"
773
+ },
774
+ {
775
+ name: "(e) guard null",
776
+ over: { guard_state: null },
777
+ nofence: false,
778
+ outcome: "durable_guard_unavailable"
779
+ },
780
+ {
781
+ name: "(e) guard without schema_version",
782
+ over: { guard_state: { turns: 0, spend_usd: 0 } },
783
+ nofence: false,
784
+ outcome: "durable_guard_unavailable"
785
+ },
786
+ {
787
+ name: "(e) guard without turns",
788
+ over: { guard_state: { schema_version: 1, spend_usd: 0 } },
789
+ nofence: false,
790
+ outcome: "durable_guard_unavailable"
791
+ },
792
+ {
793
+ name: "(e) guard turns as a string",
794
+ over: { guard_state: { schema_version: 1, turns: "0", spend_usd: 0 } },
795
+ nofence: false,
796
+ outcome: "durable_guard_unavailable"
797
+ },
798
+ {
799
+ name: "attended on a pool session: unchanged",
800
+ over: { posture: "attended" },
801
+ nofence: false,
802
+ outcome: "admit"
803
+ }
804
+ ]);
805
+ function decideUnattendedPoolVector(v) {
806
+ const base = {
807
+ posture: "unattended",
808
+ pool: true,
809
+ charter_version: 1,
810
+ guard_state: { ...GUARD_STATE_V1_OK }
811
+ };
812
+ const resolver = { ...fixtureResolver(base), ...v.over };
813
+ if (v.over.pool === void 0 && "pool" in v.over) delete resolver.pool;
814
+ return admit({
815
+ resolver,
816
+ pinAgentId: FIXTURE_AGENT,
817
+ posture: resolver.posture,
818
+ durableAdmit: v.nofence ? NOFENCE_ADMIT_VALUE : void 0
819
+ });
820
+ }
673
821
  var ADMIT_SWEEP_VECTORS_PATH = "src/vectors/admit-sweep-vectors.json";
674
822
  var ADMIT_SWEEP_VALUES = Object.freeze({
675
823
  durable_mode: ["locked", "tracked", null],
@@ -789,8 +937,122 @@ function admitSweepText(sweep) {
789
937
  }
790
938
  }
791
939
  async function admitSweepDigest(sweep) {
792
- const { tuples, text } = admitSweepText(sweep);
793
- return { tuples, verdicts_sha256: await sha256Hex(text) };
940
+ const { tuples, text: text2 } = admitSweepText(sweep);
941
+ return { tuples, verdicts_sha256: await sha256Hex(text2) };
942
+ }
943
+ var INBOX_SHA_FIXTURE = "ab".repeat(32);
944
+ var HOMED_INBOX_VECTORS = Object.freeze([
945
+ { name: "all hold: admitted unattended", over: {}, nofence: false, outcome: "admit" },
946
+ {
947
+ name: "all hold, resuming: admitted, no hello",
948
+ over: { effective_state: "resuming", state: "resuming" },
949
+ nofence: false,
950
+ outcome: "admit"
951
+ },
952
+ {
953
+ name: "(a) a pool session takes 58, not 60: still admitted",
954
+ over: { pool: true },
955
+ nofence: false,
956
+ outcome: "admit"
957
+ },
958
+ {
959
+ name: "(b) fenced key under nofence",
960
+ over: { fenced: true },
961
+ nofence: true,
962
+ outcome: "durable_unattended_unavailable"
963
+ },
964
+ {
965
+ name: "(b) no lease executor",
966
+ over: { lease_executor_id: null },
967
+ nofence: false,
968
+ outcome: "durable_unattended_unavailable"
969
+ },
970
+ {
971
+ name: "(b) lease lapsed (detached)",
972
+ over: { effective_state: "detached" },
973
+ nofence: false,
974
+ outcome: "durable_unattended_unavailable"
975
+ },
976
+ {
977
+ name: "(c) not sealed",
978
+ over: { sealed: false },
979
+ nofence: false,
980
+ outcome: "durable_unattended_unavailable"
981
+ },
982
+ {
983
+ name: "(c) no charter version",
984
+ over: { charter_version: null },
985
+ nofence: false,
986
+ outcome: "durable_unattended_unavailable"
987
+ },
988
+ {
989
+ name: "(d) host policy or the owner's opt-out says no",
990
+ over: { host_unattended: false },
991
+ nofence: false,
992
+ outcome: "durable_unattended_unavailable"
993
+ },
994
+ {
995
+ name: "(e) no inbox key on the newest entry",
996
+ over: {},
997
+ inboxSha: null,
998
+ nofence: false,
999
+ outcome: "durable_unattended_unavailable"
1000
+ },
1001
+ {
1002
+ name: "(e) inbox sha not open (acked, dead or never delivered)",
1003
+ over: { inbox_open: ["cd".repeat(32)] },
1004
+ nofence: false,
1005
+ outcome: "durable_unattended_unavailable"
1006
+ },
1007
+ {
1008
+ name: "(e) inbox_open absent",
1009
+ over: { inbox_open: void 0 },
1010
+ nofence: false,
1011
+ outcome: "durable_unattended_unavailable"
1012
+ },
1013
+ {
1014
+ name: "(e) inbox sha malformed",
1015
+ over: { inbox_open: ["AB".repeat(32)] },
1016
+ inboxSha: "AB".repeat(32),
1017
+ nofence: false,
1018
+ outcome: "durable_unattended_unavailable"
1019
+ },
1020
+ {
1021
+ name: "(f) guard null",
1022
+ over: { guard_state: null },
1023
+ nofence: false,
1024
+ outcome: "durable_guard_unavailable"
1025
+ },
1026
+ {
1027
+ name: "(f) guard without schema_version",
1028
+ over: { guard_state: { turns: 0, spend_usd: 0 } },
1029
+ nofence: false,
1030
+ outcome: "durable_guard_unavailable"
1031
+ },
1032
+ {
1033
+ name: "attended on a homed session: unchanged",
1034
+ over: { posture: "attended" },
1035
+ inboxSha: null,
1036
+ nofence: false,
1037
+ outcome: "admit"
1038
+ }
1039
+ ]);
1040
+ function decideHomedInboxVector(v) {
1041
+ const base = {
1042
+ posture: "unattended",
1043
+ charter_version: 1,
1044
+ inbox_open: [INBOX_SHA_FIXTURE],
1045
+ guard_state: { ...GUARD_STATE_V1_OK }
1046
+ };
1047
+ const resolver = { ...fixtureResolver(base), ...v.over };
1048
+ if ("inbox_open" in v.over && v.over.inbox_open === void 0) delete resolver.inbox_open;
1049
+ return admit({
1050
+ resolver,
1051
+ pinAgentId: FIXTURE_AGENT,
1052
+ posture: resolver.posture,
1053
+ durableAdmit: v.nofence ? NOFENCE_ADMIT_VALUE : void 0,
1054
+ inboxSha: "inboxSha" in v ? v.inboxSha : INBOX_SHA_FIXTURE
1055
+ });
794
1056
  }
795
1057
 
796
1058
  // ../shared/durable-contract/src/codes.ts
@@ -952,7 +1214,29 @@ var DURABLE_CODES = {
952
1214
  * POST ROUTES.int_charter names an unknown session, a hash that is not one of its charter
953
1215
  * versions, or no charter at all (a malformed body). The gateway refuses admission on it.
954
1216
  */
955
- durable_charter_not_found: { status: 404, surfaces: ["api"], fields: [] }
1217
+ durable_charter_not_found: { status: 404, surfaces: ["api"], fields: [] },
1218
+ // ---- api inbox, S1 amendment 60 (signed off by Wassim 2026-11-02; inbox.ts INBOX_REJECT_CODES) ----
1219
+ /** An agent sender with no live allowlist row for the recipient agent, set by the session's owner. */
1220
+ durable_inbox_not_allowlisted: { status: 403, surfaces: ["api", "cli"], fields: [] },
1221
+ /** The session cannot take inbox work: reason not_locked, ended, unsealed or unattended_off (host_unattended is not true, so §60 (e) could never admit the turn). */
1222
+ durable_inbox_closed: { status: 409, surfaces: ["api", "cli"], fields: ["reason"] },
1223
+ /** INBOX_OPEN_CAP messages are already open (admitted or delivered). */
1224
+ durable_inbox_full: { status: 429, surfaces: ["api", "cli"], fields: ["open", "cap"] },
1225
+ /** A send, allowlist, take or ack body outside its closed shape or limits; field names the first bad key (null: not an object). */
1226
+ durable_inbox_body_invalid: { status: 422, surfaces: ["api", "cli"], fields: ["field"] },
1227
+ /** An ack of a message not delivered to this session at this epoch with this sha. */
1228
+ durable_inbox_not_open: { status: 409, surfaces: ["api"], fields: [] },
1229
+ /** A sender identity or allowlist change the api refuses; reason is one of inbox.ts INBOX_SENDER_INVALID_REASONS. */
1230
+ durable_inbox_sender_invalid: { status: 422, surfaces: ["api", "cli"], fields: ["reason"] },
1231
+ // ---- api questions, S1 amendment 61 (signed off by Wassim 2026-11-02; question.ts) ----
1232
+ /** An ask, poll, release or answer body outside its closed shape or limits; field names the first bad key. */
1233
+ durable_question_body_invalid: { status: 422, surfaces: ["api", "cli"], fields: ["field"] },
1234
+ /** No question with that approval_id on this session. */
1235
+ durable_question_not_found: { status: 404, surfaces: ["api", "cli"], fields: [] },
1236
+ /** The question is no longer open (answered, dismissed, expired); state names which. */
1237
+ durable_question_closed: { status: 409, surfaces: ["api", "cli"], fields: ["state"] },
1238
+ /** An ask while the session's open question is a different one (another question_sha); approval_id and state name it. Nothing is stored. */
1239
+ durable_question_busy: { status: 409, surfaces: ["api", "cli"], fields: ["approval_id", "state"] }
956
1240
  };
957
1241
  function isDurableCode(v) {
958
1242
  return typeof v === "string" && Object.prototype.hasOwnProperty.call(DURABLE_CODES, v);
@@ -1387,6 +1671,23 @@ var M1_CLI_SURFACE = Object.freeze([
1387
1671
  // BACKBONE C4 (line 147: buildDurableLaunchSpec serves 'sessions adopt'); options: C14 spec
1388
1672
  // (`sessions adopt --dry-run --json`, `sessions adopt --own-agent`); ruling 32.33.
1389
1673
  entry(["agent", "sessions", "adopt"], ["--dry-run", "--json", "--own-agent"]),
1674
+ // S1 amendment 61 (signed off by Wassim 2026-11-02): the owner's answer or dismissal of a locked
1675
+ // session's question (own_answer, via coding_agent).
1676
+ entry(["agent", "sessions", "answer"], ["--dismiss", "--json"]),
1677
+ // S1 amendment 61: the session's questions to its owner (own_questions).
1678
+ entry(["agent", "sessions", "questions"], ["--all", "--json"]),
1679
+ // S1 amendment 60 (signed off by Wassim 2026-11-02): the owner door into a locked session's inbox. `send`
1680
+ // posts own_inbox_send (owner credential, or an allowlisted agent's key named by --sender-key-env);
1681
+ // --agent-id/--conversation address a session the caller cannot list (an allowlisted agent).
1682
+ entry(
1683
+ ["agent", "sessions", "send"],
1684
+ ["--agent-id", "--conversation", "--idempotency-key", "--json", "--sender-key-env"]
1685
+ ),
1686
+ // S1 amendment 60: the owner-managed inbox sender allowlist (own_inbox_senders, _sender_add, _sender_remove).
1687
+ entry(["agent", "sessions", "senders"]),
1688
+ entry(["agent", "sessions", "senders", "add"], ["--json", "--label"]),
1689
+ entry(["agent", "sessions", "senders", "list"], ["--json"]),
1690
+ entry(["agent", "sessions", "senders", "remove"], ["--json"]),
1390
1691
  // fixlines.ts launch_spec_change_sole_unlock (`mnemom agent sessions unlock <name>`); C11, C16 and
1391
1692
  // C17 specs; no source names an option; ruling 32.33.
1392
1693
  entry(["agent", "sessions", "unlock"])
@@ -1403,6 +1704,13 @@ var M1_CLI_SURFACE_BUILDERS = Object.freeze({
1403
1704
  "agent lock",
1404
1705
  "agent pause",
1405
1706
  "agent resume",
1707
+ "agent sessions answer",
1708
+ "agent sessions questions",
1709
+ "agent sessions send",
1710
+ "agent sessions senders",
1711
+ "agent sessions senders add",
1712
+ "agent sessions senders list",
1713
+ "agent sessions senders remove",
1406
1714
  "agent sessions unlock"
1407
1715
  ]),
1408
1716
  C2: Object.freeze([
@@ -1775,8 +2083,8 @@ function substitute(template, params) {
1775
2083
  }
1776
2084
  function placeholdersOf(key) {
1777
2085
  const entry2 = FIX_LINES[key];
1778
- const text = entry2.lead + (isRuled(entry2) ? DONE_CLAUSE : "") + entry2.body;
1779
- return [...new Set([...text.matchAll(PLACEHOLDER_RE)].map((m) => m[1]))];
2086
+ const text2 = entry2.lead + (isRuled(entry2) ? DONE_CLAUSE : "") + entry2.body;
2087
+ return [...new Set([...text2.matchAll(PLACEHOLDER_RE)].map((m) => m[1]))];
1780
2088
  }
1781
2089
  function renderFixLine(key, ctx) {
1782
2090
  const entry2 = FIX_LINES[key];
@@ -1910,18 +2218,18 @@ var FORBIDDEN_FIX_TOKENS = ["--local", "host breakglass"];
1910
2218
  function lintFixCatalogue(catalogue = FIX_LINES) {
1911
2219
  const out = [];
1912
2220
  for (const [key, entry2] of Object.entries(catalogue)) {
1913
- const text = entry2.lead + entry2.body;
2221
+ const text2 = entry2.lead + entry2.body;
1914
2222
  for (const tok of FORBIDDEN_FIX_TOKENS) {
1915
- if (text.includes(tok)) out.push({ key, problem: `forbidden token ${tok}` });
2223
+ if (text2.includes(tok)) out.push({ key, problem: `forbidden token ${tok}` });
1916
2224
  }
1917
2225
  if (entry2.lead.includes(NEW_TOKEN))
1918
2226
  out.push({ key, problem: "--new in the lead escapes the rule" });
1919
- if (text.includes("mnemom agent done <name>"))
2227
+ if (text2.includes("mnemom agent done <name>"))
1920
2228
  out.push({ key, problem: "done clause restated; the rule adds it" });
1921
- if (text.includes("config set durable always") && key !== "durable_only_agent") {
2229
+ if (text2.includes("config set durable always") && key !== "durable_only_agent") {
1922
2230
  out.push({ key, problem: "launcher suffix restated; the rule adds it" });
1923
2231
  }
1924
- const commands = [...text.matchAll(/`([^`]+)`/g)].map((m) => m[1]);
2232
+ const commands = [...text2.matchAll(/`([^`]+)`/g)].map((m) => m[1]);
1925
2233
  if (entry2.kind === "command" && commands.length === 0)
1926
2234
  out.push({ key, problem: "command entry has no command" });
1927
2235
  if (entry2.kind === "informational" && commands.length > 0)
@@ -2422,7 +2730,8 @@ var DURABLE_EVENT_KINDS = [
2422
2730
  "session.ready",
2423
2731
  "session.waiting",
2424
2732
  "session.wait_cleared",
2425
- "executor.turn_continued"
2733
+ "executor.turn_continued",
2734
+ "inbox.acked"
2426
2735
  ];
2427
2736
  function isDurableEventKind(v) {
2428
2737
  return typeof v === "string" && DURABLE_EVENT_KINDS.includes(v);
@@ -2687,7 +2996,62 @@ var KIND_REFS = {
2687
2996
  "session.wait_cleared": obj({ wait_reason: ENUM(PARKED_ONLY_WAIT_REASONS) }),
2688
2997
  // ---- lead ruling 2026-10-28: the spawn that sent the continue; a separate fact, so
2689
2998
  // executor.spawned's refs stay exactly {spawn_id} ----
2690
- "executor.turn_continued": obj({ spawn_id: UUID })
2999
+ "executor.turn_continued": obj({ spawn_id: UUID }),
3000
+ // ---- S1 amendment 60 (signed off by Wassim 2026-11-02): the inbox. Content-free: ids, shas, enums ----
3001
+ "inbox.admitted": obj(
3002
+ {
3003
+ inbox_id: UUID,
3004
+ sha: S({ pattern: HEX64_PATTERN, maxLength: 64 }),
3005
+ sender_kind: ENUM(["owner", "agent"]),
3006
+ sender_agent_id: S({ pattern: "^[A-Za-z0-9_-]{1,64}$" }),
3007
+ in_reply_to: UUID
3008
+ },
3009
+ ["inbox_id", "sha", "sender_kind"],
3010
+ [
3011
+ { field: "sender_kind", equals: "agent", require: ["sender_agent_id"] },
3012
+ { field: "sender_kind", equals: "owner", forbid: ["sender_agent_id"] }
3013
+ ]
3014
+ ),
3015
+ "inbox.rejected": obj({
3016
+ reason: ENUM([
3017
+ "not_allowlisted",
3018
+ "not_locked",
3019
+ "ended",
3020
+ "unsealed",
3021
+ "inbox_full",
3022
+ "body_too_large",
3023
+ "body_invalid",
3024
+ "unattended_off"
3025
+ ]),
3026
+ sender_kind: ENUM(["owner", "agent"])
3027
+ }),
3028
+ "inbox.coalesced": obj({ inbox_id: UUID, sha: S({ pattern: HEX64_PATTERN, maxLength: 64 }) }),
3029
+ "inbox.delivered": obj({
3030
+ inbox_id: UUID,
3031
+ sha: S({ pattern: HEX64_PATTERN, maxLength: 64 }),
3032
+ attempt: INT(1)
3033
+ }),
3034
+ "inbox.acked": obj({ inbox_id: UUID, sha: S({ pattern: HEX64_PATTERN, maxLength: 64 }) }),
3035
+ "inbox.reverted": obj({
3036
+ inbox_id: UUID,
3037
+ sha: S({ pattern: HEX64_PATTERN, maxLength: 64 }),
3038
+ reason: ENUM(["unconfirmed", "refused", "undeliverable"])
3039
+ }),
3040
+ "inbox.dead": obj({
3041
+ inbox_id: UUID,
3042
+ sha: S({ pattern: HEX64_PATTERN, maxLength: 64 }),
3043
+ reason: ENUM(["expired", "delivery_limit", "session_ended"])
3044
+ }),
3045
+ // ---- S1 amendment 61 (signed off by Wassim 2026-11-02): questions. Content-free: ids, shas, enums ----
3046
+ "question.asked": obj({
3047
+ approval_id: UUID,
3048
+ question_sha: S({ pattern: HEX64_PATTERN, maxLength: 64 })
3049
+ }),
3050
+ "question.answered": obj({
3051
+ approval_id: UUID,
3052
+ outcome: ENUM(["answered", "dismissed"]),
3053
+ via: ENUM(["coding_agent", "slack", "webhook", "mcp", "device"])
3054
+ })
2691
3055
  };
2692
3056
  function refsSchemaOf(kind) {
2693
3057
  return KIND_REFS[kind] ?? EMPTY;
@@ -2723,10 +3087,10 @@ function validateRefs(kind, refs) {
2723
3087
  }
2724
3088
  function eventDetail(kind, refs) {
2725
3089
  const parts = Object.keys(refs).sort().map((k) => `${k}=${refs[k] === null ? "null" : String(refs[k])}`);
2726
- const text = [kind, ...parts].join(" ");
2727
- if (text.length > EVENT_DETAIL_MAX_CHARS)
3090
+ const text2 = [kind, ...parts].join(" ");
3091
+ if (text2.length > EVENT_DETAIL_MAX_CHARS)
2728
3092
  throw new RangeError(`detail over ${EVENT_DETAIL_MAX_CHARS} chars`);
2729
- return text;
3093
+ return text2;
2730
3094
  }
2731
3095
  var EVENT_DETAIL_RE = /^[a-z_]+\.[a-z_]+( [a-z_]+=[A-Za-z0-9_.:-]+)*$/;
2732
3096
  var idempotencyKeys = {
@@ -2760,9 +3124,9 @@ var EVENT_PREIMAGE_FIELDS = [
2760
3124
  "prev_hash"
2761
3125
  ];
2762
3126
  var TS_RE2 = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}:\d{2})(?:\.(\d{1,6}))?(Z|[+-]00(?::?00)?)$/;
2763
- function canonicalEventTs(ts) {
2764
- const m = TS_RE2.exec(ts);
2765
- if (!m) throw new RangeError(`not a UTC timestamp: ${ts}`);
3127
+ function canonicalEventTs(ts3) {
3128
+ const m = TS_RE2.exec(ts3);
3129
+ if (!m) throw new RangeError(`not a UTC timestamp: ${ts3}`);
2766
3130
  return `${m[1]}T${m[2]}.${(m[3] ?? "").padEnd(6, "0")}Z`;
2767
3131
  }
2768
3132
  function stampEventTs(epochMs) {
@@ -3314,6 +3678,20 @@ function candidatePosture(entries, schemaKnown = true) {
3314
3678
  const continuation = newest.origin === "task" && newest.source === "system" && newest.producer === ATTENDED_TASK_PRODUCER && !newest.inbox && entries.slice(0, -1).some(attendedHuman);
3315
3679
  return continuation ? "attended" : "unattended";
3316
3680
  }
3681
+ var sessionTaskContinuation = (e2) => e2.origin === "task" && e2.source === "system" && e2.producer === ATTENDED_TASK_PRODUCER && !e2.inbox;
3682
+ function inboxShaOf(entries) {
3683
+ const newest = entries[entries.length - 1];
3684
+ if (newest === void 0) return null;
3685
+ const own2 = (e2) => e2.inbox && typeof e2.inboxSha === "string" && TURN_HASH_RE.test(e2.inboxSha) ? e2.inboxSha : null;
3686
+ if (newest.inbox) return own2(newest);
3687
+ if (!sessionTaskContinuation(newest)) return null;
3688
+ for (let i = entries.length - 2; i >= 0; i--) {
3689
+ const e2 = entries[i];
3690
+ if (sessionTaskContinuation(e2)) continue;
3691
+ return own2(e2);
3692
+ }
3693
+ return null;
3694
+ }
3317
3695
  async function chainIdOf(rootPromptId) {
3318
3696
  if (typeof rootPromptId !== "string" || rootPromptId.length === 0)
3319
3697
  throw new RangeError("root promptId is required");
@@ -3976,8 +4354,8 @@ function parseLaunchSpec(raw) {
3976
4354
  }
3977
4355
  function hasRemoteControl(raw) {
3978
4356
  if (raw === null || typeof raw !== "object") return false;
3979
- const text = JSON.stringify(raw).toLowerCase();
3980
- return text.includes("remote_control") || text.includes("remote-control") || text.includes("remotecontrol");
4357
+ const text2 = JSON.stringify(raw).toLowerCase();
4358
+ return text2.includes("remote_control") || text2.includes("remote-control") || text2.includes("remotecontrol");
3981
4359
  }
3982
4360
  async function specHash(spec) {
3983
4361
  return sha256Hex(canonicalJson(spec));
@@ -4206,8 +4584,8 @@ function dekWrapInput(dek) {
4206
4584
  if (dek.length !== DEK_BYTES) throw new RangeError("DEK must be 32 bytes");
4207
4585
  return base64urlEncode(dek);
4208
4586
  }
4209
- function dekFromWrapInput(text) {
4210
- const b = base64urlDecode(text);
4587
+ function dekFromWrapInput(text2) {
4588
+ const b = base64urlDecode(text2);
4211
4589
  return b !== null && b.length === DEK_BYTES ? b : null;
4212
4590
  }
4213
4591
  function newDek(random = (n) => crypto.getRandomValues(new Uint8Array(n))) {
@@ -4235,8 +4613,8 @@ async function sealContract(dek, sessionId, charterVersion, contract, random = (
4235
4613
  );
4236
4614
  return `${ENVELOPE_VERSION}.${base64urlEncode(iv)}.${base64urlEncode(new Uint8Array(ct))}`;
4237
4615
  }
4238
- function parseEnvelope(text) {
4239
- const parts = text.split(".");
4616
+ function parseEnvelope(text2) {
4617
+ const parts = text2.split(".");
4240
4618
  if (parts.length !== 3 || parts[0] !== ENVELOPE_VERSION) return null;
4241
4619
  const iv = base64urlDecode(parts[1]);
4242
4620
  const ct = base64urlDecode(parts[2]);
@@ -4409,10 +4787,10 @@ var CEILINGS = [
4409
4787
  async function splitSealedContract(seal) {
4410
4788
  const known = SEALED_CONTRACT_FIELDS;
4411
4789
  if (Object.keys(seal).some((k) => !known.includes(k))) return null;
4412
- const text = {};
4790
+ const text2 = {};
4413
4791
  for (const k of ["statement", "requirements", "allowed_paths", "forbidden_paths", "goal_id", "work_card", "shape"])
4414
- if (seal[k] !== void 0) text[k] = seal[k];
4415
- const contract = parseContract(text);
4792
+ if (seal[k] !== void 0) text2[k] = seal[k];
4793
+ const contract = parseContract(text2);
4416
4794
  if (contract === null) return null;
4417
4795
  const commitments = {
4418
4796
  statement_hash: await statementHash(contract.statement),
@@ -4488,6 +4866,22 @@ var ROUTES = Object.freeze({
4488
4866
  exec_ready_list: fixed("GET", "executor_key", "/v1/executor/ready"),
4489
4867
  /** Body {oidc_token}; answers a one-lease org-scope executor key or an OIDC_EXCHANGE_REFUSALS refusal. */
4490
4868
  exec_oidc_exchange: fixed("POST", "github_oidc", "/v1/executor/oidc/exchange"),
4869
+ // ---- inbox, executor side (S1 amendment 60) ----
4870
+ /** Content-free summary {admitted, delivered, oldest_admitted_at} (inboxSummaryResponseOk); marks nothing. */
4871
+ exec_inbox: scoped("GET", "executor_key", EXEC_SESSION_PRE, "/inbox"),
4872
+ /** Body {epoch, nonce, limit?}: marks up to limit messages delivered at the live epoch, journals inbox.delivered, answers their delivery text. */
4873
+ exec_inbox_take: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/inbox/take"),
4874
+ /** Body {epoch, nonce, inbox_id, sha}: acks a message delivered at this epoch, journals inbox.acked. */
4875
+ exec_inbox_ack: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/inbox/ack"),
4876
+ /** Body {epoch, nonce, inbox_id, sha, reason}: hands a delivered message back undone (admitted again, or dead at the limit), journals inbox.reverted. */
4877
+ exec_inbox_release: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/inbox/release"),
4878
+ // ---- questions, executor side (S1 amendment 61) ----
4879
+ /** Body {epoch, nonce, tool_use_id, questions}: opens the session's question approval (or returns it when the open one has this question_sha), journals question.asked. */
4880
+ exec_question: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/questions"),
4881
+ /** Body {epoch, nonce, approval_id}: the question's state; an answer is handed over once (marked taken). */
4882
+ exec_question_poll: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/questions/poll"),
4883
+ /** Body {epoch, nonce, approval_id}: the seat stopped waiting; the answer (now or later) goes to the inbox. */
4884
+ exec_question_release: scoped("POST", "executor_key", EXEC_SESSION_PRE, "/questions/release"),
4491
4885
  // ---- owner session verbs (ruling 33.22) ----
4492
4886
  own_lock: scoped("POST", "owner", OWNER_SESSION_PRE, "/lock"),
4493
4887
  own_unlock: scoped("POST", "owner", OWNER_SESSION_PRE, "/unlock"),
@@ -4498,6 +4892,22 @@ var ROUTES = Object.freeze({
4498
4892
  own_amend: scoped("POST", "owner", OWNER_SESSION_PRE, "/amend"),
4499
4893
  own_fail: scoped("POST", "owner", OWNER_SESSION_PRE, "/fail"),
4500
4894
  own_events: scoped("GET", "owner", OWNER_SESSION_PRE, "/events"),
4895
+ // ---- inbox, owner side (S1 amendment 60) ----
4896
+ /** Body {text, idempotency_key} (inboxSendBodyOk); answer INBOX_SEND_RESPONSE or an INBOX_REJECT_CODES refusal. */
4897
+ own_inbox_send: scoped("POST", "inbox_sender", OWNER_SESSION_PRE, "/inbox"),
4898
+ /** {messages: [INBOX_LIST_ITEM, ...]}, newest first, content-free. */
4899
+ own_inbox_list: scoped("GET", "owner", OWNER_SESSION_PRE, "/inbox"),
4900
+ /** {recipient_agent_id, senders: [INBOX_SENDER_ITEM, ...]}: the session's agent's live allowlist. */
4901
+ own_inbox_senders: scoped("GET", "owner", OWNER_SESSION_PRE, "/inbox/senders"),
4902
+ /** Body {agent_id, label?}: allowlists one of the owner's own agents in the same org. */
4903
+ own_inbox_sender_add: scoped("POST", "owner", OWNER_SESSION_PRE, "/inbox/senders"),
4904
+ /** Body {agent_id}: revokes it (the row keeps revoked_at and revoked_by). */
4905
+ own_inbox_sender_remove: scoped("DELETE", "owner", OWNER_SESSION_PRE, "/inbox/senders"),
4906
+ // ---- questions, owner side (S1 amendment 61) ----
4907
+ /** {questions: [QUESTION_LIST_ITEM, ...]}, newest first; the owner reads the question text. */
4908
+ own_questions: scoped("GET", "owner", OWNER_SESSION_PRE, "/questions"),
4909
+ /** Body {approval_id, via, answers | dismiss: true}: answers or dismisses, journals question.answered. */
4910
+ own_answer: scoped("POST", "owner", OWNER_SESSION_PRE, "/questions/answer"),
4501
4911
  // ---- gateway internal ----
4502
4912
  int_events: fixed("POST", "internal", GATEWAY_EVENTS_ROUTE),
4503
4913
  int_arm: fixed("GET", "internal", ARM_ROUTE),
@@ -4624,6 +5034,399 @@ function gatewayEventsBodyOk(v) {
4624
5034
  return validateRefs(kind, v.refs ?? {});
4625
5035
  }
4626
5036
 
5037
+ // ../shared/durable-contract/src/inbox.ts
5038
+ var INBOX_BODY_MAX_BYTES = 16384;
5039
+ var INBOX_TTL_S = 7 * 24 * 60 * 60;
5040
+ var INBOX_DELIVERY_LIMIT = 5;
5041
+ var INBOX_TAKE_LIMIT_MAX = 8;
5042
+ var INBOX_LIST_LIMIT = 100;
5043
+ var INBOX_SENDERS_MAX = 32;
5044
+ var INBOX_OPEN_CAP = INBOX_OPEN_MAX;
5045
+ var INBOX_REOFFER_S = 15 * 60;
5046
+ var INBOX_IDEMPOTENCY_KEY_RE = /^[A-Za-z0-9_.:-]{1,200}$/;
5047
+ var INBOX_AGENT_ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
5048
+ var INBOX_SENDER_LABEL_RE = /^[ -Z\\^-~]{1,128}$/;
5049
+ var INBOX_STATUSES = ["admitted", "delivered", "acked", "dead"];
5050
+ var INBOX_SENDER_KINDS = ["owner", "agent"];
5051
+ var INBOX_REJECT_REASONS = [
5052
+ "not_allowlisted",
5053
+ "not_locked",
5054
+ "ended",
5055
+ "unsealed",
5056
+ "inbox_full",
5057
+ "body_too_large",
5058
+ "body_invalid",
5059
+ "unattended_off"
5060
+ ];
5061
+ var INBOX_DEAD_REASONS = ["expired", "delivery_limit", "session_ended"];
5062
+ var INBOX_RELEASE_REASONS = ["unconfirmed", "refused", "undeliverable"];
5063
+ var INBOX_SENDER_INVALID_REASONS = [
5064
+ "sender",
5065
+ "on_behalf_of",
5066
+ "agent_id",
5067
+ "label",
5068
+ "self",
5069
+ "not_found",
5070
+ "other_org",
5071
+ "not_owned",
5072
+ "too_many"
5073
+ ];
5074
+ var INBOX_REJECT_CODES = Object.freeze({
5075
+ not_allowlisted: { code: "durable_inbox_not_allowlisted", status: 403 },
5076
+ not_locked: { code: "durable_inbox_closed", status: 409 },
5077
+ ended: { code: "durable_inbox_closed", status: 409 },
5078
+ unsealed: { code: "durable_inbox_closed", status: 409 },
5079
+ inbox_full: { code: "durable_inbox_full", status: 429 },
5080
+ body_too_large: { code: "durable_inbox_body_invalid", status: 422 },
5081
+ body_invalid: { code: "durable_inbox_body_invalid", status: 422 },
5082
+ unattended_off: { code: "durable_inbox_closed", status: 409 }
5083
+ });
5084
+ var A60_EMITTED_KINDS = [
5085
+ "inbox.admitted",
5086
+ "inbox.rejected",
5087
+ "inbox.coalesced",
5088
+ "inbox.delivered",
5089
+ "inbox.acked",
5090
+ "inbox.reverted",
5091
+ "inbox.dead"
5092
+ ];
5093
+ function inboxShaPreimage(m) {
5094
+ return canonicalJson({
5095
+ v: 1,
5096
+ session_id: m.session_id,
5097
+ sender_kind: m.sender_kind,
5098
+ sender_agent_id: m.sender_agent_id ?? null,
5099
+ on_behalf_of: m.on_behalf_of ?? null,
5100
+ in_reply_to: m.in_reply_to ?? null,
5101
+ body: m.body
5102
+ });
5103
+ }
5104
+ async function inboxSha(m) {
5105
+ return sha256Hex(inboxShaPreimage(m));
5106
+ }
5107
+ var INBOX_BODY_CONTROL_RE = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/;
5108
+ function inboxBodyOk(body) {
5109
+ if (typeof body !== "string" || body.length === 0 || INBOX_BODY_CONTROL_RE.test(body))
5110
+ return false;
5111
+ return utf8(body).length <= INBOX_BODY_MAX_BYTES;
5112
+ }
5113
+ var INBOX_MARKER_RE = /^\[mnemom inbox ([0-9a-f]{64})\]/;
5114
+ function inboxDeliveryText(m) {
5115
+ if (!HEX64_RE.test(m.sha)) throw new RangeError("inbox sha must be 64 lowercase hex");
5116
+ let who = "the owner";
5117
+ if (m.sender_kind === "agent") {
5118
+ const label = m.sender_label ?? "";
5119
+ if (!INBOX_SENDER_LABEL_RE.test(label)) throw new RangeError("inbox sender label invalid");
5120
+ who = `agent ${label} on behalf of the owner`;
5121
+ }
5122
+ if (m.in_reply_to !== null && !UUID_RE.test(m.in_reply_to))
5123
+ throw new RangeError("in_reply_to must be a lowercase uuid");
5124
+ const reply = m.in_reply_to === null ? "" : ` (answer to question ${m.in_reply_to})`;
5125
+ return `[mnemom inbox ${m.sha}] from ${who}${reply}
5126
+
5127
+ ${m.body}`;
5128
+ }
5129
+ function inboxMarkerOf(text2) {
5130
+ const nl = text2.indexOf("\n");
5131
+ return INBOX_MARKER_RE.exec(nl < 0 ? text2 : text2.slice(0, nl))?.[1] ?? null;
5132
+ }
5133
+ var isObj2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
5134
+ var keysExactly2 = (o, keys) => Object.keys(o).length === keys.length && keys.every((k) => k in o);
5135
+ var keysWithin = (o, required, optional) => required.every((k) => k in o) && Object.keys(o).every((k) => required.includes(k) || optional.includes(k));
5136
+ var str = (v, re) => typeof v === "string" && re.test(v);
5137
+ var uuid2 = (v, nullable = false) => fieldOk({ type: "uuid", nullable }, v);
5138
+ var ts = (v, nullable = false) => fieldOk({ type: "timestamp", nullable }, v);
5139
+ var int = (v, min = 0) => Number.isSafeInteger(v) && v >= min;
5140
+ var oneOf = (v, values) => typeof v === "string" && values.includes(v);
5141
+ var agentId = (v) => str(v, INBOX_AGENT_ID_RE);
5142
+ var nonEmpty2 = (v) => typeof v === "string" && v.length > 0;
5143
+ var INBOX_SEND_BODY_KEYS = ["text", "idempotency_key"];
5144
+ function inboxSendBodyOk(v) {
5145
+ return isObj2(v) && keysExactly2(v, INBOX_SEND_BODY_KEYS) && inboxBodyOk(v.text) && str(v.idempotency_key, INBOX_IDEMPOTENCY_KEY_RE);
5146
+ }
5147
+ var INBOX_SEND_RESPONSE = Object.freeze({
5148
+ inbox_id: "uuid",
5149
+ sha: "hex64",
5150
+ status: "inbox_status",
5151
+ coalesced: "boolean",
5152
+ deduped: "boolean",
5153
+ sender_kind: "inbox_sender_kind",
5154
+ sender_agent_id: "agent_id|null",
5155
+ on_behalf_of: "string|null",
5156
+ expires_at: "timestamp"
5157
+ });
5158
+ var senderOk = (v) => oneOf(v.sender_kind, INBOX_SENDER_KINDS) && (v.sender_kind === "owner" ? v.sender_agent_id === null && v.on_behalf_of === null : agentId(v.sender_agent_id) && nonEmpty2(v.on_behalf_of));
5159
+ function inboxSendResponseOk(v) {
5160
+ return isObj2(v) && keysExactly2(v, Object.keys(INBOX_SEND_RESPONSE)) && uuid2(v.inbox_id) && str(v.sha, HEX64_RE) && oneOf(v.status, INBOX_STATUSES) && typeof v.coalesced === "boolean" && typeof v.deduped === "boolean" && senderOk(v) && ts(v.expires_at);
5161
+ }
5162
+ var INBOX_LIST_ITEM = Object.freeze({
5163
+ inbox_id: "uuid",
5164
+ sha: "hex64",
5165
+ status: "inbox_status",
5166
+ sender_kind: "inbox_sender_kind",
5167
+ sender_agent_id: "agent_id|null",
5168
+ on_behalf_of: "string|null",
5169
+ in_reply_to: "uuid|null",
5170
+ body_bytes: "integer>=1",
5171
+ delivery_count: "integer",
5172
+ dead_reason: "inbox_dead_reason|null",
5173
+ created_at: "timestamp",
5174
+ delivered_at: "timestamp|null",
5175
+ acked_at: "timestamp|null",
5176
+ expires_at: "timestamp"
5177
+ });
5178
+ function inboxListItemOk(v) {
5179
+ return isObj2(v) && keysExactly2(v, Object.keys(INBOX_LIST_ITEM)) && uuid2(v.inbox_id) && str(v.sha, HEX64_RE) && oneOf(v.status, INBOX_STATUSES) && senderOk(v) && uuid2(v.in_reply_to, true) && int(v.body_bytes, 1) && v.body_bytes <= INBOX_BODY_MAX_BYTES && int(v.delivery_count) && (v.status === "dead" ? oneOf(v.dead_reason, INBOX_DEAD_REASONS) : v.dead_reason === null) && ts(v.created_at) && ts(v.delivered_at, true) && ts(v.acked_at, true) && ts(v.expires_at);
5180
+ }
5181
+ function inboxListResponseOk(v) {
5182
+ return isObj2(v) && keysExactly2(v, ["messages"]) && Array.isArray(v.messages) && v.messages.length <= INBOX_LIST_LIMIT && v.messages.every(inboxListItemOk);
5183
+ }
5184
+ var INBOX_SENDER_ADD_BODY_KEYS = ["agent_id", "label"];
5185
+ function inboxSenderAddBodyOk(v) {
5186
+ return isObj2(v) && keysWithin(v, ["agent_id"], ["label"]) && str(v.agent_id, INBOX_AGENT_ID_RE) && (!("label" in v) || str(v.label, INBOX_SENDER_LABEL_RE));
5187
+ }
5188
+ var INBOX_SENDER_REMOVE_BODY_KEYS = ["agent_id"];
5189
+ function inboxSenderRemoveBodyOk(v) {
5190
+ return isObj2(v) && keysExactly2(v, INBOX_SENDER_REMOVE_BODY_KEYS) && str(v.agent_id, INBOX_AGENT_ID_RE);
5191
+ }
5192
+ var INBOX_SENDER_ITEM = Object.freeze({
5193
+ sender_agent_id: "agent_id",
5194
+ label: "label|null",
5195
+ added_by: "string|null",
5196
+ created_at: "timestamp"
5197
+ });
5198
+ function inboxSenderItemOk(v) {
5199
+ return isObj2(v) && keysExactly2(v, Object.keys(INBOX_SENDER_ITEM)) && agentId(v.sender_agent_id) && (v.label === null || str(v.label, INBOX_SENDER_LABEL_RE)) && (v.added_by === null || nonEmpty2(v.added_by)) && ts(v.created_at);
5200
+ }
5201
+ function inboxSendersResponseOk(v) {
5202
+ return isObj2(v) && keysExactly2(v, ["recipient_agent_id", "senders"]) && agentId(v.recipient_agent_id) && Array.isArray(v.senders) && v.senders.length <= INBOX_SENDERS_MAX && v.senders.every(inboxSenderItemOk);
5203
+ }
5204
+ function inboxSenderChangeResponseOk(v) {
5205
+ return isObj2(v) && keysExactly2(v, ["recipient_agent_id", "sender_agent_id", "changed"]) && agentId(v.recipient_agent_id) && agentId(v.sender_agent_id) && v.recipient_agent_id !== v.sender_agent_id && typeof v.changed === "boolean";
5206
+ }
5207
+ function inboxSummaryResponseOk(v) {
5208
+ return isObj2(v) && keysExactly2(v, ["admitted", "delivered", "oldest_admitted_at"]) && int(v.admitted) && int(v.delivered) && (v.admitted === 0 ? v.oldest_admitted_at === null : ts(v.oldest_admitted_at));
5209
+ }
5210
+ var INBOX_TAKE_BODY_KEYS = ["epoch", "nonce", "limit"];
5211
+ function inboxTakeBodyOk(v) {
5212
+ return isObj2(v) && keysWithin(v, ["epoch", "nonce"], ["limit"]) && int(v.epoch) && nonEmpty2(v.nonce) && (!("limit" in v) || int(v.limit, 1) && v.limit <= INBOX_TAKE_LIMIT_MAX);
5213
+ }
5214
+ var INBOX_TAKE_ITEM = Object.freeze({
5215
+ inbox_id: "uuid",
5216
+ sha: "hex64",
5217
+ text: "string",
5218
+ attempt: "integer>=1",
5219
+ sender_kind: "inbox_sender_kind",
5220
+ sender_agent_id: "agent_id|null",
5221
+ on_behalf_of: "string|null",
5222
+ in_reply_to: "uuid|null",
5223
+ created_at: "timestamp"
5224
+ });
5225
+ function inboxTakeItemOk(v) {
5226
+ return isObj2(v) && keysExactly2(v, Object.keys(INBOX_TAKE_ITEM)) && uuid2(v.inbox_id) && str(v.sha, HEX64_RE) && typeof v.text === "string" && inboxMarkerOf(v.text) === v.sha && int(v.attempt, 1) && v.attempt <= INBOX_DELIVERY_LIMIT && senderOk(v) && uuid2(v.in_reply_to, true) && ts(v.created_at);
5227
+ }
5228
+ function inboxTakeResponseOk(v) {
5229
+ return isObj2(v) && keysExactly2(v, ["messages"]) && Array.isArray(v.messages) && v.messages.length <= INBOX_TAKE_LIMIT_MAX && v.messages.every(inboxTakeItemOk);
5230
+ }
5231
+ var INBOX_ACK_BODY_KEYS = ["epoch", "nonce", "inbox_id", "sha"];
5232
+ function inboxAckBodyOk(v) {
5233
+ return isObj2(v) && keysExactly2(v, INBOX_ACK_BODY_KEYS) && int(v.epoch) && nonEmpty2(v.nonce) && uuid2(v.inbox_id) && str(v.sha, HEX64_RE);
5234
+ }
5235
+ function inboxAckResponseOk(v) {
5236
+ return isObj2(v) && keysExactly2(v, ["inbox_id", "sha", "status", "deduped"]) && uuid2(v.inbox_id) && str(v.sha, HEX64_RE) && v.status === "acked" && typeof v.deduped === "boolean";
5237
+ }
5238
+ var INBOX_RELEASE_BODY_KEYS = ["epoch", "nonce", "inbox_id", "sha", "reason"];
5239
+ function inboxReleaseBodyOk(v) {
5240
+ return isObj2(v) && keysExactly2(v, INBOX_RELEASE_BODY_KEYS) && int(v.epoch) && nonEmpty2(v.nonce) && uuid2(v.inbox_id) && str(v.sha, HEX64_RE) && oneOf(v.reason, INBOX_RELEASE_REASONS);
5241
+ }
5242
+ function inboxReleaseResponseOk(v) {
5243
+ return isObj2(v) && keysExactly2(v, ["inbox_id", "sha", "status"]) && uuid2(v.inbox_id) && str(v.sha, HEX64_RE) && (v.status === "admitted" || v.status === "dead");
5244
+ }
5245
+ var inboxIdempotencyKeys = {
5246
+ admitted: (inboxId) => `inbox:${inboxId}:admitted`,
5247
+ coalesced: (inboxId, idempotencyKey) => `inbox:${inboxId}:coalesced:${idempotencyKey}`,
5248
+ /**
5249
+ * A refused send: keyed on the caller's idempotency_key when it is well-formed, else on the api's
5250
+ * own request id (a body_invalid refusal may carry no usable key).
5251
+ */
5252
+ rejected: (sessionId, idempotencyKey, requestId) => typeof idempotencyKey === "string" && INBOX_IDEMPOTENCY_KEY_RE.test(idempotencyKey) ? `inbox:${sessionId}:rejected:${idempotencyKey}` : `inbox:${sessionId}:rejected:request:${requestId}`,
5253
+ delivered: (inboxId, epoch, attempt) => `inbox:${inboxId}:delivered:${epoch}:${attempt}`,
5254
+ acked: (inboxId) => `inbox:${inboxId}:acked`,
5255
+ reverted: (inboxId, epoch, attempt) => `inbox:${inboxId}:reverted:${epoch}:${attempt}`,
5256
+ dead: (inboxId) => `inbox:${inboxId}:dead`
5257
+ };
5258
+
5259
+ // ../shared/durable-contract/src/question.ts
5260
+ var QUESTION_MAX_QUESTIONS = 4;
5261
+ var QUESTION_MIN_OPTIONS = 2;
5262
+ var QUESTION_MAX_OPTIONS = 4;
5263
+ var QUESTION_TEXT_MAX_BYTES = 2048;
5264
+ var QUESTION_HEADER_MAX_BYTES = 64;
5265
+ var QUESTION_LABEL_MAX_BYTES = 256;
5266
+ var QUESTION_DESCRIPTION_MAX_BYTES = 2048;
5267
+ var QUESTION_ANSWER_MAX_BYTES = 4096;
5268
+ var QUESTION_BODY_MAX_BYTES = 16384;
5269
+ var QUESTION_APPROVAL_TTL_S = 24 * 60 * 60;
5270
+ var QUESTION_LIST_LIMIT = 20;
5271
+ var QUESTION_TOOL_USE_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
5272
+ var QUESTION_RECOMMENDED_SUFFIX = "(Recommended)";
5273
+ var QUESTION_ANSWER_VIAS = ["coding_agent", "slack", "webhook", "mcp", "device"];
5274
+ var QUESTION_OUTCOMES = ["answered", "dismissed"];
5275
+ var QUESTION_STATES = ["open", "answered", "dismissed", "expired", "handed_off"];
5276
+ var A61_EMITTED_KINDS = ["question.asked", "question.answered"];
5277
+ var QUESTION_HANDOFF_REASON = "This question is now with your owner. End your turn now without guessing; the owner's answer will arrive as your next message.";
5278
+ var QUESTION_DISMISSED_REASON = "Your owner dismissed this question without answering. Decide without it, or ask differently.";
5279
+ var isObj3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
5280
+ var keysExactly3 = (o, keys) => Object.keys(o).length === keys.length && keys.every((k) => k in o);
5281
+ var keysWithin2 = (o, required, optional) => required.every((k) => k in o) && Object.keys(o).every((k) => required.includes(k) || optional.includes(k));
5282
+ var text = (v, min, max) => typeof v === "string" && !v.includes("\0") && utf8(v).length >= min && utf8(v).length <= max;
5283
+ var uuid3 = (v, nullable = false) => fieldOk({ type: "uuid", nullable }, v);
5284
+ var ts2 = (v, nullable = false) => fieldOk({ type: "timestamp", nullable }, v);
5285
+ var int2 = (v, min = 0) => Number.isSafeInteger(v) && v >= min;
5286
+ var nonEmpty3 = (v) => typeof v === "string" && v.length > 0;
5287
+ var oneOf2 = (v, values) => typeof v === "string" && values.includes(v);
5288
+ function optionOk(v) {
5289
+ return isObj3(v) && keysExactly3(v, ["label", "description"]) && text(v.label, 1, QUESTION_LABEL_MAX_BYTES) && text(v.description, 0, QUESTION_DESCRIPTION_MAX_BYTES);
5290
+ }
5291
+ function questionItemOk(v) {
5292
+ if (!isObj3(v) || !keysExactly3(v, ["question", "header", "options", "multiSelect"]) || !text(v.question, 1, QUESTION_TEXT_MAX_BYTES) || !text(v.header, 1, QUESTION_HEADER_MAX_BYTES) || typeof v.multiSelect !== "boolean" || !Array.isArray(v.options) || v.options.length < QUESTION_MIN_OPTIONS || v.options.length > QUESTION_MAX_OPTIONS || !v.options.every(optionOk))
5293
+ return false;
5294
+ const labels = v.options.map((o) => o.label);
5295
+ return new Set(labels).size === labels.length;
5296
+ }
5297
+ function questionsOk(v) {
5298
+ if (!Array.isArray(v) || v.length < 1 || v.length > QUESTION_MAX_QUESTIONS) return false;
5299
+ if (!v.every(questionItemOk)) return false;
5300
+ const texts = v.map((q) => q.question);
5301
+ if (new Set(texts).size !== texts.length) return false;
5302
+ return utf8(canonicalJson(v)).length <= QUESTION_BODY_MAX_BYTES;
5303
+ }
5304
+ function normalizeQuestions(v) {
5305
+ if (!Array.isArray(v)) return null;
5306
+ const out = v.map((q) => {
5307
+ const r = isObj3(q) ? q : {};
5308
+ const opts = Array.isArray(r.options) ? r.options : [];
5309
+ return {
5310
+ question: r.question,
5311
+ header: r.header,
5312
+ options: opts.map((o) => {
5313
+ const x = isObj3(o) ? o : {};
5314
+ return { label: x.label, description: x.description ?? "" };
5315
+ }),
5316
+ multiSelect: r.multiSelect ?? false
5317
+ };
5318
+ });
5319
+ return questionsOk(out) ? out : null;
5320
+ }
5321
+ function recommendedOption(q) {
5322
+ const i = q.options.findIndex((o) => o.label.trimEnd().endsWith(QUESTION_RECOMMENDED_SUFFIX));
5323
+ return i < 0 ? null : i;
5324
+ }
5325
+ function questionShaPreimage(m) {
5326
+ return canonicalJson({
5327
+ v: 1,
5328
+ session_id: m.session_id,
5329
+ tool_use_id: m.tool_use_id,
5330
+ questions: m.questions
5331
+ });
5332
+ }
5333
+ async function questionSha(m) {
5334
+ return sha256Hex(questionShaPreimage(m));
5335
+ }
5336
+ function answersOk(answers, questions) {
5337
+ return isObj3(answers) && keysExactly3(
5338
+ answers,
5339
+ questions.map((q) => q.question)
5340
+ ) && Object.values(answers).every((a) => text(a, 1, QUESTION_ANSWER_MAX_BYTES));
5341
+ }
5342
+ function answersShapeOk(answers) {
5343
+ return isObj3(answers) && Object.keys(answers).length >= 1 && Object.keys(answers).length <= QUESTION_MAX_QUESTIONS && Object.keys(answers).every((k) => text(k, 1, QUESTION_TEXT_MAX_BYTES)) && Object.values(answers).every((a) => text(a, 1, QUESTION_ANSWER_MAX_BYTES));
5344
+ }
5345
+ function answerFromChoice(q, choice) {
5346
+ const c = choice.trim();
5347
+ const byNumber = (s) => {
5348
+ if (!/^[1-9][0-9]*$/.test(s)) return null;
5349
+ const o = q.options[Number(s) - 1];
5350
+ return o ? o.label : null;
5351
+ };
5352
+ const parts = q.multiSelect ? c.split(",").map((p) => p.trim()) : [c];
5353
+ if (parts.length > 0 && parts.every((p) => byNumber(p) !== null))
5354
+ return parts.map((p) => byNumber(p)).join(", ");
5355
+ const norm = (s) => s.trim().replace(QUESTION_RECOMMENDED_SUFFIX, "").trim().toLowerCase();
5356
+ const hit = q.options.find((o) => norm(o.label) === norm(c));
5357
+ return hit ? hit.label : c;
5358
+ }
5359
+ function questionAnswerInboxBody(questions, answers) {
5360
+ const lines = ["Your owner answered your question:"];
5361
+ for (const q of questions) {
5362
+ lines.push("", `Q: ${q.question}`, `A: ${answers[q.question] ?? ""}`);
5363
+ }
5364
+ return lines.join("\n");
5365
+ }
5366
+ var QUESTION_ASK_BODY_KEYS = ["epoch", "nonce", "tool_use_id", "questions"];
5367
+ function questionAskBodyOk(v) {
5368
+ return isObj3(v) && keysExactly3(v, QUESTION_ASK_BODY_KEYS) && int2(v.epoch) && nonEmpty3(v.nonce) && typeof v.tool_use_id === "string" && QUESTION_TOOL_USE_ID_RE.test(v.tool_use_id) && questionsOk(v.questions);
5369
+ }
5370
+ function questionAskResponseOk(v) {
5371
+ return isObj3(v) && keysExactly3(v, ["approval_id", "question_sha", "created", "expires_at"]) && uuid3(v.approval_id) && typeof v.question_sha === "string" && HEX64_RE.test(v.question_sha) && typeof v.created === "boolean" && ts2(v.expires_at);
5372
+ }
5373
+ async function questionAskMatches(answer, ask) {
5374
+ return answer.question_sha === await questionSha(ask);
5375
+ }
5376
+ var QUESTION_POLL_BODY_KEYS = ["epoch", "nonce", "approval_id"];
5377
+ function questionPollBodyOk(v) {
5378
+ return isObj3(v) && keysExactly3(v, QUESTION_POLL_BODY_KEYS) && int2(v.epoch) && nonEmpty3(v.nonce) && uuid3(v.approval_id);
5379
+ }
5380
+ function questionPollResponseOk(v) {
5381
+ return isObj3(v) && keysExactly3(v, ["approval_id", "state", "answers"]) && uuid3(v.approval_id) && oneOf2(v.state, QUESTION_STATES) && (v.state === "answered" ? answersShapeOk(v.answers) : v.answers === null);
5382
+ }
5383
+ function questionReleaseResponseOk(v) {
5384
+ return isObj3(v) && keysExactly3(v, ["approval_id", "state", "inbox_id"]) && uuid3(v.approval_id) && oneOf2(v.state, QUESTION_STATES) && uuid3(v.inbox_id, true);
5385
+ }
5386
+ var QUESTION_LIST_ITEM = Object.freeze({
5387
+ approval_id: "uuid",
5388
+ state: "question_state",
5389
+ questions: "questions",
5390
+ recommended: "(integer|null)[]",
5391
+ answers: "answers|null",
5392
+ answered_via: "question_answer_via|null",
5393
+ created_at: "timestamp",
5394
+ expires_at: "timestamp",
5395
+ answered_at: "timestamp|null"
5396
+ });
5397
+ function questionListItemOk(v) {
5398
+ if (!isObj3(v) || !keysExactly3(v, Object.keys(QUESTION_LIST_ITEM))) return false;
5399
+ if (!uuid3(v.approval_id) || !oneOf2(v.state, QUESTION_STATES) || !questionsOk(v.questions))
5400
+ return false;
5401
+ const qs = v.questions;
5402
+ if (!Array.isArray(v.recommended) || v.recommended.length !== qs.length || !v.recommended.every((r, i) => r === recommendedOption(qs[i])))
5403
+ return false;
5404
+ const answered = v.answers !== null;
5405
+ if (answered && !answersOk(v.answers, qs)) return false;
5406
+ if (answered !== (v.answered_via !== null)) return false;
5407
+ if (answered && !oneOf2(v.answered_via, QUESTION_ANSWER_VIAS)) return false;
5408
+ return ts2(v.created_at) && ts2(v.expires_at) && ts2(v.answered_at, true);
5409
+ }
5410
+ function questionListResponseOk(v) {
5411
+ return isObj3(v) && keysExactly3(v, ["questions"]) && Array.isArray(v.questions) && v.questions.length <= QUESTION_LIST_LIMIT && v.questions.every(questionListItemOk);
5412
+ }
5413
+ var QUESTION_ANSWER_BODY_KEYS = ["approval_id", "via", "answers", "dismiss"];
5414
+ function questionAnswerBodyOk(v) {
5415
+ if (!isObj3(v) || !keysWithin2(v, ["approval_id", "via"], ["answers", "dismiss"])) return false;
5416
+ if (!uuid3(v.approval_id) || !oneOf2(v.via, QUESTION_ANSWER_VIAS)) return false;
5417
+ if ("answers" in v === "dismiss" in v) return false;
5418
+ return "dismiss" in v ? v.dismiss === true : answersShapeOk(v.answers);
5419
+ }
5420
+ var QUESTION_DELIVERIES = ["live", "inbox", "none"];
5421
+ function questionAnswerResponseOk(v) {
5422
+ return isObj3(v) && keysExactly3(v, ["approval_id", "outcome", "delivery", "inbox_id"]) && uuid3(v.approval_id) && oneOf2(v.outcome, QUESTION_OUTCOMES) && oneOf2(v.delivery, QUESTION_DELIVERIES) && (v.delivery === "inbox" ? uuid3(v.inbox_id) : v.inbox_id === null) && (v.outcome === "dismissed" ? v.delivery === "none" : v.delivery !== "none");
5423
+ }
5424
+ var questionIdempotencyKeys = {
5425
+ asked: (approvalId) => `question:${approvalId}:asked`,
5426
+ answered: (approvalId) => `question:${approvalId}:answered`
5427
+ };
5428
+ var QUESTION_ID_RE = UUID_RE;
5429
+
4627
5430
  // ../shared/durable-contract/src/journal-approvals.ts
4628
5431
  var JOURNAL_APPROVAL_KINDS = ["approval.resolved", "approval.consumed"];
4629
5432
  var idOf = (e2) => e2.refs !== null && typeof e2.refs.approval_id === "string" ? e2.refs.approval_id : null;
@@ -5360,7 +6163,53 @@ var SEAL_VECTORS = {
5360
6163
  "hash": "5a3bb041c155d67e50d18ee9afb0e6903f7a64988c881ab6373d3447fd5906f5"
5361
6164
  }
5362
6165
  };
6166
+ var INBOX_SHA_VECTORS = [
6167
+ {
6168
+ identity: {
6169
+ session_id: "11111111-2222-4333-8444-555555555555",
6170
+ sender_kind: "owner",
6171
+ sender_agent_id: null,
6172
+ on_behalf_of: null,
6173
+ in_reply_to: null,
6174
+ body: "Please rebase PR 3220 and rerun the contract tests."
6175
+ },
6176
+ sender_label: null,
6177
+ preimage: '{"body":"Please rebase PR 3220 and rerun the contract tests.","in_reply_to":null,"on_behalf_of":null,"sender_agent_id":null,"sender_kind":"owner","session_id":"11111111-2222-4333-8444-555555555555","v":1}',
6178
+ sha: "87fc04e971ee9fde7c3db3536772f4a7402c30a911111eff9fb37cbd3927c72f",
6179
+ text: "[mnemom inbox 87fc04e971ee9fde7c3db3536772f4a7402c30a911111eff9fb37cbd3927c72f] from the owner\n\nPlease rebase PR 3220 and rerun the contract tests."
6180
+ },
6181
+ {
6182
+ identity: {
6183
+ session_id: "11111111-2222-4333-8444-555555555555",
6184
+ sender_kind: "agent",
6185
+ sender_agent_id: "mnm-chief01",
6186
+ on_behalf_of: "user_owner_1",
6187
+ in_reply_to: null,
6188
+ body: "Wassim asks: status on the inbox migration?"
6189
+ },
6190
+ sender_label: "chief",
6191
+ preimage: '{"body":"Wassim asks: status on the inbox migration?","in_reply_to":null,"on_behalf_of":"user_owner_1","sender_agent_id":"mnm-chief01","sender_kind":"agent","session_id":"11111111-2222-4333-8444-555555555555","v":1}',
6192
+ sha: "8a955bd0811770262fa0d400472472b9cd8bc1cff8a41abb2955a2c1582eea54",
6193
+ text: "[mnemom inbox 8a955bd0811770262fa0d400472472b9cd8bc1cff8a41abb2955a2c1582eea54] from agent chief on behalf of the owner\n\nWassim asks: status on the inbox migration?"
6194
+ },
6195
+ {
6196
+ identity: {
6197
+ session_id: "11111111-2222-4333-8444-555555555555",
6198
+ sender_kind: "owner",
6199
+ sender_agent_id: null,
6200
+ on_behalf_of: null,
6201
+ in_reply_to: "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee",
6202
+ body: "Yes, ship it as a draft. \u2014 W \u2713"
6203
+ },
6204
+ sender_label: null,
6205
+ preimage: '{"body":"Yes, ship it as a draft. \u2014 W \u2713","in_reply_to":"aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee","on_behalf_of":null,"sender_agent_id":null,"sender_kind":"owner","session_id":"11111111-2222-4333-8444-555555555555","v":1}',
6206
+ sha: "f79c83ca6fd83b8b918fe6464479c79ba4abea1ec30f43fd62b9b6d5950cc218",
6207
+ text: "[mnemom inbox f79c83ca6fd83b8b918fe6464479c79ba4abea1ec30f43fd62b9b6d5950cc218] from the owner (answer to question aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee)\n\nYes, ship it as a draft. \u2014 W \u2713"
6208
+ }
6209
+ ];
5363
6210
  export {
6211
+ A60_EMITTED_KINDS,
6212
+ A61_EMITTED_KINDS,
5364
6213
  ABSENT_FUNCTIONS_M1,
5365
6214
  ADMISSION_PRECEDENCE,
5366
6215
  ADMISSION_REQUEST_KINDS,
@@ -5521,18 +6370,52 @@ export {
5521
6370
  GUARD_LISTS,
5522
6371
  GUARD_STATE_FAULT,
5523
6372
  GUARD_STATE_OK,
6373
+ GUARD_STATE_V1_OK,
5524
6374
  HEARTBEAT_DIVISOR,
5525
6375
  HEARTBEAT_ENTRY_FIELDS,
5526
6376
  HEARTBEAT_REASONS,
5527
6377
  HEARTBEAT_REQUEST_KEYS,
5528
6378
  HEARTBEAT_RESULT_FIELDS,
5529
6379
  HEX64_RE,
6380
+ HOMED_INBOX_VECTORS,
5530
6381
  HOST_STATUS_JSON,
5531
6382
  HOST_STATUS_JSON_VECTORS,
5532
6383
  HOST_STATUS_SEAT,
5533
6384
  HOST_STATUS_SESSION,
5534
6385
  ID_RE,
6386
+ INBOX_ACK_BODY_KEYS,
6387
+ INBOX_AGENT_ID_RE,
6388
+ INBOX_BODY_MAX_BYTES,
6389
+ INBOX_DEAD_REASONS,
6390
+ INBOX_DELIVERY_LIMIT,
6391
+ INBOX_IDEMPOTENCY_KEY_RE,
5535
6392
  INBOX_KEY,
6393
+ INBOX_LIST_ITEM,
6394
+ INBOX_LIST_LIMIT,
6395
+ INBOX_MARKER_RE,
6396
+ INBOX_OPEN_CAP,
6397
+ INBOX_OPEN_MAX,
6398
+ INBOX_REJECT_CODES,
6399
+ INBOX_REJECT_REASONS,
6400
+ INBOX_RELEASE_BODY_KEYS,
6401
+ INBOX_RELEASE_REASONS,
6402
+ INBOX_REOFFER_S,
6403
+ INBOX_SENDERS_MAX,
6404
+ INBOX_SENDER_ADD_BODY_KEYS,
6405
+ INBOX_SENDER_INVALID_REASONS,
6406
+ INBOX_SENDER_ITEM,
6407
+ INBOX_SENDER_KINDS,
6408
+ INBOX_SENDER_LABEL_RE,
6409
+ INBOX_SENDER_REMOVE_BODY_KEYS,
6410
+ INBOX_SEND_BODY_KEYS,
6411
+ INBOX_SEND_RESPONSE,
6412
+ INBOX_SHA_FIXTURE,
6413
+ INBOX_SHA_VECTORS,
6414
+ INBOX_STATUSES,
6415
+ INBOX_TAKE_BODY_KEYS,
6416
+ INBOX_TAKE_ITEM,
6417
+ INBOX_TAKE_LIMIT_MAX,
6418
+ INBOX_TTL_S,
5536
6419
  INTERNAL_KEY_HEADER,
5537
6420
  IV_BYTES,
5538
6421
  JOURNAL_APPROVAL_KINDS,
@@ -5626,6 +6509,30 @@ export {
5626
6509
  PURE_FN_M1_PENDING,
5627
6510
  PURE_FN_PRODUCT_BASE,
5628
6511
  PURE_FN_S1_MODULE,
6512
+ QUESTION_ANSWER_BODY_KEYS,
6513
+ QUESTION_ANSWER_MAX_BYTES,
6514
+ QUESTION_ANSWER_VIAS,
6515
+ QUESTION_APPROVAL_TTL_S,
6516
+ QUESTION_ASK_BODY_KEYS,
6517
+ QUESTION_BODY_MAX_BYTES,
6518
+ QUESTION_DELIVERIES,
6519
+ QUESTION_DESCRIPTION_MAX_BYTES,
6520
+ QUESTION_DISMISSED_REASON,
6521
+ QUESTION_HANDOFF_REASON,
6522
+ QUESTION_HEADER_MAX_BYTES,
6523
+ QUESTION_ID_RE,
6524
+ QUESTION_LABEL_MAX_BYTES,
6525
+ QUESTION_LIST_ITEM,
6526
+ QUESTION_LIST_LIMIT,
6527
+ QUESTION_MAX_OPTIONS,
6528
+ QUESTION_MAX_QUESTIONS,
6529
+ QUESTION_MIN_OPTIONS,
6530
+ QUESTION_OUTCOMES,
6531
+ QUESTION_POLL_BODY_KEYS,
6532
+ QUESTION_RECOMMENDED_SUFFIX,
6533
+ QUESTION_STATES,
6534
+ QUESTION_TEXT_MAX_BYTES,
6535
+ QUESTION_TOOL_USE_ID_RE,
5629
6536
  READY_CAUSES,
5630
6537
  READY_SESSIONS_LIMIT_MAX,
5631
6538
  READY_SESSIONS_LIMIT_MIN,
@@ -5733,6 +6640,7 @@ export {
5733
6640
  U05_CHECK_TEXT_ATTESTATION_FAILED,
5734
6641
  U05_CHECK_TEXT_RESPAWN_FAILED,
5735
6642
  U05_FIXTURE_CHECK,
6643
+ UNATTENDED_POOL_VECTORS,
5736
6644
  UNSUPPORTED_FEATURES,
5737
6645
  USER_STATES,
5738
6646
  UUID_RE,
@@ -5744,6 +6652,9 @@ export {
5744
6652
  admitSweepDigest,
5745
6653
  admitSweepText,
5746
6654
  amendDecision,
6655
+ answerFromChoice,
6656
+ answersOk,
6657
+ answersShapeOk,
5747
6658
  applyDeviceStep,
5748
6659
  approvalSubjectHash,
5749
6660
  approvalTargetOk,
@@ -5784,6 +6695,8 @@ export {
5784
6695
  contractPlaintext,
5785
6696
  countJournalApprovals,
5786
6697
  createReadySessionAnswerOk,
6698
+ decideHomedInboxVector,
6699
+ decideUnattendedPoolVector,
5787
6700
  dekFromWrapInput,
5788
6701
  dekWrapInput,
5789
6702
  deriveEffectiveState,
@@ -5813,6 +6726,7 @@ export {
5813
6726
  gatewayEventsBodyOk,
5814
6727
  grantWarningDays,
5815
6728
  guardStateOk,
6729
+ guardStateV1Ok,
5816
6730
  hasRemoteControl,
5817
6731
  heartbeatRequestOk,
5818
6732
  heartbeatResultOk,
@@ -5820,6 +6734,30 @@ export {
5820
6734
  hmacSha256Hex,
5821
6735
  hostStatusJsonOk,
5822
6736
  idempotencyKeys,
6737
+ inboxAckBodyOk,
6738
+ inboxAckResponseOk,
6739
+ inboxBodyOk,
6740
+ inboxDeliveryText,
6741
+ inboxIdempotencyKeys,
6742
+ inboxListItemOk,
6743
+ inboxListResponseOk,
6744
+ inboxMarkerOf,
6745
+ inboxReleaseBodyOk,
6746
+ inboxReleaseResponseOk,
6747
+ inboxSendBodyOk,
6748
+ inboxSendResponseOk,
6749
+ inboxSenderAddBodyOk,
6750
+ inboxSenderChangeResponseOk,
6751
+ inboxSenderItemOk,
6752
+ inboxSenderRemoveBodyOk,
6753
+ inboxSendersResponseOk,
6754
+ inboxSha,
6755
+ inboxShaOf,
6756
+ inboxShaPreimage,
6757
+ inboxSummaryResponseOk,
6758
+ inboxTakeBodyOk,
6759
+ inboxTakeItemOk,
6760
+ inboxTakeResponseOk,
5823
6761
  invalidTransitionFix,
5824
6762
  isCountTokensPath,
5825
6763
  isDurableCode,
@@ -5848,6 +6786,7 @@ export {
5848
6786
  newDek,
5849
6787
  nofenceHonoured,
5850
6788
  normalizeCellId,
6789
+ normalizeQuestions,
5851
6790
  normalizeSessionName,
5852
6791
  openContract,
5853
6792
  ownerSessionViewOk,
@@ -5873,7 +6812,24 @@ export {
5873
6812
  protectionRefusals,
5874
6813
  proxyLockPath,
5875
6814
  pureFnArgs,
6815
+ questionAnswerBodyOk,
6816
+ questionAnswerInboxBody,
6817
+ questionAnswerResponseOk,
6818
+ questionAskBodyOk,
6819
+ questionAskMatches,
6820
+ questionAskResponseOk,
6821
+ questionIdempotencyKeys,
6822
+ questionItemOk,
6823
+ questionListItemOk,
6824
+ questionListResponseOk,
6825
+ questionPollBodyOk,
6826
+ questionPollResponseOk,
6827
+ questionReleaseResponseOk,
6828
+ questionSha,
6829
+ questionShaPreimage,
6830
+ questionsOk,
5876
6831
  readyRowOk,
6832
+ recommendedOption,
5877
6833
  recordValueIn,
5878
6834
  refsInvalidDetailsOk,
5879
6835
  refsPropertyOk,
@@ -5913,6 +6869,8 @@ export {
5913
6869
  testModeActive,
5914
6870
  toolClassesSupportedM1,
5915
6871
  transcriptGuardPath,
6872
+ unattendedHomedInboxEligible,
6873
+ unattendedPoolEligible,
5916
6874
  userStateOf,
5917
6875
  utf8,
5918
6876
  validateRefs,