@corca-org/ceal-host-win32-x64 1.0.20 → 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.
@@ -29,16 +29,34 @@ function isPlainRecord(value) {
29
29
  return prototype === Object.prototype || prototype === null;
30
30
  }
31
31
 
32
+ // packages/ceal-client-protocol/src/client-wire-contract.ts
33
+ import { createHash } from "node:crypto";
34
+
32
35
  // packages/ceal-client-protocol/client-wire-contract.json
33
36
  var client_wire_contract_default = {
34
37
  schema_version: "ceal.client_wire_contract.v1",
38
+ doctor: {
39
+ schema_version: "ceal.doctor.v1"
40
+ },
41
+ client_commands: {
42
+ doctor: ["doctor"],
43
+ version: ["version"],
44
+ access_group_list: ["access-group", "list"]
45
+ },
46
+ client_environment: {
47
+ environment_prefix: "CEAL",
48
+ context_override_suffixes: ["GATEWAY_ORIGIN", "GATEWAY_TRANSPORT_ORIGIN", "ACCESS_TOKEN", "CONTEXT_STORE_PATH"]
49
+ },
35
50
  canonical_json: {
36
51
  ordering: "utf16_code_unit_ascending"
37
52
  },
38
53
  artifact_descriptor: {
39
- operation_contract_bundle_schema_version: "ceal.operation_contract_bundle.v1",
40
- schema_version: "ceal.verified_artifact_descriptor.v1",
41
- binding_schema_version: "ceal.verified_artifact_binding.v1",
54
+ operation_contract_bundle_schema_name: "ceal.operation_contract_bundle",
55
+ operation_contract_bundle_fields: ["schema_version", "catalog_revision", "operations", "failure_guidance"],
56
+ schema_name: "ceal.verified_artifact_descriptor",
57
+ fields: ["schema_version", "kind", "media_type", "byte_count", "sha256", "binding"],
58
+ binding_schema_name: "ceal.verified_artifact_binding",
59
+ binding_fields: ["schema_version", "kind", "catalog_revision"],
42
60
  kind: "verified_artifact",
43
61
  binding_kind: "catalog",
44
62
  media_type: "application/json",
@@ -54,14 +72,16 @@ var client_wire_contract_default = {
54
72
  length: 72,
55
73
  pattern: "^catalog:[a-f0-9]{64}$"
56
74
  },
57
- envelope_max_bytes: 389
75
+ envelope_max_bytes: 407
58
76
  },
59
77
  command_request: {
60
- schema_version: "ceal.command_request.v1",
78
+ schema_name: "ceal.command_request",
79
+ fields: ["schema_version", "argv", "operation", "client_nonce", "context_token"],
61
80
  absolute_transport_max_bytes: 65536
62
81
  },
63
82
  command_resolution: {
64
- schema_version: "ceal.gateway_command_resolution.v1",
83
+ schema_name: "ceal.gateway_command_resolution",
84
+ 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"],
65
85
  authority: {
66
86
  anonymous: {
67
87
  state: "anonymous",
@@ -101,7 +121,8 @@ var client_wire_contract_default = {
101
121
  }
102
122
  },
103
123
  command_response: {
104
- schema_version: "ceal.command_response.v1",
124
+ schema_name: "ceal.command_response",
125
+ fields: ["schema_version", "exit_code", "document", "operation_id", "visible_output_max_bytes", "local_delivery", "verified_artifact"],
105
126
  descriptor_field: "verified_artifact",
106
127
  required_fields: [
107
128
  "schema_version",
@@ -125,29 +146,41 @@ var client_wire_contract_default = {
125
146
  "required_action_sequence",
126
147
  "withheld"
127
148
  ],
128
- prompt_field: "prompt",
129
- prompt_schema_version: "ceal.prompt.v1",
130
- focused_prompt_document_fields: [
131
- "catalog_revision",
132
- "prompt"
133
- ],
134
- focused_prompt_continuation_document_fields: [
135
- "catalog_revision",
136
- "remaining_work",
137
- "prompt"
138
- ],
139
149
  discovery_command: "discovery",
140
- discovery_manifest_schema_version: "ceal.discovery_manifest.v1",
150
+ discovery_manifest_schema_name: "ceal.discovery_manifest",
151
+ discovery_manifest_fields: ["schema_version", "skill", "authority_scope", "connector_kinds", "personal_connector_kinds", "guide_markdown", "renderer_version", "digest"],
141
152
  discovery_manifest_document_fields: [
142
153
  "discovery_manifest"
143
154
  ],
144
155
  exit_code_max: 125,
145
156
  bare_document_max_bytes: 65536,
146
- complete_max_bytes: 66019
157
+ complete_max_bytes: 66046
158
+ },
159
+ host_invocation_authentication: {
160
+ request_schema_name: "ceal.host_invocation_authentication_request",
161
+ request_fields: [
162
+ "schema_version",
163
+ "client_nonce"
164
+ ],
165
+ result_schema_name: "ceal.host_invocation_authentication_result",
166
+ result_fields: [
167
+ "schema_version",
168
+ "context_token",
169
+ "expires_at"
170
+ ],
171
+ client_nonce: {
172
+ character_class_source: "A-Za-z0-9_-",
173
+ encoded_characters: 43
174
+ }
175
+ },
176
+ gateway_host_invocation_context_request: {
177
+ schema_name: "ceal.gateway_host_invocation_context_request",
178
+ fields: ["schema_version", "client_nonce", "context_expires_at", "request_work_context", "current_prompt", "visible_output_max_bytes"]
147
179
  },
148
180
  resource_stream: {
149
181
  path: "/api/ceal/v2/resources",
150
- request_schema_version: "ceal.resource_stream_request.v1",
182
+ request_schema_name: "ceal.resource_stream_request",
183
+ request_fields: ["schema_version", "resource_ref", "client_nonce", "context_token"],
151
184
  ref_pattern: "^resource:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
152
185
  media_type_pattern: "^[a-z0-9][a-z0-9!#$&^_.+-]{0,63}/[a-z0-9][a-z0-9!#$&^_.+-]{0,63}$",
153
186
  filename_max_bytes: 512,
@@ -164,92 +197,6 @@ var client_wire_contract_default = {
164
197
  }
165
198
  };
166
199
 
167
- // packages/ceal-client-protocol/src/client-wire-contract.ts
168
- var CEAL_CLIENT_WIRE_CONTRACT = Object.freeze(client_wire_contract_default);
169
- var commandResolution = CEAL_CLIENT_WIRE_CONTRACT.command_resolution;
170
- var commandResolutionAuthority = commandResolution.authority;
171
- var commandResolutionSemanticOptions = commandResolution.semantic_options;
172
- var CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION = commandResolution.schema_version;
173
- var CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES = CEAL_CLIENT_WIRE_CONTRACT.encoded_json_document.max_bytes;
174
- var artifact = CEAL_CLIENT_WIRE_CONTRACT.artifact_descriptor;
175
- var request = CEAL_CLIENT_WIRE_CONTRACT.command_request;
176
- var response = CEAL_CLIENT_WIRE_CONTRACT.command_response;
177
- var CEAL_OPERATION_CONTRACT_BUNDLE_SCHEMA_VERSION = artifact.operation_contract_bundle_schema_version;
178
- var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_SCHEMA_VERSION = artifact.schema_version;
179
- var CEAL_VERIFIED_ARTIFACT_BINDING_SCHEMA_VERSION = artifact.binding_schema_version;
180
- var CEAL_VERIFIED_ARTIFACT_KIND = artifact.kind;
181
- var CEAL_VERIFIED_ARTIFACT_BINDING_KIND = artifact.binding_kind;
182
- var CEAL_VERIFIED_ARTIFACT_MEDIA_TYPE = artifact.media_type;
183
- var CEAL_VERIFIED_ARTIFACT_DIGEST_LENGTH = artifact.digest.length;
184
- var CEAL_VERIFIED_ARTIFACT_DIGEST_PATTERN = artifact.digest.pattern;
185
- var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_LENGTH = artifact.catalog_revision.length;
186
- var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_PATTERN = artifact.catalog_revision.pattern;
187
- var CEAL_VERIFIED_ARTIFACT_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
188
- var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_MAX_BYTES = artifact.envelope_max_bytes;
189
- var CEAL_COMMAND_REQUEST_SCHEMA_VERSION = request.schema_version;
190
- var CEAL_COMMAND_REQUEST_MAX_BYTES = request.absolute_transport_max_bytes;
191
- var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_MAX_BYTES = response.bare_document_max_bytes;
192
- var CEAL_COMMAND_RESPONSE_PROMPT_FIELD = response.prompt_field;
193
- var CEAL_COMMAND_RESPONSE_PROMPT_SCHEMA_VERSION = response.prompt_schema_version;
194
- var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_OPTIONAL_FIELDS = Object.freeze([...response.bare_document_optional_fields]);
195
- var CEAL_COMMAND_RESPONSE_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.bare_document_fields, response.prompt_field]);
196
- var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_document_fields]);
197
- var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_CONTINUATION_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_continuation_document_fields]);
198
- var CEAL_COMMAND_RESPONSE_DISCOVERY_COMMAND = response.discovery_command;
199
- var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_SCHEMA_VERSION = response.discovery_manifest_schema_version;
200
- var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_DOCUMENT_FIELDS = Object.freeze([...response.discovery_manifest_document_fields]);
201
- var CEAL_COMMAND_RESPONSE_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
202
-
203
- // packages/ceal-client-protocol/src/vocabulary.ts
204
- function vocabulary(words) {
205
- return Object.freeze(words);
206
- }
207
- function isVocabularyWord(words, value) {
208
- return words.some((word) => word === value);
209
- }
210
-
211
- // packages/ceal-client-protocol/src/gateway-response-types.ts
212
- var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
213
- var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
214
- var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
215
- var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
216
- 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"]);
217
- var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
218
- var CEAL_GATEWAY_DERIVED_STORE_FAILURE_CLASSES = vocabulary(["storage_unavailable", "decode_mismatch", "write_conflict", "quota", "store_error"]);
219
- var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
220
- var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
221
- var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
222
- var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "provider_authenticated_event"]);
223
- var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
224
- var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
225
- var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
226
- var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
227
- var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
228
- var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verified", "not_dispatched"]);
229
- var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
230
- var CEAL_PROTOCOL_VERSION = "1.4.0";
231
- var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
232
- var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
233
- var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
234
- "audited_call_pending",
235
- "continuation_read_pending",
236
- "continuation_write_pending",
237
- "provider_pacing_pending",
238
- "corpus_replay_pending",
239
- "corpus_append_pending",
240
- "corpus_fence_pending",
241
- "corpus_content_pending",
242
- "corpus_transaction_pending"
243
- ]);
244
- var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
245
- var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
246
- var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
247
- var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
248
- "connector_ref",
249
- "connector_revision",
250
- "policy_decision_revision"
251
- ]);
252
-
253
200
  // packages/ceal-client-protocol/src/operation-next-actions.ts
254
201
  var CEAL_REPEAT_REQUEST_NEXT_ACTION = "repeat_request";
255
202
  var CEAL_INSPECT_RECEIPT_NEXT_ACTION = "inspect_receipt";
@@ -316,7 +263,10 @@ var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
316
263
  "follow_required_action_sequence",
317
264
  // The command needs caller-owned local materialization (for example an
318
265
  // @path file) before the Gateway can receive the argument.
319
- "materialize_local_argument"
266
+ "materialize_local_argument",
267
+ // A peer's derived schema identifier changed with its field set; install or
268
+ // upgrade the CLI and Host Relay to the Gateway's release before retrying.
269
+ "upgrade_ceal_client"
320
270
  ];
321
271
  var CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS = Object.freeze(CALLER_RECOVERABLE_NEXT_ACTION_VALUES);
322
272
  var NON_REMEDY_NEXT_ACTION_VALUES = [
@@ -429,47 +379,56 @@ var CEAL_OPERATION_WITHHELD_NEXT_ACTIONS = Object.freeze({
429
379
  personal_authorization_required: "connect_personal_account"
430
380
  });
431
381
 
432
- // packages/ceal-client-protocol/src/operation-withheld-cause.ts
433
- var CEAL_OPERATION_WITHHELD_CAUSES = vocabulary(["provider_permission", "access_level", "personal_authorization_required"]);
382
+ // packages/ceal-client-protocol/src/vocabulary.ts
383
+ function vocabulary(words) {
384
+ return Object.freeze(words);
385
+ }
386
+ function isVocabularyWord(words, value) {
387
+ return words.some((word) => word === value);
388
+ }
434
389
 
435
- // packages/ceal-client-protocol/src/operation-contract-types.ts
436
- var CEAL_JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
437
- var CEAL_OPERATION_ERROR_SCHEMA_VERSION = "ceal.operation_error.v1";
438
- var CEAL_ARTIFACT_STAGE_TERMINALS = vocabulary(["chunk_accepted", "artifact_ready", "idempotency_replayed"]);
439
- var CEAL_RETRY_AFTER_MAX_MS = 60 * 60 * 1e3;
440
- var CEAL_JSON_SCHEMA_TYPES = vocabulary(["object", "array", "string", "integer", "number", "boolean", "null"]);
441
- var OBSERVATION_POSTURES = vocabulary(["direct_readback", "approved_no_readback", "operator_mediated", "excluded"]);
442
- var EFFECT_DOMAINS = vocabulary(["none", "gateway_state", "provider", "external_world"]);
443
- var SAFETY_CLASSES = vocabulary(["routine", "sensitive", "high_impact"]);
444
- var IDEMPOTENCY_CLASSES = vocabulary(["idempotent", "conditionally_idempotent", "non_idempotent"]);
445
- var REVERSIBILITY_CLASSES = vocabulary(["none", "reversible", "compensatable", "irreversible"]);
446
- var COMPENSATION_OWNERS = vocabulary(["none", "connector", "operator"]);
447
- var CANCELLATION_BOUNDARIES = vocabulary(["before_admission", "before_handoff", "after_handoff_reconcile"]);
448
- var HANDOFF_CLASSES = vocabulary(["not_applicable", "submission", "effect"]);
449
- var AUTHORITATIVE_READBACK_CLASSES = vocabulary(["required", "unavailable"]);
450
- var PROVIDER_READBACK_CLASSES = vocabulary(["required", "not_available", "not_applicable"]);
451
- var RECONCILIATION_CLASSES = vocabulary(["none", "automatic", "operator"]);
452
- var NO_READBACK_APPROVAL_CLASSES = vocabulary(["not_applicable", "explicit", "operator"]);
453
- var REPLAY_PREREQUISITES = vocabulary(["never", "fresh_admission", "reconciled_non_application", "operator_approval"]);
454
- var UNKNOWN_OUTCOMES = vocabulary(["reconcile", "operator_action", "excluded"]);
455
- var RECOVERY_OWNERS = vocabulary(["gateway", "connector", "operator"]);
456
- var CEAL_OPERATION_ADMISSION_STATES = vocabulary(["proposed", "refused", "admitted"]);
457
- var CEAL_OPERATION_EXECUTION_STATES = vocabulary(["not_started", "started", "completed"]);
458
- var CEAL_OPERATION_PROVIDER_HANDOFF_STATES = vocabulary(["not_offered", "offered", "accepted", "rejected", "unknown"]);
459
- var CEAL_OPERATION_EFFECT_STATES = vocabulary(["none", "applied", "failed", "unknown", "compensated"]);
460
- var CEAL_OPERATION_OBSERVATION_STATES = vocabulary(["not_started", "not_required", "pending", "observed", "unavailable"]);
461
- var CEAL_OPERATION_RECOVERY_KINDS = vocabulary(["none", "reconcile", "compensate", "operator_action"]);
462
- var CEAL_OPERATION_RECOVERY_STATUSES = vocabulary(["not_started", "active", "completed"]);
463
- var CEAL_OPERATION_PROVIDER_READBACK_AVAILABILITY = vocabulary(["not_started", "not_applicable", "not_available", "pending", "available"]);
464
- var CEAL_OPERATION_OFFERED_HANDOFF_STATES = vocabulary(["accepted", "rejected", "unknown"]);
465
- var CEAL_OPERATION_RECEIPT_AUTHORITY_KEYS = Object.freeze(["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]);
466
- var CEAL_OPERATION_APPROVAL_DISPOSITIONS = vocabulary([
467
- "approval_not_required_by_policy",
468
- "interactive_pending",
469
- "interactive_granted",
470
- "interactive_denied",
471
- "interactive_expired",
472
- "interactive_unavailable"
390
+ // packages/ceal-client-protocol/src/gateway-response-types.ts
391
+ var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
392
+ var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
393
+ var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
394
+ var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
395
+ 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"]);
396
+ var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
397
+ var CEAL_GATEWAY_DERIVED_STORE_FAILURE_CLASSES = vocabulary(["storage_unavailable", "decode_mismatch", "write_conflict", "quota", "store_error"]);
398
+ var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
399
+ var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
400
+ var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
401
+ var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "provider_authenticated_event"]);
402
+ var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
403
+ var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
404
+ var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
405
+ var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
406
+ var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
407
+ var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verified", "not_dispatched"]);
408
+ var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
409
+ var CEAL_PROTOCOL_VERSION = "1.4.0";
410
+ var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
411
+ var CEAL_GATEWAY_CALL_RESULT_SCHEMA_VERSION = "ceal.gateway_call_result.v1";
412
+ var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
413
+ var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
414
+ "audited_call_pending",
415
+ "continuation_read_pending",
416
+ "continuation_write_pending",
417
+ "provider_pacing_pending",
418
+ "corpus_replay_pending",
419
+ "corpus_append_pending",
420
+ "corpus_fence_pending",
421
+ "corpus_content_pending",
422
+ "corpus_transaction_pending"
423
+ ]);
424
+ var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
425
+ var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
426
+ var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
427
+ var CEAL_GATEWAY_AUDIT_CALL_DETAIL_SCHEMA_VERSION = "ceal.gateway_audit_call_detail.v1";
428
+ var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
429
+ "connector_ref",
430
+ "connector_revision",
431
+ "policy_decision_revision"
473
432
  ]);
474
433
 
475
434
  // packages/ceal-client-protocol/src/gateway-validation-primitives.ts
@@ -609,6 +568,11 @@ function isCealJsonRecord(value) {
609
568
  }
610
569
  var CEAL_VALIDATION_STATUSES = vocabulary(["not_applicable", "valid", "invalid"]);
611
570
  var CEAL_PARSE_STATUSES = vocabulary(["not_applicable", "parsed", "parse_failed"]);
571
+ function hasExactKeys(value, expected) {
572
+ const actual = Object.keys(value).sort();
573
+ const wanted = [...expected].sort();
574
+ return actual.length === wanted.length && actual.every((key, index) => key === wanted[index]);
575
+ }
612
576
  function invalidByContext(options) {
613
577
  if (options.forbidAuthorityKeys) invalidRequest();
614
578
  invalidResponse();
@@ -683,51 +647,6 @@ function cut(text) {
683
647
  return text.length <= REFUSAL_VALUE_MAX_CHARS ? text : `${text.slice(0, REFUSAL_VALUE_MAX_CHARS)}...(+${text.length - REFUSAL_VALUE_MAX_CHARS} chars)`;
684
648
  }
685
649
 
686
- // packages/ceal-client-protocol/src/operation-access-class.ts
687
- var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
688
-
689
- // packages/ceal-client-protocol/src/domain-vocabulary.ts
690
- var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
691
- var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
692
- var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
693
- var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
694
- var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
695
- var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
696
- var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
697
- var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
698
- var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
699
- var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
700
- var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
701
- var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
702
-
703
- // packages/ceal-client-protocol/src/safe-json-budget.ts
704
- var SAFE_JSON_MIN_BYTES_PER_NODE = 4;
705
- function safeJsonNodeBudgetForBytes(maxBytes) {
706
- if (!Number.isSafeInteger(maxBytes) || maxBytes < SAFE_JSON_MIN_BYTES_PER_NODE) {
707
- throw new RangeError(`safe-JSON byte cap is invalid: ${refusalFact("maxBytes", maxBytes, `at least ${SAFE_JSON_MIN_BYTES_PER_NODE} bytes`)}`);
708
- }
709
- return Math.floor(maxBytes / SAFE_JSON_MIN_BYTES_PER_NODE);
710
- }
711
-
712
- // packages/ceal-client-protocol/src/protocol-bounds.ts
713
- var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES = 64 * 1024;
714
- var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_NODES = safeJsonNodeBudgetForBytes(CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES);
715
- var OPERATION_SCHEMA_LITERAL_MAX_BYTES = CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES / 4;
716
- var CEAL_OPERATION_JSON_SCHEMA_BUDGET = Object.freeze({
717
- max_depth: 8,
718
- max_properties: 96,
719
- max_string_length: OPERATION_SCHEMA_LITERAL_MAX_BYTES / 4,
720
- max_alternatives: 8,
721
- max_enum_values: 64,
722
- max_literal_bytes: OPERATION_SCHEMA_LITERAL_MAX_BYTES,
723
- /** @basis our_policy -- bounds explanatory schema prose carried in one catalog. */
724
- max_description_characters: 1024,
725
- /** @basis our_policy -- bounds regular-expression compilation input from one schema. */
726
- max_pattern_characters: 256,
727
- /** @basis our_policy -- bounds traversal work inside one already byte-bounded schema literal. */
728
- max_literal_nodes: 512
729
- });
730
-
731
650
  // packages/ceal-client-protocol/src/operation-validation-primitives.ts
732
651
  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;
733
652
  var CEAL_CALENDAR_DATE_PATTERN_SOURCE = "^\\d{4}-\\d{2}-\\d{2}$";
@@ -795,38 +714,366 @@ function requireIsoTimestamp(value, message) {
795
714
  if (!isCealIsoTimestamp(value)) throw new TypeError(message);
796
715
  }
797
716
 
798
- // packages/ceal-client-protocol/src/operation-json-schema.ts
799
- var JSON_DESCRIPTION_CONTROL_CHARACTER = new RegExp("\\p{Cc}", "u");
800
-
801
- // packages/ceal-client-protocol/src/operation-contract-validation.ts
802
- var CEAL_OPERATION_AUTHORITY_POLICY_KEYS = Object.freeze(["access_group", "instance_binding", "target", "admission_recheck"]);
803
- var MAX_OPERATION_INPUT_RESOLVER_BINDINGS = 96;
804
- var CEAL_OPERATION_DESCRIPTOR_REQUIRED_ENVELOPE_KEYS = Object.freeze(["schema_version", "id", "input_contract", "result_contract", "policy"]);
805
- var CEAL_OPERATION_DESCRIPTOR_OPTIONAL_ENVELOPE_KEYS = Object.freeze(["input_resolution", "summary"]);
806
-
807
- // packages/ceal-client-protocol/src/operation-catalog.ts
808
- var CEAL_OPERATION_CATALOG_SCHEMA_VERSION = "ceal.catalog.v3";
809
- var CLIENT_AUDIENCES = Object.freeze(["ceal", "cealctl", "agent"]);
810
- var CLIENT_AUDIENCE_SET = new Set(CLIENT_AUDIENCES);
717
+ // packages/ceal-client-protocol/src/failure-guidance-table.ts
718
+ var CEAL_FAILURE_REDACTION_CLASSES = vocabulary(["model_safe", "withheld"]);
719
+ var CEAL_GATEWAY_STARTING_FAILURE_CODE = "gateway_starting";
720
+ var CEAL_OWNER_BOOTSTRAP_ABSENT_FAILURE_CODE = "bootstrap_absent";
721
+ var CEAL_OWNER_BOOTSTRAP_UNAVAILABLE_FAILURE_CODE = "bootstrap_unavailable";
722
+ var LOCAL_COMMAND_INVALID = "The local command or argument is invalid.";
723
+ var LOCAL_TRANSPORT_INCOMPLETE = "The local Ceal transport could not complete; report this as a gap.";
724
+ var GATEWAY_TRANSPORT_UNAVAILABLE = "The Gateway transport is unavailable; ask an administrator to restore Gateway availability.";
725
+ var HOST_AUTHENTICATION_UNAVAILABLE = "The Host could not authenticate this invocation; report this as a gap.";
726
+ var SESSION_CONTEXT_UNAVAILABLE = "The local Gateway session context is unavailable.";
727
+ function guidance(message, next_action, http_status, retryable = false, redaction_class = "model_safe") {
728
+ return Object.freeze({ message, next_action, http_status, retryable, redaction_class });
729
+ }
730
+ var CEAL_FAILURE_GUIDANCE_TABLE = Object.freeze({
731
+ invalid_command: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 400),
732
+ invalid_argument: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 400),
733
+ command_not_found: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 404),
734
+ 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),
735
+ local_route_unavailable: guidance("The requested local route is unavailable or its descriptor is invalid; report this as a gap.", "answer_with_gap", 404),
736
+ local_route_ambiguous: guidance("The requested local route is ambiguous; no local executable was selected. Report this as a gap.", "answer_with_gap", 400),
737
+ local_execution_unavailable: guidance("The selected local route executable is unavailable; report this as a gap.", "answer_with_gap", 500),
738
+ local_pickup_failed: guidance("The Host Request pickup failed; the Gateway command was not sent. Report this as a gap.", "answer_with_gap", 502),
739
+ invocation_authentication_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 401),
740
+ 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),
741
+ gateway_origin_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
742
+ access_token_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
743
+ context_pair_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
744
+ context_not_selected: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
745
+ context_store_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
746
+ context_store_conflict: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
747
+ refresh_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
748
+ refresh_expired: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
749
+ refresh_replayed: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
750
+ refresh_revoked: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
751
+ refresh_recovery_unavailable: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
752
+ refresh_custody_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
753
+ 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),
754
+ local_output_exists: guidance("The selected local output path already exists; nothing was written to it.", "choose_new_output_path", 400),
755
+ 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),
756
+ 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),
757
+ 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),
758
+ 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),
759
+ 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),
760
+ response_encoding_failed: guidance("The local response could not be encoded; report this as a gap.", "answer_with_gap", 500),
761
+ request_timeout: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 504),
762
+ request_failed: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 502),
763
+ gateway_unavailable: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 503, true),
764
+ 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),
765
+ // `gateway_starting` is the one retryable envelope the serving gate answers on every route while starting
766
+ // (`cli/internal/client/gateway_startup_wait.go` re-drives it); the sentence is the client default that renders
767
+ // when the wait ends still unserved, moved from the `localFailureText` default branch.
768
+ [CEAL_GATEWAY_STARTING_FAILURE_CODE]: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503, true),
769
+ gateway_internal_error: guidance("The Gateway failed without exposing internal state; report this as a gap.", "answer_with_gap", 500),
770
+ // The serving gate emits this code with no message; the sentence is the owner-normalized form of the
771
+ // `gateway_internal_error` guidance (decided with the operator 2026-10-13).
772
+ internal_error: guidance("The Gateway failed without exposing internal state.", "answer_with_gap", 500),
773
+ // Owned by the Gateway canonical adapter (`gateway/core/src/gateway-canonical-operation-http-adapter.ts`):
774
+ // the client variant of this sentence in `localFailureText` was retired when the client started reading this table.
775
+ request_body_too_large: guidance("The Gateway request body exceeds its bounded limit.", "reduce_request_size", 413),
776
+ // Restored verbatim from the retired `localFailureText` `unknown_outcome`
777
+ // case: the outcome is unknown, so a read may be retried while a write
778
+ // must receipt-check first. Collapsing both to "do not repeat the call"
779
+ // forbids the safe read retry the sentence exists to allow.
780
+ 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),
781
+ // Restored verbatim from the retired `localFailureText`
782
+ // `refresh_unknown_outcome` case: a refresh that ends unknown leaves the
783
+ // session context unavailable, so the caller repairs or adopts the
784
+ // session instead of reporting a transport gap.
785
+ refresh_unknown_outcome: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
786
+ request_cancelled: guidance("The caller cancelled the request before it completed.", "retry", 400, true),
787
+ 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),
788
+ denied: guidance("An administrator denied this device's activation request; ask an administrator whether to reconsider it.", "ask_admin", 403),
789
+ 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),
790
+ 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),
791
+ 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),
792
+ recovery_required: guidance("The Gateway could not safely complete the activation transaction automatically; ask an administrator to recover this transaction.", "ask_admin", 500),
793
+ 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),
794
+ // Activation commit codes rendered through the `localFailureText` default before phase 2b; `retryable` is moved from the
795
+ // emitting sites in `cli/internal/client/gateway_installation_activation.go` and `activation.go:483`.
796
+ activation_state_commit_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500, true),
797
+ activation_response_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502, true),
798
+ activation_rejected: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502),
799
+ activation_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
800
+ activation_binding: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
801
+ activation_crypto: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
802
+ activation_state_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
803
+ // Long-tail local codes rendered through the `localFailureText` default before phase 2b; sentences moved from that branch.
804
+ invalid_response: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
805
+ invalid_configuration: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
806
+ activation_state_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
807
+ activation_code_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
808
+ activation_code_file_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
809
+ activation_code_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 404),
810
+ bootstrap_file_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
811
+ bootstrap_not_active: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 409),
812
+ bootstrap_unauthorized: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
813
+ // The Gateway owner-bootstrap store file was never created, so the Gateway
814
+ // holds no owner bootstrap to exchange (`gateway/src/host/owner-bootstrap-http-composition.ts`
815
+ // `absent`, stored at `gateway/src/shared/installation/owner-bootstrap-store.ts:162`);
816
+ // the sentence is the owner-normalized form of that store error. Non-retryable:
817
+ // repeating the exchange cannot create the store; only an administrator
818
+ // placing a bootstrap (fresh gateway setup) changes the outcome.
819
+ [CEAL_OWNER_BOOTSTRAP_ABSENT_FAILURE_CODE]: guidance("The Gateway holds no owner bootstrap to exchange; report this as a gap.", "answer_with_gap", 503),
820
+ // The Gateway owner-bootstrap store file exists but cannot be read, so the
821
+ // exchange cannot proceed (`gateway/src/host/owner-bootstrap-http-composition.ts`
822
+ // `unavailable`, stored at `gateway/src/shared/installation/owner-bootstrap-store.ts:163`);
823
+ // the sentence is the owner-normalized form of that store error. Retryable:
824
+ // unlike `absent`, a later exchange may succeed once the store is readable.
825
+ [CEAL_OWNER_BOOTSTRAP_UNAVAILABLE_FAILURE_CODE]: guidance("The Gateway owner bootstrap store is unavailable; retry the exchange.", "retry", 503, true),
826
+ context_selection_ambiguous: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
827
+ session_authority_unverified: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
828
+ session_authority_mismatch: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 409),
829
+ local_credential_lost: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
830
+ local_capability_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
831
+ host_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503),
832
+ host_activation_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
833
+ doctor_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503),
834
+ admin_sign_in_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502),
835
+ // `invalid_request` renders through the client default today (local encode/path faults); Gateway-answered
836
+ // envelopes carry the Gateway message verbatim and never read this sentence.
837
+ invalid_request: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
838
+ // Gateway canonical-route codes, sentences moved from their minting adapters; statuses moved from the
839
+ // `valid*Failure` decoder maps in `cli/internal/client/gateway_api.go` and `transport.go`.
840
+ invalid_json: guidance("The Gateway request body must be valid JSON.", "answer_with_gap", 400),
841
+ // Per-route variants name their route; the table holds the one stable guidance and the route's `allow`
842
+ // header stays the measured fact (decided with the operator 2026-10-13).
843
+ method_not_allowed: guidance("The HTTP method is not allowed for this Gateway route.", "answer_with_gap", 405),
844
+ request_target_too_large: guidance("The Gateway request target exceeds its bounded limit.", "reduce_request_size", 414),
845
+ route_not_found: guidance("The Gateway route is not present.", "answer_with_gap", 404),
846
+ artifact_unavailable: guidance("The Gateway artifact is unavailable.", "answer_with_gap", 503),
847
+ discovery_unavailable: guidance("The Gateway discovery manifest is unavailable.", "answer_with_gap", 503),
848
+ serialization_failed: guidance("The Gateway response could not be serialized.", "answer_with_gap", 500),
849
+ request_header_too_large: guidance("A consumed Gateway request header exceeds its bounded limit.", "answer_with_gap", 431),
850
+ request_work_context_missing: guidance("The Gateway command requires a Request work context.", "answer_with_gap", 400),
851
+ request_work_context_invalid: guidance("The Gateway Request work context encoding is invalid.", "answer_with_gap", 400),
852
+ request_work_context_refused: guidance("The Host-only discovery manifest takes no Request work context.", "answer_with_gap", 400),
853
+ // Moved from `gateway/src/operations/http-composition.ts` (`unauthorized`) and `command-runtime.ts:827`
854
+ // (`artifact_stale`); both travel with Gateway messages verbatim online, so these sentences are the
855
+ // owner fallback guidance. `unauthorized` re-authenticates through the session, hence its action.
856
+ unauthorized: guidance("Unauthorized.", "repair_or_adopt_session", 401),
857
+ artifact_stale: guidance("The requested contract artifact is not the current catalog artifact.", "answer_with_gap", 409),
858
+ // Host mint classifications. The CLI emits `invocation_authentication_unavailable` with the classification
859
+ // in its detail fact; rows without their own sentence share that code's moved sentence, while `next_action`
860
+ // is moved from `canonicalHostAuthenticationNextActions` (generated from `CEAL_HOST_AUTHENTICATION_POLICY`)
861
+ // and `retryable` from `hostAuthenticationFailureForCause` (`failure.nextAction == "retry"`). The three rows
862
+ // with their own sentences carry the retired policy remedies as Gateway-owned guidance. Statuses are the
863
+ // Gateway-owned mapping decided with the operator 2026-10-13. This covers every wire cause the maps at
864
+ // `host/invocation-authentication-command.ts:217-242` emit, through their policy classifications.
865
+ host_arguments_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 400),
866
+ host_configuration_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500),
867
+ host_session_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 401),
868
+ // Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
869
+ // missing exact-turn fact names the fresh Host turn, not a reinstall.
870
+ 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),
871
+ host_invocation_context_stale: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 401, true),
872
+ host_invocation_context_expired: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 401, true),
873
+ host_source_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 403),
874
+ host_registration_credential_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "ask_admin", 403),
875
+ host_mint_gateway_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "ask_admin", 503),
876
+ host_mint_gateway_rate_limited: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 429),
877
+ host_mint_gateway_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
878
+ host_mint_gateway_response_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
879
+ // Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
880
+ // producer-frame fault names the administrator repair step.
881
+ host_producer_frame_invalid: guidance("Ask an administrator to inspect and repair the producer frame, then start a fresh Host turn.", "ask_admin", 400),
882
+ // Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
883
+ // sandboxed Relay socket names the run-alone retry.
884
+ 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),
885
+ host_relay_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 503),
886
+ host_mint_transient_retryable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 503, true),
887
+ host_mint_failed: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500),
888
+ host_relay_failed: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
889
+ host_refusal_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500)
890
+ });
891
+ function cealFailureGuidanceForCode(code2) {
892
+ const entry = CEAL_FAILURE_GUIDANCE_TABLE[code2];
893
+ if (entry === void 0) throw new TypeError(`Failure guidance code is not guided: ${code2}.`);
894
+ return entry;
895
+ }
896
+ var FAILURE_GUIDANCE_ENTRY_KEYS = Object.freeze(["message", "next_action", "http_status", "retryable", "redaction_class"]);
811
897
 
812
- // packages/ceal-client-protocol/src/schema-literals.ts
813
- var CEAL_SCHEMA_LITERALS = [
814
- "ceal.activation_transport.v1",
815
- CEAL_OPERATION_ACCESS_SCHEMA_VERSION,
816
- CEAL_OPERATION_CATALOG_SCHEMA_VERSION,
817
- "ceal.client_refresh_request.v1",
818
- "ceal.client_refresh_result.v1",
819
- "ceal.client_revoke_request.v1",
820
- "ceal.client_revoke_result.v1",
821
- CEAL_COMMAND_REQUEST_SCHEMA_VERSION,
822
- "ceal.device_enrollment_challenge.v1",
823
- "ceal.device_enrollment_challenge_request.v1",
824
- "ceal.device_enrollment_hpke_aad.v1",
825
- "ceal.device_enrollment_hpke_info.v1",
826
- "ceal.device_enrollment_poll.v1",
827
- "ceal.device_enrollment_poll_result.v1",
828
- "ceal.device_enrollment_proof.v1",
829
- "ceal.device_enrollment_start.v1",
898
+ // packages/ceal-client-protocol/src/client-wire-contract.ts
899
+ var CEAL_CLIENT_WIRE_CONTRACT = Object.freeze(client_wire_contract_default);
900
+ var CEAL_CLIENT_DOCTOR_SCHEMA_VERSION = CEAL_CLIENT_WIRE_CONTRACT.doctor.schema_version;
901
+ var clientCommands = CEAL_CLIENT_WIRE_CONTRACT.client_commands;
902
+ var CEAL_CLIENT_DOCTOR_COMMAND = Object.freeze([...clientCommands.doctor]);
903
+ var CEAL_CLIENT_VERSION_COMMAND = Object.freeze([...clientCommands.version]);
904
+ var CEAL_CLIENT_ACCESS_GROUP_LIST_COMMAND = Object.freeze([...clientCommands.access_group_list]);
905
+ function deriveCealClientWireSchemaVersion(schema) {
906
+ 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))) {
907
+ throw new TypeError("Client wire schema name or field set is invalid.");
908
+ }
909
+ const fields = schema.fields.filter((field) => field !== "schema_version").sort();
910
+ const hash8 = createHash("sha256").update(cealCanonicalJson(fields), "utf8").digest("hex").slice(0, 8);
911
+ return `${schema.schema_name}.v1.${hash8}`;
912
+ }
913
+ var CealClientWireContractMismatchError = class extends Error {
914
+ code = "contract_mismatch";
915
+ next_action = cealFailureGuidanceForCode("contract_mismatch").next_action;
916
+ constructor(expected, actual) {
917
+ super(`The peer wire contract ${actual} does not match this client contract ${expected}.`);
918
+ this.name = "CealClientWireContractMismatchError";
919
+ }
920
+ };
921
+ function requireCealClientWireSchemaVersion(value, expected) {
922
+ if (typeof value === "string" && value !== expected) throw new CealClientWireContractMismatchError(expected, value);
923
+ }
924
+ var commandResolution = CEAL_CLIENT_WIRE_CONTRACT.command_resolution;
925
+ var commandResolutionAuthority = commandResolution.authority;
926
+ var commandResolutionSemanticOptions = commandResolution.semantic_options;
927
+ var CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(commandResolution);
928
+ var CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES = CEAL_CLIENT_WIRE_CONTRACT.encoded_json_document.max_bytes;
929
+ var artifact = CEAL_CLIENT_WIRE_CONTRACT.artifact_descriptor;
930
+ var request = CEAL_CLIENT_WIRE_CONTRACT.command_request;
931
+ var response = CEAL_CLIENT_WIRE_CONTRACT.command_response;
932
+ var CEAL_OPERATION_CONTRACT_BUNDLE_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: artifact.operation_contract_bundle_schema_name, fields: artifact.operation_contract_bundle_fields });
933
+ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(artifact);
934
+ var CEAL_VERIFIED_ARTIFACT_BINDING_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: artifact.binding_schema_name, fields: artifact.binding_fields });
935
+ var CEAL_VERIFIED_ARTIFACT_KIND = artifact.kind;
936
+ var CEAL_VERIFIED_ARTIFACT_BINDING_KIND = artifact.binding_kind;
937
+ var CEAL_VERIFIED_ARTIFACT_MEDIA_TYPE = artifact.media_type;
938
+ var CEAL_VERIFIED_ARTIFACT_DIGEST_LENGTH = artifact.digest.length;
939
+ var CEAL_VERIFIED_ARTIFACT_DIGEST_PATTERN = artifact.digest.pattern;
940
+ var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_LENGTH = artifact.catalog_revision.length;
941
+ var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_PATTERN = artifact.catalog_revision.pattern;
942
+ var CEAL_VERIFIED_ARTIFACT_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
943
+ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_MAX_BYTES = artifact.envelope_max_bytes;
944
+ var CEAL_COMMAND_REQUEST_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(request);
945
+ var CEAL_COMMAND_REQUEST_MAX_BYTES = request.absolute_transport_max_bytes;
946
+ var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_MAX_BYTES = response.bare_document_max_bytes;
947
+ var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_OPTIONAL_FIELDS = Object.freeze([...response.bare_document_optional_fields]);
948
+ var CEAL_COMMAND_RESPONSE_DISCOVERY_COMMAND = response.discovery_command;
949
+ var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: response.discovery_manifest_schema_name, fields: response.discovery_manifest_fields });
950
+ var CEAL_COMMAND_RESPONSE_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(response);
951
+ var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_DOCUMENT_FIELDS = Object.freeze([...response.discovery_manifest_document_fields]);
952
+ var CEAL_COMMAND_RESPONSE_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
953
+ 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 });
954
+ var CEAL_RESOURCE_STREAM_REQUEST_FIELDS = Object.freeze([...CEAL_CLIENT_WIRE_CONTRACT.resource_stream.request_fields]);
955
+
956
+ // packages/ceal-client-protocol/src/operation-withheld-cause.ts
957
+ var CEAL_OPERATION_WITHHELD_CAUSES = vocabulary(["provider_permission", "access_level", "personal_authorization_required"]);
958
+
959
+ // packages/ceal-client-protocol/src/operation-contract-types.ts
960
+ var CEAL_JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
961
+ var CEAL_OPERATION_DESCRIPTOR_SCHEMA_VERSION = "ceal.operation_descriptor.v1";
962
+ var CEAL_OPERATION_ERROR_SCHEMA_VERSION = "ceal.operation_error.v1";
963
+ var CEAL_ARTIFACT_STAGE_TERMINALS = vocabulary(["chunk_accepted", "artifact_ready", "idempotency_replayed"]);
964
+ var CEAL_RETRY_AFTER_MAX_MS = 60 * 60 * 1e3;
965
+ var CEAL_JSON_SCHEMA_TYPES = vocabulary(["object", "array", "string", "integer", "number", "boolean", "null"]);
966
+ var OBSERVATION_POSTURES = vocabulary(["direct_readback", "approved_no_readback", "operator_mediated", "excluded"]);
967
+ var EFFECT_DOMAINS = vocabulary(["none", "gateway_state", "provider", "external_world"]);
968
+ var SAFETY_CLASSES = vocabulary(["routine", "sensitive", "high_impact"]);
969
+ var IDEMPOTENCY_CLASSES = vocabulary(["idempotent", "conditionally_idempotent", "non_idempotent"]);
970
+ var REVERSIBILITY_CLASSES = vocabulary(["none", "reversible", "compensatable", "irreversible"]);
971
+ var COMPENSATION_OWNERS = vocabulary(["none", "connector", "operator"]);
972
+ var CANCELLATION_BOUNDARIES = vocabulary(["before_admission", "before_handoff", "after_handoff_reconcile"]);
973
+ var HANDOFF_CLASSES = vocabulary(["not_applicable", "submission", "effect"]);
974
+ var AUTHORITATIVE_READBACK_CLASSES = vocabulary(["required", "unavailable"]);
975
+ var PROVIDER_READBACK_CLASSES = vocabulary(["required", "not_available", "not_applicable"]);
976
+ var RECONCILIATION_CLASSES = vocabulary(["none", "automatic", "operator"]);
977
+ var NO_READBACK_APPROVAL_CLASSES = vocabulary(["not_applicable", "explicit", "operator"]);
978
+ var REPLAY_PREREQUISITES = vocabulary(["never", "fresh_admission", "reconciled_non_application", "operator_approval"]);
979
+ var UNKNOWN_OUTCOMES = vocabulary(["reconcile", "operator_action", "excluded"]);
980
+ var RECOVERY_OWNERS = vocabulary(["gateway", "connector", "operator"]);
981
+ var CEAL_OPERATION_ADMISSION_STATES = vocabulary(["proposed", "refused", "admitted"]);
982
+ var CEAL_OPERATION_EXECUTION_STATES = vocabulary(["not_started", "started", "completed"]);
983
+ var CEAL_OPERATION_PROVIDER_HANDOFF_STATES = vocabulary(["not_offered", "offered", "accepted", "rejected", "unknown"]);
984
+ var CEAL_OPERATION_EFFECT_STATES = vocabulary(["none", "applied", "failed", "unknown", "compensated"]);
985
+ var CEAL_OPERATION_OBSERVATION_STATES = vocabulary(["not_started", "not_required", "pending", "observed", "unavailable"]);
986
+ var CEAL_OPERATION_RECOVERY_KINDS = vocabulary(["none", "reconcile", "compensate", "operator_action"]);
987
+ var CEAL_OPERATION_RECOVERY_STATUSES = vocabulary(["not_started", "active", "completed"]);
988
+ var CEAL_OPERATION_PROVIDER_READBACK_AVAILABILITY = vocabulary(["not_started", "not_applicable", "not_available", "pending", "available"]);
989
+ var CEAL_OPERATION_OFFERED_HANDOFF_STATES = vocabulary(["accepted", "rejected", "unknown"]);
990
+ var CEAL_OPERATION_RECEIPT_AUTHORITY_KEYS = Object.freeze(["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]);
991
+ var CEAL_OPERATION_APPROVAL_DISPOSITIONS = vocabulary([
992
+ "approval_not_required_by_policy",
993
+ "interactive_pending",
994
+ "interactive_granted",
995
+ "interactive_denied",
996
+ "interactive_expired",
997
+ "interactive_unavailable"
998
+ ]);
999
+
1000
+ // packages/ceal-client-protocol/src/operation-access-class.ts
1001
+ var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
1002
+
1003
+ // packages/ceal-client-protocol/src/domain-vocabulary.ts
1004
+ var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
1005
+ var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
1006
+ var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
1007
+ var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
1008
+ var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
1009
+ var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
1010
+ var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
1011
+ var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
1012
+ var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
1013
+ var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
1014
+ var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
1015
+ var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
1016
+
1017
+ // packages/ceal-client-protocol/src/safe-json-budget.ts
1018
+ var SAFE_JSON_MIN_BYTES_PER_NODE = 4;
1019
+ function safeJsonNodeBudgetForBytes(maxBytes) {
1020
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < SAFE_JSON_MIN_BYTES_PER_NODE) {
1021
+ throw new RangeError(`safe-JSON byte cap is invalid: ${refusalFact("maxBytes", maxBytes, `at least ${SAFE_JSON_MIN_BYTES_PER_NODE} bytes`)}`);
1022
+ }
1023
+ return Math.floor(maxBytes / SAFE_JSON_MIN_BYTES_PER_NODE);
1024
+ }
1025
+
1026
+ // packages/ceal-client-protocol/src/protocol-bounds.ts
1027
+ var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES = 64 * 1024;
1028
+ var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_NODES = safeJsonNodeBudgetForBytes(CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES);
1029
+ var OPERATION_SCHEMA_LITERAL_MAX_BYTES = CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES / 4;
1030
+ var CEAL_OPERATION_JSON_SCHEMA_BUDGET = Object.freeze({
1031
+ max_depth: 8,
1032
+ max_properties: 96,
1033
+ max_string_length: OPERATION_SCHEMA_LITERAL_MAX_BYTES / 4,
1034
+ max_alternatives: 8,
1035
+ max_enum_values: 64,
1036
+ max_literal_bytes: OPERATION_SCHEMA_LITERAL_MAX_BYTES,
1037
+ /** @basis our_policy -- bounds explanatory schema prose carried in one catalog. */
1038
+ max_description_characters: 1024,
1039
+ /** @basis our_policy -- bounds regular-expression compilation input from one schema. */
1040
+ max_pattern_characters: 256,
1041
+ /** @basis our_policy -- bounds traversal work inside one already byte-bounded schema literal. */
1042
+ max_literal_nodes: 512
1043
+ });
1044
+
1045
+ // packages/ceal-client-protocol/src/operation-json-schema.ts
1046
+ var JSON_DESCRIPTION_CONTROL_CHARACTER = new RegExp("\\p{Cc}", "u");
1047
+
1048
+ // packages/ceal-client-protocol/src/operation-contract-validation.ts
1049
+ var CEAL_OPERATION_AUTHORITY_POLICY_KEYS = Object.freeze(["access_group", "instance_binding", "target", "admission_recheck"]);
1050
+ var MAX_OPERATION_INPUT_RESOLVER_BINDINGS = 96;
1051
+ var CEAL_OPERATION_DESCRIPTOR_REQUIRED_ENVELOPE_KEYS = Object.freeze(["schema_version", "id", "input_contract", "result_contract", "policy"]);
1052
+ var CEAL_OPERATION_DESCRIPTOR_OPTIONAL_ENVELOPE_KEYS = Object.freeze(["input_resolution", "summary"]);
1053
+
1054
+ // packages/ceal-client-protocol/src/operation-catalog.ts
1055
+ var CEAL_OPERATION_CATALOG_SCHEMA_VERSION = "ceal.catalog.v3";
1056
+ var CLIENT_AUDIENCES = Object.freeze(["ceal", "cealctl", "agent"]);
1057
+ var CLIENT_AUDIENCE_SET = new Set(CLIENT_AUDIENCES);
1058
+
1059
+ // packages/ceal-client-protocol/src/schema-literals.ts
1060
+ var CEAL_SCHEMA_LITERALS = [
1061
+ "ceal.activation_transport.v1",
1062
+ CEAL_OPERATION_ACCESS_SCHEMA_VERSION,
1063
+ CEAL_OPERATION_CATALOG_SCHEMA_VERSION,
1064
+ "ceal.client_refresh_request.v1",
1065
+ "ceal.client_refresh_result.v1",
1066
+ "ceal.client_revoke_request.v1",
1067
+ "ceal.client_revoke_result.v1",
1068
+ CEAL_COMMAND_REQUEST_SCHEMA_VERSION,
1069
+ "ceal.device_enrollment_challenge.v1",
1070
+ "ceal.device_enrollment_challenge_request.v1",
1071
+ "ceal.device_enrollment_hpke_aad.v1",
1072
+ "ceal.device_enrollment_hpke_info.v1",
1073
+ "ceal.device_enrollment_poll.v1",
1074
+ "ceal.device_enrollment_poll_result.v1",
1075
+ "ceal.device_enrollment_proof.v1",
1076
+ "ceal.device_enrollment_start.v1",
830
1077
  "ceal.device_enrollment_start_result.v1",
831
1078
  "ceal.effect_approval.v1",
832
1079
  "ceal.effect_approval_confirmation.v1",
@@ -836,12 +1083,12 @@ var CEAL_SCHEMA_LITERALS = [
836
1083
  "ceal.enrollment_result.v1",
837
1084
  "ceal.first_activation_causal_join.v1",
838
1085
  "ceal.gateway_announcement_policy.v1",
839
- "ceal.gateway_audit_call_detail.v1",
1086
+ CEAL_GATEWAY_AUDIT_CALL_DETAIL_SCHEMA_VERSION,
840
1087
  "ceal.gateway_audit_event.v1",
841
1088
  "ceal.gateway_audit_readback.v1",
842
1089
  "ceal.gateway_authorization_snapshot.v1",
843
1090
  "ceal.gateway_cache_origin.v1",
844
- "ceal.gateway_call_result.v1",
1091
+ CEAL_GATEWAY_CALL_RESULT_SCHEMA_VERSION,
845
1092
  CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION,
846
1093
  "ceal.gateway_connector_route_failure.v1",
847
1094
  "ceal.gateway_discovery.v1",
@@ -866,7 +1113,7 @@ var CEAL_SCHEMA_LITERALS = [
866
1113
  "ceal.host_observation.v1",
867
1114
  "ceal.host_registration_request.v1",
868
1115
  "ceal.host_registration_result.v1",
869
- "ceal.operation_descriptor.v1",
1116
+ CEAL_OPERATION_DESCRIPTOR_SCHEMA_VERSION,
870
1117
  "ceal.operation_error.v1",
871
1118
  "ceal.operation_failure.v1",
872
1119
  "ceal.operation_failure.compact.v1",
@@ -974,10 +1221,20 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
974
1221
  var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "block", "object"]);
975
1222
  var CEAL_OPAQUE_REF_KINDS = vocabulary([...CEAL_OPAQUE_HANDLE_KINDS, "result", "operation-receipt", "reply-receipt", "field", "notion-data-source"]);
976
1223
 
1224
+ // packages/ceal-client-protocol/src/client-environment-generated.ts
1225
+ var CEAL_CLIENT_CONTEXT_OVERRIDE_ENVIRONMENTS = Object.freeze(["CEAL_GATEWAY_ORIGIN", "CEAL_GATEWAY_TRANSPORT_ORIGIN", "CEAL_ACCESS_TOKEN", "CEAL_CONTEXT_STORE_PATH"]);
1226
+
1227
+ // packages/ceal-client-protocol/src/client-process-contract.ts
1228
+ var CEAL_CLIENT_VERSION_SCHEMA_SUFFIX = ".version.v1";
1229
+ var CEAL_CLIENT_VERSION_SCHEMA_VERSION = `ceal${CEAL_CLIENT_VERSION_SCHEMA_SUFFIX}`;
1230
+
977
1231
  // packages/ceal-client-protocol/src/bare-document.ts
978
1232
  var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_fields;
979
1233
  var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
980
1234
 
1235
+ // packages/ceal-client-protocol/src/actor-kind.ts
1236
+ var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
1237
+
981
1238
  // packages/ceal-client-protocol/src/resource-row.ts
982
1239
  var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
983
1240
  var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
@@ -992,6 +1249,8 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
992
1249
  "resource_created",
993
1250
  "review_submitted",
994
1251
  "comment_created",
1252
+ "reaction_created",
1253
+ "ceal_write_completed",
995
1254
  "event_start",
996
1255
  "all_day_start",
997
1256
  "file_created",
@@ -1026,6 +1285,10 @@ var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
1026
1285
  "github.review.submitted",
1027
1286
  "github.issue_comment.created",
1028
1287
  "github.review_comment.created",
1288
+ "github.reaction.created",
1289
+ "github.reaction.deleted",
1290
+ "github.pull_request.review_requested",
1291
+ "github.pull_request.review_request_removed",
1029
1292
  "calendar.organized",
1030
1293
  "calendar.accepted_invite",
1031
1294
  "calendar.recorded",
@@ -1047,6 +1310,10 @@ var CEAL_ACTIVITY_EVENT_TIMESTAMP_BASIS_MAP = Object.freeze({
1047
1310
  "github.pull_request.opened": "resource_created",
1048
1311
  "github.issue.edited": "resource_modified",
1049
1312
  "github.pull_request.edited": "resource_modified",
1313
+ "github.reaction.created": "reaction_created",
1314
+ "github.reaction.deleted": "ceal_write_completed",
1315
+ "github.pull_request.review_requested": "ceal_write_completed",
1316
+ "github.pull_request.review_request_removed": "ceal_write_completed",
1050
1317
  "calendar.organized": "resource_created",
1051
1318
  "drive.file.created": "file_created",
1052
1319
  "drive.file.modified": "file_modified_by_me",
@@ -1070,6 +1337,7 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1070
1337
  source_url: { required: false, timestamp: false },
1071
1338
  source: { required: true, timestamp: false },
1072
1339
  owner: { required: false, timestamp: false },
1340
+ actor_kind: { required: false, timestamp: false },
1073
1341
  counterparts: { required: false, timestamp: false },
1074
1342
  attachments: { required: false, timestamp: false },
1075
1343
  created_at: { required: false, timestamp: true },
@@ -1077,9 +1345,18 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1077
1345
  modified_at: { required: false, timestamp: true },
1078
1346
  modified_at_local: { required: false, timestamp: true },
1079
1347
  viewed_by_me_at: { required: false, timestamp: true },
1348
+ modified_by_me_at: { required: false, timestamp: true },
1080
1349
  own_ceal_write: { required: false, timestamp: false },
1081
1350
  owned_by_me: { required: false, timestamp: false },
1351
+ mime_type: { required: false, timestamp: false },
1352
+ size_bytes: { required: false, timestamp: false },
1353
+ version: { required: false, timestamp: false },
1354
+ trashed: { required: false, timestamp: false },
1082
1355
  visibility: { required: false, timestamp: false },
1356
+ archived: { required: false, timestamp: false },
1357
+ tag: { required: false, timestamp: false },
1358
+ labels: { required: false, timestamp: false },
1359
+ labels_incomplete: { required: false, timestamp: false },
1083
1360
  start: { required: false, timestamp: false },
1084
1361
  end: { required: false, timestamp: false },
1085
1362
  status: { required: false, timestamp: false },
@@ -1152,18 +1429,29 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
1152
1429
  // The serving gate answers this while the Gateway is still starting and the
1153
1430
  // refresh was never attempted; the same attempt stays retryable. It is never
1154
1431
  // stored by the session owner, only answered at the serving edge.
1155
- "gateway_starting"
1432
+ CEAL_GATEWAY_STARTING_FAILURE_CODE
1433
+ ]);
1434
+ var CEAL_CLIENT_REVOKE_FAILURE_CODES = vocabulary([
1435
+ "refresh_invalid",
1436
+ "refresh_expired",
1437
+ "refresh_replayed",
1438
+ "refresh_revoked"
1156
1439
  ]);
1440
+ var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX = "ceal_personal_";
1441
+ var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX = "ceal_refresh_";
1442
+ var PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN = "[A-Za-z0-9_-]{43}";
1443
+ var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
1444
+ var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
1157
1445
 
1158
1446
  // packages/ceal-client-protocol/src/request-validation-primitives.ts
1159
- import { createHash } from "node:crypto";
1447
+ import { createHash as createHash2 } from "node:crypto";
1160
1448
  var DIGEST = /^[a-f0-9]{64}$/u;
1161
1449
  var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
1162
1450
  function isCealDigest(value) {
1163
1451
  return typeof value === "string" && DIGEST.test(value);
1164
1452
  }
1165
1453
  function cealRequestSha256(value) {
1166
- return createHash("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
1454
+ return createHash2("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
1167
1455
  }
1168
1456
  function operationRecord(value, message) {
1169
1457
  requireOperationRecord(value, message);
@@ -1179,40 +1467,71 @@ function requirePrefixedRef(value, prefix, message) {
1179
1467
  // packages/ceal-client-protocol/src/request-authority.ts
1180
1468
  var CEAL_REQUEST_SCHEMA_VERSION = "ceal.request.v1";
1181
1469
  var CEAL_REQUEST_ARTIFACT_OWNERS = vocabulary(["gateway", "host", "connector"]);
1182
- var CEAL_REQUEST_HOST_BINDING_KEYS = vocabulary(["prompt_ref", "host_session_ref", "host_turn_ref", "host_source_ref"]);
1470
+ var CEAL_HOST_INVOCATION_IDENTITY_KEYS = vocabulary(["host_session_ref", "host_turn_ref", "host_source_ref"]);
1183
1471
  var CEAL_REQUEST_ARTIFACT_RETENTION_CLASSES = vocabulary(["request", "source", "result"]);
1184
1472
  var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
1185
- 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"]);
1473
+ var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority"]);
1474
+ var CEAL_REQUEST_COMMON_PROPERTIES = Object.freeze({
1475
+ schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
1476
+ ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
1477
+ created_at: { type: "string", minLength: 1, maxLength: 64 },
1478
+ authority: {
1479
+ type: "object",
1480
+ maxProperties: 5,
1481
+ additionalProperties: false,
1482
+ properties: {
1483
+ instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1484
+ access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1485
+ principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1486
+ execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1487
+ authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1488
+ },
1489
+ required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
1490
+ }
1491
+ });
1492
+ var CEAL_REQUEST_HOST_PROPERTIES = Object.freeze({
1493
+ prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1494
+ host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1495
+ host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1496
+ host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1497
+ host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1498
+ });
1499
+ var CEAL_REQUEST_CALLER_PROPERTIES = Object.freeze({
1500
+ 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." },
1501
+ 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." }
1502
+ });
1503
+ var CEAL_REQUEST_OPTIONAL_PROPERTIES = Object.freeze({
1504
+ requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1505
+ 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." }
1506
+ });
1507
+ var CEAL_REQUEST_HOST_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, ...CEAL_HOST_INVOCATION_IDENTITY_KEYS, "prompt_ref"]);
1508
+ var CEAL_REQUEST_CALLER_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, "caller_call_group_ref", "caller_call_input_sha256"]);
1186
1509
  var CEAL_REQUEST_JSON_SCHEMA = {
1187
1510
  type: "object",
1188
- maxProperties: 11,
1511
+ 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.",
1512
+ maxProperties: 13,
1189
1513
  additionalProperties: false,
1190
1514
  properties: {
1191
- schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
1192
- ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
1193
- created_at: { type: "string", minLength: 1, maxLength: 64 },
1194
- authority: {
1515
+ ...CEAL_REQUEST_COMMON_PROPERTIES,
1516
+ ...CEAL_REQUEST_HOST_PROPERTIES,
1517
+ ...CEAL_REQUEST_CALLER_PROPERTIES,
1518
+ ...CEAL_REQUEST_OPTIONAL_PROPERTIES
1519
+ },
1520
+ required: CEAL_REQUEST_KEYS,
1521
+ anyOf: [
1522
+ {
1195
1523
  type: "object",
1196
- maxProperties: 5,
1197
- additionalProperties: false,
1198
- properties: {
1199
- instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1200
- access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1201
- principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1202
- execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1203
- authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1204
- },
1205
- required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
1524
+ required: CEAL_REQUEST_HOST_REQUIRED_KEYS,
1525
+ properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_HOST_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
1526
+ additionalProperties: false
1206
1527
  },
1207
- prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1208
- host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1209
- host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1210
- host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1211
- host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1212
- requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1213
- 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." }
1214
- },
1215
- required: CEAL_REQUEST_KEYS
1528
+ {
1529
+ type: "object",
1530
+ required: CEAL_REQUEST_CALLER_REQUIRED_KEYS,
1531
+ properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_CALLER_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
1532
+ additionalProperties: false
1533
+ }
1534
+ ]
1216
1535
  };
1217
1536
 
1218
1537
  // packages/ceal-client-protocol/src/request-operands.ts
@@ -1323,10 +1642,6 @@ var CEAL_REQUEST_RUN_BATCH_JSON_SCHEMA = {
1323
1642
  };
1324
1643
 
1325
1644
  // packages/ceal-client-protocol/src/host-observation.ts
1326
- var CEAL_HOST_INPUT_KINDS = vocabulary(["human", "host_automatic"]);
1327
- function isCealHostInputKind(value) {
1328
- return typeof value === "string" && CEAL_HOST_INPUT_KINDS.some((kind) => kind === value);
1329
- }
1330
1645
  var CEAL_HOST_OBSERVATION_REF_PREFIX = "host-observation:";
1331
1646
  var HOST_OBSERVATION_PRODUCER = /^[a-z][a-z0-9]{0,31}$/u;
1332
1647
  var HOST_OBSERVATION_DIGEST = /^[a-f0-9]{8,64}$/u;
@@ -1379,7 +1694,7 @@ function validateCealHostObservationDraft(value) {
1379
1694
  assertHostObservationValue(record5.value, record5.observation_kind);
1380
1695
  }
1381
1696
  var CEAL_REQUEST_MAX_OBSERVATION_VALUE_BYTES = 64 * 1024;
1382
- var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "cost", "timing"]);
1697
+ var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "timing"]);
1383
1698
  function isCealHostPromptObservationValue(value) {
1384
1699
  return isOperationRecord(value) && Object.keys(value).length === 1 && isCealDigest(value.body_sha256);
1385
1700
  }
@@ -1502,17 +1817,128 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
1502
1817
  };
1503
1818
 
1504
1819
  // packages/ceal-client-protocol/src/host-invocation-authentication.ts
1505
- var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA = "ceal.host_invocation_authentication_request.v1";
1506
- var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = "ceal.host_invocation_authentication_result.v1";
1507
- var CEAL_HOST_NONCE_ENCODED_CHARACTERS = 43;
1508
- var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(`^[A-Za-z0-9_-]{${CEAL_HOST_NONCE_ENCODED_CHARACTERS}}$`, "u");
1509
- var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(`^ceal_context_[A-Za-z0-9_-]{${CEAL_HOST_NONCE_ENCODED_CHARACTERS}}$`, "u");
1510
- var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = new TextEncoder().encode(`${JSON.stringify({
1511
- schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA,
1512
- context_token: `ceal_context_${"A".repeat(CEAL_HOST_NONCE_ENCODED_CHARACTERS)}`,
1513
- expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
1514
- })}
1820
+ var hostInvocationAuthenticationContract = CEAL_CLIENT_WIRE_CONTRACT.host_invocation_authentication;
1821
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.request_schema_name, fields: hostInvocationAuthenticationContract.request_fields });
1822
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.request_fields]);
1823
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.result_schema_name, fields: hostInvocationAuthenticationContract.result_fields });
1824
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.result_fields]);
1825
+ var CEAL_HOST_AUTHENTICATION_BUDGET_ENVIRONMENT = "CEAL_HOST_AUTHENTICATION_BUDGET_MS";
1826
+ var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE = "^(?:0|[1-9][0-9]{0,8})$";
1827
+ var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN = new RegExp(CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE, "u");
1828
+ var CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA = "ceal.gateway_host_invocation_context_request.v2";
1829
+ var CEAL_HOST_INVOCATION_CONTEXT_REQUEST_MAX_BYTES = 16 * 1024;
1830
+ var CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES = 2 * 1024;
1831
+ var CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES = 32;
1832
+ var HOST_CONTEXT_EXPIRY_MAX_CHARS = 64;
1833
+ var CEAL_HOST_INVOCATION_CONTEXT_VISIBLE_OUTPUT_MAX_BYTES = CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
1834
+ var CEAL_HOST_CONTEXT_TOKEN_PREFIX = "ceal_context_";
1835
+ function boundCealHostInvocationHumanMessageWindow(newestFirst, sourceTruncated = false) {
1836
+ const messages = [];
1837
+ let truncated = sourceTruncated || newestFirst.length > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES;
1838
+ for (const candidate of newestFirst.slice(0, CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES)) {
1839
+ const next = [...messages, candidate];
1840
+ if (new TextEncoder().encode(JSON.stringify(next)).byteLength > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES) {
1841
+ truncated = true;
1842
+ break;
1843
+ }
1844
+ messages.push(Object.freeze({ host_turn_ref: candidate.host_turn_ref, host_input_ref: candidate.host_input_ref, text: candidate.text }));
1845
+ }
1846
+ return Object.freeze({ messages: Object.freeze(messages), truncated });
1847
+ }
1848
+ function decodeCealHostInvocationContextRequest(value) {
1849
+ if (!isCealJsonRecord(value)) throw invalidInvocationContextRequest();
1850
+ requireCealClientWireSchemaVersion(value.schema_version, CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA);
1851
+ if (!hasInvocationContextRequestKeys(value)) throw invalidInvocationContextRequest();
1852
+ const clientNonce = requireClientNonce(value.client_nonce);
1853
+ const expiresAt = requireContextExpiration(value.context_expires_at);
1854
+ const hostBinding = requireHostBinding(value.host_binding);
1855
+ const messageWindow = requireHumanMessageWindow(value.human_message_window);
1856
+ const lastRequestRef = optionalSafeReference(value.last_seen_request_ref);
1857
+ const visibleOutputMaxBytes = optionalVisibleOutputBudget(value.visible_output_max_bytes);
1858
+ return Object.freeze({
1859
+ schema_version: CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA,
1860
+ client_nonce: clientNonce,
1861
+ context_expires_at: expiresAt,
1862
+ host_binding: Object.freeze({ ...hostBinding }),
1863
+ human_message_window: messageWindow,
1864
+ ...lastRequestRef === void 0 ? {} : { last_seen_request_ref: lastRequestRef },
1865
+ ...visibleOutputMaxBytes === void 0 ? {} : { visible_output_max_bytes: visibleOutputMaxBytes }
1866
+ });
1867
+ }
1868
+ function hasInvocationContextRequestKeys(value) {
1869
+ const required = ["schema_version", "client_nonce", "context_expires_at", "host_binding", "human_message_window"];
1870
+ const optional = ["last_seen_request_ref", "visible_output_max_bytes"];
1871
+ const optionalKeys = Object.keys(value).filter((key) => optional.includes(key));
1872
+ return hasExactKeys(value, [...required, ...optionalKeys]) && required.every((key) => Object.hasOwn(value, key)) && value.schema_version === CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA;
1873
+ }
1874
+ function requireClientNonce(value) {
1875
+ if (typeof value !== "string" || !CEAL_HOST_CLIENT_NONCE_PATTERN.test(value)) throw invalidInvocationContextRequest();
1876
+ return value;
1877
+ }
1878
+ function requireContextExpiration(value) {
1879
+ if (typeof value !== "string" || value.length > HOST_CONTEXT_EXPIRY_MAX_CHARS || !Number.isFinite(Date.parse(value))) throw invalidInvocationContextRequest();
1880
+ return value;
1881
+ }
1882
+ function requireHostBinding(value) {
1883
+ if (!isCealJsonRecord(value) || !exactOptionalKeys(value, ["host_source_ref", "host_session_ref", "host_turn_ref"], []) || !safeReference(value.host_source_ref) || !safeReference(value.host_session_ref) || !safeReference(value.host_turn_ref)) throw invalidInvocationContextRequest();
1884
+ return { host_source_ref: value.host_source_ref, host_session_ref: value.host_session_ref, host_turn_ref: value.host_turn_ref };
1885
+ }
1886
+ function requireHumanMessageWindow(value) {
1887
+ if (!isCealJsonRecord(value) || !exactOptionalKeys(value, ["messages", "truncated"], []) || !Array.isArray(value.messages) || value.messages.length > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES || typeof value.truncated !== "boolean") throw invalidInvocationContextRequest();
1888
+ const messages = value.messages.map(decodeHumanMessage);
1889
+ const bytes = new TextEncoder().encode(JSON.stringify(messages)).byteLength;
1890
+ if (bytes > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES) throw invalidInvocationContextRequest();
1891
+ return Object.freeze({ messages: Object.freeze(messages), truncated: value.truncated });
1892
+ }
1893
+ function decodeHumanMessage(candidate) {
1894
+ if (!isCealJsonRecord(candidate) || !exactOptionalKeys(candidate, ["host_turn_ref", "host_input_ref", "text"], []) || !safeReference(candidate.host_turn_ref) || !safeReference(candidate.host_input_ref) || typeof candidate.text !== "string") throw invalidInvocationContextRequest();
1895
+ return Object.freeze({ host_turn_ref: candidate.host_turn_ref, host_input_ref: candidate.host_input_ref, text: candidate.text });
1896
+ }
1897
+ function optionalSafeReference(value) {
1898
+ if (value === void 0) return void 0;
1899
+ if (!safeReference(value)) throw invalidInvocationContextRequest();
1900
+ return value;
1901
+ }
1902
+ function safeReference(value) {
1903
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
1904
+ }
1905
+ function exactOptionalKeys(value, required, optional = []) {
1906
+ const keys = Object.keys(value);
1907
+ return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
1908
+ }
1909
+ function optionalVisibleOutputBudget(value) {
1910
+ if (value === void 0) return void 0;
1911
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > CEAL_HOST_INVOCATION_CONTEXT_VISIBLE_OUTPUT_MAX_BYTES) throw invalidInvocationContextRequest();
1912
+ return value;
1913
+ }
1914
+ function invalidInvocationContextRequest() {
1915
+ return new TypeError("The Host invocation context request is invalid.");
1916
+ }
1917
+ function deriveCealHostInvocationNonceContract(contract = CEAL_CLIENT_WIRE_CONTRACT) {
1918
+ const clientNonce = contract.host_invocation_authentication.client_nonce;
1919
+ return {
1920
+ encoded_characters: clientNonce.encoded_characters,
1921
+ client_nonce_pattern_source: `^[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`,
1922
+ context_token_pattern_source: `^${CEAL_HOST_CONTEXT_TOKEN_PREFIX}[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`
1923
+ };
1924
+ }
1925
+ var hostNonceContract = deriveCealHostInvocationNonceContract();
1926
+ var CEAL_HOST_NONCE_ENCODED_CHARACTERS = hostNonceContract.encoded_characters;
1927
+ var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(hostNonceContract.client_nonce_pattern_source, "u");
1928
+ var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(hostNonceContract.context_token_pattern_source, "u");
1929
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = deriveCealHostInvocationAuthenticationResultMaxBytes();
1930
+ function deriveCealHostInvocationAuthenticationResultMaxBytes(contract = CEAL_CLIENT_WIRE_CONTRACT) {
1931
+ const hostAuthentication = contract.host_invocation_authentication;
1932
+ const values = {
1933
+ schema_version: deriveCealClientWireSchemaVersion({ schema_name: hostAuthentication.result_schema_name, fields: hostAuthentication.result_fields }),
1934
+ context_token: `${CEAL_HOST_CONTEXT_TOKEN_PREFIX}${"A".repeat(hostAuthentication.client_nonce.encoded_characters)}`,
1935
+ expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
1936
+ };
1937
+ const fields = hostAuthentication.result_fields;
1938
+ if (fields.some((field) => !Object.hasOwn(values, field))) throw new TypeError("Host invocation result fields have no bounded sample value.");
1939
+ return new TextEncoder().encode(`${JSON.stringify(Object.fromEntries(fields.map((field) => [field, values[field]])))}
1515
1940
  `).byteLength;
1941
+ }
1516
1942
  var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
1517
1943
  "request_authority_unavailable",
1518
1944
  "request_missing",
@@ -1536,6 +1962,7 @@ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
1536
1962
  "host_credential_refused",
1537
1963
  "gateway_unavailable",
1538
1964
  "gateway_rate_limited",
1965
+ "contract_mismatch",
1539
1966
  "gateway_refused",
1540
1967
  "gateway_response_invalid",
1541
1968
  "producer_frame_invalid",
@@ -1550,19 +1977,23 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
1550
1977
  invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
1551
1978
  options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
1552
1979
  session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1553
- context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn; send a new message to start a fresh Host turn, then retry this command on that turn (run `ceal-host install` only if the Host hook registration is missing or drifted)" }),
1554
- context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1555
- no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
1556
- context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1557
- context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "refresh_host_invocation_context" }),
1980
+ context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "retry" }),
1981
+ context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
1982
+ // `no_binding` now means the producer has no real current Host session. The
1983
+ // Host Relay never uses it for an uncreated Gateway Request; there is no
1984
+ // useful retry until the consumer is running inside a Host session.
1985
+ no_binding: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1986
+ context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
1987
+ context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "retry" }),
1558
1988
  host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
1559
1989
  host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
1560
1990
  gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
1561
1991
  gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
1992
+ contract_mismatch: Object.freeze({ classification: "contract_mismatch", next_action: "upgrade_ceal_client" }),
1562
1993
  gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
1563
1994
  gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
1564
- 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" }),
1565
- 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" }),
1995
+ producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin" }),
1996
+ relay_permission_denied: Object.freeze({ classification: "host_relay_permission_denied", next_action: "rerun_outside_agent_sandbox" }),
1566
1997
  relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
1567
1998
  transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
1568
1999
  mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
@@ -1572,21 +2003,33 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
1572
2003
  function isCealHostAuthenticationCause(value) {
1573
2004
  return isVocabularyWord(CEAL_HOST_AUTHENTICATION_CAUSES, value);
1574
2005
  }
2006
+ function cealHostAuthenticationCauseForExchangeFailure(failure2) {
2007
+ if (failure2 === "permission_denied") return "relay_permission_denied";
2008
+ if (failure2 === "timed_out") return "transient_retryable";
2009
+ return "relay_unavailable";
2010
+ }
1575
2011
  function decodeCealHostInvocationAuthenticationRequest(value) {
1576
- if (!isCealJsonRecord(value) || !exactKeys(value, ["schema_version", "client_nonce"]) || value.schema_version !== CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA || typeof value.client_nonce !== "string" || !CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce)) {
2012
+ if (!isCealJsonRecord(value)) throw new TypeError("The Host invocation authentication request is invalid.");
2013
+ requireCealClientWireSchemaVersion(value.schema_version, CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA);
2014
+ if (!exactKeys(value, CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_FIELDS) || typeof value.client_nonce !== "string" || !CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce)) {
1577
2015
  throw new TypeError("The Host invocation authentication request is invalid.");
1578
2016
  }
1579
2017
  return Object.freeze({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA, client_nonce: value.client_nonce });
1580
2018
  }
1581
2019
  function decodeCealHostInvocationAuthenticationResult(value, now = /* @__PURE__ */ new Date()) {
1582
- if (!isCealJsonRecord(value) || !exactKeys(value, ["schema_version", "context_token", "expires_at"]) || value.schema_version !== CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA || typeof value.context_token !== "string" || !CEAL_HOST_CONTEXT_TOKEN_PATTERN.test(value.context_token) || typeof value.expires_at !== "string" || !validFutureTimestamp(value.expires_at, now)) {
2020
+ return decodeHostInvocationAuthenticationResult(value, CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_FIELDS, now);
2021
+ }
2022
+ function decodeHostInvocationAuthenticationResult(value, fields, now) {
2023
+ if (!isCealJsonRecord(value)) throw new TypeError("The Host invocation authentication result is invalid.");
2024
+ requireCealClientWireSchemaVersion(value.schema_version, CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA);
2025
+ if (!exactKeys(value, fields) || typeof value.context_token !== "string" || !CEAL_HOST_CONTEXT_TOKEN_PATTERN.test(value.context_token) || typeof value.expires_at !== "string" || !validFutureTimestamp(value.expires_at, now)) {
1583
2026
  throw new TypeError("The Host invocation authentication result is invalid.");
1584
2027
  }
1585
2028
  return Object.freeze({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA, context_token: value.context_token, expires_at: value.expires_at });
1586
2029
  }
1587
2030
  function validFutureTimestamp(value, now) {
1588
- const timestamp4 = Date.parse(value);
1589
- return Number.isFinite(timestamp4) && new Date(timestamp4).toISOString() === value && timestamp4 > now.getTime();
2031
+ const timestamp3 = Date.parse(value);
2032
+ return Number.isFinite(timestamp3) && new Date(timestamp3).toISOString() === value && timestamp3 > now.getTime();
1590
2033
  }
1591
2034
  function exactKeys(value, expected) {
1592
2035
  const keys = Object.keys(value);
@@ -1600,10 +2043,6 @@ var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
1600
2043
  "hook_invoked",
1601
2044
  "registration_written",
1602
2045
  "hook_failed",
1603
- "watcher_turn_observed",
1604
- "current_prompt_registered",
1605
- "current_prompt_registration_failed",
1606
- "current_prompt_capacity_exhausted",
1607
2046
  "activation_recorded",
1608
2047
  "activation_failed",
1609
2048
  "watcher_scan_failed",
@@ -1626,6 +2065,9 @@ var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "un
1626
2065
  var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
1627
2066
  var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "items_path", "row_count", "result_complete", "coverage", "local_result_root", "model_disclosure", "continue_argv", "how_to_read"]);
1628
2067
 
2068
+ // packages/ceal-client-protocol/src/discovery-manifest.ts
2069
+ var DISCOVERY_MANIFEST_KEYS = CEAL_CLIENT_WIRE_CONTRACT.command_response.discovery_manifest_fields;
2070
+
1629
2071
  // packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
1630
2072
  var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
1631
2073
 
@@ -1684,6 +2126,7 @@ var ADMIN_UNAVAILABLE_REASONS = vocabulary([
1684
2126
  "legacy",
1685
2127
  "invalid_observation",
1686
2128
  "redacted",
2129
+ "organization_setting_disabled",
1687
2130
  "owner_unavailable",
1688
2131
  "unit_unknown",
1689
2132
  "retention_gap",
@@ -1713,15 +2156,13 @@ var ADMIN_APPROVAL_DISPOSITIONS = CEAL_OPERATION_APPROVAL_DISPOSITIONS;
1713
2156
  var ADMIN_APPROVAL_POSTURES = vocabulary([...ADMIN_APPROVAL_DISPOSITIONS, "not_applicable"]);
1714
2157
  var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "approval", "effect", "readback"]);
1715
2158
 
1716
- // packages/ceal-client-protocol/src/actor-kind.ts
1717
- var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
1718
-
1719
2159
  // packages/ceal-client-protocol/src/device-enrollment.ts
1720
2160
  var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
1721
2161
  var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
1722
2162
 
1723
2163
  // packages/ceal-client-protocol/src/personal-client-session.ts
1724
2164
  var REFRESH_FAILURE_CODES = new Set(CEAL_REFRESH_FAILURE_CODES);
2165
+ var REVOKE_FAILURE_CODES = new Set(CEAL_CLIENT_REVOKE_FAILURE_CODES);
1725
2166
 
1726
2167
  // packages/ceal-client-protocol/src/index.ts
1727
2168
  var MAX_REQUEST_BYTES = 32 * 1024;
@@ -1816,16 +2257,9 @@ function hostClientProbeRefusal(probe, subject) {
1816
2257
  import { performance } from "node:perf_hooks";
1817
2258
 
1818
2259
  // host/relay/src/producer-frame.ts
1819
- var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
1820
- var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
1821
2260
  var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
1822
- var SAFE_REF2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
1823
- var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
1824
- var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "user_input", "turn_terminal", "producer_status"];
2261
+ var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "turn_terminal", "producer_status"];
1825
2262
  var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
1826
- function isHostProducerFrameKind(value) {
1827
- return typeof value === "string" && FRAME_KINDS.has(value);
1828
- }
1829
2263
  var HOST_PRODUCER_FRAME_ERROR_CODES = [
1830
2264
  "invalid_frame_encoding",
1831
2265
  "invalid_frame_length",
@@ -1835,544 +2269,205 @@ var HOST_PRODUCER_FRAME_ERROR_CODES = [
1835
2269
  "invalid_producer_identity",
1836
2270
  "frame_too_large"
1837
2271
  ];
1838
- var HostProducerFrameError = class extends Error {
1839
- code;
1840
- constructor(code2, message = code2) {
2272
+
2273
+ // host/relay/src/host-producer-core.ts
2274
+ import { createHash as createHash3 } from "node:crypto";
2275
+
2276
+ // host/secure-file.ts
2277
+ import { execFile } from "node:child_process";
2278
+ import { constants } from "node:fs";
2279
+ import { lstat, open } from "node:fs/promises";
2280
+ import { dirname, win32 } from "node:path";
2281
+ import { fileURLToPath } from "node:url";
2282
+ var SecureFileCustodyError = class extends Error {
2283
+ name = "SecureFileCustodyError";
2284
+ reason;
2285
+ constructor(reason, message) {
1841
2286
  super(message);
1842
- this.code = code2;
2287
+ this.reason = reason;
2288
+ }
2289
+ get code() {
2290
+ if (this.reason === "reparse_point") return "ELOOP";
2291
+ if (this.reason === "not_found") return "ENOENT";
2292
+ if (this.reason === "too_large") return "secure_file_too_large";
2293
+ if (this.reason === "already_exists") return "EEXIST";
2294
+ return this.reason === "not_private" ? "secure_file_not_private" : "secure_file_unavailable";
1843
2295
  }
1844
2296
  };
1845
- function encodeHostProducerFrame(frame) {
1846
- validateFrame(frame);
1847
- const json = Buffer.from(JSON.stringify(frame), "utf8");
1848
- 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}`)}`);
1849
- const result = Buffer.allocUnsafe(json.byteLength + HOST_PRODUCER_FRAME_LENGTH_BYTES);
1850
- result.writeUInt32BE(json.byteLength, 0);
1851
- json.copy(result, HOST_PRODUCER_FRAME_LENGTH_BYTES);
1852
- return result;
1853
- }
1854
- function decodeHostProducerFrame(input) {
1855
- 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}`)}`);
1856
- const length = input.readUInt32BE(0);
1857
- 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}`)}`);
1858
- if (length === 0) throw new HostProducerFrameError("invalid_frame_length");
1859
- 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");
1860
- const bytes = input.subarray(HOST_PRODUCER_FRAME_LENGTH_BYTES);
1861
- const text = new TextDecoder("utf-8", { fatal: true });
1862
- let value;
2297
+ var WINDOWS_SECUREFS_REFUSAL_SCHEMA = "ceal.host_securefs_refusal.v1";
2298
+ var WINDOWS_SECUREFS_MAX_RANDOM_FILE_BYTES = 4096;
2299
+ var WINDOWS_SECUREFS_TIMEOUT_MS = 1e4;
2300
+ var WINDOWS_SECUREFS_DIAGNOSTIC_BYTES = 4096;
2301
+ function secureOpenFlags(flags, platform) {
2302
+ if (platform === "win32") return flags;
2303
+ const nofollow = constants.O_NOFOLLOW;
2304
+ if (typeof nofollow === "number" && nofollow !== 0) return flags | nofollow;
2305
+ throw new Error("Secure file reads require O_NOFOLLOW on this platform.");
2306
+ }
2307
+ async function openNoFollow(path, flags = constants.O_RDONLY, mode, platform = process.platform, _custody = {}) {
2308
+ if (platform !== "win32") return await open(path, secureOpenFlags(flags, platform), mode);
2309
+ return await openWindowsIdentityScreened(path, flags, mode);
2310
+ }
2311
+ async function openWindowsIdentityScreened(path, flags, mode) {
2312
+ const handle = await open(path, flags, mode);
1863
2313
  try {
1864
- value = JSON.parse(text.decode(bytes));
2314
+ const first = await handle.stat();
2315
+ if (!first.isFile()) throw new SecureFileCustodyError("not_private", "The Windows Host file is not a regular file.");
2316
+ await assertWindowsLinkFree(path, first);
2317
+ await assertWindowsIdentityStable(handle, first, path);
2318
+ return handle;
1865
2319
  } catch (error) {
1866
- if (error instanceof TypeError) throw new HostProducerFrameError("invalid_frame_encoding");
1867
- throw new HostProducerFrameError("frame_truncated");
2320
+ await handle.close();
2321
+ throw error;
1868
2322
  }
1869
- validateFrame(value);
1870
- return value;
1871
2323
  }
1872
- function isHostSafeRef(value) {
1873
- return safeRef(value);
1874
- }
1875
- function validateFrame(value) {
1876
- if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
1877
- 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)}`);
1878
- if (!exactKeys2(value, ["schema_version", "producer_id", "producer_installation_ref", "producer_epoch", "sequence", "frame_kind", "occurred_at", "payload"])) throw new HostProducerFrameError("invalid_frame_shape");
1879
- if (!PRODUCERS.has(String(value.producer_id)) || !safeRef(value.producer_installation_ref)) throw new HostProducerFrameError("invalid_producer_identity");
1880
- 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");
1881
- validatePayload(value.frame_kind, value.payload);
1882
- }
1883
- function validatePayload(kind, value) {
1884
- const valid = kind === "session_started" ? validSession(value) : kind === "turn_started" ? validTurn(value) : kind === "user_input" ? validInput(value) : kind === "turn_terminal" ? validTerminal(value) : validStatus(value);
1885
- if (!valid) throw new HostProducerFrameError("invalid_frame_shape");
1886
- }
1887
- function validSession(value) {
1888
- return exactOptional(value, ["local_session_ref", "working_directory_ref"], ["model", "host_version", "observations"]) && safeRef(value.local_session_ref) && safeRef(value.working_directory_ref) && optionalText(value.model, 256) && optionalText(value.host_version, 256) && validObservations(value.observations);
2324
+ async function assertWindowsLinkFree(path, first) {
2325
+ const entry = await lstat(path);
2326
+ if (entry.isSymbolicLink() || entry.dev !== first.dev || entry.ino !== first.ino) {
2327
+ throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
2328
+ }
2329
+ }
2330
+ async function assertWindowsIdentityStable(handle, first, path) {
2331
+ const second = await handle.stat();
2332
+ if (second.dev !== first.dev || second.ino !== first.ino || second.size !== first.size || second.mtimeMs !== first.mtimeMs) {
2333
+ throw new SecureFileCustodyError("unavailable", "The Windows Host file changed while its identity was checked.");
2334
+ }
2335
+ const entry = await lstat(path);
2336
+ if (entry.isSymbolicLink()) throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
2337
+ if (entry.dev !== second.dev || entry.ino !== second.ino) throw new SecureFileCustodyError("unavailable", "The Windows Host file path no longer names the opened file.");
2338
+ }
2339
+ async function readPrivateFileWithSecurefs(path, maxBytes, custody = {}) {
2340
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) throw new TypeError("The securefs read bound must be a positive safe integer.");
2341
+ const result = await runWindowsSecurefs(["internal", "securefs", "read-private-file", path, String(maxBytes)], maxBytes, custody);
2342
+ if (result.exit_code === 0 && result.stderr.length === 0 && result.stdout.length <= maxBytes) return result.stdout;
2343
+ if (result.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned bytes with a refusal.");
2344
+ if (result.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command exceeded its output contract.");
2345
+ throw securefsRefusal(result.stderr);
2346
+ }
2347
+ async function createRandomPrivateFileWithSecurefs(path, byteLength2, custody = {}) {
2348
+ if (!Number.isSafeInteger(byteLength2) || byteLength2 < 1 || byteLength2 > WINDOWS_SECUREFS_MAX_RANDOM_FILE_BYTES) throw new TypeError(`The securefs random-file size must be between 1 and ${WINDOWS_SECUREFS_MAX_RANDOM_FILE_BYTES} bytes.`);
2349
+ const result = await runWindowsSecurefs(["internal", "securefs", "create-random-private-file", path, String(byteLength2)], 0, custody);
2350
+ if (result.exit_code === 0 && result.stdout.length === 0 && result.stderr.length === 0) return;
2351
+ if (result.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned unexpected creation bytes.");
2352
+ if (result.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an invalid creation result.");
2353
+ throw securefsRefusal(result.stderr);
2354
+ }
2355
+ async function runWindowsSecurefs(args, maxBytes, custody) {
2356
+ if (custody.run_securefs_command !== void 0) return await custody.run_securefs_command(args);
2357
+ const executable = custody.securefs_executable ?? resolveInstalledHostExecutable();
2358
+ const processArgs = [...custody.securefs_args_prefix ?? [], ...args];
2359
+ return await new Promise((resolve, reject) => {
2360
+ execFile(executable, processArgs, { encoding: "buffer", timeout: WINDOWS_SECUREFS_TIMEOUT_MS, maxBuffer: maxBytes + WINDOWS_SECUREFS_DIAGNOSTIC_BYTES }, (error, stdout, stderr) => {
2361
+ const exitCode = error === null ? 0 : typeof error.code === "number" ? error.code : null;
2362
+ if (exitCode === null) {
2363
+ reject(new SecureFileCustodyError("unavailable", "The installed Windows Host securefs command could not run."));
2364
+ return;
2365
+ }
2366
+ resolve({ exit_code: exitCode, stdout: Buffer.from(stdout), stderr: Buffer.from(stderr) });
2367
+ });
2368
+ });
1889
2369
  }
1890
- function validTurn(value) {
1891
- return exactOptional(value, ["local_session_ref", "local_turn_ref"], ["model", "observations"]) && safeRef(value.local_session_ref) && safeRef(value.local_turn_ref) && optionalText(value.model, 256) && validObservations(value.observations);
2370
+ function securefsRefusal(stderr) {
2371
+ let value;
2372
+ try {
2373
+ value = JSON.parse(stderr.toString("utf8"));
2374
+ } catch {
2375
+ return new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an unreadable refusal.");
2376
+ }
2377
+ if (!isRecord(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.");
2378
+ if (value.code === "reparse_point") return new SecureFileCustodyError("reparse_point", "The Windows Host securefs file is a reparse point.");
2379
+ if (value.code === "not_found") return new SecureFileCustodyError("not_found", "The Windows Host securefs file was not found.");
2380
+ if (value.code === "too_large") return new SecureFileCustodyError("too_large", "The Windows Host securefs file exceeds its read bound.");
2381
+ if (value.code === "already_exists") return new SecureFileCustodyError("already_exists", "The Windows Host securefs target already exists.");
2382
+ if (value.code === "not_private") return new SecureFileCustodyError("not_private", "The Windows Host securefs file is not private to the current user.");
2383
+ return new SecureFileCustodyError("unavailable", "The Windows Host securefs command refused the file read.");
1892
2384
  }
1893
- function validInput(value) {
1894
- return exactOptional(value, ["local_session_ref", "local_turn_ref", "local_input_ref", "input_kind", "origin_input", "observations"], ["local_turn_aliases", "context_expires_at"]) && safeRef(value.local_session_ref) && safeRef(value.local_turn_ref) && safeRef(value.local_input_ref) && validInputObservations(value.observations) && isCealHostInputKind(value.input_kind) && typeof value.origin_input === "boolean" && validInputOptionals(value);
2385
+ function resolveInstalledHostExecutable() {
2386
+ const privateRuntimeRoot = dirname(fileURLToPath(import.meta.url));
2387
+ return win32.join(privateRuntimeRoot, "..", "..", "bin", "ceal-host.exe");
1895
2388
  }
1896
- function validInputObservations(value) {
1897
- return validObservations(value) && Array.isArray(value) && value.filter((item) => record(item) && item.observation_kind === "prompt").length === 1;
2389
+ function isRecord(value) {
2390
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1898
2391
  }
1899
- function validInputOptionals(value) {
1900
- return optionalRefs(value.local_turn_aliases) && (value.context_expires_at === void 0 || timestamp(value.context_expires_at));
2392
+ function isOwnedBy(uid, expectedUid) {
2393
+ return Number.isSafeInteger(uid) && Number.isSafeInteger(expectedUid) && uid === expectedUid;
1901
2394
  }
1902
- function validTerminal(value) {
1903
- return exactOptional(value, ["local_session_ref", "local_turn_ref", "terminal_state"], ["abort_reason", "duration_ms", "time_to_first_token_ms", "usage", "observations"]) && safeRef(value.local_session_ref) && safeRef(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);
2395
+ function isSelfOwned(uid, platform = process.platform, _handle) {
2396
+ if (platform === "win32" || typeof process.geteuid !== "function") return false;
2397
+ return isOwnedBy(uid, process.geteuid());
1904
2398
  }
1905
- function validTerminalDisposition(state, reason) {
1906
- return (state === "completed" || state === "aborted") && (reason === void 0 || state === "aborted" && safeRef(reason));
2399
+ function isPrivateFileMode(mode, platform = process.platform, _handle) {
2400
+ if (platform === "win32") return false;
2401
+ return (mode & 63) === 0;
1907
2402
  }
1908
- function validStatus(value) {
1909
- return exactOptional(value, ["status", "reason_code"], ["detail"]) && ["ready", "degraded", "stopping"].includes(String(value.status)) && safeRef(value.reason_code) && optionalText(value.detail, 4096);
2403
+
2404
+ // host/relay/src/host-producer-core.ts
2405
+ var HOST_TRANSCRIPT_CURSOR_REASONS = vocabulary(["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"]);
2406
+ function isHostTranscriptCursorReason(value) {
2407
+ return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
1910
2408
  }
1911
- function optionalText(value, maxLength) {
1912
- return value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maxLength;
2409
+ var TRANSCRIPT_MISSING_REASON = "transcript_missing";
2410
+ var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
2411
+ var HOST_INVOCATION_TRANSCRIPT_TAIL_MAX_BYTES = 1024 * 1024;
2412
+ async function readBoundedHostTranscriptTail(path, maximumBytes = HOST_INVOCATION_TRANSCRIPT_TAIL_MAX_BYTES) {
2413
+ if (!Number.isSafeInteger(maximumBytes) || maximumBytes < 1 || maximumBytes > HOST_INVOCATION_TRANSCRIPT_TAIL_MAX_BYTES) throw new TypeError("Host transcript window byte bound is invalid.");
2414
+ const handle = await openNoFollow(path);
2415
+ try {
2416
+ const stats = await handle.stat();
2417
+ if (!stats.isFile() || !isSelfOwned(stats.uid)) throw new TypeError("Host transcript source is not an owner-owned regular file.");
2418
+ const truncated = stats.size > maximumBytes;
2419
+ const start = Math.max(0, stats.size - maximumBytes);
2420
+ const bytes = await readTranscriptBytes(handle, start, stats.size - start);
2421
+ return { bytes: truncated ? dropPartialLeadingTranscriptRecord(bytes) : bytes, truncated };
2422
+ } finally {
2423
+ await handle.close();
2424
+ }
1913
2425
  }
1914
- function optionalNonNegative(value) {
1915
- return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
2426
+ async function readTranscriptBytes(handle, start, length) {
2427
+ const output = Buffer.alloc(Math.max(0, length));
2428
+ let offset = 0;
2429
+ while (offset < output.length) {
2430
+ const { bytesRead } = await handle.read(output, offset, output.length - offset, start + offset);
2431
+ if (bytesRead === 0) break;
2432
+ offset += bytesRead;
2433
+ }
2434
+ return output.subarray(0, offset);
1916
2435
  }
1917
- function optionalRefs(value) {
1918
- return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef);
2436
+ function dropPartialLeadingTranscriptRecord(bytes) {
2437
+ if (bytes.length === 0) return bytes;
2438
+ const newline = bytes.indexOf(10);
2439
+ return newline < 0 ? Buffer.alloc(0) : bytes.subarray(newline + 1);
1919
2440
  }
1920
- function validObservations(value) {
1921
- if (value === void 0) return true;
1922
- if (!Array.isArray(value) || value.length > 32) return false;
1923
- return value.every((item) => {
1924
- try {
1925
- validateCealHostObservationDraft(item);
1926
- return true;
1927
- } catch {
1928
- return false;
1929
- }
1930
- });
1931
- }
1932
- function jsonValue(value, depth = 0) {
1933
- if (depth > 16) return false;
1934
- if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1935
- if (typeof value === "number") return Number.isFinite(value);
1936
- if (Array.isArray(value)) return value.length <= 256 && value.every((item) => jsonValue(item, depth + 1));
1937
- return record(value) && Object.keys(value).length <= 256 && Object.entries(value).every(([key, item]) => key.length > 0 && key.length <= 128 && jsonValue(item, depth + 1));
1938
- }
1939
- function record(value) {
1940
- return value !== null && typeof value === "object" && !Array.isArray(value);
1941
- }
1942
- function safeRef(value) {
1943
- return typeof value === "string" && SAFE_REF2.test(value);
1944
- }
1945
- function positive(value) {
1946
- return Number.isSafeInteger(value) && Number(value) > 0;
1947
- }
1948
- function timestamp(value) {
1949
- return typeof value === "string" && Number.isFinite(Date.parse(value));
1950
- }
1951
- function hostProducerFrameKind(value) {
1952
- return isHostProducerFrameKind(value);
1953
- }
1954
- function exactKeys2(value, keys) {
1955
- return JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
1956
- }
1957
- function exactOptional(value, required, optional) {
1958
- const keys = Object.keys(value);
1959
- return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
1960
- }
1961
-
1962
- // host/relay/src/host-producer-core.ts
1963
- import { createHash as createHash2 } from "node:crypto";
1964
- import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
1965
- import { mkdir as mkdir2, open as open2, readFile, rename as rename2, writeFile } from "node:fs/promises";
1966
- import { join as join2 } from "node:path";
1967
-
1968
- // host/relay/src/relay-ipc.ts
1969
- import { createConnection, createServer } from "node:net";
1970
-
1971
- // host/relay/src/durable-atomic-replacement.ts
1972
- import { O_RDONLY } from "node:constants";
1973
- import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
1974
- import { dirname } from "node:path";
1975
- var DEFAULT_PORT = {
1976
- platform: process.platform,
1977
- mkdir: (path, options) => mkdir(path, options),
1978
- lstat: (path) => lstat(path),
1979
- open_file: (path) => open(path, "wx", 384),
1980
- rename,
1981
- unlink,
1982
- open_directory: (path) => open(path, O_RDONLY),
1983
- unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
1984
- };
1985
- async function durableAtomicReplace(target, data, port = DEFAULT_PORT) {
1986
- const parent = dirname(target);
1987
- await port.mkdir(parent, { recursive: true, mode: 448 });
1988
- const parentStats = await port.lstat(parent);
1989
- if (!parentStats.isDirectory() || parentStats.isSymbolicLink() || !ownerPrivate(parentStats, port.platform)) throw new TypeError(`Host Relay durable-state directory is unsafe: ${refusalFacts([{ name: "parent_stats", value: parentStats, expected: "an owner-private directory" }, { name: "platform", value: port.platform }])}.`);
1990
- const temporary = `${target}.${port.unique_ref()}.new`;
1991
- let staged;
1992
- const failures = [];
2441
+ async function readRegisteredHostInvocationHumanMessageWindow(listRegistrations, identity, decodeLine) {
1993
2442
  try {
1994
- staged = await port.open_file(temporary);
1995
- await staged.writeFile(data, typeof data === "string" ? { encoding: "utf8" } : void 0);
1996
- await staged.sync();
1997
- const closing = staged;
1998
- staged = void 0;
1999
- await closing.close();
2000
- await port.rename(temporary, target);
2001
- await syncParentDirectory(parent, port);
2002
- } catch (error) {
2003
- failures.push(error);
2004
- }
2005
- await closeStaged(staged, failures);
2006
- await removeTemporary(temporary, port, failures);
2007
- throwFailures(failures);
2008
- }
2009
- async function syncParentDirectory(path, port) {
2010
- if (port.platform === "win32") return;
2011
- const handle = await port.open_directory(path);
2012
- let failure2;
2013
- try {
2014
- await handle.sync();
2015
- } catch (error) {
2016
- if (!directorySyncUnsupported(error)) failure2 = error;
2017
- }
2018
- try {
2019
- await handle.close();
2020
- } catch (error) {
2021
- if (failure2 === void 0) failure2 = error;
2022
- else failure2 = new AggregateError([failure2, error], "Host Relay directory sync and close failed.");
2023
- }
2024
- if (failure2 !== void 0) throw failure2;
2025
- }
2026
- function ownerPrivate(stats, platform) {
2027
- if (platform === "win32") return true;
2028
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
2029
- return stats.uid === uid && (stats.mode & 63) === 0;
2030
- }
2031
- async function closeStaged(staged, failures) {
2032
- if (staged !== void 0) try {
2033
- await staged.close();
2034
- } catch (error) {
2035
- failures.push(error);
2036
- }
2037
- }
2038
- async function removeTemporary(path, port, failures) {
2039
- try {
2040
- await port.unlink(path);
2041
- } catch (error) {
2042
- if (errorCode2(error) !== "ENOENT") failures.push(error);
2043
- }
2044
- }
2045
- function throwFailures(failures) {
2046
- if (failures.length === 1) throw failures[0];
2047
- if (failures.length > 1) throw new AggregateError(failures, "Host Relay durable replacement and cleanup failed.");
2048
- }
2049
- function directorySyncUnsupported(error) {
2050
- return ["EINVAL", "ENOSYS", "ENOTSUP"].includes(errorCode2(error) ?? "");
2051
- }
2052
- function errorCode2(error) {
2053
- return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2054
- }
2055
-
2056
- // host/relay/src/relay-request-binding-store.ts
2057
- var MAX_STATE_BYTES = 8 * 1024 * 1024;
2058
-
2059
- // host/relay/src/current-prompt-custody.ts
2060
- var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
2061
-
2062
- // host/service-log.ts
2063
- function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
2064
- 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 } : {} })}
2065
- `;
2066
- try {
2067
- (sink ?? defaultHostServiceDiagnosticSink)(line);
2443
+ const registration = (await listRegistrations()).find((candidate) => candidate.host_source_ref === identity.host_source_ref && candidate.host_session_ref === identity.host_session_ref && Date.parse(candidate.expires_at) > Date.now());
2444
+ if (registration === void 0) throw new TypeError("context_unavailable");
2445
+ return await readHostInvocationHumanMessageWindow(registration.transcript_path, (line) => decodeLine(line, registration));
2068
2446
  } catch {
2069
- }
2070
- }
2071
- function defaultHostServiceDiagnosticSink(line) {
2072
- process.stderr.write(line);
2073
- }
2074
- var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
2075
- var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
2076
-
2077
- // host/relay/src/relay-ipc.ts
2078
- var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
2079
- "schema_version",
2080
- "producer_id",
2081
- "producer_installation_ref",
2082
- "local_session_ref",
2083
- "local_turn_ref",
2084
- "local_turn_aliases",
2085
- "prompt_ref",
2086
- "request_ref",
2087
- "bound_at",
2088
- "host_source_ref",
2089
- "host_session_ref",
2090
- "host_turn_ref",
2091
- "host_input_ref"
2092
- ]);
2093
- var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
2094
- var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v1";
2095
- var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v1";
2096
- var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
2097
- var HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA = "ceal.host_relay_current_prompt_release.v1";
2098
- var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
2099
- var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
2100
- var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
2101
- var HOST_RELAY_QUERY_RESPONSE_MAX_BYTES = 8192;
2102
- var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
2103
- "context_stale",
2104
- "context_expired",
2105
- "host_source_refused",
2106
- "host_credential_refused",
2107
- "gateway_unavailable",
2108
- "gateway_refused",
2109
- "gateway_response_invalid",
2110
- "gateway_rate_limited",
2111
- "producer_frame_invalid",
2112
- "deadline",
2113
- "cancelled",
2114
- "mint_failed",
2115
- "relay_failed",
2116
- "request_authority_unavailable",
2117
- "request_missing",
2118
- "authority_mismatch",
2119
- "host_source_mismatch",
2120
- "host_session_mismatch",
2121
- "host_turn_mismatch",
2122
- "host_input_mismatch",
2123
- "host_prompt_mismatch"
2124
- ]);
2125
- var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
2126
- "context_stale",
2127
- "context_expired",
2128
- "host_source_refused",
2129
- "host_credential_refused",
2130
- "gateway_unavailable",
2131
- "gateway_rate_limited",
2132
- "gateway_refused",
2133
- "gateway_response_invalid",
2134
- "producer_frame_invalid"
2135
- ]);
2136
- var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
2137
- var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
2138
- "permission_denied",
2139
- "socket_missing",
2140
- "connection_refused",
2141
- "closed_early",
2142
- "response_invalid",
2143
- "timed_out",
2144
- "cancelled",
2145
- "unavailable"
2146
- ]);
2147
- var HostRelayIpcError = class extends Error {
2148
- code;
2149
- /** Set only when the client-side socket exchange itself failed. */
2150
- failure;
2151
- constructor(code2, message = code2, failure2) {
2152
- super(message);
2153
- this.code = code2;
2154
- this.failure = failure2;
2155
- }
2156
- };
2157
- var HostRelayCurrentPromptCapacityRefusal = class extends Error {
2158
- constructor() {
2159
- super("current_prompt_capacity_exhausted");
2160
- this.name = "HostRelayCurrentPromptCapacityRefusal";
2161
- }
2162
- };
2163
- function hostRelayExchangeFailure(error) {
2164
- return error instanceof HostRelayIpcError ? error.failure : void 0;
2165
- }
2166
- function socketErrorFailure(error) {
2167
- switch (errorCode3(error)) {
2168
- case "EACCES":
2169
- case "EPERM":
2170
- return "permission_denied";
2171
- case "ENOENT":
2172
- return "socket_missing";
2173
- case "ECONNREFUSED":
2174
- return "connection_refused";
2175
- default:
2176
- return "unavailable";
2177
- }
2178
- }
2179
- var HostRelayInvocationContextPort = class {
2180
- endpoint;
2181
- constructor(endpoint) {
2182
- this.endpoint = endpoint;
2183
- }
2184
- mintInvocationContext(input, signal) {
2185
- return queryHostRelayInvocationContext(this.endpoint, input, signal);
2186
- }
2187
- };
2188
- var HostRelayIpcAdmissionPort = class {
2189
- endpoint;
2190
- timeout_ms;
2191
- constructor(endpoint, timeoutMs = 5e3) {
2192
- this.endpoint = endpoint;
2193
- this.timeout_ms = timeoutMs;
2194
- }
2195
- async admit(frame) {
2196
- const response2 = await submitHostProducerFrame(this.endpoint, frame, this.timeout_ms);
2197
- if (!relayAdmissionCode(response2.code)) throw new HostRelayIpcError(response2.code);
2198
- return { code: response2.code, admitted_through_sequence: response2.admitted_through_sequence, delivered_through_sequence: response2.delivered_through_sequence };
2199
- }
2200
- };
2201
- var HostRelayCurrentPromptPort = class {
2202
- endpoint;
2203
- timeout_ms;
2204
- constructor(endpoint, timeoutMs = 5e3) {
2205
- this.endpoint = endpoint;
2206
- this.timeout_ms = timeoutMs;
2207
- }
2208
- async register(input) {
2209
- const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
2210
- const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
2211
- if (response2.status === "capacity_exhausted") throw new HostRelayCurrentPromptCapacityRefusal();
2212
- if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
2213
- }
2214
- async release(identity) {
2215
- const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA, ...identity };
2216
- const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
2217
- if (response2.status !== "released") throw new HostRelayIpcError("relay_unavailable", response2.status, "response_invalid");
2218
- }
2219
- };
2220
- function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
2221
- return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
2222
- }
2223
- function queryHostRelayInvocationContext(endpoint, query, signal) {
2224
- return exchange(endpoint, encodeInvocationContextQuery(query), void 0, decodeInvocationContextResponseBuffer, signal);
2225
- }
2226
- function exchange(endpoint, encoded, timeoutMs, decode, signal) {
2227
- return new Promise((resolve, reject) => {
2228
- const socket = createConnection(endpoint);
2229
- let buffer = Buffer.alloc(0);
2230
- let settled = false;
2231
- const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
2232
- const finish = (error, response2) => {
2233
- if (settled) return;
2234
- settled = true;
2235
- signal?.removeEventListener("abort", aborted);
2236
- socket.destroy();
2237
- if (error !== null) reject(error);
2238
- else if (response2 !== void 0) resolve(response2);
2239
- };
2240
- if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
2241
- if (signal?.aborted === true) {
2242
- aborted();
2243
- return;
2447
+ throw new TypeError("context_unavailable");
2448
+ }
2449
+ }
2450
+ async function readHostInvocationHumanMessageWindow(path, decodeLine) {
2451
+ const tail = await readBoundedHostTranscriptTail(path);
2452
+ const lines = new TextDecoder("utf-8", { fatal: true }).decode(tail.bytes).split("\n");
2453
+ const incompleteTail = lines.at(-1) !== "";
2454
+ if (incompleteTail) lines.pop();
2455
+ let truncated = tail.truncated || incompleteTail;
2456
+ const newestFirst = [];
2457
+ const seen = /* @__PURE__ */ new Set();
2458
+ for (const line of lines.reverse()) {
2459
+ if (line.length === 0) continue;
2460
+ const message = decodeLine(line);
2461
+ if (message === "malformed") {
2462
+ truncated = true;
2463
+ continue;
2244
2464
  }
2245
- signal?.addEventListener("abort", aborted, { once: true });
2246
- socket.once("error", (error) => {
2247
- const failure2 = socketErrorFailure(error);
2248
- finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure2}.`, failure2));
2249
- });
2250
- socket.once("connect", () => socket.write(encoded));
2251
- socket.on("data", (chunk) => {
2252
- try {
2253
- buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
2254
- const decoded = decode(buffer);
2255
- if (decoded !== null) finish(null, decoded);
2256
- } catch (error) {
2257
- finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
2258
- }
2259
- });
2260
- socket.once("end", () => {
2261
- if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
2262
- });
2263
- });
2264
- }
2265
- function decodeHostRelayIpcResponse(input) {
2266
- 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}`)}.`);
2267
- const length = input.readUInt32BE(0);
2268
- 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}` }])}.`);
2269
- let value;
2270
- try {
2271
- value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
2272
- } catch {
2273
- throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response is invalid.");
2274
- }
2275
- if (!validResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response shape is invalid.");
2276
- return value;
2277
- }
2278
- function relayAdmissionCode(value) {
2279
- 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";
2280
- }
2281
- function validResponse(value) {
2282
- 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;
2283
- }
2284
- function hostRelayIpcCode(value) {
2285
- 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);
2286
- }
2287
- function nonnegative(value) {
2288
- return Number.isSafeInteger(value) && Number(value) >= 0;
2289
- }
2290
- function record2(value) {
2291
- return value !== null && typeof value === "object" && !Array.isArray(value);
2292
- }
2293
- function decodeResponseBuffer(buffer) {
2294
- if (buffer.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
2295
- const length = buffer.readUInt32BE(0);
2296
- 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}`)}.`);
2297
- if (buffer.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
2298
- 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}` }])}.`);
2299
- return decodeHostRelayIpcResponse(buffer);
2300
- }
2301
- function decodeInvocationContextResponseBuffer(buffer) {
2302
- const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_QUERY_RESPONSE_MAX_BYTES);
2303
- if (value === null) return null;
2304
- if (!validInvocationContextResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay invocation context response is invalid.");
2305
- return value;
2306
- }
2307
- function decodeCurrentPromptResponseBuffer(buffer) {
2308
- const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
2309
- if (value === null) return null;
2310
- if (!record2(value) || !exactKeys3(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "capacity_exhausted" && value.status !== "current_prompt_unavailable" && value.status !== "released") throw new HostRelayIpcError("relay_unavailable", "Host Relay current-prompt response is invalid.", "response_invalid");
2311
- return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
2312
- }
2313
- function encodeInvocationContextQuery(query) {
2314
- const value = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA, ...query };
2315
- if (!validInvocationContextQueryRequest(value)) throw new HostRelayIpcError("invalid_frame_shape");
2316
- return encodeLengthPrefixed(value);
2317
- }
2318
- function validInvocationContextQueryRequest(value) {
2319
- return validQueryIdentity(value, HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) && exactKeys3(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "client_nonce"], ["visible_output_max_bytes"]) && typeof value.client_nonce === "string" && CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce) && (value.visible_output_max_bytes === void 0 || validVisibleOutputBudget(value.visible_output_max_bytes));
2320
- }
2321
- function validQueryIdentity(value, schema) {
2322
- return record2(value) && value.schema_version === schema && (value.producer_id === "codex" || value.producer_id === "claude") && safeRef2(value.producer_installation_ref) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref);
2323
- }
2324
- function validVisibleOutputBudget(value) {
2325
- return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
2326
- }
2327
- function validInvocationContextResponse(value) {
2328
- if (!record2(value) || value.schema_version !== HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA) return false;
2329
- if (value.status === "no_binding") return Object.keys(value).length === 2;
2330
- if (value.status === "refused") return Object.keys(value).length === 3 && isVocabularyWord(HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS, value.reason);
2331
- if (value.status !== "ready" || Object.keys(value).length !== 3) return false;
2332
- try {
2333
- decodeCealHostInvocationAuthenticationResult(value.result);
2334
- return true;
2335
- } catch {
2336
- return false;
2337
- }
2338
- }
2339
- function decodeLengthPrefixedJson(input, maximum) {
2340
- if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
2341
- const length = input.readUInt32BE(0);
2342
- 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}`)}.`);
2343
- if (input.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
2344
- 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}` }])}.`);
2345
- try {
2346
- return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
2347
- } catch {
2348
- throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC message is invalid.");
2465
+ if (message === null || seen.has(message.host_input_ref)) continue;
2466
+ seen.add(message.host_input_ref);
2467
+ newestFirst.push(message);
2349
2468
  }
2469
+ return boundCealHostInvocationHumanMessageWindow(newestFirst, truncated);
2350
2470
  }
2351
- function encodeLengthPrefixed(value) {
2352
- const body = Buffer.from(JSON.stringify(value), "utf8");
2353
- const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
2354
- output.writeUInt32BE(body.length, 0);
2355
- body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
2356
- return output;
2357
- }
2358
- function exactKeys3(value, required, optional = []) {
2359
- const keys = Object.keys(value);
2360
- return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
2361
- }
2362
- function safeRef2(value) {
2363
- return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
2364
- }
2365
- function errorCode3(error) {
2366
- return record2(error) && typeof error.code === "string" ? error.code : null;
2367
- }
2368
-
2369
- // host/relay/src/host-producer-core.ts
2370
- var HOST_TRANSCRIPT_CURSOR_REASONS = vocabulary(["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"]);
2371
- function isHostTranscriptCursorReason(value) {
2372
- return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
2373
- }
2374
- var TRANSCRIPT_MISSING_REASON = "transcript_missing";
2375
- var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
2376
2471
  function transcriptScanFailureKind(error, adapterFailureKind) {
2377
2472
  if (transcriptErrorCode(error) === "ELOOP") return "symlink";
2378
2473
  const adapter = adapterFailureKind(error);
@@ -2382,16 +2477,6 @@ function transcriptScanFailureKind(error, adapterFailureKind) {
2382
2477
  if (code2 !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code2)) return "read_error";
2383
2478
  return "other";
2384
2479
  }
2385
- async function registerHostCurrentPromptDiagnosed(register, diagnostic) {
2386
- try {
2387
- await register();
2388
- diagnostic("current_prompt_registered");
2389
- return true;
2390
- } catch (error) {
2391
- if (!(error instanceof HostRelayCurrentPromptCapacityRefusal)) diagnostic("current_prompt_registration_failed");
2392
- throw error;
2393
- }
2394
- }
2395
2480
  function transcriptErrorCode(error) {
2396
2481
  return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
2397
2482
  }
@@ -2439,10 +2524,10 @@ async function transcriptCursorBoundsReason(handle, cursor, size) {
2439
2524
  return null;
2440
2525
  }
2441
2526
  function transcriptSha256(value) {
2442
- return createHash2("sha256").update(value).digest("hex");
2527
+ return createHash3("sha256").update(value).digest("hex");
2443
2528
  }
2444
2529
  function transcriptObjectSha256(value) {
2445
- return createHash2("sha256").update(JSON.stringify(value), "utf8").digest("hex");
2530
+ return createHash3("sha256").update(JSON.stringify(value), "utf8").digest("hex");
2446
2531
  }
2447
2532
  function isTranscriptRecord(value) {
2448
2533
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -2503,24 +2588,18 @@ var HostTranscriptWatcher = class {
2503
2588
  return scanTranscriptRegistrations(await this.listRegistrations(), (registration) => this.scanRegistration(registration));
2504
2589
  }
2505
2590
  async scanRegistration(registration) {
2506
- try {
2507
- return await this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
2508
- missing: () => this.retireMissing(registration),
2509
- failed: (handle, error) => this.scanFailed(handle, registration, error)
2510
- });
2511
- } catch (error) {
2512
- if (error instanceof HostRelayCurrentPromptCapacityRefusal) return { records: 0, enqueued: 0, quarantined: false };
2513
- throw error;
2514
- }
2591
+ return this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
2592
+ missing: () => this.retireMissing(registration),
2593
+ failed: (handle, error) => this.scanFailed(handle, registration, error)
2594
+ });
2515
2595
  }
2516
2596
  async openTranscript(registration, scan, handlers) {
2517
2597
  let handle;
2518
2598
  try {
2519
- handle = await open2(this.transcriptPathOf(registration), O_RDONLY2 | O_NOFOLLOW);
2599
+ handle = await openNoFollow(this.transcriptPathOf(registration));
2520
2600
  return await scan(handle);
2521
2601
  } catch (error) {
2522
2602
  if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
2523
- if (hostRelayExchangeFailure(error) !== void 0 || error instanceof HostRelayCurrentPromptCapacityRefusal) throw error;
2524
2603
  return handlers.failed(handle, error);
2525
2604
  } finally {
2526
2605
  await handle?.close();
@@ -2566,29 +2645,12 @@ var HostTranscriptWatcher = class {
2566
2645
  return transcriptCursorBoundsReason(handle, cursor, size);
2567
2646
  }
2568
2647
  };
2569
- var HOST_PRODUCER_STATE_IDENTITY = {
2570
- codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
2571
- claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
2572
- };
2573
2648
  var HostProducerDrainCore = class {
2574
2649
  host;
2575
- relay;
2576
- statePath;
2577
- stateSchema;
2578
- installationRef;
2579
- epoch;
2580
2650
  now;
2581
2651
  tail = Promise.resolve();
2582
2652
  constructor(host, runtime) {
2583
- if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
2584
- const epoch = runtime.producer_epoch ?? 1;
2585
- if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
2586
2653
  this.host = host;
2587
- this.relay = runtime.relay;
2588
- this.statePath = join2(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
2589
- this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
2590
- this.installationRef = runtime.producer_installation_ref;
2591
- this.epoch = epoch;
2592
2654
  this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
2593
2655
  }
2594
2656
  drainOutbox() {
@@ -2598,145 +2660,681 @@ var HostProducerDrainCore = class {
2598
2660
  }
2599
2661
  async drainLocked() {
2600
2662
  const read = await this.host.listEnvelopes();
2601
- let admitted = 0;
2663
+ let removed = 0;
2602
2664
  let expired = 0;
2603
- let retained = 0;
2604
2665
  for (const envelope of read.entries) {
2605
- const reserved = await this.hasReservation(this.host.submissionRefOf(envelope));
2606
- if (!reserved && Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) {
2607
- await this.host.removeEnvelope(envelope);
2608
- expired += 1;
2609
- continue;
2666
+ if (Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) expired += 1;
2667
+ else removed += 1;
2668
+ await this.host.removeEnvelope(envelope);
2669
+ }
2670
+ return { removed, expired, malformed: read.malformed };
2671
+ }
2672
+ };
2673
+
2674
+ // host/relay/src/durable-atomic-replacement.ts
2675
+ import { lstat as lstat2, mkdir, open as open2, rename, unlink } from "node:fs/promises";
2676
+ import { dirname as dirname2 } from "node:path";
2677
+
2678
+ // host/windows-rename-retry.ts
2679
+ import { setTimeout as setTimeout2 } from "node:timers/promises";
2680
+ var RETRY_WINDOW_MS = 1e4;
2681
+ var RETRY_START_DELAY_MS = 100;
2682
+ var RETRY_MAX_DELAY_MS = 2e3;
2683
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set(["EBUSY", "EPERM", "EACCES"]);
2684
+ var WindowsRenameRetryError = class extends Error {
2685
+ code = "rename_unavailable";
2686
+ constructor(target, attempts, options = {}) {
2687
+ super(`Windows did not permit renaming into place within the bounded retry window: ${target} (after ${attempts} attempts). Wait for the process holding the path to release it, then rerun the same install or update command.`, options);
2688
+ this.name = "WindowsRenameRetryError";
2689
+ }
2690
+ };
2691
+ function resolveRetrySchedule(tuning) {
2692
+ return {
2693
+ windowMs: tuning.windowMs ?? RETRY_WINDOW_MS,
2694
+ startDelayMs: tuning.startDelayMs ?? RETRY_START_DELAY_MS,
2695
+ maxDelayMs: tuning.maxDelayMs ?? RETRY_MAX_DELAY_MS,
2696
+ sleep: tuning.sleep ?? ((ms) => setTimeout2(ms)),
2697
+ now: tuning.now ?? (() => Date.now())
2698
+ };
2699
+ }
2700
+ async function renameWithWindowsRetry(operation, tuning) {
2701
+ const schedule = resolveRetrySchedule(tuning);
2702
+ const deadline = schedule.now() + schedule.windowMs;
2703
+ let delayMs = schedule.startDelayMs;
2704
+ let attempts = 0;
2705
+ for (; ; ) {
2706
+ attempts += 1;
2707
+ try {
2708
+ await operation();
2709
+ return;
2710
+ } catch (error) {
2711
+ if (tuning.platform !== "win32" || !isRetryableRenameError(error)) throw error;
2712
+ await sleepWithinWindow(schedule, deadline, delayMs, () => new WindowsRenameRetryError(tuning.target, attempts, { cause: error }));
2713
+ delayMs = Math.min(delayMs * 2, schedule.maxDelayMs);
2714
+ }
2715
+ }
2716
+ }
2717
+ async function sleepWithinWindow(schedule, deadline, delayMs, exhausted) {
2718
+ if (schedule.now() >= deadline) throw exhausted();
2719
+ await schedule.sleep(Math.min(delayMs, deadline - schedule.now()));
2720
+ if (schedule.now() >= deadline) throw exhausted();
2721
+ }
2722
+ function isRetryableRenameError(error) {
2723
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && RETRYABLE_CODES.has(error.code);
2724
+ }
2725
+
2726
+ // host/relay/src/durable-atomic-replacement.ts
2727
+ var DEFAULT_PORT = {
2728
+ platform: process.platform,
2729
+ mkdir: (path, options) => mkdir(path, options),
2730
+ lstat: (path) => lstat2(path),
2731
+ open_file: (path) => open2(path, "wx", 384),
2732
+ rename,
2733
+ unlink,
2734
+ open_directory: (path) => open2(path),
2735
+ unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
2736
+ };
2737
+ async function durableAtomicReplace(target, data, port = DEFAULT_PORT) {
2738
+ const parent = dirname2(target);
2739
+ await port.mkdir(parent, { recursive: true, mode: 448 });
2740
+ const parentStats = await port.lstat(parent);
2741
+ const expectedUid = typeof process.geteuid === "function" ? process.geteuid() : parentStats.uid;
2742
+ const ownerPrivate = port.platform === "win32" || isOwnedBy(parentStats.uid, expectedUid) && isPrivateFileMode(parentStats.mode, port.platform);
2743
+ if (!parentStats.isDirectory() || parentStats.isSymbolicLink() || !ownerPrivate) throw new TypeError(`Host Relay durable-state directory is unsafe: ${refusalFacts([{ name: "parent_stats", value: parentStats, expected: "an owner-private directory" }, { name: "platform", value: port.platform }])}.`);
2744
+ const temporary = `${target}.${port.unique_ref()}.new`;
2745
+ let staged;
2746
+ const failures = [];
2747
+ try {
2748
+ staged = await port.open_file(temporary);
2749
+ await staged.writeFile(data, typeof data === "string" ? { encoding: "utf8" } : void 0);
2750
+ await staged.sync();
2751
+ const closing = staged;
2752
+ staged = void 0;
2753
+ await closing.close();
2754
+ await renameWithWindowsRetry(() => port.rename(temporary, target), { ...port.rename_retry, platform: port.platform, target });
2755
+ await syncParentDirectory(parent, port);
2756
+ } catch (error) {
2757
+ failures.push(error);
2758
+ }
2759
+ await closeStaged(staged, failures);
2760
+ await removeTemporary(temporary, port, failures);
2761
+ throwFailures(failures);
2762
+ }
2763
+ async function syncParentDirectory(path, port) {
2764
+ if (port.platform === "win32") return;
2765
+ const handle = await port.open_directory(path);
2766
+ let failure2;
2767
+ try {
2768
+ await handle.sync();
2769
+ } catch (error) {
2770
+ if (!directorySyncUnsupported(error)) failure2 = error;
2771
+ }
2772
+ try {
2773
+ await handle.close();
2774
+ } catch (error) {
2775
+ if (failure2 === void 0) failure2 = error;
2776
+ else failure2 = new AggregateError([failure2, error], "Host Relay directory sync and close failed.");
2777
+ }
2778
+ if (failure2 !== void 0) throw failure2;
2779
+ }
2780
+ async function closeStaged(staged, failures) {
2781
+ if (staged !== void 0) try {
2782
+ await staged.close();
2783
+ } catch (error) {
2784
+ failures.push(error);
2785
+ }
2786
+ }
2787
+ async function removeTemporary(path, port, failures) {
2788
+ try {
2789
+ await port.unlink(path);
2790
+ } catch (error) {
2791
+ if (errorCode2(error) !== "ENOENT") failures.push(error);
2792
+ }
2793
+ }
2794
+ function throwFailures(failures) {
2795
+ if (failures.length === 1) throw failures[0];
2796
+ if (failures.length > 1) throw new AggregateError(failures, "Host Relay durable replacement and cleanup failed.");
2797
+ }
2798
+ function directorySyncUnsupported(error) {
2799
+ return ["EINVAL", "ENOSYS", "ENOTSUP"].includes(errorCode2(error) ?? "");
2800
+ }
2801
+ function errorCode2(error) {
2802
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2803
+ }
2804
+
2805
+ // host/relay/src/relay-quarantine-store.ts
2806
+ var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
2807
+ var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
2808
+ var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
2809
+
2810
+ // host/relay/src/relay-admission-store.ts
2811
+ var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
2812
+ var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
2813
+ var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
2814
+
2815
+ // host/relay/src/relay-ipc.ts
2816
+ import { createConnection, createServer } from "node:net";
2817
+
2818
+ // host/relay/src/relay-pipe-handshake.ts
2819
+ import { createHmac, randomBytes } from "node:crypto";
2820
+ import { constants as constants2 } from "node:fs";
2821
+ import { link, unlink as unlink2 } from "node:fs/promises";
2822
+ import { dirname as dirname3, join as join2 } from "node:path";
2823
+ var RELAY_PIPE_HANDSHAKE_TIMEOUT_MS = 5e3;
2824
+ var RELAY_PIPE_KEY_BYTES = 32;
2825
+ var RELAY_PIPE_KEY_FILENAME = "relay-pipe.key";
2826
+ var RELAY_PIPE_NONCE_BYTES = 32;
2827
+ var RELAY_PIPE_NONCE_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
2828
+ var RELAY_PIPE_HELLO_SCHEMA = "ceal.host_relay_pipe_hello.v1";
2829
+ var RELAY_PIPE_CHALLENGE_SCHEMA = "ceal.host_relay_pipe_challenge.v1";
2830
+ var RELAY_PIPE_RESPONSE_SCHEMA = "ceal.host_relay_pipe_response.v1";
2831
+ var RELAY_PIPE_FRAME_BYTES = Uint32Array.BYTES_PER_ELEMENT;
2832
+ var RELAY_PIPE_FRAME_MAX_BYTES = 1024;
2833
+ var RelayPipeHandshakeError = class extends Error {
2834
+ name = "RelayPipeHandshakeError";
2835
+ code;
2836
+ constructor(code2, message) {
2837
+ super(message);
2838
+ this.code = code2;
2839
+ }
2840
+ };
2841
+ function relayPipeStateRootForOutboxDir(outboxDir) {
2842
+ return join2(dirname3(dirname3(dirname3(outboxDir))), "relay");
2843
+ }
2844
+ function relayPipeKeyPath(relayStateRoot) {
2845
+ return join2(relayStateRoot, RELAY_PIPE_KEY_FILENAME);
2846
+ }
2847
+ async function loadRelayPipeKey(relayStateRoot, platform = process.platform, custody = {}) {
2848
+ const path = relayPipeKeyPath(relayStateRoot);
2849
+ if (platform === "win32") return await loadWindowsRelayPipeKey(path, custody);
2850
+ const existing = await readRelayPipeKey(path, platform, custody);
2851
+ if (existing !== null) return existing;
2852
+ return await createPosixRelayPipeKey(path, platform, custody);
2853
+ }
2854
+ async function loadWindowsRelayPipeKey(path, custody) {
2855
+ const existing = await readRelayPipeKey(path, "win32", custody);
2856
+ if (existing !== null) return existing;
2857
+ try {
2858
+ await createRandomPrivateFileWithSecurefs(path, RELAY_PIPE_KEY_BYTES, custody);
2859
+ } catch (error) {
2860
+ if (errorCode3(error) === "EEXIST") return await readWinnerPipeKey(path, "win32", custody);
2861
+ throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}; delete it and restart the Host Relay.`);
2862
+ }
2863
+ return await readWinnerPipeKey(path, "win32", custody);
2864
+ }
2865
+ async function createPosixRelayPipeKey(path, platform, custody) {
2866
+ const key = randomBytes(RELAY_PIPE_KEY_BYTES);
2867
+ const staging = `${path}.${randomBytes(8).toString("hex")}.tmp`;
2868
+ let handle;
2869
+ try {
2870
+ handle = await openNoFollow(staging, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL, 384);
2871
+ } catch (error) {
2872
+ if (errorCode3(error) !== "EEXIST") throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
2873
+ return await readWinnerPipeKey(path, platform, custody);
2874
+ }
2875
+ try {
2876
+ const stats = await handle.stat();
2877
+ if (!stats.isFile() || !isSelfOwned(stats.uid) || !isPrivateFileMode(stats.mode)) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} is not a private regular file owned by the current user.`);
2878
+ await handle.writeFile(key);
2879
+ await handle.sync();
2880
+ } finally {
2881
+ await handle.close();
2882
+ }
2883
+ try {
2884
+ await link(staging, path);
2885
+ } catch (error) {
2886
+ if (errorCode3(error) !== "EEXIST") throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
2887
+ return await readWinnerPipeKey(path, platform, custody);
2888
+ } finally {
2889
+ await unlink2(staging).catch(() => void 0);
2890
+ }
2891
+ return Buffer.from(key);
2892
+ }
2893
+ async function readWinnerPipeKey(path, platform, custody) {
2894
+ const winner = await readRelayPipeKey(path, platform, custody);
2895
+ if (winner === null) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
2896
+ return winner;
2897
+ }
2898
+ async function readRelayPipeKey(path, platform, custody) {
2899
+ if (platform === "win32") return await readWindowsRelayPipeKey(path, custody);
2900
+ return await readPosixRelayPipeKey(path);
2901
+ }
2902
+ async function readWindowsRelayPipeKey(path, custody) {
2903
+ let key;
2904
+ try {
2905
+ key = await readPrivateFileWithSecurefs(path, RELAY_PIPE_KEY_BYTES, custody);
2906
+ } catch (error) {
2907
+ if (errorCode3(error) === "ENOENT" || errorCode3(error) === "ELOOP") return null;
2908
+ throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}; delete it and restart the Host Relay.`);
2909
+ }
2910
+ if (key.byteLength !== RELAY_PIPE_KEY_BYTES) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} has ${key.byteLength} bytes; want ${RELAY_PIPE_KEY_BYTES}. Delete the file and restart the Host Relay.`);
2911
+ return Buffer.from(key);
2912
+ }
2913
+ async function readPosixRelayPipeKey(path) {
2914
+ let handle;
2915
+ try {
2916
+ handle = await openNoFollow(path);
2917
+ } catch (error) {
2918
+ if (errorCode3(error) === "ENOENT" || errorCode3(error) === "ELOOP") return null;
2919
+ throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
2920
+ }
2921
+ try {
2922
+ const stats = await handle.stat();
2923
+ if (!stats.isFile()) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} is not a regular file; delete it and restart the Host Relay.`);
2924
+ if (!isSelfOwned(stats.uid) || !isPrivateFileMode(stats.mode)) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} is not a private file owned by the current user; delete it and restart the Host Relay.`);
2925
+ if (stats.size !== RELAY_PIPE_KEY_BYTES) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} has ${stats.size} bytes; want ${RELAY_PIPE_KEY_BYTES}. Delete the file and restart the Host Relay.`);
2926
+ return await handle.readFile();
2927
+ } finally {
2928
+ await handle.close();
2929
+ }
2930
+ }
2931
+ function createPipeClientNonce() {
2932
+ return randomBytes(RELAY_PIPE_NONCE_BYTES).toString("base64url");
2933
+ }
2934
+ function pipeProof(key, role, firstNonce, secondNonce) {
2935
+ return createHmac("sha256", key).update(`${role}\0${firstNonce}\0${secondNonce}`, "utf8").digest("hex");
2936
+ }
2937
+ function createPipeServerProof(key, clientNonce, serverNonce) {
2938
+ return pipeProof(key, "server", clientNonce, serverNonce);
2939
+ }
2940
+ function createPipeClientProof(key, serverNonce, clientNonce) {
2941
+ return pipeProof(key, "client", serverNonce, clientNonce);
2942
+ }
2943
+ function encodePipeFrame(value) {
2944
+ const body = Buffer.from(JSON.stringify(value), "utf8");
2945
+ const output = Buffer.allocUnsafe(body.length + RELAY_PIPE_FRAME_BYTES);
2946
+ output.writeUInt32BE(body.length, 0);
2947
+ body.copy(output, RELAY_PIPE_FRAME_BYTES);
2948
+ return output;
2949
+ }
2950
+ function decodePipeFrame(buffer) {
2951
+ if (buffer.length < RELAY_PIPE_FRAME_BYTES) return null;
2952
+ const length = buffer.readUInt32BE(0);
2953
+ if (length === 0 || length > RELAY_PIPE_FRAME_MAX_BYTES) throw new RelayPipeHandshakeError("pipe_handshake_invalid", `Host Relay pipe handshake frame declares ${length} bytes; want between 1 and ${RELAY_PIPE_FRAME_MAX_BYTES}.`);
2954
+ if (buffer.length < length + RELAY_PIPE_FRAME_BYTES) return null;
2955
+ let value;
2956
+ try {
2957
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(RELAY_PIPE_FRAME_BYTES, length + RELAY_PIPE_FRAME_BYTES)));
2958
+ } catch {
2959
+ throw new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is not JSON.");
2960
+ }
2961
+ return { value, rest: buffer.subarray(length + RELAY_PIPE_FRAME_BYTES) };
2962
+ }
2963
+ function validNonce(value) {
2964
+ return typeof value === "string" && RELAY_PIPE_NONCE_PATTERN.test(value);
2965
+ }
2966
+ function writePipeFrame(socket, frame) {
2967
+ if (socket.write(frame)) return Promise.resolve();
2968
+ return new Promise((resolve, reject) => {
2969
+ socket.once("drain", resolve);
2970
+ socket.once("error", reject);
2971
+ socket.once("close", () => reject(new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake connection closed.")));
2972
+ });
2973
+ }
2974
+ function readPipeFrames() {
2975
+ let buffer = Buffer.alloc(0);
2976
+ let pending = null;
2977
+ const feed = (chunk) => {
2978
+ buffer = Buffer.concat([buffer, chunk]);
2979
+ if (pending === null) return;
2980
+ let frame;
2981
+ try {
2982
+ frame = decodePipeFrame(buffer);
2983
+ } catch (error) {
2984
+ const waiting2 = pending;
2985
+ pending = null;
2986
+ waiting2.reject(error instanceof Error ? error : new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is invalid."));
2987
+ return;
2988
+ }
2989
+ if (frame === null) return;
2990
+ buffer = frame.rest;
2991
+ const waiting = pending;
2992
+ pending = null;
2993
+ waiting.resolve({ value: frame.value, rest: Buffer.alloc(0) });
2994
+ };
2995
+ const waiter = {
2996
+ wait: () => new Promise((resolve, reject) => {
2997
+ let frame;
2998
+ try {
2999
+ frame = decodePipeFrame(buffer);
3000
+ } catch (error) {
3001
+ reject(error instanceof Error ? error : new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is invalid."));
3002
+ return;
3003
+ }
3004
+ if (frame !== null) {
3005
+ buffer = frame.rest;
3006
+ resolve({ value: frame.value, rest: Buffer.alloc(0) });
3007
+ return;
2610
3008
  }
2611
- const frames = await this.reserve(envelope);
2612
- let complete = true;
2613
- for (const frame of frames) {
2614
- const outcome = await this.relay.admit(frame);
2615
- if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
2616
- complete = false;
2617
- break;
3009
+ pending = { resolve, reject };
3010
+ }),
3011
+ cancel: (error) => {
3012
+ if (pending !== null) {
3013
+ const waiting = pending;
3014
+ pending = null;
3015
+ waiting.reject(error);
3016
+ }
3017
+ }
3018
+ };
3019
+ return { waiter, feed, remainder: () => buffer };
3020
+ }
3021
+ function attachHandshakeReader(socket, waiter, feed) {
3022
+ const onData = (chunk) => feed(Buffer.from(chunk));
3023
+ const onError = (error) => waiter.cancel(error);
3024
+ const onClose = () => waiter.cancel(new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake connection closed."));
3025
+ socket.on("data", onData);
3026
+ socket.once("error", onError);
3027
+ socket.once("close", onClose);
3028
+ return () => {
3029
+ socket.removeListener("data", onData);
3030
+ socket.removeListener("error", onError);
3031
+ socket.removeListener("close", onClose);
3032
+ };
3033
+ }
3034
+ function handshakeTimeout(timeoutMs) {
3035
+ let timer;
3036
+ const done = new Promise((_resolve, reject) => {
3037
+ timer = setTimeout(() => reject(new RelayPipeHandshakeError("pipe_handshake_timeout", `Host Relay pipe handshake stalled past ${timeoutMs} ms; dropping the connection.`)), timeoutMs);
3038
+ timer.unref?.();
3039
+ });
3040
+ return { done, clear: () => clearTimeout(timer) };
3041
+ }
3042
+ async function conductPipeClientHandshake(socket, key, timeoutMs = RELAY_PIPE_HANDSHAKE_TIMEOUT_MS) {
3043
+ const bound = handshakeTimeout(timeoutMs);
3044
+ const { waiter, feed } = readPipeFrames();
3045
+ const detach = attachHandshakeReader(socket, waiter, feed);
3046
+ try {
3047
+ const clientNonce = createPipeClientNonce();
3048
+ await Promise.race([writePipeFrame(socket, encodePipeFrame({ schema_version: RELAY_PIPE_HELLO_SCHEMA, client_nonce: clientNonce })), bound.done]);
3049
+ const challenge = await Promise.race([waiter.wait(), bound.done]);
3050
+ if (!isRecord2(challenge.value) || challenge.value.schema_version !== RELAY_PIPE_CHALLENGE_SCHEMA || !validNonce(challenge.value.server_nonce) || typeof challenge.value.server_proof !== "string") {
3051
+ throw new RelayPipeHandshakeError("pipe_server_unproven", "Host Relay pipe server proof is invalid; aborting before sending any request.");
3052
+ }
3053
+ const serverNonce = challenge.value.server_nonce;
3054
+ const expected = createPipeServerProof(key, clientNonce, serverNonce);
3055
+ if (!timingSafeEqualHex(expected, challenge.value.server_proof)) {
3056
+ throw new RelayPipeHandshakeError("pipe_server_unproven", "Host Relay pipe server proof failed; aborting before sending any request.");
3057
+ }
3058
+ const clientProof = createPipeClientProof(key, serverNonce, clientNonce);
3059
+ await Promise.race([writePipeFrame(socket, encodePipeFrame({ schema_version: RELAY_PIPE_RESPONSE_SCHEMA, client_proof: clientProof })), bound.done]);
3060
+ } finally {
3061
+ bound.clear();
3062
+ detach();
3063
+ }
3064
+ }
3065
+ function timingSafeEqualHex(left, right) {
3066
+ if (left.length !== right.length) return false;
3067
+ let diff = 0;
3068
+ for (let index = 0; index < left.length; index += 1) diff |= left.charCodeAt(index) ^ right.charCodeAt(index);
3069
+ return diff === 0;
3070
+ }
3071
+ function isRecord2(value) {
3072
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3073
+ }
3074
+ function errorCode3(error) {
3075
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
3076
+ }
3077
+
3078
+ // host/relay/src/relay-ipc.ts
3079
+ var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v2";
3080
+ var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v2";
3081
+ var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
3082
+ var HOST_RELAY_QUERY_RESPONSE_MAX_BYTES = 8192;
3083
+ var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
3084
+ "permission_denied",
3085
+ "socket_missing",
3086
+ "connection_refused",
3087
+ "closed_early",
3088
+ "response_invalid",
3089
+ "timed_out",
3090
+ "cancelled",
3091
+ "unavailable"
3092
+ ]);
3093
+ var HostRelayIpcError = class extends Error {
3094
+ code;
3095
+ /** Set only when the client-side socket exchange itself failed. */
3096
+ failure;
3097
+ constructor(code2, message = code2, failure2) {
3098
+ super(message);
3099
+ this.code = code2;
3100
+ this.failure = failure2;
3101
+ }
3102
+ };
3103
+ function hostRelayExchangeFailure(error) {
3104
+ return error instanceof HostRelayIpcError ? error.failure : void 0;
3105
+ }
3106
+ function socketErrorFailure(error) {
3107
+ switch (errorCode4(error)) {
3108
+ case "EACCES":
3109
+ case "EPERM":
3110
+ return "permission_denied";
3111
+ case "ENOENT":
3112
+ return "socket_missing";
3113
+ case "ECONNREFUSED":
3114
+ return "connection_refused";
3115
+ default:
3116
+ return "unavailable";
3117
+ }
3118
+ }
3119
+ var HostRelayInvocationContextPort = class {
3120
+ endpoint;
3121
+ pipe_key;
3122
+ timeout_ms;
3123
+ constructor(endpoint, pipeKey, timeoutMs = 5e3) {
3124
+ this.endpoint = endpoint;
3125
+ this.pipe_key = pipeKey;
3126
+ this.timeout_ms = timeoutMs;
3127
+ }
3128
+ mintInvocationContext(input, signal) {
3129
+ return queryHostRelayInvocationContext(this.endpoint, input, this.timeout_ms, signal, this.pipe_key);
3130
+ }
3131
+ };
3132
+ function queryHostRelayInvocationContext(endpoint, query, timeoutMs, signal, pipeKey) {
3133
+ return exchange(endpoint, encodeInvocationContextQuery(query), timeoutMs, decodeInvocationContextResponseBuffer, signal, pipeKey);
3134
+ }
3135
+ function exchange(endpoint, encoded, timeoutMs, decode, signal, pipeKey) {
3136
+ return new Promise((resolve, reject) => {
3137
+ const socket = createConnection(endpoint);
3138
+ let buffer = Buffer.alloc(0);
3139
+ let settled = false;
3140
+ const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
3141
+ const finish = (error, response2) => {
3142
+ if (settled) return;
3143
+ settled = true;
3144
+ signal?.removeEventListener("abort", aborted);
3145
+ socket.destroy();
3146
+ if (error !== null) reject(error);
3147
+ else if (response2 !== void 0) resolve(response2);
3148
+ };
3149
+ if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
3150
+ if (signal?.aborted === true) {
3151
+ aborted();
3152
+ return;
3153
+ }
3154
+ signal?.addEventListener("abort", aborted, { once: true });
3155
+ socket.once("error", (error) => {
3156
+ const failure2 = socketErrorFailure(error);
3157
+ finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure2}.`, failure2));
3158
+ });
3159
+ socket.once("connect", () => {
3160
+ void beginExchange();
3161
+ });
3162
+ async function beginExchange() {
3163
+ try {
3164
+ if (process.platform === "win32") {
3165
+ if (pipeKey === void 0) throw new HostRelayIpcError("relay_unavailable", "Host Relay pipe key is unavailable; refusing to send any request.");
3166
+ await conductPipeClientHandshake(socket, pipeKey, RELAY_PIPE_HANDSHAKE_TIMEOUT_MS);
2618
3167
  }
3168
+ } catch (error) {
3169
+ finish(error instanceof HostRelayIpcError ? error : new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay pipe handshake failed.", "closed_early"));
3170
+ return;
2619
3171
  }
2620
- if (!complete) {
2621
- retained += 1;
2622
- break;
2623
- }
2624
- await this.host.removeEnvelope(envelope);
2625
- await this.release(this.host.submissionRefOf(envelope));
2626
- admitted += 1;
2627
- }
2628
- return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
2629
- }
2630
- async hasReservation(submissionRef2) {
2631
- return (await this.readState()).pending[submissionRef2] !== void 0;
2632
- }
2633
- async reserve(envelope) {
2634
- const state = await this.readState();
2635
- const submissionRef2 = this.host.submissionRefOf(envelope);
2636
- const existing = state.pending[submissionRef2];
2637
- if (existing !== void 0) return existing;
2638
- const drafts = await this.host.mapEnvelope(envelope);
2639
- const frames = drafts.map((draft, offset) => ({
2640
- schema_version: "ceal.host_producer_frame.v1",
2641
- producer_id: this.host.producer_id,
2642
- producer_installation_ref: this.installationRef,
2643
- producer_epoch: this.epoch,
2644
- sequence: state.next_sequence + offset,
2645
- frame_kind: draft.frame_kind,
2646
- occurred_at: this.host.createdAtOf(envelope),
2647
- payload: draft.payload
2648
- }));
2649
- await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [submissionRef2]: frames } });
2650
- return frames;
2651
- }
2652
- async release(submissionRef2) {
2653
- const state = await this.readState();
2654
- if (state.pending[submissionRef2] === void 0) return;
2655
- const pending = { ...state.pending };
2656
- delete pending[submissionRef2];
2657
- await this.writeState({ ...state, pending });
2658
- }
2659
- async readState() {
2660
- try {
2661
- const value = JSON.parse(await readFile(this.statePath, "utf8"));
2662
- return this.decodeState(value);
2663
- } catch (error) {
2664
- if (errorCode4(error) !== "ENOENT") throw error;
2665
- return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
3172
+ if (settled) return;
3173
+ socket.once("error", (error) => {
3174
+ const failure2 = socketErrorFailure(error);
3175
+ finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure2}.`, failure2));
3176
+ });
3177
+ socket.on("data", (chunk) => {
3178
+ try {
3179
+ buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
3180
+ const decoded = decode(buffer);
3181
+ if (decoded !== null) finish(null, decoded);
3182
+ } catch (error) {
3183
+ finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
3184
+ }
3185
+ });
3186
+ socket.once("end", () => {
3187
+ if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
3188
+ });
3189
+ socket.write(encoded);
2666
3190
  }
3191
+ });
3192
+ }
3193
+ function record(value) {
3194
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3195
+ }
3196
+ function decodeInvocationContextResponseBuffer(buffer) {
3197
+ const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_QUERY_RESPONSE_MAX_BYTES);
3198
+ if (value === null) return null;
3199
+ if (!validInvocationContextResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay invocation context response is invalid.");
3200
+ return value;
3201
+ }
3202
+ function encodeInvocationContextQuery(query) {
3203
+ const value = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA, ...query };
3204
+ if (!validInvocationContextQueryRequest(value)) throw new HostRelayIpcError("invalid_frame_shape");
3205
+ return encodeLengthPrefixed(value);
3206
+ }
3207
+ function validInvocationContextQueryRequest(value) {
3208
+ if (!validQueryEnvelope(value)) return false;
3209
+ try {
3210
+ decodeCealHostInvocationContextRequest({
3211
+ schema_version: CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA,
3212
+ client_nonce: value.client_nonce,
3213
+ context_expires_at: value.context_expires_at,
3214
+ host_binding: value.host_binding,
3215
+ human_message_window: value.human_message_window,
3216
+ ...value.visible_output_max_bytes === void 0 ? {} : { visible_output_max_bytes: value.visible_output_max_bytes }
3217
+ });
3218
+ return true;
3219
+ } catch {
3220
+ return false;
2667
3221
  }
2668
- async writeState(state) {
2669
- await mkdir2(this.host.outbox_directory, { recursive: true, mode: 448 });
2670
- const temporary = `${this.statePath}.${process.pid}.new`;
2671
- await writeFile(temporary, JSON.stringify(state), { mode: 384 });
2672
- await rename2(temporary, this.statePath);
2673
- }
2674
- decodeState(value) {
2675
- const label = this.host.producer_label;
2676
- const fail = (code2, message) => this.host.fail(code2, message);
2677
- if (!isDrainStateHeader(value, this.stateSchema, this.installationRef)) {
2678
- throw fail("storage", `${label} producer state header is malformed or belongs to another installation (${refusalFacts([
2679
- { name: "schema_version", value: isTranscriptRecord(value) ? value.schema_version : void 0, expected: this.stateSchema },
2680
- { name: "producer_installation_ref", value: isTranscriptRecord(value) ? value.producer_installation_ref : void 0, expected: this.installationRef }
2681
- ])}).`);
2682
- }
2683
- 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}`)}).`);
2684
- const pending = {};
2685
- const storedEpoch = Number(value.producer_epoch);
2686
- for (const [submissionRef2, frames] of Object.entries(value.pending)) {
2687
- if (!isHostSafeRef(submissionRef2) || !Array.isArray(frames) || frames.length === 0) throw fail("storage", `${label} producer pending reservation is malformed.`);
2688
- pending[submissionRef2] = frames.map((frame) => this.decodeStoredFrame(frame, storedEpoch));
2689
- }
2690
- return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: Number(value.next_sequence), pending };
2691
- }
2692
- decodeStoredFrame(value, storedEpoch) {
2693
- const label = this.host.producer_label;
2694
- const json = Buffer.from(JSON.stringify(value), "utf8");
2695
- const encoded = Buffer.allocUnsafe(json.length + 4);
2696
- encoded.writeUInt32BE(json.length, 0);
2697
- json.copy(encoded, 4);
2698
- const frame = decodeHostProducerFrame(encoded);
2699
- 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([
2700
- { name: "producer_id", value: frame.producer_id, expected: this.host.producer_id },
2701
- { name: "producer_installation_ref", value: frame.producer_installation_ref, expected: this.installationRef },
2702
- { name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${storedEpoch}` }
2703
- ])}).`);
2704
- return frame;
3222
+ }
3223
+ function validQueryEnvelope(value) {
3224
+ if (!validQueryIdentity(value, HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) || !exactKeys2(value, ["schema_version", "producer_id", "producer_installation_ref", "client_nonce", "context_expires_at", "host_binding", "human_message_window"], ["visible_output_max_bytes"])) return false;
3225
+ return typeof value.client_nonce === "string" && CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce) && typeof value.context_expires_at === "string" && (value.visible_output_max_bytes === void 0 || validVisibleOutputBudget(value.visible_output_max_bytes));
3226
+ }
3227
+ function validQueryIdentity(value, schema) {
3228
+ return record(value) && value.schema_version === schema && (value.producer_id === "codex" || value.producer_id === "claude") && safeRef(value.producer_installation_ref);
3229
+ }
3230
+ function validVisibleOutputBudget(value) {
3231
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
3232
+ }
3233
+ function validInvocationContextResponse(value) {
3234
+ if (!record(value) || value.schema_version !== HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA) return false;
3235
+ return value.status === "refused" ? validInvocationContextRefusal(value) : validReadyInvocationContext(value);
3236
+ }
3237
+ function validInvocationContextRefusal(value) {
3238
+ return exactKeys2(value, ["schema_version", "status", "reason"]) && isCealHostAuthenticationCause(value.reason) && value.reason !== "no_binding";
3239
+ }
3240
+ function validReadyInvocationContext(value) {
3241
+ if (value.status !== "ready" || !exactKeys2(value, ["schema_version", "status", "request_ref", "result"]) || !safeRef(value.request_ref) || !value.request_ref.startsWith("ceal-request:") || !record(value.result)) return false;
3242
+ const result = value.result;
3243
+ return exactKeys2(result, ["schema_version", "context_token", "expires_at"]) && result.schema_version === CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA && typeof result.context_token === "string" && CEAL_HOST_CONTEXT_TOKEN_PATTERN.test(result.context_token) && typeof result.expires_at === "string" && Number.isFinite(Date.parse(result.expires_at));
3244
+ }
3245
+ function decodeLengthPrefixedJson(input, maximum) {
3246
+ if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
3247
+ const length = input.readUInt32BE(0);
3248
+ 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}`)}.`);
3249
+ if (input.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
3250
+ 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}` }])}.`);
3251
+ try {
3252
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
3253
+ } catch {
3254
+ throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC message is invalid.");
2705
3255
  }
2706
- };
2707
- function isDrainStateHeader(value, schema, installationRef) {
2708
- 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);
3256
+ }
3257
+ function encodeLengthPrefixed(value) {
3258
+ const body = Buffer.from(JSON.stringify(value), "utf8");
3259
+ const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
3260
+ output.writeUInt32BE(body.length, 0);
3261
+ body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
3262
+ return output;
3263
+ }
3264
+ function exactKeys2(value, required, optional = []) {
3265
+ const keys = Object.keys(value);
3266
+ return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
3267
+ }
3268
+ function safeRef(value) {
3269
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
2709
3270
  }
2710
3271
  function errorCode4(error) {
2711
- return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
3272
+ return record(error) && typeof error.code === "string" ? error.code : null;
2712
3273
  }
2713
3274
 
2714
- // host/relay/src/relay-quarantine-store.ts
2715
- var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
2716
- var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
2717
- var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
3275
+ // host/service-log.ts
3276
+ import { copyFile, mkdir as mkdir2, stat, truncate, appendFile } from "node:fs/promises";
3277
+ import { dirname as dirname4, isAbsolute, posix, win32 as win322 } from "node:path";
3278
+ var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
3279
+ function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
3280
+ 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 } : {} })}
3281
+ `;
3282
+ try {
3283
+ (sink ?? defaultHostServiceDiagnosticSink)(line);
3284
+ } catch {
3285
+ }
3286
+ }
3287
+ function defaultHostServiceDiagnosticSink(line) {
3288
+ writeHostServiceLogLine(line);
3289
+ process.stderr.write(line);
3290
+ }
3291
+ var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
3292
+ function writeHostServiceLogLine(line, environment = process.env) {
3293
+ if (process.platform !== "win32") return;
3294
+ const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
3295
+ if (path === void 0 || !win322.isAbsolute(path) || win322.normalize(path) !== path) return;
3296
+ void appendHostServiceLogLine(path, line).catch(() => void 0);
3297
+ }
3298
+ async function appendHostServiceLogLine(path, line) {
3299
+ if (!isAbsolute(path)) return;
3300
+ await ensureHostServiceLogDirectory(path);
3301
+ await appendFile(path, line);
3302
+ }
3303
+ var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
3304
+ var hostServiceLogDirectories = /* @__PURE__ */ new Map();
3305
+ function ensureHostServiceLogDirectory(path) {
3306
+ const directory = dirname4(path);
3307
+ const existing = hostServiceLogDirectories.get(directory);
3308
+ if (existing !== void 0) return existing;
3309
+ const pending = mkdir2(directory, { recursive: true, mode: 448 }).then(() => void 0).catch((error) => {
3310
+ hostServiceLogDirectories.delete(directory);
3311
+ throw error;
3312
+ });
3313
+ hostServiceLogDirectories.set(directory, pending);
3314
+ return pending;
3315
+ }
2718
3316
 
2719
- // host/relay/src/relay-admission-store.ts
2720
- var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
2721
- var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
2722
- var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
3317
+ // host/local-capability-descriptor.ts
3318
+ var HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX = Object.freeze(["authenticate", "--client-nonce"]);
2723
3319
 
2724
3320
  // host/invocation-authentication-command.ts
2725
- var HOST_INVOCATION_AUTHENTICATION_COMMAND_PREFIX = ["authenticate", "--client-nonce"];
2726
- var HOST_AUTHENTICATION_BUDGET_ENVIRONMENT = "CEAL_HOST_AUTHENTICATION_BUDGET_MS";
2727
3321
  var HOST_CONTEXT_ACTIVATION_WAIT_MAX_MS = 2e4;
2728
3322
  var CLAUDE_CONTEXT_ACTIVATION_WAIT_MAX_MS = HOST_CONTEXT_ACTIVATION_WAIT_MAX_MS;
2729
3323
  var HOST_AUTHENTICATION_COMPLETION_RESERVE_MS = 5e3;
2730
3324
  var INVOCATION_CONTEXT_POLL_INTERVAL_MS = 250;
2731
3325
  var REFUSAL_REASON_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
2732
3326
  function installedHostInvocationAuthenticationCommandOptions(config, contextBudgetMs, maxContextWaitMs) {
2733
- const port = new HostRelayInvocationContextPort(config.relay_endpoint);
3327
+ let pipeKey;
3328
+ const loadPipeKey = () => {
3329
+ if (pipeKey === void 0) pipeKey = process.platform === "win32" ? loadRelayPipeKey(relayPipeStateRootForOutboxDir(config.outbox_dir)) : Promise.resolve(void 0);
3330
+ return pipeKey;
3331
+ };
2734
3332
  return {
2735
3333
  host_source_ref: config.host_source_ref,
2736
3334
  outbox_dir: config.outbox_dir,
2737
3335
  producer_installation_ref: config.producer_installation_ref,
2738
3336
  context_activation_wait_ms: boundedContextActivationWait(contextBudgetMs, config.ipc_timeout_ms, maxContextWaitMs),
2739
- mint_invocation_context: (input) => port.mintInvocationContext({ ...input, producer_id: input.producer_id })
3337
+ mint_invocation_context: async (input) => new HostRelayInvocationContextPort(config.relay_endpoint, await loadPipeKey(), config.ipc_timeout_ms).mintInvocationContext(input)
2740
3338
  };
2741
3339
  }
2742
3340
  async function runHostInvocationAuthenticationCommand(descriptor, args, options) {
@@ -2757,26 +3355,31 @@ function authenticationRefusalReason(error) {
2757
3355
  return isCealHostAuthenticationCause(message) ? message : "host_refusal_invalid";
2758
3356
  }
2759
3357
  async function authenticateHostInvocation(descriptor, input, options) {
2760
- if (input.length !== HOST_INVOCATION_AUTHENTICATION_COMMAND_PREFIX.length + 1 || input[0] !== HOST_INVOCATION_AUTHENTICATION_COMMAND_PREFIX[0] || input[1] !== HOST_INVOCATION_AUTHENTICATION_COMMAND_PREFIX[1]) throw new TypeError("invalid_arguments");
3358
+ if (input.length !== HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX.length + 1 || input[0] !== HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX[0] || input[1] !== HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX[1]) throw new TypeError("invalid_arguments");
2761
3359
  const request2 = decodeCealHostInvocationAuthenticationRequest({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA, client_nonce: input[2] });
2762
3360
  const config = validateOptions(options);
2763
3361
  const now = config.now ?? (() => /* @__PURE__ */ new Date());
2764
3362
  const { store, current } = await currentInvocation(descriptor, config, now);
2765
3363
  if (config.mint_invocation_context === void 0) throw new TypeError("relay_failed");
2766
- const query = {
2767
- producer_id: descriptor.producer_id,
2768
- producer_installation_ref: config.producer_installation_ref,
2769
- local_session_ref: current.identity.host_session_ref,
2770
- local_turn_ref: current.identity.host_turn_ref,
2771
- client_nonce: request2.client_nonce,
2772
- ...await visibleOutputBudget(descriptor)
2773
- };
3364
+ const query = await invocationMintQuery(descriptor, store, current, request2.client_nonce, config);
2774
3365
  const response2 = await mintedResult(config.mint_invocation_context, query);
2775
3366
  const completedAt = now();
2776
3367
  const rechecked = await store.readInvocationContext(current.identity.host_source_ref, current.identity.host_session_ref, completedAt);
2777
3368
  if (rechecked === null || !sameTurn(current, rechecked)) throw new TypeError("context_replaced");
2778
3369
  return decodeCealHostInvocationAuthenticationResult(response2.result, completedAt);
2779
3370
  }
3371
+ async function invocationMintQuery(descriptor, store, current, clientNonce, options) {
3372
+ const readWindow = options.read_human_message_window?.(current.identity) ?? descriptor.read_human_message_window?.({ store, identity: current.identity }) ?? Promise.resolve({ messages: [], truncated: false });
3373
+ return {
3374
+ producer_id: descriptor.producer_id,
3375
+ producer_installation_ref: options.producer_installation_ref,
3376
+ client_nonce: clientNonce,
3377
+ context_expires_at: current.expires_at,
3378
+ host_binding: { ...current.identity },
3379
+ human_message_window: await readWindow,
3380
+ ...await visibleOutputBudget(descriptor)
3381
+ };
3382
+ }
2780
3383
  async function visibleOutputBudget(descriptor) {
2781
3384
  const bytes = await descriptor.resolve_visible_output_max_bytes?.();
2782
3385
  return bytes === void 0 ? {} : { visible_output_max_bytes: bytes };
@@ -2786,30 +3389,15 @@ async function mintedResult(mint, query) {
2786
3389
  try {
2787
3390
  response2 = await mint(query);
2788
3391
  } catch (error) {
2789
- throw new TypeError(exchangeFailureCause(hostRelayExchangeFailure(error) ?? "unavailable"));
3392
+ throw new TypeError(cealHostAuthenticationCauseForExchangeFailure(hostRelayExchangeFailure(error) ?? "unavailable"));
2790
3393
  }
2791
- if (response2.status === "refused") throw new TypeError(mintRefusalCause(response2.reason));
2792
- if (response2.status !== "ready") throw new TypeError("no_binding");
3394
+ if (response2.status !== "ready") throw new TypeError(isCealHostAuthenticationCause(response2.reason) && response2.reason !== "no_binding" ? response2.reason : "host_refusal_invalid");
2793
3395
  return response2;
2794
3396
  }
2795
- function mintRefusalCause(reason) {
2796
- if (isDirectMintRefusalCause(reason)) return reason;
2797
- if (reason === "deadline" || reason === "cancelled") return "transient_retryable";
2798
- if (reason === "mint_failed" || reason === "relay_failed") return reason;
2799
- return isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, reason) ? "context_stale" : "host_refusal_invalid";
2800
- }
2801
- function isDirectMintRefusalCause(reason) {
2802
- return isVocabularyWord(HOST_RELAY_DIRECT_WIRE_MINT_REASONS, reason);
2803
- }
2804
- function exchangeFailureCause(failure2) {
2805
- if (failure2 === "permission_denied") return "relay_permission_denied";
2806
- if (failure2 === "timed_out") return "transient_retryable";
2807
- return "relay_unavailable";
2808
- }
2809
3397
  async function currentInvocation(descriptor, config, now) {
2810
3398
  const store = descriptor.create_store(config.outbox_dir);
2811
3399
  const identity = resolveIdentity(descriptor, config);
2812
- if (identity.status !== "ready") throw new TypeError(identity.status);
3400
+ if (identity.status === "session_unavailable") throw new TypeError("no_binding");
2813
3401
  const waitBudgetMs = config.context_activation_wait_ms ?? 0;
2814
3402
  const monotonicNow = config.monotonic_now ?? (() => performance.now());
2815
3403
  const deadline = monotonicNow() + waitBudgetMs;
@@ -2873,9 +3461,9 @@ async function resolveOptions(descriptor, args, options) {
2873
3461
  };
2874
3462
  }
2875
3463
  function authenticationBudgetMsFromEnvironment() {
2876
- const value = process.env[HOST_AUTHENTICATION_BUDGET_ENVIRONMENT];
3464
+ const value = process.env[CEAL_HOST_AUTHENTICATION_BUDGET_ENVIRONMENT];
2877
3465
  if (value === void 0) return 0;
2878
- if (!/^(?:0|[1-9][0-9]{0,8})$/u.test(value)) throw new TypeError("invalid_arguments");
3466
+ if (!CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN.test(value)) throw new TypeError("invalid_arguments");
2879
3467
  return Number(value);
2880
3468
  }
2881
3469
  function boundedContextActivationWait(contextBudgetMs, relayTimeoutMs, maxContextWaitMs) {
@@ -2912,16 +3500,16 @@ function writeResult(value, sink) {
2912
3500
  }
2913
3501
 
2914
3502
  // host/producer-service.ts
2915
- import { posix as posix3, win32 as win323 } from "node:path";
3503
+ import { posix as posix4, win32 as win325 } from "node:path";
2916
3504
 
2917
3505
  // host/runtime-source-paths.ts
2918
- import { createHash as createHash3 } from "node:crypto";
2919
- import { posix as posix2, win32 as win322 } from "node:path";
3506
+ import { createHash as createHash4 } from "node:crypto";
3507
+ import { posix as posix3, win32 as win324 } from "node:path";
2920
3508
 
2921
3509
  // host/absolute-path.ts
2922
- import { posix, win32 } from "node:path";
3510
+ import { posix as posix2, win32 as win323 } from "node:path";
2923
3511
  function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allowRoot = false) {
2924
- const pathApi = flavor === "win32" || flavor === "windows" ? win32 : posix;
3512
+ const pathApi = flavor === "win32" || flavor === "windows" ? win323 : posix2;
2925
3513
  return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
2926
3514
  }
2927
3515
 
@@ -2933,28 +3521,23 @@ function hostSourceUuid(sourceRef) {
2933
3521
  function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
2934
3522
  const sourceId = hostSourceUuid(sourceRef);
2935
3523
  if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
2936
- const path = platform === "win32" ? win322 : posix2;
3524
+ const path = platform === "win32" ? win324 : posix3;
2937
3525
  const sourcesRoot = path.join(stateRoot, "sources");
2938
3526
  const sourceRoot = path.join(sourcesRoot, sourceId);
2939
3527
  const runtimeRoot = path.join(stateRoot, "run");
2940
- const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash3("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
2941
- assertHostRuntimeIpcLength(endpoint, platform);
3528
+ const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash4("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
2942
3529
  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 };
2943
3530
  }
2944
- function assertHostRuntimeIpcLength(endpoint, platform) {
2945
- if (platform === "win32") return;
2946
- const maximum = platform === "linux" ? 107 : 103;
2947
- 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" });
2948
- }
2949
3531
  function assertHostProducerSourcePaths(stateRoot, config, producer, platform = process.platform) {
2950
3532
  const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
2951
- 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.");
3533
+ 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.");
3534
+ return paths.endpoint;
2952
3535
  }
2953
3536
 
2954
3537
  // host/producer-service.ts
2955
- import { constants } from "node:fs";
2956
- import { lstat as lstat2, open as open3 } from "node:fs/promises";
2957
- var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3538
+ import { constants as constants3 } from "node:fs";
3539
+ import { lstat as lstat3 } from "node:fs/promises";
3540
+ var SAFE_REF2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2958
3541
  var HOST_PRODUCER_CONFIG_FLAG = "--ceal-host-config";
2959
3542
  var HostProducerServiceError = class extends Error {
2960
3543
  code;
@@ -2965,18 +3548,19 @@ var HostProducerServiceError = class extends Error {
2965
3548
  }
2966
3549
  };
2967
3550
  var HOST_PRODUCER_CONFIG_KEYS = ["schema_version", "host_source_ref", "producer_installation_ref", "producer_epoch", "outbox_dir", "relay_endpoint"];
3551
+ var HOST_PRODUCER_CONFIG_ENDPOINT_KEY = "relay_endpoint";
2968
3552
  var HOST_PRODUCER_TIMING_DEFAULTS = Object.freeze({ reconcile_interval_ms: 250, retry_backoff_ms: 1e3, ipc_timeout_ms: 5e3 });
2969
3553
  function hostProducerExtraPath(extras, key) {
2970
3554
  const value = extras[key];
2971
- if (value === void 0) throw hostProducerError("invalid_config");
3555
+ if (value === void 0) throw hostProducerError("invalid_config", refusalFact(key, "missing", "an absolute path"));
2972
3556
  return value;
2973
3557
  }
2974
3558
  function parseHostProducerConfigFlag(args, platform = process.platform) {
2975
3559
  return parseProducerHostConfigFlag(args, HOST_PRODUCER_CONFIG_FLAG, platform, hostProducerError);
2976
3560
  }
2977
- async function readHostProducerConfig(descriptor, configPath, platform = process.platform) {
2978
- const config = decodeHostProducerConfigText(descriptor, await readProducerConfigText(configPath, platform, hostProducerError), platform);
2979
- const path = platform === "win32" ? win323 : posix3;
3561
+ async function readHostProducerConfig(descriptor, configPath, platform = process.platform, custody = {}) {
3562
+ const config = decodeHostProducerConfigText(descriptor, await readProducerConfigText(configPath, platform, hostProducerError, custody), platform);
3563
+ const path = platform === "win32" ? win325 : posix4;
2980
3564
  const root = path.resolve(config.outbox_dir, "../../../../..");
2981
3565
  try {
2982
3566
  assertHostProducerSourcePaths(root, config, descriptor.producer_id, platform);
@@ -3002,15 +3586,31 @@ function parseProducerHostConfigFlag(args, flag, platform, error) {
3002
3586
  assertProducerAbsolutePath(value, platform, "config", error);
3003
3587
  return value;
3004
3588
  }
3005
- async function readProducerConfigText(configPath, platform, error) {
3006
- assertProducerAbsolutePath(configPath, platform, "config", error);
3589
+ async function readProducerConfigText(configPath, platform, error, custody) {
3590
+ assertProducerAbsolutePath(configPath, process.platform, "config", error);
3591
+ if (platform === "win32") {
3592
+ let bytes;
3593
+ try {
3594
+ bytes = await readPrivateFileWithSecurefs(configPath, CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES, custody);
3595
+ } catch (cause) {
3596
+ if (cause instanceof SecureFileCustodyError && cause.reason === "not_private") throw error("config_not_private");
3597
+ 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}`));
3598
+ if (cause instanceof SecureFileCustodyError && cause.reason === "reparse_point") throw error("invalid_config");
3599
+ throw error("config_unavailable");
3600
+ }
3601
+ if (bytes.byteLength === 0) throw error("invalid_config", refusalFact("config_bytes", 0, `between 1 and ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
3602
+ try {
3603
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
3604
+ } catch {
3605
+ throw error("invalid_config");
3606
+ }
3607
+ }
3007
3608
  const pathStat = await configPathStat(configPath, error);
3008
3609
  validateConfigFile(pathStat, platform, error);
3009
- const noFollow = typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
3010
- const handle = await openConfigFile(configPath, constants.O_RDONLY | noFollow, error);
3610
+ const handle = await openConfigFile(configPath, platform, error, custody);
3011
3611
  try {
3012
3612
  const opened = await handle.stat();
3013
- validateConfigFile(opened, platform, error);
3613
+ validateConfigFile(opened, platform, error, handle);
3014
3614
  validateOpenedFile(opened, pathStat, platform, error);
3015
3615
  return await readBoundedConfig(handle, opened.size, error);
3016
3616
  } finally {
@@ -3018,8 +3618,9 @@ async function readProducerConfigText(configPath, platform, error) {
3018
3618
  }
3019
3619
  }
3020
3620
  function decodeProducerConfigCore(text, options) {
3621
+ const ownership = producerConfigKeyOwnership(options.keys, options.platform);
3021
3622
  const value = parseCanonicalJson(text, options.error);
3022
- if (!isRecord(value) || !hasConfigKeys(value, options.keys) || value.schema_version !== options.schema) throw options.error("invalid_config");
3623
+ if (!isRecord3(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 })));
3023
3624
  const hostSourceRef = typeof value.host_source_ref === "string" ? value.host_source_ref : "";
3024
3625
  const installationRef = typeof value.producer_installation_ref === "string" ? value.producer_installation_ref : "";
3025
3626
  const producerEpoch = validateIdentity(hostSourceRef, installationRef, value.producer_epoch, options.error);
@@ -3037,14 +3638,21 @@ function decodeProducerConfigCore(text, options) {
3037
3638
  producer_installation_ref: installationRef,
3038
3639
  producer_epoch: producerEpoch,
3039
3640
  outbox_dir: value.outbox_dir,
3040
- relay_endpoint: value.relay_endpoint,
3641
+ relay_endpoint: producerConfigEndpoint(value),
3041
3642
  reconcile_interval_ms: intervals[0],
3042
3643
  retry_backoff_ms: intervals[1],
3043
3644
  ipc_timeout_ms: intervals[2]
3044
3645
  };
3045
3646
  }
3647
+ function producerConfigKeyOwnership(keys, platform) {
3648
+ if (platform !== "win32") return { required: keys, tolerated: [] };
3649
+ return { required: keys.filter((key) => key !== HOST_PRODUCER_CONFIG_ENDPOINT_KEY), tolerated: [HOST_PRODUCER_CONFIG_ENDPOINT_KEY] };
3650
+ }
3651
+ function producerConfigEndpoint(value) {
3652
+ return typeof value.relay_endpoint === "string" ? value.relay_endpoint : "";
3653
+ }
3046
3654
  function assertProducerRelayEndpoint(value, platform, error) {
3047
- if (platform === "win32" && typeof value === "string" && /^\\\\\.\\pipe\\[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(value)) return;
3655
+ if (platform === "win32") return;
3048
3656
  assertProducerAbsolutePath(value, platform, "runtime", error);
3049
3657
  }
3050
3658
  function assertProducerAbsolutePath(value, platform, kind, error) {
@@ -3052,26 +3660,27 @@ function assertProducerAbsolutePath(value, platform, kind, error) {
3052
3660
  }
3053
3661
  async function configPathStat(configPath, error) {
3054
3662
  try {
3055
- return await lstat2(configPath);
3663
+ return await lstat3(configPath);
3056
3664
  } catch {
3057
3665
  throw error("config_unavailable");
3058
3666
  }
3059
3667
  }
3060
- function validateConfigFile(value, platform, error) {
3668
+ function validateConfigFile(value, platform, error, handle) {
3061
3669
  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" }]));
3062
3670
  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}`));
3063
- 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" }]));
3671
+ 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" }]));
3064
3672
  }
3065
- async function openConfigFile(configPath, flags, error) {
3673
+ async function openConfigFile(configPath, platform, error, custody) {
3066
3674
  try {
3067
- return await open3(configPath, flags);
3068
- } catch {
3675
+ return await openNoFollow(configPath, constants3.O_RDONLY, void 0, platform, custody);
3676
+ } catch (error_) {
3677
+ if (error_ instanceof SecureFileCustodyError) throw error(error_.reason === "not_private" ? "config_not_private" : "invalid_config");
3069
3678
  throw error("config_unavailable");
3070
3679
  }
3071
3680
  }
3072
3681
  function validateOpenedFile(opened, pathStat, platform, error) {
3073
3682
  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" }]));
3074
- 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) }]));
3683
+ 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) }]));
3075
3684
  }
3076
3685
  async function readBoundedConfig(handle, expectedBytes, error) {
3077
3686
  const buffer = Buffer.allocUnsafe(expectedBytes + 1);
@@ -3103,29 +3712,54 @@ function firstDifferenceIndex(left, right) {
3103
3712
  for (let index = 0; index < shared; index += 1) if (left[index] !== right[index]) return index;
3104
3713
  return shared;
3105
3714
  }
3715
+ function producerShapeFacts(value, options) {
3716
+ if (!isRecord3(value)) return [{ name: "config", value: "wrong_type", expected: "a record" }];
3717
+ const tolerated = options.tolerated ?? [];
3718
+ const allowed = [...options.keys, ...tolerated, ...Object.keys(HOST_PRODUCER_TIMING_DEFAULTS)];
3719
+ const facts = [
3720
+ ...Object.keys(value).filter((key) => !allowed.includes(key)).map((key) => ({ name: key, value: "unknown", expected: `one of ${[...options.keys, ...tolerated].join(", ")}` })),
3721
+ ...options.keys.filter((key) => !Object.hasOwn(value, key)).map((key) => ({ name: key, value: "missing", expected: "present" }))
3722
+ ];
3723
+ if (Object.hasOwn(value, "schema_version") && value.schema_version !== options.schema) facts.push({ name: "schema_version", value: "wrong_type", expected: options.schema });
3724
+ return facts;
3725
+ }
3106
3726
  function validateIdentity(sourceRef, installationRef, epoch, error) {
3107
- if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(installationRef) || !positive2(epoch, 2147483647)) throw error("invalid_config");
3727
+ if (!SAFE_REF2.test(sourceRef) || !SAFE_REF2.test(installationRef) || !positive(epoch, 2147483647)) throw error("invalid_config", refusalFacts(producerIdentityFacts(sourceRef, installationRef, epoch)));
3108
3728
  return epoch;
3109
3729
  }
3730
+ function producerIdentityFacts(sourceRef, installationRef, epoch) {
3731
+ const facts = [];
3732
+ if (!SAFE_REF2.test(sourceRef)) facts.push({ name: "host_source_ref", value: "wrong_type", expected: "a safe reference" });
3733
+ if (!SAFE_REF2.test(installationRef)) facts.push({ name: "producer_installation_ref", value: "wrong_type", expected: "a safe reference" });
3734
+ if (!positive(epoch, 2147483647)) facts.push({ name: "producer_epoch", value: "wrong_type", expected: "a positive integer" });
3735
+ return facts;
3736
+ }
3110
3737
  function validateIntervals(reconcile, retry, timeout, error) {
3111
- if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config");
3738
+ if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config", refusalFacts(producerIntervalFacts(reconcile, retry, timeout)));
3112
3739
  return [reconcile, retry, timeout];
3113
3740
  }
3114
- function hasConfigKeys(value, required) {
3115
- return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || Object.hasOwn(HOST_PRODUCER_TIMING_DEFAULTS, key));
3741
+ function producerIntervalFacts(reconcile, retry, timeout) {
3742
+ const facts = [];
3743
+ if (!boundedMilliseconds(reconcile)) facts.push({ name: "reconcile_interval_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
3744
+ if (!boundedMilliseconds(retry)) facts.push({ name: "retry_backoff_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
3745
+ if (!boundedMilliseconds(timeout)) facts.push({ name: "ipc_timeout_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
3746
+ return facts;
3116
3747
  }
3117
- function isRecord(value) {
3748
+ function hasConfigKeys(value, required, tolerated = []) {
3749
+ 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));
3750
+ }
3751
+ function isRecord3(value) {
3118
3752
  return value !== null && typeof value === "object" && !Array.isArray(value);
3119
3753
  }
3120
- function positive2(value, maximum) {
3754
+ function positive(value, maximum) {
3121
3755
  return Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= maximum;
3122
3756
  }
3123
3757
  function boundedMilliseconds(value) {
3124
- return positive2(value, 6e4) && Number(value) >= 25;
3758
+ return positive(value, 6e4) && Number(value) >= 25;
3125
3759
  }
3126
3760
 
3127
3761
  // host/claude/src/claude-decoder.ts
3128
- import { createHash as createHash4 } from "node:crypto";
3762
+ import { createHash as createHash5 } from "node:crypto";
3129
3763
 
3130
3764
  // host/claude/src/claude-types.ts
3131
3765
  var CLAUDE_SESSION_ID_ENVIRONMENT = "CLAUDE_CODE_SESSION_ID";
@@ -3156,6 +3790,76 @@ var ClaudeHostError = class extends Error {
3156
3790
 
3157
3791
  // host/claude/src/claude-decoder.ts
3158
3792
  var MAX_HOOK_BYTES = 256 * 1024;
3793
+ function decodeClaudeQueuedPromptRow(row, sessionId) {
3794
+ if (!record2(row)) return null;
3795
+ if (row.type === "user") return decodeArrayTextPrompt(row, sessionId);
3796
+ if (row.type === "attachment") return decodeQueuedCommandPrompt(row, sessionId);
3797
+ if (row.type === "queue-operation") return decodeQueueEnqueuePrompt(row, sessionId);
3798
+ return null;
3799
+ }
3800
+ function decodeClaudeHumanMessageRow(row, sessionId) {
3801
+ if (!isClaudeNativeHumanMessageRow(row)) return decodeClaudeQueuedPromptRow(row, sessionId);
3802
+ return decodeClaudeNativeHumanMessage(row, sessionId);
3803
+ }
3804
+ function isClaudeNativeHumanMessageRow(row) {
3805
+ return record2(row) && row.type === "user" && record2(row.message) && typeof row.message.content === "string";
3806
+ }
3807
+ function decodeClaudeNativeHumanMessage(row, sessionId) {
3808
+ if (!sessionMatches(row, sessionId)) return null;
3809
+ if (row.isMeta === true && (!record2(row.origin) || typeof row.origin.kind !== "string")) return null;
3810
+ const identity = queuedRowIdentity(row);
3811
+ if (identity === null) throw new ClaudeHostError("payload_rejected", "Claude transcript human message identity is malformed.");
3812
+ const message = row.message;
3813
+ if (!record2(message) || typeof message.content !== "string") return null;
3814
+ return { turnId: identity.uuid, timestamp: identity.timestamp, content: message.content };
3815
+ }
3816
+ function decodeArrayTextPrompt(row, sessionId) {
3817
+ if (!record2(row.message) || !Array.isArray(row.message.content)) return null;
3818
+ if (!sessionMatches(row, sessionId)) return null;
3819
+ if (row.isMeta === true && (!record2(row.origin) || typeof row.origin.kind !== "string")) return null;
3820
+ const texts = arrayTextBlocks(row.message.content);
3821
+ if (texts === null) return null;
3822
+ const identity = queuedRowIdentity(row);
3823
+ if (identity === null) return null;
3824
+ return { turnId: identity.uuid, timestamp: identity.timestamp, content: texts.join("") };
3825
+ }
3826
+ function arrayTextBlocks(content) {
3827
+ const texts = [];
3828
+ for (const block of content) {
3829
+ if (!record2(block) || block.type !== "text" || typeof block.text !== "string") return null;
3830
+ texts.push(block.text);
3831
+ }
3832
+ return texts.length === 0 ? null : texts;
3833
+ }
3834
+ function decodeQueuedCommandPrompt(row, sessionId) {
3835
+ if (!record2(row.attachment) || row.attachment.type !== "queued_command" || row.attachment.commandMode !== "prompt") return null;
3836
+ if (typeof row.attachment.prompt !== "string" || row.attachment.prompt.length === 0) return null;
3837
+ if (!sessionMatches(row, sessionId)) return null;
3838
+ const identity = queuedRowIdentity(row);
3839
+ if (identity === null) return null;
3840
+ return { turnId: identity.uuid, timestamp: identity.timestamp, content: row.attachment.prompt };
3841
+ }
3842
+ function decodeQueueEnqueuePrompt(row, sessionId) {
3843
+ if (row.operation !== "enqueue" || typeof row.content !== "string" || row.content.length === 0) return null;
3844
+ if (!sessionMatches(row, sessionId)) return null;
3845
+ if (typeof row.timestamp !== "string" || !Number.isFinite(Date.parse(row.timestamp))) return null;
3846
+ return { turnId: queueEnqueueTurnId(sessionId, row.timestamp, row.content), timestamp: row.timestamp, content: row.content };
3847
+ }
3848
+ function sessionMatches(row, sessionId) {
3849
+ const hasSessionId = Object.hasOwn(row, "sessionId");
3850
+ const hasSnakeSessionId = Object.hasOwn(row, "session_id");
3851
+ return (hasSessionId || hasSnakeSessionId) && (!hasSessionId || row.sessionId === sessionId) && (!hasSnakeSessionId || row.session_id === sessionId);
3852
+ }
3853
+ function queuedRowIdentity(row) {
3854
+ if (typeof row.uuid !== "string" || row.uuid.length === 0 || row.uuid.length > 256) return null;
3855
+ if (typeof row.timestamp !== "string" || !Number.isFinite(Date.parse(row.timestamp))) return null;
3856
+ return { uuid: row.uuid, timestamp: row.timestamp };
3857
+ }
3858
+ function queueEnqueueTurnId(sessionId, timestamp3, content) {
3859
+ return `queue-enqueue:${createHash5("sha256").update(`${sessionId}
3860
+ ${timestamp3}
3861
+ ${content}`, "utf8").digest("hex").slice(0, 32)}`;
3862
+ }
3159
3863
  function claudeLocalSessionRef(sessionId) {
3160
3864
  return `claude-session:${digest(sessionId)}`;
3161
3865
  }
@@ -3165,25 +3869,25 @@ function claudeLocalTurnRef(sessionId, turnId) {
3165
3869
  function claudeLocalInputRef(sessionId, turnId) {
3166
3870
  return `claude-input:${cealRequestSha256({ session_id: sessionId, prompt_id: turnId })}`;
3167
3871
  }
3168
- function claudeWorkspaceRef(cwd) {
3169
- return `workspace:${digest(cwd)}`;
3170
- }
3171
3872
  function digest(value) {
3172
- return createHash4("sha256").update(value, "utf8").digest("hex");
3873
+ return createHash5("sha256").update(value, "utf8").digest("hex");
3874
+ }
3875
+ function record2(value) {
3876
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3173
3877
  }
3174
3878
 
3175
3879
  // host/claude/src/claude-outbox.ts
3176
- import { mkdir as mkdir3, readdir, readFile as readFile2, unlink as unlink2 } from "node:fs/promises";
3177
- import { isAbsolute, join as join3 } from "node:path";
3880
+ import { mkdir as mkdir3, readdir, readFile, unlink as unlink3 } from "node:fs/promises";
3881
+ import { isAbsolute as isAbsolute2, join as join3 } from "node:path";
3178
3882
 
3179
3883
  // host/invocation-context.ts
3180
- var HOST_INVOCATION_IDENTITY_KEYS = ["host_session_ref", "host_turn_ref", "host_source_ref"];
3884
+ var HOST_INVOCATION_TURN_SELECTION_SCHEMA = "ceal.host_invocation_turn_selection.v1";
3181
3885
  var HOST_INVOCATION_CONTEXT_REQUIRED_KEYS = ["schema_version", "identity", "prompt_ref", "expires_at"];
3182
3886
  function invocationContextKey(identity) {
3183
3887
  return cealRequestSha256({ host_source_ref: identity.host_source_ref, host_session_ref: identity.host_session_ref });
3184
3888
  }
3185
3889
  function hasHostInvocationIdentityKeys(value) {
3186
- return exactKeys4(value, HOST_INVOCATION_IDENTITY_KEYS);
3890
+ return exactKeys3(value, CEAL_HOST_INVOCATION_IDENTITY_KEYS);
3187
3891
  }
3188
3892
  function hasHostInvocationContextKeys(value, optional = []) {
3189
3893
  return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
@@ -3209,12 +3913,9 @@ function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor) {
3209
3913
  return Object.freeze(context);
3210
3914
  }
3211
3915
  function isHostInvocationTurnSelection(value) {
3212
- 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) => safeRef3(value[key]));
3213
- }
3214
- function invocationTurnSelectionMatches(context, selection) {
3215
- 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;
3916
+ return isRecord4(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) => safeRef2(value[key]));
3216
3917
  }
3217
- function exactKeys4(value, keys) {
3918
+ function exactKeys3(value, keys) {
3218
3919
  const actual = Object.keys(value);
3219
3920
  return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
3220
3921
  }
@@ -3223,10 +3924,10 @@ function requiredKeys(value, required, optional) {
3223
3924
  const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
3224
3925
  return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
3225
3926
  }
3226
- function isRecord2(value) {
3927
+ function isRecord4(value) {
3227
3928
  return value !== null && typeof value === "object" && !Array.isArray(value);
3228
3929
  }
3229
- function safeRef3(value) {
3930
+ function safeRef2(value) {
3230
3931
  return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
3231
3932
  }
3232
3933
 
@@ -3241,34 +3942,30 @@ function validateClaudeHostInvocationContext(value) {
3241
3942
  validateContextTimes(value);
3242
3943
  }
3243
3944
  function contextShape(value) {
3244
- return record3(value) && value.schema_version === CLAUDE_INVOCATION_CONTEXT_SCHEMA_VERSION && hasHostInvocationContextKeys(value, ["request_ref", "bound_at", "working_directory", "turn_generation_ref"]);
3945
+ return record3(value) && value.schema_version === CLAUDE_INVOCATION_CONTEXT_SCHEMA_VERSION && hasHostInvocationContextKeys(value, ["working_directory", "turn_generation_ref"]);
3245
3946
  }
3246
3947
  function validateIdentity2(value) {
3247
3948
  if (!validIdentity(value)) throw new ClaudeHostError("storage", "Claude Host invocation identity is malformed.");
3248
3949
  }
3249
3950
  function validateContextReferences(value) {
3250
- if (!safeRef4(value.prompt_ref) || !optionalSafeRef(value.request_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new ClaudeHostError("storage", "Claude Host invocation context is malformed.");
3951
+ if (!safeRef3(value.prompt_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new ClaudeHostError("storage", "Claude Host invocation context is malformed.");
3251
3952
  }
3252
3953
  function validateContextTimes(value) {
3253
- if (!timestamp2(value.expires_at) || !optionalTimestamp(value.bound_at) || !optionalDirectory(value.working_directory)) throw new ClaudeHostError("storage", "Claude Host invocation context is malformed.");
3254
- if (typeof value.bound_at === "string" && Date.parse(value.bound_at) > Date.parse(String(value.expires_at))) throw new ClaudeHostError("storage", `Claude Host invocation context has invalid binding time (${refusalFact("bound_at", value.bound_at, `at or before ${String(value.expires_at)}`)}).`);
3954
+ if (!timestamp(value.expires_at) || !optionalDirectory(value.working_directory)) throw new ClaudeHostError("storage", "Claude Host invocation context is malformed.");
3255
3955
  }
3256
3956
  function validIdentity(value) {
3257
- return record3(value) && hasHostInvocationIdentityKeys(value) && [value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef4);
3957
+ return record3(value) && hasHostInvocationIdentityKeys(value) && [value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef3);
3258
3958
  }
3259
3959
  function optionalSafeRef(value) {
3260
- return value === void 0 || safeRef4(value);
3261
- }
3262
- function optionalTimestamp(value) {
3263
- return value === void 0 || timestamp2(value);
3960
+ return value === void 0 || safeRef3(value);
3264
3961
  }
3265
3962
  function optionalDirectory(value) {
3266
3963
  return value === void 0 || typeof value === "string" && value.startsWith("/") && !value.includes("\0") && value.length <= 4096;
3267
3964
  }
3268
- function safeRef4(value) {
3965
+ function safeRef3(value) {
3269
3966
  return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
3270
3967
  }
3271
- function timestamp2(value) {
3968
+ function timestamp(value) {
3272
3969
  return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
3273
3970
  }
3274
3971
  function record3(value) {
@@ -3276,7 +3973,7 @@ function record3(value) {
3276
3973
  }
3277
3974
 
3278
3975
  // host/claude/src/claude-outbox.ts
3279
- var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3976
+ var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3280
3977
  var CLAUDE_TRANSCRIPT_PATH_MAX_CHARACTERS = 4096;
3281
3978
  var CLAUDE_TRANSCRIPT_SUBAGENT_NAME_MAX_CHARACTERS = 256;
3282
3979
  var ClaudeOutboxStore = class {
@@ -3334,10 +4031,10 @@ var ClaudeOutboxStore = class {
3334
4031
  * a missing one. Every other caller keeps the fail-closed read above.
3335
4032
  */
3336
4033
  async readRawInvocationContext(hostSourceRef, hostSessionRef) {
3337
- if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new ClaudeHostError("invalid_input", "Claude Host context identity or clock is invalid.");
4034
+ if (!SAFE_REF3.test(hostSourceRef) || !SAFE_REF3.test(hostSessionRef)) throw new ClaudeHostError("invalid_input", "Claude Host context identity or clock is invalid.");
3338
4035
  await this.prepare();
3339
4036
  const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, readProjectionFile);
3340
- return invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
4037
+ return context;
3341
4038
  }
3342
4039
  async listInvocationContexts(now = /* @__PURE__ */ new Date()) {
3343
4040
  await this.prepare();
@@ -3345,9 +4042,9 @@ var ClaudeOutboxStore = class {
3345
4042
  for (const name of await readdir(this.activeContexts)) {
3346
4043
  if (!name.endsWith(".json")) continue;
3347
4044
  try {
3348
- const value = JSON.parse(await readFile2(join3(this.activeContexts, name), "utf8"));
4045
+ const value = JSON.parse(await readFile(join3(this.activeContexts, name), "utf8"));
3349
4046
  validateClaudeHostInvocationContext(value);
3350
- if (Date.parse(value.expires_at) > now.getTime() && invocationTurnSelectionMatches(value, await this.readInvocationTurnSelection(value.identity.host_source_ref, value.identity.host_session_ref))) contexts.push(value);
4047
+ if (Date.parse(value.expires_at) > now.getTime()) contexts.push(value);
3351
4048
  } catch {
3352
4049
  }
3353
4050
  }
@@ -3365,7 +4062,7 @@ var ClaudeOutboxStore = class {
3365
4062
  for (const name of await readdir(this.envelopes)) {
3366
4063
  if (!name.endsWith(".json")) continue;
3367
4064
  try {
3368
- const value = JSON.parse(await readFile2(join3(this.envelopes, name), "utf8"));
4065
+ const value = JSON.parse(await readFile(join3(this.envelopes, name), "utf8"));
3369
4066
  validateEnvelope(value);
3370
4067
  entries.push(value);
3371
4068
  } catch {
@@ -3378,7 +4075,7 @@ var ClaudeOutboxStore = class {
3378
4075
  async removeEnvelope(entry) {
3379
4076
  validateEnvelope(entry);
3380
4077
  try {
3381
- await unlink2(this.envelopePath(entry.submission_ref));
4078
+ await unlink3(this.envelopePath(entry.submission_ref));
3382
4079
  } catch (error) {
3383
4080
  if (code(error) !== "ENOENT") throw error;
3384
4081
  }
@@ -3389,10 +4086,10 @@ var ClaudeOutboxStore = class {
3389
4086
  await durableAtomicReplace(join3(this.sessions, `${cealRequestSha256({ source: snapshot.host_source_ref, session: snapshot.host_session_ref })}.json`), JSON.stringify(snapshot));
3390
4087
  }
3391
4088
  async readSession(sourceRef, sessionRef) {
3392
- if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) throw new ClaudeHostError("invalid_input", "Claude session identity is invalid.");
4089
+ if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef)) throw new ClaudeHostError("invalid_input", "Claude session identity is invalid.");
3393
4090
  await this.prepare();
3394
4091
  try {
3395
- const value = JSON.parse(await readFile2(join3(this.sessions, `${cealRequestSha256({ source: sourceRef, session: sessionRef })}.json`), "utf8"));
4092
+ const value = JSON.parse(await readFile(join3(this.sessions, `${cealRequestSha256({ source: sourceRef, session: sessionRef })}.json`), "utf8"));
3396
4093
  validateSession(value);
3397
4094
  return value;
3398
4095
  } catch (error) {
@@ -3427,7 +4124,7 @@ var ClaudeOutboxStore = class {
3427
4124
  for (const name of await readdir(this.registrations)) {
3428
4125
  if (!name.endsWith(".json")) continue;
3429
4126
  try {
3430
- const value = JSON.parse(await readFile2(join3(this.registrations, name), "utf8"));
4127
+ const value = JSON.parse(await readFile(join3(this.registrations, name), "utf8"));
3431
4128
  validateRegistration(value);
3432
4129
  registrations.push(value);
3433
4130
  } catch {
@@ -3436,10 +4133,10 @@ var ClaudeOutboxStore = class {
3436
4133
  return registrations.sort((left, right) => left.registered_at.localeCompare(right.registered_at));
3437
4134
  }
3438
4135
  async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
3439
- if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new ClaudeHostError("invalid_input", "Claude Host turn selection identity is invalid.");
4136
+ if (!SAFE_REF3.test(hostSourceRef) || !SAFE_REF3.test(hostSessionRef)) throw new ClaudeHostError("invalid_input", "Claude Host turn selection identity is invalid.");
3440
4137
  await this.prepare();
3441
4138
  try {
3442
- const value = JSON.parse(await readFile2(this.turnSelectionPath(hostSourceRef, hostSessionRef), "utf8"));
4139
+ const value = JSON.parse(await readFile(this.turnSelectionPath(hostSourceRef, hostSessionRef), "utf8"));
3443
4140
  if (!isHostInvocationTurnSelection(value) || value.host_source_ref !== hostSourceRef || value.host_session_ref !== hostSessionRef) throw new ClaudeHostError("storage", "Claude Host turn selection is malformed.");
3444
4141
  return value;
3445
4142
  } catch (error) {
@@ -3451,7 +4148,7 @@ var ClaudeOutboxStore = class {
3451
4148
  validateRegistration(registration);
3452
4149
  await this.prepare();
3453
4150
  try {
3454
- const value = JSON.parse(await readFile2(this.cursorPath(registration.host_source_ref, registration.host_session_ref), "utf8"));
4151
+ const value = JSON.parse(await readFile(this.cursorPath(registration.host_source_ref, registration.host_session_ref), "utf8"));
3455
4152
  validateCursor(value);
3456
4153
  return value;
3457
4154
  } catch (error) {
@@ -3478,7 +4175,7 @@ var ClaudeOutboxStore = class {
3478
4175
  }
3479
4176
  async readRegistrationFile(path) {
3480
4177
  try {
3481
- const value = JSON.parse(await readFile2(path, "utf8"));
4178
+ const value = JSON.parse(await readFile(path, "utf8"));
3482
4179
  validateRegistration(value);
3483
4180
  return value;
3484
4181
  } catch (error) {
@@ -3488,7 +4185,7 @@ var ClaudeOutboxStore = class {
3488
4185
  }
3489
4186
  async removeStateFile(path) {
3490
4187
  try {
3491
- await unlink2(path);
4188
+ await unlink3(path);
3492
4189
  } catch (error) {
3493
4190
  if (code(error) !== "ENOENT") throw error;
3494
4191
  }
@@ -3498,11 +4195,11 @@ var ClaudeOutboxStore = class {
3498
4195
  }
3499
4196
  };
3500
4197
  function validateInvocationContextReadArguments(hostSourceRef, hostSessionRef, now) {
3501
- if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef) || !(now instanceof Date) || !Number.isFinite(now.getTime())) throw new ClaudeHostError("invalid_input", "Claude Host context identity or clock is invalid.");
4198
+ if (!SAFE_REF3.test(hostSourceRef) || !SAFE_REF3.test(hostSessionRef) || !(now instanceof Date) || !Number.isFinite(now.getTime())) throw new ClaudeHostError("invalid_input", "Claude Host context identity or clock is invalid.");
3502
4199
  }
3503
4200
  async function readProjectionFile(file) {
3504
4201
  try {
3505
- const context = JSON.parse(await readFile2(file, "utf8"));
4202
+ const context = JSON.parse(await readFile(file, "utf8"));
3506
4203
  validateClaudeHostInvocationContext(context);
3507
4204
  return context;
3508
4205
  } catch (error) {
@@ -3511,10 +4208,10 @@ async function readProjectionFile(file) {
3511
4208
  throw new ClaudeHostError("storage", "Unable to read the Claude Host invocation context.");
3512
4209
  }
3513
4210
  }
3514
- function safeRef5(value) {
3515
- return typeof value === "string" && SAFE_REF4.test(value);
4211
+ function safeRef4(value) {
4212
+ return typeof value === "string" && SAFE_REF3.test(value);
3516
4213
  }
3517
- function timestamp3(value) {
4214
+ function timestamp2(value) {
3518
4215
  return typeof value === "string" && Number.isFinite(Date.parse(value));
3519
4216
  }
3520
4217
  function record4(value) {
@@ -3525,26 +4222,24 @@ function code(error) {
3525
4222
  }
3526
4223
  function validateEnvelope(value) {
3527
4224
  if (!record4(value)) throw new ClaudeHostError("storage", "Claude outbox entry is malformed.");
3528
- if (!sameKeys(value, ["schema_version", "identity", "host_input_ref", "input_kind", "prompt_ref", "observations", "origin_input", "submission_ref", "created_at", "expires_at"], ["request_ref"])) {
4225
+ if (!sameKeys(value, ["schema_version", "identity", "host_input_ref", "prompt_ref", "observations", "origin_input", "submission_ref", "created_at", "expires_at"])) {
3529
4226
  throw new ClaudeHostError("storage", `Claude outbox entry key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the outbox entry key set")}).`);
3530
4227
  }
3531
4228
  if (value.schema_version !== CLAUDE_OUTBOX_SCHEMA_VERSION) throw new ClaudeHostError("storage", `Claude outbox entry is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CLAUDE_OUTBOX_SCHEMA_VERSION}`)}).`);
3532
4229
  validateEnvelopeIdentity(value);
3533
4230
  validateEnvelopeObservations(value.observations);
3534
4231
  if (typeof value.origin_input !== "boolean") throw new ClaudeHostError("storage", "Claude outbox origin disposition is malformed.");
3535
- if (!timestamp3(value.created_at) || !timestamp3(value.expires_at)) throw new ClaudeHostError("storage", "Claude outbox timestamps are malformed.");
4232
+ if (!timestamp2(value.created_at) || !timestamp2(value.expires_at)) throw new ClaudeHostError("storage", "Claude outbox timestamps are malformed.");
3536
4233
  }
3537
4234
  function validateEnvelopeIdentity(value) {
3538
4235
  validateIdentity3(value.identity);
3539
- if (!safeRef5(value.host_input_ref)) throw new ClaudeHostError("storage", "Claude outbox input reference is malformed.");
3540
- if (!isCealHostInputKind(value.input_kind)) throw new ClaudeHostError("storage", "Claude outbox input provenance is malformed.");
3541
- if (!safeRef5(value.prompt_ref) || !safeRef5(value.submission_ref)) throw new ClaudeHostError("storage", "Claude outbox references are malformed.");
3542
- if (value.request_ref !== void 0 && !safeRef5(value.request_ref)) throw new ClaudeHostError("storage", "Claude outbox Request reference is malformed.");
4236
+ if (!safeRef4(value.host_input_ref)) throw new ClaudeHostError("storage", "Claude outbox input reference is malformed.");
4237
+ if (!safeRef4(value.prompt_ref) || !safeRef4(value.submission_ref)) throw new ClaudeHostError("storage", "Claude outbox references are malformed.");
3543
4238
  }
3544
4239
  function validateEnvelopeObservations(value) {
3545
4240
  if (!Array.isArray(value)) throw new ClaudeHostError("storage", "Claude outbox observations are malformed.");
3546
4241
  try {
3547
- for (const observation2 of value) validateCealHostObservationDraft(observation2);
4242
+ for (const observation of value) validateCealHostObservationDraft(observation);
3548
4243
  } catch {
3549
4244
  throw new ClaudeHostError("storage", "Claude outbox observations are malformed.");
3550
4245
  }
@@ -3555,7 +4250,7 @@ function validateSession(value) {
3555
4250
  throw new ClaudeHostError("storage", `Claude session snapshot key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the session snapshot key set")}).`);
3556
4251
  }
3557
4252
  if (value.schema_version !== CLAUDE_SESSION_SCHEMA_VERSION) throw new ClaudeHostError("storage", `Claude session snapshot is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CLAUDE_SESSION_SCHEMA_VERSION}`)}).`);
3558
- if (!safeRef5(value.host_session_ref) || !safeRef5(value.host_source_ref) || !timestamp3(value.observed_at) || !timestamp3(value.expires_at)) {
4253
+ if (!safeRef4(value.host_session_ref) || !safeRef4(value.host_source_ref) || !timestamp2(value.observed_at) || !timestamp2(value.expires_at)) {
3559
4254
  throw new ClaudeHostError("storage", "Claude session snapshot fields are malformed.");
3560
4255
  }
3561
4256
  }
@@ -3565,14 +4260,14 @@ function validateRegistration(value) {
3565
4260
  throw new ClaudeHostError("storage", `Claude transcript registration key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the registration key set")}).`);
3566
4261
  }
3567
4262
  if (value.schema_version !== CLAUDE_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION) throw new ClaudeHostError("storage", "Claude transcript registration is malformed.");
3568
- if (![value.host_source_ref, value.host_session_ref, value.claude_session_id].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate))) {
4263
+ if (![value.host_source_ref, value.host_session_ref, value.claude_session_id].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate))) {
3569
4264
  throw new ClaudeHostError("storage", "Claude transcript registration references are malformed.");
3570
4265
  }
3571
4266
  validateRegistrationPaths(value.cwd, value.transcript_path);
3572
- if (!timestamp3(value.registered_at) || !timestamp3(value.expires_at)) throw new ClaudeHostError("storage", "Claude transcript registration timestamps are malformed.");
4267
+ if (!timestamp2(value.registered_at) || !timestamp2(value.expires_at)) throw new ClaudeHostError("storage", "Claude transcript registration timestamps are malformed.");
3573
4268
  }
3574
4269
  function validateRegistrationPaths(cwd, transcriptPath) {
3575
- if (cwd !== void 0 && (typeof cwd !== "string" || !isAbsolute(cwd) || cwd.length > CLAUDE_TRANSCRIPT_PATH_MAX_CHARACTERS || cwd.includes("\0"))) {
4270
+ if (cwd !== void 0 && (typeof cwd !== "string" || !isAbsolute2(cwd) || cwd.length > CLAUDE_TRANSCRIPT_PATH_MAX_CHARACTERS || cwd.includes("\0"))) {
3576
4271
  throw new ClaudeHostError("storage", "Claude transcript registration working directory is malformed.");
3577
4272
  }
3578
4273
  if (typeof transcriptPath !== "string" || transcriptPath.length === 0 || transcriptPath.length > CLAUDE_TRANSCRIPT_PATH_MAX_CHARACTERS) {
@@ -3585,7 +4280,7 @@ function validateCursor(value) {
3585
4280
  throw new ClaudeHostError("storage", "Claude transcript cursor key set is malformed.");
3586
4281
  }
3587
4282
  if (value.schema_version !== CLAUDE_TRANSCRIPT_CURSOR_SCHEMA_VERSION) throw new ClaudeHostError("storage", "Claude transcript cursor is malformed.");
3588
- if (![value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate))) throw new ClaudeHostError("storage", "Claude transcript cursor references are malformed.");
4283
+ if (![value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate))) throw new ClaudeHostError("storage", "Claude transcript cursor references are malformed.");
3589
4284
  if (typeof value.transcript_path !== "string" || value.transcript_path.length === 0) throw new ClaudeHostError("storage", "Claude transcript cursor path is malformed.");
3590
4285
  validateCursorPosition(value);
3591
4286
  validateCursorTurn(value.open_turn, value.unattributed, value.session_observed);
@@ -3597,7 +4292,7 @@ function validateCursorPosition(value) {
3597
4292
  if (value.device !== void 0 && typeof value.device !== "string") throw new ClaudeHostError("storage", "Claude transcript cursor device is malformed.");
3598
4293
  if (value.inode !== void 0 && typeof value.inode !== "string") throw new ClaudeHostError("storage", "Claude transcript cursor inode is malformed.");
3599
4294
  if (value.device === void 0 && value.offset !== 0) throw new ClaudeHostError("storage", "Claude transcript cursor is unpinned past its start.");
3600
- if (value.status !== "active" && value.status !== "quarantined" || !timestamp3(value.updated_at)) throw new ClaudeHostError("storage", "Claude transcript cursor status is malformed.");
4295
+ if (value.status !== "active" && value.status !== "quarantined" || !timestamp2(value.updated_at)) throw new ClaudeHostError("storage", "Claude transcript cursor status is malformed.");
3601
4296
  }
3602
4297
  function validateCursorTurn(openTurn, unattributed, sessionObserved) {
3603
4298
  if (openTurn !== void 0 && !validOpenTurn(openTurn)) throw new ClaudeHostError("storage", "Claude transcript turn cursor is malformed.");
@@ -3628,8 +4323,8 @@ function validCursorPosition(value) {
3628
4323
  }
3629
4324
  function validOpenTurn(value) {
3630
4325
  if (!record4(value) || !sameKeys(value, ["turn_key", "user_uuid", "prompt_ref", "host_input_ref", "started_at", "last_observed_at", "message_ids", "counts"], ["last_terminal_digest"])) return false;
3631
- if (![value.turn_key, value.user_uuid, value.prompt_ref, value.host_input_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate))) return false;
3632
- if (!timestamp3(value.started_at) || !timestamp3(value.last_observed_at)) return false;
4326
+ if (![value.turn_key, value.user_uuid, value.prompt_ref, value.host_input_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate))) return false;
4327
+ if (!timestamp2(value.started_at) || !timestamp2(value.last_observed_at)) return false;
3633
4328
  if (!validMessageIds(value.message_ids)) return false;
3634
4329
  if (!validTurnCounts(value.counts)) return false;
3635
4330
  return value.last_terminal_digest === void 0 || typeof value.last_terminal_digest === "string" && /^[a-f0-9]{64}$/u.test(value.last_terminal_digest);
@@ -3642,7 +4337,7 @@ function validMessageIds(value) {
3642
4337
  function validUnattributed(value) {
3643
4338
  if (!record4(value) || !sameKeys(value, ["input", "cached_input", "cache_write_input", "output", "message_ids", "last_observed_at"])) return false;
3644
4339
  if (!validMessageIds(value.message_ids) || value.message_ids.length === 0) return false;
3645
- if (!timestamp3(value.last_observed_at)) return false;
4340
+ if (!timestamp2(value.last_observed_at)) return false;
3646
4341
  return validTurnCounts(value);
3647
4342
  }
3648
4343
  function validTurnCounts(value) {
@@ -3650,7 +4345,7 @@ function validTurnCounts(value) {
3650
4345
  return [value.input, value.cached_input, value.cache_write_input, value.output].every(nonNegativeInteger);
3651
4346
  }
3652
4347
  function validateIdentity3(value) {
3653
- if (!record4(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate))) {
4348
+ if (!record4(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate))) {
3654
4349
  throw new ClaudeHostError("storage", "Claude Host identity is malformed.");
3655
4350
  }
3656
4351
  }
@@ -3663,126 +4358,32 @@ function sameKeys(value, required, optional = []) {
3663
4358
 
3664
4359
  // host/claude/src/claude-producer.ts
3665
4360
  var ClaudeProducer = class {
3666
- producer_id = "claude";
3667
- producer_label = "Claude";
3668
- outbox_directory;
3669
4361
  store;
3670
4362
  core;
3671
4363
  constructor(options) {
3672
4364
  this.store = new ClaudeOutboxStore(options.outbox_dir);
3673
- this.outbox_directory = this.store.directory;
3674
4365
  this.core = new HostProducerDrainCore(this, options);
3675
4366
  }
3676
4367
  drainOutbox() {
3677
4368
  return this.core.drainOutbox();
3678
4369
  }
3679
- fail(code2, message) {
3680
- return new ClaudeHostError(code2, message);
3681
- }
3682
4370
  listEnvelopes() {
3683
4371
  return this.store.listEnvelopes();
3684
4372
  }
3685
4373
  removeEnvelope(envelope) {
3686
4374
  return this.store.removeEnvelope(envelope);
3687
4375
  }
3688
- submissionRefOf(envelope) {
3689
- return envelope.submission_ref;
3690
- }
3691
4376
  expiresAtOf(envelope) {
3692
4377
  return envelope.expires_at;
3693
4378
  }
3694
- createdAtOf(envelope) {
3695
- return envelope.created_at;
3696
- }
3697
- async mapEnvelope(envelope) {
3698
- const prompt = observation(envelope, "prompt");
3699
- if (prompt !== void 0) return this.mapInput(envelope, prompt);
3700
- return [mapTerminal(envelope)];
3701
- }
3702
- async mapInput(envelope, prompt) {
3703
- const drafts = [];
3704
- const session = observation(envelope, "session");
3705
- if (session !== void 0) {
3706
- const registrations = await this.store.listTranscriptRegistrations();
3707
- const registration = registrations.find((item) => item.host_source_ref === envelope.identity.host_source_ref && item.host_session_ref === envelope.identity.host_session_ref);
3708
- const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
3709
- drafts.push({
3710
- frame_kind: "session_started",
3711
- payload: compact({
3712
- local_session_ref: envelope.identity.host_session_ref,
3713
- working_directory_ref: claudeWorkspaceRef(directoryIdentity),
3714
- model: recordText(session.value, "model"),
3715
- observations: [session]
3716
- })
3717
- });
3718
- }
3719
- const turn = observation(envelope, "turn");
3720
- if (turn !== void 0) drafts.push({
3721
- frame_kind: "turn_started",
3722
- payload: compact({
3723
- local_session_ref: envelope.identity.host_session_ref,
3724
- local_turn_ref: localTurnRef(envelope),
3725
- observations: [turn]
3726
- })
3727
- });
3728
- if (!isCealHostPromptObservationValue(prompt.value)) throw new ClaudeHostError("storage", "Claude prompt observation is malformed.");
3729
- drafts.push({
3730
- frame_kind: "user_input",
3731
- payload: compact({
3732
- local_session_ref: envelope.identity.host_session_ref,
3733
- local_turn_ref: localTurnRef(envelope),
3734
- local_input_ref: envelope.host_input_ref,
3735
- input_kind: envelope.input_kind,
3736
- origin_input: envelope.origin_input,
3737
- observations: [prompt],
3738
- context_expires_at: envelope.expires_at
3739
- })
3740
- });
3741
- return drafts;
3742
- }
3743
4379
  };
3744
- function mapTerminal(envelope) {
3745
- const usage = envelope.observations.filter((item) => ["token", "cost"].includes(item.observation_kind));
3746
- return {
3747
- frame_kind: "turn_terminal",
3748
- payload: compact({
3749
- local_session_ref: envelope.identity.host_session_ref,
3750
- local_turn_ref: localTurnRef(envelope),
3751
- terminal_state: "completed",
3752
- usage: usage.length === 0 ? void 0 : Object.fromEntries(usage.map((item) => [item.observation_kind, item.value])),
3753
- observations: envelope.observations
3754
- })
3755
- };
3756
- }
3757
- function observation(envelope, kind) {
3758
- return envelope.observations.find((item) => item.observation_kind === kind);
3759
- }
3760
- function localTurnRef(envelope) {
3761
- return envelope.identity.host_turn_ref;
3762
- }
3763
- function compact(value) {
3764
- return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
3765
- }
3766
- function asRecord(value) {
3767
- return isRecord3(value) ? value : void 0;
3768
- }
3769
- function recordText(value, key) {
3770
- const record5 = asRecord(value);
3771
- return typeof record5?.[key] === "string" ? record5[key] : void 0;
3772
- }
3773
- function isRecord3(value) {
3774
- return value !== null && typeof value === "object" && !Array.isArray(value);
3775
- }
3776
4380
 
3777
4381
  // host/claude/src/claude-transcript-watcher.ts
3778
- import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
3779
- import { open as open4, readdir as readdir2 } from "node:fs/promises";
4382
+ import { readdir as readdir2 } from "node:fs/promises";
3780
4383
  import { join as join4 } from "node:path";
3781
4384
  var TOKEN_OBSERVATION_SOURCE = "claude_transcript.message.usage";
3782
4385
  var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
3783
4386
  store;
3784
- registerCurrentPrompt;
3785
- releaseCurrentPrompt;
3786
4387
  diagnostic;
3787
4388
  constructor(options) {
3788
4389
  super(options, {
@@ -3791,8 +4392,6 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
3791
4392
  clock_failure: () => new ClaudeHostError("storage", "Claude watcher clock is invalid.")
3792
4393
  });
3793
4394
  this.store = new ClaudeOutboxStore(options.outbox_dir);
3794
- this.registerCurrentPrompt = options.register_current_prompt;
3795
- this.releaseCurrentPrompt = options.release_current_prompt;
3796
4395
  this.diagnostic = options.diagnostic ?? (() => void 0);
3797
4396
  }
3798
4397
  transcriptPathOf(registration) {
@@ -3806,7 +4405,6 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
3806
4405
  const cursor = await this.preflight(registration, handle, size, device, inode);
3807
4406
  if (cursor === null) return { records: 0, enqueued: 0, quarantined: true };
3808
4407
  if (cursor.status === "quarantined") {
3809
- if (cursor.reason !== "transcript_malformed") await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
3810
4408
  return { records: 0, enqueued: 0, quarantined: true };
3811
4409
  }
3812
4410
  const draft = mutableCursor(cursor);
@@ -3821,7 +4419,6 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
3821
4419
  return { records: 0, enqueued: 0, quarantined: true };
3822
4420
  }
3823
4421
  async retireMissing(registration) {
3824
- await this.releaseStoredPromptBestEffort(registration.host_session_ref, await this.store.readTranscriptCursor(registration).catch(() => null));
3825
4422
  return this.retireMissingTranscript(registration.transcript_path, "Claude", () => this.store.retireTranscriptRegistration(registration));
3826
4423
  }
3827
4424
  initialCursor(registration) {
@@ -3868,52 +4465,52 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
3868
4465
  if (isUserTurnLine(record5)) await this.openTurn(registration, draft, record5);
3869
4466
  else if (isAssistantUsageLine(record5)) this.accumulateUsage(draft, toUsage(record5.uuid, record5.timestamp, record5.message.usage));
3870
4467
  else if (isAssistantLine(record5) && record5.message.usage !== void 0) throw new ClaudeHostError("payload_rejected", "Claude transcript usage is malformed.");
3871
- if (isAssistantTerminalLine(record5)) await this.releaseOpenTurn(registration, draft);
4468
+ else {
4469
+ const queued = decodeClaudeQueuedPromptRow(record5, registration.claude_session_id);
4470
+ if (queued !== null) await this.openQueuedTurn(registration, draft, queued);
4471
+ }
3872
4472
  }
3873
4473
  }
3874
4474
  async openTurn(registration, draft, record5) {
3875
- await this.closeTurn(registration, draft, record5.timestamp);
4475
+ await this.openPromptTurn(registration, draft, { uuid: record5.uuid, timestamp: record5.timestamp, content: record5.message.content });
4476
+ }
4477
+ async openQueuedTurn(registration, draft, queued) {
4478
+ await this.openPromptTurn(registration, draft, { uuid: queued.turnId, timestamp: queued.timestamp, content: queued.content });
4479
+ }
4480
+ async openPromptTurn(registration, draft, prompt) {
4481
+ await this.closeTurn(registration, draft, prompt.timestamp);
3876
4482
  const sessionRef = registration.host_session_ref;
3877
- const turnRef = claudeLocalTurnRef(registration.claude_session_id, record5.uuid);
3878
- const inputRef = claudeLocalInputRef(registration.claude_session_id, record5.uuid);
3879
- const promptRef = hostProducerPromptRef(this.sha(record5.message.content));
4483
+ const turnRef = claudeLocalTurnRef(registration.claude_session_id, prompt.uuid);
4484
+ const inputRef = claudeLocalInputRef(registration.claude_session_id, prompt.uuid);
4485
+ const promptRef = hostProducerPromptRef(this.sha(prompt.content));
3880
4486
  const identity = { host_source_ref: registration.host_source_ref, host_session_ref: sessionRef, host_turn_ref: turnRef };
3881
4487
  const observations = [
3882
- { observation_ref: observationRef("transcript_user_text", inputRef, record5.message.content), observation_kind: "prompt", observed_at: record5.timestamp, value: { body_sha256: this.sha(record5.message.content) } },
3883
- { observation_ref: observationRef("transcript_turn_started", turnRef, record5.uuid), observation_kind: "turn", observed_at: record5.timestamp, value: { status: "started", host_session_id_sha256: this.sha(registration.claude_session_id), host_turn_ref: turnRef } }
4488
+ { observation_ref: observationRef(registration.host_source_ref, "transcript_user_text", inputRef, prompt.content), observation_kind: "prompt", observed_at: prompt.timestamp, value: { body_sha256: this.sha(prompt.content) } },
4489
+ { observation_ref: observationRef(registration.host_source_ref, "transcript_turn_started", turnRef, prompt.uuid), observation_kind: "turn", observed_at: prompt.timestamp, value: { status: "started", host_session_id_sha256: this.sha(registration.claude_session_id), host_turn_ref: turnRef } }
3884
4490
  ];
3885
4491
  if (!draft.session_observed) {
3886
4492
  const session = await this.store.readSession(registration.host_source_ref, sessionRef);
3887
- if (session !== null) observations.push({ observation_ref: observationRef("transcript_session", sessionRef, session.value), observation_kind: "session", observed_at: record5.timestamp, value: session.value });
4493
+ if (session !== null) observations.push({ observation_ref: observationRef(registration.host_source_ref, "transcript_session", sessionRef, session.value), observation_kind: "session", observed_at: prompt.timestamp, value: session.value });
3888
4494
  draft.session_observed = true;
3889
4495
  }
3890
- await this.persistOpenedTurnContext(registration, draft, record5, inputRef, turnRef, promptRef, identity, observations);
4496
+ await this.persistOpenedTurnContext(draft, { uuid: prompt.uuid, timestamp: prompt.timestamp, message: { content: prompt.content } }, inputRef, turnRef, promptRef, identity, observations);
3891
4497
  }
3892
- async persistOpenedTurnContext(registration, draft, record5, inputRef, turnRef, promptRef, identity, observations) {
3893
- const sessionRef = registration.host_session_ref;
4498
+ async persistOpenedTurnContext(draft, record5, inputRef, turnRef, promptRef, identity, observations) {
3894
4499
  const expiresAt = this.expire(this.outboxRetentionMs);
3895
- const custodyRegistered = await this.registerPrompt(sessionRef, turnRef, promptRef, record5.message.content, expiresAt);
3896
- if (custodyRegistered) this.diagnostic("watcher_turn_observed");
3897
- try {
3898
- await this.store.putEnvelope({
3899
- schema_version: "ceal.claude_observation_outbox.v1",
3900
- identity,
3901
- host_input_ref: inputRef,
3902
- input_kind: "human",
3903
- prompt_ref: promptRef,
3904
- observations,
3905
- origin_input: true,
3906
- submission_ref: submissionRef("input", turnRef, inputRef, promptRef),
3907
- created_at: record5.timestamp,
3908
- expires_at: expiresAt
3909
- });
3910
- draft.enqueued += 1;
3911
- await this.activateOpenedTurnContext(identity, promptRef, expiresAt);
3912
- this.setOpenedTurn(draft, record5, inputRef, turnRef, promptRef);
3913
- } catch (error) {
3914
- if (custodyRegistered) await this.releasePrompt(sessionRef, { turn_key: turnRef, prompt_ref: promptRef }).catch(() => void 0);
3915
- throw error;
3916
- }
4500
+ await this.store.putEnvelope({
4501
+ schema_version: "ceal.claude_observation_outbox.v1",
4502
+ identity,
4503
+ host_input_ref: inputRef,
4504
+ prompt_ref: promptRef,
4505
+ observations,
4506
+ origin_input: true,
4507
+ submission_ref: submissionRef("input", turnRef, inputRef, promptRef),
4508
+ created_at: record5.timestamp,
4509
+ expires_at: expiresAt
4510
+ });
4511
+ draft.enqueued += 1;
4512
+ await this.activateOpenedTurnContext(identity, promptRef, expiresAt);
4513
+ this.setOpenedTurn(draft, record5, inputRef, turnRef, promptRef);
3917
4514
  }
3918
4515
  async activateOpenedTurnContext(identity, promptRef, expiresAt) {
3919
4516
  const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
@@ -3943,14 +4540,6 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
3943
4540
  await this.putTerminalEnvelope(registration, turn, digest2, turn.message_ids.length === 0 ? at : void 0);
3944
4541
  draft.enqueued += 1;
3945
4542
  }
3946
- await this.releasePrompt(registration.host_session_ref, turn);
3947
- }
3948
- async releaseOpenTurn(registration, draft) {
3949
- if (draft.open_turn !== void 0) await this.releasePrompt(registration.host_session_ref, draft.open_turn);
3950
- }
3951
- async releasePrompt(sessionRef, turn) {
3952
- if (this.producerInstallationRef === void 0 || this.releaseCurrentPrompt === void 0) return;
3953
- await this.releaseCurrentPrompt({ producer_id: "claude", producer_installation_ref: this.producerInstallationRef, local_session_ref: sessionRef, local_turn_ref: turn.turn_key, prompt_ref: turn.prompt_ref });
3954
4543
  }
3955
4544
  accumulateUsage(draft, usage) {
3956
4545
  const turn = draft.open_turn;
@@ -4000,7 +4589,7 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
4000
4589
  const path = join4(directory, name);
4001
4590
  let handle;
4002
4591
  try {
4003
- handle = await open4(path, O_RDONLY3 | O_NOFOLLOW2);
4592
+ handle = await openNoFollow(path);
4004
4593
  const { size, device, inode } = await ownedClaudeTranscriptFile(handle);
4005
4594
  const entry = draft.subagents[name] ?? { offset: 0, anchor_start: 0, anchor_sha256: this.sha(Buffer.alloc(0)) };
4006
4595
  const pinned = await this.preflightSubagent(entry, size, device, inode);
@@ -4067,12 +4656,10 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
4067
4656
  schema_version: "ceal.claude_observation_outbox.v1",
4068
4657
  identity,
4069
4658
  host_input_ref: turn.host_input_ref,
4070
- input_kind: "host_automatic",
4071
4659
  prompt_ref: turn.prompt_ref,
4072
4660
  observations: [
4073
- { observation_ref: observationRef("transcript_terminal", turn.turn_key, digest2), observation_kind: "turn", observed_at: observedAt, value: { status: "stopped", host_session_id_sha256: this.sha(registration.claude_session_id), host_turn_ref: turn.turn_key } },
4074
- { observation_ref: observationRef("transcript_token", turn.turn_key, digest2), observation_kind: "token", observed_at: observedAt, value: tokenValue },
4075
- { observation_ref: observationRef("transcript_cost", turn.turn_key, digest2), observation_kind: "cost", observed_at: observedAt, value: hostObservationNotRecorded("provider_cost_not_observed") }
4661
+ { observation_ref: observationRef(registration.host_source_ref, "transcript_terminal", turn.turn_key, digest2), observation_kind: "turn", observed_at: observedAt, value: { status: "stopped", host_session_id_sha256: this.sha(registration.claude_session_id), host_turn_ref: turn.turn_key } },
4662
+ { observation_ref: observationRef(registration.host_source_ref, "transcript_token", turn.turn_key, digest2), observation_kind: "token", observed_at: observedAt, value: tokenValue }
4076
4663
  ],
4077
4664
  origin_input: false,
4078
4665
  submission_ref: submissionRef("terminal", turn.turn_key, digest2, turn.prompt_ref),
@@ -4100,30 +4687,6 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
4100
4687
  updated_at: this.stamp()
4101
4688
  });
4102
4689
  }
4103
- async registerPrompt(sessionRef, turnRef, promptRef, prompt, expiresAt) {
4104
- if (this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return false;
4105
- const register = this.registerCurrentPrompt;
4106
- const installationRef = this.producerInstallationRef;
4107
- return registerHostCurrentPromptDiagnosed(
4108
- () => register({
4109
- producer_id: "claude",
4110
- producer_installation_ref: installationRef,
4111
- local_session_ref: sessionRef,
4112
- local_turn_ref: turnRef,
4113
- prompt_ref: promptRef,
4114
- current_prompt: prompt,
4115
- context_expires_at: expiresAt
4116
- }),
4117
- (event) => this.diagnostic(event)
4118
- );
4119
- }
4120
- async releaseStoredPromptBestEffort(sessionRef, cursor) {
4121
- await this.releaseOpenTurnBestEffort(sessionRef, cursor?.open_turn);
4122
- }
4123
- async releaseOpenTurnBestEffort(sessionRef, turn) {
4124
- if (turn === void 0) return;
4125
- await this.releasePrompt(sessionRef, turn).catch(() => void 0);
4126
- }
4127
4690
  async activateContext(identity, promptRef, turnGenerationRef, expiresAt) {
4128
4691
  try {
4129
4692
  await this.store.activateInvocationContext(identity, promptRef, expiresAt, void 0, turnGenerationRef);
@@ -4134,7 +4697,6 @@ var ClaudeTranscriptWatcher = class extends HostTranscriptWatcher {
4134
4697
  }
4135
4698
  }
4136
4699
  async quarantineStored(cursor, device, inode, reason) {
4137
- await this.releaseStoredPromptBestEffort(cursor.host_session_ref, cursor);
4138
4700
  await this.store.writeTranscriptCursor({ ...cursor, device, inode, status: "quarantined", reason, updated_at: this.stamp() });
4139
4701
  return null;
4140
4702
  }
@@ -4254,9 +4816,6 @@ function isUserTurnLine(record5) {
4254
4816
  function isAssistantLine(record5) {
4255
4817
  return isTranscriptRecord(record5) && record5.type === "assistant" && isTranscriptRecord(record5.message);
4256
4818
  }
4257
- function isAssistantTerminalLine(record5) {
4258
- return isAssistantLine(record5) && (record5.message.stop_reason === "end_turn" || record5.message.stop_reason === "stop_sequence");
4259
- }
4260
4819
  function isAssistantUsageLine(record5) {
4261
4820
  if (!isTranscriptRecord(record5)) return false;
4262
4821
  if (!isAssistantLine(record5) || record5.message.usage === void 0) return false;
@@ -4268,17 +4827,16 @@ function subagentsDirectory(transcriptPath) {
4268
4827
  const stem = transcriptPath.endsWith(".jsonl") ? transcriptPath.slice(0, -".jsonl".length) : transcriptPath;
4269
4828
  return join4(stem, "subagents");
4270
4829
  }
4271
- function observationRef(event, identity, value) {
4272
- return cealHostObservationRef("claude", transcriptObjectSha256({ event, identity, value }));
4830
+ function observationRef(hostSourceRef, event, identity, value) {
4831
+ return cealHostObservationRef("claude", transcriptObjectSha256({ host_source_ref: hostSourceRef, event, identity, value }));
4273
4832
  }
4274
4833
  function submissionRef(kind, turn, digest2, prompt) {
4275
4834
  return `claude-submission:${transcriptObjectSha256({ kind, turn, digest: digest2, prompt })}`;
4276
4835
  }
4277
4836
  async function ownedClaudeTranscriptFile(handle) {
4278
4837
  const stats = await handle.stat();
4279
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
4280
4838
  if (!stats.isFile()) throw new ClaudeHostError("payload_rejected", "Claude transcript source is not a regular file.");
4281
- if (stats.uid !== uid) throw new ClaudeHostError("payload_rejected", "Claude transcript source is owned by another user.");
4839
+ if (process.platform !== "win32" && !isSelfOwned(stats.uid, process.platform, handle)) throw new ClaudeHostError("payload_rejected", "Claude transcript source is owned by another user.");
4282
4840
  return { size: stats.size, device: String(stats.dev), inode: String(stats.ino) };
4283
4841
  }
4284
4842
  function isNotDirectory(error) {
@@ -4305,18 +4863,14 @@ function isSymlinkTranscript(error) {
4305
4863
 
4306
4864
  // host/claude/src/claude-producer-runtime.ts
4307
4865
  async function drainClaudeProducerOnce(options) {
4308
- const promptPort = new HostRelayCurrentPromptPort(options.relay_endpoint, options.timeout_ms);
4309
4866
  const scan = await new ClaudeTranscriptWatcher({
4310
4867
  outbox_dir: options.outbox_dir,
4311
- producer_installation_ref: options.producer_installation_ref,
4312
- register_current_prompt: (input) => promptPort.register(input),
4313
- release_current_prompt: (identity) => promptPort.release(identity),
4314
4868
  diagnostic: (event, failure_kind, cause) => writeHostServiceDiagnostic("claude", event, failure_kind, cause)
4315
4869
  }).scanOnce().catch((error) => {
4316
4870
  writeHostServiceDiagnostic("claude", "watcher_scan_failed", claudeTranscriptScanFailureKind(error), claudeTranscriptScanCause(void 0, error));
4317
4871
  throw error;
4318
4872
  });
4319
- const producer = new ClaudeProducer({ 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 } });
4873
+ const producer = new ClaudeProducer({ outbox_dir: options.outbox_dir, ...options.now === void 0 ? {} : { now: options.now } });
4320
4874
  return { scanned: scan.enqueued, ...await producer.drainOutbox() };
4321
4875
  }
4322
4876
 
@@ -4329,19 +4883,25 @@ var CLAUDE_PRODUCER_DESCRIPTOR = {
4329
4883
  extra_paths: [CLAUDE_TRANSCRIPTS_ROOT],
4330
4884
  config: (shared, extras) => ({ ...shared, claude_transcripts_root: hostProducerExtraPath(extras, CLAUDE_TRANSCRIPTS_ROOT) }),
4331
4885
  runtime_options: (config) => Object.freeze({
4332
- outbox_dir: config.outbox_dir,
4333
- producer_installation_ref: config.producer_installation_ref,
4334
- producer_epoch: config.producer_epoch,
4335
- relay_endpoint: config.relay_endpoint,
4336
- timeout_ms: config.ipc_timeout_ms
4886
+ outbox_dir: config.outbox_dir
4337
4887
  }),
4338
4888
  drain: drainClaudeProducerOnce
4339
4889
  };
4340
4890
 
4341
4891
  // host/claude/src/claude-visible-output-budget.ts
4342
- import { readFile as readFile3 } from "node:fs/promises";
4892
+ import { readFile as readFile2 } from "node:fs/promises";
4343
4893
  import { homedir } from "node:os";
4894
+ import { join as join6 } from "node:path";
4895
+
4896
+ // host/claude/src/claude-runtime-paths.ts
4344
4897
  import { join as join5 } from "node:path";
4898
+ var CLAUDE_CONFIG_DIRECTORY_ENVIRONMENT = "CLAUDE_CONFIG_DIR";
4899
+ var CLAUDE_HOME_DEFAULT_DIRECTORY = ".claude";
4900
+ function resolveClaudeConfigHome(environment, home) {
4901
+ return environment[CLAUDE_CONFIG_DIRECTORY_ENVIRONMENT] || join5(home, CLAUDE_HOME_DEFAULT_DIRECTORY);
4902
+ }
4903
+
4904
+ // host/claude/src/claude-visible-output-budget.ts
4345
4905
  var CLAUDE_DEFAULT_BASH_OUTPUT_MAX_CHARS = 3e4;
4346
4906
  var CLAUDE_MAX_INLINE_OUTPUT_MAX_CHARS = 128e3;
4347
4907
  async function resolveClaudeVisibleOutputMaxBytes(environment = process.env, workingDirectory = process.cwd()) {
@@ -4351,17 +4911,17 @@ async function resolveClaudeVisibleOutputMaxBytes(environment = process.env, wor
4351
4911
  return Math.min(bashReadback ?? CLAUDE_DEFAULT_BASH_OUTPUT_MAX_CHARS, CLAUDE_DEFAULT_BASH_OUTPUT_MAX_CHARS);
4352
4912
  }
4353
4913
  async function projectOrUserInlineLimit(environment, workingDirectory) {
4354
- const configHome = environment.CLAUDE_CONFIG_DIR ?? join5(homedir(), ".claude");
4355
- const userLimit = await settingsInlineLimit(join5(configHome, "settings.json"));
4356
- const projectLimit = await settingsInlineLimit(join5(workingDirectory, ".claude", "settings.json"));
4357
- const localLimit = await settingsInlineLimit(join5(workingDirectory, ".claude", "settings.local.json"));
4914
+ const configHome = resolveClaudeConfigHome(environment, homedir());
4915
+ const userLimit = await settingsInlineLimit(join6(configHome, "settings.json"));
4916
+ const projectLimit = await settingsInlineLimit(join6(workingDirectory, ".claude", "settings.json"));
4917
+ const localLimit = await settingsInlineLimit(join6(workingDirectory, ".claude", "settings.local.json"));
4358
4918
  const candidate = localLimit ?? projectLimit ?? userLimit;
4359
4919
  return candidate === void 0 ? void 0 : Math.min(candidate, CLAUDE_MAX_INLINE_OUTPUT_MAX_CHARS);
4360
4920
  }
4361
4921
  async function settingsInlineLimit(path) {
4362
4922
  try {
4363
- const parsed = JSON.parse(await readFile3(path, "utf8"));
4364
- if (!isRecord4(parsed)) return void 0;
4923
+ const parsed = JSON.parse(await readFile2(path, "utf8"));
4924
+ if (!isRecord5(parsed)) return void 0;
4365
4925
  return positiveInteger(parsed.bashOutputMaxChars);
4366
4926
  } catch {
4367
4927
  return void 0;
@@ -4372,7 +4932,7 @@ function positiveInteger(value) {
4372
4932
  const parsed = typeof normalized === "string" && normalized.length > 0 ? Number(normalized) : normalized;
4373
4933
  return typeof parsed === "number" && Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
4374
4934
  }
4375
- function isRecord4(value) {
4935
+ function isRecord5(value) {
4376
4936
  return value !== null && typeof value === "object" && !Array.isArray(value);
4377
4937
  }
4378
4938
 
@@ -4386,23 +4946,83 @@ var CLAUDE_INVOCATION_AUTHENTICATION_DESCRIPTOR = {
4386
4946
  config_flag: HOST_PRODUCER_CONFIG_FLAG,
4387
4947
  parse_config_flag: parseHostProducerConfigFlag,
4388
4948
  read_producer_config: (configPath) => readHostProducerConfig(CLAUDE_PRODUCER_DESCRIPTOR, configPath),
4389
- resolve_visible_output_max_bytes: () => resolveClaudeVisibleOutputMaxBytes()
4949
+ resolve_visible_output_max_bytes: () => resolveClaudeVisibleOutputMaxBytes(),
4950
+ read_human_message_window: readClaudeInvocationHumanMessageWindow
4390
4951
  };
4391
4952
  function runClaudeInvocationAuthenticationCommand(args, options) {
4392
4953
  return runHostInvocationAuthenticationCommand(CLAUDE_INVOCATION_AUTHENTICATION_DESCRIPTOR, args, options);
4393
4954
  }
4955
+ async function readClaudeInvocationHumanMessageWindow(input) {
4956
+ return readRegisteredHostInvocationHumanMessageWindow(() => input.store.listTranscriptRegistrations(), input.identity, (line, registration) => {
4957
+ let row;
4958
+ try {
4959
+ row = JSON.parse(line);
4960
+ } catch {
4961
+ return "malformed";
4962
+ }
4963
+ const message = decodeClaudeHumanMessageRow(row, registration.claude_session_id);
4964
+ return message === null ? null : {
4965
+ host_turn_ref: claudeLocalTurnRef(registration.claude_session_id, message.turnId),
4966
+ host_input_ref: claudeLocalInputRef(registration.claude_session_id, message.turnId),
4967
+ text: message.content
4968
+ };
4969
+ });
4970
+ }
4394
4971
 
4395
4972
  // host/client-doctor.ts
4396
- import { createHash as createHash5 } from "node:crypto";
4397
- import { execFile } from "node:child_process";
4398
- import { access, open as open5, readFile as readFile4, realpath, stat } from "node:fs/promises";
4399
- import { delimiter, isAbsolute as isAbsolute2, join as join6 } from "node:path";
4973
+ import { createHash as createHash6 } from "node:crypto";
4974
+ import { execFile as execFile2 } from "node:child_process";
4975
+ import { access, open as open3, readFile as readFile3, realpath, stat as stat2 } from "node:fs/promises";
4976
+ import { delimiter, isAbsolute as isAbsolute3, join as join7 } from "node:path";
4400
4977
  import { promisify } from "node:util";
4401
- var execFileAsync = promisify(execFile);
4978
+
4979
+ // host/command-vector.ts
4980
+ import { win32 as win326 } from "node:path";
4981
+ var MAX_COMMAND_BYTES = 4096;
4982
+ function formatHostCommand(vector, platform) {
4983
+ return formatWith(vector, platform === "windows" ? quoteWindowsArgument : quotePosixArgument);
4984
+ }
4985
+ function hostScriptLauncherInvocation(launcherPath, args, platform = process.platform, environment = process.env) {
4986
+ if (/\.(?:cjs|js|mjs)$/iu.test(launcherPath)) return { executable: process.execPath, args: [launcherPath, ...args] };
4987
+ if (platform === "win32" && /\.(?:bat|cmd)$/iu.test(launcherPath)) {
4988
+ return {
4989
+ executable: environment.ComSpec ?? "cmd.exe",
4990
+ args: ["/d", "/s", "/c", `call ${formatHostCommand({ executable: launcherPath, args }, "windows")}`],
4991
+ windowsVerbatimArguments: true
4992
+ };
4993
+ }
4994
+ if (platform === "win32" && /\.ps1$/iu.test(launcherPath)) {
4995
+ return {
4996
+ executable: win326.join(environment.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
4997
+ args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-File", launcherPath, ...args]
4998
+ };
4999
+ }
5000
+ return { executable: launcherPath, args };
5001
+ }
5002
+ function formatWith(vector, quote) {
5003
+ const command = [vector.executable, ...vector.args].map(quote).join(" ");
5004
+ const commandBytes = Buffer.byteLength(command, "utf8");
5005
+ if (commandBytes > MAX_COMMAND_BYTES) throw new TypeError(`Host command vector is too large (${refusalFact("bytes", commandBytes, `at most ${MAX_COMMAND_BYTES} UTF-8 bytes`)}).`);
5006
+ return command;
5007
+ }
5008
+ function quotePosixArgument(value) {
5009
+ return `'${value.replaceAll("'", "'\\''")}'`;
5010
+ }
5011
+ function quoteWindowsArgument(value) {
5012
+ if (value.includes("%") || value.includes("!")) throw new TypeError("Host command vector contains unsafe Windows expansion syntax.");
5013
+ return quoteWindowsExecArgument(value);
5014
+ }
5015
+ function quoteWindowsExecArgument(value) {
5016
+ const escaped = value.replace(/(\\*)"/gu, '$1$1\\"').replace(/(\\+)$/gu, "$1$1");
5017
+ return `"${escaped}"`;
5018
+ }
5019
+
5020
+ // host/client-doctor.ts
5021
+ var execFileAsync = promisify(execFile2);
4402
5022
  var GIT_OID = /^[a-f0-9]{40}$/u;
4403
5023
  var SHA2562 = /^[a-f0-9]{64}$/u;
4404
- var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
4405
- var CEAL_ENVIRONMENT_OVERRIDES = ["CEAL_GATEWAY_ORIGIN", "CEAL_GATEWAY_TRANSPORT_ORIGIN", "CEAL_ACCESS_TOKEN", "CEAL_CONTEXT_STORE_PATH"];
5024
+ var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
5025
+ var CEAL_ENVIRONMENT_OVERRIDES = CEAL_CLIENT_CONTEXT_OVERRIDE_ENVIRONMENTS;
4406
5026
  async function doctorClient(options = {}, component) {
4407
5027
  const environment = canonicalClientEnvironment(options.environment ?? process.env);
4408
5028
  const run = options.run ?? executeCealCommand;
@@ -4439,7 +5059,7 @@ async function inspectExecutable(options, environment, component) {
4439
5059
  return { ok: true, value: executable };
4440
5060
  }
4441
5061
  async function inspectVersion(options, executable, environment, run, component) {
4442
- const probe = hostClientProbe(await safeRun(run, executable.path, ["version"], environment), (result) => versionIdentity(commandDocument(result)));
5062
+ const probe = hostClientProbe(await safeRun(run, executable.path, CEAL_CLIENT_VERSION_COMMAND, environment), (result) => versionIdentity(commandDocument(result)));
4443
5063
  if (probe.kind !== "value") return probeFailure(probe, "version", component, options);
4444
5064
  if (options.expected_source_commit !== void 0 && probe.value.source_commit !== options.expected_source_commit) return inspectedFailure("ceal_identity_mismatch", `The Ceal executable visible to the ${component} Host did not return the expected source identity (${refusalFact("source_commit", probe.value.source_commit, `expected ${options.expected_source_commit}`)}).`, `Install the exact candidate, restart ${component}, and verify \`ceal version\` from that Host.`);
4445
5065
  return { ok: true, value: probe.value };
@@ -4456,8 +5076,8 @@ function probeCode(code2) {
4456
5076
  return "ceal_output_unreadable";
4457
5077
  }
4458
5078
  async function inspectCanonicalContext(options, executable, environment, run, component) {
4459
- const probe = hostClientProbe(await safeRun(run, executable.path, ["access-group", "list"], environment), (result) => selectedContext(commandDocument(result)));
4460
- if (probe.kind === "unreachable") return probeFailure(probe, "access-group list", component, options);
5079
+ const probe = hostClientProbe(await safeRun(run, executable.path, CEAL_CLIENT_ACCESS_GROUP_LIST_COMMAND, environment), (result) => selectedContext(commandDocument(result)));
5080
+ if (probe.kind === "unreachable") return probeFailure(probe, CEAL_CLIENT_ACCESS_GROUP_LIST_COMMAND.join(" "), component, options);
4461
5081
  if (probe.kind !== "value" || options.expected_instance_ref !== void 0 && probe.value.instance_ref !== options.expected_instance_ref) return inspectedFailure("canonical_context_unavailable", `The ${component} Host cannot select the intended authenticated context from Ceal's canonical user configuration (${refusalFacts([{ name: "probe_kind", value: probe.kind, expected: "value" }, { name: "instance_ref", value: probe.kind === "value" ? probe.value.instance_ref : "unavailable", expected: options.expected_instance_ref ?? "a selected context" }])}).`, `Run \`ceal session adopt --email <email>\` without CEAL context overrides, then restart ${component}.`);
4462
5082
  return { ok: true, value: probe.value };
4463
5083
  }
@@ -4470,15 +5090,15 @@ function canonicalClientEnvironment(input) {
4470
5090
  return environment;
4471
5091
  }
4472
5092
  async function resolveCealExecutable(command, environment) {
4473
- const candidates = isAbsolute2(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter((entry) => entry.length > 0).map((entry) => join6(entry, command));
5093
+ const candidates = isAbsolute3(command) ? [command] : (environment.PATH ?? "").split(delimiter).filter((entry) => entry.length > 0).map((entry) => join7(entry, command));
4474
5094
  for (const candidate of candidates) {
4475
5095
  try {
4476
5096
  await access(candidate);
4477
5097
  const path = await realpath(candidate);
4478
- const metadata = await stat(path);
5098
+ const metadata = await stat2(path);
4479
5099
  if (!metadata.isFile()) continue;
4480
5100
  if (await isLauncher(path)) return resolveLauncherBinary(path, environment);
4481
- return { path, sha256: createHash5("sha256").update(await readFile4(path)).digest("hex") };
5101
+ return { path, sha256: createHash6("sha256").update(await readFile3(path)).digest("hex") };
4482
5102
  } catch {
4483
5103
  }
4484
5104
  }
@@ -4486,7 +5106,7 @@ async function resolveCealExecutable(command, environment) {
4486
5106
  }
4487
5107
  async function isLauncher(path) {
4488
5108
  if (/\.(?:bat|cjs|cmd|js|mjs|ps1)$/u.test(path)) return true;
4489
- const handle = await open5(path, "r");
5109
+ const handle = await open3(path, "r");
4490
5110
  try {
4491
5111
  const buffer = Buffer.alloc(128);
4492
5112
  const { bytesRead } = await handle.read(buffer, 0, buffer.byteLength, 0);
@@ -4497,25 +5117,26 @@ async function isLauncher(path) {
4497
5117
  }
4498
5118
  }
4499
5119
  async function resolveLauncherBinary(launcherPath, environment) {
4500
- const result = await execFileAsync(launcherPath, ["doctor"], { env: environment, timeout: 15e3, encoding: "utf8", windowsHide: false });
5120
+ const invocation = hostScriptLauncherInvocation(launcherPath, CEAL_CLIENT_DOCTOR_COMMAND, process.platform, environment);
5121
+ const result = await execFileAsync(invocation.executable, [...invocation.args], { env: environment, timeout: 15e3, encoding: "utf8", windowsHide: false, ...invocation.windowsVerbatimArguments === void 0 ? {} : { windowsVerbatimArguments: invocation.windowsVerbatimArguments } });
4501
5122
  let parsed;
4502
5123
  try {
4503
5124
  parsed = JSON.parse(result.stdout);
4504
5125
  } catch {
4505
5126
  throw new Error("launcher doctor returned unreadable output");
4506
5127
  }
4507
- if (!isRecord5(parsed) || parsed.schema_version !== "ceal.doctor.v1" || parsed.ok !== true || typeof parsed.binary_path !== "string" || parsed.binary_path.length === 0) throw new Error("launcher doctor did not report a binary path");
5128
+ if (!isRecord6(parsed) || parsed.schema_version !== CEAL_CLIENT_DOCTOR_SCHEMA_VERSION || parsed.ok !== true || typeof parsed.binary_path !== "string" || parsed.binary_path.length === 0) throw new Error("launcher doctor did not report a binary path");
4508
5129
  const binaryPath = await realpath(parsed.binary_path);
4509
- const metadata = await stat(binaryPath);
5130
+ const metadata = await stat2(binaryPath);
4510
5131
  if (!metadata.isFile()) throw new Error("launcher doctor reported a non-file binary path");
4511
- return { path: binaryPath, sha256: createHash5("sha256").update(await readFile4(binaryPath)).digest("hex") };
5132
+ return { path: binaryPath, sha256: createHash6("sha256").update(await readFile3(binaryPath)).digest("hex") };
4512
5133
  }
4513
5134
  async function executeCealCommand(executable, args, environment) {
4514
5135
  try {
4515
5136
  const result = await execFileAsync(executable, [...args], { env: environment, timeout: 15e3, encoding: "utf8" });
4516
5137
  return { exit_code: 0, stdout: result.stdout, stderr: result.stderr };
4517
5138
  } catch (error) {
4518
- if (isRecord5(error)) return { exit_code: typeof error.code === "number" ? error.code : 1, stdout: typeof error.stdout === "string" ? error.stdout : "", stderr: typeof error.stderr === "string" ? error.stderr : "" };
5139
+ if (isRecord6(error)) return { exit_code: typeof error.code === "number" ? error.code : 1, stdout: typeof error.stdout === "string" ? error.stdout : "", stderr: typeof error.stderr === "string" ? error.stderr : "" };
4519
5140
  throw error;
4520
5141
  }
4521
5142
  }
@@ -4526,34 +5147,34 @@ function commandDocument(result) {
4526
5147
  if (result === null) return null;
4527
5148
  try {
4528
5149
  const value = JSON.parse(result.stdout);
4529
- return isRecord5(value) ? value : null;
5150
+ return isRecord6(value) ? value : null;
4530
5151
  } catch {
4531
5152
  return null;
4532
5153
  }
4533
5154
  }
4534
5155
  function selectedContext(value) {
4535
5156
  if (value?.schema_version !== "ceal.access-group.v1" || value.ok !== true || !Array.isArray(value.contexts)) return null;
4536
- const selected = value.contexts.filter((item) => isRecord5(item) && item.selected === true);
5157
+ const selected = value.contexts.filter((item) => isRecord6(item) && item.selected === true);
4537
5158
  if (selected.length !== 1) return null;
4538
5159
  return selectedContextRecord(selected[0]);
4539
5160
  }
4540
5161
  function selectedContextRecord(item) {
4541
- if (!isRecord5(item) || typeof item.name !== "string" || typeof item.instance_ref !== "string") return null;
4542
- if (!SAFE_REF5.test(item.instance_ref)) return null;
5162
+ if (!isRecord6(item) || typeof item.name !== "string" || typeof item.instance_ref !== "string") return null;
5163
+ if (!SAFE_REF4.test(item.instance_ref)) return null;
4543
5164
  return { name: item.name, instance_ref: item.instance_ref };
4544
5165
  }
4545
5166
  function versionIdentity(value) {
4546
- if (value?.schema_version !== "ceal.version.v1" || value.ok !== true || typeof value.version !== "string" || typeof value.source_commit !== "string" || typeof value.source_tree !== "string") return null;
5167
+ if (value?.schema_version !== CEAL_CLIENT_VERSION_SCHEMA_VERSION || value.ok !== true || typeof value.version !== "string" || typeof value.source_commit !== "string" || typeof value.source_tree !== "string") return null;
4547
5168
  if (value.version.length === 0 || !GIT_OID.test(value.source_commit) || !GIT_OID.test(value.source_tree)) return null;
4548
5169
  return { version: value.version, source_commit: value.source_commit, source_tree: value.source_tree };
4549
5170
  }
4550
5171
  function validExpectations(options) {
4551
- return (options.expected_sha256 === void 0 || SHA2562.test(options.expected_sha256)) && (options.expected_source_commit === void 0 || GIT_OID.test(options.expected_source_commit)) && (options.expected_instance_ref === void 0 || SAFE_REF5.test(options.expected_instance_ref));
5172
+ return (options.expected_sha256 === void 0 || SHA2562.test(options.expected_sha256)) && (options.expected_source_commit === void 0 || GIT_OID.test(options.expected_source_commit)) && (options.expected_instance_ref === void 0 || SAFE_REF4.test(options.expected_instance_ref));
4552
5173
  }
4553
5174
  function failure(code2, message, next_action) {
4554
5175
  return { ok: false, status: "failure", error: { code: code2, message, next_action } };
4555
5176
  }
4556
- function isRecord5(value) {
5177
+ function isRecord6(value) {
4557
5178
  return value !== null && typeof value === "object" && !Array.isArray(value);
4558
5179
  }
4559
5180