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

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"]);
@@ -991,6 +1016,7 @@ var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_f
991
1016
  var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
992
1017
 
993
1018
  // packages/ceal-client-protocol/src/resource-row.ts
1019
+ var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
994
1020
  var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
995
1021
  var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
996
1022
  var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "since", "until", "authored_by_me", "authored_by", "include_replies", "kinds", "container_ref", "container_refs", "container_names", "addressed_to_me", "unread"]);
@@ -1014,6 +1040,7 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
1014
1040
  "resource_modified"
1015
1041
  ]);
1016
1042
  var CEAL_ACTIVITY_CONNECTOR_KINDS = vocabulary(["slack", "github", "google-calendar", "google-drive", "notion"]);
1043
+ var CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE = "unsupported_event_kind";
1017
1044
  var CEAL_ACTIVITY_GAP_CODES = vocabulary([
1018
1045
  "provider_page_budget",
1019
1046
  "call_deadline",
@@ -1021,7 +1048,7 @@ var CEAL_ACTIVITY_GAP_CODES = vocabulary([
1021
1048
  "provider_cursor_unavailable",
1022
1049
  "malformed_provider_page",
1023
1050
  "visibility_incomplete",
1024
- "unsupported_event_kind",
1051
+ CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE,
1025
1052
  "direct_messages_excluded"
1026
1053
  ]);
1027
1054
  var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
@@ -1073,6 +1100,7 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1073
1100
  next: { required: true, timestamp: false },
1074
1101
  kind: { required: true, timestamp: false },
1075
1102
  matched_kinds: { required: false, timestamp: false },
1103
+ title_match: { required: false, timestamp: false },
1076
1104
  title: { required: true, timestamp: false },
1077
1105
  container_label: { required: true, timestamp: false },
1078
1106
  container_ref: { required: false, timestamp: false },
@@ -1086,6 +1114,8 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1086
1114
  modified_at: { required: false, timestamp: true },
1087
1115
  modified_at_local: { required: false, timestamp: true },
1088
1116
  viewed_by_me_at: { required: false, timestamp: true },
1117
+ own_ceal_write: { required: false, timestamp: false },
1118
+ owned_by_me: { required: false, timestamp: false },
1089
1119
  visibility: { required: false, timestamp: false },
1090
1120
  start: { required: false, timestamp: false },
1091
1121
  end: { required: false, timestamp: false },
@@ -1119,6 +1149,15 @@ function cealExcludedTargetCoverageJsonSchema() {
1119
1149
  };
1120
1150
  }
1121
1151
 
1152
+ // packages/ceal-client-protocol/src/gateway-write-identity.ts
1153
+ var CEAL_GATEWAY_WRITE_IDENTITY_ROLES = Object.freeze(["replay_identity", "lookup_handle", "collision_evidence"]);
1154
+ var CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN = /^gateway-write-request:[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/u;
1155
+ var CEAL_GATEWAY_WRITE_IDENTITY_FIELDS = Object.freeze({
1156
+ replay_identity: "idempotency_claim_sha256",
1157
+ lookup_handle: "write_request_sha256",
1158
+ collision_evidence: "normalized_mutation_sha256"
1159
+ });
1160
+
1122
1161
  // packages/ceal-client-protocol/src/write-safety-finding.ts
1123
1162
  var CEAL_WRITE_SAFETY_FINDING_STATUSES = vocabulary(["warning", "unchecked"]);
1124
1163
  var CEAL_WRITE_SAFETY_AUDIENCES = vocabulary(["workspace", "person", "members", "unknown"]);
@@ -1146,7 +1185,11 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
1146
1185
  "refresh_revoked",
1147
1186
  "authority_replaced",
1148
1187
  "refresh_recovery_unavailable",
1149
- "refresh_recovery_expired"
1188
+ "refresh_recovery_expired",
1189
+ // The serving gate answers this while the Gateway is still starting and the
1190
+ // refresh was never attempted; the same attempt stays retryable. It is never
1191
+ // stored by the session owner, only answered at the serving edge.
1192
+ "gateway_starting"
1150
1193
  ]);
1151
1194
 
1152
1195
  // packages/ceal-client-protocol/src/request-validation-primitives.ts
@@ -1179,7 +1222,7 @@ var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
1179
1222
  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
1223
  var CEAL_REQUEST_JSON_SCHEMA = {
1181
1224
  type: "object",
1182
- maxProperties: 10,
1225
+ maxProperties: 11,
1183
1226
  additionalProperties: false,
1184
1227
  properties: {
1185
1228
  schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
@@ -1203,7 +1246,8 @@ var CEAL_REQUEST_JSON_SCHEMA = {
1203
1246
  host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1204
1247
  host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1205
1248
  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." }
1249
+ requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1250
+ 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
1251
  },
1208
1252
  required: CEAL_REQUEST_KEYS
1209
1253
  };
@@ -1262,6 +1306,7 @@ var REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES = {
1262
1306
  retryable: { type: "boolean" },
1263
1307
  source: { type: "string", enum: ["provider", "gateway"] },
1264
1308
  retry_after_ms: { type: "integer", minimum: 0, maximum: CEAL_RETRY_AFTER_MAX_MS },
1309
+ write_request_ref: { type: "string", pattern: CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN.source, maxLength: 128 },
1265
1310
  excluded_targets: cealExcludedTargetCoverageJsonSchema(),
1266
1311
  repaired_input: {},
1267
1312
  unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
@@ -1326,6 +1371,43 @@ function cealHostObservationRef(producer, digest2) {
1326
1371
  if (!HOST_OBSERVATION_PRODUCER.test(producer) || !HOST_OBSERVATION_DIGEST.test(digest2)) invalid("Ceal Host observation reference parts are invalid.");
1327
1372
  return `${CEAL_HOST_OBSERVATION_REF_PREFIX}${producer}-${digest2}`;
1328
1373
  }
1374
+ var HOST_TOKEN_OBSERVATION_BREAKDOWN_KEYS = vocabulary(["cached_input", "cache_write_input", "reasoning_output"]);
1375
+ function createHostTokenObservationValue(counts, source) {
1376
+ const core = validTokenCore(counts);
1377
+ if (typeof source !== "string" || source.length === 0 || source.length > 256) invalid("Ceal Host token observation source is invalid.");
1378
+ const value = { status: "available", unit: "tokens", input: core.input, output: core.output, total: core.total, source };
1379
+ for (const breakdown of validTokenBreakdowns(counts)) value[breakdown.key] = breakdown.tokens;
1380
+ assertHostObservationValue(value, "token");
1381
+ return value;
1382
+ }
1383
+ function validTokenCore(counts) {
1384
+ if (!isOperationRecord(counts) || !safeCount(counts.input) || !safeCount(counts.output) || !safeCount(counts.total)) invalid("Ceal Host token observation counts are invalid.");
1385
+ if (counts.total !== counts.input + counts.output) invalid("Ceal Host token observation counts are invalid.");
1386
+ return { input: counts.input, output: counts.output, total: counts.total };
1387
+ }
1388
+ function validTokenBreakdowns(counts) {
1389
+ const breakdowns = [
1390
+ { key: "cached_input", tokens: counts.cached_input },
1391
+ { key: "cache_write_input", tokens: counts.cache_write_input },
1392
+ { key: "reasoning_output", tokens: counts.reasoning_output }
1393
+ ];
1394
+ const stated = breakdowns.filter((breakdown) => breakdown.tokens !== void 0).map((breakdown) => ({ key: breakdown.key, tokens: Number(breakdown.tokens) }));
1395
+ for (const breakdown of stated) {
1396
+ if (!safeCount(breakdown.tokens)) invalid("Ceal Host token observation counts are invalid.");
1397
+ }
1398
+ const cached = counts.cached_input ?? 0;
1399
+ const created = counts.cache_write_input ?? 0;
1400
+ const reasoning = counts.reasoning_output ?? 0;
1401
+ if (cached + created > counts.input || reasoning > counts.output) invalid("Ceal Host token observation counts are invalid.");
1402
+ return stated;
1403
+ }
1404
+ function hostObservationNotRecorded(reason) {
1405
+ if (typeof reason !== "string" || reason.length === 0 || reason.length > 256) invalid("Ceal Host observation gap reason is invalid.");
1406
+ return { status: "not_recorded", reason };
1407
+ }
1408
+ function safeCount(value) {
1409
+ return Number.isSafeInteger(value) && Number(value) >= 0;
1410
+ }
1329
1411
  function validateCealHostObservationDraft(value) {
1330
1412
  const record3 = operationRecord(value, "Ceal Host observation draft is invalid.");
1331
1413
  requirePrefixedRef(record3.observation_ref, CEAL_HOST_OBSERVATION_REF_PREFIX, "Ceal Host observation reference is invalid.");
@@ -1477,12 +1559,85 @@ var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
1477
1559
  "host_input_mismatch",
1478
1560
  "host_prompt_mismatch"
1479
1561
  ]);
1562
+ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
1563
+ "invalid_arguments",
1564
+ "options_invalid",
1565
+ "session_unavailable",
1566
+ "context_unavailable",
1567
+ "context_replaced",
1568
+ "no_binding",
1569
+ "context_stale",
1570
+ "context_expired",
1571
+ "host_source_refused",
1572
+ "host_credential_refused",
1573
+ "gateway_unavailable",
1574
+ "gateway_rate_limited",
1575
+ "gateway_refused",
1576
+ "gateway_response_invalid",
1577
+ "producer_frame_invalid",
1578
+ "relay_permission_denied",
1579
+ "relay_unavailable",
1580
+ "transient_retryable",
1581
+ "mint_failed",
1582
+ "relay_failed",
1583
+ "host_refusal_invalid"
1584
+ ]);
1585
+ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
1586
+ invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
1587
+ options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
1588
+ session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1589
+ 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; send a new message to start a fresh Host turn, then retry this command on that turn (run `ceal-host install` only if the Host hook registration is missing or drifted)" }),
1590
+ context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1591
+ no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
1592
+ context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1593
+ context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "refresh_host_invocation_context" }),
1594
+ host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
1595
+ host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
1596
+ gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
1597
+ gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
1598
+ gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
1599
+ gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
1600
+ 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" }),
1601
+ 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" }),
1602
+ relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
1603
+ transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
1604
+ mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
1605
+ relay_failed: Object.freeze({ classification: "host_relay_failed", next_action: "answer_with_gap" }),
1606
+ host_refusal_invalid: Object.freeze({ classification: "host_refusal_invalid", next_action: "answer_with_gap" })
1607
+ });
1608
+
1609
+ // packages/ceal-client-protocol/src/host-service-diagnostic.ts
1610
+ var CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA = "ceal.host_service_diagnostic.v1";
1611
+ var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
1612
+ var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
1613
+ "hook_invoked",
1614
+ "registration_written",
1615
+ "hook_failed",
1616
+ "watcher_turn_observed",
1617
+ "current_prompt_registered",
1618
+ "current_prompt_registration_failed",
1619
+ "current_prompt_capacity_exhausted",
1620
+ "activation_recorded",
1621
+ "activation_failed",
1622
+ "watcher_scan_failed",
1623
+ "activation_wait_skipped",
1624
+ "activation_wait_engaged",
1625
+ "activation_wait_completed",
1626
+ "activation_wait_exhausted"
1627
+ ]);
1628
+ var CEAL_HOST_SERVICE_DIAGNOSTIC_FAILURE_KINDS = vocabulary([
1629
+ "malformed_line",
1630
+ "other",
1631
+ "payload_rejected",
1632
+ "read_error",
1633
+ "size_budget",
1634
+ "symlink"
1635
+ ]);
1480
1636
 
1481
1637
  // packages/ceal-client-protocol/src/client-result-materialization.ts
1482
1638
  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"]);
1639
+ var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
1640
+ 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
1641
 
1487
1642
  // packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
1488
1643
  var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
@@ -1512,14 +1667,6 @@ var CEAL_GATEWAY_ANNOUNCEMENT_POLICY_NON_CLAIMS = vocabulary([
1512
1667
  "target_specific_scope_not_declared"
1513
1668
  ]);
1514
1669
 
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
1670
  // packages/ceal-client-protocol/src/gateway-scoped-identity-projection-validation.ts
1524
1671
  var PROJECTION_REVISION_KEYS = Object.freeze(["graph_revision", "subject_key_revision", "projection_revision"]);
1525
1672
 
@@ -1532,6 +1679,18 @@ var CEAL_SUPPORTED_GATEWAY_PROTOCOL_RANGE = Object.freeze({
1532
1679
  // packages/ceal-client-protocol/src/admin-projection-vocabulary.ts
1533
1680
  var ADMIN_PROJECTION_STATES = vocabulary(["ready", "empty", "partial", "unknown"]);
1534
1681
  var ADMIN_VIEW_KINDS = vocabulary(["request_detail", "resource_access_timeline", "weekly_usage"]);
1682
+ var ADMIN_RECORD_PROJECTION_KINDS = vocabulary([
1683
+ "request_list",
1684
+ "usage_series",
1685
+ "connector_health",
1686
+ "person_connections",
1687
+ "client_sessions",
1688
+ "knowledge_base",
1689
+ "change_history",
1690
+ "approval_queue",
1691
+ "access_group_list",
1692
+ "capability_catalogue"
1693
+ ]);
1535
1694
  var ADMIN_GAP_KINDS = vocabulary(["unknown", "unavailable", "partial"]);
1536
1695
  var ADMIN_UNAVAILABLE_REASONS = vocabulary([
1537
1696
  "not_recorded",
@@ -1555,7 +1714,11 @@ var ADMIN_SOURCE_OWNERS = vocabulary([
1555
1714
  "person",
1556
1715
  "resource",
1557
1716
  "prompt",
1558
- "usage"
1717
+ "usage",
1718
+ "connector",
1719
+ "session",
1720
+ "knowledge",
1721
+ "policy"
1559
1722
  ]);
1560
1723
  var ADMIN_COMPLETENESS_STATES = vocabulary(["complete", "partial", "unknown"]);
1561
1724
  var ADMIN_OPERATION_OUTCOMES = vocabulary(["completed", "failed", "denied", "cancelled", "unknown"]);
@@ -1790,6 +1953,15 @@ var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
1790
1953
  function isHostProducerFrameKind(value) {
1791
1954
  return typeof value === "string" && FRAME_KINDS.has(value);
1792
1955
  }
1956
+ var HOST_PRODUCER_FRAME_ERROR_CODES = [
1957
+ "invalid_frame_encoding",
1958
+ "invalid_frame_length",
1959
+ "frame_truncated",
1960
+ "unsupported_frame_schema",
1961
+ "invalid_frame_shape",
1962
+ "invalid_producer_identity",
1963
+ "frame_too_large"
1964
+ ];
1793
1965
  var HostProducerFrameError = class extends Error {
1794
1966
  code;
1795
1967
  constructor(code, message = code) {
@@ -1824,6 +1996,9 @@ function decodeHostProducerFrame(input) {
1824
1996
  validateFrame(value);
1825
1997
  return value;
1826
1998
  }
1999
+ function isHostSafeRef(value) {
2000
+ return safeRef(value);
2001
+ }
1827
2002
  function validateFrame(value) {
1828
2003
  if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
1829
2004
  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 +2086,15 @@ function exactOptional(value, required, optional) {
1911
2086
  return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
1912
2087
  }
1913
2088
 
2089
+ // host/relay/src/host-producer-core.ts
2090
+ import { createHash as createHash3 } from "node:crypto";
2091
+ import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
2092
+ import { mkdir as mkdir2, open as open4, readFile, rename as rename2, writeFile } from "node:fs/promises";
2093
+ import { join as join2 } from "node:path";
2094
+
2095
+ // host/relay/src/relay-ipc.ts
2096
+ import { createConnection, createServer } from "node:net";
2097
+
1914
2098
  // host/relay/src/durable-atomic-replacement.ts
1915
2099
  import { O_RDONLY } from "node:constants";
1916
2100
  import { lstat as lstat2, mkdir, open as open2, rename, unlink } from "node:fs/promises";
@@ -1928,20 +2112,67 @@ var DEFAULT_PORT = {
1928
2112
  // host/relay/src/relay-request-binding-store.ts
1929
2113
  var MAX_STATE_BYTES = 8 * 1024 * 1024;
1930
2114
 
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
2115
  // host/relay/src/current-prompt-custody.ts
1943
2116
  var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
1944
2117
 
2118
+ // host/service-log.ts
2119
+ import { copyFile, open as open3, stat, truncate } from "node:fs/promises";
2120
+ import { isAbsolute } from "node:path";
2121
+ var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
2122
+ function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
2123
+ const line = `${JSON.stringify({ schema_version: CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA, producer_id, event, ...event === "watcher_scan_failed" && failure_kind !== void 0 ? { failure_kind } : {}, ...event === "watcher_scan_failed" && cause !== void 0 ? { cause } : {} })}
2124
+ `;
2125
+ try {
2126
+ (sink ?? defaultHostServiceDiagnosticSink)(line);
2127
+ } catch {
2128
+ }
2129
+ }
2130
+ function defaultHostServiceDiagnosticSink(line) {
2131
+ process.stderr.write(line);
2132
+ }
2133
+ var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
2134
+ var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
2135
+ function hostServiceLogPreviousPath(path) {
2136
+ return `${path}.1`;
2137
+ }
2138
+ async function rotateHostServiceLog(path, maxBytes = HOST_SERVICE_LOG_MAX_BYTES) {
2139
+ let size;
2140
+ try {
2141
+ size = (await stat(path)).size;
2142
+ } catch (error) {
2143
+ if (isNotFound(error)) return false;
2144
+ throw error;
2145
+ }
2146
+ if (size <= maxBytes) return false;
2147
+ await copyFile(path, hostServiceLogPreviousPath(path));
2148
+ await truncate(path, 0);
2149
+ return true;
2150
+ }
2151
+ function boundHostServiceLog(environment = process.env) {
2152
+ const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
2153
+ if (path === void 0 || !isAbsolute(path)) return () => void 0;
2154
+ let reported = false;
2155
+ const check = () => {
2156
+ rotateHostServiceLog(path).catch((error) => {
2157
+ if (reported) return;
2158
+ reported = true;
2159
+ process.stderr.write(`[host-service-log] rotation_failed code=${errorCode(error)}
2160
+ `);
2161
+ });
2162
+ };
2163
+ check();
2164
+ const timer = setInterval(check, HOST_SERVICE_LOG_CHECK_INTERVAL_MS);
2165
+ timer.unref();
2166
+ return () => clearInterval(timer);
2167
+ }
2168
+ var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
2169
+ function isNotFound(error) {
2170
+ return errorCode(error) === "ENOENT";
2171
+ }
2172
+ function errorCode(error) {
2173
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "unknown";
2174
+ }
2175
+
1945
2176
  // host/relay/src/relay-ipc.ts
1946
2177
  var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
1947
2178
  "schema_version",
@@ -1960,6 +2191,7 @@ var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
1960
2191
  ]);
1961
2192
  var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
1962
2193
  var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
2194
+ var HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA = "ceal.host_relay_current_prompt_release.v1";
1963
2195
  var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
1964
2196
  var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
1965
2197
  var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
@@ -1971,6 +2203,8 @@ var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
1971
2203
  "gateway_unavailable",
1972
2204
  "gateway_refused",
1973
2205
  "gateway_response_invalid",
2206
+ "gateway_rate_limited",
2207
+ "producer_frame_invalid",
1974
2208
  "deadline",
1975
2209
  "cancelled",
1976
2210
  "mint_failed",
@@ -1984,6 +2218,17 @@ var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
1984
2218
  "host_input_mismatch",
1985
2219
  "host_prompt_mismatch"
1986
2220
  ]);
2221
+ var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
2222
+ "context_stale",
2223
+ "context_expired",
2224
+ "host_source_refused",
2225
+ "host_credential_refused",
2226
+ "gateway_unavailable",
2227
+ "gateway_rate_limited",
2228
+ "gateway_refused",
2229
+ "gateway_response_invalid",
2230
+ "producer_frame_invalid"
2231
+ ]);
1987
2232
  var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
1988
2233
  var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
1989
2234
  "permission_denied",
@@ -2005,8 +2250,17 @@ var HostRelayIpcError = class extends Error {
2005
2250
  this.failure = failure;
2006
2251
  }
2007
2252
  };
2253
+ var HostRelayCurrentPromptCapacityRefusal = class extends Error {
2254
+ constructor() {
2255
+ super("current_prompt_capacity_exhausted");
2256
+ this.name = "HostRelayCurrentPromptCapacityRefusal";
2257
+ }
2258
+ };
2259
+ function hostRelayExchangeFailure(error) {
2260
+ return error instanceof HostRelayIpcError ? error.failure : void 0;
2261
+ }
2008
2262
  function socketErrorFailure(error) {
2009
- switch (errorCode(error)) {
2263
+ switch (errorCode2(error)) {
2010
2264
  case "EACCES":
2011
2265
  case "EPERM":
2012
2266
  return "permission_denied";
@@ -2041,8 +2295,14 @@ var HostRelayCurrentPromptPort = class {
2041
2295
  async register(input) {
2042
2296
  const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
2043
2297
  const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
2298
+ if (response2.status === "capacity_exhausted") throw new HostRelayCurrentPromptCapacityRefusal();
2044
2299
  if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
2045
2300
  }
2301
+ async release(identity) {
2302
+ const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA, ...identity };
2303
+ const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
2304
+ if (response2.status !== "released") throw new HostRelayIpcError("relay_unavailable", response2.status, "response_invalid");
2305
+ }
2046
2306
  };
2047
2307
  function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
2048
2308
  return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
@@ -2125,7 +2385,7 @@ function decodeResponseBuffer(buffer) {
2125
2385
  function decodeCurrentPromptResponseBuffer(buffer) {
2126
2386
  const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
2127
2387
  if (value === null) return null;
2128
- if (!record2(value) || !exactKeys2(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "current_prompt_unavailable") throw new HostRelayIpcError("relay_unavailable");
2388
+ if (!record2(value) || !exactKeys2(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "capacity_exhausted" && value.status !== "current_prompt_unavailable" && value.status !== "released") throw new HostRelayIpcError("relay_unavailable", "Host Relay current-prompt response is invalid.", "response_invalid");
2129
2389
  return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
2130
2390
  }
2131
2391
  function decodeLengthPrefixedJson(input, maximum) {
@@ -2147,131 +2407,503 @@ function encodeLengthPrefixed(value) {
2147
2407
  body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
2148
2408
  return output;
2149
2409
  }
2150
- function exactKeys2(value, keys) {
2151
- return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
2410
+ function exactKeys2(value, required, optional = []) {
2411
+ const keys = Object.keys(value);
2412
+ return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
2152
2413
  }
2153
- function errorCode(error) {
2414
+ function errorCode2(error) {
2154
2415
  return record2(error) && typeof error.code === "string" ? error.code : null;
2155
2416
  }
2156
2417
 
2157
- // 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";
2161
-
2162
- // 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";
2166
-
2167
- // host/invocation-context.ts
2168
- var HOST_INVOCATION_IDENTITY_KEYS = ["host_session_ref", "host_turn_ref", "host_source_ref"];
2169
- var HOST_INVOCATION_CONTEXT_REQUIRED_KEYS = ["schema_version", "identity", "prompt_ref", "expires_at"];
2170
- function invocationContextKey(identity) {
2171
- return cealRequestSha256({ host_source_ref: identity.host_source_ref, host_session_ref: identity.host_session_ref });
2418
+ // host/relay/src/host-producer-core.ts
2419
+ var HOST_TRANSCRIPT_CURSOR_REASONS = vocabulary(["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"]);
2420
+ function isHostTranscriptCursorReason(value) {
2421
+ return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
2172
2422
  }
2173
- function hasHostInvocationIdentityKeys(value) {
2174
- return exactKeys3(value, HOST_INVOCATION_IDENTITY_KEYS);
2423
+ var TRANSCRIPT_MISSING_REASON = "transcript_missing";
2424
+ var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
2425
+ function transcriptScanFailureKind(error, adapterFailureKind) {
2426
+ if (transcriptErrorCode(error) === "ELOOP") return "symlink";
2427
+ const adapter = adapterFailureKind(error);
2428
+ if (adapter !== void 0) return adapter;
2429
+ const code = transcriptErrorCode(error);
2430
+ if (code === "EFBIG") return "size_budget";
2431
+ if (code !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code)) return "read_error";
2432
+ return "other";
2433
+ }
2434
+ async function registerHostCurrentPromptDiagnosed(register, diagnostic) {
2435
+ try {
2436
+ await register();
2437
+ diagnostic("current_prompt_registered");
2438
+ return true;
2439
+ } catch (error) {
2440
+ if (!(error instanceof HostRelayCurrentPromptCapacityRefusal)) diagnostic("current_prompt_registration_failed");
2441
+ throw error;
2442
+ }
2175
2443
  }
2176
- function hasHostInvocationContextKeys(value, optional = []) {
2177
- return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
2444
+ function transcriptErrorCode(error) {
2445
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
2178
2446
  }
2179
- function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor) {
2180
- const context = {
2181
- schema_version: descriptor.schema_version,
2182
- identity,
2183
- prompt_ref: promptRef,
2184
- expires_at: expiresAt,
2185
- ...descriptor.working_directory === void 0 ? {} : { working_directory: descriptor.working_directory }
2186
- };
2187
- descriptor.validate(context);
2188
- return Object.freeze(context);
2447
+ var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
2448
+ async function readTranscriptCompleteChunk(handle, offset, size) {
2449
+ const available = Math.max(0, size - offset);
2450
+ if (available === 0) return "empty";
2451
+ const chunks = [];
2452
+ let position = offset;
2453
+ let total = 0;
2454
+ let lastNewline = -1;
2455
+ while (position < size) {
2456
+ const buffer = Buffer.alloc(Math.min(HOST_TRANSCRIPT_READ_CHUNK_BYTES, size - position));
2457
+ const read = await handle.read(buffer, 0, buffer.length, position);
2458
+ if (read.bytesRead === 0) break;
2459
+ const chunk = buffer.subarray(0, read.bytesRead);
2460
+ chunks.push(chunk);
2461
+ const newline = chunk.lastIndexOf(10);
2462
+ if (newline >= 0) lastNewline = total + newline;
2463
+ total += chunk.length;
2464
+ position += chunk.length;
2465
+ if (lastNewline >= 0) break;
2466
+ }
2467
+ if (lastNewline < 0) return "partial";
2468
+ return Buffer.concat(chunks, total).subarray(0, lastNewline + 1);
2189
2469
  }
2190
- function exactKeys3(value, keys) {
2191
- const actual = Object.keys(value);
2192
- return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
2470
+ async function readTranscriptAnchor(handle, offset, anchorBytes) {
2471
+ const start = Math.max(0, offset - anchorBytes);
2472
+ const anchor = Buffer.alloc(offset - start);
2473
+ if (anchor.length > 0) await handle.read(anchor, 0, anchor.length, start);
2474
+ return { start, sha256: transcriptSha256(anchor) };
2193
2475
  }
2194
- function requiredKeys(value, required, optional) {
2195
- const actual = Object.keys(value);
2196
- const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
2197
- return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
2476
+ async function transcriptAnchorMatches(handle, cursor) {
2477
+ const buffer = Buffer.alloc(cursor.offset - cursor.anchor_start);
2478
+ if (buffer.length > 0) await handle.read(buffer, 0, buffer.length, cursor.anchor_start);
2479
+ return transcriptSha256(buffer) === cursor.anchor_sha256;
2198
2480
  }
2199
-
2200
- // host/codex/src/codex-types.ts
2201
- var CODEX_OUTBOX_SCHEMA_VERSION = "ceal.codex_observation_outbox.v1";
2202
- var CODEX_TURN_BINDING_SCHEMA_VERSION = "ceal.codex_turn_binding.v1";
2203
- var CODEX_SESSION_SCHEMA_VERSION = "ceal.codex_session.v1";
2204
- var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1";
2205
- var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
2206
- var CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION = "ceal.codex_transcript_cursor.v1";
2207
- 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);
2481
+ function transcriptCursorIdentityReason(cursor, transcriptPath, device, inode) {
2482
+ if (cursor.device !== device || cursor.inode !== inode || cursor.transcript_path !== transcriptPath) return "transcript_replaced";
2483
+ return null;
2211
2484
  }
2212
- var CodexHostAdapterError = class extends Error {
2213
- name = "CodexHostAdapterError";
2214
- code;
2215
- constructor(code, message) {
2216
- super(message);
2217
- this.code = code;
2218
- }
2219
- };
2220
-
2221
- // 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 });
2485
+ async function transcriptCursorBoundsReason(handle, cursor, size) {
2486
+ if (size < cursor.offset) return "transcript_truncated";
2487
+ if (cursor.offset > 0 && !await transcriptAnchorMatches(handle, cursor)) return "transcript_rewritten";
2488
+ return null;
2224
2489
  }
2225
- function validateCodexHostInvocationContext(value) {
2226
- if (!isContextShape(value)) {
2227
- throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2228
- }
2229
- validateIdentity2(value.identity);
2230
- validateContextReferences(value);
2231
- validateContextTimes(value);
2490
+ function transcriptSha256(value) {
2491
+ return createHash3("sha256").update(value).digest("hex");
2232
2492
  }
2233
- function invocationContextFromBinding(binding) {
2234
- const context = {
2235
- schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
2236
- identity: binding.identity,
2237
- prompt_ref: binding.prompt_ref,
2238
- request_ref: binding.request_ref,
2239
- bound_at: binding.bound_at,
2240
- expires_at: binding.expires_at
2241
- };
2242
- validateCodexHostInvocationContext(context);
2243
- return Object.freeze(context);
2493
+ function transcriptObjectSha256(value) {
2494
+ return createHash3("sha256").update(JSON.stringify(value), "utf8").digest("hex");
2244
2495
  }
2245
- function validateIdentity2(value) {
2246
- if (!isRecord2(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef2)) {
2247
- throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
2248
- }
2496
+ function isTranscriptRecord(value) {
2497
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2249
2498
  }
2250
- function isContextShape(value) {
2251
- return isRecord2(value) && hasHostInvocationContextKeys(value, ["request_ref", "bound_at"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
2499
+ function isMissingTranscriptError(error) {
2500
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2252
2501
  }
2253
- function validateContextReferences(value) {
2254
- if (!safeRef2(value.prompt_ref) || !optionalSafeRef(value.request_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2502
+ function transcriptTimestamp(now, fail) {
2503
+ const value = now();
2504
+ if (!Number.isFinite(value.getTime())) throw fail();
2505
+ return value.toISOString();
2255
2506
  }
2256
- function validateContextTimes(value) {
2257
- const expiresAt = value.expires_at;
2258
- const boundAt = value.bound_at;
2259
- if (!timestamp2(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2507
+ function transcriptExpiration(now, retentionMs, fail) {
2508
+ return new Date(Date.parse(transcriptTimestamp(now, fail)) + retentionMs).toISOString();
2509
+ }
2510
+ function createTranscriptWatcherBase(options, owner) {
2511
+ if (typeof options.outbox_dir !== "string" || options.outbox_dir.length === 0) throw owner.invalid_outbox();
2512
+ return {
2513
+ outboxRetentionMs: options.retention_ms ?? owner.default_retention_ms,
2514
+ now: options.now ?? (() => /* @__PURE__ */ new Date()),
2515
+ log: options.log ?? ((line) => {
2516
+ process.stderr.write(`${line}
2517
+ `);
2518
+ }),
2519
+ producerInstallationRef: options.producer_installation_ref
2520
+ };
2521
+ }
2522
+ async function scanTranscriptRegistrations(registrations, scanOne) {
2523
+ let records = 0;
2524
+ let enqueued = 0;
2525
+ let quarantined = 0;
2526
+ for (const registration of registrations) {
2527
+ const result = await scanOne(registration);
2528
+ records += result.records;
2529
+ enqueued += result.enqueued;
2530
+ if (result.quarantined) quarantined += 1;
2531
+ }
2532
+ return { registrations: registrations.length, records, enqueued, quarantined };
2533
+ }
2534
+ function formatRetiredTranscriptLog(producerLabel, transcriptPath) {
2535
+ return `${producerLabel} retired a transcript registration: transcript_path=${JSON.stringify(transcriptPath)}, reason=${TRANSCRIPT_MISSING_REASON}.`;
2536
+ }
2537
+ var HostTranscriptWatcher = class {
2538
+ outboxRetentionMs;
2539
+ now;
2540
+ log;
2541
+ producerInstallationRef;
2542
+ clockFailure;
2543
+ constructor(options, owner) {
2544
+ const base = createTranscriptWatcherBase(options, owner);
2545
+ this.outboxRetentionMs = base.outboxRetentionMs;
2546
+ this.now = base.now;
2547
+ this.log = base.log;
2548
+ this.producerInstallationRef = base.producerInstallationRef;
2549
+ this.clockFailure = owner.clock_failure;
2550
+ }
2551
+ async scanOnce() {
2552
+ return scanTranscriptRegistrations(await this.listRegistrations(), (registration) => this.scanRegistration(registration));
2553
+ }
2554
+ async scanRegistration(registration) {
2555
+ try {
2556
+ return await this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
2557
+ missing: () => this.retireMissing(registration),
2558
+ failed: (handle, error) => this.scanFailed(handle, registration, error)
2559
+ });
2560
+ } catch (error) {
2561
+ if (error instanceof HostRelayCurrentPromptCapacityRefusal) return { records: 0, enqueued: 0, quarantined: false };
2562
+ throw error;
2563
+ }
2564
+ }
2565
+ async openTranscript(registration, scan, handlers) {
2566
+ let handle;
2567
+ try {
2568
+ handle = await open4(this.transcriptPathOf(registration), O_RDONLY2 | O_NOFOLLOW);
2569
+ return await scan(handle);
2570
+ } catch (error) {
2571
+ if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
2572
+ if (hostRelayExchangeFailure(error) !== void 0 || error instanceof HostRelayCurrentPromptCapacityRefusal) throw error;
2573
+ return handlers.failed(handle, error);
2574
+ } finally {
2575
+ await handle?.close();
2576
+ }
2577
+ }
2578
+ /**
2579
+ * A transcript the Host deleted never comes back, so retrying its
2580
+ * registration once per reconcile interval is a failure with no end.
2581
+ * Retire it at the scan that finds it missing and say so once. Only
2582
+ * ENOENT means gone; a permission or busy failure is about this moment,
2583
+ * not about the file, and keeps the registration for the next scan.
2584
+ */
2585
+ async retireMissingTranscript(transcriptPath, producerLabel, retire) {
2586
+ await retire();
2587
+ this.log(formatRetiredTranscriptLog(producerLabel, transcriptPath));
2588
+ return { records: 0, enqueued: 0, quarantined: false };
2589
+ }
2590
+ stamp() {
2591
+ return transcriptTimestamp(this.now, this.clockFailure);
2592
+ }
2593
+ expire(retentionMs) {
2594
+ return transcriptExpiration(this.now, retentionMs, this.clockFailure);
2595
+ }
2596
+ sha(value) {
2597
+ return transcriptSha256(value);
2598
+ }
2599
+ objectSha(value) {
2600
+ return transcriptObjectSha256(value);
2601
+ }
2602
+ isRecord(value) {
2603
+ return isTranscriptRecord(value);
2604
+ }
2605
+ readChunk(handle, offset, size) {
2606
+ return readTranscriptCompleteChunk(handle, offset, size);
2607
+ }
2608
+ readAnchor(handle, offset, anchorBytes) {
2609
+ return readTranscriptAnchor(handle, offset, anchorBytes);
2610
+ }
2611
+ cursorIdentityReason(cursor, transcriptPath, device, inode) {
2612
+ return transcriptCursorIdentityReason(cursor, transcriptPath, device, inode);
2613
+ }
2614
+ cursorBoundsReason(handle, cursor, size) {
2615
+ return transcriptCursorBoundsReason(handle, cursor, size);
2616
+ }
2617
+ };
2618
+ var HOST_PRODUCER_STATE_IDENTITY = {
2619
+ codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
2620
+ claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
2621
+ };
2622
+ var HostProducerDrainCore = class {
2623
+ host;
2624
+ relay;
2625
+ statePath;
2626
+ stateSchema;
2627
+ installationRef;
2628
+ epoch;
2629
+ now;
2630
+ tail = Promise.resolve();
2631
+ constructor(host, runtime) {
2632
+ if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
2633
+ const epoch = runtime.producer_epoch ?? 1;
2634
+ if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
2635
+ this.host = host;
2636
+ this.relay = runtime.relay;
2637
+ this.statePath = join2(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
2638
+ this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
2639
+ this.installationRef = runtime.producer_installation_ref;
2640
+ this.epoch = epoch;
2641
+ this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
2642
+ }
2643
+ drainOutbox() {
2644
+ const run = this.tail.then(() => this.drainLocked());
2645
+ this.tail = run.then(() => void 0, () => void 0);
2646
+ return run;
2647
+ }
2648
+ async drainLocked() {
2649
+ const read = await this.host.listEnvelopes();
2650
+ let admitted = 0;
2651
+ let expired = 0;
2652
+ let retained = 0;
2653
+ for (const envelope of read.entries) {
2654
+ const reserved = await this.hasReservation(this.host.submissionRefOf(envelope));
2655
+ if (!reserved && Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) {
2656
+ await this.host.removeEnvelope(envelope);
2657
+ expired += 1;
2658
+ continue;
2659
+ }
2660
+ const frames = await this.reserve(envelope);
2661
+ let complete = true;
2662
+ for (const frame of frames) {
2663
+ const outcome = await this.relay.admit(frame);
2664
+ if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
2665
+ complete = false;
2666
+ break;
2667
+ }
2668
+ }
2669
+ if (!complete) {
2670
+ retained += 1;
2671
+ break;
2672
+ }
2673
+ await this.host.removeEnvelope(envelope);
2674
+ await this.release(this.host.submissionRefOf(envelope));
2675
+ admitted += 1;
2676
+ }
2677
+ return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
2678
+ }
2679
+ async hasReservation(submissionRef) {
2680
+ return (await this.readState()).pending[submissionRef] !== void 0;
2681
+ }
2682
+ async reserve(envelope) {
2683
+ const state = await this.readState();
2684
+ const submissionRef = this.host.submissionRefOf(envelope);
2685
+ const existing = state.pending[submissionRef];
2686
+ if (existing !== void 0) return existing;
2687
+ const drafts = await this.host.mapEnvelope(envelope);
2688
+ const frames = drafts.map((draft, offset) => ({
2689
+ schema_version: "ceal.host_producer_frame.v1",
2690
+ producer_id: this.host.producer_id,
2691
+ producer_installation_ref: this.installationRef,
2692
+ producer_epoch: this.epoch,
2693
+ sequence: state.next_sequence + offset,
2694
+ frame_kind: draft.frame_kind,
2695
+ occurred_at: this.host.createdAtOf(envelope),
2696
+ payload: draft.payload
2697
+ }));
2698
+ await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [submissionRef]: frames } });
2699
+ return frames;
2700
+ }
2701
+ async release(submissionRef) {
2702
+ const state = await this.readState();
2703
+ if (state.pending[submissionRef] === void 0) return;
2704
+ const pending = { ...state.pending };
2705
+ delete pending[submissionRef];
2706
+ await this.writeState({ ...state, pending });
2707
+ }
2708
+ async readState() {
2709
+ try {
2710
+ const value = JSON.parse(await readFile(this.statePath, "utf8"));
2711
+ return this.decodeState(value);
2712
+ } catch (error) {
2713
+ if (errorCode3(error) !== "ENOENT") throw error;
2714
+ return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
2715
+ }
2716
+ }
2717
+ async writeState(state) {
2718
+ await mkdir2(this.host.outbox_directory, { recursive: true, mode: 448 });
2719
+ const temporary = `${this.statePath}.${process.pid}.new`;
2720
+ await writeFile(temporary, JSON.stringify(state), { mode: 384 });
2721
+ await rename2(temporary, this.statePath);
2722
+ }
2723
+ decodeState(value) {
2724
+ const label = this.host.producer_label;
2725
+ const fail = (code, message) => this.host.fail(code, message);
2726
+ if (!isDrainStateHeader(value, this.stateSchema, this.installationRef)) {
2727
+ throw fail("storage", `${label} producer state header is malformed or belongs to another installation (${refusalFacts([
2728
+ { name: "schema_version", value: isTranscriptRecord(value) ? value.schema_version : void 0, expected: this.stateSchema },
2729
+ { name: "producer_installation_ref", value: isTranscriptRecord(value) ? value.producer_installation_ref : void 0, expected: this.installationRef }
2730
+ ])}).`);
2731
+ }
2732
+ 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}`)}).`);
2733
+ const pending = {};
2734
+ const storedEpoch = Number(value.producer_epoch);
2735
+ for (const [submissionRef, frames] of Object.entries(value.pending)) {
2736
+ if (!isHostSafeRef(submissionRef) || !Array.isArray(frames) || frames.length === 0) throw fail("storage", `${label} producer pending reservation is malformed.`);
2737
+ pending[submissionRef] = frames.map((frame) => this.decodeStoredFrame(frame, storedEpoch));
2738
+ }
2739
+ return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: Number(value.next_sequence), pending };
2740
+ }
2741
+ decodeStoredFrame(value, storedEpoch) {
2742
+ const label = this.host.producer_label;
2743
+ const json = Buffer.from(JSON.stringify(value), "utf8");
2744
+ const encoded = Buffer.allocUnsafe(json.length + 4);
2745
+ encoded.writeUInt32BE(json.length, 0);
2746
+ json.copy(encoded, 4);
2747
+ const frame = decodeHostProducerFrame(encoded);
2748
+ 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([
2749
+ { name: "producer_id", value: frame.producer_id, expected: this.host.producer_id },
2750
+ { name: "producer_installation_ref", value: frame.producer_installation_ref, expected: this.installationRef },
2751
+ { name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${storedEpoch}` }
2752
+ ])}).`);
2753
+ return frame;
2754
+ }
2755
+ };
2756
+ function isDrainStateHeader(value, schema, installationRef) {
2757
+ 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);
2758
+ }
2759
+ function errorCode3(error) {
2760
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2761
+ }
2762
+
2763
+ // host/relay/src/relay-quarantine-store.ts
2764
+ var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
2765
+ var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
2766
+ var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
2767
+
2768
+ // host/relay/src/relay-admission-store.ts
2769
+ var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
2770
+ var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
2771
+ var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
2772
+
2773
+ // host/codex/src/codex-producer.ts
2774
+ import { createHash as createHash4 } from "node:crypto";
2775
+
2776
+ // host/codex/src/codex-outbox.ts
2777
+ import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
2778
+ import { lstat as lstat3, mkdir as mkdir3, open as open5, readdir, rename as rename3, unlink as unlink2 } from "node:fs/promises";
2779
+ import { dirname, isAbsolute as isAbsolute2, join as join3 } from "node:path";
2780
+
2781
+ // host/invocation-context.ts
2782
+ var HOST_INVOCATION_IDENTITY_KEYS = ["host_session_ref", "host_turn_ref", "host_source_ref"];
2783
+ var HOST_INVOCATION_CONTEXT_REQUIRED_KEYS = ["schema_version", "identity", "prompt_ref", "expires_at"];
2784
+ function invocationContextKey(identity) {
2785
+ return cealRequestSha256({ host_source_ref: identity.host_source_ref, host_session_ref: identity.host_session_ref });
2786
+ }
2787
+ function hasHostInvocationIdentityKeys(value) {
2788
+ return exactKeys3(value, HOST_INVOCATION_IDENTITY_KEYS);
2789
+ }
2790
+ function hasHostInvocationContextKeys(value, optional = []) {
2791
+ return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
2792
+ }
2793
+ async function readLatestSessionProjection(file, hostSourceRef, hostSessionRef, readProjection) {
2794
+ const context = await readProjection(file);
2795
+ if (context === null || context.identity.host_source_ref !== hostSourceRef || context.identity.host_session_ref !== hostSessionRef) return null;
2796
+ return context;
2797
+ }
2798
+ function isLiveSessionProjection(context, now) {
2799
+ return context !== null && Date.parse(context.expires_at) > now.getTime();
2800
+ }
2801
+ function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor) {
2802
+ const context = {
2803
+ schema_version: descriptor.schema_version,
2804
+ identity,
2805
+ prompt_ref: promptRef,
2806
+ expires_at: expiresAt,
2807
+ ...descriptor.turn_generation_ref === void 0 ? {} : { turn_generation_ref: descriptor.turn_generation_ref },
2808
+ ...descriptor.working_directory === void 0 ? {} : { working_directory: descriptor.working_directory }
2809
+ };
2810
+ descriptor.validate(context);
2811
+ return Object.freeze(context);
2812
+ }
2813
+ function isHostInvocationTurnSelection(value) {
2814
+ 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]));
2815
+ }
2816
+ function invocationTurnSelectionMatches(context, selection) {
2817
+ 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;
2818
+ }
2819
+ function exactKeys3(value, keys) {
2820
+ const actual = Object.keys(value);
2821
+ return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
2822
+ }
2823
+ function requiredKeys(value, required, optional) {
2824
+ const actual = Object.keys(value);
2825
+ const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
2826
+ return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
2827
+ }
2828
+ function isRecord2(value) {
2829
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2830
+ }
2831
+ function safeRef2(value) {
2832
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
2833
+ }
2834
+
2835
+ // host/codex/src/codex-types.ts
2836
+ var CODEX_OUTBOX_SCHEMA_VERSION = "ceal.codex_observation_outbox.v1";
2837
+ var CODEX_TURN_BINDING_SCHEMA_VERSION = "ceal.codex_turn_binding.v1";
2838
+ var CODEX_SESSION_SCHEMA_VERSION = "ceal.codex_session.v1";
2839
+ var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1";
2840
+ var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
2841
+ var CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION = "ceal.codex_transcript_cursor.v1";
2842
+ var CODEX_DEFAULT_RETENTION_MS = 24 * 60 * 60 * 1e3;
2843
+ var CodexHostAdapterError = class extends Error {
2844
+ name = "CodexHostAdapterError";
2845
+ code;
2846
+ constructor(code, message) {
2847
+ super(message);
2848
+ this.code = code;
2849
+ }
2850
+ };
2851
+
2852
+ // host/codex/src/codex-invocation-context.ts
2853
+ function codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef) {
2854
+ return invocationContextFromTurn(identity, promptRef, expiresAt, { schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION, validate: validateCodexHostInvocationContext, turn_generation_ref: turnGenerationRef });
2855
+ }
2856
+ function validateCodexHostInvocationContext(value) {
2857
+ if (!isContextShape(value)) {
2858
+ throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2859
+ }
2860
+ validateIdentity2(value.identity);
2861
+ validateContextReferences(value);
2862
+ validateContextTimes(value);
2863
+ }
2864
+ function invocationContextFromBinding(binding, turnGenerationRef) {
2865
+ const context = {
2866
+ schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
2867
+ identity: binding.identity,
2868
+ prompt_ref: binding.prompt_ref,
2869
+ request_ref: binding.request_ref,
2870
+ bound_at: binding.bound_at,
2871
+ expires_at: binding.expires_at,
2872
+ ...turnGenerationRef === void 0 ? {} : { turn_generation_ref: turnGenerationRef }
2873
+ };
2874
+ validateCodexHostInvocationContext(context);
2875
+ return Object.freeze(context);
2876
+ }
2877
+ function validateIdentity2(value) {
2878
+ if (!isRecord3(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef3)) {
2879
+ throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
2880
+ }
2881
+ }
2882
+ function isContextShape(value) {
2883
+ return isRecord3(value) && hasHostInvocationContextKeys(value, ["request_ref", "bound_at", "turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
2884
+ }
2885
+ function validateContextReferences(value) {
2886
+ if (!safeRef3(value.prompt_ref) || !optionalSafeRef(value.request_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2887
+ }
2888
+ function validateContextTimes(value) {
2889
+ const expiresAt = value.expires_at;
2890
+ const boundAt = value.bound_at;
2891
+ if (!timestamp2(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2260
2892
  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
2893
  }
2262
2894
  function optionalSafeRef(value) {
2263
- return value === void 0 || safeRef2(value);
2895
+ return value === void 0 || safeRef3(value);
2264
2896
  }
2265
2897
  function optionalTimestamp(value) {
2266
2898
  return value === void 0 || timestamp2(value);
2267
2899
  }
2268
- function safeRef2(value) {
2900
+ function safeRef3(value) {
2269
2901
  return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
2270
2902
  }
2271
2903
  function timestamp2(value) {
2272
2904
  return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
2273
2905
  }
2274
- function isRecord2(value) {
2906
+ function isRecord3(value) {
2275
2907
  return value !== null && typeof value === "object" && !Array.isArray(value);
2276
2908
  }
2277
2909
 
@@ -2284,20 +2916,22 @@ var CodexOutboxStore = class {
2284
2916
  bindingsDirectory;
2285
2917
  sessionsDirectory;
2286
2918
  activeContextsDirectory;
2919
+ turnSelectionsDirectory;
2287
2920
  registrationsDirectory;
2288
2921
  cursorsDirectory;
2289
2922
  constructor(directory) {
2290
2923
  if (typeof directory !== "string" || directory.length === 0) throw new CodexHostAdapterError("configuration", "Codex outbox directory is required.");
2291
2924
  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");
2925
+ this.entriesDirectory = join3(directory, "entries");
2926
+ this.bindingsDirectory = join3(directory, "bindings");
2927
+ this.sessionsDirectory = join3(directory, "sessions");
2928
+ this.activeContextsDirectory = join3(directory, "active-contexts");
2929
+ this.turnSelectionsDirectory = join3(directory, "turn-selections");
2930
+ this.registrationsDirectory = join3(directory, "transcript-registrations");
2931
+ this.cursorsDirectory = join3(directory, "transcript-cursors");
2298
2932
  }
2299
2933
  async prepare() {
2300
- for (const path of [this.directory, this.entriesDirectory, this.bindingsDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
2934
+ for (const path of [this.directory, this.entriesDirectory, this.bindingsDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
2301
2935
  await ensurePrivateOwnerDirectory(path);
2302
2936
  }
2303
2937
  }
@@ -2313,7 +2947,7 @@ var CodexOutboxStore = class {
2313
2947
  for (const name of await readdir(this.entriesDirectory)) {
2314
2948
  if (!name.endsWith(".json")) continue;
2315
2949
  try {
2316
- const value = JSON.parse(await readPrivateFile(join2(this.entriesDirectory, name)));
2950
+ const value = JSON.parse(await readPrivateFile(join3(this.entriesDirectory, name)));
2317
2951
  validateEnvelope(value);
2318
2952
  entries.push(value);
2319
2953
  } catch {
@@ -2328,7 +2962,7 @@ var CodexOutboxStore = class {
2328
2962
  try {
2329
2963
  await unlink2(this.entryPath(entry.submission_ref));
2330
2964
  } catch (error) {
2331
- if (!isNotFound(error)) throw storageError("Unable to remove the Codex outbox entry.");
2965
+ if (!isNotFound2(error)) throw storageError("Unable to remove the Codex outbox entry.");
2332
2966
  }
2333
2967
  }
2334
2968
  async writeBinding(binding) {
@@ -2341,8 +2975,8 @@ var CodexOutboxStore = class {
2341
2975
  * prompt replaces this projection, so it cannot inherit the prior turn's
2342
2976
  * Request by session alone.
2343
2977
  */
2344
- async activateInvocationContext(identity, promptRef, expiresAt) {
2345
- const context = codexInvocationContextFromTurn(identity, promptRef, expiresAt);
2978
+ async activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef) {
2979
+ const context = codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef);
2346
2980
  await this.prepare();
2347
2981
  const path = this.activeContextPath(identity);
2348
2982
  try {
@@ -2351,11 +2985,30 @@ var CodexOutboxStore = class {
2351
2985
  try {
2352
2986
  await unlink2(path);
2353
2987
  } catch (unlinkError) {
2354
- if (!isNotFound(unlinkError)) throw storageError("Unable to clear a failed Codex Host context replacement.");
2988
+ if (!isNotFound2(unlinkError)) throw storageError("Unable to clear a failed Codex Host context replacement.");
2355
2989
  }
2356
2990
  throw error;
2357
2991
  }
2358
2992
  }
2993
+ async writeInvocationTurnSelection(selection) {
2994
+ if (!isHostInvocationTurnSelection(selection)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection is invalid.");
2995
+ await this.prepare();
2996
+ await writeAtomicJson(this.turnSelectionPath(selection.host_source_ref, selection.host_session_ref), selection);
2997
+ }
2998
+ async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
2999
+ if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
3000
+ await this.prepare();
3001
+ return readOptionalJson(this.turnSelectionPath(hostSourceRef, hostSessionRef), validateTurnSelection);
3002
+ }
3003
+ async attachInvocationTurnGeneration(identity, promptRef, turnGenerationRef) {
3004
+ const selection = await this.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
3005
+ if (selection === null || selection.prompt_ref !== promptRef || selection.turn_generation_ref !== turnGenerationRef) return false;
3006
+ const path = this.activeContextPath(identity);
3007
+ const active = await readOptionalJson(path, validateCodexHostInvocationContext);
3008
+ if (active === null || active.prompt_ref !== promptRef || !sameIdentity(active.identity, identity)) return false;
3009
+ await writeAtomicJson(path, { ...active, turn_generation_ref: turnGenerationRef });
3010
+ return true;
3011
+ }
2359
3012
  /**
2360
3013
  * Publish a Request reference only while the acknowledged binding is still
2361
3014
  * the active turn. A delayed relay response therefore cannot overwrite a
@@ -2365,17 +3018,25 @@ var CodexOutboxStore = class {
2365
3018
  validateBinding(binding);
2366
3019
  const active = await this.readInvocationContext(binding.identity.host_source_ref, binding.identity.host_session_ref, now);
2367
3020
  if (active === null || !sameIdentity(active.identity, binding.identity)) return false;
2368
- await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding));
3021
+ await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding, active.turn_generation_ref));
2369
3022
  return true;
2370
3023
  }
2371
- /** Return only the active session projection; never fall back to another turn. */
3024
+ /** Return only the live session projection; never fall back to another turn. */
2372
3025
  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
3026
  if (!(now instanceof Date) || !Number.isFinite(now.getTime())) throw new CodexHostAdapterError("invalid_input", "Codex Host context clock is invalid.");
3027
+ const context = await this.readRawInvocationContext(hostSourceRef, hostSessionRef);
3028
+ return isLiveSessionProjection(context, now) && invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
3029
+ }
3030
+ /**
3031
+ * The latest Codex session projection even when past expiry, so the
3032
+ * invocation authenticator can report an expired context distinctly from
3033
+ * a missing one. Every other caller keeps the fail-closed read above.
3034
+ */
3035
+ async readRawInvocationContext(hostSourceRef, hostSessionRef) {
3036
+ if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
2375
3037
  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;
3038
+ const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, (file) => readOptionalJson(file, validateCodexHostInvocationContext));
3039
+ return invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
2379
3040
  }
2380
3041
  /** Clear the projection only if the caller still owns its exact active turn. */
2381
3042
  async clearInvocationContext(identity, now = /* @__PURE__ */ new Date()) {
@@ -2385,7 +3046,7 @@ var CodexOutboxStore = class {
2385
3046
  try {
2386
3047
  await unlink2(this.activeContextPath(identity));
2387
3048
  } catch (error) {
2388
- if (!isNotFound(error)) throw storageError("Unable to clear the Codex Host invocation context.");
3049
+ if (!isNotFound2(error)) throw storageError("Unable to clear the Codex Host invocation context.");
2389
3050
  }
2390
3051
  return true;
2391
3052
  }
@@ -2434,7 +3095,7 @@ var CodexOutboxStore = class {
2434
3095
  for (const name of await readdir(this.registrationsDirectory)) {
2435
3096
  if (!name.endsWith(".json")) continue;
2436
3097
  try {
2437
- const value = JSON.parse(await readPrivateFile(join2(this.registrationsDirectory, name)));
3098
+ const value = JSON.parse(await readPrivateFile(join3(this.registrationsDirectory, name)));
2438
3099
  validateRegistration(value);
2439
3100
  registrations.push(value);
2440
3101
  } catch {
@@ -2476,29 +3137,32 @@ var CodexOutboxStore = class {
2476
3137
  }
2477
3138
  entryPath(submissionRef) {
2478
3139
  if (!SAFE_REF4.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
2479
- return join2(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
3140
+ return join3(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
2480
3141
  }
2481
3142
  bindingPath(identity) {
2482
- return join2(this.bindingsDirectory, `${identityKey(identity)}.json`);
3143
+ return join3(this.bindingsDirectory, `${identityKey(identity)}.json`);
2483
3144
  }
2484
3145
  sessionPath(sourceRef, sessionRef) {
2485
- return join2(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
3146
+ return join3(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
2486
3147
  }
2487
3148
  activeContextPath(identity) {
2488
- return join2(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
3149
+ return join3(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
3150
+ }
3151
+ turnSelectionPath(sourceRef, sessionRef) {
3152
+ return join3(this.turnSelectionsDirectory, `${invocationContextKey({ host_source_ref: sourceRef, host_session_ref: sessionRef })}.json`);
2489
3153
  }
2490
3154
  registrationPath(sourceRef, sessionRef) {
2491
- return join2(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
3155
+ return join3(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
2492
3156
  }
2493
3157
  cursorPath(sourceRef, sessionRef) {
2494
- return join2(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
3158
+ return join3(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
2495
3159
  }
2496
3160
  };
2497
3161
  async function removeOwnerState(path, what) {
2498
3162
  try {
2499
3163
  await unlink2(path);
2500
3164
  } catch (error) {
2501
- if (!isNotFound(error)) throw storageError(`Unable to retire the Codex transcript ${what}.`);
3165
+ if (!isNotFound2(error)) throw storageError(`Unable to retire the Codex transcript ${what}.`);
2502
3166
  }
2503
3167
  }
2504
3168
  function sessionStateKey(sourceRef, sessionRef) {
@@ -2529,7 +3193,7 @@ async function findBindingByAlias(directory, sourceRef, sessionRef, alias) {
2529
3193
  }
2530
3194
  async function readBindingCandidate(directory, name) {
2531
3195
  try {
2532
- const value = JSON.parse(await readPrivateFile(join2(directory, name)));
3196
+ const value = JSON.parse(await readPrivateFile(join3(directory, name)));
2533
3197
  validateBinding(value);
2534
3198
  return value;
2535
3199
  } catch {
@@ -2548,14 +3212,14 @@ async function writeAtomicJson(path, value) {
2548
3212
  if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
2549
3213
  const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
2550
3214
  try {
2551
- const handle = await open3(temp, "wx", 384);
3215
+ const handle = await open5(temp, "wx", 384);
2552
3216
  try {
2553
3217
  await handle.writeFile(text, "utf8");
2554
3218
  await handle.sync();
2555
3219
  } finally {
2556
3220
  await handle.close();
2557
3221
  }
2558
- await rename2(temp, path);
3222
+ await rename3(temp, path);
2559
3223
  await syncDirectory(dirname(path));
2560
3224
  } catch (error) {
2561
3225
  try {
@@ -2568,7 +3232,7 @@ async function writeAtomicJson(path, value) {
2568
3232
  }
2569
3233
  async function syncDirectory(path) {
2570
3234
  try {
2571
- const handle = await open3(path, "r");
3235
+ const handle = await open5(path, "r");
2572
3236
  try {
2573
3237
  await handle.sync();
2574
3238
  } finally {
@@ -2583,7 +3247,7 @@ async function readOptionalJson(path, validate) {
2583
3247
  validate(value);
2584
3248
  return value;
2585
3249
  } catch (error) {
2586
- if (isNotFound(error)) return null;
3250
+ if (isNotFound2(error)) return null;
2587
3251
  if (error instanceof CodexHostAdapterError) throw error;
2588
3252
  throw storageError("Codex owner state is malformed.");
2589
3253
  }
@@ -2592,8 +3256,8 @@ async function ensurePrivateOwnerDirectory(path) {
2592
3256
  try {
2593
3257
  await lstat3(path);
2594
3258
  } catch (error) {
2595
- if (!isNotFound(error)) throw storageError("Codex owner state directory is unavailable.");
2596
- await mkdir2(path, { recursive: true, mode: 448 });
3259
+ if (!isNotFound2(error)) throw storageError("Codex owner state directory is unavailable.");
3260
+ await mkdir3(path, { recursive: true, mode: 448 });
2597
3261
  }
2598
3262
  const stats = await lstat3(path);
2599
3263
  const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
@@ -2604,7 +3268,7 @@ async function ensurePrivateOwnerDirectory(path) {
2604
3268
  async function readPrivateFile(path) {
2605
3269
  let handle;
2606
3270
  try {
2607
- handle = await open3(path, O_RDONLY2 | O_NOFOLLOW);
3271
+ handle = await open5(path, O_RDONLY3 | O_NOFOLLOW2);
2608
3272
  const stats = await handle.stat();
2609
3273
  const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
2610
3274
  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 +3280,7 @@ async function readPrivateFile(path) {
2616
3280
  }
2617
3281
  }
2618
3282
  function validateEnvelope(value) {
2619
- if (!isRecord3(value)) throw storageError("Codex outbox entry is malformed.");
3283
+ if (!isRecord4(value)) throw storageError("Codex outbox entry is malformed.");
2620
3284
  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
3285
  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
3286
  validateIdentity3(value.identity);
@@ -2634,7 +3298,7 @@ function validateEnvelopeReferences(value) {
2634
3298
  if (typeof value.origin_input !== "boolean") throw storageError("Codex outbox origin disposition is malformed.");
2635
3299
  }
2636
3300
  function validateRegistration(value) {
2637
- if (!isRecord3(value)) throw storageError("Codex transcript registration is malformed.");
3301
+ if (!isRecord4(value)) throw storageError("Codex transcript registration is malformed.");
2638
3302
  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
3303
  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
3304
  validateRegistrationFields(value);
@@ -2647,15 +3311,15 @@ function validateRegistrationFields(value) {
2647
3311
  if (!isTimestamp(value.registered_at) || !isTimestamp(value.expires_at)) throw storageError(`Codex transcript registration timestamps are malformed (${refusalFacts([{ name: "registered_at", value: value.registered_at }, { name: "expires_at", value: value.expires_at }])}).`);
2648
3312
  }
2649
3313
  function validRegistrationCwd(value) {
2650
- return value === void 0 || typeof value === "string" && isAbsolute(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
3314
+ return value === void 0 || typeof value === "string" && isAbsolute2(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
2651
3315
  }
2652
3316
  function validateCursor(value) {
2653
- if (!isRecord3(value)) throw storageError("Codex transcript cursor is malformed.");
3317
+ if (!isRecord4(value)) throw storageError("Codex transcript cursor is malformed.");
2654
3318
  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
3319
  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
3320
  if (!validCursorSource(value) || !validCursorPosition(value) || !validCursorStatus(value)) throw storageError("Codex transcript cursor fields are malformed.");
2657
3321
  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.");
3322
+ if (value.reason !== void 0 && !isHostTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
2659
3323
  }
2660
3324
  function validCursorSource(value) {
2661
3325
  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 +3331,7 @@ function validCursorStatus(value) {
2667
3331
  return (value.status === "active" || value.status === "quarantined") && isTimestamp(value.updated_at);
2668
3332
  }
2669
3333
  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;
3334
+ 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
3335
  return validActiveTaskIdentity(value) && validActiveTaskReferences(value) && validActiveTaskUsage(value);
2672
3336
  }
2673
3337
  function validActiveTaskIdentity(value) {
@@ -2683,7 +3347,7 @@ function validActiveTaskUsage(value) {
2683
3347
  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
3348
  }
2685
3349
  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;
3350
+ if (!isRecord4(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
2687
3351
  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
3352
  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
3353
  }
@@ -2703,7 +3367,7 @@ function validateEnvelopeTimestamps(value) {
2703
3367
  if (!isTimestamp(value.expires_at)) throw storageError("Codex outbox timestamps are malformed.");
2704
3368
  }
2705
3369
  function validateBinding(value) {
2706
- if (!isRecord3(value)) throw storageError("Codex turn binding is malformed.");
3370
+ if (!isRecord4(value)) throw storageError("Codex turn binding is malformed.");
2707
3371
  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
3372
  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
3373
  validateIdentity3(value.identity);
@@ -2711,19 +3375,22 @@ function validateBinding(value) {
2711
3375
  if (!isStringList(value.codex_turn_ids) || !isTimestamp(value.bound_at) || !isTimestamp(value.expires_at)) throw storageError("Codex turn binding fields are malformed.");
2712
3376
  }
2713
3377
  function validateSession(value) {
2714
- if (!isRecord3(value)) throw storageError("Codex session snapshot is malformed.");
3378
+ if (!isRecord4(value)) throw storageError("Codex session snapshot is malformed.");
2715
3379
  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
3380
  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
3381
  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
3382
  }
3383
+ function validateTurnSelection(value) {
3384
+ if (!isHostInvocationTurnSelection(value)) throw storageError("Codex Host turn selection is malformed.");
3385
+ }
2719
3386
  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.");
3387
+ 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
3388
  }
2722
3389
  function isJsonValue(value) {
2723
3390
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
2724
3391
  if (typeof value === "number") return Number.isFinite(value);
2725
3392
  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));
3393
+ return isRecord4(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
2727
3394
  }
2728
3395
  function isStringList(value) {
2729
3396
  return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate));
@@ -2736,101 +3403,49 @@ function sameKeys(value, required, optional = []) {
2736
3403
  const keys = Object.keys(value);
2737
3404
  return keys.length >= required.length && keys.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
2738
3405
  }
2739
- function isRecord3(value) {
3406
+ function isRecord4(value) {
2740
3407
  return value !== null && typeof value === "object" && !Array.isArray(value);
2741
3408
  }
2742
- function isNotFound(error) {
2743
- return isRecord3(error) && error.code === "ENOENT";
3409
+ function isNotFound2(error) {
3410
+ return isRecord4(error) && error.code === "ENOENT";
2744
3411
  }
2745
3412
  function storageError(message) {
2746
3413
  return new CodexHostAdapterError("storage", message);
2747
3414
  }
2748
3415
 
2749
3416
  // host/codex/src/codex-producer.ts
2750
- var STATE_SCHEMA = "ceal.codex_producer_state.v1";
2751
3417
  var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2752
3418
  var CodexProducer = class {
3419
+ producer_id = "codex";
3420
+ producer_label = "Codex";
3421
+ outbox_directory;
2753
3422
  store;
2754
- statePath;
2755
- installationRef;
2756
- epoch;
2757
- relay;
2758
- now;
2759
- tail = Promise.resolve();
3423
+ core;
2760
3424
  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
3425
  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());
3426
+ this.outbox_directory = this.store.directory;
3427
+ this.core = new HostProducerDrainCore(this, options);
2770
3428
  }
2771
3429
  drainOutbox() {
2772
- const run = this.tail.then(() => this.drainLocked());
2773
- this.tail = run.then(() => void 0, () => void 0);
2774
- return run;
3430
+ return this.core.drainOutbox();
2775
3431
  }
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 };
3432
+ fail(code, message) {
3433
+ return new CodexHostAdapterError(code, message);
2806
3434
  }
2807
- async hasReservation(submissionRef) {
2808
- return (await this.readState()).pending[submissionRef] !== void 0;
3435
+ listEnvelopes() {
3436
+ return this.store.list();
2809
3437
  }
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;
3438
+ removeEnvelope(envelope) {
3439
+ return this.store.remove(envelope);
2827
3440
  }
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 });
3441
+ submissionRefOf(envelope) {
3442
+ return envelope.submission_ref;
3443
+ }
3444
+ expiresAtOf(envelope) {
3445
+ return envelope.expires_at;
3446
+ }
3447
+ createdAtOf(envelope) {
3448
+ return envelope.created_at;
2834
3449
  }
2835
3450
  async mapEnvelope(envelope) {
2836
3451
  const prompt = observation(envelope, "prompt");
@@ -2846,7 +3461,7 @@ var CodexProducer = class {
2846
3461
  const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
2847
3462
  drafts.push({ frame_kind: "session_started", payload: compact({
2848
3463
  local_session_ref: envelope.identity.host_session_ref,
2849
- working_directory_ref: `workspace:${createHash3("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
3464
+ working_directory_ref: `workspace:${createHash4("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
2850
3465
  model: recordText(session.value, "model"),
2851
3466
  host_version: nestedText(session.value, "host_version"),
2852
3467
  observations: [session]
@@ -2871,21 +3486,6 @@ var CodexProducer = class {
2871
3486
  }) });
2872
3487
  return drafts;
2873
3488
  }
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
3489
  };
2890
3490
  function mapTerminal(envelope) {
2891
3491
  const timing = observation(envelope, "timing");
@@ -2913,7 +3513,7 @@ function compact(value) {
2913
3513
  return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
2914
3514
  }
2915
3515
  function asRecord(value) {
2916
- return isRecord4(value) ? value : void 0;
3516
+ return isRecord5(value) ? value : void 0;
2917
3517
  }
2918
3518
  function recordText(value, key) {
2919
3519
  const record3 = asRecord(value);
@@ -2927,55 +3527,16 @@ function nonNegativeNumber(value) {
2927
3527
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
2928
3528
  }
2929
3529
  function safeReasonRef(value) {
2930
- return SAFE_REF5.test(value) ? value : `reason:${createHash3("sha256").update(value, "utf8").digest("hex")}`;
3530
+ return SAFE_REF5.test(value) ? value : `reason:${createHash4("sha256").update(value, "utf8").digest("hex")}`;
2931
3531
  }
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) {
3532
+ function isRecord5(value) {
2967
3533
  return value !== null && typeof value === "object" && !Array.isArray(value);
2968
3534
  }
2969
3535
 
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
3536
  // host/codex/src/codex-decoder.ts
2976
- import { createHash as createHash4 } from "node:crypto";
3537
+ import { createHash as createHash5 } from "node:crypto";
2977
3538
  var SAFE_REF6 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2978
- var MAX_PAYLOAD_BYTES = 64 * 1024;
3539
+ var MAX_PAYLOAD_BYTES = 512 * 1024;
2979
3540
  function observationRef(event, identity, value) {
2980
3541
  return cealHostObservationRef("codex", shortDigest({ event, identity, value }));
2981
3542
  }
@@ -2996,14 +3557,14 @@ function turnObservationValue(sessionId, turnRef, status) {
2996
3557
  return { status, host_session_id_sha256: sha256(sessionId), host_turn_ref: turnRef };
2997
3558
  }
2998
3559
  function sha256(value) {
2999
- return createHash4("sha256").update(value, "utf8").digest("hex");
3560
+ return createHash5("sha256").update(value, "utf8").digest("hex");
3000
3561
  }
3001
3562
  function shortDigest(value) {
3002
3563
  return cealRequestSha256(value);
3003
3564
  }
3004
3565
 
3005
3566
  // host/codex/src/codex-transcript-baseline.ts
3006
- import { createHash as createHash5 } from "node:crypto";
3567
+ import { createHash as createHash6 } from "node:crypto";
3007
3568
 
3008
3569
  // host/codex/src/codex-transcript-decoder.ts
3009
3570
  var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
@@ -3015,40 +3576,40 @@ function decodeCodexTranscriptLine(line) {
3015
3576
  } catch {
3016
3577
  reject("Codex transcript record is not valid JSON.");
3017
3578
  }
3018
- if (!isRecord5(value)) reject("Codex transcript record envelope is malformed.");
3579
+ if (!isRecord6(value)) reject("Codex transcript record envelope is malformed.");
3019
3580
  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) {
3581
+ const timestamp4 = safeTimestamp(value.timestamp);
3582
+ if (!isRecord6(value.payload) || typeof value.type !== "string") reject("Codex transcript record payload is malformed.");
3583
+ if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal, timestamp4);
3584
+ if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal, timestamp4);
3585
+ if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal, timestamp4);
3586
+ if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal, timestamp4);
3587
+ return { kind: "ignored", ordinal, timestamp: timestamp4 };
3588
+ }
3589
+ function decodeTurnContext(payload, ordinal, timestamp4) {
3029
3590
  return {
3030
3591
  kind: "turn_context",
3031
3592
  ordinal,
3032
- timestamp: timestamp5,
3033
- turn_id: safeRef3(payload.turn_id, "Codex turn context identity is invalid."),
3593
+ timestamp: timestamp4,
3594
+ turn_id: safeRef4(payload.turn_id, "Codex turn context identity is invalid."),
3034
3595
  model: boundedNonEmptyText(payload.model, 256, "Codex turn context model is invalid.")
3035
3596
  };
3036
3597
  }
3037
- function decodeSessionMeta(payload, ordinal, timestamp5) {
3038
- const sessionId = safeRef3(payload.id, "Codex transcript session identity is invalid.");
3598
+ function decodeSessionMeta(payload, ordinal, timestamp4) {
3599
+ const sessionId = safeRef4(payload.id, "Codex transcript session identity is invalid.");
3039
3600
  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 };
3601
+ return { kind: "session_meta", ordinal, timestamp: timestamp4, session_id: sessionId, cli_version: cliVersion };
3041
3602
  }
3042
- function decodeEventMessage(payload, ordinal, timestamp5) {
3603
+ function decodeEventMessage(payload, ordinal, timestamp4) {
3043
3604
  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) };
3605
+ 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
3606
  }
3046
3607
  if (payload.type === "task_complete") {
3047
3608
  return {
3048
3609
  kind: "task_complete",
3049
3610
  ordinal,
3050
- timestamp: timestamp5,
3051
- turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."),
3611
+ timestamp: timestamp4,
3612
+ turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
3052
3613
  started_at: safeNumber(payload.started_at),
3053
3614
  completed_at: safeNumber(payload.completed_at),
3054
3615
  duration_ms: safeNumber(payload.duration_ms),
@@ -3059,20 +3620,20 @@ function decodeEventMessage(payload, ordinal, timestamp5) {
3059
3620
  return {
3060
3621
  kind: "turn_aborted",
3061
3622
  ordinal,
3062
- timestamp: timestamp5,
3063
- turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."),
3623
+ timestamp: timestamp4,
3624
+ turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
3064
3625
  started_at: safeNumber(payload.started_at),
3065
3626
  completed_at: safeNumber(payload.completed_at),
3066
3627
  duration_ms: safeNumber(payload.duration_ms),
3067
3628
  reason: boundedText(payload.reason, 256, "Codex task abort reason is invalid.")
3068
3629
  };
3069
3630
  }
3070
- if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp5);
3071
- return { kind: "ignored", ordinal, timestamp: timestamp5 };
3631
+ if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp4);
3632
+ return { kind: "ignored", ordinal, timestamp: timestamp4 };
3072
3633
  }
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" };
3634
+ function decodeTokenCount(payload, ordinal, timestamp4) {
3635
+ if (payload.info === null) return { kind: "token_count", ordinal, timestamp: timestamp4 };
3636
+ if (!isRecord6(payload.info)) return { kind: "token_count", ordinal, timestamp: timestamp4, usage_gap_reason: "token_count_shape_not_attributable" };
3076
3637
  const lastUsage = optionalTokenUsage(payload.info.last_token_usage);
3077
3638
  const totalUsage = optionalTokenUsage(payload.info.total_token_usage);
3078
3639
  const gap = lastUsage.status === "invalid" || totalUsage.status === "invalid";
@@ -3080,7 +3641,7 @@ function decodeTokenCount(payload, ordinal, timestamp5) {
3080
3641
  return {
3081
3642
  kind: "token_count",
3082
3643
  ordinal,
3083
- timestamp: timestamp5,
3644
+ timestamp: timestamp4,
3084
3645
  ...lastUsage.status === "valid" ? { last_usage: lastUsage.value } : {},
3085
3646
  ...totalUsage.status === "valid" ? { total_usage: totalUsage.value } : {},
3086
3647
  ...usageGapReason === void 0 ? {} : { usage_gap_reason: usageGapReason }
@@ -3096,7 +3657,7 @@ function optionalTokenUsage(value) {
3096
3657
  }
3097
3658
  }
3098
3659
  function decodeTokenUsage(value) {
3099
- if (!isRecord5(value)) reject("Codex token usage is malformed.");
3660
+ if (!isRecord6(value)) reject("Codex token usage is malformed.");
3100
3661
  const usage = {
3101
3662
  input_tokens: safeTokenCount(value.input_tokens),
3102
3663
  cached_input_tokens: safeTokenCount(value.cached_input_tokens),
@@ -3108,20 +3669,20 @@ function decodeTokenUsage(value) {
3108
3669
  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
3670
  return usage;
3110
3671
  }
3111
- function decodeResponseItem(payload, ordinal, timestamp5) {
3112
- if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp5 };
3672
+ function decodeResponseItem(payload, ordinal, timestamp4) {
3673
+ if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp4 };
3113
3674
  const metadata = userMessageMetadata(payload.internal_chat_message_metadata_passthrough);
3114
3675
  const inputKind = inputKindFromContentItems(metadata.content_item_kinds);
3115
- if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp5 };
3676
+ if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp4 };
3116
3677
  const prompt = userTextContent(payload.content);
3117
3678
  return {
3118
3679
  kind: "user_text",
3119
3680
  ordinal,
3120
- timestamp: timestamp5,
3681
+ timestamp: timestamp4,
3121
3682
  prompt,
3122
3683
  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.")
3684
+ message_id: safeRef4(payload.id, "Codex user message identity is invalid."),
3685
+ turn_id: safeRef4(metadata.turn_id, "Codex user message task identity is invalid.")
3125
3686
  };
3126
3687
  }
3127
3688
  function inputKindFromContentItems(kinds) {
@@ -3130,13 +3691,13 @@ function inputKindFromContentItems(kinds) {
3130
3691
  return kinds[0] === "goal.internal_context" ? "host_automatic" : void 0;
3131
3692
  }
3132
3693
  function userMessageMetadata(value) {
3133
- if (!isRecord5(value) || !Array.isArray(value.content_item_kinds)) reject("Codex user message provenance is malformed.");
3694
+ if (!isRecord6(value) || !Array.isArray(value.content_item_kinds)) reject("Codex user message provenance is malformed.");
3134
3695
  return { ...value, content_item_kinds: value.content_item_kinds };
3135
3696
  }
3136
3697
  function userTextContent(value) {
3137
3698
  if (!Array.isArray(value)) reject(`Codex user text content is malformed (${refusalFact("kind", typeof value, "array")}).`);
3138
3699
  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")}).`);
3700
+ 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
3701
  return boundedText(value[0].text, MAX_PROMPT_BYTES, "Codex user text is invalid.");
3141
3702
  }
3142
3703
  function safeOrdinal(value) {
@@ -3157,7 +3718,7 @@ function safeTimestamp(value) {
3157
3718
  if (!Number.isFinite(Date.parse(value))) reject(`Codex transcript timestamp is invalid (${refusalFact("parsed_ms", Date.parse(value), "a finite epoch milliseconds value")}).`);
3158
3719
  return value;
3159
3720
  }
3160
- function safeRef3(value, message) {
3721
+ function safeRef4(value, message) {
3161
3722
  if (typeof value !== "string" || !SAFE_REF7.test(value)) reject(message);
3162
3723
  return value;
3163
3724
  }
@@ -3172,7 +3733,7 @@ function boundedNonEmptyText(value, maximum, message) {
3172
3733
  if (result.length === 0) reject(message);
3173
3734
  return result;
3174
3735
  }
3175
- function isRecord5(value) {
3736
+ function isRecord6(value) {
3176
3737
  return value !== null && typeof value === "object" && !Array.isArray(value);
3177
3738
  }
3178
3739
  function reject(message) {
@@ -3216,7 +3777,7 @@ function assertCodexTranscriptHeader(registration, record3) {
3216
3777
  ])}).`);
3217
3778
  }
3218
3779
  function digest(value) {
3219
- return createHash5("sha256").update(value).digest("hex");
3780
+ return createHash6("sha256").update(value).digest("hex");
3220
3781
  }
3221
3782
  function timestamp3(now) {
3222
3783
  const value = now();
@@ -3232,78 +3793,53 @@ function codexSubmissionReference(hostInputRef, event, ordinal) {
3232
3793
  }
3233
3794
 
3234
3795
  // 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 {
3796
+ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
3238
3797
  store;
3239
- outboxRetentionMs;
3240
- now;
3241
- log;
3242
- producerInstallationRef;
3243
3798
  registerCurrentPrompt;
3799
+ releaseCurrentPrompt;
3800
+ diagnostic;
3244
3801
  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
- `);
3802
+ super(options, {
3803
+ default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
3804
+ invalid_outbox: () => new CodexHostAdapterError("configuration", "Codex transcript watcher outbox is invalid."),
3805
+ clock_failure: () => new CodexHostAdapterError("storage", "Codex watcher clock is invalid.")
3252
3806
  });
3253
- this.producerInstallationRef = options.producer_installation_ref;
3807
+ this.store = new CodexOutboxStore(options.outbox_dir);
3254
3808
  this.registerCurrentPrompt = options.register_current_prompt;
3809
+ this.releaseCurrentPrompt = options.release_current_prompt;
3810
+ this.diagnostic = options.diagnostic ?? (() => void 0);
3255
3811
  }
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 };
3812
+ transcriptPathOf(registration) {
3813
+ return registration.transcript_path;
3268
3814
  }
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
- }
3815
+ async listRegistrations() {
3816
+ return this.store.listTranscriptRegistrations();
3286
3817
  }
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 };
3818
+ async scanOpened(handle, registration) {
3819
+ const { size, device, inode } = await ownedCodexTranscriptFile(handle);
3820
+ const preflight = await this.preflight(registration, handle, size, device, inode);
3821
+ if (preflight.cursor === null) return preflight.result;
3822
+ const complete = await this.readChunk(handle, preflight.cursor.offset, size);
3823
+ if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
3824
+ return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
3825
+ }
3826
+ async scanFailed(handle, registration, error) {
3827
+ this.diagnostic("watcher_scan_failed", codexTranscriptScanFailureKind(error));
3828
+ await this.quarantineFailure(registration, handle);
3829
+ return { records: 0, enqueued: 0, quarantined: true };
3830
+ }
3831
+ async retireMissing(registration) {
3832
+ await this.releaseStoredPromptBestEffort(registration.host_session_ref, await this.store.readTranscriptCursor(registration).catch(() => null));
3833
+ return this.retireMissingTranscript(registration.transcript_path, "Codex", () => this.store.retireTranscriptRegistration(registration));
3301
3834
  }
3302
3835
  async preflight(registration, handle, size, device, inode) {
3303
3836
  const existing = await this.store.readTranscriptCursor(registration);
3304
3837
  let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
3305
- 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);
3838
+ if (cursor.status === "quarantined") {
3839
+ if (cursor.reason !== "transcript_malformed") await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
3840
+ return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
3841
+ }
3842
+ const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
3307
3843
  if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
3308
3844
  await this.quarantine(registration, cursor, device, inode, reason);
3309
3845
  return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
@@ -3329,39 +3865,53 @@ var CodexTranscriptWatcher = class {
3329
3865
  async observeSessionMetadata(registration, record3) {
3330
3866
  if (record3.kind !== "session_meta") return;
3331
3867
  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 } });
3868
+ if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record3.cli_version } });
3333
3869
  }
3334
3870
  async commitCursor(registration, handle, cursor, consumedBytes, device, inode, lastOrdinal, activeTask) {
3335
3871
  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) });
3872
+ const anchor = await this.readAnchor(handle, offset, CODEX_TRANSCRIPT_ANCHOR_BYTES);
3873
+ 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
3874
  }
3341
3875
  async quarantineFailure(registration, handle) {
3342
3876
  if (handle === void 0) return;
3343
3877
  const stats = await handle.stat().catch(() => null);
3344
3878
  if (stats === null) return;
3345
3879
  const existing = await this.store.readTranscriptCursor(registration).catch(() => null);
3346
- await this.quarantine(registration, existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now), String(stats.dev), String(stats.ino), "transcript_malformed");
3880
+ const quarantined = {
3881
+ ...existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now),
3882
+ transcript_path: registration.transcript_path,
3883
+ device: String(stats.dev),
3884
+ inode: String(stats.ino),
3885
+ status: "quarantined",
3886
+ reason: "transcript_malformed",
3887
+ updated_at: this.stamp()
3888
+ };
3889
+ await this.store.writeTranscriptCursor(quarantined);
3347
3890
  }
3348
3891
  async processRecord(registration, activeTask, record3) {
3349
- if (record3.kind === "task_started") {
3350
- return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
3351
- }
3892
+ if (record3.kind === "task_started") return this.processTaskStarted(registration, activeTask, record3);
3352
3893
  if (record3.kind === "turn_context") return { ...activeTask === void 0 ? {} : { activeTask: record3.turn_id === activeTask.codex_turn_id ? { ...activeTask, model: record3.model } : activeTask }, enqueued: 0 };
3353
3894
  if (record3.kind === "token_count") return { ...activeTask === void 0 ? {} : { activeTask: accumulateTokenCount(activeTask, record3) }, enqueued: 0 };
3354
3895
  if (record3.kind === "user_text") return this.processUserInput(registration, activeTask, record3);
3355
3896
  if (record3.kind === "task_complete" || record3.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record3);
3356
3897
  return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
3357
3898
  }
3899
+ async processTaskStarted(registration, activeTask, record3) {
3900
+ if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
3901
+ return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
3902
+ }
3358
3903
  async processUserInput(registration, activeTask, record3) {
3359
- if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
3904
+ if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id) {
3905
+ const adopted = await this.adoptHookCorrelatedTurn(registration, activeTask, record3);
3906
+ if (adopted === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
3907
+ activeTask = adopted;
3908
+ }
3360
3909
  const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id, record3.message_id, record3.ordinal, record3.prompt);
3361
3910
  const origin = activeTask.origin_host_input_ref === void 0;
3362
3911
  const promptValue = promptObservationValue(record3.prompt);
3363
3912
  const currentPromptRef = hostProducerPromptRef(promptValue.body_sha256);
3364
3913
  const promptRef = activeTask.prompt_ref ?? currentPromptRef;
3914
+ const steeredPromptRef = await this.steeredPromptRef(registration, activeTask, origin, currentPromptRef);
3365
3915
  const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
3366
3916
  const observations = [{
3367
3917
  observation_ref: observationRef("transcript_user_text", hostInputRef, record3.prompt),
@@ -3385,16 +3935,90 @@ var CodexTranscriptWatcher = class {
3385
3935
  origin_input: origin,
3386
3936
  submission_ref: codexSubmissionReference(hostInputRef, "input", record3.ordinal),
3387
3937
  created_at: record3.timestamp,
3388
- expires_at: expiration(this.now, this.outboxRetentionMs)
3938
+ expires_at: this.expire(this.outboxRetentionMs)
3939
+ };
3940
+ const persisted = await this.persistUserInputTurn(registration, activeTask, record3, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope);
3941
+ if (steeredPromptRef !== null) await this.activateTranscriptTurnContext(identity, steeredPromptRef, envelope.expires_at);
3942
+ return persisted;
3943
+ }
3944
+ /**
3945
+ * The steered prompt to re-activate under the running task, or null when
3946
+ * there is nothing to repair. A steered message joins the running turn
3947
+ * with new prompt bytes but no task_started. When the steer hook already
3948
+ * replaced the turn selection (a turn id the hook did not recognize as
3949
+ * the running one, or a hook that ran before any context was live), the
3950
+ * running task's context no longer matches and the next invocation would
3951
+ * wait for a boundary that never comes. When the hook kept the live
3952
+ * selection, the context still matches and this stays null.
3953
+ */
3954
+ async steeredPromptRef(registration, activeTask, origin, currentPromptRef) {
3955
+ if (origin || activeTask.prompt_ref === void 0 || activeTask.prompt_ref === currentPromptRef) return null;
3956
+ const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref).catch(() => null);
3957
+ return selection?.prompt_ref === currentPromptRef ? currentPromptRef : null;
3958
+ }
3959
+ /**
3960
+ * Adopt the live hooked turn when its transcript message arrives before
3961
+ * (or without) its task_started, as an interrupted turn's follow-up can.
3962
+ * The UserPromptSubmit hook names the exact hooked prompt, so only a
3963
+ * message carrying those bytes adopts; inherited history and other turns
3964
+ * stay ignored. A stale active task for another turn releases first, the
3965
+ * same as a task_started supersession.
3966
+ */
3967
+ async adoptHookCorrelatedTurn(registration, activeTask, record3) {
3968
+ const currentPromptRef = hostProducerPromptRef(promptObservationValue(record3.prompt).body_sha256);
3969
+ const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref);
3970
+ if (selection?.prompt_ref !== currentPromptRef) return void 0;
3971
+ if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) {
3972
+ await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
3973
+ }
3974
+ return {
3975
+ codex_turn_id: record3.turn_id,
3976
+ host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id),
3977
+ started_at: record3.timestamp
3389
3978
  };
3390
- await this.registerPrompt(origin, registration, activeTask, currentPromptRef, record3.prompt, envelope.expires_at);
3391
- await this.store.put(envelope);
3392
- if (origin) await this.store.activateInvocationContext(identity, promptRef, envelope.expires_at);
3979
+ }
3980
+ async persistUserInputTurn(registration, activeTask, record3, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope) {
3981
+ const custodyRegistered = await this.registerPrompt(origin, registration, activeTask, currentPromptRef, record3.prompt, envelope.expires_at);
3982
+ if (custodyRegistered) this.diagnostic("watcher_turn_observed");
3983
+ try {
3984
+ await this.store.put(envelope);
3985
+ if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
3986
+ } catch (error) {
3987
+ if (custodyRegistered) await this.releasePrompt(registration.host_session_ref, activeTask, currentPromptRef).catch(() => void 0);
3988
+ throw error;
3989
+ }
3393
3990
  return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
3394
3991
  }
3395
3992
  async registerPrompt(origin, registration, activeTask, promptRef, prompt, expiresAt) {
3396
- if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return;
3397
- await this.registerCurrentPrompt({ producer_id: "codex", producer_installation_ref: this.producerInstallationRef, local_session_ref: registration.host_session_ref, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef, current_prompt: prompt, context_expires_at: expiresAt });
3993
+ if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return false;
3994
+ const register = this.registerCurrentPrompt;
3995
+ const installationRef = this.producerInstallationRef;
3996
+ return registerHostCurrentPromptDiagnosed(
3997
+ () => register({ producer_id: "codex", producer_installation_ref: installationRef, local_session_ref: registration.host_session_ref, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef, current_prompt: prompt, context_expires_at: expiresAt }),
3998
+ (event) => this.diagnostic(event)
3999
+ );
4000
+ }
4001
+ async activateTranscriptTurnContext(identity, promptRef, expiresAt) {
4002
+ try {
4003
+ const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
4004
+ const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
4005
+ await this.store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
4006
+ this.diagnostic("activation_recorded");
4007
+ } catch (error) {
4008
+ this.diagnostic("activation_failed");
4009
+ throw error;
4010
+ }
4011
+ }
4012
+ async releasePrompt(sessionRef, activeTask, promptRef) {
4013
+ if (this.producerInstallationRef === void 0 || this.releaseCurrentPrompt === void 0) return;
4014
+ await this.releaseCurrentPrompt({ producer_id: "codex", producer_installation_ref: this.producerInstallationRef, local_session_ref: sessionRef, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef });
4015
+ }
4016
+ async releaseStoredPromptBestEffort(sessionRef, cursor) {
4017
+ await this.releaseActiveTaskBestEffort(sessionRef, cursor?.active_task);
4018
+ }
4019
+ async releaseActiveTaskBestEffort(sessionRef, activeTask) {
4020
+ if (activeTask?.prompt_ref === void 0) return;
4021
+ await this.releasePrompt(sessionRef, activeTask, activeTask.prompt_ref).catch(() => void 0);
3398
4022
  }
3399
4023
  async processTerminal(registration, activeTask, record3) {
3400
4024
  if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id || activeTask.origin_host_input_ref === void 0 || activeTask.prompt_ref === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
@@ -3412,16 +4036,26 @@ var CodexTranscriptWatcher = class {
3412
4036
  origin_input: false,
3413
4037
  submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record3.ordinal),
3414
4038
  created_at: record3.timestamp,
3415
- expires_at: expiration(this.now, this.outboxRetentionMs)
4039
+ expires_at: this.expire(this.outboxRetentionMs)
3416
4040
  });
4041
+ await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
3417
4042
  await this.store.clearInvocationContext(identity, this.now());
3418
4043
  return { enqueued: 1 };
3419
4044
  }
3420
4045
  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) };
4046
+ await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
4047
+ const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at: this.stamp() };
3422
4048
  await this.store.writeTranscriptCursor(quarantined);
3423
4049
  }
3424
4050
  };
4051
+ function codexTranscriptScanFailureKind(error) {
4052
+ return transcriptScanFailureKind(error, (candidate) => {
4053
+ if (!(candidate instanceof CodexHostAdapterError)) return void 0;
4054
+ if (candidate.code === "payload_rejected") return "payload_rejected";
4055
+ if (candidate.code === "storage") return "read_error";
4056
+ return void 0;
4057
+ });
4058
+ }
3425
4059
  function accumulateTokenCount(activeTask, record3) {
3426
4060
  if (record3.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record3.usage_gap_reason };
3427
4061
  const last = record3.last_usage;
@@ -3455,19 +4089,18 @@ function terminalCostValue() {
3455
4089
  return notRecorded("provider_cost_not_observed");
3456
4090
  }
3457
4091
  function notRecorded(reason) {
3458
- return { status: "not_recorded", reason };
4092
+ return hostObservationNotRecorded(reason);
3459
4093
  }
3460
4094
  function tokenObservationValue(usage) {
3461
4095
  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",
4096
+ ...createHostTokenObservationValue({
4097
+ input: usage.input_tokens,
4098
+ output: usage.output_tokens,
4099
+ total: usage.total_tokens,
4100
+ cached_input: usage.cached_input_tokens,
4101
+ cache_write_input: usage.cache_write_input_tokens,
4102
+ reasoning_output: usage.reasoning_output_tokens
4103
+ }, "codex_transcript.event_msg.token_count.info.last_token_usage"),
3471
4104
  attribution: "sequential_active_task",
3472
4105
  total_usage_role: "dedup_and_reset_detection_only"
3473
4106
  };
@@ -3501,65 +4134,24 @@ function looksLikeStaleRegression(current, previous, last) {
3501
4134
  if (previous.total_tokens <= 0 || current.total_tokens <= 0 || last.total_tokens <= 0) return false;
3502
4135
  return current.total_tokens * 100 >= previous.total_tokens * 98 || current.total_tokens + last.total_tokens * 2 >= previous.total_tokens;
3503
4136
  }
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
4137
  function assertRecordSequence(registration, cursor, records, lastOrdinal, record3) {
3536
4138
  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
4139
  if (!cursor.header_validated && records === 0) assertCodexTranscriptHeader(registration, record3);
3538
4140
  }
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
4141
 
3559
4142
  // host/codex/src/codex-producer-runtime.ts
3560
4143
  async function drainCodexProducerOnce(options) {
3561
4144
  const promptPort = new HostRelayCurrentPromptPort(options.relay_endpoint, options.timeout_ms);
3562
- const scan = await new CodexTranscriptWatcher({ outbox_dir: options.outbox_dir, producer_installation_ref: options.producer_installation_ref, register_current_prompt: (input) => promptPort.register(input) }).scanOnce();
4145
+ const scan = await new CodexTranscriptWatcher({
4146
+ outbox_dir: options.outbox_dir,
4147
+ producer_installation_ref: options.producer_installation_ref,
4148
+ register_current_prompt: (input) => promptPort.register(input),
4149
+ release_current_prompt: (identity) => promptPort.release(identity),
4150
+ diagnostic: (event, failure_kind) => writeHostServiceDiagnostic("codex", event, failure_kind)
4151
+ }).scanOnce().catch((error) => {
4152
+ writeHostServiceDiagnostic("codex", "watcher_scan_failed", codexTranscriptScanFailureKind(error));
4153
+ throw error;
4154
+ });
3563
4155
  const producer = new CodexProducer({ outbox_dir: options.outbox_dir, producer_installation_ref: options.producer_installation_ref, relay: new HostRelayIpcAdmissionPort(options.relay_endpoint, options.timeout_ms), ...options.producer_epoch === void 0 ? {} : { producer_epoch: options.producer_epoch }, ...options.now === void 0 ? {} : { now: options.now } });
3564
4156
  return { scanned: scan.enqueued, ...await producer.drainOutbox() };
3565
4157
  }
@@ -3585,53 +4177,6 @@ function runCodexProducerCli(args, signal, dependencies = {}) {
3585
4177
  return runHostProducerCli(CODEX_PRODUCER_DESCRIPTOR, args, signal, dependencies);
3586
4178
  }
3587
4179
 
3588
- // host/service-log.ts
3589
- import { copyFile, open as open5, stat, truncate } from "node:fs/promises";
3590
- import { isAbsolute as isAbsolute2 } from "node:path";
3591
- var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
3592
- var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
3593
- var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
3594
- function hostServiceLogPreviousPath(path) {
3595
- return `${path}.1`;
3596
- }
3597
- async function rotateHostServiceLog(path, maxBytes = HOST_SERVICE_LOG_MAX_BYTES) {
3598
- let size;
3599
- try {
3600
- size = (await stat(path)).size;
3601
- } catch (error) {
3602
- if (isNotFound2(error)) return false;
3603
- throw error;
3604
- }
3605
- if (size <= maxBytes) return false;
3606
- await copyFile(path, hostServiceLogPreviousPath(path));
3607
- await truncate(path, 0);
3608
- return true;
3609
- }
3610
- function boundHostServiceLog(environment = process.env) {
3611
- const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
3612
- if (path === void 0 || !isAbsolute2(path)) return () => void 0;
3613
- let reported = false;
3614
- const check = () => {
3615
- rotateHostServiceLog(path).catch((error) => {
3616
- if (reported) return;
3617
- reported = true;
3618
- process.stderr.write(`[host-service-log] rotation_failed code=${errorCode3(error)}
3619
- `);
3620
- });
3621
- };
3622
- check();
3623
- const timer = setInterval(check, HOST_SERVICE_LOG_CHECK_INTERVAL_MS);
3624
- timer.unref();
3625
- return () => clearInterval(timer);
3626
- }
3627
- var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
3628
- function isNotFound2(error) {
3629
- return errorCode3(error) === "ENOENT";
3630
- }
3631
- function errorCode3(error) {
3632
- return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "unknown";
3633
- }
3634
-
3635
4180
  // host/producer-command.ts
3636
4181
  async function runProducerCommand(schemaVersion, run) {
3637
4182
  const controller = new AbortController();