@corca-org/ceal-host-win32-x64 1.0.19 → 1.0.21

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.
@@ -5,7 +5,7 @@ const require = __cealCreateRequire(import.meta.url);
5
5
  // host/codex/src/codex-adapter.ts
6
6
  import { randomUUID } from "node:crypto";
7
7
  import { homedir } from "node:os";
8
- import { isAbsolute as isAbsolute3, join as join4, relative, resolve as resolve2 } from "node:path";
8
+ import { isAbsolute as isAbsolute3, relative, resolve as resolve2 } from "node:path";
9
9
 
10
10
  // packages/ceal-client-protocol/src/canonical-json.ts
11
11
  function cealCanonicalJson(value) {
@@ -34,16 +34,34 @@ function isPlainRecord(value) {
34
34
  return prototype === Object.prototype || prototype === null;
35
35
  }
36
36
 
37
+ // packages/ceal-client-protocol/src/client-wire-contract.ts
38
+ import { createHash } from "node:crypto";
39
+
37
40
  // packages/ceal-client-protocol/client-wire-contract.json
38
41
  var client_wire_contract_default = {
39
42
  schema_version: "ceal.client_wire_contract.v1",
43
+ doctor: {
44
+ schema_version: "ceal.doctor.v1"
45
+ },
46
+ client_commands: {
47
+ doctor: ["doctor"],
48
+ version: ["version"],
49
+ access_group_list: ["access-group", "list"]
50
+ },
51
+ client_environment: {
52
+ environment_prefix: "CEAL",
53
+ context_override_suffixes: ["GATEWAY_ORIGIN", "GATEWAY_TRANSPORT_ORIGIN", "ACCESS_TOKEN", "CONTEXT_STORE_PATH"]
54
+ },
40
55
  canonical_json: {
41
56
  ordering: "utf16_code_unit_ascending"
42
57
  },
43
58
  artifact_descriptor: {
44
- operation_contract_bundle_schema_version: "ceal.operation_contract_bundle.v1",
45
- schema_version: "ceal.verified_artifact_descriptor.v1",
46
- binding_schema_version: "ceal.verified_artifact_binding.v1",
59
+ operation_contract_bundle_schema_name: "ceal.operation_contract_bundle",
60
+ operation_contract_bundle_fields: ["schema_version", "catalog_revision", "operations", "failure_guidance"],
61
+ schema_name: "ceal.verified_artifact_descriptor",
62
+ fields: ["schema_version", "kind", "media_type", "byte_count", "sha256", "binding"],
63
+ binding_schema_name: "ceal.verified_artifact_binding",
64
+ binding_fields: ["schema_version", "kind", "catalog_revision"],
47
65
  kind: "verified_artifact",
48
66
  binding_kind: "catalog",
49
67
  media_type: "application/json",
@@ -59,14 +77,16 @@ var client_wire_contract_default = {
59
77
  length: 72,
60
78
  pattern: "^catalog:[a-f0-9]{64}$"
61
79
  },
62
- envelope_max_bytes: 389
80
+ envelope_max_bytes: 407
63
81
  },
64
82
  command_request: {
65
- schema_version: "ceal.command_request.v1",
83
+ schema_name: "ceal.command_request",
84
+ fields: ["schema_version", "argv", "operation", "client_nonce", "context_token"],
66
85
  absolute_transport_max_bytes: 65536
67
86
  },
68
87
  command_resolution: {
69
- schema_version: "ceal.gateway_command_resolution.v1",
88
+ schema_name: "ceal.gateway_command_resolution",
89
+ fields: ["schema_version", "kind", "ok", "execution_started", "program", "route", "authority", "routes", "operations", "required_action_sequence", "semantic_options", "result_delivery", "operation", "invocation", "entrypoints", "common_concepts", "withheld", "catalog_revision", "operation_id", "input", "transport", "output", "unresolved_inputs", "unresolved_refusal", "attempted_route", "did_you_mean", "error"],
70
90
  authority: {
71
91
  anonymous: {
72
92
  state: "anonymous",
@@ -106,7 +126,8 @@ var client_wire_contract_default = {
106
126
  }
107
127
  },
108
128
  command_response: {
109
- schema_version: "ceal.command_response.v1",
129
+ schema_name: "ceal.command_response",
130
+ fields: ["schema_version", "exit_code", "document", "operation_id", "visible_output_max_bytes", "local_delivery", "verified_artifact"],
110
131
  descriptor_field: "verified_artifact",
111
132
  required_fields: [
112
133
  "schema_version",
@@ -130,29 +151,41 @@ var client_wire_contract_default = {
130
151
  "required_action_sequence",
131
152
  "withheld"
132
153
  ],
133
- prompt_field: "prompt",
134
- prompt_schema_version: "ceal.prompt.v1",
135
- focused_prompt_document_fields: [
136
- "catalog_revision",
137
- "prompt"
138
- ],
139
- focused_prompt_continuation_document_fields: [
140
- "catalog_revision",
141
- "remaining_work",
142
- "prompt"
143
- ],
144
154
  discovery_command: "discovery",
145
- discovery_manifest_schema_version: "ceal.discovery_manifest.v1",
155
+ discovery_manifest_schema_name: "ceal.discovery_manifest",
156
+ discovery_manifest_fields: ["schema_version", "skill", "authority_scope", "connector_kinds", "personal_connector_kinds", "guide_markdown", "renderer_version", "digest"],
146
157
  discovery_manifest_document_fields: [
147
158
  "discovery_manifest"
148
159
  ],
149
160
  exit_code_max: 125,
150
161
  bare_document_max_bytes: 65536,
151
- complete_max_bytes: 66019
162
+ complete_max_bytes: 66046
163
+ },
164
+ host_invocation_authentication: {
165
+ request_schema_name: "ceal.host_invocation_authentication_request",
166
+ request_fields: [
167
+ "schema_version",
168
+ "client_nonce"
169
+ ],
170
+ result_schema_name: "ceal.host_invocation_authentication_result",
171
+ result_fields: [
172
+ "schema_version",
173
+ "context_token",
174
+ "expires_at"
175
+ ],
176
+ client_nonce: {
177
+ character_class_source: "A-Za-z0-9_-",
178
+ encoded_characters: 43
179
+ }
180
+ },
181
+ gateway_host_invocation_context_request: {
182
+ schema_name: "ceal.gateway_host_invocation_context_request",
183
+ fields: ["schema_version", "client_nonce", "context_expires_at", "request_work_context", "current_prompt", "visible_output_max_bytes"]
152
184
  },
153
185
  resource_stream: {
154
186
  path: "/api/ceal/v2/resources",
155
- request_schema_version: "ceal.resource_stream_request.v1",
187
+ request_schema_name: "ceal.resource_stream_request",
188
+ request_fields: ["schema_version", "resource_ref", "client_nonce", "context_token"],
156
189
  ref_pattern: "^resource:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
157
190
  media_type_pattern: "^[a-z0-9][a-z0-9!#$&^_.+-]{0,63}/[a-z0-9][a-z0-9!#$&^_.+-]{0,63}$",
158
191
  filename_max_bytes: 512,
@@ -169,88 +202,6 @@ var client_wire_contract_default = {
169
202
  }
170
203
  };
171
204
 
172
- // packages/ceal-client-protocol/src/client-wire-contract.ts
173
- var CEAL_CLIENT_WIRE_CONTRACT = Object.freeze(client_wire_contract_default);
174
- var commandResolution = CEAL_CLIENT_WIRE_CONTRACT.command_resolution;
175
- var commandResolutionAuthority = commandResolution.authority;
176
- var commandResolutionSemanticOptions = commandResolution.semantic_options;
177
- var CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION = commandResolution.schema_version;
178
- var CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES = CEAL_CLIENT_WIRE_CONTRACT.encoded_json_document.max_bytes;
179
- var artifact = CEAL_CLIENT_WIRE_CONTRACT.artifact_descriptor;
180
- var request = CEAL_CLIENT_WIRE_CONTRACT.command_request;
181
- var response = CEAL_CLIENT_WIRE_CONTRACT.command_response;
182
- var CEAL_OPERATION_CONTRACT_BUNDLE_SCHEMA_VERSION = artifact.operation_contract_bundle_schema_version;
183
- var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_SCHEMA_VERSION = artifact.schema_version;
184
- var CEAL_VERIFIED_ARTIFACT_BINDING_SCHEMA_VERSION = artifact.binding_schema_version;
185
- var CEAL_VERIFIED_ARTIFACT_KIND = artifact.kind;
186
- var CEAL_VERIFIED_ARTIFACT_BINDING_KIND = artifact.binding_kind;
187
- var CEAL_VERIFIED_ARTIFACT_MEDIA_TYPE = artifact.media_type;
188
- var CEAL_VERIFIED_ARTIFACT_DIGEST_LENGTH = artifact.digest.length;
189
- var CEAL_VERIFIED_ARTIFACT_DIGEST_PATTERN = artifact.digest.pattern;
190
- var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_LENGTH = artifact.catalog_revision.length;
191
- var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_PATTERN = artifact.catalog_revision.pattern;
192
- var CEAL_VERIFIED_ARTIFACT_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
193
- var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_MAX_BYTES = artifact.envelope_max_bytes;
194
- var CEAL_COMMAND_REQUEST_SCHEMA_VERSION = request.schema_version;
195
- var CEAL_COMMAND_REQUEST_MAX_BYTES = request.absolute_transport_max_bytes;
196
- var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_MAX_BYTES = response.bare_document_max_bytes;
197
- var CEAL_COMMAND_RESPONSE_PROMPT_FIELD = response.prompt_field;
198
- var CEAL_COMMAND_RESPONSE_PROMPT_SCHEMA_VERSION = response.prompt_schema_version;
199
- var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_OPTIONAL_FIELDS = Object.freeze([...response.bare_document_optional_fields]);
200
- var CEAL_COMMAND_RESPONSE_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.bare_document_fields, response.prompt_field]);
201
- var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_document_fields]);
202
- var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_CONTINUATION_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_continuation_document_fields]);
203
- var CEAL_COMMAND_RESPONSE_DISCOVERY_COMMAND = response.discovery_command;
204
- var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_SCHEMA_VERSION = response.discovery_manifest_schema_version;
205
- var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_DOCUMENT_FIELDS = Object.freeze([...response.discovery_manifest_document_fields]);
206
-
207
- // packages/ceal-client-protocol/src/vocabulary.ts
208
- function vocabulary(words) {
209
- return Object.freeze(words);
210
- }
211
-
212
- // packages/ceal-client-protocol/src/gateway-response-types.ts
213
- var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
214
- var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
215
- var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
216
- var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
217
- var CEAL_GATEWAY_READ_REUSE_DISPOSITIONS = vocabulary(["response_cache_hit", "corpus_hit", "corpus_hit_after_revalidation", "corpus_partial_provider_supplement", "provider_direct", "provider_continuation", "reuse_unavailable"]);
218
- var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
219
- var CEAL_GATEWAY_DERIVED_STORE_FAILURE_CLASSES = vocabulary(["storage_unavailable", "decode_mismatch", "write_conflict", "quota", "store_error"]);
220
- var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
221
- var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
222
- var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
223
- var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "provider_authenticated_event"]);
224
- var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
225
- var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
226
- var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
227
- var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
228
- var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
229
- var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verified", "not_dispatched"]);
230
- var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
231
- var CEAL_PROTOCOL_VERSION = "1.4.0";
232
- var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
233
- var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
234
- var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
235
- "audited_call_pending",
236
- "continuation_read_pending",
237
- "continuation_write_pending",
238
- "provider_pacing_pending",
239
- "corpus_replay_pending",
240
- "corpus_append_pending",
241
- "corpus_fence_pending",
242
- "corpus_content_pending",
243
- "corpus_transaction_pending"
244
- ]);
245
- var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
246
- var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
247
- var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
248
- var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
249
- "connector_ref",
250
- "connector_revision",
251
- "policy_decision_revision"
252
- ]);
253
-
254
205
  // packages/ceal-client-protocol/src/operation-next-actions.ts
255
206
  var CEAL_REPEAT_REQUEST_NEXT_ACTION = "repeat_request";
256
207
  var CEAL_INSPECT_RECEIPT_NEXT_ACTION = "inspect_receipt";
@@ -317,7 +268,10 @@ var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
317
268
  "follow_required_action_sequence",
318
269
  // The command needs caller-owned local materialization (for example an
319
270
  // @path file) before the Gateway can receive the argument.
320
- "materialize_local_argument"
271
+ "materialize_local_argument",
272
+ // A peer's derived schema identifier changed with its field set; install or
273
+ // upgrade the CLI and Host Relay to the Gateway's release before retrying.
274
+ "upgrade_ceal_client"
321
275
  ];
322
276
  var CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS = Object.freeze(CALLER_RECOVERABLE_NEXT_ACTION_VALUES);
323
277
  var NON_REMEDY_NEXT_ACTION_VALUES = [
@@ -430,47 +384,53 @@ var CEAL_OPERATION_WITHHELD_NEXT_ACTIONS = Object.freeze({
430
384
  personal_authorization_required: "connect_personal_account"
431
385
  });
432
386
 
433
- // packages/ceal-client-protocol/src/operation-withheld-cause.ts
434
- var CEAL_OPERATION_WITHHELD_CAUSES = vocabulary(["provider_permission", "access_level", "personal_authorization_required"]);
387
+ // packages/ceal-client-protocol/src/vocabulary.ts
388
+ function vocabulary(words) {
389
+ return Object.freeze(words);
390
+ }
435
391
 
436
- // packages/ceal-client-protocol/src/operation-contract-types.ts
437
- var CEAL_JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
438
- var CEAL_OPERATION_ERROR_SCHEMA_VERSION = "ceal.operation_error.v1";
439
- var CEAL_ARTIFACT_STAGE_TERMINALS = vocabulary(["chunk_accepted", "artifact_ready", "idempotency_replayed"]);
440
- var CEAL_RETRY_AFTER_MAX_MS = 60 * 60 * 1e3;
441
- var CEAL_JSON_SCHEMA_TYPES = vocabulary(["object", "array", "string", "integer", "number", "boolean", "null"]);
442
- var OBSERVATION_POSTURES = vocabulary(["direct_readback", "approved_no_readback", "operator_mediated", "excluded"]);
443
- var EFFECT_DOMAINS = vocabulary(["none", "gateway_state", "provider", "external_world"]);
444
- var SAFETY_CLASSES = vocabulary(["routine", "sensitive", "high_impact"]);
445
- var IDEMPOTENCY_CLASSES = vocabulary(["idempotent", "conditionally_idempotent", "non_idempotent"]);
446
- var REVERSIBILITY_CLASSES = vocabulary(["none", "reversible", "compensatable", "irreversible"]);
447
- var COMPENSATION_OWNERS = vocabulary(["none", "connector", "operator"]);
448
- var CANCELLATION_BOUNDARIES = vocabulary(["before_admission", "before_handoff", "after_handoff_reconcile"]);
449
- var HANDOFF_CLASSES = vocabulary(["not_applicable", "submission", "effect"]);
450
- var AUTHORITATIVE_READBACK_CLASSES = vocabulary(["required", "unavailable"]);
451
- var PROVIDER_READBACK_CLASSES = vocabulary(["required", "not_available", "not_applicable"]);
452
- var RECONCILIATION_CLASSES = vocabulary(["none", "automatic", "operator"]);
453
- var NO_READBACK_APPROVAL_CLASSES = vocabulary(["not_applicable", "explicit", "operator"]);
454
- var REPLAY_PREREQUISITES = vocabulary(["never", "fresh_admission", "reconciled_non_application", "operator_approval"]);
455
- var UNKNOWN_OUTCOMES = vocabulary(["reconcile", "operator_action", "excluded"]);
456
- var RECOVERY_OWNERS = vocabulary(["gateway", "connector", "operator"]);
457
- var CEAL_OPERATION_ADMISSION_STATES = vocabulary(["proposed", "refused", "admitted"]);
458
- var CEAL_OPERATION_EXECUTION_STATES = vocabulary(["not_started", "started", "completed"]);
459
- var CEAL_OPERATION_PROVIDER_HANDOFF_STATES = vocabulary(["not_offered", "offered", "accepted", "rejected", "unknown"]);
460
- var CEAL_OPERATION_EFFECT_STATES = vocabulary(["none", "applied", "failed", "unknown", "compensated"]);
461
- var CEAL_OPERATION_OBSERVATION_STATES = vocabulary(["not_started", "not_required", "pending", "observed", "unavailable"]);
462
- var CEAL_OPERATION_RECOVERY_KINDS = vocabulary(["none", "reconcile", "compensate", "operator_action"]);
463
- var CEAL_OPERATION_RECOVERY_STATUSES = vocabulary(["not_started", "active", "completed"]);
464
- var CEAL_OPERATION_PROVIDER_READBACK_AVAILABILITY = vocabulary(["not_started", "not_applicable", "not_available", "pending", "available"]);
465
- var CEAL_OPERATION_OFFERED_HANDOFF_STATES = vocabulary(["accepted", "rejected", "unknown"]);
466
- var CEAL_OPERATION_RECEIPT_AUTHORITY_KEYS = Object.freeze(["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]);
467
- var CEAL_OPERATION_APPROVAL_DISPOSITIONS = vocabulary([
468
- "approval_not_required_by_policy",
469
- "interactive_pending",
470
- "interactive_granted",
471
- "interactive_denied",
472
- "interactive_expired",
473
- "interactive_unavailable"
392
+ // packages/ceal-client-protocol/src/gateway-response-types.ts
393
+ var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
394
+ var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
395
+ var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
396
+ var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
397
+ var CEAL_GATEWAY_READ_REUSE_DISPOSITIONS = vocabulary(["response_cache_hit", "corpus_hit", "corpus_hit_after_revalidation", "corpus_partial_provider_supplement", "provider_direct", "provider_continuation", "reuse_unavailable"]);
398
+ var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
399
+ var CEAL_GATEWAY_DERIVED_STORE_FAILURE_CLASSES = vocabulary(["storage_unavailable", "decode_mismatch", "write_conflict", "quota", "store_error"]);
400
+ var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
401
+ var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
402
+ var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
403
+ var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "provider_authenticated_event"]);
404
+ var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
405
+ var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
406
+ var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
407
+ var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
408
+ var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
409
+ var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verified", "not_dispatched"]);
410
+ var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
411
+ var CEAL_PROTOCOL_VERSION = "1.4.0";
412
+ var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
413
+ var CEAL_GATEWAY_CALL_RESULT_SCHEMA_VERSION = "ceal.gateway_call_result.v1";
414
+ var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
415
+ var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
416
+ "audited_call_pending",
417
+ "continuation_read_pending",
418
+ "continuation_write_pending",
419
+ "provider_pacing_pending",
420
+ "corpus_replay_pending",
421
+ "corpus_append_pending",
422
+ "corpus_fence_pending",
423
+ "corpus_content_pending",
424
+ "corpus_transaction_pending"
425
+ ]);
426
+ var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
427
+ var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
428
+ var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
429
+ var CEAL_GATEWAY_AUDIT_CALL_DETAIL_SCHEMA_VERSION = "ceal.gateway_audit_call_detail.v1";
430
+ var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
431
+ "connector_ref",
432
+ "connector_revision",
433
+ "policy_decision_revision"
474
434
  ]);
475
435
 
476
436
  // packages/ceal-client-protocol/src/gateway-validation-primitives.ts
@@ -512,10 +472,10 @@ function assertSafeJsonArray(value, options, depth, count) {
512
472
  if (value.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
513
473
  for (const item of value) assertSafeJsonValue(item, options, depth + 1, count);
514
474
  }
515
- function assertSafeJsonRecord(record3, options, depth, count) {
516
- const entries = Object.entries(record3);
475
+ function assertSafeJsonRecord(record, options, depth, count) {
476
+ const entries = Object.entries(record);
517
477
  if (entries.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
518
- const sourceUrlColumn = options.allowHttpsUrl ? compactSourceUrlColumn(record3.fields) : null;
478
+ const sourceUrlColumn = options.allowHttpsUrl ? compactSourceUrlColumn(record.fields) : null;
519
479
  for (const [key, child] of entries) {
520
480
  if (key === "credential_material_included" && child !== false) invalidByContext(options);
521
481
  if (!isSafeNegativeMaterialAssertion(key, child)) assertSafeJsonKey(key, options);
@@ -684,51 +644,6 @@ function cut(text) {
684
644
  return text.length <= REFUSAL_VALUE_MAX_CHARS ? text : `${text.slice(0, REFUSAL_VALUE_MAX_CHARS)}...(+${text.length - REFUSAL_VALUE_MAX_CHARS} chars)`;
685
645
  }
686
646
 
687
- // packages/ceal-client-protocol/src/operation-access-class.ts
688
- var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
689
-
690
- // packages/ceal-client-protocol/src/domain-vocabulary.ts
691
- var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
692
- var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
693
- var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
694
- var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
695
- var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
696
- var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
697
- var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
698
- var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
699
- var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
700
- var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
701
- var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
702
- var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
703
-
704
- // packages/ceal-client-protocol/src/safe-json-budget.ts
705
- var SAFE_JSON_MIN_BYTES_PER_NODE = 4;
706
- function safeJsonNodeBudgetForBytes(maxBytes) {
707
- if (!Number.isSafeInteger(maxBytes) || maxBytes < SAFE_JSON_MIN_BYTES_PER_NODE) {
708
- throw new RangeError(`safe-JSON byte cap is invalid: ${refusalFact("maxBytes", maxBytes, `at least ${SAFE_JSON_MIN_BYTES_PER_NODE} bytes`)}`);
709
- }
710
- return Math.floor(maxBytes / SAFE_JSON_MIN_BYTES_PER_NODE);
711
- }
712
-
713
- // packages/ceal-client-protocol/src/protocol-bounds.ts
714
- var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES = 64 * 1024;
715
- var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_NODES = safeJsonNodeBudgetForBytes(CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES);
716
- var OPERATION_SCHEMA_LITERAL_MAX_BYTES = CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES / 4;
717
- var CEAL_OPERATION_JSON_SCHEMA_BUDGET = Object.freeze({
718
- max_depth: 8,
719
- max_properties: 96,
720
- max_string_length: OPERATION_SCHEMA_LITERAL_MAX_BYTES / 4,
721
- max_alternatives: 8,
722
- max_enum_values: 64,
723
- max_literal_bytes: OPERATION_SCHEMA_LITERAL_MAX_BYTES,
724
- /** @basis our_policy -- bounds explanatory schema prose carried in one catalog. */
725
- max_description_characters: 1024,
726
- /** @basis our_policy -- bounds regular-expression compilation input from one schema. */
727
- max_pattern_characters: 256,
728
- /** @basis our_policy -- bounds traversal work inside one already byte-bounded schema literal. */
729
- max_literal_nodes: 512
730
- });
731
-
732
647
  // packages/ceal-client-protocol/src/operation-validation-primitives.ts
733
648
  var ISO_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?([Zz]|([+-])(\d{2}):(\d{2}))$/u;
734
649
  var CEAL_CALENDAR_DATE_PATTERN_SOURCE = "^\\d{4}-\\d{2}-\\d{2}$";
@@ -796,6 +711,317 @@ function requireIsoTimestamp(value, message) {
796
711
  if (!isCealIsoTimestamp(value)) throw new TypeError(message);
797
712
  }
798
713
 
714
+ // packages/ceal-client-protocol/src/failure-guidance-table.ts
715
+ var CEAL_FAILURE_REDACTION_CLASSES = vocabulary(["model_safe", "withheld"]);
716
+ var CEAL_GATEWAY_STARTING_FAILURE_CODE = "gateway_starting";
717
+ var CEAL_OWNER_BOOTSTRAP_ABSENT_FAILURE_CODE = "bootstrap_absent";
718
+ var CEAL_OWNER_BOOTSTRAP_UNAVAILABLE_FAILURE_CODE = "bootstrap_unavailable";
719
+ var LOCAL_COMMAND_INVALID = "The local command or argument is invalid.";
720
+ var LOCAL_TRANSPORT_INCOMPLETE = "The local Ceal transport could not complete; report this as a gap.";
721
+ var GATEWAY_TRANSPORT_UNAVAILABLE = "The Gateway transport is unavailable; ask an administrator to restore Gateway availability.";
722
+ var HOST_AUTHENTICATION_UNAVAILABLE = "The Host could not authenticate this invocation; report this as a gap.";
723
+ var SESSION_CONTEXT_UNAVAILABLE = "The local Gateway session context is unavailable.";
724
+ function guidance(message, next_action, http_status, retryable = false, redaction_class = "model_safe") {
725
+ return Object.freeze({ message, next_action, http_status, retryable, redaction_class });
726
+ }
727
+ var CEAL_FAILURE_GUIDANCE_TABLE = Object.freeze({
728
+ invalid_command: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 400),
729
+ invalid_argument: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 400),
730
+ command_not_found: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 404),
731
+ unknown_operation: guidance("The requested Operation is absent from the current verified catalog; report this as a gap unless a catalog suggestion names a replacement.", "answer_with_gap", 404),
732
+ local_route_unavailable: guidance("The requested local route is unavailable or its descriptor is invalid; report this as a gap.", "answer_with_gap", 404),
733
+ local_route_ambiguous: guidance("The requested local route is ambiguous; no local executable was selected. Report this as a gap.", "answer_with_gap", 400),
734
+ local_execution_unavailable: guidance("The selected local route executable is unavailable; report this as a gap.", "answer_with_gap", 500),
735
+ local_pickup_failed: guidance("The Host Request pickup failed; the Gateway command was not sent. Report this as a gap.", "answer_with_gap", 502),
736
+ invocation_authentication_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 401),
737
+ local_execution_cancellation_unknown: guidance("The selected local route cancellation outcome is unknown; a retry can repeat an effect the cancelled route already completed. Report this as a gap and do not retry.", "answer_with_gap", 409),
738
+ gateway_origin_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
739
+ access_token_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
740
+ context_pair_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
741
+ context_not_selected: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
742
+ context_store_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
743
+ context_store_conflict: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
744
+ refresh_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
745
+ refresh_expired: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
746
+ refresh_replayed: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
747
+ refresh_revoked: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
748
+ refresh_recovery_unavailable: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
749
+ refresh_custody_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
750
+ session_store_unwritable: guidance("The Ceal client could not write its local session store; the operating system refused the write (EACCES, EPERM, or EROFS). rerun_outside_agent_sandbox reruns it with escalated execution.", "rerun_outside_agent_sandbox", 500),
751
+ local_output_exists: guidance("The selected local output path already exists; nothing was written to it.", "choose_new_output_path", 400),
752
+ local_output_outside_result_root: guidance("The selected local output path lies outside this run's result root; only a path inside the result root named here is writable.", "choose_new_output_path", 400),
753
+ local_output_unwritable: guidance("The Ceal client could not write the result file at its local path; the operating system refused the write. When that step followed the Gateway call, the call may already have completed a write effect that a rerun repeats. rerun_outside_agent_sandbox reruns it with escalated execution.", "rerun_outside_agent_sandbox", 500),
754
+ local_output_unavailable: guidance("The Gateway result could not be materialized at the selected local path; report this as a gap.", "answer_with_gap", 500),
755
+ local_materialization_required: guidance("The Gateway result could not be materialized at the selected local path; report this as a gap.", "answer_with_gap", 500),
756
+ contract_artifact_cache_invalid: guidance("The verified contract artifact could not be stored in the private local cache; report this as a gap.", "answer_with_gap", 500),
757
+ response_encoding_failed: guidance("The local response could not be encoded; report this as a gap.", "answer_with_gap", 500),
758
+ request_timeout: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 504),
759
+ request_failed: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 502),
760
+ gateway_unavailable: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 503, true),
761
+ contract_mismatch: guidance("Install or upgrade the Ceal CLI and Host Relay to the same release as the Gateway, then retry.", "upgrade_ceal_client", 409),
762
+ // `gateway_starting` is the one retryable envelope the serving gate answers on every route while starting
763
+ // (`cli/internal/client/gateway_startup_wait.go` re-drives it); the sentence is the client default that renders
764
+ // when the wait ends still unserved, moved from the `localFailureText` default branch.
765
+ [CEAL_GATEWAY_STARTING_FAILURE_CODE]: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503, true),
766
+ gateway_internal_error: guidance("The Gateway failed without exposing internal state; report this as a gap.", "answer_with_gap", 500),
767
+ // The serving gate emits this code with no message; the sentence is the owner-normalized form of the
768
+ // `gateway_internal_error` guidance (decided with the operator 2026-10-13).
769
+ internal_error: guidance("The Gateway failed without exposing internal state.", "answer_with_gap", 500),
770
+ // Owned by the Gateway canonical adapter (`gateway/core/src/gateway-canonical-operation-http-adapter.ts`):
771
+ // the client variant of this sentence in `localFailureText` was retired when the client started reading this table.
772
+ request_body_too_large: guidance("The Gateway request body exceeds its bounded limit.", "reduce_request_size", 413),
773
+ // Restored verbatim from the retired `localFailureText` `unknown_outcome`
774
+ // case: the outcome is unknown, so a read may be retried while a write
775
+ // must receipt-check first. Collapsing both to "do not repeat the call"
776
+ // forbids the safe read retry the sentence exists to allow.
777
+ unknown_outcome: guidance("The transport outcome is unknown: a read may be retried; for a write, check write_receipt.get before repeating.", "answer_with_gap", 500),
778
+ // Restored verbatim from the retired `localFailureText`
779
+ // `refresh_unknown_outcome` case: a refresh that ends unknown leaves the
780
+ // session context unavailable, so the caller repairs or adopts the
781
+ // session instead of reporting a transport gap.
782
+ refresh_unknown_outcome: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
783
+ request_cancelled: guidance("The caller cancelled the request before it completed.", "retry", 400, true),
784
+ expired: guidance("The activation transaction or its administrator approval request expired before completion; repeat the original activation arguments to start a new transaction.", "repeat_original_arguments", 410, true),
785
+ denied: guidance("An administrator denied this device's activation request; ask an administrator whether to reconsider it.", "ask_admin", 403),
786
+ device_approval_pending_limit: guidance("Device approval requests are already waiting for an administrator decision; ask an administrator to decide a pending request before adding another device.", "ask_admin", 429),
787
+ session_already_adopted: guidance("The requested enrollment was not started because this machine already has a live session with the Gateway. Report this as a gap if that local session needs replacement.", "answer_with_gap", 409),
788
+ pending_adoption_limit: guidance("This device's pending activation file is full; another activation is refused while an administrator decision is pending. Report this as a gap.", "answer_with_gap", 429),
789
+ recovery_required: guidance("The Gateway could not safely complete the activation transaction automatically; ask an administrator to recover this transaction.", "ask_admin", 500),
790
+ unsupported_feature: guidance("This Ceal client does not support the Gateway's required activation protocol; ask an administrator to update the client.", "ask_admin", 501),
791
+ // Activation commit codes rendered through the `localFailureText` default before phase 2b; `retryable` is moved from the
792
+ // emitting sites in `cli/internal/client/gateway_installation_activation.go` and `activation.go:483`.
793
+ activation_state_commit_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500, true),
794
+ activation_response_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502, true),
795
+ activation_rejected: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502),
796
+ activation_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
797
+ activation_binding: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
798
+ activation_crypto: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
799
+ activation_state_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
800
+ // Long-tail local codes rendered through the `localFailureText` default before phase 2b; sentences moved from that branch.
801
+ invalid_response: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
802
+ invalid_configuration: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
803
+ activation_state_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
804
+ activation_code_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
805
+ activation_code_file_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
806
+ activation_code_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 404),
807
+ bootstrap_file_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
808
+ bootstrap_not_active: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 409),
809
+ bootstrap_unauthorized: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
810
+ // The Gateway owner-bootstrap store file was never created, so the Gateway
811
+ // holds no owner bootstrap to exchange (`gateway/src/host/owner-bootstrap-http-composition.ts`
812
+ // `absent`, stored at `gateway/src/shared/installation/owner-bootstrap-store.ts:162`);
813
+ // the sentence is the owner-normalized form of that store error. Non-retryable:
814
+ // repeating the exchange cannot create the store; only an administrator
815
+ // placing a bootstrap (fresh gateway setup) changes the outcome.
816
+ [CEAL_OWNER_BOOTSTRAP_ABSENT_FAILURE_CODE]: guidance("The Gateway holds no owner bootstrap to exchange; report this as a gap.", "answer_with_gap", 503),
817
+ // The Gateway owner-bootstrap store file exists but cannot be read, so the
818
+ // exchange cannot proceed (`gateway/src/host/owner-bootstrap-http-composition.ts`
819
+ // `unavailable`, stored at `gateway/src/shared/installation/owner-bootstrap-store.ts:163`);
820
+ // the sentence is the owner-normalized form of that store error. Retryable:
821
+ // unlike `absent`, a later exchange may succeed once the store is readable.
822
+ [CEAL_OWNER_BOOTSTRAP_UNAVAILABLE_FAILURE_CODE]: guidance("The Gateway owner bootstrap store is unavailable; retry the exchange.", "retry", 503, true),
823
+ context_selection_ambiguous: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
824
+ session_authority_unverified: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
825
+ session_authority_mismatch: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 409),
826
+ local_credential_lost: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
827
+ local_capability_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
828
+ host_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503),
829
+ host_activation_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
830
+ doctor_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503),
831
+ admin_sign_in_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502),
832
+ // `invalid_request` renders through the client default today (local encode/path faults); Gateway-answered
833
+ // envelopes carry the Gateway message verbatim and never read this sentence.
834
+ invalid_request: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
835
+ // Gateway canonical-route codes, sentences moved from their minting adapters; statuses moved from the
836
+ // `valid*Failure` decoder maps in `cli/internal/client/gateway_api.go` and `transport.go`.
837
+ invalid_json: guidance("The Gateway request body must be valid JSON.", "answer_with_gap", 400),
838
+ // Per-route variants name their route; the table holds the one stable guidance and the route's `allow`
839
+ // header stays the measured fact (decided with the operator 2026-10-13).
840
+ method_not_allowed: guidance("The HTTP method is not allowed for this Gateway route.", "answer_with_gap", 405),
841
+ request_target_too_large: guidance("The Gateway request target exceeds its bounded limit.", "reduce_request_size", 414),
842
+ route_not_found: guidance("The Gateway route is not present.", "answer_with_gap", 404),
843
+ artifact_unavailable: guidance("The Gateway artifact is unavailable.", "answer_with_gap", 503),
844
+ discovery_unavailable: guidance("The Gateway discovery manifest is unavailable.", "answer_with_gap", 503),
845
+ serialization_failed: guidance("The Gateway response could not be serialized.", "answer_with_gap", 500),
846
+ request_header_too_large: guidance("A consumed Gateway request header exceeds its bounded limit.", "answer_with_gap", 431),
847
+ request_work_context_missing: guidance("The Gateway command requires a Request work context.", "answer_with_gap", 400),
848
+ request_work_context_invalid: guidance("The Gateway Request work context encoding is invalid.", "answer_with_gap", 400),
849
+ request_work_context_refused: guidance("The Host-only discovery manifest takes no Request work context.", "answer_with_gap", 400),
850
+ // Moved from `gateway/src/operations/http-composition.ts` (`unauthorized`) and `command-runtime.ts:827`
851
+ // (`artifact_stale`); both travel with Gateway messages verbatim online, so these sentences are the
852
+ // owner fallback guidance. `unauthorized` re-authenticates through the session, hence its action.
853
+ unauthorized: guidance("Unauthorized.", "repair_or_adopt_session", 401),
854
+ artifact_stale: guidance("The requested contract artifact is not the current catalog artifact.", "answer_with_gap", 409),
855
+ // Host mint classifications. The CLI emits `invocation_authentication_unavailable` with the classification
856
+ // in its detail fact; rows without their own sentence share that code's moved sentence, while `next_action`
857
+ // is moved from `canonicalHostAuthenticationNextActions` (generated from `CEAL_HOST_AUTHENTICATION_POLICY`)
858
+ // and `retryable` from `hostAuthenticationFailureForCause` (`failure.nextAction == "retry"`). The three rows
859
+ // with their own sentences carry the retired policy remedies as Gateway-owned guidance. Statuses are the
860
+ // Gateway-owned mapping decided with the operator 2026-10-13. This covers every wire cause the maps at
861
+ // `host/invocation-authentication-command.ts:217-242` emit, through their policy classifications.
862
+ host_arguments_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 400),
863
+ host_configuration_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500),
864
+ host_session_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 401),
865
+ // Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
866
+ // missing exact-turn fact names the fresh Host turn, not a reinstall.
867
+ host_context_unavailable: guidance("The Host has not activated an invocation context for this session yet. Wait for the Host turn hook to register the session, then retry this command.", "retry", 401, true),
868
+ host_invocation_context_stale: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 401, true),
869
+ host_invocation_context_expired: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 401, true),
870
+ host_source_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 403),
871
+ host_registration_credential_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "ask_admin", 403),
872
+ host_mint_gateway_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "ask_admin", 503),
873
+ host_mint_gateway_rate_limited: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 429),
874
+ host_mint_gateway_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
875
+ host_mint_gateway_response_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
876
+ // Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
877
+ // producer-frame fault names the administrator repair step.
878
+ host_producer_frame_invalid: guidance("Ask an administrator to inspect and repair the producer frame, then start a fresh Host turn.", "ask_admin", 400),
879
+ // Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
880
+ // sandboxed Relay socket names the run-alone retry.
881
+ host_relay_permission_denied: guidance("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.", "rerun_outside_agent_sandbox", 403),
882
+ host_relay_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 503),
883
+ host_mint_transient_retryable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 503, true),
884
+ host_mint_failed: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500),
885
+ host_relay_failed: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
886
+ host_refusal_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500)
887
+ });
888
+ var FAILURE_GUIDANCE_ENTRY_KEYS = Object.freeze(["message", "next_action", "http_status", "retryable", "redaction_class"]);
889
+
890
+ // packages/ceal-client-protocol/src/client-wire-contract.ts
891
+ var CEAL_CLIENT_WIRE_CONTRACT = Object.freeze(client_wire_contract_default);
892
+ var CEAL_CLIENT_DOCTOR_SCHEMA_VERSION = CEAL_CLIENT_WIRE_CONTRACT.doctor.schema_version;
893
+ var clientCommands = CEAL_CLIENT_WIRE_CONTRACT.client_commands;
894
+ var CEAL_CLIENT_DOCTOR_COMMAND = Object.freeze([...clientCommands.doctor]);
895
+ var CEAL_CLIENT_VERSION_COMMAND = Object.freeze([...clientCommands.version]);
896
+ var CEAL_CLIENT_ACCESS_GROUP_LIST_COMMAND = Object.freeze([...clientCommands.access_group_list]);
897
+ function deriveCealClientWireSchemaVersion(schema) {
898
+ if (!/^ceal\.[a-z][a-z0-9_]*$/u.test(schema.schema_name) || !schema.fields.includes("schema_version") || new Set(schema.fields).size !== schema.fields.length || schema.fields.some((field) => typeof field !== "string" || !/^[a-z][a-z0-9_]*$/u.test(field))) {
899
+ throw new TypeError("Client wire schema name or field set is invalid.");
900
+ }
901
+ const fields = schema.fields.filter((field) => field !== "schema_version").sort();
902
+ const hash8 = createHash("sha256").update(cealCanonicalJson(fields), "utf8").digest("hex").slice(0, 8);
903
+ return `${schema.schema_name}.v1.${hash8}`;
904
+ }
905
+ var commandResolution = CEAL_CLIENT_WIRE_CONTRACT.command_resolution;
906
+ var commandResolutionAuthority = commandResolution.authority;
907
+ var commandResolutionSemanticOptions = commandResolution.semantic_options;
908
+ var CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(commandResolution);
909
+ var CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES = CEAL_CLIENT_WIRE_CONTRACT.encoded_json_document.max_bytes;
910
+ var artifact = CEAL_CLIENT_WIRE_CONTRACT.artifact_descriptor;
911
+ var request = CEAL_CLIENT_WIRE_CONTRACT.command_request;
912
+ var response = CEAL_CLIENT_WIRE_CONTRACT.command_response;
913
+ var CEAL_OPERATION_CONTRACT_BUNDLE_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: artifact.operation_contract_bundle_schema_name, fields: artifact.operation_contract_bundle_fields });
914
+ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(artifact);
915
+ var CEAL_VERIFIED_ARTIFACT_BINDING_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: artifact.binding_schema_name, fields: artifact.binding_fields });
916
+ var CEAL_VERIFIED_ARTIFACT_KIND = artifact.kind;
917
+ var CEAL_VERIFIED_ARTIFACT_BINDING_KIND = artifact.binding_kind;
918
+ var CEAL_VERIFIED_ARTIFACT_MEDIA_TYPE = artifact.media_type;
919
+ var CEAL_VERIFIED_ARTIFACT_DIGEST_LENGTH = artifact.digest.length;
920
+ var CEAL_VERIFIED_ARTIFACT_DIGEST_PATTERN = artifact.digest.pattern;
921
+ var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_LENGTH = artifact.catalog_revision.length;
922
+ var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_PATTERN = artifact.catalog_revision.pattern;
923
+ var CEAL_VERIFIED_ARTIFACT_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
924
+ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_MAX_BYTES = artifact.envelope_max_bytes;
925
+ var CEAL_COMMAND_REQUEST_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(request);
926
+ var CEAL_COMMAND_REQUEST_MAX_BYTES = request.absolute_transport_max_bytes;
927
+ var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_MAX_BYTES = response.bare_document_max_bytes;
928
+ var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_OPTIONAL_FIELDS = Object.freeze([...response.bare_document_optional_fields]);
929
+ var CEAL_COMMAND_RESPONSE_DISCOVERY_COMMAND = response.discovery_command;
930
+ var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: response.discovery_manifest_schema_name, fields: response.discovery_manifest_fields });
931
+ var CEAL_COMMAND_RESPONSE_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(response);
932
+ var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_DOCUMENT_FIELDS = Object.freeze([...response.discovery_manifest_document_fields]);
933
+ var CEAL_RESOURCE_STREAM_REQUEST_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: CEAL_CLIENT_WIRE_CONTRACT.resource_stream.request_schema_name, fields: CEAL_CLIENT_WIRE_CONTRACT.resource_stream.request_fields });
934
+ var CEAL_RESOURCE_STREAM_REQUEST_FIELDS = Object.freeze([...CEAL_CLIENT_WIRE_CONTRACT.resource_stream.request_fields]);
935
+
936
+ // packages/ceal-client-protocol/src/operation-withheld-cause.ts
937
+ var CEAL_OPERATION_WITHHELD_CAUSES = vocabulary(["provider_permission", "access_level", "personal_authorization_required"]);
938
+
939
+ // packages/ceal-client-protocol/src/operation-contract-types.ts
940
+ var CEAL_JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
941
+ var CEAL_OPERATION_DESCRIPTOR_SCHEMA_VERSION = "ceal.operation_descriptor.v1";
942
+ var CEAL_OPERATION_ERROR_SCHEMA_VERSION = "ceal.operation_error.v1";
943
+ var CEAL_ARTIFACT_STAGE_TERMINALS = vocabulary(["chunk_accepted", "artifact_ready", "idempotency_replayed"]);
944
+ var CEAL_RETRY_AFTER_MAX_MS = 60 * 60 * 1e3;
945
+ var CEAL_JSON_SCHEMA_TYPES = vocabulary(["object", "array", "string", "integer", "number", "boolean", "null"]);
946
+ var OBSERVATION_POSTURES = vocabulary(["direct_readback", "approved_no_readback", "operator_mediated", "excluded"]);
947
+ var EFFECT_DOMAINS = vocabulary(["none", "gateway_state", "provider", "external_world"]);
948
+ var SAFETY_CLASSES = vocabulary(["routine", "sensitive", "high_impact"]);
949
+ var IDEMPOTENCY_CLASSES = vocabulary(["idempotent", "conditionally_idempotent", "non_idempotent"]);
950
+ var REVERSIBILITY_CLASSES = vocabulary(["none", "reversible", "compensatable", "irreversible"]);
951
+ var COMPENSATION_OWNERS = vocabulary(["none", "connector", "operator"]);
952
+ var CANCELLATION_BOUNDARIES = vocabulary(["before_admission", "before_handoff", "after_handoff_reconcile"]);
953
+ var HANDOFF_CLASSES = vocabulary(["not_applicable", "submission", "effect"]);
954
+ var AUTHORITATIVE_READBACK_CLASSES = vocabulary(["required", "unavailable"]);
955
+ var PROVIDER_READBACK_CLASSES = vocabulary(["required", "not_available", "not_applicable"]);
956
+ var RECONCILIATION_CLASSES = vocabulary(["none", "automatic", "operator"]);
957
+ var NO_READBACK_APPROVAL_CLASSES = vocabulary(["not_applicable", "explicit", "operator"]);
958
+ var REPLAY_PREREQUISITES = vocabulary(["never", "fresh_admission", "reconciled_non_application", "operator_approval"]);
959
+ var UNKNOWN_OUTCOMES = vocabulary(["reconcile", "operator_action", "excluded"]);
960
+ var RECOVERY_OWNERS = vocabulary(["gateway", "connector", "operator"]);
961
+ var CEAL_OPERATION_ADMISSION_STATES = vocabulary(["proposed", "refused", "admitted"]);
962
+ var CEAL_OPERATION_EXECUTION_STATES = vocabulary(["not_started", "started", "completed"]);
963
+ var CEAL_OPERATION_PROVIDER_HANDOFF_STATES = vocabulary(["not_offered", "offered", "accepted", "rejected", "unknown"]);
964
+ var CEAL_OPERATION_EFFECT_STATES = vocabulary(["none", "applied", "failed", "unknown", "compensated"]);
965
+ var CEAL_OPERATION_OBSERVATION_STATES = vocabulary(["not_started", "not_required", "pending", "observed", "unavailable"]);
966
+ var CEAL_OPERATION_RECOVERY_KINDS = vocabulary(["none", "reconcile", "compensate", "operator_action"]);
967
+ var CEAL_OPERATION_RECOVERY_STATUSES = vocabulary(["not_started", "active", "completed"]);
968
+ var CEAL_OPERATION_PROVIDER_READBACK_AVAILABILITY = vocabulary(["not_started", "not_applicable", "not_available", "pending", "available"]);
969
+ var CEAL_OPERATION_OFFERED_HANDOFF_STATES = vocabulary(["accepted", "rejected", "unknown"]);
970
+ var CEAL_OPERATION_RECEIPT_AUTHORITY_KEYS = Object.freeze(["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]);
971
+ var CEAL_OPERATION_APPROVAL_DISPOSITIONS = vocabulary([
972
+ "approval_not_required_by_policy",
973
+ "interactive_pending",
974
+ "interactive_granted",
975
+ "interactive_denied",
976
+ "interactive_expired",
977
+ "interactive_unavailable"
978
+ ]);
979
+
980
+ // packages/ceal-client-protocol/src/operation-access-class.ts
981
+ var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
982
+
983
+ // packages/ceal-client-protocol/src/domain-vocabulary.ts
984
+ var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
985
+ var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
986
+ var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
987
+ var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
988
+ var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
989
+ var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
990
+ var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
991
+ var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
992
+ var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
993
+ var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
994
+ var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
995
+ var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
996
+
997
+ // packages/ceal-client-protocol/src/safe-json-budget.ts
998
+ var SAFE_JSON_MIN_BYTES_PER_NODE = 4;
999
+ function safeJsonNodeBudgetForBytes(maxBytes) {
1000
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < SAFE_JSON_MIN_BYTES_PER_NODE) {
1001
+ throw new RangeError(`safe-JSON byte cap is invalid: ${refusalFact("maxBytes", maxBytes, `at least ${SAFE_JSON_MIN_BYTES_PER_NODE} bytes`)}`);
1002
+ }
1003
+ return Math.floor(maxBytes / SAFE_JSON_MIN_BYTES_PER_NODE);
1004
+ }
1005
+
1006
+ // packages/ceal-client-protocol/src/protocol-bounds.ts
1007
+ var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES = 64 * 1024;
1008
+ var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_NODES = safeJsonNodeBudgetForBytes(CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES);
1009
+ var OPERATION_SCHEMA_LITERAL_MAX_BYTES = CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES / 4;
1010
+ var CEAL_OPERATION_JSON_SCHEMA_BUDGET = Object.freeze({
1011
+ max_depth: 8,
1012
+ max_properties: 96,
1013
+ max_string_length: OPERATION_SCHEMA_LITERAL_MAX_BYTES / 4,
1014
+ max_alternatives: 8,
1015
+ max_enum_values: 64,
1016
+ max_literal_bytes: OPERATION_SCHEMA_LITERAL_MAX_BYTES,
1017
+ /** @basis our_policy -- bounds explanatory schema prose carried in one catalog. */
1018
+ max_description_characters: 1024,
1019
+ /** @basis our_policy -- bounds regular-expression compilation input from one schema. */
1020
+ max_pattern_characters: 256,
1021
+ /** @basis our_policy -- bounds traversal work inside one already byte-bounded schema literal. */
1022
+ max_literal_nodes: 512
1023
+ });
1024
+
799
1025
  // packages/ceal-client-protocol/src/operation-json-schema.ts
800
1026
  var JSON_DESCRIPTION_CONTROL_CHARACTER = new RegExp("\\p{Cc}", "u");
801
1027
 
@@ -837,12 +1063,12 @@ var CEAL_SCHEMA_LITERALS = [
837
1063
  "ceal.enrollment_result.v1",
838
1064
  "ceal.first_activation_causal_join.v1",
839
1065
  "ceal.gateway_announcement_policy.v1",
840
- "ceal.gateway_audit_call_detail.v1",
1066
+ CEAL_GATEWAY_AUDIT_CALL_DETAIL_SCHEMA_VERSION,
841
1067
  "ceal.gateway_audit_event.v1",
842
1068
  "ceal.gateway_audit_readback.v1",
843
1069
  "ceal.gateway_authorization_snapshot.v1",
844
1070
  "ceal.gateway_cache_origin.v1",
845
- "ceal.gateway_call_result.v1",
1071
+ CEAL_GATEWAY_CALL_RESULT_SCHEMA_VERSION,
846
1072
  CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION,
847
1073
  "ceal.gateway_connector_route_failure.v1",
848
1074
  "ceal.gateway_discovery.v1",
@@ -867,7 +1093,7 @@ var CEAL_SCHEMA_LITERALS = [
867
1093
  "ceal.host_observation.v1",
868
1094
  "ceal.host_registration_request.v1",
869
1095
  "ceal.host_registration_result.v1",
870
- "ceal.operation_descriptor.v1",
1096
+ CEAL_OPERATION_DESCRIPTOR_SCHEMA_VERSION,
871
1097
  "ceal.operation_error.v1",
872
1098
  "ceal.operation_failure.v1",
873
1099
  "ceal.operation_failure.compact.v1",
@@ -972,11 +1198,22 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
972
1198
  var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "block", "object"]);
973
1199
  var CEAL_OPAQUE_REF_KINDS = vocabulary([...CEAL_OPAQUE_HANDLE_KINDS, "result", "operation-receipt", "reply-receipt", "field", "notion-data-source"]);
974
1200
 
1201
+ // packages/ceal-client-protocol/src/client-environment-generated.ts
1202
+ var CEAL_CLIENT_CONTEXT_OVERRIDE_ENVIRONMENTS = Object.freeze(["CEAL_GATEWAY_ORIGIN", "CEAL_GATEWAY_TRANSPORT_ORIGIN", "CEAL_ACCESS_TOKEN", "CEAL_CONTEXT_STORE_PATH"]);
1203
+
1204
+ // packages/ceal-client-protocol/src/client-process-contract.ts
1205
+ var CEAL_CLIENT_VERSION_SCHEMA_SUFFIX = ".version.v1";
1206
+ var CEAL_CLIENT_VERSION_SCHEMA_VERSION = `ceal${CEAL_CLIENT_VERSION_SCHEMA_SUFFIX}`;
1207
+
975
1208
  // packages/ceal-client-protocol/src/bare-document.ts
976
1209
  var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_fields;
977
1210
  var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
978
1211
 
1212
+ // packages/ceal-client-protocol/src/actor-kind.ts
1213
+ var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
1214
+
979
1215
  // packages/ceal-client-protocol/src/resource-row.ts
1216
+ var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
980
1217
  var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
981
1218
  var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
982
1219
  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"]);
@@ -989,6 +1226,8 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
989
1226
  "resource_created",
990
1227
  "review_submitted",
991
1228
  "comment_created",
1229
+ "reaction_created",
1230
+ "ceal_write_completed",
992
1231
  "event_start",
993
1232
  "all_day_start",
994
1233
  "file_created",
@@ -1023,6 +1262,10 @@ var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
1023
1262
  "github.review.submitted",
1024
1263
  "github.issue_comment.created",
1025
1264
  "github.review_comment.created",
1265
+ "github.reaction.created",
1266
+ "github.reaction.deleted",
1267
+ "github.pull_request.review_requested",
1268
+ "github.pull_request.review_request_removed",
1026
1269
  "calendar.organized",
1027
1270
  "calendar.accepted_invite",
1028
1271
  "calendar.recorded",
@@ -1044,6 +1287,10 @@ var CEAL_ACTIVITY_EVENT_TIMESTAMP_BASIS_MAP = Object.freeze({
1044
1287
  "github.pull_request.opened": "resource_created",
1045
1288
  "github.issue.edited": "resource_modified",
1046
1289
  "github.pull_request.edited": "resource_modified",
1290
+ "github.reaction.created": "reaction_created",
1291
+ "github.reaction.deleted": "ceal_write_completed",
1292
+ "github.pull_request.review_requested": "ceal_write_completed",
1293
+ "github.pull_request.review_request_removed": "ceal_write_completed",
1047
1294
  "calendar.organized": "resource_created",
1048
1295
  "drive.file.created": "file_created",
1049
1296
  "drive.file.modified": "file_modified_by_me",
@@ -1060,12 +1307,14 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1060
1307
  next: { required: true, timestamp: false },
1061
1308
  kind: { required: true, timestamp: false },
1062
1309
  matched_kinds: { required: false, timestamp: false },
1310
+ title_match: { required: false, timestamp: false },
1063
1311
  title: { required: true, timestamp: false },
1064
1312
  container_label: { required: true, timestamp: false },
1065
1313
  container_ref: { required: false, timestamp: false },
1066
1314
  source_url: { required: false, timestamp: false },
1067
1315
  source: { required: true, timestamp: false },
1068
1316
  owner: { required: false, timestamp: false },
1317
+ actor_kind: { required: false, timestamp: false },
1069
1318
  counterparts: { required: false, timestamp: false },
1070
1319
  attachments: { required: false, timestamp: false },
1071
1320
  created_at: { required: false, timestamp: true },
@@ -1073,9 +1322,18 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1073
1322
  modified_at: { required: false, timestamp: true },
1074
1323
  modified_at_local: { required: false, timestamp: true },
1075
1324
  viewed_by_me_at: { required: false, timestamp: true },
1325
+ modified_by_me_at: { required: false, timestamp: true },
1076
1326
  own_ceal_write: { required: false, timestamp: false },
1077
1327
  owned_by_me: { required: false, timestamp: false },
1328
+ mime_type: { required: false, timestamp: false },
1329
+ size_bytes: { required: false, timestamp: false },
1330
+ version: { required: false, timestamp: false },
1331
+ trashed: { required: false, timestamp: false },
1078
1332
  visibility: { required: false, timestamp: false },
1333
+ archived: { required: false, timestamp: false },
1334
+ tag: { required: false, timestamp: false },
1335
+ labels: { required: false, timestamp: false },
1336
+ labels_incomplete: { required: false, timestamp: false },
1079
1337
  start: { required: false, timestamp: false },
1080
1338
  end: { required: false, timestamp: false },
1081
1339
  status: { required: false, timestamp: false },
@@ -1148,18 +1406,29 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
1148
1406
  // The serving gate answers this while the Gateway is still starting and the
1149
1407
  // refresh was never attempted; the same attempt stays retryable. It is never
1150
1408
  // stored by the session owner, only answered at the serving edge.
1151
- "gateway_starting"
1409
+ CEAL_GATEWAY_STARTING_FAILURE_CODE
1410
+ ]);
1411
+ var CEAL_CLIENT_REVOKE_FAILURE_CODES = vocabulary([
1412
+ "refresh_invalid",
1413
+ "refresh_expired",
1414
+ "refresh_replayed",
1415
+ "refresh_revoked"
1152
1416
  ]);
1417
+ var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX = "ceal_personal_";
1418
+ var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX = "ceal_refresh_";
1419
+ var PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN = "[A-Za-z0-9_-]{43}";
1420
+ var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
1421
+ var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
1153
1422
 
1154
1423
  // packages/ceal-client-protocol/src/request-validation-primitives.ts
1155
- import { createHash } from "node:crypto";
1424
+ import { createHash as createHash2 } from "node:crypto";
1156
1425
  var DIGEST = /^[a-f0-9]{64}$/u;
1157
1426
  var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
1158
1427
  function isCealDigest(value) {
1159
1428
  return typeof value === "string" && DIGEST.test(value);
1160
1429
  }
1161
1430
  function cealRequestSha256(value) {
1162
- return createHash("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
1431
+ return createHash2("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
1163
1432
  }
1164
1433
  function operationRecord(value, message) {
1165
1434
  requireOperationRecord(value, message);
@@ -1175,40 +1444,71 @@ function requirePrefixedRef(value, prefix, message) {
1175
1444
  // packages/ceal-client-protocol/src/request-authority.ts
1176
1445
  var CEAL_REQUEST_SCHEMA_VERSION = "ceal.request.v1";
1177
1446
  var CEAL_REQUEST_ARTIFACT_OWNERS = vocabulary(["gateway", "host", "connector"]);
1178
- var CEAL_REQUEST_HOST_BINDING_KEYS = vocabulary(["prompt_ref", "host_session_ref", "host_turn_ref", "host_source_ref"]);
1447
+ var CEAL_HOST_INVOCATION_IDENTITY_KEYS = vocabulary(["host_session_ref", "host_turn_ref", "host_source_ref"]);
1179
1448
  var CEAL_REQUEST_ARTIFACT_RETENTION_CLASSES = vocabulary(["request", "source", "result"]);
1180
1449
  var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
1181
- 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"]);
1450
+ var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority"]);
1451
+ var CEAL_REQUEST_COMMON_PROPERTIES = Object.freeze({
1452
+ schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
1453
+ ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
1454
+ created_at: { type: "string", minLength: 1, maxLength: 64 },
1455
+ authority: {
1456
+ type: "object",
1457
+ maxProperties: 5,
1458
+ additionalProperties: false,
1459
+ properties: {
1460
+ instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1461
+ access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1462
+ principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1463
+ execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1464
+ authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1465
+ },
1466
+ required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
1467
+ }
1468
+ });
1469
+ var CEAL_REQUEST_HOST_PROPERTIES = Object.freeze({
1470
+ prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1471
+ host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1472
+ host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1473
+ host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1474
+ host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1475
+ });
1476
+ var CEAL_REQUEST_CALLER_PROPERTIES = Object.freeze({
1477
+ caller_call_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source, description: "Gateway-minted call-group identity for a non-Host caller." },
1478
+ caller_call_input_sha256: { type: "string", minLength: 64, maxLength: 64, pattern: "^[a-f0-9]{64}$", description: "Commitment to the exact input for active non-Host identity-reuse checks; a migrated legacy call group commits to its legacy group key because the original input may no longer exist." }
1479
+ });
1480
+ var CEAL_REQUEST_OPTIONAL_PROPERTIES = Object.freeze({
1481
+ requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1482
+ device_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source, description: "Device that submitted this Request through its client session, including Host turns when the authenticated Host registration carries the device; absent for older records." }
1483
+ });
1484
+ var CEAL_REQUEST_HOST_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, ...CEAL_HOST_INVOCATION_IDENTITY_KEYS, "prompt_ref"]);
1485
+ var CEAL_REQUEST_CALLER_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, "caller_call_group_ref", "caller_call_input_sha256"]);
1182
1486
  var CEAL_REQUEST_JSON_SCHEMA = {
1183
1487
  type: "object",
1184
- maxProperties: 11,
1488
+ description: "A Request has exactly one creation identity: the complete Host tuple and prompt reference, or a caller call-group identity. Caller Requests retain no prompt reference or prompt body.",
1489
+ maxProperties: 13,
1185
1490
  additionalProperties: false,
1186
1491
  properties: {
1187
- schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
1188
- ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
1189
- created_at: { type: "string", minLength: 1, maxLength: 64 },
1190
- authority: {
1492
+ ...CEAL_REQUEST_COMMON_PROPERTIES,
1493
+ ...CEAL_REQUEST_HOST_PROPERTIES,
1494
+ ...CEAL_REQUEST_CALLER_PROPERTIES,
1495
+ ...CEAL_REQUEST_OPTIONAL_PROPERTIES
1496
+ },
1497
+ required: CEAL_REQUEST_KEYS,
1498
+ anyOf: [
1499
+ {
1191
1500
  type: "object",
1192
- maxProperties: 5,
1193
- additionalProperties: false,
1194
- properties: {
1195
- instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1196
- access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1197
- principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1198
- execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1199
- authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1200
- },
1201
- required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
1501
+ required: CEAL_REQUEST_HOST_REQUIRED_KEYS,
1502
+ properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_HOST_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
1503
+ additionalProperties: false
1202
1504
  },
1203
- prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1204
- host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1205
- host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1206
- host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1207
- host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1208
- requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1209
- 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." }
1210
- },
1211
- required: CEAL_REQUEST_KEYS
1505
+ {
1506
+ type: "object",
1507
+ required: CEAL_REQUEST_CALLER_REQUIRED_KEYS,
1508
+ properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_CALLER_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
1509
+ additionalProperties: false
1510
+ }
1511
+ ]
1212
1512
  };
1213
1513
 
1214
1514
  // packages/ceal-client-protocol/src/request-operands.ts
@@ -1319,10 +1619,6 @@ var CEAL_REQUEST_RUN_BATCH_JSON_SCHEMA = {
1319
1619
  };
1320
1620
 
1321
1621
  // packages/ceal-client-protocol/src/host-observation.ts
1322
- var CEAL_HOST_INPUT_KINDS = vocabulary(["human", "host_automatic"]);
1323
- function isCealHostInputKind(value) {
1324
- return typeof value === "string" && CEAL_HOST_INPUT_KINDS.some((kind) => kind === value);
1325
- }
1326
1622
  var CEAL_HOST_OBSERVATION_REF_PREFIX = "host-observation:";
1327
1623
  var HOST_OBSERVATION_PRODUCER = /^[a-z][a-z0-9]{0,31}$/u;
1328
1624
  var HOST_OBSERVATION_DIGEST = /^[a-f0-9]{8,64}$/u;
@@ -1368,14 +1664,14 @@ function safeCount(value) {
1368
1664
  return Number.isSafeInteger(value) && Number(value) >= 0;
1369
1665
  }
1370
1666
  function validateCealHostObservationDraft(value) {
1371
- const record3 = operationRecord(value, "Ceal Host observation draft is invalid.");
1372
- requirePrefixedRef(record3.observation_ref, CEAL_HOST_OBSERVATION_REF_PREFIX, "Ceal Host observation reference is invalid.");
1373
- requireClosedValue(record3.observation_kind, CEAL_HOST_OBSERVATION_KINDS, "Ceal Host observation kind is invalid.");
1374
- requireIsoTimestamp(record3.observed_at, "Ceal Host observation time is invalid.");
1375
- assertHostObservationValue(record3.value, record3.observation_kind);
1667
+ const record = operationRecord(value, "Ceal Host observation draft is invalid.");
1668
+ requirePrefixedRef(record.observation_ref, CEAL_HOST_OBSERVATION_REF_PREFIX, "Ceal Host observation reference is invalid.");
1669
+ requireClosedValue(record.observation_kind, CEAL_HOST_OBSERVATION_KINDS, "Ceal Host observation kind is invalid.");
1670
+ requireIsoTimestamp(record.observed_at, "Ceal Host observation time is invalid.");
1671
+ assertHostObservationValue(record.value, record.observation_kind);
1376
1672
  }
1377
1673
  var CEAL_REQUEST_MAX_OBSERVATION_VALUE_BYTES = 64 * 1024;
1378
- var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "cost", "timing"]);
1674
+ var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "timing"]);
1379
1675
  function isCealHostPromptObservationValue(value) {
1380
1676
  return isOperationRecord(value) && Object.keys(value).length === 1 && isCealDigest(value.body_sha256);
1381
1677
  }
@@ -1498,16 +1794,41 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
1498
1794
  };
1499
1795
 
1500
1796
  // packages/ceal-client-protocol/src/host-invocation-authentication.ts
1501
- var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = "ceal.host_invocation_authentication_result.v1";
1502
- var CEAL_HOST_NONCE_ENCODED_CHARACTERS = 43;
1503
- var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(`^[A-Za-z0-9_-]{${CEAL_HOST_NONCE_ENCODED_CHARACTERS}}$`, "u");
1504
- var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(`^ceal_context_[A-Za-z0-9_-]{${CEAL_HOST_NONCE_ENCODED_CHARACTERS}}$`, "u");
1505
- var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = new TextEncoder().encode(`${JSON.stringify({
1506
- schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA,
1507
- context_token: `ceal_context_${"A".repeat(CEAL_HOST_NONCE_ENCODED_CHARACTERS)}`,
1508
- expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
1509
- })}
1797
+ var hostInvocationAuthenticationContract = CEAL_CLIENT_WIRE_CONTRACT.host_invocation_authentication;
1798
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.request_schema_name, fields: hostInvocationAuthenticationContract.request_fields });
1799
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.request_fields]);
1800
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.result_schema_name, fields: hostInvocationAuthenticationContract.result_fields });
1801
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.result_fields]);
1802
+ var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE = "^(?:0|[1-9][0-9]{0,8})$";
1803
+ var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN = new RegExp(CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE, "u");
1804
+ var CEAL_HOST_INVOCATION_CONTEXT_REQUEST_MAX_BYTES = 16 * 1024;
1805
+ var CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES = 2 * 1024;
1806
+ var CEAL_HOST_CONTEXT_TOKEN_PREFIX = "ceal_context_";
1807
+ function deriveCealHostInvocationNonceContract(contract = CEAL_CLIENT_WIRE_CONTRACT) {
1808
+ const clientNonce = contract.host_invocation_authentication.client_nonce;
1809
+ return {
1810
+ encoded_characters: clientNonce.encoded_characters,
1811
+ client_nonce_pattern_source: `^[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`,
1812
+ context_token_pattern_source: `^${CEAL_HOST_CONTEXT_TOKEN_PREFIX}[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`
1813
+ };
1814
+ }
1815
+ var hostNonceContract = deriveCealHostInvocationNonceContract();
1816
+ var CEAL_HOST_NONCE_ENCODED_CHARACTERS = hostNonceContract.encoded_characters;
1817
+ var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(hostNonceContract.client_nonce_pattern_source, "u");
1818
+ var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(hostNonceContract.context_token_pattern_source, "u");
1819
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = deriveCealHostInvocationAuthenticationResultMaxBytes();
1820
+ function deriveCealHostInvocationAuthenticationResultMaxBytes(contract = CEAL_CLIENT_WIRE_CONTRACT) {
1821
+ const hostAuthentication = contract.host_invocation_authentication;
1822
+ const values = {
1823
+ schema_version: deriveCealClientWireSchemaVersion({ schema_name: hostAuthentication.result_schema_name, fields: hostAuthentication.result_fields }),
1824
+ context_token: `${CEAL_HOST_CONTEXT_TOKEN_PREFIX}${"A".repeat(hostAuthentication.client_nonce.encoded_characters)}`,
1825
+ expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
1826
+ };
1827
+ const fields = hostAuthentication.result_fields;
1828
+ if (fields.some((field) => !Object.hasOwn(values, field))) throw new TypeError("Host invocation result fields have no bounded sample value.");
1829
+ return new TextEncoder().encode(`${JSON.stringify(Object.fromEntries(fields.map((field) => [field, values[field]])))}
1510
1830
  `).byteLength;
1831
+ }
1511
1832
  var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
1512
1833
  "request_authority_unavailable",
1513
1834
  "request_missing",
@@ -1531,6 +1852,7 @@ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
1531
1852
  "host_credential_refused",
1532
1853
  "gateway_unavailable",
1533
1854
  "gateway_rate_limited",
1855
+ "contract_mismatch",
1534
1856
  "gateway_refused",
1535
1857
  "gateway_response_invalid",
1536
1858
  "producer_frame_invalid",
@@ -1545,19 +1867,23 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
1545
1867
  invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
1546
1868
  options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
1547
1869
  session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1548
- context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn; run `ceal-host install` to repair the Host hook registration, then retry this command on a fresh Host turn" }),
1549
- context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1550
- no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
1551
- context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1552
- context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "refresh_host_invocation_context" }),
1870
+ context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "retry" }),
1871
+ context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
1872
+ // `no_binding` now means the producer has no real current Host session. The
1873
+ // Host Relay never uses it for an uncreated Gateway Request; there is no
1874
+ // useful retry until the consumer is running inside a Host session.
1875
+ no_binding: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1876
+ context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
1877
+ context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "retry" }),
1553
1878
  host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
1554
1879
  host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
1555
1880
  gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
1556
1881
  gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
1882
+ contract_mismatch: Object.freeze({ classification: "contract_mismatch", next_action: "upgrade_ceal_client" }),
1557
1883
  gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
1558
1884
  gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
1559
- 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" }),
1560
- 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" }),
1885
+ producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin" }),
1886
+ relay_permission_denied: Object.freeze({ classification: "host_relay_permission_denied", next_action: "rerun_outside_agent_sandbox" }),
1561
1887
  relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
1562
1888
  transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
1563
1889
  mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
@@ -1566,14 +1892,12 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
1566
1892
  });
1567
1893
 
1568
1894
  // packages/ceal-client-protocol/src/host-service-diagnostic.ts
1895
+ var CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA = "ceal.host_service_diagnostic.v1";
1569
1896
  var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
1570
1897
  var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
1571
1898
  "hook_invoked",
1572
1899
  "registration_written",
1573
1900
  "hook_failed",
1574
- "watcher_turn_observed",
1575
- "current_prompt_registered",
1576
- "current_prompt_registration_failed",
1577
1901
  "activation_recorded",
1578
1902
  "activation_failed",
1579
1903
  "watcher_scan_failed",
@@ -1596,6 +1920,9 @@ var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "un
1596
1920
  var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
1597
1921
  var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "items_path", "row_count", "result_complete", "coverage", "local_result_root", "model_disclosure", "continue_argv", "how_to_read"]);
1598
1922
 
1923
+ // packages/ceal-client-protocol/src/discovery-manifest.ts
1924
+ var DISCOVERY_MANIFEST_KEYS = CEAL_CLIENT_WIRE_CONTRACT.command_response.discovery_manifest_fields;
1925
+
1599
1926
  // packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
1600
1927
  var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
1601
1928
 
@@ -1654,6 +1981,7 @@ var ADMIN_UNAVAILABLE_REASONS = vocabulary([
1654
1981
  "legacy",
1655
1982
  "invalid_observation",
1656
1983
  "redacted",
1984
+ "organization_setting_disabled",
1657
1985
  "owner_unavailable",
1658
1986
  "unit_unknown",
1659
1987
  "retention_gap",
@@ -1683,15 +2011,13 @@ var ADMIN_APPROVAL_DISPOSITIONS = CEAL_OPERATION_APPROVAL_DISPOSITIONS;
1683
2011
  var ADMIN_APPROVAL_POSTURES = vocabulary([...ADMIN_APPROVAL_DISPOSITIONS, "not_applicable"]);
1684
2012
  var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "approval", "effect", "readback"]);
1685
2013
 
1686
- // packages/ceal-client-protocol/src/actor-kind.ts
1687
- var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
1688
-
1689
2014
  // packages/ceal-client-protocol/src/device-enrollment.ts
1690
2015
  var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
1691
2016
  var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
1692
2017
 
1693
2018
  // packages/ceal-client-protocol/src/personal-client-session.ts
1694
2019
  var REFRESH_FAILURE_CODES = new Set(CEAL_REFRESH_FAILURE_CODES);
2020
+ var REVOKE_FAILURE_CODES = new Set(CEAL_CLIENT_REVOKE_FAILURE_CODES);
1695
2021
 
1696
2022
  // packages/ceal-client-protocol/src/index.ts
1697
2023
  var MAX_REQUEST_BYTES = 32 * 1024;
@@ -1701,12 +2027,11 @@ var AUDIT_IDENTITY_KEYS = Object.freeze(["event_ref", "membership_ref", "registr
1701
2027
  var FAILURE_RESPONSE_KEYS = Object.freeze(["error", "ok", "proof_ref_or_unavailable", "protocol_version", "request_id"]);
1702
2028
 
1703
2029
  // host/codex/src/codex-decoder.ts
1704
- import { createHash as createHash2 } from "node:crypto";
2030
+ import { createHash as createHash3 } from "node:crypto";
1705
2031
  import { isAbsolute, resolve } from "node:path";
1706
2032
 
1707
2033
  // host/codex/src/codex-types.ts
1708
2034
  var CODEX_OUTBOX_SCHEMA_VERSION = "ceal.codex_observation_outbox.v1";
1709
- var CODEX_TURN_BINDING_SCHEMA_VERSION = "ceal.codex_turn_binding.v1";
1710
2035
  var CODEX_SESSION_SCHEMA_VERSION = "ceal.codex_session.v1";
1711
2036
  var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1";
1712
2037
  var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
@@ -1734,8 +2059,8 @@ function decodeCodexHookPayload(event, raw) {
1734
2059
  if (event === "SessionStart") return decodeSession(input, event);
1735
2060
  return decodeUserPromptSubmit(input, event);
1736
2061
  }
1737
- function observationRef(event, identity, value) {
1738
- return cealHostObservationRef("codex", shortDigest({ event, identity, value }));
2062
+ function observationRef(hostSourceRef, event, identity, value) {
2063
+ return cealHostObservationRef("codex", shortDigest({ host_source_ref: hostSourceRef, event, identity, value }));
1739
2064
  }
1740
2065
  function hostSessionReference(sourceRef, sessionId) {
1741
2066
  return `codex-session:${shortDigest({ source_ref: sourceRef, session_id: sessionId })}`;
@@ -1830,7 +2155,7 @@ function isRecord(value) {
1830
2155
  return value !== null && typeof value === "object" && !Array.isArray(value);
1831
2156
  }
1832
2157
  function sha256(value) {
1833
- return createHash2("sha256").update(value, "utf8").digest("hex");
2158
+ return createHash3("sha256").update(value, "utf8").digest("hex");
1834
2159
  }
1835
2160
  function shortDigest(value) {
1836
2161
  return cealRequestSha256(value);
@@ -1840,18 +2165,17 @@ function reject(message) {
1840
2165
  }
1841
2166
 
1842
2167
  // host/codex/src/codex-outbox.ts
1843
- import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
1844
- import { lstat as lstat2, mkdir as mkdir3, open as open3, readdir, rename as rename3, unlink as unlink2 } from "node:fs/promises";
1845
- import { dirname, isAbsolute as isAbsolute2, join as join3 } from "node:path";
2168
+ import { mkdir as mkdir2, open as open3, readdir, rename as rename2, stat, unlink as unlink2 } from "node:fs/promises";
2169
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2 } from "node:path";
1846
2170
 
1847
2171
  // host/invocation-context.ts
1848
- var HOST_INVOCATION_IDENTITY_KEYS = ["host_session_ref", "host_turn_ref", "host_source_ref"];
2172
+ var HOST_INVOCATION_TURN_SELECTION_SCHEMA = "ceal.host_invocation_turn_selection.v1";
1849
2173
  var HOST_INVOCATION_CONTEXT_REQUIRED_KEYS = ["schema_version", "identity", "prompt_ref", "expires_at"];
1850
2174
  function invocationContextKey(identity) {
1851
2175
  return cealRequestSha256({ host_source_ref: identity.host_source_ref, host_session_ref: identity.host_session_ref });
1852
2176
  }
1853
2177
  function hasHostInvocationIdentityKeys(value) {
1854
- return exactKeys2(value, HOST_INVOCATION_IDENTITY_KEYS);
2178
+ return exactKeys2(value, CEAL_HOST_INVOCATION_IDENTITY_KEYS);
1855
2179
  }
1856
2180
  function hasHostInvocationContextKeys(value, optional = []) {
1857
2181
  return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
@@ -1872,432 +2196,163 @@ function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor) {
1872
2196
  expires_at: expiresAt,
1873
2197
  ...descriptor.turn_generation_ref === void 0 ? {} : { turn_generation_ref: descriptor.turn_generation_ref },
1874
2198
  ...descriptor.working_directory === void 0 ? {} : { working_directory: descriptor.working_directory }
1875
- };
1876
- descriptor.validate(context);
1877
- return Object.freeze(context);
1878
- }
1879
- function isHostInvocationTurnSelection(value) {
1880
- 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) => safeRef(value[key]));
1881
- }
1882
- function invocationTurnSelectionMatches(context, selection) {
1883
- 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;
1884
- }
1885
- function exactKeys2(value, keys) {
1886
- const actual = Object.keys(value);
1887
- return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
1888
- }
1889
- function requiredKeys(value, required, optional) {
1890
- const actual = Object.keys(value);
1891
- const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
1892
- return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
1893
- }
1894
- function isRecord2(value) {
1895
- return value !== null && typeof value === "object" && !Array.isArray(value);
1896
- }
1897
- function safeRef(value) {
1898
- return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
1899
- }
1900
-
1901
- // host/relay/src/producer-frame.ts
1902
- var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
1903
- var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
1904
- var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
1905
- var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
1906
- var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
1907
- var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "user_input", "turn_terminal", "producer_status"];
1908
- var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
1909
- function isHostProducerFrameKind(value) {
1910
- return typeof value === "string" && FRAME_KINDS.has(value);
1911
- }
1912
- var HOST_PRODUCER_FRAME_ERROR_CODES = [
1913
- "invalid_frame_encoding",
1914
- "invalid_frame_length",
1915
- "frame_truncated",
1916
- "unsupported_frame_schema",
1917
- "invalid_frame_shape",
1918
- "invalid_producer_identity",
1919
- "frame_too_large"
1920
- ];
1921
- var HostProducerFrameError = class extends Error {
1922
- code;
1923
- constructor(code, message = code) {
1924
- super(message);
1925
- this.code = code;
1926
- }
1927
- };
1928
- function encodeHostProducerFrame(frame) {
1929
- validateFrame(frame);
1930
- const json = Buffer.from(JSON.stringify(frame), "utf8");
1931
- if (json.byteLength > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("frame_bytes", json.byteLength, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
1932
- const result = Buffer.allocUnsafe(json.byteLength + HOST_PRODUCER_FRAME_LENGTH_BYTES);
1933
- result.writeUInt32BE(json.byteLength, 0);
1934
- json.copy(result, HOST_PRODUCER_FRAME_LENGTH_BYTES);
1935
- return result;
1936
- }
1937
- function decodeHostProducerFrame(input) {
1938
- if (input.byteLength < HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError("frame_truncated", `frame_truncated: ${refusalFact("frame_bytes", input.byteLength, `at least ${HOST_PRODUCER_FRAME_LENGTH_BYTES}`)}`);
1939
- const length = input.readUInt32BE(0);
1940
- if (length > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("declared_bytes", length, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
1941
- if (length === 0) throw new HostProducerFrameError("invalid_frame_length");
1942
- if (input.byteLength !== length + HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError(input.byteLength < length + HOST_PRODUCER_FRAME_LENGTH_BYTES ? "frame_truncated" : "invalid_frame_length");
1943
- const bytes = input.subarray(HOST_PRODUCER_FRAME_LENGTH_BYTES);
1944
- const text = new TextDecoder("utf-8", { fatal: true });
1945
- let value;
1946
- try {
1947
- value = JSON.parse(text.decode(bytes));
1948
- } catch (error) {
1949
- if (error instanceof TypeError) throw new HostProducerFrameError("invalid_frame_encoding");
1950
- throw new HostProducerFrameError("frame_truncated");
1951
- }
1952
- validateFrame(value);
1953
- return value;
1954
- }
1955
- function isHostSafeRef(value) {
1956
- return safeRef2(value);
1957
- }
1958
- function validateFrame(value) {
1959
- if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
1960
- 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)}`);
1961
- if (!exactKeys3(value, ["schema_version", "producer_id", "producer_installation_ref", "producer_epoch", "sequence", "frame_kind", "occurred_at", "payload"])) throw new HostProducerFrameError("invalid_frame_shape");
1962
- if (!PRODUCERS.has(String(value.producer_id)) || !safeRef2(value.producer_installation_ref)) throw new HostProducerFrameError("invalid_producer_identity");
1963
- if (!positive(value.producer_epoch) || !positive(value.sequence) || !hostProducerFrameKind(value.frame_kind) || !timestamp(value.occurred_at) || !record(value.payload)) throw new HostProducerFrameError("invalid_frame_shape");
1964
- validatePayload(value.frame_kind, value.payload);
1965
- }
1966
- function validatePayload(kind, value) {
1967
- const valid = kind === "session_started" ? validSession(value) : kind === "turn_started" ? validTurn(value) : kind === "user_input" ? validInput(value) : kind === "turn_terminal" ? validTerminal(value) : validStatus(value);
1968
- if (!valid) throw new HostProducerFrameError("invalid_frame_shape");
1969
- }
1970
- function validSession(value) {
1971
- return exactOptional(value, ["local_session_ref", "working_directory_ref"], ["model", "host_version", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.working_directory_ref) && optionalText(value.model, 256) && optionalText(value.host_version, 256) && validObservations(value.observations);
1972
- }
1973
- function validTurn(value) {
1974
- return exactOptional(value, ["local_session_ref", "local_turn_ref"], ["model", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && optionalText(value.model, 256) && validObservations(value.observations);
1975
- }
1976
- function validInput(value) {
1977
- return exactOptional(value, ["local_session_ref", "local_turn_ref", "local_input_ref", "input_kind", "origin_input", "observations"], ["local_turn_aliases", "context_expires_at"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && safeRef2(value.local_input_ref) && validInputObservations(value.observations) && isCealHostInputKind(value.input_kind) && typeof value.origin_input === "boolean" && validInputOptionals(value);
1978
- }
1979
- function validInputObservations(value) {
1980
- return validObservations(value) && Array.isArray(value) && value.filter((item) => record(item) && item.observation_kind === "prompt").length === 1;
1981
- }
1982
- function validInputOptionals(value) {
1983
- return optionalRefs(value.local_turn_aliases) && (value.context_expires_at === void 0 || timestamp(value.context_expires_at));
1984
- }
1985
- function validTerminal(value) {
1986
- return exactOptional(value, ["local_session_ref", "local_turn_ref", "terminal_state"], ["abort_reason", "duration_ms", "time_to_first_token_ms", "usage", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && validTerminalDisposition(value.terminal_state, value.abort_reason) && optionalNonNegative(value.duration_ms) && optionalNonNegative(value.time_to_first_token_ms) && (value.usage === void 0 || jsonValue(value.usage)) && validObservations(value.observations);
1987
- }
1988
- function validTerminalDisposition(state, reason) {
1989
- return (state === "completed" || state === "aborted") && (reason === void 0 || state === "aborted" && safeRef2(reason));
1990
- }
1991
- function validStatus(value) {
1992
- return exactOptional(value, ["status", "reason_code"], ["detail"]) && ["ready", "degraded", "stopping"].includes(String(value.status)) && safeRef2(value.reason_code) && optionalText(value.detail, 4096);
1993
- }
1994
- function optionalText(value, maxLength) {
1995
- return value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maxLength;
1996
- }
1997
- function optionalNonNegative(value) {
1998
- return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
1999
- }
2000
- function optionalRefs(value) {
2001
- return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef2);
2002
- }
2003
- function validObservations(value) {
2004
- if (value === void 0) return true;
2005
- if (!Array.isArray(value) || value.length > 32) return false;
2006
- return value.every((item) => {
2007
- try {
2008
- validateCealHostObservationDraft(item);
2009
- return true;
2010
- } catch {
2011
- return false;
2012
- }
2013
- });
2014
- }
2015
- function jsonValue(value, depth = 0) {
2016
- if (depth > 16) return false;
2017
- if (value === null || typeof value === "string" || typeof value === "boolean") return true;
2018
- if (typeof value === "number") return Number.isFinite(value);
2019
- if (Array.isArray(value)) return value.length <= 256 && value.every((item) => jsonValue(item, depth + 1));
2020
- return record(value) && Object.keys(value).length <= 256 && Object.entries(value).every(([key, item]) => key.length > 0 && key.length <= 128 && jsonValue(item, depth + 1));
2021
- }
2022
- function record(value) {
2023
- return value !== null && typeof value === "object" && !Array.isArray(value);
2024
- }
2025
- function safeRef2(value) {
2026
- return typeof value === "string" && SAFE_REF3.test(value);
2027
- }
2028
- function positive(value) {
2029
- return Number.isSafeInteger(value) && Number(value) > 0;
2030
- }
2031
- function timestamp(value) {
2032
- return typeof value === "string" && Number.isFinite(Date.parse(value));
2033
- }
2034
- function hostProducerFrameKind(value) {
2035
- return isHostProducerFrameKind(value);
2036
- }
2037
- function exactKeys3(value, keys) {
2038
- return JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
2039
- }
2040
- function exactOptional(value, required, optional) {
2041
- const keys = Object.keys(value);
2042
- return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
2043
- }
2044
-
2045
- // host/relay/src/host-producer-core.ts
2046
- import { createHash as createHash3 } from "node:crypto";
2047
- import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
2048
- import { mkdir as mkdir2, open as open2, readFile, rename as rename2, writeFile } from "node:fs/promises";
2049
- import { join as join2 } from "node:path";
2050
-
2051
- // host/relay/src/relay-ipc.ts
2052
- import { createConnection, createServer } from "node:net";
2053
-
2054
- // host/relay/src/durable-atomic-replacement.ts
2055
- import { O_RDONLY } from "node:constants";
2056
- import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
2057
- var DEFAULT_PORT = {
2058
- platform: process.platform,
2059
- mkdir: (path, options) => mkdir(path, options),
2060
- lstat: (path) => lstat(path),
2061
- open_file: (path) => open(path, "wx", 384),
2062
- rename,
2063
- unlink,
2064
- open_directory: (path) => open(path, O_RDONLY),
2065
- unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
2066
- };
2067
-
2068
- // host/relay/src/relay-request-binding-store.ts
2069
- var MAX_STATE_BYTES = 8 * 1024 * 1024;
2070
-
2071
- // host/relay/src/current-prompt-custody.ts
2072
- var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
2073
-
2074
- // host/relay/src/relay-ipc.ts
2075
- var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
2076
- "schema_version",
2077
- "producer_id",
2078
- "producer_installation_ref",
2079
- "local_session_ref",
2080
- "local_turn_ref",
2081
- "local_turn_aliases",
2082
- "prompt_ref",
2083
- "request_ref",
2084
- "bound_at",
2085
- "host_source_ref",
2086
- "host_session_ref",
2087
- "host_turn_ref",
2088
- "host_input_ref"
2089
- ]);
2090
- var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
2091
- var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
2092
- var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
2093
- var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
2094
- var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
2095
- var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
2096
- "context_stale",
2097
- "context_expired",
2098
- "host_source_refused",
2099
- "host_credential_refused",
2100
- "gateway_unavailable",
2101
- "gateway_refused",
2102
- "gateway_response_invalid",
2103
- "gateway_rate_limited",
2104
- "producer_frame_invalid",
2105
- "deadline",
2106
- "cancelled",
2107
- "mint_failed",
2108
- "relay_failed",
2109
- "request_authority_unavailable",
2110
- "request_missing",
2111
- "authority_mismatch",
2112
- "host_source_mismatch",
2113
- "host_session_mismatch",
2114
- "host_turn_mismatch",
2115
- "host_input_mismatch",
2116
- "host_prompt_mismatch"
2117
- ]);
2118
- var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
2119
- "context_stale",
2120
- "context_expired",
2121
- "host_source_refused",
2122
- "host_credential_refused",
2123
- "gateway_unavailable",
2124
- "gateway_rate_limited",
2125
- "gateway_refused",
2126
- "gateway_response_invalid",
2127
- "producer_frame_invalid"
2128
- ]);
2129
- var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
2130
- var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
2131
- "permission_denied",
2132
- "socket_missing",
2133
- "connection_refused",
2134
- "closed_early",
2135
- "response_invalid",
2136
- "timed_out",
2137
- "cancelled",
2138
- "unavailable"
2139
- ]);
2140
- var HostRelayIpcError = class extends Error {
2141
- code;
2142
- /** Set only when the client-side socket exchange itself failed. */
2143
- failure;
2144
- constructor(code, message = code, failure) {
2199
+ };
2200
+ descriptor.validate(context);
2201
+ return Object.freeze(context);
2202
+ }
2203
+ function isHostInvocationTurnSelection(value) {
2204
+ return isRecord2(value) && Object.keys(value).length === 5 && value.schema_version === HOST_INVOCATION_TURN_SELECTION_SCHEMA && ["host_source_ref", "host_session_ref", "prompt_ref", "turn_generation_ref"].every((key) => safeRef(value[key]));
2205
+ }
2206
+ function exactKeys2(value, keys) {
2207
+ const actual = Object.keys(value);
2208
+ return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
2209
+ }
2210
+ function requiredKeys(value, required, optional) {
2211
+ const actual = Object.keys(value);
2212
+ const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
2213
+ return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
2214
+ }
2215
+ function isRecord2(value) {
2216
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2217
+ }
2218
+ function safeRef(value) {
2219
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
2220
+ }
2221
+
2222
+ // host/relay/src/producer-frame.ts
2223
+ var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
2224
+ var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "turn_terminal", "producer_status"];
2225
+ var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
2226
+ var HOST_PRODUCER_FRAME_ERROR_CODES = [
2227
+ "invalid_frame_encoding",
2228
+ "invalid_frame_length",
2229
+ "frame_truncated",
2230
+ "unsupported_frame_schema",
2231
+ "invalid_frame_shape",
2232
+ "invalid_producer_identity",
2233
+ "frame_too_large"
2234
+ ];
2235
+
2236
+ // host/relay/src/host-producer-core.ts
2237
+ import { createHash as createHash4 } from "node:crypto";
2238
+
2239
+ // host/secure-file.ts
2240
+ import { execFile } from "node:child_process";
2241
+ import { constants } from "node:fs";
2242
+ import { lstat, open } from "node:fs/promises";
2243
+ import { dirname, win32 } from "node:path";
2244
+ import { fileURLToPath } from "node:url";
2245
+ var SecureFileCustodyError = class extends Error {
2246
+ name = "SecureFileCustodyError";
2247
+ reason;
2248
+ constructor(reason, message) {
2145
2249
  super(message);
2146
- this.code = code;
2147
- this.failure = failure;
2250
+ this.reason = reason;
2148
2251
  }
2149
- };
2150
- function hostRelayExchangeFailure(error) {
2151
- return error instanceof HostRelayIpcError ? error.failure : void 0;
2152
- }
2153
- function socketErrorFailure(error) {
2154
- switch (errorCode(error)) {
2155
- case "EACCES":
2156
- case "EPERM":
2157
- return "permission_denied";
2158
- case "ENOENT":
2159
- return "socket_missing";
2160
- case "ECONNREFUSED":
2161
- return "connection_refused";
2162
- default:
2163
- return "unavailable";
2164
- }
2165
- }
2166
- var HostRelayIpcAdmissionPort = class {
2167
- endpoint;
2168
- timeout_ms;
2169
- constructor(endpoint, timeoutMs = 5e3) {
2170
- this.endpoint = endpoint;
2171
- this.timeout_ms = timeoutMs;
2172
- }
2173
- async admit(frame) {
2174
- const response2 = await submitHostProducerFrame(this.endpoint, frame, this.timeout_ms);
2175
- if (!relayAdmissionCode(response2.code)) throw new HostRelayIpcError(response2.code);
2176
- return { code: response2.code, admitted_through_sequence: response2.admitted_through_sequence, delivered_through_sequence: response2.delivered_through_sequence };
2252
+ get code() {
2253
+ if (this.reason === "reparse_point") return "ELOOP";
2254
+ if (this.reason === "not_found") return "ENOENT";
2255
+ if (this.reason === "too_large") return "secure_file_too_large";
2256
+ if (this.reason === "already_exists") return "EEXIST";
2257
+ return this.reason === "not_private" ? "secure_file_not_private" : "secure_file_unavailable";
2177
2258
  }
2178
2259
  };
2179
- var HostRelayCurrentPromptPort = class {
2180
- endpoint;
2181
- timeout_ms;
2182
- constructor(endpoint, timeoutMs = 5e3) {
2183
- this.endpoint = endpoint;
2184
- this.timeout_ms = timeoutMs;
2185
- }
2186
- async register(input) {
2187
- const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
2188
- const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
2189
- if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
2260
+ var WINDOWS_SECUREFS_REFUSAL_SCHEMA = "ceal.host_securefs_refusal.v1";
2261
+ var WINDOWS_SECUREFS_TIMEOUT_MS = 1e4;
2262
+ var WINDOWS_SECUREFS_DIAGNOSTIC_BYTES = 4096;
2263
+ function secureOpenFlags(flags, platform) {
2264
+ if (platform === "win32") return flags;
2265
+ const nofollow = constants.O_NOFOLLOW;
2266
+ if (typeof nofollow === "number" && nofollow !== 0) return flags | nofollow;
2267
+ throw new Error("Secure file reads require O_NOFOLLOW on this platform.");
2268
+ }
2269
+ async function openNoFollow(path, flags = constants.O_RDONLY, mode, platform = process.platform, _custody = {}) {
2270
+ if (platform !== "win32") return await open(path, secureOpenFlags(flags, platform), mode);
2271
+ return await openWindowsIdentityScreened(path, flags, mode);
2272
+ }
2273
+ async function openWindowsIdentityScreened(path, flags, mode) {
2274
+ const handle = await open(path, flags, mode);
2275
+ try {
2276
+ const first = await handle.stat();
2277
+ if (!first.isFile()) throw new SecureFileCustodyError("not_private", "The Windows Host file is not a regular file.");
2278
+ await assertWindowsLinkFree(path, first);
2279
+ await assertWindowsIdentityStable(handle, first, path);
2280
+ return handle;
2281
+ } catch (error) {
2282
+ await handle.close();
2283
+ throw error;
2190
2284
  }
2191
- };
2192
- function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
2193
- return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
2194
- }
2195
- function exchange(endpoint, encoded, timeoutMs, decode, signal) {
2196
- return new Promise((resolve3, reject3) => {
2197
- const socket = createConnection(endpoint);
2198
- let buffer = Buffer.alloc(0);
2199
- let settled = false;
2200
- const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
2201
- const finish = (error, response2) => {
2202
- if (settled) return;
2203
- settled = true;
2204
- signal?.removeEventListener("abort", aborted);
2205
- socket.destroy();
2206
- if (error !== null) reject3(error);
2207
- else if (response2 !== void 0) resolve3(response2);
2208
- };
2209
- if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
2210
- if (signal?.aborted === true) {
2211
- aborted();
2212
- return;
2213
- }
2214
- signal?.addEventListener("abort", aborted, { once: true });
2215
- socket.once("error", (error) => {
2216
- const failure = socketErrorFailure(error);
2217
- finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure}.`, failure));
2218
- });
2219
- socket.once("connect", () => socket.write(encoded));
2220
- socket.on("data", (chunk) => {
2221
- try {
2222
- buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
2223
- const decoded = decode(buffer);
2224
- if (decoded !== null) finish(null, decoded);
2225
- } catch (error) {
2226
- finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
2285
+ }
2286
+ async function assertWindowsLinkFree(path, first) {
2287
+ const entry = await lstat(path);
2288
+ if (entry.isSymbolicLink() || entry.dev !== first.dev || entry.ino !== first.ino) {
2289
+ throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
2290
+ }
2291
+ }
2292
+ async function assertWindowsIdentityStable(handle, first, path) {
2293
+ const second = await handle.stat();
2294
+ if (second.dev !== first.dev || second.ino !== first.ino || second.size !== first.size || second.mtimeMs !== first.mtimeMs) {
2295
+ throw new SecureFileCustodyError("unavailable", "The Windows Host file changed while its identity was checked.");
2296
+ }
2297
+ const entry = await lstat(path);
2298
+ if (entry.isSymbolicLink()) throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
2299
+ if (entry.dev !== second.dev || entry.ino !== second.ino) throw new SecureFileCustodyError("unavailable", "The Windows Host file path no longer names the opened file.");
2300
+ }
2301
+ async function readPrivateFileWithSecurefs(path, maxBytes, custody = {}) {
2302
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) throw new TypeError("The securefs read bound must be a positive safe integer.");
2303
+ const result = await runWindowsSecurefs(["internal", "securefs", "read-private-file", path, String(maxBytes)], maxBytes, custody);
2304
+ if (result.exit_code === 0 && result.stderr.length === 0 && result.stdout.length <= maxBytes) return result.stdout;
2305
+ if (result.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned bytes with a refusal.");
2306
+ if (result.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command exceeded its output contract.");
2307
+ throw securefsRefusal(result.stderr);
2308
+ }
2309
+ async function runWindowsSecurefs(args, maxBytes, custody) {
2310
+ if (custody.run_securefs_command !== void 0) return await custody.run_securefs_command(args);
2311
+ const executable = custody.securefs_executable ?? resolveInstalledHostExecutable();
2312
+ const processArgs = [...custody.securefs_args_prefix ?? [], ...args];
2313
+ return await new Promise((resolve3, reject3) => {
2314
+ execFile(executable, processArgs, { encoding: "buffer", timeout: WINDOWS_SECUREFS_TIMEOUT_MS, maxBuffer: maxBytes + WINDOWS_SECUREFS_DIAGNOSTIC_BYTES }, (error, stdout, stderr) => {
2315
+ const exitCode = error === null ? 0 : typeof error.code === "number" ? error.code : null;
2316
+ if (exitCode === null) {
2317
+ reject3(new SecureFileCustodyError("unavailable", "The installed Windows Host securefs command could not run."));
2318
+ return;
2227
2319
  }
2228
- });
2229
- socket.once("end", () => {
2230
- if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
2320
+ resolve3({ exit_code: exitCode, stdout: Buffer.from(stdout), stderr: Buffer.from(stderr) });
2231
2321
  });
2232
2322
  });
2233
2323
  }
2234
- function decodeHostRelayIpcResponse(input) {
2235
- if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response is truncated: ${refusalFact("response_bytes", input.byteLength, `at least ${HOST_RELAY_IPC_LENGTH_BYTES}`)}.`);
2236
- const length = input.readUInt32BE(0);
2237
- if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES || input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
2324
+ function securefsRefusal(stderr) {
2238
2325
  let value;
2239
2326
  try {
2240
- value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
2327
+ value = JSON.parse(stderr.toString("utf8"));
2241
2328
  } catch {
2242
- throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response is invalid.");
2329
+ return new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an unreadable refusal.");
2243
2330
  }
2244
- if (!validResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response shape is invalid.");
2245
- return value;
2246
- }
2247
- function relayAdmissionCode(value) {
2248
- return value === "admitted" || value === "already_admitted" || value === "producer_epoch_stale" || value === "sequence_gap" || value === "frame_identity_conflict" || value === "producer_queue_full" || value === "relay_queue_full" || value === "producer_already_connected" || value === "invalid_producer_identity";
2249
- }
2250
- function validResponse(value) {
2251
- return record2(value) && value.schema_version === HOST_RELAY_IPC_RESPONSE_SCHEMA && hostRelayIpcCode(value.code) && nonnegative(value.admitted_through_sequence) && nonnegative(value.delivered_through_sequence) && Object.keys(value).length === 4;
2331
+ if (!isRecord3(value) || value.schema_version !== WINDOWS_SECUREFS_REFUSAL_SCHEMA || typeof value.code !== "string") return new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an unreadable refusal.");
2332
+ if (value.code === "reparse_point") return new SecureFileCustodyError("reparse_point", "The Windows Host securefs file is a reparse point.");
2333
+ if (value.code === "not_found") return new SecureFileCustodyError("not_found", "The Windows Host securefs file was not found.");
2334
+ if (value.code === "too_large") return new SecureFileCustodyError("too_large", "The Windows Host securefs file exceeds its read bound.");
2335
+ if (value.code === "already_exists") return new SecureFileCustodyError("already_exists", "The Windows Host securefs target already exists.");
2336
+ if (value.code === "not_private") return new SecureFileCustodyError("not_private", "The Windows Host securefs file is not private to the current user.");
2337
+ return new SecureFileCustodyError("unavailable", "The Windows Host securefs command refused the file read.");
2252
2338
  }
2253
- function hostRelayIpcCode(value) {
2254
- return typeof value === "string" && ["admitted", "already_admitted", "producer_epoch_stale", "sequence_gap", "frame_identity_conflict", "producer_queue_full", "relay_queue_full", "invalid_frame_encoding", "invalid_frame_length", "frame_truncated", "unsupported_frame_schema", "invalid_frame_shape", "invalid_producer_identity", "frame_too_large", "producer_already_connected", "relay_unavailable"].includes(value);
2339
+ function resolveInstalledHostExecutable() {
2340
+ const privateRuntimeRoot = dirname(fileURLToPath(import.meta.url));
2341
+ return win32.join(privateRuntimeRoot, "..", "..", "bin", "ceal-host.exe");
2255
2342
  }
2256
- function nonnegative(value) {
2257
- return Number.isSafeInteger(value) && Number(value) >= 0;
2258
- }
2259
- function record2(value) {
2343
+ function isRecord3(value) {
2260
2344
  return value !== null && typeof value === "object" && !Array.isArray(value);
2261
2345
  }
2262
- function decodeResponseBuffer(buffer) {
2263
- if (buffer.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
2264
- const length = buffer.readUInt32BE(0);
2265
- if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFact("declared_bytes", length, `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}`)}.`);
2266
- if (buffer.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
2267
- if (buffer.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC returned trailing bytes: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `the received message length minus ${HOST_RELAY_IPC_LENGTH_BYTES}` }, { name: "received_bytes", value: buffer.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
2268
- return decodeHostRelayIpcResponse(buffer);
2269
- }
2270
- function decodeCurrentPromptResponseBuffer(buffer) {
2271
- const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
2272
- if (value === null) return null;
2273
- if (!record2(value) || !exactKeys4(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");
2274
- return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
2275
- }
2276
- function decodeLengthPrefixedJson(input, maximum) {
2277
- if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
2278
- const length = input.readUInt32BE(0);
2279
- if (length === 0 || length > maximum) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC message length is invalid: ${refusalFact("declared_bytes", length, `between 1 and ${maximum}`)}.`);
2280
- if (input.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
2281
- if (input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC message has trailing bytes: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `the received message length minus ${HOST_RELAY_IPC_LENGTH_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
2282
- try {
2283
- return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
2284
- } catch {
2285
- throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC message is invalid.");
2286
- }
2287
- }
2288
- function encodeLengthPrefixed(value) {
2289
- const body = Buffer.from(JSON.stringify(value), "utf8");
2290
- const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
2291
- output.writeUInt32BE(body.length, 0);
2292
- body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
2293
- return output;
2346
+ function isOwnedBy(uid, expectedUid) {
2347
+ return Number.isSafeInteger(uid) && Number.isSafeInteger(expectedUid) && uid === expectedUid;
2294
2348
  }
2295
- function exactKeys4(value, required, optional = []) {
2296
- const keys = Object.keys(value);
2297
- return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
2349
+ function isSelfOwned(uid, platform = process.platform, _handle) {
2350
+ if (platform === "win32" || typeof process.geteuid !== "function") return false;
2351
+ return isOwnedBy(uid, process.geteuid());
2298
2352
  }
2299
- function errorCode(error) {
2300
- return record2(error) && typeof error.code === "string" ? error.code : null;
2353
+ function isPrivateFileMode(mode, platform = process.platform, _handle) {
2354
+ if (platform === "win32") return false;
2355
+ return (mode & 63) === 0;
2301
2356
  }
2302
2357
 
2303
2358
  // host/relay/src/host-producer-core.ts
@@ -2306,6 +2361,20 @@ function isHostTranscriptCursorReason(value) {
2306
2361
  return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
2307
2362
  }
2308
2363
  var TRANSCRIPT_MISSING_REASON = "transcript_missing";
2364
+ var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
2365
+ var HOST_INVOCATION_TRANSCRIPT_TAIL_MAX_BYTES = 1024 * 1024;
2366
+ function transcriptScanFailureKind(error, adapterFailureKind) {
2367
+ if (transcriptErrorCode(error) === "ELOOP") return "symlink";
2368
+ const adapter = adapterFailureKind(error);
2369
+ if (adapter !== void 0) return adapter;
2370
+ const code = transcriptErrorCode(error);
2371
+ if (code === "EFBIG") return "size_budget";
2372
+ if (code !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code)) return "read_error";
2373
+ return "other";
2374
+ }
2375
+ function transcriptErrorCode(error) {
2376
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
2377
+ }
2309
2378
  var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
2310
2379
  async function readTranscriptCompleteChunk(handle, offset, size) {
2311
2380
  const available = Math.max(0, size - offset);
@@ -2350,10 +2419,10 @@ async function transcriptCursorBoundsReason(handle, cursor, size) {
2350
2419
  return null;
2351
2420
  }
2352
2421
  function transcriptSha256(value) {
2353
- return createHash3("sha256").update(value).digest("hex");
2422
+ return createHash4("sha256").update(value).digest("hex");
2354
2423
  }
2355
2424
  function transcriptObjectSha256(value) {
2356
- return createHash3("sha256").update(JSON.stringify(value), "utf8").digest("hex");
2425
+ return createHash4("sha256").update(JSON.stringify(value), "utf8").digest("hex");
2357
2426
  }
2358
2427
  function isTranscriptRecord(value) {
2359
2428
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -2422,11 +2491,10 @@ var HostTranscriptWatcher = class {
2422
2491
  async openTranscript(registration, scan, handlers) {
2423
2492
  let handle;
2424
2493
  try {
2425
- handle = await open2(this.transcriptPathOf(registration), O_RDONLY2 | O_NOFOLLOW);
2494
+ handle = await openNoFollow(this.transcriptPathOf(registration));
2426
2495
  return await scan(handle);
2427
2496
  } catch (error) {
2428
2497
  if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
2429
- if (hostRelayExchangeFailure(error) !== void 0) throw error;
2430
2498
  return handlers.failed(handle, error);
2431
2499
  } finally {
2432
2500
  await handle?.close();
@@ -2472,29 +2540,12 @@ var HostTranscriptWatcher = class {
2472
2540
  return transcriptCursorBoundsReason(handle, cursor, size);
2473
2541
  }
2474
2542
  };
2475
- var HOST_PRODUCER_STATE_IDENTITY = {
2476
- codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
2477
- claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
2478
- };
2479
2543
  var HostProducerDrainCore = class {
2480
2544
  host;
2481
- relay;
2482
- statePath;
2483
- stateSchema;
2484
- installationRef;
2485
- epoch;
2486
2545
  now;
2487
2546
  tail = Promise.resolve();
2488
2547
  constructor(host, runtime) {
2489
- if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
2490
- const epoch = runtime.producer_epoch ?? 1;
2491
- if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
2492
2548
  this.host = host;
2493
- this.relay = runtime.relay;
2494
- this.statePath = join2(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
2495
- this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
2496
- this.installationRef = runtime.producer_installation_ref;
2497
- this.epoch = epoch;
2498
2549
  this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
2499
2550
  }
2500
2551
  drainOutbox() {
@@ -2504,118 +2555,29 @@ var HostProducerDrainCore = class {
2504
2555
  }
2505
2556
  async drainLocked() {
2506
2557
  const read = await this.host.listEnvelopes();
2507
- let admitted = 0;
2558
+ let removed = 0;
2508
2559
  let expired = 0;
2509
- let retained = 0;
2510
2560
  for (const envelope of read.entries) {
2511
- const reserved = await this.hasReservation(this.host.submissionRefOf(envelope));
2512
- if (!reserved && Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) {
2513
- await this.host.removeEnvelope(envelope);
2514
- expired += 1;
2515
- continue;
2516
- }
2517
- const frames = await this.reserve(envelope);
2518
- let complete = true;
2519
- for (const frame of frames) {
2520
- const outcome = await this.relay.admit(frame);
2521
- if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
2522
- complete = false;
2523
- break;
2524
- }
2525
- }
2526
- if (!complete) {
2527
- retained += 1;
2528
- break;
2529
- }
2561
+ if (Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) expired += 1;
2562
+ else removed += 1;
2530
2563
  await this.host.removeEnvelope(envelope);
2531
- await this.release(this.host.submissionRefOf(envelope));
2532
- admitted += 1;
2533
- }
2534
- return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
2535
- }
2536
- async hasReservation(submissionRef) {
2537
- return (await this.readState()).pending[submissionRef] !== void 0;
2538
- }
2539
- async reserve(envelope) {
2540
- const state = await this.readState();
2541
- const submissionRef = this.host.submissionRefOf(envelope);
2542
- const existing = state.pending[submissionRef];
2543
- if (existing !== void 0) return existing;
2544
- const drafts = await this.host.mapEnvelope(envelope);
2545
- const frames = drafts.map((draft, offset) => ({
2546
- schema_version: "ceal.host_producer_frame.v1",
2547
- producer_id: this.host.producer_id,
2548
- producer_installation_ref: this.installationRef,
2549
- producer_epoch: this.epoch,
2550
- sequence: state.next_sequence + offset,
2551
- frame_kind: draft.frame_kind,
2552
- occurred_at: this.host.createdAtOf(envelope),
2553
- payload: draft.payload
2554
- }));
2555
- await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [submissionRef]: frames } });
2556
- return frames;
2557
- }
2558
- async release(submissionRef) {
2559
- const state = await this.readState();
2560
- if (state.pending[submissionRef] === void 0) return;
2561
- const pending = { ...state.pending };
2562
- delete pending[submissionRef];
2563
- await this.writeState({ ...state, pending });
2564
- }
2565
- async readState() {
2566
- try {
2567
- const value = JSON.parse(await readFile(this.statePath, "utf8"));
2568
- return this.decodeState(value);
2569
- } catch (error) {
2570
- if (errorCode2(error) !== "ENOENT") throw error;
2571
- return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
2572
- }
2573
- }
2574
- async writeState(state) {
2575
- await mkdir2(this.host.outbox_directory, { recursive: true, mode: 448 });
2576
- const temporary = `${this.statePath}.${process.pid}.new`;
2577
- await writeFile(temporary, JSON.stringify(state), { mode: 384 });
2578
- await rename2(temporary, this.statePath);
2579
- }
2580
- decodeState(value) {
2581
- const label = this.host.producer_label;
2582
- const fail = (code, message) => this.host.fail(code, message);
2583
- if (!isDrainStateHeader(value, this.stateSchema, this.installationRef)) {
2584
- throw fail("storage", `${label} producer state header is malformed or belongs to another installation (${refusalFacts([
2585
- { name: "schema_version", value: isTranscriptRecord(value) ? value.schema_version : void 0, expected: this.stateSchema },
2586
- { name: "producer_installation_ref", value: isTranscriptRecord(value) ? value.producer_installation_ref : void 0, expected: this.installationRef }
2587
- ])}).`);
2588
- }
2589
- 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}`)}).`);
2590
- const pending = {};
2591
- const storedEpoch = Number(value.producer_epoch);
2592
- for (const [submissionRef, frames] of Object.entries(value.pending)) {
2593
- if (!isHostSafeRef(submissionRef) || !Array.isArray(frames) || frames.length === 0) throw fail("storage", `${label} producer pending reservation is malformed.`);
2594
- pending[submissionRef] = frames.map((frame) => this.decodeStoredFrame(frame, storedEpoch));
2595
2564
  }
2596
- return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: Number(value.next_sequence), pending };
2597
- }
2598
- decodeStoredFrame(value, storedEpoch) {
2599
- const label = this.host.producer_label;
2600
- const json = Buffer.from(JSON.stringify(value), "utf8");
2601
- const encoded = Buffer.allocUnsafe(json.length + 4);
2602
- encoded.writeUInt32BE(json.length, 0);
2603
- json.copy(encoded, 4);
2604
- const frame = decodeHostProducerFrame(encoded);
2605
- 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([
2606
- { name: "producer_id", value: frame.producer_id, expected: this.host.producer_id },
2607
- { name: "producer_installation_ref", value: frame.producer_installation_ref, expected: this.installationRef },
2608
- { name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${storedEpoch}` }
2609
- ])}).`);
2610
- return frame;
2565
+ return { removed, expired, malformed: read.malformed };
2611
2566
  }
2612
2567
  };
2613
- function isDrainStateHeader(value, schema, installationRef) {
2614
- 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);
2615
- }
2616
- function errorCode2(error) {
2617
- return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2618
- }
2568
+
2569
+ // host/relay/src/durable-atomic-replacement.ts
2570
+ import { lstat as lstat2, mkdir, open as open2, rename, unlink } from "node:fs/promises";
2571
+ var DEFAULT_PORT = {
2572
+ platform: process.platform,
2573
+ mkdir: (path, options) => mkdir(path, options),
2574
+ lstat: (path) => lstat2(path),
2575
+ open_file: (path) => open2(path, "wx", 384),
2576
+ rename,
2577
+ unlink,
2578
+ open_directory: (path) => open2(path),
2579
+ unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
2580
+ };
2619
2581
 
2620
2582
  // host/relay/src/relay-quarantine-store.ts
2621
2583
  var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
@@ -2627,11 +2589,21 @@ var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, produ
2627
2589
  var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
2628
2590
  var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
2629
2591
 
2630
- // host/relay/src/relay-gateway-bridge.ts
2631
- var HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS = ["created", "adopted", "replayed", "approval_approved", "approval_denied", "approval_replayed"];
2632
- function isHostGatewayAcknowledgementDisposition(value) {
2633
- return typeof value === "string" && HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS.some((disposition) => disposition === value);
2634
- }
2592
+ // host/relay/src/relay-pipe-handshake.ts
2593
+ var RELAY_PIPE_FRAME_BYTES = Uint32Array.BYTES_PER_ELEMENT;
2594
+
2595
+ // host/relay/src/relay-ipc.ts
2596
+ var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
2597
+ var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
2598
+ "permission_denied",
2599
+ "socket_missing",
2600
+ "connection_refused",
2601
+ "closed_early",
2602
+ "response_invalid",
2603
+ "timed_out",
2604
+ "cancelled",
2605
+ "unavailable"
2606
+ ]);
2635
2607
 
2636
2608
  // host/codex/src/codex-invocation-context.ts
2637
2609
  function codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef) {
@@ -2645,59 +2617,40 @@ function validateCodexHostInvocationContext(value) {
2645
2617
  validateContextReferences(value);
2646
2618
  validateContextTimes(value);
2647
2619
  }
2648
- function invocationContextFromBinding(binding, turnGenerationRef) {
2649
- const context = {
2650
- schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
2651
- identity: binding.identity,
2652
- prompt_ref: binding.prompt_ref,
2653
- request_ref: binding.request_ref,
2654
- bound_at: binding.bound_at,
2655
- expires_at: binding.expires_at,
2656
- ...turnGenerationRef === void 0 ? {} : { turn_generation_ref: turnGenerationRef }
2657
- };
2658
- validateCodexHostInvocationContext(context);
2659
- return Object.freeze(context);
2660
- }
2661
2620
  function validateIdentity(value) {
2662
- if (!isRecord3(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef3)) {
2621
+ if (!isRecord4(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef2)) {
2663
2622
  throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
2664
2623
  }
2665
2624
  }
2666
2625
  function isContextShape(value) {
2667
- return isRecord3(value) && hasHostInvocationContextKeys(value, ["request_ref", "bound_at", "turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
2626
+ return isRecord4(value) && hasHostInvocationContextKeys(value, ["turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
2668
2627
  }
2669
2628
  function validateContextReferences(value) {
2670
- if (!safeRef3(value.prompt_ref) || !optionalSafeRef(value.request_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2629
+ if (!safeRef2(value.prompt_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2671
2630
  }
2672
2631
  function validateContextTimes(value) {
2673
2632
  const expiresAt = value.expires_at;
2674
- const boundAt = value.bound_at;
2675
- if (!timestamp2(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2676
- 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}`)}).`);
2633
+ if (!timestamp(expiresAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
2677
2634
  }
2678
2635
  function optionalSafeRef(value) {
2679
- return value === void 0 || safeRef3(value);
2680
- }
2681
- function optionalTimestamp(value) {
2682
- return value === void 0 || timestamp2(value);
2636
+ return value === void 0 || safeRef2(value);
2683
2637
  }
2684
- function safeRef3(value) {
2638
+ function safeRef2(value) {
2685
2639
  return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
2686
2640
  }
2687
- function timestamp2(value) {
2641
+ function timestamp(value) {
2688
2642
  return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
2689
2643
  }
2690
- function isRecord3(value) {
2644
+ function isRecord4(value) {
2691
2645
  return value !== null && typeof value === "object" && !Array.isArray(value);
2692
2646
  }
2693
2647
 
2694
2648
  // host/codex/src/codex-outbox.ts
2695
- var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2649
+ var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2696
2650
  var CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS = 4096;
2697
2651
  var CodexOutboxStore = class {
2698
2652
  directory;
2699
2653
  entriesDirectory;
2700
- bindingsDirectory;
2701
2654
  sessionsDirectory;
2702
2655
  activeContextsDirectory;
2703
2656
  turnSelectionsDirectory;
@@ -2706,17 +2659,16 @@ var CodexOutboxStore = class {
2706
2659
  constructor(directory) {
2707
2660
  if (typeof directory !== "string" || directory.length === 0) throw new CodexHostAdapterError("configuration", "Codex outbox directory is required.");
2708
2661
  this.directory = directory;
2709
- this.entriesDirectory = join3(directory, "entries");
2710
- this.bindingsDirectory = join3(directory, "bindings");
2711
- this.sessionsDirectory = join3(directory, "sessions");
2712
- this.activeContextsDirectory = join3(directory, "active-contexts");
2713
- this.turnSelectionsDirectory = join3(directory, "turn-selections");
2714
- this.registrationsDirectory = join3(directory, "transcript-registrations");
2715
- this.cursorsDirectory = join3(directory, "transcript-cursors");
2662
+ this.entriesDirectory = join2(directory, "entries");
2663
+ this.sessionsDirectory = join2(directory, "sessions");
2664
+ this.activeContextsDirectory = join2(directory, "active-contexts");
2665
+ this.turnSelectionsDirectory = join2(directory, "turn-selections");
2666
+ this.registrationsDirectory = join2(directory, "transcript-registrations");
2667
+ this.cursorsDirectory = join2(directory, "transcript-cursors");
2716
2668
  }
2717
2669
  async prepare() {
2718
- for (const path of [this.directory, this.entriesDirectory, this.bindingsDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
2719
- await ensurePrivateOwnerDirectory(path);
2670
+ for (const path of [this.directory, this.entriesDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
2671
+ await ensureOwnerDirectory(path);
2720
2672
  }
2721
2673
  }
2722
2674
  async put(entry) {
@@ -2731,7 +2683,7 @@ var CodexOutboxStore = class {
2731
2683
  for (const name of await readdir(this.entriesDirectory)) {
2732
2684
  if (!name.endsWith(".json")) continue;
2733
2685
  try {
2734
- const value = JSON.parse(await readPrivateFile(join3(this.entriesDirectory, name)));
2686
+ const value = JSON.parse(await readOwnerStateFile(join2(this.entriesDirectory, name)));
2735
2687
  validateEnvelope(value);
2736
2688
  entries.push(value);
2737
2689
  } catch {
@@ -2749,11 +2701,6 @@ var CodexOutboxStore = class {
2749
2701
  if (!isNotFound(error)) throw storageError("Unable to remove the Codex outbox entry.");
2750
2702
  }
2751
2703
  }
2752
- async writeBinding(binding) {
2753
- validateBinding(binding);
2754
- await this.prepare();
2755
- await writeAtomicJson(this.bindingPath(binding.identity), binding);
2756
- }
2757
2704
  /**
2758
2705
  * Select the exact Host turn that model-reachable commands may use. A new
2759
2706
  * prompt replaces this projection, so it cannot inherit the prior turn's
@@ -2780,7 +2727,7 @@ var CodexOutboxStore = class {
2780
2727
  await writeAtomicJson(this.turnSelectionPath(selection.host_source_ref, selection.host_session_ref), selection);
2781
2728
  }
2782
2729
  async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
2783
- if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
2730
+ if (!SAFE_REF3.test(hostSourceRef) || !SAFE_REF3.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
2784
2731
  await this.prepare();
2785
2732
  return readOptionalJson(this.turnSelectionPath(hostSourceRef, hostSessionRef), validateTurnSelection);
2786
2733
  }
@@ -2793,23 +2740,11 @@ var CodexOutboxStore = class {
2793
2740
  await writeAtomicJson(path, { ...active, turn_generation_ref: turnGenerationRef });
2794
2741
  return true;
2795
2742
  }
2796
- /**
2797
- * Publish a Request reference only while the acknowledged binding is still
2798
- * the active turn. A delayed relay response therefore cannot overwrite a
2799
- * newer prompt's context.
2800
- */
2801
- async bindInvocationContext(binding, now = /* @__PURE__ */ new Date()) {
2802
- validateBinding(binding);
2803
- const active = await this.readInvocationContext(binding.identity.host_source_ref, binding.identity.host_session_ref, now);
2804
- if (active === null || !sameIdentity(active.identity, binding.identity)) return false;
2805
- await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding, active.turn_generation_ref));
2806
- return true;
2807
- }
2808
2743
  /** Return only the live session projection; never fall back to another turn. */
2809
2744
  async readInvocationContext(hostSourceRef, hostSessionRef, now = /* @__PURE__ */ new Date()) {
2810
2745
  if (!(now instanceof Date) || !Number.isFinite(now.getTime())) throw new CodexHostAdapterError("invalid_input", "Codex Host context clock is invalid.");
2811
2746
  const context = await this.readRawInvocationContext(hostSourceRef, hostSessionRef);
2812
- return isLiveSessionProjection(context, now) && invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
2747
+ return isLiveSessionProjection(context, now) ? context : null;
2813
2748
  }
2814
2749
  /**
2815
2750
  * The latest Codex session projection even when past expiry, so the
@@ -2817,10 +2752,10 @@ var CodexOutboxStore = class {
2817
2752
  * a missing one. Every other caller keeps the fail-closed read above.
2818
2753
  */
2819
2754
  async readRawInvocationContext(hostSourceRef, hostSessionRef) {
2820
- if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
2755
+ if (!SAFE_REF3.test(hostSourceRef) || !SAFE_REF3.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
2821
2756
  await this.prepare();
2822
2757
  const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, (file) => readOptionalJson(file, validateCodexHostInvocationContext));
2823
- return invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
2758
+ return context;
2824
2759
  }
2825
2760
  /** Clear the projection only if the caller still owns its exact active turn. */
2826
2761
  async clearInvocationContext(identity, now = /* @__PURE__ */ new Date()) {
@@ -2834,11 +2769,6 @@ var CodexOutboxStore = class {
2834
2769
  }
2835
2770
  return true;
2836
2771
  }
2837
- async readBinding(identity) {
2838
- validateIdentity2(identity);
2839
- await this.prepare();
2840
- return readOptionalJson(this.bindingPath(identity), validateBinding);
2841
- }
2842
2772
  async saveSession(snapshot) {
2843
2773
  validateSession(snapshot);
2844
2774
  await this.prepare();
@@ -2846,7 +2776,7 @@ var CodexOutboxStore = class {
2846
2776
  }
2847
2777
  async readSession(sourceRef, sessionRef) {
2848
2778
  const identity = { host_source_ref: sourceRef, host_session_ref: sessionRef };
2849
- if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex session identity is invalid.");
2779
+ if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex session identity is invalid.");
2850
2780
  await this.prepare();
2851
2781
  return readOptionalJson(this.sessionPath(identity.host_source_ref, identity.host_session_ref), validateSession);
2852
2782
  }
@@ -2879,7 +2809,7 @@ var CodexOutboxStore = class {
2879
2809
  for (const name of await readdir(this.registrationsDirectory)) {
2880
2810
  if (!name.endsWith(".json")) continue;
2881
2811
  try {
2882
- const value = JSON.parse(await readPrivateFile(join3(this.registrationsDirectory, name)));
2812
+ const value = JSON.parse(await readOwnerStateFile(join2(this.registrationsDirectory, name)));
2883
2813
  validateRegistration(value);
2884
2814
  registrations.push(value);
2885
2815
  } catch {
@@ -2898,48 +2828,37 @@ var CodexOutboxStore = class {
2898
2828
  await writeAtomicJson(this.cursorPath(cursor.host_source_ref, cursor.host_session_ref), cursor);
2899
2829
  }
2900
2830
  async findByAlias(sourceRef, sessionRef, alias) {
2901
- if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef) || !SAFE_REF4.test(alias)) return null;
2831
+ if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef) || !SAFE_REF3.test(alias)) return null;
2902
2832
  const read = await this.list();
2903
2833
  const envelope = findEnvelopeByAlias(read.entries, sourceRef, sessionRef, alias);
2904
2834
  if (envelope !== null) return envelopeMatch(envelope);
2905
2835
  await this.prepare();
2906
- const binding = await findBindingByAlias(this.bindingsDirectory, sourceRef, sessionRef, alias);
2907
- if (binding !== null) return bindingMatch(binding);
2908
2836
  return null;
2909
2837
  }
2910
2838
  async hasPriorTurn(sourceRef, sessionRef) {
2911
- if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) return false;
2839
+ if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef)) return false;
2912
2840
  const read = await this.list();
2913
2841
  if (read.entries.some((entry) => entry.identity.host_source_ref === sourceRef && entry.identity.host_session_ref === sessionRef)) return true;
2914
- await this.prepare();
2915
- for (const name of await readdir(this.bindingsDirectory)) {
2916
- if (!name.endsWith(".json")) continue;
2917
- const binding = await readBindingCandidate(this.bindingsDirectory, name);
2918
- if (binding?.identity.host_source_ref === sourceRef && binding.identity.host_session_ref === sessionRef) return true;
2919
- }
2920
2842
  return false;
2921
2843
  }
2922
2844
  entryPath(submissionRef) {
2923
- if (!SAFE_REF4.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
2924
- return join3(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
2925
- }
2926
- bindingPath(identity) {
2927
- return join3(this.bindingsDirectory, `${identityKey(identity)}.json`);
2845
+ if (!SAFE_REF3.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
2846
+ return join2(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
2928
2847
  }
2929
2848
  sessionPath(sourceRef, sessionRef) {
2930
- return join3(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
2849
+ return join2(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
2931
2850
  }
2932
2851
  activeContextPath(identity) {
2933
- return join3(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
2852
+ return join2(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
2934
2853
  }
2935
2854
  turnSelectionPath(sourceRef, sessionRef) {
2936
- return join3(this.turnSelectionsDirectory, `${invocationContextKey({ host_source_ref: sourceRef, host_session_ref: sessionRef })}.json`);
2855
+ return join2(this.turnSelectionsDirectory, `${invocationContextKey({ host_source_ref: sourceRef, host_session_ref: sessionRef })}.json`);
2937
2856
  }
2938
2857
  registrationPath(sourceRef, sessionRef) {
2939
- return join3(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
2858
+ return join2(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
2940
2859
  }
2941
2860
  cursorPath(sourceRef, sessionRef) {
2942
- return join3(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
2861
+ return join2(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
2943
2862
  }
2944
2863
  };
2945
2864
  async function removeOwnerState(path, what) {
@@ -2950,14 +2869,11 @@ async function removeOwnerState(path, what) {
2950
2869
  }
2951
2870
  }
2952
2871
  function sessionStateKey(sourceRef, sessionRef) {
2953
- if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) throw storageError("Codex transcript session identity is malformed.");
2872
+ if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef)) throw storageError("Codex transcript session identity is malformed.");
2954
2873
  return cealRequestSha256({ host_source_ref: sourceRef, host_session_ref: sessionRef });
2955
2874
  }
2956
2875
  function envelopeMatch(envelope) {
2957
- return { identity: envelope.identity, prompt_ref: envelope.prompt_ref, ...envelope.request_ref === void 0 ? {} : { request_ref: envelope.request_ref }, envelope };
2958
- }
2959
- function bindingMatch(binding) {
2960
- return { identity: binding.identity, prompt_ref: binding.prompt_ref, request_ref: binding.request_ref, binding };
2876
+ return { identity: envelope.identity, prompt_ref: envelope.prompt_ref, envelope };
2961
2877
  }
2962
2878
  function findEnvelopeByAlias(entries, sourceRef, sessionRef, alias) {
2963
2879
  for (const envelope of entries) {
@@ -2966,24 +2882,6 @@ function findEnvelopeByAlias(entries, sourceRef, sessionRef, alias) {
2966
2882
  }
2967
2883
  return null;
2968
2884
  }
2969
- async function findBindingByAlias(directory, sourceRef, sessionRef, alias) {
2970
- for (const name of await readdir(directory)) {
2971
- if (!name.endsWith(".json")) continue;
2972
- const binding = await readBindingCandidate(directory, name);
2973
- if (binding === null || binding.identity.host_source_ref !== sourceRef || binding.identity.host_session_ref !== sessionRef) continue;
2974
- if (binding.codex_turn_ids.includes(alias)) return binding;
2975
- }
2976
- return null;
2977
- }
2978
- async function readBindingCandidate(directory, name) {
2979
- try {
2980
- const value = JSON.parse(await readPrivateFile(join3(directory, name)));
2981
- validateBinding(value);
2982
- return value;
2983
- } catch {
2984
- return null;
2985
- }
2986
- }
2987
2885
  function identityKey(identity) {
2988
2886
  validateIdentity2(identity);
2989
2887
  return cealRequestSha256(identity);
@@ -2996,15 +2894,15 @@ async function writeAtomicJson(path, value) {
2996
2894
  if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
2997
2895
  const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
2998
2896
  try {
2999
- const handle = await open3(temp, "wx", 384);
2897
+ const handle = await open3(temp, "wx");
3000
2898
  try {
3001
2899
  await handle.writeFile(text, "utf8");
3002
2900
  await handle.sync();
3003
2901
  } finally {
3004
2902
  await handle.close();
3005
2903
  }
3006
- await rename3(temp, path);
3007
- await syncDirectory(dirname(path));
2904
+ await rename2(temp, path);
2905
+ await syncDirectory(dirname2(path));
3008
2906
  } catch (error) {
3009
2907
  try {
3010
2908
  await unlink2(temp);
@@ -3027,7 +2925,7 @@ async function syncDirectory(path) {
3027
2925
  }
3028
2926
  async function readOptionalJson(path, validate) {
3029
2927
  try {
3030
- const value = JSON.parse(await readPrivateFile(path));
2928
+ const value = JSON.parse(await readOwnerStateFile(path));
3031
2929
  validate(value);
3032
2930
  return value;
3033
2931
  } catch (error) {
@@ -3036,36 +2934,30 @@ async function readOptionalJson(path, validate) {
3036
2934
  throw storageError("Codex owner state is malformed.");
3037
2935
  }
3038
2936
  }
3039
- async function ensurePrivateOwnerDirectory(path) {
2937
+ async function ensureOwnerDirectory(path) {
3040
2938
  try {
3041
- await lstat2(path);
2939
+ await stat(path);
3042
2940
  } catch (error) {
3043
2941
  if (!isNotFound(error)) throw storageError("Codex owner state directory is unavailable.");
3044
- await mkdir3(path, { recursive: true, mode: 448 });
2942
+ await mkdir2(path, { recursive: true });
3045
2943
  }
3046
- const stats = await lstat2(path);
3047
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
3048
- if (!stats.isDirectory() || stats.isSymbolicLink()) throw storageError(`Codex owner state path is not a directory (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a directory mode (0o40xxx)")}).`);
3049
- if ((stats.mode & 63) !== 0) throw storageError(`Codex owner state directory is readable beyond its owner (${refusalFact("permission_bits", `0o${(stats.mode & 511).toString(8)}`, "no group or other bits")}).`);
3050
- if (stats.uid !== uid) throw storageError(`Codex owner state directory is owned by another user (${refusalFact("uid", stats.uid, `expected ${uid}`)}).`);
2944
+ const stats = await stat(path);
2945
+ if (!stats.isDirectory()) throw storageError("Codex owner state path is not a directory.");
3051
2946
  }
3052
- async function readPrivateFile(path) {
2947
+ async function readOwnerStateFile(path) {
3053
2948
  let handle;
3054
2949
  try {
3055
- handle = await open3(path, O_RDONLY3 | O_NOFOLLOW2);
2950
+ handle = await open3(path);
3056
2951
  const stats = await handle.stat();
3057
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
3058
- 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)")}).`);
3059
- if ((stats.mode & 63) !== 0) throw storageError(`Codex owner state file is readable beyond its owner (${refusalFact("permission_bits", `0o${(stats.mode & 511).toString(8)}`, "no group or other bits")}).`);
3060
- if (stats.uid !== uid) throw storageError(`Codex owner state file is owned by another user (${refusalFact("uid", stats.uid, `expected ${uid}`)}).`);
2952
+ if (!stats.isFile()) throw storageError("Codex owner state path is not a regular file.");
3061
2953
  return await handle.readFile("utf8");
3062
2954
  } finally {
3063
2955
  await handle?.close();
3064
2956
  }
3065
2957
  }
3066
2958
  function validateEnvelope(value) {
3067
- if (!isRecord4(value)) throw storageError("Codex outbox entry is malformed.");
3068
- 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")}).`);
2959
+ if (!isRecord5(value)) throw storageError("Codex outbox entry is malformed.");
2960
+ if (!sameKeys(value, ["schema_version", "identity", "host_input_ref", "prompt_ref", "observations", "origin_input", "submission_ref", "created_at", "expires_at"], ["codex_turn_id"])) throw storageError(`Codex outbox entry key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the outbox entry key set")}).`);
3069
2961
  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}`)}).`);
3070
2962
  validateIdentity2(value.identity);
3071
2963
  validateEnvelopeReferences(value);
@@ -3073,22 +2965,20 @@ function validateEnvelope(value) {
3073
2965
  validateEnvelopeTimestamps(value);
3074
2966
  }
3075
2967
  function validateEnvelopeReferences(value) {
3076
- if (!SAFE_REF4.test(String(value.host_input_ref))) throw storageError("Codex outbox input reference is malformed.");
3077
- if (!isCealHostInputKind(value.input_kind)) throw storageError("Codex outbox input provenance is malformed.");
3078
- if (!SAFE_REF4.test(String(value.prompt_ref))) throw storageError("Codex outbox references are malformed.");
3079
- if (!SAFE_REF4.test(String(value.submission_ref))) throw storageError("Codex outbox references are malformed.");
3080
- if (value.request_ref !== void 0 && !SAFE_REF4.test(String(value.request_ref))) throw storageError("Codex outbox Request reference is malformed.");
3081
- if (value.codex_turn_id !== void 0 && !SAFE_REF4.test(String(value.codex_turn_id))) throw storageError("Codex outbox turn identity is malformed.");
2968
+ if (!SAFE_REF3.test(String(value.host_input_ref))) throw storageError("Codex outbox input reference is malformed.");
2969
+ if (!SAFE_REF3.test(String(value.prompt_ref))) throw storageError("Codex outbox references are malformed.");
2970
+ if (!SAFE_REF3.test(String(value.submission_ref))) throw storageError("Codex outbox references are malformed.");
2971
+ if (value.codex_turn_id !== void 0 && !SAFE_REF3.test(String(value.codex_turn_id))) throw storageError("Codex outbox turn identity is malformed.");
3082
2972
  if (typeof value.origin_input !== "boolean") throw storageError("Codex outbox origin disposition is malformed.");
3083
2973
  }
3084
2974
  function validateRegistration(value) {
3085
- if (!isRecord4(value)) throw storageError("Codex transcript registration is malformed.");
2975
+ if (!isRecord5(value)) throw storageError("Codex transcript registration is malformed.");
3086
2976
  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")}).`);
3087
2977
  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}`)}).`);
3088
2978
  validateRegistrationFields(value);
3089
2979
  }
3090
2980
  function validateRegistrationFields(value) {
3091
- if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate))) throw storageError(`Codex transcript registration references are malformed (${refusalFacts([{ name: "host_source_ref", value: value.host_source_ref }, { name: "host_session_ref", value: value.host_session_ref }, { name: "codex_session_id", value: value.codex_session_id }])}).`);
2981
+ if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate))) throw storageError(`Codex transcript registration references are malformed (${refusalFacts([{ name: "host_source_ref", value: value.host_source_ref }, { name: "host_session_ref", value: value.host_session_ref }, { name: "codex_session_id", value: value.codex_session_id }])}).`);
3092
2982
  if (!validRegistrationCwd(value.cwd)) throw storageError(`Codex transcript registration working directory is malformed (${refusalFact("cwd_characters", typeof value.cwd === "string" ? value.cwd.length : 0, `an absolute path of at most ${CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS} characters`)}).`);
3093
2983
  if (typeof value.transcript_path !== "string") throw storageError(`Codex transcript registration path is malformed (${refusalFact("kind", typeof value.transcript_path, "string")}).`);
3094
2984
  if (value.transcript_path.length === 0 || value.transcript_path.length > CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS) throw storageError(`Codex transcript registration path is malformed (${refusalFact("characters", value.transcript_path.length, `from 1 through ${CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS}`)}).`);
@@ -3098,7 +2988,7 @@ function validRegistrationCwd(value) {
3098
2988
  return value === void 0 || typeof value === "string" && isAbsolute2(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
3099
2989
  }
3100
2990
  function validateCursor(value) {
3101
- if (!isRecord4(value)) throw storageError("Codex transcript cursor is malformed.");
2991
+ if (!isRecord5(value)) throw storageError("Codex transcript cursor is malformed.");
3102
2992
  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")}).`);
3103
2993
  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}`)}).`);
3104
2994
  if (!validCursorSource(value) || !validCursorPosition(value) || !validCursorStatus(value)) throw storageError("Codex transcript cursor fields are malformed.");
@@ -3106,7 +2996,7 @@ function validateCursor(value) {
3106
2996
  if (value.reason !== void 0 && !isHostTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
3107
2997
  }
3108
2998
  function validCursorSource(value) {
3109
- 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";
2999
+ return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate)) && typeof value.transcript_path === "string" && value.transcript_path.length > 0 && typeof value.device === "string" && typeof value.inode === "string";
3110
3000
  }
3111
3001
  function validCursorPosition(value) {
3112
3002
  return nonNegativeInteger(value.offset) && nonNegativeInteger(value.last_ordinal) && nonNegativeInteger(value.anchor_start) && typeof value.anchor_sha256 === "string" && /^[a-f0-9]{64}$/u.test(value.anchor_sha256) && typeof value.header_validated === "boolean";
@@ -3115,23 +3005,23 @@ function validCursorStatus(value) {
3115
3005
  return (value.status === "active" || value.status === "quarantined") && isTimestamp(value.updated_at);
3116
3006
  }
3117
3007
  function validActiveTask(value) {
3118
- 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;
3008
+ if (!isRecord5(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;
3119
3009
  return validActiveTaskIdentity(value) && validActiveTaskReferences(value) && validActiveTaskUsage(value);
3120
3010
  }
3121
3011
  function validActiveTaskIdentity(value) {
3122
- return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate)) && isTimestamp(value.started_at);
3012
+ return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate)) && isTimestamp(value.started_at);
3123
3013
  }
3124
3014
  function validActiveTaskReferences(value) {
3125
3015
  return optionalSafeRef2(value.origin_host_input_ref) && optionalSafeRef2(value.prompt_ref) && (value.model === void 0 || typeof value.model === "string" && value.model.length > 0 && value.model.length <= 256);
3126
3016
  }
3127
3017
  function optionalSafeRef2(value) {
3128
- return value === void 0 || typeof value === "string" && SAFE_REF4.test(value);
3018
+ return value === void 0 || typeof value === "string" && SAFE_REF3.test(value);
3129
3019
  }
3130
3020
  function validActiveTaskUsage(value) {
3131
3021
  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");
3132
3022
  }
3133
3023
  function validTokenUsage(value) {
3134
- if (!isRecord4(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
3024
+ if (!isRecord5(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
3135
3025
  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;
3136
3026
  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);
3137
3027
  }
@@ -3141,7 +3031,7 @@ function nonNegativeInteger(value) {
3141
3031
  function validateEnvelopeObservations(value) {
3142
3032
  if (!Array.isArray(value.observations)) throw storageError("Codex outbox observations are malformed.");
3143
3033
  try {
3144
- for (const observation2 of value.observations) validateCealHostObservationDraft(observation2);
3034
+ for (const observation of value.observations) validateCealHostObservationDraft(observation);
3145
3035
  } catch {
3146
3036
  throw storageError("Codex outbox observations are malformed.");
3147
3037
  }
@@ -3150,34 +3040,23 @@ function validateEnvelopeTimestamps(value) {
3150
3040
  if (!isTimestamp(value.created_at)) throw storageError("Codex outbox timestamps are malformed.");
3151
3041
  if (!isTimestamp(value.expires_at)) throw storageError("Codex outbox timestamps are malformed.");
3152
3042
  }
3153
- function validateBinding(value) {
3154
- if (!isRecord4(value)) throw storageError("Codex turn binding is malformed.");
3155
- 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")}).`);
3156
- 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}`)}).`);
3157
- validateIdentity2(value.identity);
3158
- if (!SAFE_REF4.test(String(value.prompt_ref)) || !SAFE_REF4.test(String(value.request_ref))) throw storageError("Codex turn binding references are malformed.");
3159
- if (!isStringList(value.codex_turn_ids) || !isTimestamp(value.bound_at) || !isTimestamp(value.expires_at)) throw storageError("Codex turn binding fields are malformed.");
3160
- }
3161
3043
  function validateSession(value) {
3162
- if (!isRecord4(value)) throw storageError("Codex session snapshot is malformed.");
3044
+ if (!isRecord5(value)) throw storageError("Codex session snapshot is malformed.");
3163
3045
  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")}).`);
3164
3046
  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}`)}).`);
3165
- 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.");
3047
+ if (!SAFE_REF3.test(String(value.host_session_ref)) || !SAFE_REF3.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.");
3166
3048
  }
3167
3049
  function validateTurnSelection(value) {
3168
3050
  if (!isHostInvocationTurnSelection(value)) throw storageError("Codex Host turn selection is malformed.");
3169
3051
  }
3170
3052
  function validateIdentity2(value) {
3171
- 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.");
3053
+ if (!isRecord5(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate))) throw storageError("Codex Host identity is malformed.");
3172
3054
  }
3173
3055
  function isJsonValue(value) {
3174
3056
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
3175
3057
  if (typeof value === "number") return Number.isFinite(value);
3176
3058
  if (Array.isArray(value)) return value.every(isJsonValue);
3177
- return isRecord4(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
3178
- }
3179
- function isStringList(value) {
3180
- return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate));
3059
+ return isRecord5(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
3181
3060
  }
3182
3061
  function isTimestamp(value) {
3183
3062
  return typeof value === "string" && value.length <= 64 && !Number.isNaN(Date.parse(value));
@@ -3187,23 +3066,22 @@ function sameKeys(value, required, optional = []) {
3187
3066
  const keys = Object.keys(value);
3188
3067
  return keys.length >= required.length && keys.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
3189
3068
  }
3190
- function isRecord4(value) {
3069
+ function isRecord5(value) {
3191
3070
  return value !== null && typeof value === "object" && !Array.isArray(value);
3192
3071
  }
3193
3072
  function isNotFound(error) {
3194
- return isRecord4(error) && error.code === "ENOENT";
3073
+ return isRecord5(error) && error.code === "ENOENT";
3195
3074
  }
3196
3075
  function storageError(message) {
3197
3076
  return new CodexHostAdapterError("storage", message);
3198
3077
  }
3199
3078
 
3200
3079
  // host/codex/src/codex-transcript-baseline.ts
3201
- import { createHash as createHash4 } from "node:crypto";
3202
- import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
3203
- import { open as open4 } from "node:fs/promises";
3080
+ import { createHash as createHash5 } from "node:crypto";
3081
+ import { constants as constants2 } from "node:fs";
3204
3082
 
3205
3083
  // host/codex/src/codex-transcript-decoder.ts
3206
- var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
3084
+ var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
3207
3085
  var MAX_PROMPT_BYTES = 64 * 1024;
3208
3086
  function decodeCodexTranscriptLine(line) {
3209
3087
  let value;
@@ -3212,40 +3090,40 @@ function decodeCodexTranscriptLine(line) {
3212
3090
  } catch {
3213
3091
  reject2("Codex transcript record is not valid JSON.");
3214
3092
  }
3215
- if (!isRecord5(value)) reject2("Codex transcript record envelope is malformed.");
3093
+ if (!isRecord6(value)) reject2("Codex transcript record envelope is malformed.");
3216
3094
  const ordinal = safeOrdinal(value.ordinal);
3217
- const timestamp5 = safeTimestamp(value.timestamp);
3218
- if (!isRecord5(value.payload) || typeof value.type !== "string") reject2("Codex transcript record payload is malformed.");
3219
- if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal, timestamp5);
3220
- if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal, timestamp5);
3221
- if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal, timestamp5);
3222
- if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal, timestamp5);
3223
- return { kind: "ignored", ordinal, timestamp: timestamp5 };
3224
- }
3225
- function decodeTurnContext(payload, ordinal, timestamp5) {
3095
+ const timestamp4 = safeTimestamp(value.timestamp);
3096
+ if (!isRecord6(value.payload) || typeof value.type !== "string") reject2("Codex transcript record payload is malformed.");
3097
+ if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal, timestamp4);
3098
+ if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal, timestamp4);
3099
+ if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal, timestamp4);
3100
+ if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal, timestamp4);
3101
+ return { kind: "ignored", ordinal, timestamp: timestamp4 };
3102
+ }
3103
+ function decodeTurnContext(payload, ordinal, timestamp4) {
3226
3104
  return {
3227
3105
  kind: "turn_context",
3228
3106
  ordinal,
3229
- timestamp: timestamp5,
3230
- turn_id: safeRef4(payload.turn_id, "Codex turn context identity is invalid."),
3107
+ timestamp: timestamp4,
3108
+ turn_id: safeRef3(payload.turn_id, "Codex turn context identity is invalid."),
3231
3109
  model: boundedNonEmptyText(payload.model, 256, "Codex turn context model is invalid.")
3232
3110
  };
3233
3111
  }
3234
- function decodeSessionMeta(payload, ordinal, timestamp5) {
3235
- const sessionId = safeRef4(payload.id, "Codex transcript session identity is invalid.");
3112
+ function decodeSessionMeta(payload, ordinal, timestamp4) {
3113
+ const sessionId = safeRef3(payload.id, "Codex transcript session identity is invalid.");
3236
3114
  const cliVersion = boundedNonEmptyText(payload.cli_version, 64, "Codex transcript version is invalid.");
3237
- return { kind: "session_meta", ordinal, timestamp: timestamp5, session_id: sessionId, cli_version: cliVersion };
3115
+ return { kind: "session_meta", ordinal, timestamp: timestamp4, session_id: sessionId, cli_version: cliVersion };
3238
3116
  }
3239
- function decodeEventMessage(payload, ordinal, timestamp5) {
3117
+ function decodeEventMessage(payload, ordinal, timestamp4) {
3240
3118
  if (payload.type === "task_started") {
3241
- return { kind: "task_started", ordinal, timestamp: timestamp5, turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."), started_at: safeNumber(payload.started_at) };
3119
+ return { kind: "task_started", ordinal, timestamp: timestamp4, turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."), started_at: safeNumber(payload.started_at) };
3242
3120
  }
3243
3121
  if (payload.type === "task_complete") {
3244
3122
  return {
3245
3123
  kind: "task_complete",
3246
3124
  ordinal,
3247
- timestamp: timestamp5,
3248
- turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
3125
+ timestamp: timestamp4,
3126
+ turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."),
3249
3127
  started_at: safeNumber(payload.started_at),
3250
3128
  completed_at: safeNumber(payload.completed_at),
3251
3129
  duration_ms: safeNumber(payload.duration_ms),
@@ -3256,20 +3134,20 @@ function decodeEventMessage(payload, ordinal, timestamp5) {
3256
3134
  return {
3257
3135
  kind: "turn_aborted",
3258
3136
  ordinal,
3259
- timestamp: timestamp5,
3260
- turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
3137
+ timestamp: timestamp4,
3138
+ turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."),
3261
3139
  started_at: safeNumber(payload.started_at),
3262
3140
  completed_at: safeNumber(payload.completed_at),
3263
3141
  duration_ms: safeNumber(payload.duration_ms),
3264
3142
  reason: boundedText(payload.reason, 256, "Codex task abort reason is invalid.")
3265
3143
  };
3266
3144
  }
3267
- if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp5);
3268
- return { kind: "ignored", ordinal, timestamp: timestamp5 };
3145
+ if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp4);
3146
+ return { kind: "ignored", ordinal, timestamp: timestamp4 };
3269
3147
  }
3270
- function decodeTokenCount(payload, ordinal, timestamp5) {
3271
- if (payload.info === null) return { kind: "token_count", ordinal, timestamp: timestamp5 };
3272
- if (!isRecord5(payload.info)) return { kind: "token_count", ordinal, timestamp: timestamp5, usage_gap_reason: "token_count_shape_not_attributable" };
3148
+ function decodeTokenCount(payload, ordinal, timestamp4) {
3149
+ if (payload.info === null) return { kind: "token_count", ordinal, timestamp: timestamp4 };
3150
+ if (!isRecord6(payload.info)) return { kind: "token_count", ordinal, timestamp: timestamp4, usage_gap_reason: "token_count_shape_not_attributable" };
3273
3151
  const lastUsage = optionalTokenUsage(payload.info.last_token_usage);
3274
3152
  const totalUsage = optionalTokenUsage(payload.info.total_token_usage);
3275
3153
  const gap = lastUsage.status === "invalid" || totalUsage.status === "invalid";
@@ -3277,7 +3155,7 @@ function decodeTokenCount(payload, ordinal, timestamp5) {
3277
3155
  return {
3278
3156
  kind: "token_count",
3279
3157
  ordinal,
3280
- timestamp: timestamp5,
3158
+ timestamp: timestamp4,
3281
3159
  ...lastUsage.status === "valid" ? { last_usage: lastUsage.value } : {},
3282
3160
  ...totalUsage.status === "valid" ? { total_usage: totalUsage.value } : {},
3283
3161
  ...usageGapReason === void 0 ? {} : { usage_gap_reason: usageGapReason }
@@ -3293,7 +3171,7 @@ function optionalTokenUsage(value) {
3293
3171
  }
3294
3172
  }
3295
3173
  function decodeTokenUsage(value) {
3296
- if (!isRecord5(value)) reject2("Codex token usage is malformed.");
3174
+ if (!isRecord6(value)) reject2("Codex token usage is malformed.");
3297
3175
  const usage = {
3298
3176
  input_tokens: safeTokenCount(value.input_tokens),
3299
3177
  cached_input_tokens: safeTokenCount(value.cached_input_tokens),
@@ -3305,35 +3183,33 @@ function decodeTokenUsage(value) {
3305
3183
  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) reject2("Codex token usage buckets are inconsistent.");
3306
3184
  return usage;
3307
3185
  }
3308
- function decodeResponseItem(payload, ordinal, timestamp5) {
3309
- if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp5 };
3186
+ function decodeResponseItem(payload, ordinal, timestamp4) {
3187
+ if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp4 };
3310
3188
  const metadata = userMessageMetadata(payload.internal_chat_message_metadata_passthrough);
3311
- const inputKind = inputKindFromContentItems(metadata.content_item_kinds);
3312
- if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp5 };
3189
+ const contentItemKind = userTextContentKind(metadata.content_item_kinds);
3190
+ if (contentItemKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp4 };
3313
3191
  const prompt = userTextContent(payload.content);
3314
3192
  return {
3315
3193
  kind: "user_text",
3316
3194
  ordinal,
3317
- timestamp: timestamp5,
3195
+ timestamp: timestamp4,
3318
3196
  prompt,
3319
- input_kind: inputKind,
3320
- message_id: safeRef4(payload.id, "Codex user message identity is invalid."),
3321
- turn_id: safeRef4(metadata.turn_id, "Codex user message task identity is invalid.")
3197
+ content_item_kind: contentItemKind,
3198
+ message_id: safeRef3(payload.id, "Codex user message identity is invalid."),
3199
+ turn_id: safeRef3(metadata.turn_id, "Codex user message task identity is invalid.")
3322
3200
  };
3323
3201
  }
3324
- function inputKindFromContentItems(kinds) {
3325
- if (kinds.length !== 1) return void 0;
3326
- if (kinds[0] === "user.text") return "human";
3327
- return kinds[0] === "goal.internal_context" ? "host_automatic" : void 0;
3202
+ function userTextContentKind(kinds) {
3203
+ return kinds.length === 1 && (kinds[0] === "user.text" || kinds[0] === "goal.internal_context") ? kinds[0] : void 0;
3328
3204
  }
3329
3205
  function userMessageMetadata(value) {
3330
- if (!isRecord5(value) || !Array.isArray(value.content_item_kinds)) reject2("Codex user message provenance is malformed.");
3206
+ if (!isRecord6(value) || !Array.isArray(value.content_item_kinds)) reject2("Codex user message provenance is malformed.");
3331
3207
  return { ...value, content_item_kinds: value.content_item_kinds };
3332
3208
  }
3333
3209
  function userTextContent(value) {
3334
3210
  if (!Array.isArray(value)) reject2(`Codex user text content is malformed (${refusalFact("kind", typeof value, "array")}).`);
3335
3211
  if (value.length !== 1) reject2(`Codex user text content is malformed (${refusalFact("items", value.length, "exactly one")}).`);
3336
- if (!isRecord5(value[0]) || value[0].type !== "input_text") reject2(`Codex user text content is malformed (${refusalFact("item_type", isRecord5(value[0]) ? value[0].type : typeof value[0], "input_text")}).`);
3212
+ if (!isRecord6(value[0]) || value[0].type !== "input_text") reject2(`Codex user text content is malformed (${refusalFact("item_type", isRecord6(value[0]) ? value[0].type : typeof value[0], "input_text")}).`);
3337
3213
  return boundedText(value[0].text, MAX_PROMPT_BYTES, "Codex user text is invalid.");
3338
3214
  }
3339
3215
  function safeOrdinal(value) {
@@ -3354,8 +3230,8 @@ function safeTimestamp(value) {
3354
3230
  if (!Number.isFinite(Date.parse(value))) reject2(`Codex transcript timestamp is invalid (${refusalFact("parsed_ms", Date.parse(value), "a finite epoch milliseconds value")}).`);
3355
3231
  return value;
3356
3232
  }
3357
- function safeRef4(value, message) {
3358
- if (typeof value !== "string" || !SAFE_REF5.test(value)) reject2(message);
3233
+ function safeRef3(value, message) {
3234
+ if (typeof value !== "string" || !SAFE_REF4.test(value)) reject2(message);
3359
3235
  return value;
3360
3236
  }
3361
3237
  function boundedText(value, maximum, message) {
@@ -3369,7 +3245,7 @@ function boundedNonEmptyText(value, maximum, message) {
3369
3245
  if (result.length === 0) reject2(message);
3370
3246
  return result;
3371
3247
  }
3372
- function isRecord5(value) {
3248
+ function isRecord6(value) {
3373
3249
  return value !== null && typeof value === "object" && !Array.isArray(value);
3374
3250
  }
3375
3251
  function reject2(message) {
@@ -3379,11 +3255,11 @@ function reject2(message) {
3379
3255
  // host/codex/src/codex-transcript-baseline.ts
3380
3256
  var BASELINE_READ_CHUNK_BYTES = 256 * 1024;
3381
3257
  var CODEX_TRANSCRIPT_ANCHOR_BYTES = 256;
3382
- async function captureCodexTranscriptBaseline(registration, now = () => /* @__PURE__ */ new Date()) {
3258
+ async function captureCodexTranscriptBaseline(registration, now = () => /* @__PURE__ */ new Date(), platform = process.platform, custody = {}) {
3383
3259
  let handle;
3384
3260
  try {
3385
- handle = await open4(registration.transcript_path, O_RDONLY4 | O_NOFOLLOW3);
3386
- const { size, device, inode } = await ownedCodexTranscriptFile(handle);
3261
+ handle = await openNoFollow(registration.transcript_path, constants2.O_RDONLY, void 0, platform, custody);
3262
+ const { size, device, inode } = await ownedCodexTranscriptFile(handle, platform);
3387
3263
  const empty = codexTranscriptInitialCursor(registration, device, inode, now);
3388
3264
  const headerLine = await firstCompleteLine(handle, size);
3389
3265
  if (headerLine === null) throw new CodexHostAdapterError("payload_rejected", "Codex transcript header is incomplete.");
@@ -3402,7 +3278,7 @@ async function captureCodexTranscriptBaseline(registration, now = () => /* @__PU
3402
3278
  anchor_start: anchorStart,
3403
3279
  anchor_sha256: digest(anchor),
3404
3280
  header_validated: true,
3405
- updated_at: timestamp3(now)
3281
+ updated_at: timestamp2(now)
3406
3282
  };
3407
3283
  } finally {
3408
3284
  await handle?.close();
@@ -3422,23 +3298,19 @@ function codexTranscriptInitialCursor(registration, device, inode, now) {
3422
3298
  anchor_sha256: digest(Buffer.alloc(0)),
3423
3299
  header_validated: false,
3424
3300
  status: "active",
3425
- updated_at: timestamp3(now)
3301
+ updated_at: timestamp2(now)
3426
3302
  };
3427
3303
  }
3428
- async function ownedCodexTranscriptFile(handle) {
3304
+ async function ownedCodexTranscriptFile(handle, platform = process.platform) {
3429
3305
  const stats = await handle.stat();
3430
- assertCodexTranscriptOwner(stats);
3306
+ if (!stats.isFile()) throw new CodexHostAdapterError("storage", "Codex transcript source is not a regular file.");
3307
+ if (platform !== "win32" && !isSelfOwned(stats.uid, platform, handle)) throw new CodexHostAdapterError("storage", "Codex transcript source is owned by another user.");
3431
3308
  return { size: stats.size, device: String(stats.dev), inode: String(stats.ino) };
3432
3309
  }
3433
- function assertCodexTranscriptOwner(stats) {
3434
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
3435
- if (!stats.isFile()) throw new CodexHostAdapterError("storage", `Codex transcript source is not a regular file (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a regular-file mode (0o100xxx)")}).`);
3436
- if (stats.uid !== uid) throw new CodexHostAdapterError("storage", `Codex transcript source is owned by another user (${refusalFact("uid", stats.uid, `expected ${uid}`)}).`);
3437
- }
3438
- function assertCodexTranscriptHeader(registration, record3) {
3439
- if (record3.kind !== "session_meta" || record3.session_id !== registration.codex_session_id) throw new CodexHostAdapterError("payload_rejected", `Codex transcript header does not match its SessionStart registration (${refusalFacts([
3440
- { name: "kind", value: record3.kind, expected: "session_meta" },
3441
- { name: "session_id", value: record3.kind === "session_meta" ? record3.session_id : "unavailable", expected: registration.codex_session_id }
3310
+ function assertCodexTranscriptHeader(registration, record) {
3311
+ if (record.kind !== "session_meta" || record.session_id !== registration.codex_session_id) throw new CodexHostAdapterError("payload_rejected", `Codex transcript header does not match its SessionStart registration (${refusalFacts([
3312
+ { name: "kind", value: record.kind, expected: "session_meta" },
3313
+ { name: "session_id", value: record.kind === "session_meta" ? record.session_id : "unavailable", expected: registration.codex_session_id }
3442
3314
  ])}).`);
3443
3315
  }
3444
3316
  async function firstCompleteLine(handle, size) {
@@ -3524,24 +3396,32 @@ async function* completeLinesBackward(handle, end) {
3524
3396
  yield { start: 0, bytes: Buffer.concat(carry) };
3525
3397
  }
3526
3398
  function digest(value) {
3527
- return createHash4("sha256").update(value).digest("hex");
3399
+ return createHash5("sha256").update(value).digest("hex");
3528
3400
  }
3529
- function timestamp3(now) {
3401
+ function timestamp2(now) {
3530
3402
  const value = now();
3531
3403
  if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
3532
3404
  return value.toISOString();
3533
3405
  }
3534
3406
 
3407
+ // host/codex/src/codex-runtime-paths.ts
3408
+ import { join as join3 } from "node:path";
3409
+ var CODEX_HOME_ENVIRONMENT = "CODEX_HOME";
3410
+ var CODEX_HOME_DEFAULT_DIRECTORY = ".codex";
3411
+ var CODEX_SESSIONS_DIRECTORY = "sessions";
3412
+ function resolveCodexHome(environment, home) {
3413
+ return environment[CODEX_HOME_ENVIRONMENT] || join3(home, CODEX_HOME_DEFAULT_DIRECTORY);
3414
+ }
3415
+ function resolveCodexSessionsRoot(environment, home) {
3416
+ return join3(resolveCodexHome(environment, home), CODEX_SESSIONS_DIRECTORY);
3417
+ }
3418
+
3535
3419
  // host/codex/src/codex-adapter.ts
3536
- var SAFE_REF6 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3537
- var DEFAULT_TIMEOUT_MS = 5e3;
3538
- var ORIGIN_DELIVERY_CONCURRENCY = 8;
3539
- var CODEX_RELAY_TIMEOUT_MAX_MS = 1e4;
3420
+ var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3540
3421
  function createCodexHostObservationAdapter(options) {
3541
3422
  validateOptions(options);
3542
3423
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
3543
3424
  const retentionMs = options.retention_ms ?? CODEX_DEFAULT_RETENTION_MS;
3544
- const timeoutMs = options.timeout_ms ?? DEFAULT_TIMEOUT_MS;
3545
3425
  const store = new CodexOutboxStore(options.outbox_dir);
3546
3426
  return Object.freeze({
3547
3427
  handle: async (event, payload) => {
@@ -3550,15 +3430,14 @@ function createCodexHostObservationAdapter(options) {
3550
3430
  } catch (error) {
3551
3431
  return { event, disposition: "rejected", reason: safeReason(error) };
3552
3432
  }
3553
- },
3554
- drainOutbox: (signal) => drainOutbox(options, store, now, timeoutMs, signal)
3433
+ }
3555
3434
  });
3556
3435
  }
3557
3436
  async function registerSession(event, options, store, now, retentionMs) {
3558
- const defaultRoot = join4(process.env.CODEX_HOME || join4(homedir(), ".codex"), "sessions");
3437
+ const defaultRoot = resolveCodexSessionsRoot(process.env, homedir());
3559
3438
  const transcriptPath = sourceBoundTranscriptPath(event.transcript_path, options.codex_sessions_root ?? defaultRoot);
3560
3439
  const hostSessionRef = hostSessionReference(options.host_source_ref, event.session_id);
3561
- const observedAt = timestamp4(now);
3440
+ const observedAt = timestamp3(now);
3562
3441
  const expiresAt = new Date(Date.parse(observedAt) + retentionMs).toISOString();
3563
3442
  const registration = {
3564
3443
  schema_version: "ceal.codex_transcript_registration.v1",
@@ -3576,6 +3455,9 @@ async function registerSession(event, options, store, now, retentionMs) {
3576
3455
  await store.writeTranscriptCursor(await captureCodexTranscriptBaseline(registration, now));
3577
3456
  }
3578
3457
  await store.registerTranscript(registration);
3458
+ if (existing !== void 0 && currentTurn !== void 0 && (await store.readTranscriptCursor(registration))?.status === "quarantined") {
3459
+ await store.writeTranscriptCursor(await captureCodexTranscriptBaseline(registration, now));
3460
+ }
3579
3461
  if (event.event === "SessionStart") {
3580
3462
  await store.saveSession({
3581
3463
  schema_version: "ceal.codex_session.v1",
@@ -3592,13 +3474,16 @@ async function registerSession(event, options, store, now, retentionMs) {
3592
3474
  }
3593
3475
  async function selectCurrentTurn(event, options, store, hostSessionRef) {
3594
3476
  if (event.event !== "UserPromptSubmit") return void 0;
3477
+ const host_turn_ref = hostTurnReference(options.host_source_ref, hostSessionRef, event.turn_id);
3478
+ const steered = await liveSteeredTurn(options, store, hostSessionRef, host_turn_ref);
3479
+ if (steered !== void 0) return steered;
3595
3480
  const currentTurn = {
3596
- host_turn_ref: hostTurnReference(options.host_source_ref, hostSessionRef, event.turn_id),
3481
+ host_turn_ref,
3597
3482
  prompt_ref: hostProducerPromptRef(promptObservationValue(event.prompt).body_sha256),
3598
3483
  turn_generation_ref: options.random_uuid?.() ?? randomUUID()
3599
3484
  };
3600
3485
  await store.writeInvocationTurnSelection({
3601
- schema_version: "ceal.host_invocation_turn_selection.v1",
3486
+ schema_version: HOST_INVOCATION_TURN_SELECTION_SCHEMA,
3602
3487
  host_source_ref: options.host_source_ref,
3603
3488
  host_session_ref: hostSessionRef,
3604
3489
  prompt_ref: currentTurn.prompt_ref,
@@ -3606,6 +3491,18 @@ async function selectCurrentTurn(event, options, store, hostSessionRef) {
3606
3491
  });
3607
3492
  return currentTurn;
3608
3493
  }
3494
+ async function liveSteeredTurn(options, store, hostSessionRef, hostTurnRef) {
3495
+ let live;
3496
+ try {
3497
+ live = await store.readInvocationContext(options.host_source_ref, hostSessionRef, (options.now ?? (() => /* @__PURE__ */ new Date()))());
3498
+ } catch {
3499
+ return void 0;
3500
+ }
3501
+ if (live === null || live.identity.host_turn_ref !== hostTurnRef) return void 0;
3502
+ const selection = await store.readInvocationTurnSelection(options.host_source_ref, hostSessionRef).catch(() => null);
3503
+ if (selection === null || selection.prompt_ref !== live.prompt_ref || selection.turn_generation_ref !== live.turn_generation_ref) return void 0;
3504
+ return { host_turn_ref: hostTurnRef, prompt_ref: selection.prompt_ref, turn_generation_ref: selection.turn_generation_ref };
3505
+ }
3609
3506
  function sourceBoundTranscriptPath(path, sessionsRoot) {
3610
3507
  if (!isAbsolute3(path) || !isAbsolute3(sessionsRoot)) throw new CodexHostAdapterError("configuration", "Codex transcript registration paths must be absolute.");
3611
3508
  const transcript = resolve2(path);
@@ -3616,164 +3513,47 @@ function sourceBoundTranscriptPath(path, sessionsRoot) {
3616
3513
  }
3617
3514
  return transcript;
3618
3515
  }
3619
- async function drainOutbox(options, store, now, timeoutMs, signal) {
3620
- const read = await store.list();
3621
- const { live, expired } = await liveOutboxEntries(read.entries, store, now, signal);
3622
- const origins = live.filter((entry) => entry.origin_input === true).sort((left, right) => right.created_at.localeCompare(left.created_at));
3623
- const originResult = await deliverOriginEntries(origins, options, store, now, timeoutMs, signal);
3624
- const boundResult = await deliverBoundEntries(live.filter((entry) => entry.origin_input !== true), options, store, now, timeoutMs, signal);
3625
- return { delivered: originResult.delivered + boundResult.delivered, retained: originResult.retained + boundResult.retained, expired, malformed: read.malformed };
3626
- }
3627
- async function liveOutboxEntries(entries, store, now, signal) {
3628
- const live = [];
3629
- let expired = 0;
3630
- for (const entry of entries) {
3631
- if (signal?.aborted === true) break;
3632
- if (Date.parse(entry.expires_at) > now().getTime()) live.push(entry);
3633
- else {
3634
- await store.remove(entry);
3635
- expired += 1;
3636
- }
3637
- }
3638
- return { live, expired };
3639
- }
3640
- async function deliverOriginEntries(entries, options, store, now, timeoutMs, signal) {
3641
- const counts = { delivered: 0, retained: 0 };
3642
- for (let offset = 0; offset < entries.length; offset += ORIGIN_DELIVERY_CONCURRENCY) {
3643
- if (signal?.aborted === true) break;
3644
- const outcomes = await Promise.allSettled(entries.slice(offset, offset + ORIGIN_DELIVERY_CONCURRENCY).map((entry) => deliverPreparedEntry(entry, entry, options, store, now, timeoutMs, signal)));
3645
- for (const outcome of outcomes) {
3646
- if (outcome.status === "rejected") throw outcome.reason;
3647
- incrementDelivery(counts, outcome.value);
3648
- }
3649
- }
3650
- return counts;
3651
- }
3652
- async function deliverBoundEntries(entries, options, store, now, timeoutMs, signal) {
3653
- const counts = { delivered: 0, retained: 0 };
3654
- for (const entry of entries) {
3655
- if (signal?.aborted === true) break;
3656
- const prepared = await prepareForDelivery(entry, store);
3657
- if (prepared === null) {
3658
- counts.retained += 1;
3659
- continue;
3660
- }
3661
- const outcome = await deliverPreparedEntry(entry, prepared, options, store, now, timeoutMs, signal);
3662
- incrementDelivery(counts, outcome);
3663
- }
3664
- return counts;
3665
- }
3666
- function incrementDelivery(counts, outcome) {
3667
- counts[outcome] += 1;
3668
- }
3669
- async function deliverPreparedEntry(entry, prepared, options, store, now, timeoutMs, signal) {
3670
- try {
3671
- const acknowledgement = await submitWithDeadline(options.gateway, gatewaySubmission(prepared), timeoutMs, signal);
3672
- if (prepared.request_ref === void 0) {
3673
- const binding = bindingFor(prepared, acknowledgement, now);
3674
- await store.writeBinding(binding);
3675
- await store.bindInvocationContext(binding, now());
3676
- }
3677
- await store.remove(entry);
3678
- return "delivered";
3679
- } catch (error) {
3680
- if (error instanceof CodexHostAdapterError && error.code === "payload_rejected") throw error;
3681
- return "retained";
3682
- }
3683
- }
3684
- async function prepareForDelivery(entry, store) {
3685
- const binding = await store.readBinding(entry.identity);
3686
- if (!entry.origin_input && binding === null) return null;
3687
- if (binding === null) return entry;
3688
- return { ...entry, prompt_ref: binding.prompt_ref, request_ref: binding.request_ref };
3689
- }
3690
- function gatewaySubmission(envelope) {
3691
- return {
3692
- identity: envelope.identity,
3693
- host_input_ref: envelope.host_input_ref,
3694
- input_kind: envelope.input_kind,
3695
- prompt_ref: envelope.prompt_ref,
3696
- observations: envelope.observations,
3697
- ...envelope.request_ref === void 0 ? {} : { request_ref: envelope.request_ref },
3698
- submission_ref: envelope.submission_ref
3699
- };
3700
- }
3701
- async function submitWithDeadline(gateway, submission, timeoutMs, signal) {
3702
- if (signal?.aborted === true) throw new CodexHostAdapterError("storage", "Codex relay delivery was cancelled.");
3703
- const controller = new AbortController();
3704
- const abort = () => controller.abort();
3705
- signal?.addEventListener("abort", abort, { once: true });
3706
- let timeoutHandle;
3707
- const timeout = new Promise((_resolve, reject3) => {
3708
- timeoutHandle = setTimeout(() => {
3709
- controller.abort();
3710
- reject3(new CodexHostAdapterError("storage", "Codex relay delivery timed out."));
3711
- }, timeoutMs);
3712
- });
3713
- try {
3714
- const acknowledgement = await Promise.race([gateway.submitHostTurn(submission, controller.signal), timeout]);
3715
- if (!isRecord6(acknowledgement) || !SAFE_REF6.test(String(acknowledgement.request_ref)) || !isHostGatewayAcknowledgementDisposition(acknowledgement.disposition)) {
3716
- throw new CodexHostAdapterError("payload_rejected", "Gateway Host acknowledgement is invalid.");
3717
- }
3718
- return acknowledgement;
3719
- } finally {
3720
- if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
3721
- signal?.removeEventListener("abort", abort);
3722
- }
3723
- }
3724
- function bindingFor(envelope, acknowledgement, now) {
3725
- return {
3726
- schema_version: "ceal.codex_turn_binding.v1",
3727
- identity: envelope.identity,
3728
- prompt_ref: envelope.prompt_ref,
3729
- request_ref: acknowledgement.request_ref,
3730
- codex_turn_ids: envelope.codex_turn_id === void 0 ? [] : [envelope.codex_turn_id],
3731
- bound_at: timestamp4(now),
3732
- expires_at: envelope.expires_at
3733
- };
3734
- }
3735
3516
  function codexSubmissionReference(hostInputRef, event, ordinal) {
3736
- if (!SAFE_REF6.test(hostInputRef) || !Number.isSafeInteger(ordinal) || ordinal < 0) throw new CodexHostAdapterError("invalid_input", "Codex submission identity is invalid.");
3517
+ if (!SAFE_REF5.test(hostInputRef) || !Number.isSafeInteger(ordinal) || ordinal < 0) throw new CodexHostAdapterError("invalid_input", "Codex submission identity is invalid.");
3737
3518
  return `codex-submission:${cealRequestSha256({ host_input_ref: hostInputRef, event, ordinal })}`;
3738
3519
  }
3739
- function timestamp4(now) {
3520
+ function timestamp3(now) {
3740
3521
  const value = now();
3741
3522
  if (!(value instanceof Date) || !Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex adapter clock is invalid.");
3742
3523
  return value.toISOString();
3743
3524
  }
3744
3525
  function validateOptions(options) {
3745
- if (!isRecord6(options)) throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
3526
+ if (!isRecord7(options)) throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
3746
3527
  validateAdapterOwner(options);
3747
3528
  validateAdapterBehavior(options);
3748
3529
  }
3749
3530
  function validateAdapterOwner(options) {
3750
- if (!SAFE_REF6.test(options.host_source_ref) || options.outbox_dir.length === 0 || !isRecord6(options.gateway) || typeof options.gateway.submitHostTurn !== "function") {
3531
+ if (!SAFE_REF5.test(options.host_source_ref) || options.outbox_dir.length === 0) {
3751
3532
  throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
3752
3533
  }
3753
3534
  if (options.codex_sessions_root !== void 0 && !isAbsolute3(options.codex_sessions_root)) throw new CodexHostAdapterError("configuration", "Codex sessions root is invalid.");
3754
3535
  }
3755
3536
  function validateAdapterBehavior(options) {
3756
- if (options.timeout_ms !== void 0 && (!Number.isSafeInteger(options.timeout_ms) || options.timeout_ms < 1 || options.timeout_ms > CODEX_RELAY_TIMEOUT_MAX_MS)) throw new CodexHostAdapterError("configuration", `Codex relay timeout is invalid (${refusalFact("timeout_ms", options.timeout_ms, `an integer from 1 through ${CODEX_RELAY_TIMEOUT_MAX_MS}`)}).`);
3757
3537
  if (options.retention_ms !== void 0 && (!Number.isSafeInteger(options.retention_ms) || options.retention_ms < 1)) throw new CodexHostAdapterError("configuration", `Codex retention is invalid (${refusalFact("retention_ms", options.retention_ms, "a positive integer")}).`);
3758
3538
  }
3759
3539
  function safeReason(error) {
3760
3540
  return error instanceof CodexHostAdapterError ? error.message.slice(0, 128) : "codex_session_registration_rejected";
3761
3541
  }
3762
- function isRecord6(value) {
3542
+ function isRecord7(value) {
3763
3543
  return value !== null && typeof value === "object" && !Array.isArray(value);
3764
3544
  }
3765
3545
 
3766
3546
  // host/producer-service.ts
3767
- import { posix as posix3, win32 as win323 } from "node:path";
3547
+ import { posix as posix3, win32 as win324 } from "node:path";
3768
3548
 
3769
3549
  // host/runtime-source-paths.ts
3770
- import { createHash as createHash5 } from "node:crypto";
3771
- import { posix as posix2, win32 as win322 } from "node:path";
3550
+ import { createHash as createHash6 } from "node:crypto";
3551
+ import { posix as posix2, win32 as win323 } from "node:path";
3772
3552
 
3773
3553
  // host/absolute-path.ts
3774
- import { posix, win32 } from "node:path";
3554
+ import { posix, win32 as win322 } from "node:path";
3775
3555
  function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allowRoot = false) {
3776
- const pathApi = flavor === "win32" || flavor === "windows" ? win32 : posix;
3556
+ const pathApi = flavor === "win32" || flavor === "windows" ? win322 : posix;
3777
3557
  return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
3778
3558
  }
3779
3559
 
@@ -3785,28 +3565,23 @@ function hostSourceUuid(sourceRef) {
3785
3565
  function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
3786
3566
  const sourceId = hostSourceUuid(sourceRef);
3787
3567
  if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
3788
- const path = platform === "win32" ? win322 : posix2;
3568
+ const path = platform === "win32" ? win323 : posix2;
3789
3569
  const sourcesRoot = path.join(stateRoot, "sources");
3790
3570
  const sourceRoot = path.join(sourcesRoot, sourceId);
3791
3571
  const runtimeRoot = path.join(stateRoot, "run");
3792
- const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash5("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
3793
- assertHostRuntimeIpcLength(endpoint, platform);
3572
+ const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash6("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
3794
3573
  return { sourceId, sourcesRoot, sourceRoot, relayRoot: path.join(sourceRoot, "relay"), codexOutbox: path.join(sourceRoot, "producers", "codex", "outbox"), claudeOutbox: path.join(sourceRoot, "producers", "claude", "outbox"), endpoint, runtimeRoot };
3795
3574
  }
3796
- function assertHostRuntimeIpcLength(endpoint, platform) {
3797
- if (platform === "win32") return;
3798
- const maximum = platform === "linux" ? 107 : 103;
3799
- if (Buffer.byteLength(endpoint, "utf8") > maximum) throw Object.assign(new TypeError(`ipc_endpoint_too_long: Host IPC path exceeds ${maximum} bytes; use a shorter Host state root.`), { code: "ipc_endpoint_too_long" });
3800
- }
3801
3575
  function assertHostProducerSourcePaths(stateRoot, config, producer, platform = process.platform) {
3802
3576
  const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
3803
- if (config.outbox_dir !== (producer === "codex" ? paths.codexOutbox : paths.claudeOutbox) || config.relay_endpoint !== paths.endpoint) throw new TypeError("Host producer outbox and IPC must belong to its configured source.");
3577
+ if (config.outbox_dir !== (producer === "codex" ? paths.codexOutbox : paths.claudeOutbox) || platform !== "win32" && config.relay_endpoint !== paths.endpoint) throw new TypeError("Host producer outbox and IPC must belong to its configured source.");
3578
+ return paths.endpoint;
3804
3579
  }
3805
3580
 
3806
3581
  // host/producer-service.ts
3807
- import { constants } from "node:fs";
3808
- import { lstat as lstat3, open as open5 } from "node:fs/promises";
3809
- var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3582
+ import { constants as constants3 } from "node:fs";
3583
+ import { lstat as lstat3 } from "node:fs/promises";
3584
+ var SAFE_REF6 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3810
3585
  var HOST_PRODUCER_CONFIG_FLAG = "--ceal-host-config";
3811
3586
  var HostProducerServiceError = class extends Error {
3812
3587
  code;
@@ -3817,18 +3592,19 @@ var HostProducerServiceError = class extends Error {
3817
3592
  }
3818
3593
  };
3819
3594
  var HOST_PRODUCER_CONFIG_KEYS = ["schema_version", "host_source_ref", "producer_installation_ref", "producer_epoch", "outbox_dir", "relay_endpoint"];
3595
+ var HOST_PRODUCER_CONFIG_ENDPOINT_KEY = "relay_endpoint";
3820
3596
  var HOST_PRODUCER_TIMING_DEFAULTS = Object.freeze({ reconcile_interval_ms: 250, retry_backoff_ms: 1e3, ipc_timeout_ms: 5e3 });
3821
3597
  function hostProducerExtraPath(extras, key) {
3822
3598
  const value = extras[key];
3823
- if (value === void 0) throw hostProducerError("invalid_config");
3599
+ if (value === void 0) throw hostProducerError("invalid_config", refusalFact(key, "missing", "an absolute path"));
3824
3600
  return value;
3825
3601
  }
3826
3602
  function parseHostProducerConfigFlag(args, platform = process.platform) {
3827
3603
  return parseProducerHostConfigFlag(args, HOST_PRODUCER_CONFIG_FLAG, platform, hostProducerError);
3828
3604
  }
3829
- async function readHostProducerConfig(descriptor, configPath, platform = process.platform) {
3830
- const config = decodeHostProducerConfigText(descriptor, await readProducerConfigText(configPath, platform, hostProducerError), platform);
3831
- const path = platform === "win32" ? win323 : posix3;
3605
+ async function readHostProducerConfig(descriptor, configPath, platform = process.platform, custody = {}) {
3606
+ const config = decodeHostProducerConfigText(descriptor, await readProducerConfigText(configPath, platform, hostProducerError, custody), platform);
3607
+ const path = platform === "win32" ? win324 : posix3;
3832
3608
  const root = path.resolve(config.outbox_dir, "../../../../..");
3833
3609
  try {
3834
3610
  assertHostProducerSourcePaths(root, config, descriptor.producer_id, platform);
@@ -3854,15 +3630,31 @@ function parseProducerHostConfigFlag(args, flag, platform, error) {
3854
3630
  assertProducerAbsolutePath(value, platform, "config", error);
3855
3631
  return value;
3856
3632
  }
3857
- async function readProducerConfigText(configPath, platform, error) {
3858
- assertProducerAbsolutePath(configPath, platform, "config", error);
3633
+ async function readProducerConfigText(configPath, platform, error, custody) {
3634
+ assertProducerAbsolutePath(configPath, process.platform, "config", error);
3635
+ if (platform === "win32") {
3636
+ let bytes;
3637
+ try {
3638
+ bytes = await readPrivateFileWithSecurefs(configPath, CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES, custody);
3639
+ } catch (cause) {
3640
+ if (cause instanceof SecureFileCustodyError && cause.reason === "not_private") throw error("config_not_private");
3641
+ if (cause instanceof SecureFileCustodyError && cause.reason === "too_large") throw error("invalid_config", refusalFact("config_bytes", "over_limit", `at most ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
3642
+ if (cause instanceof SecureFileCustodyError && cause.reason === "reparse_point") throw error("invalid_config");
3643
+ throw error("config_unavailable");
3644
+ }
3645
+ if (bytes.byteLength === 0) throw error("invalid_config", refusalFact("config_bytes", 0, `between 1 and ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
3646
+ try {
3647
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
3648
+ } catch {
3649
+ throw error("invalid_config");
3650
+ }
3651
+ }
3859
3652
  const pathStat = await configPathStat(configPath, error);
3860
3653
  validateConfigFile(pathStat, platform, error);
3861
- const noFollow = typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
3862
- const handle = await openConfigFile(configPath, constants.O_RDONLY | noFollow, error);
3654
+ const handle = await openConfigFile(configPath, platform, error, custody);
3863
3655
  try {
3864
3656
  const opened = await handle.stat();
3865
- validateConfigFile(opened, platform, error);
3657
+ validateConfigFile(opened, platform, error, handle);
3866
3658
  validateOpenedFile(opened, pathStat, platform, error);
3867
3659
  return await readBoundedConfig(handle, opened.size, error);
3868
3660
  } finally {
@@ -3870,8 +3662,9 @@ async function readProducerConfigText(configPath, platform, error) {
3870
3662
  }
3871
3663
  }
3872
3664
  function decodeProducerConfigCore(text, options) {
3665
+ const ownership = producerConfigKeyOwnership(options.keys, options.platform);
3873
3666
  const value = parseCanonicalJson(text, options.error);
3874
- if (!isRecord7(value) || !hasConfigKeys(value, options.keys) || value.schema_version !== options.schema) throw options.error("invalid_config");
3667
+ if (!isRecord8(value) || !hasConfigKeys(value, ownership.required, ownership.tolerated) || value.schema_version !== options.schema) throw options.error("invalid_config", refusalFacts(producerShapeFacts(value, { schema: options.schema, keys: ownership.required, tolerated: ownership.tolerated })));
3875
3668
  const hostSourceRef = typeof value.host_source_ref === "string" ? value.host_source_ref : "";
3876
3669
  const installationRef = typeof value.producer_installation_ref === "string" ? value.producer_installation_ref : "";
3877
3670
  const producerEpoch = validateIdentity3(hostSourceRef, installationRef, value.producer_epoch, options.error);
@@ -3889,14 +3682,21 @@ function decodeProducerConfigCore(text, options) {
3889
3682
  producer_installation_ref: installationRef,
3890
3683
  producer_epoch: producerEpoch,
3891
3684
  outbox_dir: value.outbox_dir,
3892
- relay_endpoint: value.relay_endpoint,
3685
+ relay_endpoint: producerConfigEndpoint(value),
3893
3686
  reconcile_interval_ms: intervals[0],
3894
3687
  retry_backoff_ms: intervals[1],
3895
3688
  ipc_timeout_ms: intervals[2]
3896
3689
  };
3897
3690
  }
3691
+ function producerConfigKeyOwnership(keys, platform) {
3692
+ if (platform !== "win32") return { required: keys, tolerated: [] };
3693
+ return { required: keys.filter((key) => key !== HOST_PRODUCER_CONFIG_ENDPOINT_KEY), tolerated: [HOST_PRODUCER_CONFIG_ENDPOINT_KEY] };
3694
+ }
3695
+ function producerConfigEndpoint(value) {
3696
+ return typeof value.relay_endpoint === "string" ? value.relay_endpoint : "";
3697
+ }
3898
3698
  function assertProducerRelayEndpoint(value, platform, error) {
3899
- if (platform === "win32" && typeof value === "string" && /^\\\\\.\\pipe\\[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(value)) return;
3699
+ if (platform === "win32") return;
3900
3700
  assertProducerAbsolutePath(value, platform, "runtime", error);
3901
3701
  }
3902
3702
  function assertProducerAbsolutePath(value, platform, kind, error) {
@@ -3909,21 +3709,22 @@ async function configPathStat(configPath, error) {
3909
3709
  throw error("config_unavailable");
3910
3710
  }
3911
3711
  }
3912
- function validateConfigFile(value, platform, error) {
3712
+ function validateConfigFile(value, platform, error, handle) {
3913
3713
  if (!value.isFile() || value.isSymbolicLink()) throw error("invalid_config", refusalFacts([{ name: "config_is_file", value: value.isFile(), expected: "true" }, { name: "config_is_symlink", value: value.isSymbolicLink(), expected: "false" }]));
3914
3714
  if (value.size <= 0 || value.size > CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES) throw error("invalid_config", refusalFact("config_bytes", value.size, `between 1 and ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
3915
- if (platform !== "win32" && (value.uid !== process.getuid?.() || (value.mode & 63) !== 0)) throw error("config_not_private", refusalFacts([{ name: "platform", value: platform, expected: "win32 or owner-private" }, { name: "config_uid", value: value.uid, expected: "the current user" }, { name: "config_mode", value: value.mode, expected: "owner-private" }]));
3715
+ if (!isSelfOwned(value.uid, platform, handle) || !isPrivateFileMode(value.mode, platform, handle)) throw error("config_not_private", refusalFacts([{ name: "platform", value: platform, expected: "a platform with verifiable owner and private-mode facts" }, { name: "config_uid", value: value.uid, expected: "the current user" }, { name: "config_mode", value: value.mode, expected: "private to its owner" }]));
3916
3716
  }
3917
- async function openConfigFile(configPath, flags, error) {
3717
+ async function openConfigFile(configPath, platform, error, custody) {
3918
3718
  try {
3919
- return await open5(configPath, flags);
3920
- } catch {
3719
+ return await openNoFollow(configPath, constants3.O_RDONLY, void 0, platform, custody);
3720
+ } catch (error_) {
3721
+ if (error_ instanceof SecureFileCustodyError) throw error(error_.reason === "not_private" ? "config_not_private" : "invalid_config");
3921
3722
  throw error("config_unavailable");
3922
3723
  }
3923
3724
  }
3924
3725
  function validateOpenedFile(opened, pathStat, platform, error) {
3925
3726
  if (!opened.isFile()) throw error("config_unavailable", refusalFacts([{ name: "opened_is_file", value: opened.isFile(), expected: "true" }, { name: "opened_mode", value: opened.mode.toString(8), expected: "a regular file mode" }]));
3926
- if (platform !== "win32" && (opened.dev !== pathStat.dev || opened.ino !== pathStat.ino)) throw error("config_unavailable", refusalFacts([{ name: "platform", value: platform, expected: "win32, or the same file the path named" }, { name: "opened_device", value: opened.dev, expected: String(pathStat.dev) }, { name: "opened_inode", value: opened.ino, expected: String(pathStat.ino) }]));
3727
+ if (opened.dev !== pathStat.dev || opened.ino !== pathStat.ino) throw error("config_unavailable", refusalFacts([{ name: "platform", value: platform, expected: "the same file the path named" }, { name: "opened_device", value: opened.dev, expected: String(pathStat.dev) }, { name: "opened_inode", value: opened.ino, expected: String(pathStat.ino) }]));
3927
3728
  }
3928
3729
  async function readBoundedConfig(handle, expectedBytes, error) {
3929
3730
  const buffer = Buffer.allocUnsafe(expectedBytes + 1);
@@ -3955,152 +3756,120 @@ function firstDifferenceIndex(left, right) {
3955
3756
  for (let index = 0; index < shared; index += 1) if (left[index] !== right[index]) return index;
3956
3757
  return shared;
3957
3758
  }
3759
+ function producerShapeFacts(value, options) {
3760
+ if (!isRecord8(value)) return [{ name: "config", value: "wrong_type", expected: "a record" }];
3761
+ const tolerated = options.tolerated ?? [];
3762
+ const allowed = [...options.keys, ...tolerated, ...Object.keys(HOST_PRODUCER_TIMING_DEFAULTS)];
3763
+ const facts = [
3764
+ ...Object.keys(value).filter((key) => !allowed.includes(key)).map((key) => ({ name: key, value: "unknown", expected: `one of ${[...options.keys, ...tolerated].join(", ")}` })),
3765
+ ...options.keys.filter((key) => !Object.hasOwn(value, key)).map((key) => ({ name: key, value: "missing", expected: "present" }))
3766
+ ];
3767
+ if (Object.hasOwn(value, "schema_version") && value.schema_version !== options.schema) facts.push({ name: "schema_version", value: "wrong_type", expected: options.schema });
3768
+ return facts;
3769
+ }
3958
3770
  function validateIdentity3(sourceRef, installationRef, epoch, error) {
3959
- if (!SAFE_REF7.test(sourceRef) || !SAFE_REF7.test(installationRef) || !positive2(epoch, 2147483647)) throw error("invalid_config");
3771
+ if (!SAFE_REF6.test(sourceRef) || !SAFE_REF6.test(installationRef) || !positive(epoch, 2147483647)) throw error("invalid_config", refusalFacts(producerIdentityFacts(sourceRef, installationRef, epoch)));
3960
3772
  return epoch;
3961
3773
  }
3774
+ function producerIdentityFacts(sourceRef, installationRef, epoch) {
3775
+ const facts = [];
3776
+ if (!SAFE_REF6.test(sourceRef)) facts.push({ name: "host_source_ref", value: "wrong_type", expected: "a safe reference" });
3777
+ if (!SAFE_REF6.test(installationRef)) facts.push({ name: "producer_installation_ref", value: "wrong_type", expected: "a safe reference" });
3778
+ if (!positive(epoch, 2147483647)) facts.push({ name: "producer_epoch", value: "wrong_type", expected: "a positive integer" });
3779
+ return facts;
3780
+ }
3962
3781
  function validateIntervals(reconcile, retry, timeout, error) {
3963
- if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config");
3782
+ if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config", refusalFacts(producerIntervalFacts(reconcile, retry, timeout)));
3964
3783
  return [reconcile, retry, timeout];
3965
3784
  }
3966
- function hasConfigKeys(value, required) {
3967
- return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || Object.hasOwn(HOST_PRODUCER_TIMING_DEFAULTS, key));
3785
+ function producerIntervalFacts(reconcile, retry, timeout) {
3786
+ const facts = [];
3787
+ if (!boundedMilliseconds(reconcile)) facts.push({ name: "reconcile_interval_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
3788
+ if (!boundedMilliseconds(retry)) facts.push({ name: "retry_backoff_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
3789
+ if (!boundedMilliseconds(timeout)) facts.push({ name: "ipc_timeout_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
3790
+ return facts;
3968
3791
  }
3969
- function isRecord7(value) {
3792
+ function hasConfigKeys(value, required, tolerated = []) {
3793
+ return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || tolerated.includes(key) || Object.hasOwn(HOST_PRODUCER_TIMING_DEFAULTS, key));
3794
+ }
3795
+ function isRecord8(value) {
3970
3796
  return value !== null && typeof value === "object" && !Array.isArray(value);
3971
3797
  }
3972
- function positive2(value, maximum) {
3798
+ function positive(value, maximum) {
3973
3799
  return Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= maximum;
3974
3800
  }
3975
3801
  function boundedMilliseconds(value) {
3976
- return positive2(value, 6e4) && Number(value) >= 25;
3802
+ return positive(value, 6e4) && Number(value) >= 25;
3803
+ }
3804
+
3805
+ // host/service-log.ts
3806
+ import { copyFile, mkdir as mkdir3, stat as stat2, truncate, appendFile } from "node:fs/promises";
3807
+ import { dirname as dirname3, isAbsolute as isAbsolute4, posix as posix4, win32 as win325 } from "node:path";
3808
+ var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
3809
+ function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
3810
+ 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 } : {} })}
3811
+ `;
3812
+ try {
3813
+ (sink ?? defaultHostServiceDiagnosticSink)(line);
3814
+ } catch {
3815
+ }
3816
+ }
3817
+ function defaultHostServiceDiagnosticSink(line) {
3818
+ writeHostServiceLogLine(line);
3819
+ process.stderr.write(line);
3820
+ }
3821
+ var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
3822
+ function writeHostServiceLogLine(line, environment = process.env) {
3823
+ if (process.platform !== "win32") return;
3824
+ const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
3825
+ if (path === void 0 || !win325.isAbsolute(path) || win325.normalize(path) !== path) return;
3826
+ void appendHostServiceLogLine(path, line).catch(() => void 0);
3827
+ }
3828
+ async function appendHostServiceLogLine(path, line) {
3829
+ if (!isAbsolute4(path)) return;
3830
+ await ensureHostServiceLogDirectory(path);
3831
+ await appendFile(path, line);
3832
+ }
3833
+ var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
3834
+ var hostServiceLogDirectories = /* @__PURE__ */ new Map();
3835
+ function ensureHostServiceLogDirectory(path) {
3836
+ const directory = dirname3(path);
3837
+ const existing = hostServiceLogDirectories.get(directory);
3838
+ if (existing !== void 0) return existing;
3839
+ const pending = mkdir3(directory, { recursive: true, mode: 448 }).then(() => void 0).catch((error) => {
3840
+ hostServiceLogDirectories.delete(directory);
3841
+ throw error;
3842
+ });
3843
+ hostServiceLogDirectories.set(directory, pending);
3844
+ return pending;
3977
3845
  }
3978
3846
 
3979
3847
  // host/codex/src/codex-producer.ts
3980
- import { createHash as createHash6 } from "node:crypto";
3981
- var SAFE_REF8 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3982
3848
  var CodexProducer = class {
3983
- producer_id = "codex";
3984
- producer_label = "Codex";
3985
- outbox_directory;
3986
3849
  store;
3987
3850
  core;
3988
3851
  constructor(options) {
3989
3852
  this.store = new CodexOutboxStore(options.outbox_dir);
3990
- this.outbox_directory = this.store.directory;
3991
3853
  this.core = new HostProducerDrainCore(this, options);
3992
3854
  }
3993
3855
  drainOutbox() {
3994
3856
  return this.core.drainOutbox();
3995
3857
  }
3996
- fail(code, message) {
3997
- return new CodexHostAdapterError(code, message);
3998
- }
3999
3858
  listEnvelopes() {
4000
3859
  return this.store.list();
4001
3860
  }
4002
3861
  removeEnvelope(envelope) {
4003
3862
  return this.store.remove(envelope);
4004
3863
  }
4005
- submissionRefOf(envelope) {
4006
- return envelope.submission_ref;
4007
- }
4008
3864
  expiresAtOf(envelope) {
4009
3865
  return envelope.expires_at;
4010
3866
  }
4011
- createdAtOf(envelope) {
4012
- return envelope.created_at;
4013
- }
4014
- async mapEnvelope(envelope) {
4015
- const prompt = observation(envelope, "prompt");
4016
- if (prompt !== void 0) return this.mapInput(envelope, prompt);
4017
- return [mapTerminal(envelope)];
4018
- }
4019
- async mapInput(envelope, prompt) {
4020
- const drafts = [];
4021
- const session = observation(envelope, "session");
4022
- if (session !== void 0) {
4023
- const registrations = await this.store.listTranscriptRegistrations();
4024
- const registration = registrations.find((item) => item.host_source_ref === envelope.identity.host_source_ref && item.host_session_ref === envelope.identity.host_session_ref);
4025
- const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
4026
- drafts.push({ frame_kind: "session_started", payload: compact({
4027
- local_session_ref: envelope.identity.host_session_ref,
4028
- working_directory_ref: `workspace:${createHash6("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
4029
- model: recordText(session.value, "model"),
4030
- host_version: nestedText(session.value, "host_version"),
4031
- observations: [session]
4032
- }) });
4033
- }
4034
- const turn = observation(envelope, "turn");
4035
- if (turn !== void 0) drafts.push({ frame_kind: "turn_started", payload: compact({
4036
- local_session_ref: envelope.identity.host_session_ref,
4037
- local_turn_ref: localTurnRef(envelope),
4038
- observations: [turn]
4039
- }) });
4040
- if (!isCealHostPromptObservationValue(prompt.value)) throw new CodexHostAdapterError("storage", "Codex prompt observation is malformed.");
4041
- drafts.push({ frame_kind: "user_input", payload: compact({
4042
- local_session_ref: envelope.identity.host_session_ref,
4043
- local_turn_ref: localTurnRef(envelope),
4044
- local_input_ref: envelope.host_input_ref,
4045
- input_kind: envelope.input_kind,
4046
- origin_input: envelope.origin_input,
4047
- observations: [prompt],
4048
- local_turn_aliases: envelope.codex_turn_id === void 0 ? void 0 : [envelope.codex_turn_id],
4049
- context_expires_at: envelope.expires_at
4050
- }) });
4051
- return drafts;
4052
- }
4053
3867
  };
4054
- function mapTerminal(envelope) {
4055
- const timing = observation(envelope, "timing");
4056
- const timingValue = asRecord(timing?.value);
4057
- const aborted = timingValue?.terminal === "aborted";
4058
- const usage = envelope.observations.filter((item) => ["token", "cost", "model"].includes(item.observation_kind));
4059
- return { frame_kind: "turn_terminal", payload: compact({
4060
- local_session_ref: envelope.identity.host_session_ref,
4061
- local_turn_ref: localTurnRef(envelope),
4062
- terminal_state: aborted ? "aborted" : "completed",
4063
- abort_reason: aborted && typeof timingValue?.reason === "string" ? safeReasonRef(timingValue.reason) : void 0,
4064
- duration_ms: nonNegativeNumber(timingValue?.duration_ms),
4065
- time_to_first_token_ms: nonNegativeNumber(timingValue?.time_to_first_token_ms),
4066
- usage: usage.length === 0 ? void 0 : Object.fromEntries(usage.map((item) => [item.observation_kind, item.value])),
4067
- observations: envelope.observations
4068
- }) };
4069
- }
4070
- function observation(envelope, kind) {
4071
- return envelope.observations.find((item) => item.observation_kind === kind);
4072
- }
4073
- function localTurnRef(envelope) {
4074
- return envelope.identity.host_turn_ref;
4075
- }
4076
- function compact(value) {
4077
- return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
4078
- }
4079
- function asRecord(value) {
4080
- return isRecord8(value) ? value : void 0;
4081
- }
4082
- function recordText(value, key) {
4083
- const record3 = asRecord(value);
4084
- return typeof record3?.[key] === "string" ? record3[key] : void 0;
4085
- }
4086
- function nestedText(value, key) {
4087
- const nested = asRecord(asRecord(value)?.[key]);
4088
- return typeof nested?.value === "string" ? nested.value : void 0;
4089
- }
4090
- function nonNegativeNumber(value) {
4091
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
4092
- }
4093
- function safeReasonRef(value) {
4094
- return SAFE_REF8.test(value) ? value : `reason:${createHash6("sha256").update(value, "utf8").digest("hex")}`;
4095
- }
4096
- function isRecord8(value) {
4097
- return value !== null && typeof value === "object" && !Array.isArray(value);
4098
- }
4099
3868
 
4100
3869
  // host/codex/src/codex-transcript-watcher.ts
4101
3870
  var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
4102
3871
  store;
4103
- registerCurrentPrompt;
3872
+ diagnostic;
4104
3873
  constructor(options) {
4105
3874
  super(options, {
4106
3875
  default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
@@ -4108,7 +3877,7 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
4108
3877
  clock_failure: () => new CodexHostAdapterError("storage", "Codex watcher clock is invalid.")
4109
3878
  });
4110
3879
  this.store = new CodexOutboxStore(options.outbox_dir);
4111
- this.registerCurrentPrompt = options.register_current_prompt;
3880
+ this.diagnostic = options.diagnostic ?? (() => void 0);
4112
3881
  }
4113
3882
  transcriptPathOf(registration) {
4114
3883
  return registration.transcript_path;
@@ -4124,7 +3893,8 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
4124
3893
  if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
4125
3894
  return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
4126
3895
  }
4127
- async scanFailed(handle, registration, _error) {
3896
+ async scanFailed(handle, registration, error) {
3897
+ this.diagnostic("watcher_scan_failed", codexTranscriptScanFailureKind(error));
4128
3898
  await this.quarantineFailure(registration, handle);
4129
3899
  return { records: 0, enqueued: 0, quarantined: true };
4130
3900
  }
@@ -4134,7 +3904,9 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
4134
3904
  async preflight(registration, handle, size, device, inode) {
4135
3905
  const existing = await this.store.readTranscriptCursor(registration);
4136
3906
  let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
4137
- if (cursor.status === "quarantined") return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
3907
+ if (cursor.status === "quarantined") {
3908
+ return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
3909
+ }
4138
3910
  const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
4139
3911
  if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
4140
3912
  await this.quarantine(registration, cursor, device, inode, reason);
@@ -4146,22 +3918,22 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
4146
3918
  let records = 0;
4147
3919
  let enqueued = 0;
4148
3920
  for (const line of complete.toString("utf8").split("\n").filter((value) => value.length > 0)) {
4149
- const record3 = decodeCodexTranscriptLine(line);
4150
- assertRecordSequence(registration, cursor, records, lastOrdinal, record3);
4151
- await this.observeSessionMetadata(registration, record3);
4152
- const outcome = await this.processRecord(registration, activeTask, record3);
3921
+ const record = decodeCodexTranscriptLine(line);
3922
+ assertRecordSequence(registration, cursor, records, lastOrdinal, record);
3923
+ await this.observeSessionMetadata(registration, record);
3924
+ const outcome = await this.processRecord(registration, activeTask, record);
4153
3925
  activeTask = outcome.activeTask;
4154
3926
  enqueued += outcome.enqueued;
4155
- lastOrdinal = record3.ordinal;
3927
+ lastOrdinal = record.ordinal;
4156
3928
  records += 1;
4157
3929
  }
4158
3930
  await this.commitCursor(registration, handle, cursor, complete.length, device, inode, lastOrdinal, activeTask);
4159
3931
  return { records, enqueued, quarantined: false };
4160
3932
  }
4161
- async observeSessionMetadata(registration, record3) {
4162
- if (record3.kind !== "session_meta") return;
3933
+ async observeSessionMetadata(registration, record) {
3934
+ if (record.kind !== "session_meta") return;
4163
3935
  const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
4164
- if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record3.cli_version } });
3936
+ if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record.cli_version } });
4165
3937
  }
4166
3938
  async commitCursor(registration, handle, cursor, consumedBytes, device, inode, lastOrdinal, activeTask) {
4167
3939
  const offset = cursor.offset + consumedBytes;
@@ -4173,75 +3945,132 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
4173
3945
  const stats = await handle.stat().catch(() => null);
4174
3946
  if (stats === null) return;
4175
3947
  const existing = await this.store.readTranscriptCursor(registration).catch(() => null);
4176
- await this.quarantine(registration, existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now), String(stats.dev), String(stats.ino), "transcript_malformed");
3948
+ const quarantined = {
3949
+ ...existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now),
3950
+ transcript_path: registration.transcript_path,
3951
+ device: String(stats.dev),
3952
+ inode: String(stats.ino),
3953
+ status: "quarantined",
3954
+ reason: "transcript_malformed",
3955
+ updated_at: this.stamp()
3956
+ };
3957
+ await this.store.writeTranscriptCursor(quarantined);
4177
3958
  }
4178
- async processRecord(registration, activeTask, record3) {
4179
- if (record3.kind === "task_started") {
4180
- 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 };
4181
- }
4182
- if (record3.kind === "turn_context") return { ...activeTask === void 0 ? {} : { activeTask: record3.turn_id === activeTask.codex_turn_id ? { ...activeTask, model: record3.model } : activeTask }, enqueued: 0 };
4183
- if (record3.kind === "token_count") return { ...activeTask === void 0 ? {} : { activeTask: accumulateTokenCount(activeTask, record3) }, enqueued: 0 };
4184
- if (record3.kind === "user_text") return this.processUserInput(registration, activeTask, record3);
4185
- if (record3.kind === "task_complete" || record3.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record3);
3959
+ async processRecord(registration, activeTask, record) {
3960
+ if (record.kind === "task_started") return this.processTaskStarted(registration, record);
3961
+ if (record.kind === "turn_context") return { ...activeTask === void 0 ? {} : { activeTask: record.turn_id === activeTask.codex_turn_id ? { ...activeTask, model: record.model } : activeTask }, enqueued: 0 };
3962
+ if (record.kind === "token_count") return { ...activeTask === void 0 ? {} : { activeTask: accumulateTokenCount(activeTask, record) }, enqueued: 0 };
3963
+ if (record.kind === "user_text") return this.processUserInput(registration, activeTask, record);
3964
+ if (record.kind === "task_complete" || record.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record);
4186
3965
  return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
4187
3966
  }
4188
- async processUserInput(registration, activeTask, record3) {
4189
- if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
4190
- const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id, record3.message_id, record3.ordinal, record3.prompt);
3967
+ async processTaskStarted(registration, record) {
3968
+ return { activeTask: { codex_turn_id: record.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record.turn_id), started_at: record.timestamp }, enqueued: 0 };
3969
+ }
3970
+ async processUserInput(registration, activeTask, record) {
3971
+ if (activeTask === void 0 || activeTask.codex_turn_id !== record.turn_id) {
3972
+ const adopted = await this.adoptHookCorrelatedTurn(registration, record);
3973
+ if (adopted === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
3974
+ activeTask = adopted;
3975
+ }
3976
+ const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record.turn_id, record.message_id, record.ordinal, record.prompt);
4191
3977
  const origin = activeTask.origin_host_input_ref === void 0;
4192
- const promptValue = promptObservationValue(record3.prompt);
3978
+ const promptValue = promptObservationValue(record.prompt);
4193
3979
  const currentPromptRef = hostProducerPromptRef(promptValue.body_sha256);
4194
3980
  const promptRef = activeTask.prompt_ref ?? currentPromptRef;
3981
+ const steeredPromptRef = await this.steeredPromptRef(registration, activeTask, origin, currentPromptRef);
4195
3982
  const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
4196
3983
  const observations = [{
4197
- observation_ref: observationRef("transcript_user_text", hostInputRef, record3.prompt),
3984
+ observation_ref: observationRef(registration.host_source_ref, "transcript_user_text", hostInputRef, record.prompt),
4198
3985
  observation_kind: "prompt",
4199
- observed_at: record3.timestamp,
3986
+ observed_at: record.timestamp,
4200
3987
  value: promptValue
4201
3988
  }];
4202
3989
  if (origin) {
4203
- observations.push({ observation_ref: observationRef("transcript_task_started", activeTask.host_turn_ref, record3.turn_id), observation_kind: "turn", observed_at: record3.timestamp, value: turnObservationValue(registration.codex_session_id, activeTask.host_turn_ref, "started") });
3990
+ observations.push({ observation_ref: observationRef(registration.host_source_ref, "transcript_task_started", activeTask.host_turn_ref, record.turn_id), observation_kind: "turn", observed_at: record.timestamp, value: turnObservationValue(registration.codex_session_id, activeTask.host_turn_ref, "started") });
4204
3991
  const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
4205
- if (session !== null) observations.push({ observation_ref: observationRef("transcript_session", registration.host_session_ref, session.value), observation_kind: "session", observed_at: record3.timestamp, value: session.value });
3992
+ if (session !== null) observations.push({ observation_ref: observationRef(registration.host_source_ref, "transcript_session", registration.host_session_ref, session.value), observation_kind: "session", observed_at: record.timestamp, value: session.value });
4206
3993
  }
4207
3994
  const envelope = {
4208
3995
  schema_version: "ceal.codex_observation_outbox.v1",
4209
3996
  identity,
4210
3997
  host_input_ref: hostInputRef,
4211
- input_kind: record3.input_kind,
4212
3998
  prompt_ref: promptRef,
4213
3999
  observations,
4214
- codex_turn_id: record3.turn_id,
4000
+ codex_turn_id: record.turn_id,
4215
4001
  origin_input: origin,
4216
- submission_ref: codexSubmissionReference(hostInputRef, "input", record3.ordinal),
4217
- created_at: record3.timestamp,
4002
+ submission_ref: codexSubmissionReference(hostInputRef, "input", record.ordinal),
4003
+ created_at: record.timestamp,
4218
4004
  expires_at: this.expire(this.outboxRetentionMs)
4219
4005
  };
4220
- await this.registerPrompt(origin, registration, activeTask, currentPromptRef, record3.prompt, envelope.expires_at);
4006
+ const persisted = await this.persistUserInputTurn(activeTask, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope);
4007
+ if (steeredPromptRef !== null) await this.activateTranscriptTurnContext(identity, steeredPromptRef, envelope.expires_at);
4008
+ return persisted;
4009
+ }
4010
+ /**
4011
+ * The steered prompt to re-activate under the running task, or null when
4012
+ * there is nothing to repair. A steered message joins the running turn
4013
+ * with new prompt bytes but no task_started. When the steer hook already
4014
+ * replaced the turn selection (a turn id the hook did not recognize as
4015
+ * the running one, or a hook that ran before any context was live), the
4016
+ * running task's context no longer matches and the next invocation would
4017
+ * wait for a boundary that never comes. When the hook kept the live
4018
+ * selection, the context still matches and this stays null.
4019
+ */
4020
+ async steeredPromptRef(registration, activeTask, origin, currentPromptRef) {
4021
+ if (origin || activeTask.prompt_ref === void 0 || activeTask.prompt_ref === currentPromptRef) return null;
4022
+ const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref).catch(() => null);
4023
+ return selection?.prompt_ref === currentPromptRef ? currentPromptRef : null;
4024
+ }
4025
+ /**
4026
+ * Adopt the live hooked turn when its transcript message arrives before
4027
+ * (or without) its task_started, as an interrupted turn's follow-up can.
4028
+ * The UserPromptSubmit hook names the exact hooked prompt, so only a
4029
+ * message carrying those bytes adopts; inherited history and other turns
4030
+ * stay ignored. A stale active task for another turn releases first, the
4031
+ * same as a task_started supersession.
4032
+ */
4033
+ async adoptHookCorrelatedTurn(registration, record) {
4034
+ const currentPromptRef = hostProducerPromptRef(promptObservationValue(record.prompt).body_sha256);
4035
+ const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref);
4036
+ if (selection?.prompt_ref !== currentPromptRef) return void 0;
4037
+ return {
4038
+ codex_turn_id: record.turn_id,
4039
+ host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record.turn_id),
4040
+ started_at: record.timestamp
4041
+ };
4042
+ }
4043
+ async persistUserInputTurn(activeTask, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope) {
4221
4044
  await this.store.put(envelope);
4222
- if (origin) await activateTranscriptTurnContext(this.store, identity, promptRef, envelope.expires_at);
4045
+ if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
4223
4046
  return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
4224
4047
  }
4225
- async registerPrompt(origin, registration, activeTask, promptRef, prompt, expiresAt) {
4226
- if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return;
4227
- 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 });
4048
+ async activateTranscriptTurnContext(identity, promptRef, expiresAt) {
4049
+ try {
4050
+ const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
4051
+ const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
4052
+ await this.store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
4053
+ this.diagnostic("activation_recorded");
4054
+ } catch (error) {
4055
+ this.diagnostic("activation_failed");
4056
+ throw error;
4057
+ }
4228
4058
  }
4229
- async processTerminal(registration, activeTask, record3) {
4230
- 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 };
4059
+ async processTerminal(registration, activeTask, record) {
4060
+ if (activeTask === void 0 || activeTask.codex_turn_id !== record.turn_id || activeTask.origin_host_input_ref === void 0 || activeTask.prompt_ref === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
4231
4061
  const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
4232
- const timing = record3.kind === "task_complete" ? { status: "available", unit: "milliseconds", started_at: record3.started_at, completed_at: record3.completed_at, duration_ms: record3.duration_ms, time_to_first_token_ms: record3.time_to_first_token_ms } : { status: "available", unit: "milliseconds", started_at: record3.started_at, completed_at: record3.completed_at, duration_ms: record3.duration_ms, terminal: "aborted", reason: record3.reason };
4233
- const observations = terminalObservations(registration, activeTask, record3, timing);
4062
+ const timing = record.kind === "task_complete" ? { status: "available", unit: "milliseconds", started_at: record.started_at, completed_at: record.completed_at, duration_ms: record.duration_ms, time_to_first_token_ms: record.time_to_first_token_ms } : { status: "available", unit: "milliseconds", started_at: record.started_at, completed_at: record.completed_at, duration_ms: record.duration_ms, terminal: "aborted", reason: record.reason };
4063
+ const observations = terminalObservations(registration, activeTask, record, timing);
4234
4064
  await this.store.put({
4235
4065
  schema_version: "ceal.codex_observation_outbox.v1",
4236
4066
  identity,
4237
4067
  host_input_ref: activeTask.origin_host_input_ref,
4238
- input_kind: "host_automatic",
4239
4068
  prompt_ref: activeTask.prompt_ref,
4240
4069
  observations,
4241
- codex_turn_id: record3.turn_id,
4070
+ codex_turn_id: record.turn_id,
4242
4071
  origin_input: false,
4243
- submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record3.ordinal),
4244
- created_at: record3.timestamp,
4072
+ submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record.ordinal),
4073
+ created_at: record.timestamp,
4245
4074
  expires_at: this.expire(this.outboxRetentionMs)
4246
4075
  });
4247
4076
  await this.store.clearInvocationContext(identity, this.now());
@@ -4252,15 +4081,18 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
4252
4081
  await this.store.writeTranscriptCursor(quarantined);
4253
4082
  }
4254
4083
  };
4255
- async function activateTranscriptTurnContext(store, identity, promptRef, expiresAt) {
4256
- const selection = await store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
4257
- const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
4258
- await store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
4259
- }
4260
- function accumulateTokenCount(activeTask, record3) {
4261
- if (record3.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record3.usage_gap_reason };
4262
- const last = record3.last_usage;
4263
- const total = record3.total_usage;
4084
+ function codexTranscriptScanFailureKind(error) {
4085
+ return transcriptScanFailureKind(error, (candidate) => {
4086
+ if (!(candidate instanceof CodexHostAdapterError)) return void 0;
4087
+ if (candidate.code === "payload_rejected") return "payload_rejected";
4088
+ if (candidate.code === "storage") return "read_error";
4089
+ return void 0;
4090
+ });
4091
+ }
4092
+ function accumulateTokenCount(activeTask, record) {
4093
+ if (record.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record.usage_gap_reason };
4094
+ const last = record.last_usage;
4095
+ const total = record.total_usage;
4264
4096
  if (last === void 0 || ignoreTokenCount(activeTask.last_total_usage, total, last)) return activeTask;
4265
4097
  const tokenUsage = addUsage(activeTask.token_usage, last);
4266
4098
  return {
@@ -4274,21 +4106,16 @@ function ignoreTokenCount(previous, total, last) {
4274
4106
  if (total === void 0 || previous === void 0) return false;
4275
4107
  return sameUsage(total, previous) || regressedUsage(total, previous) && looksLikeStaleRegression(total, previous, last);
4276
4108
  }
4277
- function terminalObservations(registration, activeTask, record3, timing) {
4109
+ function terminalObservations(registration, activeTask, record, timing) {
4278
4110
  const tokenValue = activeTask.token_usage_gap_reason !== void 0 ? notRecorded(activeTask.token_usage_gap_reason) : activeTask.token_usage === void 0 ? notRecorded("token_count_not_observed_for_active_turn") : tokenObservationValue(activeTask.token_usage);
4279
- const costValue = terminalCostValue();
4280
4111
  const observations = [
4281
- { observation_ref: observationRef("transcript_terminal", activeTask.host_turn_ref, record3), observation_kind: "turn", observed_at: record3.timestamp, value: turnObservationValue(registration.codex_session_id, activeTask.host_turn_ref, "stopped") },
4282
- { observation_ref: observationRef("transcript_timing", activeTask.host_turn_ref, timing), observation_kind: "timing", observed_at: record3.timestamp, value: timing },
4283
- { observation_ref: observationRef("transcript_token", activeTask.host_turn_ref, tokenValue), observation_kind: "token", observed_at: record3.timestamp, value: tokenValue },
4284
- { observation_ref: observationRef("transcript_cost", activeTask.host_turn_ref, costValue), observation_kind: "cost", observed_at: record3.timestamp, value: costValue }
4112
+ { observation_ref: observationRef(registration.host_source_ref, "transcript_terminal", activeTask.host_turn_ref, record), observation_kind: "turn", observed_at: record.timestamp, value: turnObservationValue(registration.codex_session_id, activeTask.host_turn_ref, "stopped") },
4113
+ { observation_ref: observationRef(registration.host_source_ref, "transcript_timing", activeTask.host_turn_ref, timing), observation_kind: "timing", observed_at: record.timestamp, value: timing },
4114
+ { observation_ref: observationRef(registration.host_source_ref, "transcript_token", activeTask.host_turn_ref, tokenValue), observation_kind: "token", observed_at: record.timestamp, value: tokenValue }
4285
4115
  ];
4286
- if (activeTask.model !== void 0) observations.push({ observation_ref: observationRef("transcript_model", activeTask.host_turn_ref, activeTask.model), observation_kind: "model", observed_at: record3.timestamp, value: { status: "available", value: activeTask.model, source: "turn_context.model" } });
4116
+ if (activeTask.model !== void 0) observations.push({ observation_ref: observationRef(registration.host_source_ref, "transcript_model", activeTask.host_turn_ref, activeTask.model), observation_kind: "model", observed_at: record.timestamp, value: { status: "available", value: activeTask.model, source: "turn_context.model" } });
4287
4117
  return observations;
4288
4118
  }
4289
- function terminalCostValue() {
4290
- return notRecorded("provider_cost_not_observed");
4291
- }
4292
4119
  function notRecorded(reason) {
4293
4120
  return hostObservationNotRecorded(reason);
4294
4121
  }
@@ -4335,16 +4162,21 @@ function looksLikeStaleRegression(current, previous, last) {
4335
4162
  if (previous.total_tokens <= 0 || current.total_tokens <= 0 || last.total_tokens <= 0) return false;
4336
4163
  return current.total_tokens * 100 >= previous.total_tokens * 98 || current.total_tokens + last.total_tokens * 2 >= previous.total_tokens;
4337
4164
  }
4338
- function assertRecordSequence(registration, cursor, records, lastOrdinal, record3) {
4339
- 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}`)}).`);
4340
- if (!cursor.header_validated && records === 0) assertCodexTranscriptHeader(registration, record3);
4165
+ function assertRecordSequence(registration, cursor, records, lastOrdinal, record) {
4166
+ if ((cursor.header_validated || records > 0) && record.ordinal <= lastOrdinal) throw new CodexHostAdapterError("payload_rejected", `Codex transcript ordinal is not monotonic (${refusalFact("ordinal", record.ordinal, `greater than ${lastOrdinal}`)}).`);
4167
+ if (!cursor.header_validated && records === 0) assertCodexTranscriptHeader(registration, record);
4341
4168
  }
4342
4169
 
4343
4170
  // host/codex/src/codex-producer-runtime.ts
4344
4171
  async function drainCodexProducerOnce(options) {
4345
- const promptPort = new HostRelayCurrentPromptPort(options.relay_endpoint, options.timeout_ms);
4346
- 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();
4347
- 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 } });
4172
+ const scan = await new CodexTranscriptWatcher({
4173
+ outbox_dir: options.outbox_dir,
4174
+ diagnostic: (event, failure_kind) => writeHostServiceDiagnostic("codex", event, failure_kind)
4175
+ }).scanOnce().catch((error) => {
4176
+ writeHostServiceDiagnostic("codex", "watcher_scan_failed", codexTranscriptScanFailureKind(error));
4177
+ throw error;
4178
+ });
4179
+ const producer = new CodexProducer({ outbox_dir: options.outbox_dir, ...options.now === void 0 ? {} : { now: options.now } });
4348
4180
  return { scanned: scan.enqueued, ...await producer.drainOutbox() };
4349
4181
  }
4350
4182
 
@@ -4357,11 +4189,7 @@ var CODEX_PRODUCER_DESCRIPTOR = {
4357
4189
  extra_paths: [CODEX_SESSIONS_ROOT],
4358
4190
  config: (shared, extras) => ({ ...shared, codex_sessions_root: hostProducerExtraPath(extras, CODEX_SESSIONS_ROOT) }),
4359
4191
  runtime_options: (config) => Object.freeze({
4360
- outbox_dir: config.outbox_dir,
4361
- producer_installation_ref: config.producer_installation_ref,
4362
- producer_epoch: config.producer_epoch,
4363
- relay_endpoint: config.relay_endpoint,
4364
- timeout_ms: config.ipc_timeout_ms
4192
+ outbox_dir: config.outbox_dir
4365
4193
  }),
4366
4194
  drain: drainCodexProducerOnce
4367
4195
  };
@@ -4408,18 +4236,13 @@ async function readStdin(stdin) {
4408
4236
  return Buffer.concat(chunks).toString("utf8");
4409
4237
  }
4410
4238
  async function runCodexHookCli() {
4411
- const unavailableGateway = {
4412
- submitHostTurn: async () => {
4413
- throw new Error("Codex Gateway adapter is not configured");
4414
- }
4415
- };
4416
4239
  const args = process.argv.slice(2);
4417
- const owner = await installedCodexHookRunnerOptions(args, unavailableGateway);
4240
+ const owner = await installedCodexHookRunnerOptions(args);
4418
4241
  return runCodexHookProcess(args, process.stdin, owner);
4419
4242
  }
4420
- async function installedCodexHookRunnerOptions(args, gateway) {
4243
+ async function installedCodexHookRunnerOptions(args) {
4421
4244
  const config = await readHostProducerConfig(CODEX_PRODUCER_DESCRIPTOR, parseHostProducerConfigFlag(args));
4422
- return { gateway, host_source_ref: config.host_source_ref, outbox_dir: config.outbox_dir, codex_sessions_root: config.codex_sessions_root };
4245
+ return { host_source_ref: config.host_source_ref, outbox_dir: config.outbox_dir, codex_sessions_root: config.codex_sessions_root };
4423
4246
  }
4424
4247
 
4425
4248
  // host/codex/bin/ceal-codex-hook.ts