@corca-org/ceal-host-linux-arm64 1.0.18 → 1.0.19

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.
@@ -153,6 +153,9 @@ var client_wire_contract_default = {
153
153
  "document"
154
154
  ],
155
155
  optional_fields: [
156
+ "operation_id",
157
+ "visible_output_max_bytes",
158
+ "local_delivery",
156
159
  "verified_artifact"
157
160
  ],
158
161
  bare_document_fields: [
@@ -266,6 +269,7 @@ var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verifie
266
269
  var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
267
270
  var CEAL_PROTOCOL_VERSION = "1.4.0";
268
271
  var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
272
+ var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
269
273
  var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
270
274
  "audited_call_pending",
271
275
  "continuation_read_pending",
@@ -289,6 +293,7 @@ var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
289
293
  // packages/ceal-client-protocol/src/operation-next-actions.ts
290
294
  var CEAL_REPEAT_REQUEST_NEXT_ACTION = "repeat_request";
291
295
  var CEAL_INSPECT_RECEIPT_NEXT_ACTION = "inspect_receipt";
296
+ var CEAL_ANSWER_WITH_GAP_NEXT_ACTION = "answer_with_gap";
292
297
  var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
293
298
  "select_did_you_mean",
294
299
  "choose_new_output_path",
@@ -342,10 +347,20 @@ var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
342
347
  "retry_after",
343
348
  // The ref names a container of rows (a Notion database or data source), which
344
349
  // has no body of its own; the caller searches its rows instead of reading it.
345
- "search_container_rows"
350
+ "search_container_rows",
351
+ // A settled unknown write names the exact receipt Operation that can inspect
352
+ // its outcome without issuing another provider mutation.
353
+ "write_receipt.get",
354
+ // Command resolution carries the exact required Operation sequence beside
355
+ // this action, so the caller can execute each declared input in order.
356
+ "follow_required_action_sequence",
357
+ // The command needs caller-owned local materialization (for example an
358
+ // @path file) before the Gateway can receive the argument.
359
+ "materialize_local_argument"
346
360
  ];
347
361
  var CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS = Object.freeze(CALLER_RECOVERABLE_NEXT_ACTION_VALUES);
348
362
  var NON_REMEDY_NEXT_ACTION_VALUES = [
363
+ CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
349
364
  // Only an administrator can take the named step -- approving a place for an
350
365
  // Operation, granting a withheld capability -- so a non-administrator asks
351
366
  // one. The refusal names the administrator step; this word is the ask.
@@ -393,7 +408,58 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
393
408
  "select_exact_approval"
394
409
  ];
395
410
  var CEAL_NON_REMEDY_NEXT_ACTIONS = Object.freeze(NON_REMEDY_NEXT_ACTION_VALUES);
396
- var DECLARED = /* @__PURE__ */ new Set([...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS, ...CEAL_NON_REMEDY_NEXT_ACTIONS]);
411
+ var CEAL_DECLARED_NEXT_ACTIONS = Object.freeze([...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS, ...CEAL_NON_REMEDY_NEXT_ACTIONS]);
412
+ var DECLARED = new Set(CEAL_DECLARED_NEXT_ACTIONS);
413
+ function defineCealNonModelNextActions(actions) {
414
+ return actions;
415
+ }
416
+ var NON_MODEL_NEXT_ACTION_VALUES = defineCealNonModelNextActions([
417
+ CEAL_INSPECT_RECEIPT_NEXT_ACTION,
418
+ "await_pending_approval",
419
+ "inspect_gateway",
420
+ "operator_action",
421
+ "none",
422
+ "check_gateway",
423
+ "inspect_context_store",
424
+ "inspect_local_cache",
425
+ "inspect_local_client",
426
+ "inspect_local_command",
427
+ "inspect_local_output",
428
+ "inspect_local_route",
429
+ "inspect_request",
430
+ "reconcile",
431
+ "reconcile_occurrence",
432
+ "retry_after_owner_recovery",
433
+ "select_exact_approval"
434
+ ]);
435
+ var NON_MODEL_NEXT_ACTIONS = new Set(NON_MODEL_NEXT_ACTION_VALUES);
436
+ function isCealModelActionableNonRemedyNextAction(action) {
437
+ return !NON_MODEL_NEXT_ACTIONS.has(action);
438
+ }
439
+ var CEAL_MODEL_ACTIONABLE_NEXT_ACTIONS = Object.freeze([
440
+ ...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS,
441
+ ...CEAL_NON_REMEDY_NEXT_ACTIONS.filter(isCealModelActionableNonRemedyNextAction)
442
+ ]);
443
+ var MODEL_ACTIONABLE = new Set(CEAL_MODEL_ACTIONABLE_NEXT_ACTIONS);
444
+ var NON_MODEL_ACTION_TO_MODEL_ACTION = Object.freeze({
445
+ [CEAL_INSPECT_RECEIPT_NEXT_ACTION]: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
446
+ await_pending_approval: "ask_admin",
447
+ inspect_gateway: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
448
+ operator_action: "ask_admin",
449
+ none: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
450
+ check_gateway: "ask_admin",
451
+ inspect_context_store: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
452
+ inspect_local_cache: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
453
+ inspect_local_client: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
454
+ inspect_local_command: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
455
+ inspect_local_output: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
456
+ inspect_local_route: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
457
+ inspect_request: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
458
+ reconcile: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
459
+ reconcile_occurrence: "ask_admin",
460
+ retry_after_owner_recovery: "ask_admin",
461
+ select_exact_approval: "ask_admin"
462
+ });
397
463
  var RETRY_PROMISE_NEXT_ACTIONS = ["retry", "retry_after"];
398
464
  var RETRY_PROMISE = new Set(RETRY_PROMISE_NEXT_ACTIONS);
399
465
  var RECOVERABLE = new Set(CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS);
@@ -663,21 +729,15 @@ var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
663
729
  // packages/ceal-client-protocol/src/domain-vocabulary.ts
664
730
  var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
665
731
  var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
666
- var CEAL_PROVIDER_BOUNDARY_KINDS = vocabulary([
667
- "github_account",
668
- "github_organization",
669
- "google_workspace_domain",
670
- "notion_workspace",
671
- "slack_workspace"
672
- ]);
673
- var CEAL_PROVIDER_BOUNDARY_EVIDENCE = vocabulary(["provider_readback", "operator_stated"]);
674
732
  var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
675
733
  var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
676
734
  var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
677
735
  var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
678
736
  var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
679
- var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
737
+ var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
738
+ var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
680
739
  var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
740
+ var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
681
741
  var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
682
742
 
683
743
  // packages/ceal-client-protocol/src/safe-json-budget.ts
@@ -853,7 +913,7 @@ var CEAL_SCHEMA_LITERALS = [
853
913
  "ceal.operation_receipt.v1",
854
914
  "ceal.operation_result.v1",
855
915
  "ceal.operation_result.compact.v1",
856
- "ceal.local_result_materialization.v1",
916
+ "ceal.local_result_file.v1",
857
917
  "ceal.local_result_root.v1",
858
918
  "ceal.owner_bootstrap_exchange_request.v1",
859
919
  "ceal.owner_bootstrap_exchange_result.v1",
@@ -947,41 +1007,6 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
947
1007
  }
948
1008
  });
949
1009
 
950
- // packages/ceal-client-protocol/src/provider-operation-vocabulary.ts
951
- var CEAL_FILE_TYPE_FAMILIES = vocabulary(["all", "image", "pdf"]);
952
- var CEAL_GITHUB_ISSUE_STATES = vocabulary(["open", "closed", "all"]);
953
- var CEAL_NOTION_UPDATABLE_BLOCK_TYPES = vocabulary([
954
- "paragraph",
955
- "heading_1",
956
- "heading_2",
957
- "heading_3",
958
- "bulleted_list_item",
959
- "numbered_list_item",
960
- "to_do",
961
- "code"
962
- ]);
963
- var CEAL_NOTION_NEUTRAL_TYPES = vocabulary([
964
- "text",
965
- "number",
966
- "boolean",
967
- "date",
968
- "select",
969
- "multi_select",
970
- "people",
971
- "url",
972
- "email",
973
- "phone",
974
- "unsupported"
975
- ]);
976
- var CEAL_NOTION_IDENTITY_GAP_REASONS = vocabulary([
977
- "identity_unavailable",
978
- "identity_stale",
979
- "identity_ambiguous",
980
- "identity_revoked",
981
- "identity_unlinked"
982
- ]);
983
- var CEAL_NOTION_STRING_VALUE_TYPES = vocabulary(["text", "select", "url", "email", "phone"]);
984
-
985
1010
  // packages/ceal-client-protocol/src/opaque-reference.ts
986
1011
  var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "block", "object"]);
987
1012
  var CEAL_OPAQUE_REF_KINDS = vocabulary([...CEAL_OPAQUE_HANDLE_KINDS, "result", "operation-receipt", "reply-receipt", "field", "notion-data-source"]);
@@ -1014,6 +1039,7 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
1014
1039
  "resource_modified"
1015
1040
  ]);
1016
1041
  var CEAL_ACTIVITY_CONNECTOR_KINDS = vocabulary(["slack", "github", "google-calendar", "google-drive", "notion"]);
1042
+ var CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE = "unsupported_event_kind";
1017
1043
  var CEAL_ACTIVITY_GAP_CODES = vocabulary([
1018
1044
  "provider_page_budget",
1019
1045
  "call_deadline",
@@ -1021,7 +1047,7 @@ var CEAL_ACTIVITY_GAP_CODES = vocabulary([
1021
1047
  "provider_cursor_unavailable",
1022
1048
  "malformed_provider_page",
1023
1049
  "visibility_incomplete",
1024
- "unsupported_event_kind",
1050
+ CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE,
1025
1051
  "direct_messages_excluded"
1026
1052
  ]);
1027
1053
  var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
@@ -1086,6 +1112,8 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1086
1112
  modified_at: { required: false, timestamp: true },
1087
1113
  modified_at_local: { required: false, timestamp: true },
1088
1114
  viewed_by_me_at: { required: false, timestamp: true },
1115
+ own_ceal_write: { required: false, timestamp: false },
1116
+ owned_by_me: { required: false, timestamp: false },
1089
1117
  visibility: { required: false, timestamp: false },
1090
1118
  start: { required: false, timestamp: false },
1091
1119
  end: { required: false, timestamp: false },
@@ -1119,6 +1147,15 @@ function cealExcludedTargetCoverageJsonSchema() {
1119
1147
  };
1120
1148
  }
1121
1149
 
1150
+ // packages/ceal-client-protocol/src/gateway-write-identity.ts
1151
+ var CEAL_GATEWAY_WRITE_IDENTITY_ROLES = Object.freeze(["replay_identity", "lookup_handle", "collision_evidence"]);
1152
+ var CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN = /^gateway-write-request:[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/u;
1153
+ var CEAL_GATEWAY_WRITE_IDENTITY_FIELDS = Object.freeze({
1154
+ replay_identity: "idempotency_claim_sha256",
1155
+ lookup_handle: "write_request_sha256",
1156
+ collision_evidence: "normalized_mutation_sha256"
1157
+ });
1158
+
1122
1159
  // packages/ceal-client-protocol/src/write-safety-finding.ts
1123
1160
  var CEAL_WRITE_SAFETY_FINDING_STATUSES = vocabulary(["warning", "unchecked"]);
1124
1161
  var CEAL_WRITE_SAFETY_AUDIENCES = vocabulary(["workspace", "person", "members", "unknown"]);
@@ -1146,7 +1183,11 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
1146
1183
  "refresh_revoked",
1147
1184
  "authority_replaced",
1148
1185
  "refresh_recovery_unavailable",
1149
- "refresh_recovery_expired"
1186
+ "refresh_recovery_expired",
1187
+ // The serving gate answers this while the Gateway is still starting and the
1188
+ // refresh was never attempted; the same attempt stays retryable. It is never
1189
+ // stored by the session owner, only answered at the serving edge.
1190
+ "gateway_starting"
1150
1191
  ]);
1151
1192
 
1152
1193
  // packages/ceal-client-protocol/src/request-validation-primitives.ts
@@ -1179,7 +1220,7 @@ var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
1179
1220
  var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority", "prompt_ref", "host_session_ref", "host_turn_ref", "host_source_ref"]);
1180
1221
  var CEAL_REQUEST_JSON_SCHEMA = {
1181
1222
  type: "object",
1182
- maxProperties: 10,
1223
+ maxProperties: 11,
1183
1224
  additionalProperties: false,
1184
1225
  properties: {
1185
1226
  schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
@@ -1203,7 +1244,8 @@ var CEAL_REQUEST_JSON_SCHEMA = {
1203
1244
  host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1204
1245
  host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1205
1246
  host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1206
- requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." }
1247
+ requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1248
+ device_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source, description: "Device that submitted this Request through its client session; absent for Host turns and older records." }
1207
1249
  },
1208
1250
  required: CEAL_REQUEST_KEYS
1209
1251
  };
@@ -1262,6 +1304,7 @@ var REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES = {
1262
1304
  retryable: { type: "boolean" },
1263
1305
  source: { type: "string", enum: ["provider", "gateway"] },
1264
1306
  retry_after_ms: { type: "integer", minimum: 0, maximum: CEAL_RETRY_AFTER_MAX_MS },
1307
+ write_request_ref: { type: "string", pattern: CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN.source, maxLength: 128 },
1265
1308
  excluded_targets: cealExcludedTargetCoverageJsonSchema(),
1266
1309
  repaired_input: {},
1267
1310
  unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
@@ -1326,6 +1369,43 @@ function cealHostObservationRef(producer, digest2) {
1326
1369
  if (!HOST_OBSERVATION_PRODUCER.test(producer) || !HOST_OBSERVATION_DIGEST.test(digest2)) invalid("Ceal Host observation reference parts are invalid.");
1327
1370
  return `${CEAL_HOST_OBSERVATION_REF_PREFIX}${producer}-${digest2}`;
1328
1371
  }
1372
+ var HOST_TOKEN_OBSERVATION_BREAKDOWN_KEYS = vocabulary(["cached_input", "cache_write_input", "reasoning_output"]);
1373
+ function createHostTokenObservationValue(counts, source) {
1374
+ const core = validTokenCore(counts);
1375
+ if (typeof source !== "string" || source.length === 0 || source.length > 256) invalid("Ceal Host token observation source is invalid.");
1376
+ const value = { status: "available", unit: "tokens", input: core.input, output: core.output, total: core.total, source };
1377
+ for (const breakdown of validTokenBreakdowns(counts)) value[breakdown.key] = breakdown.tokens;
1378
+ assertHostObservationValue(value, "token");
1379
+ return value;
1380
+ }
1381
+ function validTokenCore(counts) {
1382
+ if (!isOperationRecord(counts) || !safeCount(counts.input) || !safeCount(counts.output) || !safeCount(counts.total)) invalid("Ceal Host token observation counts are invalid.");
1383
+ if (counts.total !== counts.input + counts.output) invalid("Ceal Host token observation counts are invalid.");
1384
+ return { input: counts.input, output: counts.output, total: counts.total };
1385
+ }
1386
+ function validTokenBreakdowns(counts) {
1387
+ const breakdowns = [
1388
+ { key: "cached_input", tokens: counts.cached_input },
1389
+ { key: "cache_write_input", tokens: counts.cache_write_input },
1390
+ { key: "reasoning_output", tokens: counts.reasoning_output }
1391
+ ];
1392
+ const stated = breakdowns.filter((breakdown) => breakdown.tokens !== void 0).map((breakdown) => ({ key: breakdown.key, tokens: Number(breakdown.tokens) }));
1393
+ for (const breakdown of stated) {
1394
+ if (!safeCount(breakdown.tokens)) invalid("Ceal Host token observation counts are invalid.");
1395
+ }
1396
+ const cached = counts.cached_input ?? 0;
1397
+ const created = counts.cache_write_input ?? 0;
1398
+ const reasoning = counts.reasoning_output ?? 0;
1399
+ if (cached + created > counts.input || reasoning > counts.output) invalid("Ceal Host token observation counts are invalid.");
1400
+ return stated;
1401
+ }
1402
+ function hostObservationNotRecorded(reason) {
1403
+ if (typeof reason !== "string" || reason.length === 0 || reason.length > 256) invalid("Ceal Host observation gap reason is invalid.");
1404
+ return { status: "not_recorded", reason };
1405
+ }
1406
+ function safeCount(value) {
1407
+ return Number.isSafeInteger(value) && Number(value) >= 0;
1408
+ }
1329
1409
  function validateCealHostObservationDraft(value) {
1330
1410
  const record3 = operationRecord(value, "Ceal Host observation draft is invalid.");
1331
1411
  requirePrefixedRef(record3.observation_ref, CEAL_HOST_OBSERVATION_REF_PREFIX, "Ceal Host observation reference is invalid.");
@@ -1477,12 +1557,83 @@ var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
1477
1557
  "host_input_mismatch",
1478
1558
  "host_prompt_mismatch"
1479
1559
  ]);
1560
+ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
1561
+ "invalid_arguments",
1562
+ "options_invalid",
1563
+ "session_unavailable",
1564
+ "context_unavailable",
1565
+ "context_replaced",
1566
+ "no_binding",
1567
+ "context_stale",
1568
+ "context_expired",
1569
+ "host_source_refused",
1570
+ "host_credential_refused",
1571
+ "gateway_unavailable",
1572
+ "gateway_rate_limited",
1573
+ "gateway_refused",
1574
+ "gateway_response_invalid",
1575
+ "producer_frame_invalid",
1576
+ "relay_permission_denied",
1577
+ "relay_unavailable",
1578
+ "transient_retryable",
1579
+ "mint_failed",
1580
+ "relay_failed",
1581
+ "host_refusal_invalid"
1582
+ ]);
1583
+ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
1584
+ invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
1585
+ options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
1586
+ session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1587
+ context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn; run `ceal-host install` to repair the Host hook registration, then retry this command on a fresh Host turn" }),
1588
+ context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1589
+ no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
1590
+ context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1591
+ context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "refresh_host_invocation_context" }),
1592
+ host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
1593
+ host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
1594
+ gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
1595
+ gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
1596
+ gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
1597
+ gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
1598
+ producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin", remedy: "ask an administrator to inspect and repair the producer frame, then start a fresh Host turn" }),
1599
+ relay_permission_denied: Object.freeze({ classification: "host_relay_permission_denied", next_action: "rerun_outside_agent_sandbox", remedy: "an agent sandbox denied the Host Relay socket; run `ceal` as the only command, with no pipe, redirect, or `&&`, so the Host allow rule runs it outside the sandbox" }),
1600
+ relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
1601
+ transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
1602
+ mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
1603
+ relay_failed: Object.freeze({ classification: "host_relay_failed", next_action: "answer_with_gap" }),
1604
+ host_refusal_invalid: Object.freeze({ classification: "host_refusal_invalid", next_action: "answer_with_gap" })
1605
+ });
1606
+
1607
+ // packages/ceal-client-protocol/src/host-service-diagnostic.ts
1608
+ var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
1609
+ var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
1610
+ "hook_invoked",
1611
+ "registration_written",
1612
+ "hook_failed",
1613
+ "watcher_turn_observed",
1614
+ "current_prompt_registered",
1615
+ "current_prompt_registration_failed",
1616
+ "activation_recorded",
1617
+ "activation_failed",
1618
+ "watcher_scan_failed",
1619
+ "activation_wait_skipped",
1620
+ "activation_wait_engaged",
1621
+ "activation_wait_completed",
1622
+ "activation_wait_exhausted"
1623
+ ]);
1624
+ var CEAL_HOST_SERVICE_DIAGNOSTIC_FAILURE_KINDS = vocabulary([
1625
+ "malformed_line",
1626
+ "other",
1627
+ "payload_rejected",
1628
+ "read_error",
1629
+ "size_budget",
1630
+ "symlink"
1631
+ ]);
1480
1632
 
1481
1633
  // packages/ceal-client-protocol/src/client-result-materialization.ts
1482
1634
  var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "unsupported", "download_failed", "permission_denied"]);
1483
- var CEAL_MODEL_VISIBLE_COMMAND_OUTPUT_MAX_BYTES = 32 * 1024;
1484
- var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "read_argv"]);
1485
- var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "local_result_root", "continue_argv", "model_disclosure"]);
1635
+ var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
1636
+ var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "items_path", "row_count", "result_complete", "coverage", "local_result_root", "model_disclosure", "continue_argv", "how_to_read"]);
1486
1637
 
1487
1638
  // packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
1488
1639
  var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
@@ -1512,14 +1663,6 @@ var CEAL_GATEWAY_ANNOUNCEMENT_POLICY_NON_CLAIMS = vocabulary([
1512
1663
  "target_specific_scope_not_declared"
1513
1664
  ]);
1514
1665
 
1515
- // packages/ceal-client-protocol/src/gateway-write-identity.ts
1516
- var CEAL_GATEWAY_WRITE_IDENTITY_ROLES = Object.freeze(["replay_identity", "lookup_handle", "collision_evidence"]);
1517
- var CEAL_GATEWAY_WRITE_IDENTITY_FIELDS = Object.freeze({
1518
- replay_identity: "idempotency_claim_sha256",
1519
- lookup_handle: "write_request_sha256",
1520
- collision_evidence: "normalized_mutation_sha256"
1521
- });
1522
-
1523
1666
  // packages/ceal-client-protocol/src/gateway-scoped-identity-projection-validation.ts
1524
1667
  var PROJECTION_REVISION_KEYS = Object.freeze(["graph_revision", "subject_key_revision", "projection_revision"]);
1525
1668
 
@@ -1532,6 +1675,18 @@ var CEAL_SUPPORTED_GATEWAY_PROTOCOL_RANGE = Object.freeze({
1532
1675
  // packages/ceal-client-protocol/src/admin-projection-vocabulary.ts
1533
1676
  var ADMIN_PROJECTION_STATES = vocabulary(["ready", "empty", "partial", "unknown"]);
1534
1677
  var ADMIN_VIEW_KINDS = vocabulary(["request_detail", "resource_access_timeline", "weekly_usage"]);
1678
+ var ADMIN_RECORD_PROJECTION_KINDS = vocabulary([
1679
+ "request_list",
1680
+ "usage_series",
1681
+ "connector_health",
1682
+ "person_connections",
1683
+ "client_sessions",
1684
+ "knowledge_base",
1685
+ "change_history",
1686
+ "approval_queue",
1687
+ "access_group_list",
1688
+ "capability_catalogue"
1689
+ ]);
1535
1690
  var ADMIN_GAP_KINDS = vocabulary(["unknown", "unavailable", "partial"]);
1536
1691
  var ADMIN_UNAVAILABLE_REASONS = vocabulary([
1537
1692
  "not_recorded",
@@ -1555,7 +1710,11 @@ var ADMIN_SOURCE_OWNERS = vocabulary([
1555
1710
  "person",
1556
1711
  "resource",
1557
1712
  "prompt",
1558
- "usage"
1713
+ "usage",
1714
+ "connector",
1715
+ "session",
1716
+ "knowledge",
1717
+ "policy"
1559
1718
  ]);
1560
1719
  var ADMIN_COMPLETENESS_STATES = vocabulary(["complete", "partial", "unknown"]);
1561
1720
  var ADMIN_OPERATION_OUTCOMES = vocabulary(["completed", "failed", "denied", "cancelled", "unknown"]);
@@ -1790,6 +1949,15 @@ var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
1790
1949
  function isHostProducerFrameKind(value) {
1791
1950
  return typeof value === "string" && FRAME_KINDS.has(value);
1792
1951
  }
1952
+ var HOST_PRODUCER_FRAME_ERROR_CODES = [
1953
+ "invalid_frame_encoding",
1954
+ "invalid_frame_length",
1955
+ "frame_truncated",
1956
+ "unsupported_frame_schema",
1957
+ "invalid_frame_shape",
1958
+ "invalid_producer_identity",
1959
+ "frame_too_large"
1960
+ ];
1793
1961
  var HostProducerFrameError = class extends Error {
1794
1962
  code;
1795
1963
  constructor(code, message = code) {
@@ -1824,6 +1992,9 @@ function decodeHostProducerFrame(input) {
1824
1992
  validateFrame(value);
1825
1993
  return value;
1826
1994
  }
1995
+ function isHostSafeRef(value) {
1996
+ return safeRef(value);
1997
+ }
1827
1998
  function validateFrame(value) {
1828
1999
  if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
1829
2000
  if (value.schema_version !== HOST_PRODUCER_FRAME_SCHEMA) throw new HostProducerFrameError("unsupported_frame_schema", `unsupported_frame_schema: ${refusalFact("schema_version", value.schema_version, HOST_PRODUCER_FRAME_SCHEMA)}`);
@@ -1911,6 +2082,15 @@ function exactOptional(value, required, optional) {
1911
2082
  return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
1912
2083
  }
1913
2084
 
2085
+ // host/relay/src/host-producer-core.ts
2086
+ import { createHash as createHash3 } from "node:crypto";
2087
+ import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
2088
+ import { mkdir as mkdir2, open as open3, readFile, rename as rename2, writeFile } from "node:fs/promises";
2089
+ import { join as join2 } from "node:path";
2090
+
2091
+ // host/relay/src/relay-ipc.ts
2092
+ import { createConnection, createServer } from "node:net";
2093
+
1914
2094
  // host/relay/src/durable-atomic-replacement.ts
1915
2095
  import { O_RDONLY } from "node:constants";
1916
2096
  import { lstat as lstat2, mkdir, open as open2, rename, unlink } from "node:fs/promises";
@@ -1928,17 +2108,6 @@ var DEFAULT_PORT = {
1928
2108
  // host/relay/src/relay-request-binding-store.ts
1929
2109
  var MAX_STATE_BYTES = 8 * 1024 * 1024;
1930
2110
 
1931
- // host/relay/src/relay-quarantine-store.ts
1932
- var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
1933
-
1934
- // host/relay/src/relay-admission-store.ts
1935
- var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
1936
- var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
1937
- var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
1938
-
1939
- // host/relay/src/relay-ipc.ts
1940
- import { createConnection, createServer } from "node:net";
1941
-
1942
2111
  // host/relay/src/current-prompt-custody.ts
1943
2112
  var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
1944
2113
 
@@ -1971,6 +2140,8 @@ var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
1971
2140
  "gateway_unavailable",
1972
2141
  "gateway_refused",
1973
2142
  "gateway_response_invalid",
2143
+ "gateway_rate_limited",
2144
+ "producer_frame_invalid",
1974
2145
  "deadline",
1975
2146
  "cancelled",
1976
2147
  "mint_failed",
@@ -1984,6 +2155,17 @@ var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
1984
2155
  "host_input_mismatch",
1985
2156
  "host_prompt_mismatch"
1986
2157
  ]);
2158
+ var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
2159
+ "context_stale",
2160
+ "context_expired",
2161
+ "host_source_refused",
2162
+ "host_credential_refused",
2163
+ "gateway_unavailable",
2164
+ "gateway_rate_limited",
2165
+ "gateway_refused",
2166
+ "gateway_response_invalid",
2167
+ "producer_frame_invalid"
2168
+ ]);
1987
2169
  var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
1988
2170
  var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
1989
2171
  "permission_denied",
@@ -2005,6 +2187,9 @@ var HostRelayIpcError = class extends Error {
2005
2187
  this.failure = failure;
2006
2188
  }
2007
2189
  };
2190
+ function hostRelayExchangeFailure(error) {
2191
+ return error instanceof HostRelayIpcError ? error.failure : void 0;
2192
+ }
2008
2193
  function socketErrorFailure(error) {
2009
2194
  switch (errorCode(error)) {
2010
2195
  case "EACCES":
@@ -2147,22 +2332,348 @@ function encodeLengthPrefixed(value) {
2147
2332
  body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
2148
2333
  return output;
2149
2334
  }
2150
- function exactKeys2(value, keys) {
2151
- return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
2335
+ function exactKeys2(value, required, optional = []) {
2336
+ const keys = Object.keys(value);
2337
+ return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
2152
2338
  }
2153
2339
  function errorCode(error) {
2154
2340
  return record2(error) && typeof error.code === "string" ? error.code : null;
2155
2341
  }
2156
2342
 
2343
+ // host/relay/src/host-producer-core.ts
2344
+ var HOST_TRANSCRIPT_CURSOR_REASONS = vocabulary(["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"]);
2345
+ function isHostTranscriptCursorReason(value) {
2346
+ return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
2347
+ }
2348
+ var TRANSCRIPT_MISSING_REASON = "transcript_missing";
2349
+ var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
2350
+ async function readTranscriptCompleteChunk(handle, offset, size) {
2351
+ const available = Math.max(0, size - offset);
2352
+ if (available === 0) return "empty";
2353
+ const chunks = [];
2354
+ let position = offset;
2355
+ let total = 0;
2356
+ let lastNewline = -1;
2357
+ while (position < size) {
2358
+ const buffer = Buffer.alloc(Math.min(HOST_TRANSCRIPT_READ_CHUNK_BYTES, size - position));
2359
+ const read = await handle.read(buffer, 0, buffer.length, position);
2360
+ if (read.bytesRead === 0) break;
2361
+ const chunk = buffer.subarray(0, read.bytesRead);
2362
+ chunks.push(chunk);
2363
+ const newline = chunk.lastIndexOf(10);
2364
+ if (newline >= 0) lastNewline = total + newline;
2365
+ total += chunk.length;
2366
+ position += chunk.length;
2367
+ if (lastNewline >= 0) break;
2368
+ }
2369
+ if (lastNewline < 0) return "partial";
2370
+ return Buffer.concat(chunks, total).subarray(0, lastNewline + 1);
2371
+ }
2372
+ async function readTranscriptAnchor(handle, offset, anchorBytes) {
2373
+ const start = Math.max(0, offset - anchorBytes);
2374
+ const anchor = Buffer.alloc(offset - start);
2375
+ if (anchor.length > 0) await handle.read(anchor, 0, anchor.length, start);
2376
+ return { start, sha256: transcriptSha256(anchor) };
2377
+ }
2378
+ async function transcriptAnchorMatches(handle, cursor) {
2379
+ const buffer = Buffer.alloc(cursor.offset - cursor.anchor_start);
2380
+ if (buffer.length > 0) await handle.read(buffer, 0, buffer.length, cursor.anchor_start);
2381
+ return transcriptSha256(buffer) === cursor.anchor_sha256;
2382
+ }
2383
+ function transcriptCursorIdentityReason(cursor, transcriptPath, device, inode) {
2384
+ if (cursor.device !== device || cursor.inode !== inode || cursor.transcript_path !== transcriptPath) return "transcript_replaced";
2385
+ return null;
2386
+ }
2387
+ async function transcriptCursorBoundsReason(handle, cursor, size) {
2388
+ if (size < cursor.offset) return "transcript_truncated";
2389
+ if (cursor.offset > 0 && !await transcriptAnchorMatches(handle, cursor)) return "transcript_rewritten";
2390
+ return null;
2391
+ }
2392
+ function transcriptSha256(value) {
2393
+ return createHash3("sha256").update(value).digest("hex");
2394
+ }
2395
+ function transcriptObjectSha256(value) {
2396
+ return createHash3("sha256").update(JSON.stringify(value), "utf8").digest("hex");
2397
+ }
2398
+ function isTranscriptRecord(value) {
2399
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2400
+ }
2401
+ function isMissingTranscriptError(error) {
2402
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2403
+ }
2404
+ function transcriptTimestamp(now, fail) {
2405
+ const value = now();
2406
+ if (!Number.isFinite(value.getTime())) throw fail();
2407
+ return value.toISOString();
2408
+ }
2409
+ function transcriptExpiration(now, retentionMs, fail) {
2410
+ return new Date(Date.parse(transcriptTimestamp(now, fail)) + retentionMs).toISOString();
2411
+ }
2412
+ function createTranscriptWatcherBase(options, owner) {
2413
+ if (typeof options.outbox_dir !== "string" || options.outbox_dir.length === 0) throw owner.invalid_outbox();
2414
+ return {
2415
+ outboxRetentionMs: options.retention_ms ?? owner.default_retention_ms,
2416
+ now: options.now ?? (() => /* @__PURE__ */ new Date()),
2417
+ log: options.log ?? ((line) => {
2418
+ process.stderr.write(`${line}
2419
+ `);
2420
+ }),
2421
+ producerInstallationRef: options.producer_installation_ref
2422
+ };
2423
+ }
2424
+ async function scanTranscriptRegistrations(registrations, scanOne) {
2425
+ let records = 0;
2426
+ let enqueued = 0;
2427
+ let quarantined = 0;
2428
+ for (const registration of registrations) {
2429
+ const result = await scanOne(registration);
2430
+ records += result.records;
2431
+ enqueued += result.enqueued;
2432
+ if (result.quarantined) quarantined += 1;
2433
+ }
2434
+ return { registrations: registrations.length, records, enqueued, quarantined };
2435
+ }
2436
+ function formatRetiredTranscriptLog(producerLabel, transcriptPath) {
2437
+ return `${producerLabel} retired a transcript registration: transcript_path=${JSON.stringify(transcriptPath)}, reason=${TRANSCRIPT_MISSING_REASON}.`;
2438
+ }
2439
+ var HostTranscriptWatcher = class {
2440
+ outboxRetentionMs;
2441
+ now;
2442
+ log;
2443
+ producerInstallationRef;
2444
+ clockFailure;
2445
+ constructor(options, owner) {
2446
+ const base = createTranscriptWatcherBase(options, owner);
2447
+ this.outboxRetentionMs = base.outboxRetentionMs;
2448
+ this.now = base.now;
2449
+ this.log = base.log;
2450
+ this.producerInstallationRef = base.producerInstallationRef;
2451
+ this.clockFailure = owner.clock_failure;
2452
+ }
2453
+ async scanOnce() {
2454
+ return scanTranscriptRegistrations(await this.listRegistrations(), (registration) => this.scanRegistration(registration));
2455
+ }
2456
+ async scanRegistration(registration) {
2457
+ return this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
2458
+ missing: () => this.retireMissing(registration),
2459
+ failed: (handle, error) => this.scanFailed(handle, registration, error)
2460
+ });
2461
+ }
2462
+ async openTranscript(registration, scan, handlers) {
2463
+ let handle;
2464
+ try {
2465
+ handle = await open3(this.transcriptPathOf(registration), O_RDONLY2 | O_NOFOLLOW);
2466
+ return await scan(handle);
2467
+ } catch (error) {
2468
+ if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
2469
+ if (hostRelayExchangeFailure(error) !== void 0) throw error;
2470
+ return handlers.failed(handle, error);
2471
+ } finally {
2472
+ await handle?.close();
2473
+ }
2474
+ }
2475
+ /**
2476
+ * A transcript the Host deleted never comes back, so retrying its
2477
+ * registration once per reconcile interval is a failure with no end.
2478
+ * Retire it at the scan that finds it missing and say so once. Only
2479
+ * ENOENT means gone; a permission or busy failure is about this moment,
2480
+ * not about the file, and keeps the registration for the next scan.
2481
+ */
2482
+ async retireMissingTranscript(transcriptPath, producerLabel, retire) {
2483
+ await retire();
2484
+ this.log(formatRetiredTranscriptLog(producerLabel, transcriptPath));
2485
+ return { records: 0, enqueued: 0, quarantined: false };
2486
+ }
2487
+ stamp() {
2488
+ return transcriptTimestamp(this.now, this.clockFailure);
2489
+ }
2490
+ expire(retentionMs) {
2491
+ return transcriptExpiration(this.now, retentionMs, this.clockFailure);
2492
+ }
2493
+ sha(value) {
2494
+ return transcriptSha256(value);
2495
+ }
2496
+ objectSha(value) {
2497
+ return transcriptObjectSha256(value);
2498
+ }
2499
+ isRecord(value) {
2500
+ return isTranscriptRecord(value);
2501
+ }
2502
+ readChunk(handle, offset, size) {
2503
+ return readTranscriptCompleteChunk(handle, offset, size);
2504
+ }
2505
+ readAnchor(handle, offset, anchorBytes) {
2506
+ return readTranscriptAnchor(handle, offset, anchorBytes);
2507
+ }
2508
+ cursorIdentityReason(cursor, transcriptPath, device, inode) {
2509
+ return transcriptCursorIdentityReason(cursor, transcriptPath, device, inode);
2510
+ }
2511
+ cursorBoundsReason(handle, cursor, size) {
2512
+ return transcriptCursorBoundsReason(handle, cursor, size);
2513
+ }
2514
+ };
2515
+ var HOST_PRODUCER_STATE_IDENTITY = {
2516
+ codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
2517
+ claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
2518
+ };
2519
+ var HostProducerDrainCore = class {
2520
+ host;
2521
+ relay;
2522
+ statePath;
2523
+ stateSchema;
2524
+ installationRef;
2525
+ epoch;
2526
+ now;
2527
+ tail = Promise.resolve();
2528
+ constructor(host, runtime) {
2529
+ if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
2530
+ const epoch = runtime.producer_epoch ?? 1;
2531
+ if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
2532
+ this.host = host;
2533
+ this.relay = runtime.relay;
2534
+ this.statePath = join2(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
2535
+ this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
2536
+ this.installationRef = runtime.producer_installation_ref;
2537
+ this.epoch = epoch;
2538
+ this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
2539
+ }
2540
+ drainOutbox() {
2541
+ const run = this.tail.then(() => this.drainLocked());
2542
+ this.tail = run.then(() => void 0, () => void 0);
2543
+ return run;
2544
+ }
2545
+ async drainLocked() {
2546
+ const read = await this.host.listEnvelopes();
2547
+ let admitted = 0;
2548
+ let expired = 0;
2549
+ let retained = 0;
2550
+ for (const envelope of read.entries) {
2551
+ const reserved = await this.hasReservation(this.host.submissionRefOf(envelope));
2552
+ if (!reserved && Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) {
2553
+ await this.host.removeEnvelope(envelope);
2554
+ expired += 1;
2555
+ continue;
2556
+ }
2557
+ const frames = await this.reserve(envelope);
2558
+ let complete = true;
2559
+ for (const frame of frames) {
2560
+ const outcome = await this.relay.admit(frame);
2561
+ if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
2562
+ complete = false;
2563
+ break;
2564
+ }
2565
+ }
2566
+ if (!complete) {
2567
+ retained += 1;
2568
+ break;
2569
+ }
2570
+ await this.host.removeEnvelope(envelope);
2571
+ await this.release(this.host.submissionRefOf(envelope));
2572
+ admitted += 1;
2573
+ }
2574
+ return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
2575
+ }
2576
+ async hasReservation(submissionRef) {
2577
+ return (await this.readState()).pending[submissionRef] !== void 0;
2578
+ }
2579
+ async reserve(envelope) {
2580
+ const state = await this.readState();
2581
+ const submissionRef = this.host.submissionRefOf(envelope);
2582
+ const existing = state.pending[submissionRef];
2583
+ if (existing !== void 0) return existing;
2584
+ const drafts = await this.host.mapEnvelope(envelope);
2585
+ const frames = drafts.map((draft, offset) => ({
2586
+ schema_version: "ceal.host_producer_frame.v1",
2587
+ producer_id: this.host.producer_id,
2588
+ producer_installation_ref: this.installationRef,
2589
+ producer_epoch: this.epoch,
2590
+ sequence: state.next_sequence + offset,
2591
+ frame_kind: draft.frame_kind,
2592
+ occurred_at: this.host.createdAtOf(envelope),
2593
+ payload: draft.payload
2594
+ }));
2595
+ await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [submissionRef]: frames } });
2596
+ return frames;
2597
+ }
2598
+ async release(submissionRef) {
2599
+ const state = await this.readState();
2600
+ if (state.pending[submissionRef] === void 0) return;
2601
+ const pending = { ...state.pending };
2602
+ delete pending[submissionRef];
2603
+ await this.writeState({ ...state, pending });
2604
+ }
2605
+ async readState() {
2606
+ try {
2607
+ const value = JSON.parse(await readFile(this.statePath, "utf8"));
2608
+ return this.decodeState(value);
2609
+ } catch (error) {
2610
+ if (errorCode2(error) !== "ENOENT") throw error;
2611
+ return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
2612
+ }
2613
+ }
2614
+ async writeState(state) {
2615
+ await mkdir2(this.host.outbox_directory, { recursive: true, mode: 448 });
2616
+ const temporary = `${this.statePath}.${process.pid}.new`;
2617
+ await writeFile(temporary, JSON.stringify(state), { mode: 384 });
2618
+ await rename2(temporary, this.statePath);
2619
+ }
2620
+ decodeState(value) {
2621
+ const label = this.host.producer_label;
2622
+ const fail = (code, message) => this.host.fail(code, message);
2623
+ if (!isDrainStateHeader(value, this.stateSchema, this.installationRef)) {
2624
+ throw fail("storage", `${label} producer state header is malformed or belongs to another installation (${refusalFacts([
2625
+ { name: "schema_version", value: isTranscriptRecord(value) ? value.schema_version : void 0, expected: this.stateSchema },
2626
+ { name: "producer_installation_ref", value: isTranscriptRecord(value) ? value.producer_installation_ref : void 0, expected: this.installationRef }
2627
+ ])}).`);
2628
+ }
2629
+ if (Number(value.producer_epoch) > this.epoch) throw fail("storage", `${label} producer state epoch is ahead of this installation (${refusalFact("producer_epoch", value.producer_epoch, `at most ${this.epoch}`)}).`);
2630
+ const pending = {};
2631
+ const storedEpoch = Number(value.producer_epoch);
2632
+ for (const [submissionRef, frames] of Object.entries(value.pending)) {
2633
+ if (!isHostSafeRef(submissionRef) || !Array.isArray(frames) || frames.length === 0) throw fail("storage", `${label} producer pending reservation is malformed.`);
2634
+ pending[submissionRef] = frames.map((frame) => this.decodeStoredFrame(frame, storedEpoch));
2635
+ }
2636
+ return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: Number(value.next_sequence), pending };
2637
+ }
2638
+ decodeStoredFrame(value, storedEpoch) {
2639
+ const label = this.host.producer_label;
2640
+ const json = Buffer.from(JSON.stringify(value), "utf8");
2641
+ const encoded = Buffer.allocUnsafe(json.length + 4);
2642
+ encoded.writeUInt32BE(json.length, 0);
2643
+ json.copy(encoded, 4);
2644
+ const frame = decodeHostProducerFrame(encoded);
2645
+ if (frame.producer_id !== this.host.producer_id || frame.producer_installation_ref !== this.installationRef || frame.producer_epoch > storedEpoch) throw this.host.fail("storage", `${label} producer pending frame identity is malformed (${refusalFacts([
2646
+ { name: "producer_id", value: frame.producer_id, expected: this.host.producer_id },
2647
+ { name: "producer_installation_ref", value: frame.producer_installation_ref, expected: this.installationRef },
2648
+ { name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${storedEpoch}` }
2649
+ ])}).`);
2650
+ return frame;
2651
+ }
2652
+ };
2653
+ function isDrainStateHeader(value, schema, installationRef) {
2654
+ return isTranscriptRecord(value) && value.schema_version === schema && value.producer_installation_ref === installationRef && Number.isSafeInteger(value.producer_epoch) && Number(value.producer_epoch) > 0 && Number.isSafeInteger(value.next_sequence) && Number(value.next_sequence) > 0 && isTranscriptRecord(value.pending);
2655
+ }
2656
+ function errorCode2(error) {
2657
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2658
+ }
2659
+
2660
+ // host/relay/src/relay-quarantine-store.ts
2661
+ var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
2662
+ var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
2663
+ var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
2664
+
2665
+ // host/relay/src/relay-admission-store.ts
2666
+ var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
2667
+ var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
2668
+ var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
2669
+
2157
2670
  // host/codex/src/codex-producer.ts
2158
- import { createHash as createHash3 } from "node:crypto";
2159
- import { mkdir as mkdir3, readFile, rename as rename3, writeFile } from "node:fs/promises";
2160
- import { join as join3 } from "node:path";
2671
+ import { createHash as createHash4 } from "node:crypto";
2161
2672
 
2162
2673
  // host/codex/src/codex-outbox.ts
2163
- import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
2164
- import { lstat as lstat3, mkdir as mkdir2, open as open3, readdir, rename as rename2, unlink as unlink2 } from "node:fs/promises";
2165
- import { dirname, isAbsolute, join as join2 } from "node:path";
2674
+ import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
2675
+ import { lstat as lstat3, mkdir as mkdir3, open as open4, readdir, rename as rename3, unlink as unlink2 } from "node:fs/promises";
2676
+ import { dirname, isAbsolute, join as join3 } from "node:path";
2166
2677
 
2167
2678
  // host/invocation-context.ts
2168
2679
  var HOST_INVOCATION_IDENTITY_KEYS = ["host_session_ref", "host_turn_ref", "host_source_ref"];
@@ -2176,17 +2687,32 @@ function hasHostInvocationIdentityKeys(value) {
2176
2687
  function hasHostInvocationContextKeys(value, optional = []) {
2177
2688
  return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
2178
2689
  }
2690
+ async function readLatestSessionProjection(file, hostSourceRef, hostSessionRef, readProjection) {
2691
+ const context = await readProjection(file);
2692
+ if (context === null || context.identity.host_source_ref !== hostSourceRef || context.identity.host_session_ref !== hostSessionRef) return null;
2693
+ return context;
2694
+ }
2695
+ function isLiveSessionProjection(context, now) {
2696
+ return context !== null && Date.parse(context.expires_at) > now.getTime();
2697
+ }
2179
2698
  function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor) {
2180
2699
  const context = {
2181
2700
  schema_version: descriptor.schema_version,
2182
2701
  identity,
2183
2702
  prompt_ref: promptRef,
2184
2703
  expires_at: expiresAt,
2704
+ ...descriptor.turn_generation_ref === void 0 ? {} : { turn_generation_ref: descriptor.turn_generation_ref },
2185
2705
  ...descriptor.working_directory === void 0 ? {} : { working_directory: descriptor.working_directory }
2186
2706
  };
2187
2707
  descriptor.validate(context);
2188
2708
  return Object.freeze(context);
2189
2709
  }
2710
+ function isHostInvocationTurnSelection(value) {
2711
+ return isRecord2(value) && Object.keys(value).length === 5 && value.schema_version === "ceal.host_invocation_turn_selection.v1" && ["host_source_ref", "host_session_ref", "prompt_ref", "turn_generation_ref"].every((key) => safeRef2(value[key]));
2712
+ }
2713
+ function invocationTurnSelectionMatches(context, selection) {
2714
+ return selection === null || context !== null && selection.host_source_ref === context.identity.host_source_ref && selection.host_session_ref === context.identity.host_session_ref && selection.prompt_ref === context.prompt_ref && selection.turn_generation_ref === context.turn_generation_ref;
2715
+ }
2190
2716
  function exactKeys3(value, keys) {
2191
2717
  const actual = Object.keys(value);
2192
2718
  return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
@@ -2196,6 +2722,12 @@ function requiredKeys(value, required, optional) {
2196
2722
  const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
2197
2723
  return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
2198
2724
  }
2725
+ function isRecord2(value) {
2726
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2727
+ }
2728
+ function safeRef2(value) {
2729
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
2730
+ }
2199
2731
 
2200
2732
  // host/codex/src/codex-types.ts
2201
2733
  var CODEX_OUTBOX_SCHEMA_VERSION = "ceal.codex_observation_outbox.v1";
@@ -2205,10 +2737,6 @@ var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1"
2205
2737
  var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
2206
2738
  var CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION = "ceal.codex_transcript_cursor.v1";
2207
2739
  var CODEX_DEFAULT_RETENTION_MS = 24 * 60 * 60 * 1e3;
2208
- var CODEX_TRANSCRIPT_CURSOR_REASONS = ["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"];
2209
- function isCodexTranscriptCursorReason(value) {
2210
- return typeof value === "string" && CODEX_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
2211
- }
2212
2740
  var CodexHostAdapterError = class extends Error {
2213
2741
  name = "CodexHostAdapterError";
2214
2742
  code;
@@ -2219,8 +2747,8 @@ var CodexHostAdapterError = class extends Error {
2219
2747
  };
2220
2748
 
2221
2749
  // host/codex/src/codex-invocation-context.ts
2222
- function codexInvocationContextFromTurn(identity, promptRef, expiresAt) {
2223
- return invocationContextFromTurn(identity, promptRef, expiresAt, { schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION, validate: validateCodexHostInvocationContext });
2750
+ function codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef) {
2751
+ return invocationContextFromTurn(identity, promptRef, expiresAt, { schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION, validate: validateCodexHostInvocationContext, turn_generation_ref: turnGenerationRef });
2224
2752
  }
2225
2753
  function validateCodexHostInvocationContext(value) {
2226
2754
  if (!isContextShape(value)) {
@@ -2230,28 +2758,29 @@ function validateCodexHostInvocationContext(value) {
2230
2758
  validateContextReferences(value);
2231
2759
  validateContextTimes(value);
2232
2760
  }
2233
- function invocationContextFromBinding(binding) {
2761
+ function invocationContextFromBinding(binding, turnGenerationRef) {
2234
2762
  const context = {
2235
2763
  schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
2236
2764
  identity: binding.identity,
2237
2765
  prompt_ref: binding.prompt_ref,
2238
2766
  request_ref: binding.request_ref,
2239
2767
  bound_at: binding.bound_at,
2240
- expires_at: binding.expires_at
2768
+ expires_at: binding.expires_at,
2769
+ ...turnGenerationRef === void 0 ? {} : { turn_generation_ref: turnGenerationRef }
2241
2770
  };
2242
2771
  validateCodexHostInvocationContext(context);
2243
2772
  return Object.freeze(context);
2244
2773
  }
2245
2774
  function validateIdentity2(value) {
2246
- if (!isRecord2(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef2)) {
2775
+ if (!isRecord3(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef3)) {
2247
2776
  throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
2248
2777
  }
2249
2778
  }
2250
2779
  function isContextShape(value) {
2251
- return isRecord2(value) && hasHostInvocationContextKeys(value, ["request_ref", "bound_at"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
2780
+ return isRecord3(value) && hasHostInvocationContextKeys(value, ["request_ref", "bound_at", "turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
2252
2781
  }
2253
2782
  function validateContextReferences(value) {
2254
- if (!safeRef2(value.prompt_ref) || !optionalSafeRef(value.request_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2783
+ if (!safeRef3(value.prompt_ref) || !optionalSafeRef(value.request_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2255
2784
  }
2256
2785
  function validateContextTimes(value) {
2257
2786
  const expiresAt = value.expires_at;
@@ -2260,18 +2789,18 @@ function validateContextTimes(value) {
2260
2789
  if (typeof boundAt === "string" && Date.parse(boundAt) > Date.parse(expiresAt)) throw new CodexHostAdapterError("storage", `Codex Host invocation context has invalid binding time (${refusalFact("bound_at", boundAt, `at or before ${expiresAt}`)}).`);
2261
2790
  }
2262
2791
  function optionalSafeRef(value) {
2263
- return value === void 0 || safeRef2(value);
2792
+ return value === void 0 || safeRef3(value);
2264
2793
  }
2265
2794
  function optionalTimestamp(value) {
2266
2795
  return value === void 0 || timestamp2(value);
2267
2796
  }
2268
- function safeRef2(value) {
2797
+ function safeRef3(value) {
2269
2798
  return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
2270
2799
  }
2271
2800
  function timestamp2(value) {
2272
2801
  return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
2273
2802
  }
2274
- function isRecord2(value) {
2803
+ function isRecord3(value) {
2275
2804
  return value !== null && typeof value === "object" && !Array.isArray(value);
2276
2805
  }
2277
2806
 
@@ -2284,20 +2813,22 @@ var CodexOutboxStore = class {
2284
2813
  bindingsDirectory;
2285
2814
  sessionsDirectory;
2286
2815
  activeContextsDirectory;
2816
+ turnSelectionsDirectory;
2287
2817
  registrationsDirectory;
2288
2818
  cursorsDirectory;
2289
2819
  constructor(directory) {
2290
2820
  if (typeof directory !== "string" || directory.length === 0) throw new CodexHostAdapterError("configuration", "Codex outbox directory is required.");
2291
2821
  this.directory = directory;
2292
- this.entriesDirectory = join2(directory, "entries");
2293
- this.bindingsDirectory = join2(directory, "bindings");
2294
- this.sessionsDirectory = join2(directory, "sessions");
2295
- this.activeContextsDirectory = join2(directory, "active-contexts");
2296
- this.registrationsDirectory = join2(directory, "transcript-registrations");
2297
- this.cursorsDirectory = join2(directory, "transcript-cursors");
2822
+ this.entriesDirectory = join3(directory, "entries");
2823
+ this.bindingsDirectory = join3(directory, "bindings");
2824
+ this.sessionsDirectory = join3(directory, "sessions");
2825
+ this.activeContextsDirectory = join3(directory, "active-contexts");
2826
+ this.turnSelectionsDirectory = join3(directory, "turn-selections");
2827
+ this.registrationsDirectory = join3(directory, "transcript-registrations");
2828
+ this.cursorsDirectory = join3(directory, "transcript-cursors");
2298
2829
  }
2299
2830
  async prepare() {
2300
- for (const path of [this.directory, this.entriesDirectory, this.bindingsDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
2831
+ for (const path of [this.directory, this.entriesDirectory, this.bindingsDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
2301
2832
  await ensurePrivateOwnerDirectory(path);
2302
2833
  }
2303
2834
  }
@@ -2313,7 +2844,7 @@ var CodexOutboxStore = class {
2313
2844
  for (const name of await readdir(this.entriesDirectory)) {
2314
2845
  if (!name.endsWith(".json")) continue;
2315
2846
  try {
2316
- const value = JSON.parse(await readPrivateFile(join2(this.entriesDirectory, name)));
2847
+ const value = JSON.parse(await readPrivateFile(join3(this.entriesDirectory, name)));
2317
2848
  validateEnvelope(value);
2318
2849
  entries.push(value);
2319
2850
  } catch {
@@ -2341,8 +2872,8 @@ var CodexOutboxStore = class {
2341
2872
  * prompt replaces this projection, so it cannot inherit the prior turn's
2342
2873
  * Request by session alone.
2343
2874
  */
2344
- async activateInvocationContext(identity, promptRef, expiresAt) {
2345
- const context = codexInvocationContextFromTurn(identity, promptRef, expiresAt);
2875
+ async activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef) {
2876
+ const context = codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef);
2346
2877
  await this.prepare();
2347
2878
  const path = this.activeContextPath(identity);
2348
2879
  try {
@@ -2356,6 +2887,25 @@ var CodexOutboxStore = class {
2356
2887
  throw error;
2357
2888
  }
2358
2889
  }
2890
+ async writeInvocationTurnSelection(selection) {
2891
+ if (!isHostInvocationTurnSelection(selection)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection is invalid.");
2892
+ await this.prepare();
2893
+ await writeAtomicJson(this.turnSelectionPath(selection.host_source_ref, selection.host_session_ref), selection);
2894
+ }
2895
+ async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
2896
+ if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
2897
+ await this.prepare();
2898
+ return readOptionalJson(this.turnSelectionPath(hostSourceRef, hostSessionRef), validateTurnSelection);
2899
+ }
2900
+ async attachInvocationTurnGeneration(identity, promptRef, turnGenerationRef) {
2901
+ const selection = await this.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
2902
+ if (selection === null || selection.prompt_ref !== promptRef || selection.turn_generation_ref !== turnGenerationRef) return false;
2903
+ const path = this.activeContextPath(identity);
2904
+ const active = await readOptionalJson(path, validateCodexHostInvocationContext);
2905
+ if (active === null || active.prompt_ref !== promptRef || !sameIdentity(active.identity, identity)) return false;
2906
+ await writeAtomicJson(path, { ...active, turn_generation_ref: turnGenerationRef });
2907
+ return true;
2908
+ }
2359
2909
  /**
2360
2910
  * Publish a Request reference only while the acknowledged binding is still
2361
2911
  * the active turn. A delayed relay response therefore cannot overwrite a
@@ -2365,17 +2915,25 @@ var CodexOutboxStore = class {
2365
2915
  validateBinding(binding);
2366
2916
  const active = await this.readInvocationContext(binding.identity.host_source_ref, binding.identity.host_session_ref, now);
2367
2917
  if (active === null || !sameIdentity(active.identity, binding.identity)) return false;
2368
- await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding));
2918
+ await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding, active.turn_generation_ref));
2369
2919
  return true;
2370
2920
  }
2371
- /** Return only the active session projection; never fall back to another turn. */
2921
+ /** Return only the live session projection; never fall back to another turn. */
2372
2922
  async readInvocationContext(hostSourceRef, hostSessionRef, now = /* @__PURE__ */ new Date()) {
2373
- if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
2374
2923
  if (!(now instanceof Date) || !Number.isFinite(now.getTime())) throw new CodexHostAdapterError("invalid_input", "Codex Host context clock is invalid.");
2924
+ const context = await this.readRawInvocationContext(hostSourceRef, hostSessionRef);
2925
+ return isLiveSessionProjection(context, now) && invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
2926
+ }
2927
+ /**
2928
+ * The latest Codex session projection even when past expiry, so the
2929
+ * invocation authenticator can report an expired context distinctly from
2930
+ * a missing one. Every other caller keeps the fail-closed read above.
2931
+ */
2932
+ async readRawInvocationContext(hostSourceRef, hostSessionRef) {
2933
+ if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
2375
2934
  await this.prepare();
2376
- const context = await readOptionalJson(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), validateCodexHostInvocationContext);
2377
- if (context === null || context.identity.host_source_ref !== hostSourceRef || context.identity.host_session_ref !== hostSessionRef) return null;
2378
- return Date.parse(context.expires_at) > now.getTime() ? context : null;
2935
+ const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, (file) => readOptionalJson(file, validateCodexHostInvocationContext));
2936
+ return invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
2379
2937
  }
2380
2938
  /** Clear the projection only if the caller still owns its exact active turn. */
2381
2939
  async clearInvocationContext(identity, now = /* @__PURE__ */ new Date()) {
@@ -2434,7 +2992,7 @@ var CodexOutboxStore = class {
2434
2992
  for (const name of await readdir(this.registrationsDirectory)) {
2435
2993
  if (!name.endsWith(".json")) continue;
2436
2994
  try {
2437
- const value = JSON.parse(await readPrivateFile(join2(this.registrationsDirectory, name)));
2995
+ const value = JSON.parse(await readPrivateFile(join3(this.registrationsDirectory, name)));
2438
2996
  validateRegistration(value);
2439
2997
  registrations.push(value);
2440
2998
  } catch {
@@ -2476,22 +3034,25 @@ var CodexOutboxStore = class {
2476
3034
  }
2477
3035
  entryPath(submissionRef) {
2478
3036
  if (!SAFE_REF4.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
2479
- return join2(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
3037
+ return join3(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
2480
3038
  }
2481
3039
  bindingPath(identity) {
2482
- return join2(this.bindingsDirectory, `${identityKey(identity)}.json`);
3040
+ return join3(this.bindingsDirectory, `${identityKey(identity)}.json`);
2483
3041
  }
2484
3042
  sessionPath(sourceRef, sessionRef) {
2485
- return join2(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
3043
+ return join3(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
2486
3044
  }
2487
3045
  activeContextPath(identity) {
2488
- return join2(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
3046
+ return join3(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
3047
+ }
3048
+ turnSelectionPath(sourceRef, sessionRef) {
3049
+ return join3(this.turnSelectionsDirectory, `${invocationContextKey({ host_source_ref: sourceRef, host_session_ref: sessionRef })}.json`);
2489
3050
  }
2490
3051
  registrationPath(sourceRef, sessionRef) {
2491
- return join2(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
3052
+ return join3(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
2492
3053
  }
2493
3054
  cursorPath(sourceRef, sessionRef) {
2494
- return join2(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
3055
+ return join3(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
2495
3056
  }
2496
3057
  };
2497
3058
  async function removeOwnerState(path, what) {
@@ -2529,7 +3090,7 @@ async function findBindingByAlias(directory, sourceRef, sessionRef, alias) {
2529
3090
  }
2530
3091
  async function readBindingCandidate(directory, name) {
2531
3092
  try {
2532
- const value = JSON.parse(await readPrivateFile(join2(directory, name)));
3093
+ const value = JSON.parse(await readPrivateFile(join3(directory, name)));
2533
3094
  validateBinding(value);
2534
3095
  return value;
2535
3096
  } catch {
@@ -2548,14 +3109,14 @@ async function writeAtomicJson(path, value) {
2548
3109
  if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
2549
3110
  const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
2550
3111
  try {
2551
- const handle = await open3(temp, "wx", 384);
3112
+ const handle = await open4(temp, "wx", 384);
2552
3113
  try {
2553
3114
  await handle.writeFile(text, "utf8");
2554
3115
  await handle.sync();
2555
3116
  } finally {
2556
3117
  await handle.close();
2557
3118
  }
2558
- await rename2(temp, path);
3119
+ await rename3(temp, path);
2559
3120
  await syncDirectory(dirname(path));
2560
3121
  } catch (error) {
2561
3122
  try {
@@ -2568,7 +3129,7 @@ async function writeAtomicJson(path, value) {
2568
3129
  }
2569
3130
  async function syncDirectory(path) {
2570
3131
  try {
2571
- const handle = await open3(path, "r");
3132
+ const handle = await open4(path, "r");
2572
3133
  try {
2573
3134
  await handle.sync();
2574
3135
  } finally {
@@ -2593,7 +3154,7 @@ async function ensurePrivateOwnerDirectory(path) {
2593
3154
  await lstat3(path);
2594
3155
  } catch (error) {
2595
3156
  if (!isNotFound(error)) throw storageError("Codex owner state directory is unavailable.");
2596
- await mkdir2(path, { recursive: true, mode: 448 });
3157
+ await mkdir3(path, { recursive: true, mode: 448 });
2597
3158
  }
2598
3159
  const stats = await lstat3(path);
2599
3160
  const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
@@ -2604,7 +3165,7 @@ async function ensurePrivateOwnerDirectory(path) {
2604
3165
  async function readPrivateFile(path) {
2605
3166
  let handle;
2606
3167
  try {
2607
- handle = await open3(path, O_RDONLY2 | O_NOFOLLOW);
3168
+ handle = await open4(path, O_RDONLY3 | O_NOFOLLOW2);
2608
3169
  const stats = await handle.stat();
2609
3170
  const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
2610
3171
  if (!stats.isFile()) throw storageError(`Codex owner state path is not a regular file (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a regular-file mode (0o100xxx)")}).`);
@@ -2616,7 +3177,7 @@ async function readPrivateFile(path) {
2616
3177
  }
2617
3178
  }
2618
3179
  function validateEnvelope(value) {
2619
- if (!isRecord3(value)) throw storageError("Codex outbox entry is malformed.");
3180
+ if (!isRecord4(value)) throw storageError("Codex outbox entry is malformed.");
2620
3181
  if (!sameKeys(value, ["schema_version", "identity", "host_input_ref", "input_kind", "prompt_ref", "observations", "origin_input", "submission_ref", "created_at", "expires_at"], ["request_ref", "codex_turn_id"])) throw storageError(`Codex outbox entry key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the outbox entry key set")}).`);
2621
3182
  if (value.schema_version !== CODEX_OUTBOX_SCHEMA_VERSION) throw storageError(`Codex outbox entry is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_OUTBOX_SCHEMA_VERSION}`)}).`);
2622
3183
  validateIdentity3(value.identity);
@@ -2634,7 +3195,7 @@ function validateEnvelopeReferences(value) {
2634
3195
  if (typeof value.origin_input !== "boolean") throw storageError("Codex outbox origin disposition is malformed.");
2635
3196
  }
2636
3197
  function validateRegistration(value) {
2637
- if (!isRecord3(value)) throw storageError("Codex transcript registration is malformed.");
3198
+ if (!isRecord4(value)) throw storageError("Codex transcript registration is malformed.");
2638
3199
  if (!sameKeys(value, ["schema_version", "host_source_ref", "host_session_ref", "codex_session_id", "transcript_path", "registered_at", "expires_at"], ["cwd"])) throw storageError(`Codex transcript registration key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the registration key set")}).`);
2639
3200
  if (value.schema_version !== CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION) throw storageError(`Codex transcript registration is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION}`)}).`);
2640
3201
  validateRegistrationFields(value);
@@ -2650,12 +3211,12 @@ function validRegistrationCwd(value) {
2650
3211
  return value === void 0 || typeof value === "string" && isAbsolute(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
2651
3212
  }
2652
3213
  function validateCursor(value) {
2653
- if (!isRecord3(value)) throw storageError("Codex transcript cursor is malformed.");
3214
+ if (!isRecord4(value)) throw storageError("Codex transcript cursor is malformed.");
2654
3215
  if (!sameKeys(value, ["schema_version", "host_source_ref", "host_session_ref", "transcript_path", "device", "inode", "offset", "last_ordinal", "anchor_start", "anchor_sha256", "header_validated", "status", "updated_at"], ["active_task", "reason"])) throw storageError(`Codex transcript cursor key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the cursor key set")}).`);
2655
3216
  if (value.schema_version !== CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION) throw storageError(`Codex transcript cursor is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION}`)}).`);
2656
3217
  if (!validCursorSource(value) || !validCursorPosition(value) || !validCursorStatus(value)) throw storageError("Codex transcript cursor fields are malformed.");
2657
3218
  if (value.active_task !== void 0 && !validActiveTask(value.active_task)) throw storageError("Codex transcript task cursor is malformed.");
2658
- if (value.reason !== void 0 && !isCodexTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
3219
+ if (value.reason !== void 0 && !isHostTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
2659
3220
  }
2660
3221
  function validCursorSource(value) {
2661
3222
  return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate)) && typeof value.transcript_path === "string" && value.transcript_path.length > 0 && typeof value.device === "string" && typeof value.inode === "string";
@@ -2667,7 +3228,7 @@ function validCursorStatus(value) {
2667
3228
  return (value.status === "active" || value.status === "quarantined") && isTimestamp(value.updated_at);
2668
3229
  }
2669
3230
  function validActiveTask(value) {
2670
- if (!isRecord3(value) || !sameKeys(value, ["codex_turn_id", "host_turn_ref", "started_at"], ["origin_host_input_ref", "prompt_ref", "model", "token_usage", "last_total_usage", "token_usage_gap_reason"])) return false;
3231
+ if (!isRecord4(value) || !sameKeys(value, ["codex_turn_id", "host_turn_ref", "started_at"], ["origin_host_input_ref", "prompt_ref", "model", "token_usage", "last_total_usage", "token_usage_gap_reason"])) return false;
2671
3232
  return validActiveTaskIdentity(value) && validActiveTaskReferences(value) && validActiveTaskUsage(value);
2672
3233
  }
2673
3234
  function validActiveTaskIdentity(value) {
@@ -2683,7 +3244,7 @@ function validActiveTaskUsage(value) {
2683
3244
  return (value.token_usage === void 0 || validTokenUsage(value.token_usage)) && (value.last_total_usage === void 0 || validTokenUsage(value.last_total_usage)) && (value.token_usage_gap_reason === void 0 || value.token_usage_gap_reason === "token_count_shape_not_attributable");
2684
3245
  }
2685
3246
  function validTokenUsage(value) {
2686
- if (!isRecord3(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
3247
+ if (!isRecord4(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
2687
3248
  if (![value.input_tokens, value.cached_input_tokens, value.cache_write_input_tokens, value.output_tokens, value.reasoning_output_tokens, value.total_tokens].every(nonNegativeInteger)) return false;
2688
3249
  return value.total_tokens === Number(value.input_tokens) + Number(value.output_tokens) && Number(value.cached_input_tokens) + Number(value.cache_write_input_tokens) <= Number(value.input_tokens) && Number(value.reasoning_output_tokens) <= Number(value.output_tokens);
2689
3250
  }
@@ -2703,7 +3264,7 @@ function validateEnvelopeTimestamps(value) {
2703
3264
  if (!isTimestamp(value.expires_at)) throw storageError("Codex outbox timestamps are malformed.");
2704
3265
  }
2705
3266
  function validateBinding(value) {
2706
- if (!isRecord3(value)) throw storageError("Codex turn binding is malformed.");
3267
+ if (!isRecord4(value)) throw storageError("Codex turn binding is malformed.");
2707
3268
  if (!sameKeys(value, ["schema_version", "identity", "prompt_ref", "request_ref", "codex_turn_ids", "bound_at", "expires_at"])) throw storageError(`Codex turn binding key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the turn binding key set")}).`);
2708
3269
  if (value.schema_version !== CODEX_TURN_BINDING_SCHEMA_VERSION) throw storageError(`Codex turn binding is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_TURN_BINDING_SCHEMA_VERSION}`)}).`);
2709
3270
  validateIdentity3(value.identity);
@@ -2711,19 +3272,22 @@ function validateBinding(value) {
2711
3272
  if (!isStringList(value.codex_turn_ids) || !isTimestamp(value.bound_at) || !isTimestamp(value.expires_at)) throw storageError("Codex turn binding fields are malformed.");
2712
3273
  }
2713
3274
  function validateSession(value) {
2714
- if (!isRecord3(value)) throw storageError("Codex session snapshot is malformed.");
3275
+ if (!isRecord4(value)) throw storageError("Codex session snapshot is malformed.");
2715
3276
  if (!sameKeys(value, ["schema_version", "host_session_ref", "host_source_ref", "observed_at", "value", "expires_at"])) throw storageError(`Codex session snapshot key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the session snapshot key set")}).`);
2716
3277
  if (value.schema_version !== CODEX_SESSION_SCHEMA_VERSION) throw storageError(`Codex session snapshot is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_SESSION_SCHEMA_VERSION}`)}).`);
2717
3278
  if (!SAFE_REF4.test(String(value.host_session_ref)) || !SAFE_REF4.test(String(value.host_source_ref)) || !isTimestamp(value.observed_at) || !isTimestamp(value.expires_at) || !isJsonValue(value.value)) throw storageError("Codex session snapshot fields are malformed.");
2718
3279
  }
3280
+ function validateTurnSelection(value) {
3281
+ if (!isHostInvocationTurnSelection(value)) throw storageError("Codex Host turn selection is malformed.");
3282
+ }
2719
3283
  function validateIdentity3(value) {
2720
- if (!isRecord3(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate))) throw storageError("Codex Host identity is malformed.");
3284
+ if (!isRecord4(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate))) throw storageError("Codex Host identity is malformed.");
2721
3285
  }
2722
3286
  function isJsonValue(value) {
2723
3287
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
2724
3288
  if (typeof value === "number") return Number.isFinite(value);
2725
3289
  if (Array.isArray(value)) return value.every(isJsonValue);
2726
- return isRecord3(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
3290
+ return isRecord4(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
2727
3291
  }
2728
3292
  function isStringList(value) {
2729
3293
  return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate));
@@ -2736,101 +3300,49 @@ function sameKeys(value, required, optional = []) {
2736
3300
  const keys = Object.keys(value);
2737
3301
  return keys.length >= required.length && keys.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
2738
3302
  }
2739
- function isRecord3(value) {
3303
+ function isRecord4(value) {
2740
3304
  return value !== null && typeof value === "object" && !Array.isArray(value);
2741
3305
  }
2742
3306
  function isNotFound(error) {
2743
- return isRecord3(error) && error.code === "ENOENT";
3307
+ return isRecord4(error) && error.code === "ENOENT";
2744
3308
  }
2745
3309
  function storageError(message) {
2746
3310
  return new CodexHostAdapterError("storage", message);
2747
3311
  }
2748
3312
 
2749
3313
  // host/codex/src/codex-producer.ts
2750
- var STATE_SCHEMA = "ceal.codex_producer_state.v1";
2751
3314
  var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2752
3315
  var CodexProducer = class {
3316
+ producer_id = "codex";
3317
+ producer_label = "Codex";
3318
+ outbox_directory;
2753
3319
  store;
2754
- statePath;
2755
- installationRef;
2756
- epoch;
2757
- relay;
2758
- now;
2759
- tail = Promise.resolve();
3320
+ core;
2760
3321
  constructor(options) {
2761
- if (!SAFE_REF5.test(options.producer_installation_ref)) throw new CodexHostAdapterError("configuration", "Codex producer installation identity is invalid.");
2762
- const epoch = options.producer_epoch ?? 1;
2763
- if (!Number.isSafeInteger(epoch) || epoch <= 0) throw new CodexHostAdapterError("configuration", "Codex producer epoch is invalid.");
2764
3322
  this.store = new CodexOutboxStore(options.outbox_dir);
2765
- this.statePath = join3(options.outbox_dir, "codex-producer-state.json");
2766
- this.installationRef = options.producer_installation_ref;
2767
- this.epoch = epoch;
2768
- this.relay = options.relay;
2769
- this.now = options.now ?? (() => /* @__PURE__ */ new Date());
3323
+ this.outbox_directory = this.store.directory;
3324
+ this.core = new HostProducerDrainCore(this, options);
2770
3325
  }
2771
3326
  drainOutbox() {
2772
- const run = this.tail.then(() => this.drainLocked());
2773
- this.tail = run.then(() => void 0, () => void 0);
2774
- return run;
3327
+ return this.core.drainOutbox();
2775
3328
  }
2776
- async drainLocked() {
2777
- const read = await this.store.list();
2778
- let admitted = 0;
2779
- let expired = 0;
2780
- let retained = 0;
2781
- for (const envelope of read.entries) {
2782
- const reserved = await this.hasReservation(envelope.submission_ref);
2783
- if (!reserved && Date.parse(envelope.expires_at) <= this.now().getTime()) {
2784
- await this.store.remove(envelope);
2785
- expired += 1;
2786
- continue;
2787
- }
2788
- const frames = await this.reserve(envelope);
2789
- let complete = true;
2790
- for (const frame of frames) {
2791
- const outcome = await this.relay.admit(frame);
2792
- if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
2793
- complete = false;
2794
- break;
2795
- }
2796
- }
2797
- if (!complete) {
2798
- retained += 1;
2799
- break;
2800
- }
2801
- await this.store.remove(envelope);
2802
- await this.release(envelope.submission_ref);
2803
- admitted += 1;
2804
- }
2805
- return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
3329
+ fail(code, message) {
3330
+ return new CodexHostAdapterError(code, message);
2806
3331
  }
2807
- async hasReservation(submissionRef) {
2808
- return (await this.readState()).pending[submissionRef] !== void 0;
3332
+ listEnvelopes() {
3333
+ return this.store.list();
2809
3334
  }
2810
- async reserve(envelope) {
2811
- const state = await this.readState();
2812
- const existing = state.pending[envelope.submission_ref];
2813
- if (existing !== void 0) return existing;
2814
- const drafts = await this.mapEnvelope(envelope);
2815
- const frames = drafts.map((draft, offset) => ({
2816
- schema_version: "ceal.host_producer_frame.v1",
2817
- producer_id: "codex",
2818
- producer_installation_ref: this.installationRef,
2819
- producer_epoch: this.epoch,
2820
- sequence: state.next_sequence + offset,
2821
- frame_kind: draft.frame_kind,
2822
- occurred_at: envelope.created_at,
2823
- payload: draft.payload
2824
- }));
2825
- await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [envelope.submission_ref]: frames } });
2826
- return frames;
3335
+ removeEnvelope(envelope) {
3336
+ return this.store.remove(envelope);
2827
3337
  }
2828
- async release(submissionRef) {
2829
- const state = await this.readState();
2830
- if (state.pending[submissionRef] === void 0) return;
2831
- const pending = { ...state.pending };
2832
- delete pending[submissionRef];
2833
- await this.writeState({ ...state, pending });
3338
+ submissionRefOf(envelope) {
3339
+ return envelope.submission_ref;
3340
+ }
3341
+ expiresAtOf(envelope) {
3342
+ return envelope.expires_at;
3343
+ }
3344
+ createdAtOf(envelope) {
3345
+ return envelope.created_at;
2834
3346
  }
2835
3347
  async mapEnvelope(envelope) {
2836
3348
  const prompt = observation(envelope, "prompt");
@@ -2846,7 +3358,7 @@ var CodexProducer = class {
2846
3358
  const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
2847
3359
  drafts.push({ frame_kind: "session_started", payload: compact({
2848
3360
  local_session_ref: envelope.identity.host_session_ref,
2849
- working_directory_ref: `workspace:${createHash3("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
3361
+ working_directory_ref: `workspace:${createHash4("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
2850
3362
  model: recordText(session.value, "model"),
2851
3363
  host_version: nestedText(session.value, "host_version"),
2852
3364
  observations: [session]
@@ -2871,21 +3383,6 @@ var CodexProducer = class {
2871
3383
  }) });
2872
3384
  return drafts;
2873
3385
  }
2874
- async readState() {
2875
- try {
2876
- const value = JSON.parse(await readFile(this.statePath, "utf8"));
2877
- return decodeState(value, this.installationRef, this.epoch);
2878
- } catch (error) {
2879
- if (errorCode2(error) !== "ENOENT") throw error;
2880
- return { schema_version: STATE_SCHEMA, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
2881
- }
2882
- }
2883
- async writeState(state) {
2884
- await mkdir3(this.store.directory, { recursive: true, mode: 448 });
2885
- const temporary = `${this.statePath}.${process.pid}.new`;
2886
- await writeFile(temporary, JSON.stringify(state), { mode: 384 });
2887
- await rename3(temporary, this.statePath);
2888
- }
2889
3386
  };
2890
3387
  function mapTerminal(envelope) {
2891
3388
  const timing = observation(envelope, "timing");
@@ -2913,7 +3410,7 @@ function compact(value) {
2913
3410
  return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
2914
3411
  }
2915
3412
  function asRecord(value) {
2916
- return isRecord4(value) ? value : void 0;
3413
+ return isRecord5(value) ? value : void 0;
2917
3414
  }
2918
3415
  function recordText(value, key) {
2919
3416
  const record3 = asRecord(value);
@@ -2927,55 +3424,16 @@ function nonNegativeNumber(value) {
2927
3424
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
2928
3425
  }
2929
3426
  function safeReasonRef(value) {
2930
- return SAFE_REF5.test(value) ? value : `reason:${createHash3("sha256").update(value, "utf8").digest("hex")}`;
3427
+ return SAFE_REF5.test(value) ? value : `reason:${createHash4("sha256").update(value, "utf8").digest("hex")}`;
2931
3428
  }
2932
- function errorCode2(error) {
2933
- return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2934
- }
2935
- function decodeState(value, installationRef, epoch) {
2936
- if (!validStateHeader(value, installationRef)) {
2937
- throw new CodexHostAdapterError("storage", `Codex producer state header is malformed or belongs to another installation (${refusalFacts([
2938
- { name: "schema_version", value: isRecord4(value) ? value.schema_version : void 0, expected: STATE_SCHEMA },
2939
- { name: "producer_installation_ref", value: isRecord4(value) ? value.producer_installation_ref : void 0, expected: installationRef }
2940
- ])}).`);
2941
- }
2942
- if (Number(value.producer_epoch) > epoch) throw new CodexHostAdapterError("storage", `Codex producer state epoch is ahead of this installation (${refusalFact("producer_epoch", value.producer_epoch, `at most ${epoch}`)}).`);
2943
- const pending = {};
2944
- for (const [submissionRef, frames] of Object.entries(value.pending)) {
2945
- if (!SAFE_REF5.test(submissionRef) || !Array.isArray(frames) || frames.length === 0) throw new CodexHostAdapterError("storage", "Codex producer pending reservation is malformed.");
2946
- pending[submissionRef] = frames.map((frame) => decodeStoredFrame(frame, installationRef, Number(value.producer_epoch)));
2947
- }
2948
- return { schema_version: STATE_SCHEMA, producer_installation_ref: installationRef, producer_epoch: epoch, next_sequence: Number(value.next_sequence), pending };
2949
- }
2950
- function validStateHeader(value, installationRef) {
2951
- return isRecord4(value) && value.schema_version === STATE_SCHEMA && value.producer_installation_ref === installationRef && Number.isSafeInteger(value.producer_epoch) && Number(value.producer_epoch) > 0 && Number.isSafeInteger(value.next_sequence) && Number(value.next_sequence) > 0 && isRecord4(value.pending);
2952
- }
2953
- function decodeStoredFrame(value, installationRef, epoch) {
2954
- const json = Buffer.from(JSON.stringify(value), "utf8");
2955
- const encoded = Buffer.allocUnsafe(json.length + 4);
2956
- encoded.writeUInt32BE(json.length, 0);
2957
- json.copy(encoded, 4);
2958
- const frame = decodeHostProducerFrame(encoded);
2959
- if (frame.producer_id !== "codex" || frame.producer_installation_ref !== installationRef || frame.producer_epoch > epoch) throw new CodexHostAdapterError("storage", `Codex producer pending frame identity is malformed (${refusalFacts([
2960
- { name: "producer_id", value: frame.producer_id, expected: "codex" },
2961
- { name: "producer_installation_ref", value: frame.producer_installation_ref, expected: installationRef },
2962
- { name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${epoch}` }
2963
- ])}).`);
2964
- return frame;
2965
- }
2966
- function isRecord4(value) {
3429
+ function isRecord5(value) {
2967
3430
  return value !== null && typeof value === "object" && !Array.isArray(value);
2968
3431
  }
2969
3432
 
2970
- // host/codex/src/codex-transcript-watcher.ts
2971
- import { createHash as createHash6 } from "node:crypto";
2972
- import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
2973
- import { open as open4 } from "node:fs/promises";
2974
-
2975
3433
  // host/codex/src/codex-decoder.ts
2976
- import { createHash as createHash4 } from "node:crypto";
3434
+ import { createHash as createHash5 } from "node:crypto";
2977
3435
  var SAFE_REF6 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2978
- var MAX_PAYLOAD_BYTES = 64 * 1024;
3436
+ var MAX_PAYLOAD_BYTES = 512 * 1024;
2979
3437
  function observationRef(event, identity, value) {
2980
3438
  return cealHostObservationRef("codex", shortDigest({ event, identity, value }));
2981
3439
  }
@@ -2996,14 +3454,14 @@ function turnObservationValue(sessionId, turnRef, status) {
2996
3454
  return { status, host_session_id_sha256: sha256(sessionId), host_turn_ref: turnRef };
2997
3455
  }
2998
3456
  function sha256(value) {
2999
- return createHash4("sha256").update(value, "utf8").digest("hex");
3457
+ return createHash5("sha256").update(value, "utf8").digest("hex");
3000
3458
  }
3001
3459
  function shortDigest(value) {
3002
3460
  return cealRequestSha256(value);
3003
3461
  }
3004
3462
 
3005
3463
  // host/codex/src/codex-transcript-baseline.ts
3006
- import { createHash as createHash5 } from "node:crypto";
3464
+ import { createHash as createHash6 } from "node:crypto";
3007
3465
 
3008
3466
  // host/codex/src/codex-transcript-decoder.ts
3009
3467
  var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
@@ -3015,40 +3473,40 @@ function decodeCodexTranscriptLine(line) {
3015
3473
  } catch {
3016
3474
  reject("Codex transcript record is not valid JSON.");
3017
3475
  }
3018
- if (!isRecord5(value)) reject("Codex transcript record envelope is malformed.");
3476
+ if (!isRecord6(value)) reject("Codex transcript record envelope is malformed.");
3019
3477
  const ordinal = safeOrdinal(value.ordinal);
3020
- const timestamp5 = safeTimestamp(value.timestamp);
3021
- if (!isRecord5(value.payload) || typeof value.type !== "string") reject("Codex transcript record payload is malformed.");
3022
- if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal, timestamp5);
3023
- if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal, timestamp5);
3024
- if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal, timestamp5);
3025
- if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal, timestamp5);
3026
- return { kind: "ignored", ordinal, timestamp: timestamp5 };
3027
- }
3028
- function decodeTurnContext(payload, ordinal, timestamp5) {
3478
+ const timestamp4 = safeTimestamp(value.timestamp);
3479
+ if (!isRecord6(value.payload) || typeof value.type !== "string") reject("Codex transcript record payload is malformed.");
3480
+ if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal, timestamp4);
3481
+ if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal, timestamp4);
3482
+ if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal, timestamp4);
3483
+ if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal, timestamp4);
3484
+ return { kind: "ignored", ordinal, timestamp: timestamp4 };
3485
+ }
3486
+ function decodeTurnContext(payload, ordinal, timestamp4) {
3029
3487
  return {
3030
3488
  kind: "turn_context",
3031
3489
  ordinal,
3032
- timestamp: timestamp5,
3033
- turn_id: safeRef3(payload.turn_id, "Codex turn context identity is invalid."),
3490
+ timestamp: timestamp4,
3491
+ turn_id: safeRef4(payload.turn_id, "Codex turn context identity is invalid."),
3034
3492
  model: boundedNonEmptyText(payload.model, 256, "Codex turn context model is invalid.")
3035
3493
  };
3036
3494
  }
3037
- function decodeSessionMeta(payload, ordinal, timestamp5) {
3038
- const sessionId = safeRef3(payload.id, "Codex transcript session identity is invalid.");
3495
+ function decodeSessionMeta(payload, ordinal, timestamp4) {
3496
+ const sessionId = safeRef4(payload.id, "Codex transcript session identity is invalid.");
3039
3497
  const cliVersion = boundedNonEmptyText(payload.cli_version, 64, "Codex transcript version is invalid.");
3040
- return { kind: "session_meta", ordinal, timestamp: timestamp5, session_id: sessionId, cli_version: cliVersion };
3498
+ return { kind: "session_meta", ordinal, timestamp: timestamp4, session_id: sessionId, cli_version: cliVersion };
3041
3499
  }
3042
- function decodeEventMessage(payload, ordinal, timestamp5) {
3500
+ function decodeEventMessage(payload, ordinal, timestamp4) {
3043
3501
  if (payload.type === "task_started") {
3044
- return { kind: "task_started", ordinal, timestamp: timestamp5, turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."), started_at: safeNumber(payload.started_at) };
3502
+ return { kind: "task_started", ordinal, timestamp: timestamp4, turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."), started_at: safeNumber(payload.started_at) };
3045
3503
  }
3046
3504
  if (payload.type === "task_complete") {
3047
3505
  return {
3048
3506
  kind: "task_complete",
3049
3507
  ordinal,
3050
- timestamp: timestamp5,
3051
- turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."),
3508
+ timestamp: timestamp4,
3509
+ turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
3052
3510
  started_at: safeNumber(payload.started_at),
3053
3511
  completed_at: safeNumber(payload.completed_at),
3054
3512
  duration_ms: safeNumber(payload.duration_ms),
@@ -3059,20 +3517,20 @@ function decodeEventMessage(payload, ordinal, timestamp5) {
3059
3517
  return {
3060
3518
  kind: "turn_aborted",
3061
3519
  ordinal,
3062
- timestamp: timestamp5,
3063
- turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."),
3520
+ timestamp: timestamp4,
3521
+ turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
3064
3522
  started_at: safeNumber(payload.started_at),
3065
3523
  completed_at: safeNumber(payload.completed_at),
3066
3524
  duration_ms: safeNumber(payload.duration_ms),
3067
3525
  reason: boundedText(payload.reason, 256, "Codex task abort reason is invalid.")
3068
3526
  };
3069
3527
  }
3070
- if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp5);
3071
- return { kind: "ignored", ordinal, timestamp: timestamp5 };
3528
+ if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp4);
3529
+ return { kind: "ignored", ordinal, timestamp: timestamp4 };
3072
3530
  }
3073
- function decodeTokenCount(payload, ordinal, timestamp5) {
3074
- if (payload.info === null) return { kind: "token_count", ordinal, timestamp: timestamp5 };
3075
- if (!isRecord5(payload.info)) return { kind: "token_count", ordinal, timestamp: timestamp5, usage_gap_reason: "token_count_shape_not_attributable" };
3531
+ function decodeTokenCount(payload, ordinal, timestamp4) {
3532
+ if (payload.info === null) return { kind: "token_count", ordinal, timestamp: timestamp4 };
3533
+ if (!isRecord6(payload.info)) return { kind: "token_count", ordinal, timestamp: timestamp4, usage_gap_reason: "token_count_shape_not_attributable" };
3076
3534
  const lastUsage = optionalTokenUsage(payload.info.last_token_usage);
3077
3535
  const totalUsage = optionalTokenUsage(payload.info.total_token_usage);
3078
3536
  const gap = lastUsage.status === "invalid" || totalUsage.status === "invalid";
@@ -3080,7 +3538,7 @@ function decodeTokenCount(payload, ordinal, timestamp5) {
3080
3538
  return {
3081
3539
  kind: "token_count",
3082
3540
  ordinal,
3083
- timestamp: timestamp5,
3541
+ timestamp: timestamp4,
3084
3542
  ...lastUsage.status === "valid" ? { last_usage: lastUsage.value } : {},
3085
3543
  ...totalUsage.status === "valid" ? { total_usage: totalUsage.value } : {},
3086
3544
  ...usageGapReason === void 0 ? {} : { usage_gap_reason: usageGapReason }
@@ -3096,7 +3554,7 @@ function optionalTokenUsage(value) {
3096
3554
  }
3097
3555
  }
3098
3556
  function decodeTokenUsage(value) {
3099
- if (!isRecord5(value)) reject("Codex token usage is malformed.");
3557
+ if (!isRecord6(value)) reject("Codex token usage is malformed.");
3100
3558
  const usage = {
3101
3559
  input_tokens: safeTokenCount(value.input_tokens),
3102
3560
  cached_input_tokens: safeTokenCount(value.cached_input_tokens),
@@ -3108,20 +3566,20 @@ function decodeTokenUsage(value) {
3108
3566
  if (usage.total_tokens !== usage.input_tokens + usage.output_tokens || usage.cached_input_tokens + usage.cache_write_input_tokens > usage.input_tokens || usage.reasoning_output_tokens > usage.output_tokens) reject("Codex token usage buckets are inconsistent.");
3109
3567
  return usage;
3110
3568
  }
3111
- function decodeResponseItem(payload, ordinal, timestamp5) {
3112
- if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp5 };
3569
+ function decodeResponseItem(payload, ordinal, timestamp4) {
3570
+ if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp4 };
3113
3571
  const metadata = userMessageMetadata(payload.internal_chat_message_metadata_passthrough);
3114
3572
  const inputKind = inputKindFromContentItems(metadata.content_item_kinds);
3115
- if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp5 };
3573
+ if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp4 };
3116
3574
  const prompt = userTextContent(payload.content);
3117
3575
  return {
3118
3576
  kind: "user_text",
3119
3577
  ordinal,
3120
- timestamp: timestamp5,
3578
+ timestamp: timestamp4,
3121
3579
  prompt,
3122
3580
  input_kind: inputKind,
3123
- message_id: safeRef3(payload.id, "Codex user message identity is invalid."),
3124
- turn_id: safeRef3(metadata.turn_id, "Codex user message task identity is invalid.")
3581
+ message_id: safeRef4(payload.id, "Codex user message identity is invalid."),
3582
+ turn_id: safeRef4(metadata.turn_id, "Codex user message task identity is invalid.")
3125
3583
  };
3126
3584
  }
3127
3585
  function inputKindFromContentItems(kinds) {
@@ -3130,13 +3588,13 @@ function inputKindFromContentItems(kinds) {
3130
3588
  return kinds[0] === "goal.internal_context" ? "host_automatic" : void 0;
3131
3589
  }
3132
3590
  function userMessageMetadata(value) {
3133
- if (!isRecord5(value) || !Array.isArray(value.content_item_kinds)) reject("Codex user message provenance is malformed.");
3591
+ if (!isRecord6(value) || !Array.isArray(value.content_item_kinds)) reject("Codex user message provenance is malformed.");
3134
3592
  return { ...value, content_item_kinds: value.content_item_kinds };
3135
3593
  }
3136
3594
  function userTextContent(value) {
3137
3595
  if (!Array.isArray(value)) reject(`Codex user text content is malformed (${refusalFact("kind", typeof value, "array")}).`);
3138
3596
  if (value.length !== 1) reject(`Codex user text content is malformed (${refusalFact("items", value.length, "exactly one")}).`);
3139
- if (!isRecord5(value[0]) || value[0].type !== "input_text") reject(`Codex user text content is malformed (${refusalFact("item_type", isRecord5(value[0]) ? value[0].type : typeof value[0], "input_text")}).`);
3597
+ if (!isRecord6(value[0]) || value[0].type !== "input_text") reject(`Codex user text content is malformed (${refusalFact("item_type", isRecord6(value[0]) ? value[0].type : typeof value[0], "input_text")}).`);
3140
3598
  return boundedText(value[0].text, MAX_PROMPT_BYTES, "Codex user text is invalid.");
3141
3599
  }
3142
3600
  function safeOrdinal(value) {
@@ -3157,7 +3615,7 @@ function safeTimestamp(value) {
3157
3615
  if (!Number.isFinite(Date.parse(value))) reject(`Codex transcript timestamp is invalid (${refusalFact("parsed_ms", Date.parse(value), "a finite epoch milliseconds value")}).`);
3158
3616
  return value;
3159
3617
  }
3160
- function safeRef3(value, message) {
3618
+ function safeRef4(value, message) {
3161
3619
  if (typeof value !== "string" || !SAFE_REF7.test(value)) reject(message);
3162
3620
  return value;
3163
3621
  }
@@ -3172,7 +3630,7 @@ function boundedNonEmptyText(value, maximum, message) {
3172
3630
  if (result.length === 0) reject(message);
3173
3631
  return result;
3174
3632
  }
3175
- function isRecord5(value) {
3633
+ function isRecord6(value) {
3176
3634
  return value !== null && typeof value === "object" && !Array.isArray(value);
3177
3635
  }
3178
3636
  function reject(message) {
@@ -3216,7 +3674,7 @@ function assertCodexTranscriptHeader(registration, record3) {
3216
3674
  ])}).`);
3217
3675
  }
3218
3676
  function digest(value) {
3219
- return createHash5("sha256").update(value).digest("hex");
3677
+ return createHash6("sha256").update(value).digest("hex");
3220
3678
  }
3221
3679
  function timestamp3(now) {
3222
3680
  const value = now();
@@ -3232,78 +3690,44 @@ function codexSubmissionReference(hostInputRef, event, ordinal) {
3232
3690
  }
3233
3691
 
3234
3692
  // host/codex/src/codex-transcript-watcher.ts
3235
- var READ_CHUNK_BYTES = 256 * 1024;
3236
- var TRANSCRIPT_MISSING_REASON = "transcript_missing";
3237
- var CodexTranscriptWatcher = class {
3693
+ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
3238
3694
  store;
3239
- outboxRetentionMs;
3240
- now;
3241
- log;
3242
- producerInstallationRef;
3243
3695
  registerCurrentPrompt;
3244
3696
  constructor(options) {
3245
- if (typeof options.outbox_dir !== "string" || options.outbox_dir.length === 0) throw new CodexHostAdapterError("configuration", "Codex transcript watcher outbox is invalid.");
3246
- this.store = new CodexOutboxStore(options.outbox_dir);
3247
- this.outboxRetentionMs = options.retention_ms ?? CODEX_DEFAULT_RETENTION_MS;
3248
- this.now = options.now ?? (() => /* @__PURE__ */ new Date());
3249
- this.log = options.log ?? ((line) => {
3250
- process.stderr.write(`${line}
3251
- `);
3697
+ super(options, {
3698
+ default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
3699
+ invalid_outbox: () => new CodexHostAdapterError("configuration", "Codex transcript watcher outbox is invalid."),
3700
+ clock_failure: () => new CodexHostAdapterError("storage", "Codex watcher clock is invalid.")
3252
3701
  });
3253
- this.producerInstallationRef = options.producer_installation_ref;
3702
+ this.store = new CodexOutboxStore(options.outbox_dir);
3254
3703
  this.registerCurrentPrompt = options.register_current_prompt;
3255
3704
  }
3256
- async scanOnce() {
3257
- const registrations = await this.store.listTranscriptRegistrations();
3258
- let records = 0;
3259
- let enqueued = 0;
3260
- let quarantined = 0;
3261
- for (const registration of registrations) {
3262
- const result = await this.scanRegistration(registration);
3263
- records += result.records;
3264
- enqueued += result.enqueued;
3265
- if (result.quarantined) quarantined += 1;
3266
- }
3267
- return { registrations: registrations.length, records, enqueued, quarantined };
3705
+ transcriptPathOf(registration) {
3706
+ return registration.transcript_path;
3268
3707
  }
3269
- async scanRegistration(registration) {
3270
- let handle;
3271
- try {
3272
- handle = await open4(registration.transcript_path, O_RDONLY3 | O_NOFOLLOW2);
3273
- const { size, device, inode } = await ownedCodexTranscriptFile(handle);
3274
- const preflight = await this.preflight(registration, handle, size, device, inode);
3275
- if (preflight.cursor === null) return preflight.result;
3276
- const complete = await readCompleteChunk(handle, preflight.cursor, size);
3277
- if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
3278
- return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
3279
- } catch (error) {
3280
- if (handle === void 0 && isMissingTranscript(error)) return await this.retireRegistration(registration);
3281
- await this.quarantineFailure(registration, handle);
3282
- return { records: 0, enqueued: 0, quarantined: true };
3283
- } finally {
3284
- await handle?.close();
3285
- }
3708
+ async listRegistrations() {
3709
+ return this.store.listTranscriptRegistrations();
3286
3710
  }
3287
- /**
3288
- * A transcript the Host deleted never comes back, so retrying its
3289
- * registration once per reconcile interval is a failure with no end: a
3290
- * long-lived installation collects one permanently failing registration for
3291
- * every session Codex or the operator removes. Retire it at the scan that
3292
- * finds it missing and say so once, so the removal is readable and the next
3293
- * scan has nothing left to retry. Only ENOENT means gone; a permission or
3294
- * busy failure is about this moment, not about the file, and keeps the
3295
- * registration for the next scan.
3296
- */
3297
- async retireRegistration(registration) {
3298
- await this.store.retireTranscriptRegistration(registration);
3299
- this.log(`Codex retired a transcript registration: transcript_path=${JSON.stringify(registration.transcript_path)}, reason=${TRANSCRIPT_MISSING_REASON}.`);
3300
- return { records: 0, enqueued: 0, quarantined: false };
3711
+ async scanOpened(handle, registration) {
3712
+ const { size, device, inode } = await ownedCodexTranscriptFile(handle);
3713
+ const preflight = await this.preflight(registration, handle, size, device, inode);
3714
+ if (preflight.cursor === null) return preflight.result;
3715
+ const complete = await this.readChunk(handle, preflight.cursor.offset, size);
3716
+ if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
3717
+ return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
3718
+ }
3719
+ async scanFailed(handle, registration, _error) {
3720
+ await this.quarantineFailure(registration, handle);
3721
+ return { records: 0, enqueued: 0, quarantined: true };
3722
+ }
3723
+ async retireMissing(registration) {
3724
+ return this.retireMissingTranscript(registration.transcript_path, "Codex", () => this.store.retireTranscriptRegistration(registration));
3301
3725
  }
3302
3726
  async preflight(registration, handle, size, device, inode) {
3303
3727
  const existing = await this.store.readTranscriptCursor(registration);
3304
3728
  let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
3305
3729
  if (cursor.status === "quarantined") return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
3306
- const reason = await cursorFailureReason(registration, handle, cursor, size, device, inode);
3730
+ const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
3307
3731
  if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
3308
3732
  await this.quarantine(registration, cursor, device, inode, reason);
3309
3733
  return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
@@ -3329,14 +3753,12 @@ var CodexTranscriptWatcher = class {
3329
3753
  async observeSessionMetadata(registration, record3) {
3330
3754
  if (record3.kind !== "session_meta") return;
3331
3755
  const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
3332
- if (session !== null && isRecord6(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record3.cli_version } });
3756
+ if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record3.cli_version } });
3333
3757
  }
3334
3758
  async commitCursor(registration, handle, cursor, consumedBytes, device, inode, lastOrdinal, activeTask) {
3335
3759
  const offset = cursor.offset + consumedBytes;
3336
- const anchorStart = Math.max(0, offset - CODEX_TRANSCRIPT_ANCHOR_BYTES);
3337
- const anchor = Buffer.alloc(offset - anchorStart);
3338
- if (anchor.length > 0) await handle.read(anchor, 0, anchor.length, anchorStart);
3339
- await this.store.writeTranscriptCursor({ schema_version: "ceal.codex_transcript_cursor.v1", host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, transcript_path: registration.transcript_path, device, inode, offset, last_ordinal: lastOrdinal, anchor_start: anchorStart, anchor_sha256: sha2562(anchor), header_validated: true, ...activeTask === void 0 ? {} : { active_task: activeTask }, status: "active", updated_at: timestamp4(this.now) });
3760
+ const anchor = await this.readAnchor(handle, offset, CODEX_TRANSCRIPT_ANCHOR_BYTES);
3761
+ await this.store.writeTranscriptCursor({ schema_version: "ceal.codex_transcript_cursor.v1", host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, transcript_path: registration.transcript_path, device, inode, offset, last_ordinal: lastOrdinal, anchor_start: anchor.start, anchor_sha256: anchor.sha256, header_validated: true, ...activeTask === void 0 ? {} : { active_task: activeTask }, status: "active", updated_at: this.stamp() });
3340
3762
  }
3341
3763
  async quarantineFailure(registration, handle) {
3342
3764
  if (handle === void 0) return;
@@ -3385,11 +3807,11 @@ var CodexTranscriptWatcher = class {
3385
3807
  origin_input: origin,
3386
3808
  submission_ref: codexSubmissionReference(hostInputRef, "input", record3.ordinal),
3387
3809
  created_at: record3.timestamp,
3388
- expires_at: expiration(this.now, this.outboxRetentionMs)
3810
+ expires_at: this.expire(this.outboxRetentionMs)
3389
3811
  };
3390
3812
  await this.registerPrompt(origin, registration, activeTask, currentPromptRef, record3.prompt, envelope.expires_at);
3391
3813
  await this.store.put(envelope);
3392
- if (origin) await this.store.activateInvocationContext(identity, promptRef, envelope.expires_at);
3814
+ if (origin) await activateTranscriptTurnContext(this.store, identity, promptRef, envelope.expires_at);
3393
3815
  return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
3394
3816
  }
3395
3817
  async registerPrompt(origin, registration, activeTask, promptRef, prompt, expiresAt) {
@@ -3412,16 +3834,21 @@ var CodexTranscriptWatcher = class {
3412
3834
  origin_input: false,
3413
3835
  submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record3.ordinal),
3414
3836
  created_at: record3.timestamp,
3415
- expires_at: expiration(this.now, this.outboxRetentionMs)
3837
+ expires_at: this.expire(this.outboxRetentionMs)
3416
3838
  });
3417
3839
  await this.store.clearInvocationContext(identity, this.now());
3418
3840
  return { enqueued: 1 };
3419
3841
  }
3420
3842
  async quarantine(registration, cursor, device, inode, reason) {
3421
- const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at: timestamp4(this.now) };
3843
+ const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at: this.stamp() };
3422
3844
  await this.store.writeTranscriptCursor(quarantined);
3423
3845
  }
3424
3846
  };
3847
+ async function activateTranscriptTurnContext(store, identity, promptRef, expiresAt) {
3848
+ const selection = await store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
3849
+ const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
3850
+ await store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
3851
+ }
3425
3852
  function accumulateTokenCount(activeTask, record3) {
3426
3853
  if (record3.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record3.usage_gap_reason };
3427
3854
  const last = record3.last_usage;
@@ -3455,19 +3882,18 @@ function terminalCostValue() {
3455
3882
  return notRecorded("provider_cost_not_observed");
3456
3883
  }
3457
3884
  function notRecorded(reason) {
3458
- return { status: "not_recorded", reason };
3885
+ return hostObservationNotRecorded(reason);
3459
3886
  }
3460
3887
  function tokenObservationValue(usage) {
3461
3888
  return {
3462
- status: "available",
3463
- unit: "tokens",
3464
- input: usage.input_tokens,
3465
- output: usage.output_tokens,
3466
- total: usage.total_tokens,
3467
- cached_input: usage.cached_input_tokens,
3468
- cache_write_input: usage.cache_write_input_tokens,
3469
- reasoning_output: usage.reasoning_output_tokens,
3470
- source: "codex_transcript.event_msg.token_count.info.last_token_usage",
3889
+ ...createHostTokenObservationValue({
3890
+ input: usage.input_tokens,
3891
+ output: usage.output_tokens,
3892
+ total: usage.total_tokens,
3893
+ cached_input: usage.cached_input_tokens,
3894
+ cache_write_input: usage.cache_write_input_tokens,
3895
+ reasoning_output: usage.reasoning_output_tokens
3896
+ }, "codex_transcript.event_msg.token_count.info.last_token_usage"),
3471
3897
  attribution: "sequential_active_task",
3472
3898
  total_usage_role: "dedup_and_reset_detection_only"
3473
3899
  };
@@ -3501,60 +3927,10 @@ function looksLikeStaleRegression(current, previous, last) {
3501
3927
  if (previous.total_tokens <= 0 || current.total_tokens <= 0 || last.total_tokens <= 0) return false;
3502
3928
  return current.total_tokens * 100 >= previous.total_tokens * 98 || current.total_tokens + last.total_tokens * 2 >= previous.total_tokens;
3503
3929
  }
3504
- function isMissingTranscript(error) {
3505
- return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
3506
- }
3507
- async function cursorFailureReason(registration, handle, cursor, size, device, inode) {
3508
- if (cursor.device !== device || cursor.inode !== inode || cursor.transcript_path !== registration.transcript_path) return "transcript_replaced";
3509
- if (size < cursor.offset) return "transcript_truncated";
3510
- if (cursor.offset > 0 && !await anchorMatches(handle, cursor)) return "transcript_rewritten";
3511
- return null;
3512
- }
3513
- async function readCompleteChunk(handle, cursor, size) {
3514
- const available = Math.max(0, size - cursor.offset);
3515
- if (available === 0) return "empty";
3516
- const chunks = [];
3517
- let position = cursor.offset;
3518
- let total = 0;
3519
- let lastNewline = -1;
3520
- while (position < size) {
3521
- const buffer = Buffer.alloc(Math.min(READ_CHUNK_BYTES, size - position));
3522
- const read = await handle.read(buffer, 0, buffer.length, position);
3523
- if (read.bytesRead === 0) break;
3524
- const chunk = buffer.subarray(0, read.bytesRead);
3525
- chunks.push(chunk);
3526
- const newline = chunk.lastIndexOf(10);
3527
- if (newline >= 0) lastNewline = total + newline;
3528
- total += chunk.length;
3529
- position += chunk.length;
3530
- if (lastNewline >= 0) break;
3531
- }
3532
- if (lastNewline < 0) return "partial";
3533
- return Buffer.concat(chunks, total).subarray(0, lastNewline + 1);
3534
- }
3535
3930
  function assertRecordSequence(registration, cursor, records, lastOrdinal, record3) {
3536
3931
  if ((cursor.header_validated || records > 0) && record3.ordinal <= lastOrdinal) throw new CodexHostAdapterError("payload_rejected", `Codex transcript ordinal is not monotonic (${refusalFact("ordinal", record3.ordinal, `greater than ${lastOrdinal}`)}).`);
3537
3932
  if (!cursor.header_validated && records === 0) assertCodexTranscriptHeader(registration, record3);
3538
3933
  }
3539
- async function anchorMatches(handle, cursor) {
3540
- const buffer = Buffer.alloc(cursor.offset - cursor.anchor_start);
3541
- if (buffer.length > 0) await handle.read(buffer, 0, buffer.length, cursor.anchor_start);
3542
- return sha2562(buffer) === cursor.anchor_sha256;
3543
- }
3544
- function sha2562(value) {
3545
- return createHash6("sha256").update(value).digest("hex");
3546
- }
3547
- function isRecord6(value) {
3548
- return value !== null && typeof value === "object" && !Array.isArray(value);
3549
- }
3550
- function timestamp4(now) {
3551
- const value = now();
3552
- if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
3553
- return value.toISOString();
3554
- }
3555
- function expiration(now, retentionMs) {
3556
- return new Date(Date.parse(timestamp4(now)) + retentionMs).toISOString();
3557
- }
3558
3934
 
3559
3935
  // host/codex/src/codex-producer-runtime.ts
3560
3936
  async function drainCodexProducerOnce(options) {