@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(code) {
892
+ const entry2 = CEAL_FAILURE_GUIDANCE_TABLE[code];
893
+ if (entry2 === void 0) throw new TypeError(`Failure guidance code is not guided: ${code}.`);
894
+ return entry2;
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",
@@ -975,10 +1222,20 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
975
1222
  var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "block", "object"]);
976
1223
  var CEAL_OPAQUE_REF_KINDS = vocabulary([...CEAL_OPAQUE_HANDLE_KINDS, "result", "operation-receipt", "reply-receipt", "field", "notion-data-source"]);
977
1224
 
1225
+ // packages/ceal-client-protocol/src/client-environment-generated.ts
1226
+ var CEAL_CLIENT_CONTEXT_OVERRIDE_ENVIRONMENTS = Object.freeze(["CEAL_GATEWAY_ORIGIN", "CEAL_GATEWAY_TRANSPORT_ORIGIN", "CEAL_ACCESS_TOKEN", "CEAL_CONTEXT_STORE_PATH"]);
1227
+
1228
+ // packages/ceal-client-protocol/src/client-process-contract.ts
1229
+ var CEAL_CLIENT_VERSION_SCHEMA_SUFFIX = ".version.v1";
1230
+ var CEAL_CLIENT_VERSION_SCHEMA_VERSION = `ceal${CEAL_CLIENT_VERSION_SCHEMA_SUFFIX}`;
1231
+
978
1232
  // packages/ceal-client-protocol/src/bare-document.ts
979
1233
  var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_fields;
980
1234
  var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
981
1235
 
1236
+ // packages/ceal-client-protocol/src/actor-kind.ts
1237
+ var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
1238
+
982
1239
  // packages/ceal-client-protocol/src/resource-row.ts
983
1240
  var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
984
1241
  var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
@@ -993,6 +1250,8 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
993
1250
  "resource_created",
994
1251
  "review_submitted",
995
1252
  "comment_created",
1253
+ "reaction_created",
1254
+ "ceal_write_completed",
996
1255
  "event_start",
997
1256
  "all_day_start",
998
1257
  "file_created",
@@ -1027,6 +1286,10 @@ var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
1027
1286
  "github.review.submitted",
1028
1287
  "github.issue_comment.created",
1029
1288
  "github.review_comment.created",
1289
+ "github.reaction.created",
1290
+ "github.reaction.deleted",
1291
+ "github.pull_request.review_requested",
1292
+ "github.pull_request.review_request_removed",
1030
1293
  "calendar.organized",
1031
1294
  "calendar.accepted_invite",
1032
1295
  "calendar.recorded",
@@ -1048,6 +1311,10 @@ var CEAL_ACTIVITY_EVENT_TIMESTAMP_BASIS_MAP = Object.freeze({
1048
1311
  "github.pull_request.opened": "resource_created",
1049
1312
  "github.issue.edited": "resource_modified",
1050
1313
  "github.pull_request.edited": "resource_modified",
1314
+ "github.reaction.created": "reaction_created",
1315
+ "github.reaction.deleted": "ceal_write_completed",
1316
+ "github.pull_request.review_requested": "ceal_write_completed",
1317
+ "github.pull_request.review_request_removed": "ceal_write_completed",
1051
1318
  "calendar.organized": "resource_created",
1052
1319
  "drive.file.created": "file_created",
1053
1320
  "drive.file.modified": "file_modified_by_me",
@@ -1071,6 +1338,7 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1071
1338
  source_url: { required: false, timestamp: false },
1072
1339
  source: { required: true, timestamp: false },
1073
1340
  owner: { required: false, timestamp: false },
1341
+ actor_kind: { required: false, timestamp: false },
1074
1342
  counterparts: { required: false, timestamp: false },
1075
1343
  attachments: { required: false, timestamp: false },
1076
1344
  created_at: { required: false, timestamp: true },
@@ -1078,9 +1346,18 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1078
1346
  modified_at: { required: false, timestamp: true },
1079
1347
  modified_at_local: { required: false, timestamp: true },
1080
1348
  viewed_by_me_at: { required: false, timestamp: true },
1349
+ modified_by_me_at: { required: false, timestamp: true },
1081
1350
  own_ceal_write: { required: false, timestamp: false },
1082
1351
  owned_by_me: { required: false, timestamp: false },
1352
+ mime_type: { required: false, timestamp: false },
1353
+ size_bytes: { required: false, timestamp: false },
1354
+ version: { required: false, timestamp: false },
1355
+ trashed: { required: false, timestamp: false },
1083
1356
  visibility: { required: false, timestamp: false },
1357
+ archived: { required: false, timestamp: false },
1358
+ tag: { required: false, timestamp: false },
1359
+ labels: { required: false, timestamp: false },
1360
+ labels_incomplete: { required: false, timestamp: false },
1084
1361
  start: { required: false, timestamp: false },
1085
1362
  end: { required: false, timestamp: false },
1086
1363
  status: { required: false, timestamp: false },
@@ -1153,18 +1430,29 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
1153
1430
  // The serving gate answers this while the Gateway is still starting and the
1154
1431
  // refresh was never attempted; the same attempt stays retryable. It is never
1155
1432
  // stored by the session owner, only answered at the serving edge.
1156
- "gateway_starting"
1433
+ CEAL_GATEWAY_STARTING_FAILURE_CODE
1434
+ ]);
1435
+ var CEAL_CLIENT_REVOKE_FAILURE_CODES = vocabulary([
1436
+ "refresh_invalid",
1437
+ "refresh_expired",
1438
+ "refresh_replayed",
1439
+ "refresh_revoked"
1157
1440
  ]);
1441
+ var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX = "ceal_personal_";
1442
+ var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX = "ceal_refresh_";
1443
+ var PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN = "[A-Za-z0-9_-]{43}";
1444
+ var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
1445
+ var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
1158
1446
 
1159
1447
  // packages/ceal-client-protocol/src/request-validation-primitives.ts
1160
- import { createHash } from "node:crypto";
1448
+ import { createHash as createHash2 } from "node:crypto";
1161
1449
  var DIGEST = /^[a-f0-9]{64}$/u;
1162
1450
  var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
1163
1451
  function isCealDigest(value) {
1164
1452
  return typeof value === "string" && DIGEST.test(value);
1165
1453
  }
1166
1454
  function cealRequestSha256(value) {
1167
- return createHash("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
1455
+ return createHash2("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
1168
1456
  }
1169
1457
  function operationRecord(value, message) {
1170
1458
  requireOperationRecord(value, message);
@@ -1180,40 +1468,71 @@ function requirePrefixedRef(value, prefix, message) {
1180
1468
  // packages/ceal-client-protocol/src/request-authority.ts
1181
1469
  var CEAL_REQUEST_SCHEMA_VERSION = "ceal.request.v1";
1182
1470
  var CEAL_REQUEST_ARTIFACT_OWNERS = vocabulary(["gateway", "host", "connector"]);
1183
- var CEAL_REQUEST_HOST_BINDING_KEYS = vocabulary(["prompt_ref", "host_session_ref", "host_turn_ref", "host_source_ref"]);
1471
+ var CEAL_HOST_INVOCATION_IDENTITY_KEYS = vocabulary(["host_session_ref", "host_turn_ref", "host_source_ref"]);
1184
1472
  var CEAL_REQUEST_ARTIFACT_RETENTION_CLASSES = vocabulary(["request", "source", "result"]);
1185
1473
  var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
1186
- 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"]);
1474
+ var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority"]);
1475
+ var CEAL_REQUEST_COMMON_PROPERTIES = Object.freeze({
1476
+ schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
1477
+ ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
1478
+ created_at: { type: "string", minLength: 1, maxLength: 64 },
1479
+ authority: {
1480
+ type: "object",
1481
+ maxProperties: 5,
1482
+ additionalProperties: false,
1483
+ properties: {
1484
+ instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1485
+ access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1486
+ principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1487
+ execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1488
+ authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1489
+ },
1490
+ required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
1491
+ }
1492
+ });
1493
+ var CEAL_REQUEST_HOST_PROPERTIES = Object.freeze({
1494
+ prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1495
+ host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1496
+ host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1497
+ host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1498
+ host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1499
+ });
1500
+ var CEAL_REQUEST_CALLER_PROPERTIES = Object.freeze({
1501
+ 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." },
1502
+ 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." }
1503
+ });
1504
+ var CEAL_REQUEST_OPTIONAL_PROPERTIES = Object.freeze({
1505
+ requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1506
+ 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." }
1507
+ });
1508
+ var CEAL_REQUEST_HOST_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, ...CEAL_HOST_INVOCATION_IDENTITY_KEYS, "prompt_ref"]);
1509
+ var CEAL_REQUEST_CALLER_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, "caller_call_group_ref", "caller_call_input_sha256"]);
1187
1510
  var CEAL_REQUEST_JSON_SCHEMA = {
1188
1511
  type: "object",
1189
- maxProperties: 11,
1512
+ 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.",
1513
+ maxProperties: 13,
1190
1514
  additionalProperties: false,
1191
1515
  properties: {
1192
- schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
1193
- ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
1194
- created_at: { type: "string", minLength: 1, maxLength: 64 },
1195
- authority: {
1516
+ ...CEAL_REQUEST_COMMON_PROPERTIES,
1517
+ ...CEAL_REQUEST_HOST_PROPERTIES,
1518
+ ...CEAL_REQUEST_CALLER_PROPERTIES,
1519
+ ...CEAL_REQUEST_OPTIONAL_PROPERTIES
1520
+ },
1521
+ required: CEAL_REQUEST_KEYS,
1522
+ anyOf: [
1523
+ {
1196
1524
  type: "object",
1197
- maxProperties: 5,
1198
- additionalProperties: false,
1199
- properties: {
1200
- instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1201
- access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1202
- principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1203
- execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1204
- authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1205
- },
1206
- required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
1525
+ required: CEAL_REQUEST_HOST_REQUIRED_KEYS,
1526
+ properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_HOST_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
1527
+ additionalProperties: false
1207
1528
  },
1208
- prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1209
- host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1210
- host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1211
- host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1212
- host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1213
- requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1214
- 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." }
1215
- },
1216
- required: CEAL_REQUEST_KEYS
1529
+ {
1530
+ type: "object",
1531
+ required: CEAL_REQUEST_CALLER_REQUIRED_KEYS,
1532
+ properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_CALLER_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
1533
+ additionalProperties: false
1534
+ }
1535
+ ]
1217
1536
  };
1218
1537
 
1219
1538
  // packages/ceal-client-protocol/src/request-operands.ts
@@ -1324,10 +1643,6 @@ var CEAL_REQUEST_RUN_BATCH_JSON_SCHEMA = {
1324
1643
  };
1325
1644
 
1326
1645
  // packages/ceal-client-protocol/src/host-observation.ts
1327
- var CEAL_HOST_INPUT_KINDS = vocabulary(["human", "host_automatic"]);
1328
- function isCealHostInputKind(value) {
1329
- return typeof value === "string" && CEAL_HOST_INPUT_KINDS.some((kind) => kind === value);
1330
- }
1331
1646
  var CEAL_HOST_OBSERVATION_REF_PREFIX = "host-observation:";
1332
1647
  var HOST_TOKEN_OBSERVATION_BREAKDOWN_KEYS = vocabulary(["cached_input", "cache_write_input", "reasoning_output"]);
1333
1648
  function validateCealHostObservationDraft(value) {
@@ -1338,7 +1653,7 @@ function validateCealHostObservationDraft(value) {
1338
1653
  assertHostObservationValue(record6.value, record6.observation_kind);
1339
1654
  }
1340
1655
  var CEAL_REQUEST_MAX_OBSERVATION_VALUE_BYTES = 64 * 1024;
1341
- var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "cost", "timing"]);
1656
+ var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "timing"]);
1342
1657
  function isCealHostPromptObservationValue(value) {
1343
1658
  return isOperationRecord(value) && Object.keys(value).length === 1 && isCealDigest(value.body_sha256);
1344
1659
  }
@@ -1365,10 +1680,6 @@ function inspectHostKeys(value) {
1365
1680
  // packages/ceal-client-protocol/src/host-prompt-ref.ts
1366
1681
  var CEAL_HOST_PROMPT_DIGEST_PATTERN = "^[a-f0-9]{64}$";
1367
1682
  var PROMPT_DIGEST = new RegExp(CEAL_HOST_PROMPT_DIGEST_PATTERN, "u");
1368
- function hostProducerPromptRef(promptSha256) {
1369
- if (!PROMPT_DIGEST.test(promptSha256)) throw new TypeError("Canonical Host prompt digest is invalid.");
1370
- return `ceal-host-prompt:${promptSha256}`;
1371
- }
1372
1683
 
1373
1684
  // packages/ceal-client-protocol/src/request-effect-approval.ts
1374
1685
  var CEAL_EFFECT_APPROVAL_DECISION_CHECK_PHRASES = Object.freeze([
@@ -1461,18 +1772,115 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
1461
1772
  };
1462
1773
 
1463
1774
  // packages/ceal-client-protocol/src/host-invocation-authentication.ts
1464
- var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = "ceal.host_invocation_authentication_result.v1";
1775
+ var hostInvocationAuthenticationContract = CEAL_CLIENT_WIRE_CONTRACT.host_invocation_authentication;
1776
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.request_schema_name, fields: hostInvocationAuthenticationContract.request_fields });
1777
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.request_fields]);
1778
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.result_schema_name, fields: hostInvocationAuthenticationContract.result_fields });
1779
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.result_fields]);
1780
+ var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE = "^(?:0|[1-9][0-9]{0,8})$";
1781
+ var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN = new RegExp(CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE, "u");
1465
1782
  var CEAL_HOST_INVOCATION_CONTEXT_PATH = "/api/ceal/v2/host-invocation-context";
1466
- var CEAL_HOST_INGRESS_PATH = "/api/ceal/v2/host-ingress";
1467
- var CEAL_HOST_NONCE_ENCODED_CHARACTERS = 43;
1468
- var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(`^[A-Za-z0-9_-]{${CEAL_HOST_NONCE_ENCODED_CHARACTERS}}$`, "u");
1469
- var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(`^ceal_context_[A-Za-z0-9_-]{${CEAL_HOST_NONCE_ENCODED_CHARACTERS}}$`, "u");
1470
- var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = new TextEncoder().encode(`${JSON.stringify({
1471
- schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA,
1472
- context_token: `ceal_context_${"A".repeat(CEAL_HOST_NONCE_ENCODED_CHARACTERS)}`,
1473
- expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
1474
- })}
1783
+ var CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA = "ceal.gateway_host_invocation_context_request.v2";
1784
+ var CEAL_HOST_INVOCATION_CONTEXT_REQUEST_MAX_BYTES = 16 * 1024;
1785
+ var CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES = 2 * 1024;
1786
+ var CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES = 32;
1787
+ var HOST_CONTEXT_EXPIRY_MAX_CHARS = 64;
1788
+ var CEAL_HOST_INVOCATION_CONTEXT_VISIBLE_OUTPUT_MAX_BYTES = CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
1789
+ var CEAL_HOST_CONTEXT_TOKEN_PREFIX = "ceal_context_";
1790
+ function decodeCealHostInvocationContextRequest(value) {
1791
+ if (!isCealJsonRecord(value)) throw invalidInvocationContextRequest();
1792
+ requireCealClientWireSchemaVersion(value.schema_version, CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA);
1793
+ if (!hasInvocationContextRequestKeys(value)) throw invalidInvocationContextRequest();
1794
+ const clientNonce = requireClientNonce(value.client_nonce);
1795
+ const expiresAt = requireContextExpiration(value.context_expires_at);
1796
+ const hostBinding = requireHostBinding(value.host_binding);
1797
+ const messageWindow = requireHumanMessageWindow(value.human_message_window);
1798
+ const lastRequestRef = optionalSafeReference(value.last_seen_request_ref);
1799
+ const visibleOutputMaxBytes = optionalVisibleOutputBudget(value.visible_output_max_bytes);
1800
+ return Object.freeze({
1801
+ schema_version: CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA,
1802
+ client_nonce: clientNonce,
1803
+ context_expires_at: expiresAt,
1804
+ host_binding: Object.freeze({ ...hostBinding }),
1805
+ human_message_window: messageWindow,
1806
+ ...lastRequestRef === void 0 ? {} : { last_seen_request_ref: lastRequestRef },
1807
+ ...visibleOutputMaxBytes === void 0 ? {} : { visible_output_max_bytes: visibleOutputMaxBytes }
1808
+ });
1809
+ }
1810
+ function hasInvocationContextRequestKeys(value) {
1811
+ const required = ["schema_version", "client_nonce", "context_expires_at", "host_binding", "human_message_window"];
1812
+ const optional = ["last_seen_request_ref", "visible_output_max_bytes"];
1813
+ const optionalKeys = Object.keys(value).filter((key) => optional.includes(key));
1814
+ return hasExactKeys(value, [...required, ...optionalKeys]) && required.every((key) => Object.hasOwn(value, key)) && value.schema_version === CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA;
1815
+ }
1816
+ function requireClientNonce(value) {
1817
+ if (typeof value !== "string" || !CEAL_HOST_CLIENT_NONCE_PATTERN.test(value)) throw invalidInvocationContextRequest();
1818
+ return value;
1819
+ }
1820
+ function requireContextExpiration(value) {
1821
+ if (typeof value !== "string" || value.length > HOST_CONTEXT_EXPIRY_MAX_CHARS || !Number.isFinite(Date.parse(value))) throw invalidInvocationContextRequest();
1822
+ return value;
1823
+ }
1824
+ function requireHostBinding(value) {
1825
+ 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();
1826
+ return { host_source_ref: value.host_source_ref, host_session_ref: value.host_session_ref, host_turn_ref: value.host_turn_ref };
1827
+ }
1828
+ function requireHumanMessageWindow(value) {
1829
+ 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();
1830
+ const messages = value.messages.map(decodeHumanMessage);
1831
+ const bytes2 = new TextEncoder().encode(JSON.stringify(messages)).byteLength;
1832
+ if (bytes2 > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES) throw invalidInvocationContextRequest();
1833
+ return Object.freeze({ messages: Object.freeze(messages), truncated: value.truncated });
1834
+ }
1835
+ function decodeHumanMessage(candidate) {
1836
+ 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();
1837
+ return Object.freeze({ host_turn_ref: candidate.host_turn_ref, host_input_ref: candidate.host_input_ref, text: candidate.text });
1838
+ }
1839
+ function optionalSafeReference(value) {
1840
+ if (value === void 0) return void 0;
1841
+ if (!safeReference(value)) throw invalidInvocationContextRequest();
1842
+ return value;
1843
+ }
1844
+ function safeReference(value) {
1845
+ return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
1846
+ }
1847
+ function exactOptionalKeys(value, required, optional = []) {
1848
+ const keys = Object.keys(value);
1849
+ return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
1850
+ }
1851
+ function optionalVisibleOutputBudget(value) {
1852
+ if (value === void 0) return void 0;
1853
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > CEAL_HOST_INVOCATION_CONTEXT_VISIBLE_OUTPUT_MAX_BYTES) throw invalidInvocationContextRequest();
1854
+ return value;
1855
+ }
1856
+ function invalidInvocationContextRequest() {
1857
+ return new TypeError("The Host invocation context request is invalid.");
1858
+ }
1859
+ function deriveCealHostInvocationNonceContract(contract = CEAL_CLIENT_WIRE_CONTRACT) {
1860
+ const clientNonce = contract.host_invocation_authentication.client_nonce;
1861
+ return {
1862
+ encoded_characters: clientNonce.encoded_characters,
1863
+ client_nonce_pattern_source: `^[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`,
1864
+ context_token_pattern_source: `^${CEAL_HOST_CONTEXT_TOKEN_PREFIX}[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`
1865
+ };
1866
+ }
1867
+ var hostNonceContract = deriveCealHostInvocationNonceContract();
1868
+ var CEAL_HOST_NONCE_ENCODED_CHARACTERS = hostNonceContract.encoded_characters;
1869
+ var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(hostNonceContract.client_nonce_pattern_source, "u");
1870
+ var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(hostNonceContract.context_token_pattern_source, "u");
1871
+ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = deriveCealHostInvocationAuthenticationResultMaxBytes();
1872
+ function deriveCealHostInvocationAuthenticationResultMaxBytes(contract = CEAL_CLIENT_WIRE_CONTRACT) {
1873
+ const hostAuthentication = contract.host_invocation_authentication;
1874
+ const values = {
1875
+ schema_version: deriveCealClientWireSchemaVersion({ schema_name: hostAuthentication.result_schema_name, fields: hostAuthentication.result_fields }),
1876
+ context_token: `${CEAL_HOST_CONTEXT_TOKEN_PREFIX}${"A".repeat(hostAuthentication.client_nonce.encoded_characters)}`,
1877
+ expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
1878
+ };
1879
+ const fields = hostAuthentication.result_fields;
1880
+ if (fields.some((field) => !Object.hasOwn(values, field))) throw new TypeError("Host invocation result fields have no bounded sample value.");
1881
+ return new TextEncoder().encode(`${JSON.stringify(Object.fromEntries(fields.map((field) => [field, values[field]])))}
1475
1882
  `).byteLength;
1883
+ }
1476
1884
  var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
1477
1885
  "request_authority_unavailable",
1478
1886
  "request_missing",
@@ -1496,6 +1904,7 @@ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
1496
1904
  "host_credential_refused",
1497
1905
  "gateway_unavailable",
1498
1906
  "gateway_rate_limited",
1907
+ "contract_mismatch",
1499
1908
  "gateway_refused",
1500
1909
  "gateway_response_invalid",
1501
1910
  "producer_frame_invalid",
@@ -1510,76 +1919,43 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
1510
1919
  invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
1511
1920
  options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
1512
1921
  session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1513
- 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)" }),
1514
- context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1515
- no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
1516
- context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1517
- context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "refresh_host_invocation_context" }),
1922
+ context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "retry" }),
1923
+ context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
1924
+ // `no_binding` now means the producer has no real current Host session. The
1925
+ // Host Relay never uses it for an uncreated Gateway Request; there is no
1926
+ // useful retry until the consumer is running inside a Host session.
1927
+ no_binding: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1928
+ context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
1929
+ context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "retry" }),
1518
1930
  host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
1519
1931
  host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
1520
1932
  gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
1521
1933
  gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
1934
+ contract_mismatch: Object.freeze({ classification: "contract_mismatch", next_action: "upgrade_ceal_client" }),
1522
1935
  gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
1523
1936
  gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
1524
- 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" }),
1525
- 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" }),
1937
+ producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin" }),
1938
+ relay_permission_denied: Object.freeze({ classification: "host_relay_permission_denied", next_action: "rerun_outside_agent_sandbox" }),
1526
1939
  relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
1527
1940
  transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
1528
1941
  mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
1529
1942
  relay_failed: Object.freeze({ classification: "host_relay_failed", next_action: "answer_with_gap" }),
1530
1943
  host_refusal_invalid: Object.freeze({ classification: "host_refusal_invalid", next_action: "answer_with_gap" })
1531
1944
  });
1532
- function projectCealHostAuthenticationCause(diagnostic) {
1533
- return projectRelayCodeCause(diagnostic) ?? projectGatewayCodeCause(diagnostic) ?? projectGatewayStatusCause(diagnostic) ?? "mint_failed";
1534
- }
1535
- function projectRelayCodeCause(diagnostic) {
1536
- switch (diagnostic.relayCode) {
1537
- case "credential":
1538
- return "host_credential_refused";
1539
- case "configuration":
1540
- return "options_invalid";
1541
- case "payload_rejected":
1542
- return "producer_frame_invalid";
1543
- case "invalid_response":
1544
- return "gateway_response_invalid";
1545
- case "gateway_refused":
1546
- return projectGatewayRefusedCause(diagnostic);
1547
- case "gateway_unavailable":
1548
- return diagnostic.exhausted === true ? void 0 : "gateway_unavailable";
1549
- case void 0:
1550
- case "":
1551
- return void 0;
1552
- default:
1553
- return "mint_failed";
1554
- }
1555
- }
1556
- function projectGatewayRefusedCause(diagnostic) {
1557
- if (diagnostic.gatewayStatus === 401 || diagnostic.gatewayStatus === 403) return "host_credential_refused";
1558
- const stale = projectGatewayCodeCause(diagnostic);
1559
- if (stale !== void 0) return stale;
1560
- if (diagnostic.exhausted === true && (diagnostic.gatewayStatus === 408 || diagnostic.gatewayStatus === 429)) return "gateway_rate_limited";
1561
- return "gateway_refused";
1562
- }
1563
- function projectGatewayCodeCause(diagnostic) {
1564
- if (diagnostic.gatewayCode === "request_context_refused" && diagnostic.gatewayReason !== void 0 && isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, diagnostic.gatewayReason)) return "context_stale";
1565
- if (diagnostic.gatewayCode === "context_expired") return "context_expired";
1566
- if (diagnostic.gatewayCode === "host_source_refused") return "host_source_refused";
1567
- return void 0;
1568
- }
1569
- function projectGatewayStatusCause(diagnostic) {
1570
- if (diagnostic.gatewayStatus === 401 || diagnostic.gatewayStatus === 403) return "host_credential_refused";
1571
- if (diagnostic.exhausted === true && (diagnostic.gatewayStatus === 408 || diagnostic.gatewayStatus === 429)) return "gateway_rate_limited";
1572
- return void 0;
1573
- }
1574
1945
  function decodeCealHostInvocationAuthenticationResult(value, now = /* @__PURE__ */ new Date()) {
1575
- 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)) {
1946
+ return decodeHostInvocationAuthenticationResult(value, CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_FIELDS, now);
1947
+ }
1948
+ function decodeHostInvocationAuthenticationResult(value, fields, now) {
1949
+ if (!isCealJsonRecord(value)) throw new TypeError("The Host invocation authentication result is invalid.");
1950
+ requireCealClientWireSchemaVersion(value.schema_version, CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA);
1951
+ 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)) {
1576
1952
  throw new TypeError("The Host invocation authentication result is invalid.");
1577
1953
  }
1578
1954
  return Object.freeze({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA, context_token: value.context_token, expires_at: value.expires_at });
1579
1955
  }
1580
1956
  function validFutureTimestamp(value, now) {
1581
- const timestamp3 = Date.parse(value);
1582
- return Number.isFinite(timestamp3) && new Date(timestamp3).toISOString() === value && timestamp3 > now.getTime();
1957
+ const timestamp2 = Date.parse(value);
1958
+ return Number.isFinite(timestamp2) && new Date(timestamp2).toISOString() === value && timestamp2 > now.getTime();
1583
1959
  }
1584
1960
  function exactKeys(value, expected) {
1585
1961
  const keys = Object.keys(value);
@@ -1587,16 +1963,11 @@ function exactKeys(value, expected) {
1587
1963
  }
1588
1964
 
1589
1965
  // packages/ceal-client-protocol/src/host-service-diagnostic.ts
1590
- var CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA = "ceal.host_service_diagnostic.v1";
1591
1966
  var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
1592
1967
  var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
1593
1968
  "hook_invoked",
1594
1969
  "registration_written",
1595
1970
  "hook_failed",
1596
- "watcher_turn_observed",
1597
- "current_prompt_registered",
1598
- "current_prompt_registration_failed",
1599
- "current_prompt_capacity_exhausted",
1600
1971
  "activation_recorded",
1601
1972
  "activation_failed",
1602
1973
  "watcher_scan_failed",
@@ -1619,6 +1990,9 @@ var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "un
1619
1990
  var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
1620
1991
  var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "items_path", "row_count", "result_complete", "coverage", "local_result_root", "model_disclosure", "continue_argv", "how_to_read"]);
1621
1992
 
1993
+ // packages/ceal-client-protocol/src/discovery-manifest.ts
1994
+ var DISCOVERY_MANIFEST_KEYS = CEAL_CLIENT_WIRE_CONTRACT.command_response.discovery_manifest_fields;
1995
+
1622
1996
  // packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
1623
1997
  var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
1624
1998
 
@@ -1677,6 +2051,7 @@ var ADMIN_UNAVAILABLE_REASONS = vocabulary([
1677
2051
  "legacy",
1678
2052
  "invalid_observation",
1679
2053
  "redacted",
2054
+ "organization_setting_disabled",
1680
2055
  "owner_unavailable",
1681
2056
  "unit_unknown",
1682
2057
  "retention_gap",
@@ -1706,15 +2081,13 @@ var ADMIN_APPROVAL_DISPOSITIONS = CEAL_OPERATION_APPROVAL_DISPOSITIONS;
1706
2081
  var ADMIN_APPROVAL_POSTURES = vocabulary([...ADMIN_APPROVAL_DISPOSITIONS, "not_applicable"]);
1707
2082
  var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "approval", "effect", "readback"]);
1708
2083
 
1709
- // packages/ceal-client-protocol/src/actor-kind.ts
1710
- var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
1711
-
1712
2084
  // packages/ceal-client-protocol/src/device-enrollment.ts
1713
2085
  var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
1714
2086
  var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
1715
2087
 
1716
2088
  // packages/ceal-client-protocol/src/personal-client-session.ts
1717
2089
  var REFRESH_FAILURE_CODES = new Set(CEAL_REFRESH_FAILURE_CODES);
2090
+ var REVOKE_FAILURE_CODES = new Set(CEAL_CLIENT_REVOKE_FAILURE_CODES);
1718
2091
 
1719
2092
  // packages/ceal-client-protocol/src/index.ts
1720
2093
  var MAX_REQUEST_BYTES = 32 * 1024;
@@ -1750,19 +2123,175 @@ function errorCode(error) {
1750
2123
  }
1751
2124
 
1752
2125
  // host/service-log.ts
1753
- import { copyFile, open, stat, truncate } from "node:fs/promises";
1754
- import { isAbsolute } from "node:path";
1755
- var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
1756
- function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
1757
- 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 } : {} })}
1758
- `;
2126
+ import { copyFile, mkdir, stat, truncate, appendFile } from "node:fs/promises";
2127
+ import { dirname as dirname2, isAbsolute, posix, win32 as win322 } from "node:path";
2128
+
2129
+ // host/secure-file.ts
2130
+ import { execFile } from "node:child_process";
2131
+ import { constants } from "node:fs";
2132
+ import { lstat, open } from "node:fs/promises";
2133
+ import { dirname, win32 } from "node:path";
2134
+ import { fileURLToPath } from "node:url";
2135
+ var SecureFileCustodyError = class extends Error {
2136
+ name = "SecureFileCustodyError";
2137
+ reason;
2138
+ constructor(reason, message) {
2139
+ super(message);
2140
+ this.reason = reason;
2141
+ }
2142
+ get code() {
2143
+ if (this.reason === "reparse_point") return "ELOOP";
2144
+ if (this.reason === "not_found") return "ENOENT";
2145
+ if (this.reason === "too_large") return "secure_file_too_large";
2146
+ if (this.reason === "already_exists") return "EEXIST";
2147
+ return this.reason === "not_private" ? "secure_file_not_private" : "secure_file_unavailable";
2148
+ }
2149
+ };
2150
+ var WINDOWS_SECUREFS_REFUSAL_SCHEMA = "ceal.host_securefs_refusal.v1";
2151
+ var WINDOWS_SECUREFS_MAX_RANDOM_FILE_BYTES = 4096;
2152
+ var WINDOWS_SECUREFS_TIMEOUT_MS = 1e4;
2153
+ var WINDOWS_SECUREFS_DIAGNOSTIC_BYTES = 4096;
2154
+ function secureOpenFlags(flags, platform) {
2155
+ if (platform === "win32") return flags;
2156
+ const nofollow = constants.O_NOFOLLOW;
2157
+ if (typeof nofollow === "number" && nofollow !== 0) return flags | nofollow;
2158
+ throw new Error("Secure file reads require O_NOFOLLOW on this platform.");
2159
+ }
2160
+ async function openNoFollow(path, flags = constants.O_RDONLY, mode, platform = process.platform, _custody = {}) {
2161
+ if (platform !== "win32") return await open(path, secureOpenFlags(flags, platform), mode);
2162
+ return await openWindowsIdentityScreened(path, flags, mode);
2163
+ }
2164
+ async function openWindowsIdentityScreened(path, flags, mode) {
2165
+ const handle = await open(path, flags, mode);
2166
+ try {
2167
+ const first = await handle.stat();
2168
+ if (!first.isFile()) throw new SecureFileCustodyError("not_private", "The Windows Host file is not a regular file.");
2169
+ await assertWindowsLinkFree(path, first);
2170
+ await assertWindowsIdentityStable(handle, first, path);
2171
+ return handle;
2172
+ } catch (error) {
2173
+ await handle.close();
2174
+ throw error;
2175
+ }
2176
+ }
2177
+ async function assertWindowsLinkFree(path, first) {
2178
+ const entry2 = await lstat(path);
2179
+ if (entry2.isSymbolicLink() || entry2.dev !== first.dev || entry2.ino !== first.ino) {
2180
+ throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
2181
+ }
2182
+ }
2183
+ async function assertWindowsIdentityStable(handle, first, path) {
2184
+ const second = await handle.stat();
2185
+ if (second.dev !== first.dev || second.ino !== first.ino || second.size !== first.size || second.mtimeMs !== first.mtimeMs) {
2186
+ throw new SecureFileCustodyError("unavailable", "The Windows Host file changed while its identity was checked.");
2187
+ }
2188
+ const entry2 = await lstat(path);
2189
+ if (entry2.isSymbolicLink()) throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
2190
+ if (entry2.dev !== second.dev || entry2.ino !== second.ino) throw new SecureFileCustodyError("unavailable", "The Windows Host file path no longer names the opened file.");
2191
+ }
2192
+ async function readPrivateFileWithSecurefs(path, maxBytes, custody = {}) {
2193
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) throw new TypeError("The securefs read bound must be a positive safe integer.");
2194
+ const result2 = await runWindowsSecurefs(["internal", "securefs", "read-private-file", path, String(maxBytes)], maxBytes, custody);
2195
+ if (result2.exit_code === 0 && result2.stderr.length === 0 && result2.stdout.length <= maxBytes) return result2.stdout;
2196
+ if (result2.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned bytes with a refusal.");
2197
+ if (result2.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command exceeded its output contract.");
2198
+ throw securefsRefusal(result2.stderr);
2199
+ }
2200
+ async function createRandomPrivateFileWithSecurefs(path, byteLength2, custody = {}) {
2201
+ 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.`);
2202
+ const result2 = await runWindowsSecurefs(["internal", "securefs", "create-random-private-file", path, String(byteLength2)], 0, custody);
2203
+ if (result2.exit_code === 0 && result2.stdout.length === 0 && result2.stderr.length === 0) return;
2204
+ if (result2.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned unexpected creation bytes.");
2205
+ if (result2.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an invalid creation result.");
2206
+ throw securefsRefusal(result2.stderr);
2207
+ }
2208
+ async function isPrivateWindowsDirectory(path, custody = {}) {
2209
+ await lstat(path);
2210
+ try {
2211
+ const result2 = await runWindowsSecurefs(["internal", "securefs", "verify-private-dir", path], 0, custody);
2212
+ if (result2.exit_code === 0 && result2.stdout.length === 0 && result2.stderr.length === 0) return true;
2213
+ if (result2.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host directory proof returned unexpected bytes.");
2214
+ if (result2.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host directory proof returned an invalid result.");
2215
+ const refusal = securefsRefusal(result2.stderr);
2216
+ if (refusal.reason === "not_private" || refusal.reason === "reparse_point") return false;
2217
+ throw refusal;
2218
+ } catch (error) {
2219
+ if (error instanceof SecureFileCustodyError) throw error;
2220
+ throw new SecureFileCustodyError("unavailable", "The Windows Host directory custody proof is unavailable.");
2221
+ }
2222
+ }
2223
+ async function runWindowsSecurefs(args, maxBytes, custody) {
2224
+ if (custody.run_securefs_command !== void 0) return await custody.run_securefs_command(args);
2225
+ const executable = custody.securefs_executable ?? resolveInstalledHostExecutable();
2226
+ const processArgs = [...custody.securefs_args_prefix ?? [], ...args];
2227
+ return await new Promise((resolve, reject) => {
2228
+ execFile(executable, processArgs, { encoding: "buffer", timeout: WINDOWS_SECUREFS_TIMEOUT_MS, maxBuffer: maxBytes + WINDOWS_SECUREFS_DIAGNOSTIC_BYTES }, (error, stdout, stderr) => {
2229
+ const exitCode = error === null ? 0 : typeof error.code === "number" ? error.code : null;
2230
+ if (exitCode === null) {
2231
+ reject(new SecureFileCustodyError("unavailable", "The installed Windows Host securefs command could not run."));
2232
+ return;
2233
+ }
2234
+ resolve({ exit_code: exitCode, stdout: Buffer.from(stdout), stderr: Buffer.from(stderr) });
2235
+ });
2236
+ });
2237
+ }
2238
+ function securefsRefusal(stderr) {
2239
+ let value;
1759
2240
  try {
1760
- (sink ?? defaultHostServiceDiagnosticSink)(line);
2241
+ value = JSON.parse(stderr.toString("utf8"));
1761
2242
  } catch {
2243
+ return new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an unreadable refusal.");
1762
2244
  }
2245
+ 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.");
2246
+ if (value.code === "reparse_point") return new SecureFileCustodyError("reparse_point", "The Windows Host securefs file is a reparse point.");
2247
+ if (value.code === "not_found") return new SecureFileCustodyError("not_found", "The Windows Host securefs file was not found.");
2248
+ if (value.code === "too_large") return new SecureFileCustodyError("too_large", "The Windows Host securefs file exceeds its read bound.");
2249
+ if (value.code === "already_exists") return new SecureFileCustodyError("already_exists", "The Windows Host securefs target already exists.");
2250
+ if (value.code === "not_private") return new SecureFileCustodyError("not_private", "The Windows Host securefs file is not private to the current user.");
2251
+ return new SecureFileCustodyError("unavailable", "The Windows Host securefs command refused the file read.");
2252
+ }
2253
+ function resolveInstalledHostExecutable() {
2254
+ const privateRuntimeRoot = dirname(fileURLToPath(import.meta.url));
2255
+ return win32.join(privateRuntimeRoot, "..", "..", "bin", "ceal-host.exe");
2256
+ }
2257
+ function isRecord(value) {
2258
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2259
+ }
2260
+ function isOwnedBy(uid, expectedUid) {
2261
+ return Number.isSafeInteger(uid) && Number.isSafeInteger(expectedUid) && uid === expectedUid;
2262
+ }
2263
+ function isSelfOwned(uid, platform = process.platform, _handle) {
2264
+ if (platform === "win32" || typeof process.geteuid !== "function") return false;
2265
+ return isOwnedBy(uid, process.geteuid());
2266
+ }
2267
+ function isPrivateFileMode(mode, platform = process.platform, _handle) {
2268
+ if (platform === "win32") return false;
2269
+ return (mode & 63) === 0;
1763
2270
  }
1764
- function defaultHostServiceDiagnosticSink(line) {
1765
- process.stderr.write(line);
2271
+ async function isPrivateDirectory(path, platform = process.platform, custody = {}) {
2272
+ if (platform === "win32") return await isPrivateWindowsDirectory(path, custody);
2273
+ const entry2 = await lstat(path);
2274
+ return entry2.isDirectory() && !entry2.isSymbolicLink() && isSelfOwned(entry2.uid, platform) && isPrivateFileMode(entry2.mode, platform);
2275
+ }
2276
+
2277
+ // host/service-log.ts
2278
+ var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
2279
+ var HOST_SERVICE_LOG_ARGUMENT = "--ceal-host-service-log";
2280
+ var HostServiceLogArgumentError = class extends Error {
2281
+ constructor() {
2282
+ super("The Host service log argument must occur once with an absolute normalized path.");
2283
+ this.name = "HostServiceLogArgumentError";
2284
+ }
2285
+ };
2286
+ function consumeHostServiceLogArgument(args, platform = process.platform) {
2287
+ const indexes = args.flatMap((value, index2) => value === HOST_SERVICE_LOG_ARGUMENT ? [index2] : []);
2288
+ if (indexes.length === 0) return { args: [...args] };
2289
+ const index = indexes[0];
2290
+ if (index === void 0) throw new HostServiceLogArgumentError();
2291
+ const path = args[index + 1];
2292
+ const pathApi = platform === "win32" ? win322 : posix;
2293
+ if (indexes.length !== 1 || path === void 0 || !pathApi.isAbsolute(path) || pathApi.normalize(path) !== path) throw new HostServiceLogArgumentError();
2294
+ return { args: [...args.slice(0, index), ...args.slice(index + 2)], path };
1766
2295
  }
1767
2296
  var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
1768
2297
  var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
@@ -1786,8 +2315,9 @@ function boundHostServiceLog(environment = process.env) {
1786
2315
  const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
1787
2316
  if (path === void 0 || !isAbsolute(path)) return () => void 0;
1788
2317
  let reported = false;
2318
+ const ready = ensureHostServiceLogDirectory(path);
1789
2319
  const check = () => {
1790
- rotateHostServiceLog(path).catch((error) => {
2320
+ ready.then(() => rotateHostServiceLog(path)).catch((error) => {
1791
2321
  if (reported) return;
1792
2322
  reported = true;
1793
2323
  process.stderr.write(`[host-service-log] rotation_failed code=${errorCode2(error)}
@@ -1799,8 +2329,31 @@ function boundHostServiceLog(environment = process.env) {
1799
2329
  timer.unref();
1800
2330
  return () => clearInterval(timer);
1801
2331
  }
1802
- var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
1803
- function isNotFound(error) {
2332
+ function writeHostServiceLogLine(line, environment = process.env) {
2333
+ if (process.platform !== "win32") return;
2334
+ const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
2335
+ if (path === void 0 || !win322.isAbsolute(path) || win322.normalize(path) !== path) return;
2336
+ void appendHostServiceLogLine(path, line).catch(() => void 0);
2337
+ }
2338
+ async function appendHostServiceLogLine(path, line) {
2339
+ if (!isAbsolute(path)) return;
2340
+ await ensureHostServiceLogDirectory(path);
2341
+ await appendFile(path, line);
2342
+ }
2343
+ var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
2344
+ var hostServiceLogDirectories = /* @__PURE__ */ new Map();
2345
+ function ensureHostServiceLogDirectory(path) {
2346
+ const directory = dirname2(path);
2347
+ const existing = hostServiceLogDirectories.get(directory);
2348
+ if (existing !== void 0) return existing;
2349
+ const pending = mkdir(directory, { recursive: true, mode: 448 }).then(() => void 0).catch((error) => {
2350
+ hostServiceLogDirectories.delete(directory);
2351
+ throw error;
2352
+ });
2353
+ hostServiceLogDirectories.set(directory, pending);
2354
+ return pending;
2355
+ }
2356
+ function isNotFound(error) {
1804
2357
  return errorCode2(error) === "ENOENT";
1805
2358
  }
1806
2359
  function errorCode2(error) {
@@ -1808,13 +2361,13 @@ function errorCode2(error) {
1808
2361
  }
1809
2362
 
1810
2363
  // host/runtime-source-paths.ts
1811
- import { createHash as createHash2 } from "node:crypto";
1812
- import { posix as posix2, win32 as win322 } from "node:path";
2364
+ import { createHash as createHash3 } from "node:crypto";
2365
+ import { posix as posix3, win32 as win324 } from "node:path";
1813
2366
 
1814
2367
  // host/absolute-path.ts
1815
- import { posix, win32 } from "node:path";
2368
+ import { posix as posix2, win32 as win323 } from "node:path";
1816
2369
  function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allowRoot = false) {
1817
- const pathApi = flavor === "win32" || flavor === "windows" ? win32 : posix;
2370
+ const pathApi = flavor === "win32" || flavor === "windows" ? win323 : posix2;
1818
2371
  return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
1819
2372
  }
1820
2373
 
@@ -1826,28 +2379,22 @@ function hostSourceUuid(sourceRef) {
1826
2379
  function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
1827
2380
  const sourceId = hostSourceUuid(sourceRef);
1828
2381
  if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
1829
- const path = platform === "win32" ? win322 : posix2;
2382
+ const path = platform === "win32" ? win324 : posix3;
1830
2383
  const sourcesRoot = path.join(stateRoot, "sources");
1831
2384
  const sourceRoot = path.join(sourcesRoot, sourceId);
1832
2385
  const runtimeRoot = path.join(stateRoot, "run");
1833
- const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash2("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
1834
- assertHostRuntimeIpcLength(endpoint, platform);
2386
+ const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash3("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
1835
2387
  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 };
1836
2388
  }
1837
- function assertHostRuntimeIpcLength(endpoint, platform) {
1838
- if (platform === "win32") return;
1839
- const maximum = platform === "linux" ? 107 : 103;
1840
- 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" });
1841
- }
1842
2389
  function assertHostRelaySourcePaths(stateRoot, config, platform = process.platform) {
1843
2390
  const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
1844
- if (config.state_root !== paths.relayRoot || config.ipc_endpoint !== paths.endpoint) throw new TypeError("Host Relay state and IPC must belong to its configured source.");
2391
+ if (config.state_root !== paths.relayRoot || platform !== "win32" && config.ipc_endpoint !== paths.endpoint) throw new TypeError("Host Relay state and IPC must belong to its configured source.");
2392
+ return paths.endpoint;
1845
2393
  }
1846
2394
 
1847
2395
  // host/relay/src/host-relay-runtime.ts
1848
- import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
1849
- import { lstat as lstat4, open as open5 } from "node:fs/promises";
1850
- import { dirname as dirname4, join as join7 } from "node:path";
2396
+ import { constants as constants4 } from "node:fs";
2397
+ import { dirname as dirname7, join as join6 } from "node:path";
1851
2398
 
1852
2399
  // host/abortable-wait.ts
1853
2400
  function waitForAbort(signal, milliseconds) {
@@ -1933,13 +2480,13 @@ function skipWhitespace(text, start) {
1933
2480
  }
1934
2481
 
1935
2482
  // host/relay/src/producer-frame.ts
1936
- import { createHash as createHash3 } from "node:crypto";
2483
+ import { createHash as createHash4 } from "node:crypto";
1937
2484
  var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
1938
2485
  var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
1939
2486
  var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
1940
2487
  var SAFE_REF2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
1941
2488
  var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
1942
- var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "user_input", "turn_terminal", "producer_status"];
2489
+ var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "turn_terminal", "producer_status"];
1943
2490
  var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
1944
2491
  function isHostProducerFrameKind(value) {
1945
2492
  return typeof value === "string" && FRAME_KINDS.has(value);
@@ -1991,58 +2538,15 @@ function decodeHostProducerFrame(input) {
1991
2538
  return value;
1992
2539
  }
1993
2540
  function hostProducerFrameHash(frame) {
1994
- return createHash3("sha256").update(JSON.stringify(frame), "utf8").digest("hex");
1995
- }
1996
- function extractHostBindingSelector(frame) {
1997
- if (frame.frame_kind !== "user_input") return null;
1998
- if (!PRODUCERS.has(frame.producer_id) || !safeRef(frame.producer_installation_ref)) return null;
1999
- const payload = frame.payload;
2000
- if (!safeRef(payload.local_session_ref) || !safeRef(payload.local_turn_ref)) return null;
2001
- const aliases = payload.local_turn_aliases;
2002
- if (aliases === void 0) {
2003
- return Object.freeze({
2004
- producer_id: frame.producer_id,
2005
- producer_installation_ref: frame.producer_installation_ref,
2006
- local_session_ref: payload.local_session_ref,
2007
- local_turn_ref: payload.local_turn_ref,
2008
- local_turn_aliases: Object.freeze([])
2009
- });
2010
- }
2011
- if (!Array.isArray(aliases) || aliases.length > 16 || !aliases.every(safeRef) || new Set(aliases).size !== aliases.length) return null;
2012
- return Object.freeze({
2013
- producer_id: frame.producer_id,
2014
- producer_installation_ref: frame.producer_installation_ref,
2015
- local_session_ref: payload.local_session_ref,
2016
- local_turn_ref: payload.local_turn_ref,
2017
- local_turn_aliases: Object.freeze([...aliases])
2018
- });
2019
- }
2020
- function hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef) {
2021
- if (!opaqueHostSourceRef(hostSourceRef)) throw new TypeError("Gateway host source reference is invalid.");
2022
- const host_session_ref = localSessionRef === void 0 ? void 0 : `ceal-host-session:${cealRequestSha256({ host_source_ref: hostSourceRef, producer_id: frame.producer_id, producer_installation_ref: frame.producer_installation_ref, local_session_ref: localSessionRef })}`;
2023
- const host_turn_ref = host_session_ref === void 0 || localTurnRef === void 0 ? void 0 : `ceal-host-turn:${cealRequestSha256({ host_session_ref, local_turn_ref: localTurnRef })}`;
2024
- const host_input_ref = host_turn_ref === void 0 || localInputRef === void 0 ? void 0 : `ceal-host-input:${cealRequestSha256({ host_turn_ref, local_input_ref: localInputRef })}`;
2025
- const submission_ref = `ceal-host-frame:${cealRequestSha256({ host_source_ref: hostSourceRef, producer_id: frame.producer_id, producer_installation_ref: frame.producer_installation_ref, sequence: frame.sequence })}`;
2026
- return { host_session_ref: host_session_ref ?? "", ...host_turn_ref === void 0 ? {} : { host_turn_ref }, ...host_input_ref === void 0 ? {} : { host_input_ref }, submission_ref };
2027
- }
2028
- function hostProducerSessionRef(hostSourceRef, frame, localSessionRef) {
2029
- return requiredIdentity(hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef).host_session_ref);
2030
- }
2031
- function hostProducerTurnRef(hostSourceRef, frame, localSessionRef, localTurnRef) {
2032
- return requiredIdentity(hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef).host_turn_ref);
2033
- }
2034
- function hostProducerInputRef(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef) {
2035
- return requiredIdentity(hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef).host_input_ref);
2541
+ return createHash4("sha256").update(JSON.stringify(frame), "utf8").digest("hex");
2036
2542
  }
2037
2543
  function hostProducerSubmissionRef(hostSourceRef, frame) {
2038
- return requiredIdentity(hostProducerIdentityRefs(hostSourceRef, frame).submission_ref);
2544
+ if (!opaqueHostSourceRef(hostSourceRef)) throw new TypeError("Gateway host source reference is invalid.");
2545
+ return `ceal-host-frame:${cealRequestSha256({ host_source_ref: hostSourceRef, producer_id: frame.producer_id, producer_installation_ref: frame.producer_installation_ref, sequence: frame.sequence })}`;
2039
2546
  }
2040
2547
  function opaqueHostSourceRef(value) {
2041
2548
  return typeof value === "string" && SAFE_REF2.test(value) && !value.startsWith("ceal-host-source:");
2042
2549
  }
2043
- function isHostSafeRef(value) {
2044
- return safeRef(value);
2045
- }
2046
2550
  var BOUNDED_REFUSAL_WORD = /^[a-z][a-z0-9_]{0,63}$/u;
2047
2551
  function isBoundedRefusalWord(value) {
2048
2552
  return typeof value === "string" && BOUNDED_REFUSAL_WORD.test(value);
@@ -2059,16 +2563,13 @@ var HOST_RELAY_GATEWAY_ERROR_CODES = [
2059
2563
  "gateway_unavailable",
2060
2564
  "gateway_refused",
2061
2565
  "invalid_response",
2566
+ "contract_mismatch",
2062
2567
  "payload_rejected"
2063
2568
  ];
2064
2569
  function isRelayGatewayErrorCode(value) {
2065
2570
  return isVocabularyWord(HOST_RELAY_GATEWAY_ERROR_CODES, value);
2066
2571
  }
2067
2572
  var RELAY_DELIVERY_DIAGNOSTIC_KEYS = ["relayCode", "gatewayStatus", "gatewayCode", "gatewayReason", "exhausted"];
2068
- function requiredIdentity(value) {
2069
- if (value === void 0) throw new TypeError("Canonical Host identity is incomplete.");
2070
- return value;
2071
- }
2072
2573
  function validateFrame(value) {
2073
2574
  if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
2074
2575
  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)}`);
@@ -2078,7 +2579,7 @@ function validateFrame(value) {
2078
2579
  validatePayload(value.frame_kind, value.payload);
2079
2580
  }
2080
2581
  function validatePayload(kind, value) {
2081
- const valid = kind === "session_started" ? validSession(value) : kind === "turn_started" ? validTurn(value) : kind === "user_input" ? validInput(value) : kind === "turn_terminal" ? validTerminal(value) : validStatus(value);
2582
+ const valid = kind === "session_started" ? validSession(value) : kind === "turn_started" ? validTurn(value) : kind === "turn_terminal" ? validTerminal(value) : validStatus(value);
2082
2583
  if (!valid) throw new HostProducerFrameError("invalid_frame_shape");
2083
2584
  }
2084
2585
  function validSession(value) {
@@ -2087,15 +2588,6 @@ function validSession(value) {
2087
2588
  function validTurn(value) {
2088
2589
  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);
2089
2590
  }
2090
- function validInput(value) {
2091
- 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);
2092
- }
2093
- function validInputObservations(value) {
2094
- return validObservations(value) && Array.isArray(value) && value.filter((item) => record(item) && item.observation_kind === "prompt").length === 1;
2095
- }
2096
- function validInputOptionals(value) {
2097
- return optionalRefs(value.local_turn_aliases) && (value.context_expires_at === void 0 || timestamp(value.context_expires_at));
2098
- }
2099
2591
  function validTerminal(value) {
2100
2592
  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);
2101
2593
  }
@@ -2111,9 +2603,6 @@ function optionalText(value, maxLength) {
2111
2603
  function optionalNonNegative(value) {
2112
2604
  return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
2113
2605
  }
2114
- function optionalRefs(value) {
2115
- return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef);
2116
- }
2117
2606
  function validObservations(value) {
2118
2607
  if (value === void 0) return true;
2119
2608
  if (!Array.isArray(value) || value.length > 32) return false;
@@ -2158,27 +2647,78 @@ function exactOptional(value, required, optional) {
2158
2647
 
2159
2648
  // host/relay/src/relay-admission-store.ts
2160
2649
  import { readFile as readFile2 } from "node:fs/promises";
2161
- import { join as join4 } from "node:path";
2650
+ import { join as join3 } from "node:path";
2651
+
2652
+ // host/relay/src/durable-atomic-replacement.ts
2653
+ import { lstat as lstat2, mkdir as mkdir2, open as open2, rename, unlink } from "node:fs/promises";
2654
+ import { dirname as dirname3 } from "node:path";
2655
+
2656
+ // host/windows-rename-retry.ts
2657
+ import { setTimeout as setTimeout2 } from "node:timers/promises";
2658
+ var RETRY_WINDOW_MS = 1e4;
2659
+ var RETRY_START_DELAY_MS = 100;
2660
+ var RETRY_MAX_DELAY_MS = 2e3;
2661
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set(["EBUSY", "EPERM", "EACCES"]);
2662
+ var WindowsRenameRetryError = class extends Error {
2663
+ code = "rename_unavailable";
2664
+ constructor(target, attempts, options = {}) {
2665
+ 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);
2666
+ this.name = "WindowsRenameRetryError";
2667
+ }
2668
+ };
2669
+ function resolveRetrySchedule(tuning) {
2670
+ return {
2671
+ windowMs: tuning.windowMs ?? RETRY_WINDOW_MS,
2672
+ startDelayMs: tuning.startDelayMs ?? RETRY_START_DELAY_MS,
2673
+ maxDelayMs: tuning.maxDelayMs ?? RETRY_MAX_DELAY_MS,
2674
+ sleep: tuning.sleep ?? ((ms) => setTimeout2(ms)),
2675
+ now: tuning.now ?? (() => Date.now())
2676
+ };
2677
+ }
2678
+ async function renameWithWindowsRetry(operation, tuning) {
2679
+ const schedule = resolveRetrySchedule(tuning);
2680
+ const deadline = schedule.now() + schedule.windowMs;
2681
+ let delayMs = schedule.startDelayMs;
2682
+ let attempts = 0;
2683
+ for (; ; ) {
2684
+ attempts += 1;
2685
+ try {
2686
+ await operation();
2687
+ return;
2688
+ } catch (error) {
2689
+ if (tuning.platform !== "win32" || !isRetryableRenameError(error)) throw error;
2690
+ await sleepWithinWindow(schedule, deadline, delayMs, () => new WindowsRenameRetryError(tuning.target, attempts, { cause: error }));
2691
+ delayMs = Math.min(delayMs * 2, schedule.maxDelayMs);
2692
+ }
2693
+ }
2694
+ }
2695
+ async function sleepWithinWindow(schedule, deadline, delayMs, exhausted) {
2696
+ if (schedule.now() >= deadline) throw exhausted();
2697
+ await schedule.sleep(Math.min(delayMs, deadline - schedule.now()));
2698
+ if (schedule.now() >= deadline) throw exhausted();
2699
+ }
2700
+ function isRetryableRenameError(error) {
2701
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && RETRYABLE_CODES.has(error.code);
2702
+ }
2162
2703
 
2163
2704
  // host/relay/src/durable-atomic-replacement.ts
2164
- import { O_RDONLY } from "node:constants";
2165
- import { lstat, mkdir, open as open2, rename, unlink } from "node:fs/promises";
2166
- import { dirname } from "node:path";
2167
2705
  var DEFAULT_PORT = {
2168
2706
  platform: process.platform,
2169
- mkdir: (path, options) => mkdir(path, options),
2170
- lstat: (path) => lstat(path),
2707
+ mkdir: (path, options) => mkdir2(path, options),
2708
+ lstat: (path) => lstat2(path),
2171
2709
  open_file: (path) => open2(path, "wx", 384),
2172
2710
  rename,
2173
2711
  unlink,
2174
- open_directory: (path) => open2(path, O_RDONLY),
2712
+ open_directory: (path) => open2(path),
2175
2713
  unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
2176
2714
  };
2177
2715
  async function durableAtomicReplace(target, data, port = DEFAULT_PORT) {
2178
- const parent = dirname(target);
2716
+ const parent = dirname3(target);
2179
2717
  await port.mkdir(parent, { recursive: true, mode: 448 });
2180
2718
  const parentStats = await port.lstat(parent);
2181
- 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 }])}.`);
2719
+ const expectedUid = typeof process.geteuid === "function" ? process.geteuid() : parentStats.uid;
2720
+ const ownerPrivate = port.platform === "win32" || isOwnedBy(parentStats.uid, expectedUid) && isPrivateFileMode(parentStats.mode, port.platform);
2721
+ 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 }])}.`);
2182
2722
  const temporary = `${target}.${port.unique_ref()}.new`;
2183
2723
  let staged;
2184
2724
  const failures = [];
@@ -2189,7 +2729,7 @@ async function durableAtomicReplace(target, data, port = DEFAULT_PORT) {
2189
2729
  const closing = staged;
2190
2730
  staged = void 0;
2191
2731
  await closing.close();
2192
- await port.rename(temporary, target);
2732
+ await renameWithWindowsRetry(() => port.rename(temporary, target), { ...port.rename_retry, platform: port.platform, target });
2193
2733
  await syncParentDirectory(parent, port);
2194
2734
  } catch (error) {
2195
2735
  failures.push(error);
@@ -2215,11 +2755,6 @@ async function syncParentDirectory(path, port) {
2215
2755
  }
2216
2756
  if (failure !== void 0) throw failure;
2217
2757
  }
2218
- function ownerPrivate(stats, platform) {
2219
- if (platform === "win32") return true;
2220
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
2221
- return stats.uid === uid && (stats.mode & 63) === 0;
2222
- }
2223
2758
  async function closeStaged(staged, failures) {
2224
2759
  if (staged !== void 0) try {
2225
2760
  await staged.close();
@@ -2245,249 +2780,10 @@ function errorCode3(error) {
2245
2780
  return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2246
2781
  }
2247
2782
 
2248
- // host/relay/src/relay-request-binding-store.ts
2249
- import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
2250
- import { open as open3 } from "node:fs/promises";
2251
- import { join as join2 } from "node:path";
2252
- var HOST_RELAY_REQUEST_BINDING_SCHEMA = "ceal.host_relay_request_binding.v1";
2253
- var STATE_SCHEMA = "ceal.host_relay_request_binding_state.v1";
2254
- var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2255
- var MAX_BINDINGS = 8192;
2256
- var MAX_STATE_BYTES = 8 * 1024 * 1024;
2257
- var HostRelayBindingError = class extends TypeError {
2258
- name = "HostRelayBindingError";
2259
- code;
2260
- constructor(code, message) {
2261
- super(message);
2262
- this.code = code;
2263
- }
2264
- };
2265
- var RelayRequestBindingStore = class {
2266
- tail = Promise.resolve();
2267
- bindingWaiters = /* @__PURE__ */ new Set();
2268
- root;
2269
- constructor(root) {
2270
- if (typeof root !== "string" || root.length === 0) throw new TypeError("Host Relay binding root is invalid.");
2271
- this.root = root;
2272
- }
2273
- bind(input, now = /* @__PURE__ */ new Date()) {
2274
- const run = this.tail.then(() => this.bindLocked(input, now));
2275
- this.tail = run.then(() => void 0, () => void 0);
2276
- return run;
2277
- }
2278
- query(input) {
2279
- const run = this.tail.then(() => this.queryLocked(input));
2280
- this.tail = run.then(() => void 0, () => void 0);
2281
- return run;
2282
- }
2283
- waitForBinding(input, signal) {
2284
- validateQuery(input);
2285
- if (signal.aborted) return Promise.reject(new Error("Host Relay binding wait cancelled."));
2286
- return new Promise((resolve, reject) => {
2287
- let settled = false;
2288
- const finish = (result2, error) => {
2289
- if (settled) return;
2290
- settled = true;
2291
- this.bindingWaiters.delete(waiter);
2292
- signal.removeEventListener("abort", waiter.abort);
2293
- if (error !== void 0) reject(error);
2294
- else if (result2?.status === "ready") resolve(result2);
2295
- };
2296
- const abort = () => finish(void 0, new Error("Host Relay binding wait cancelled."));
2297
- const waiter = { query: input, resolve: (result2) => finish(result2), abort };
2298
- this.bindingWaiters.add(waiter);
2299
- signal.addEventListener("abort", abort, { once: true });
2300
- void this.query(input).then((result2) => {
2301
- if (result2.status === "ready") finish(result2);
2302
- }, (error) => finish(void 0, error));
2303
- });
2304
- }
2305
- async bindLocked(input, now) {
2306
- const candidate = bindingFromGateway(input, now);
2307
- const state = await this.read();
2308
- const existing = state.bindings.find((item) => sameCanonicalIdentity(item, candidate));
2309
- if (existing !== void 0) {
2310
- if (!sameBinding(existing, candidate)) throw new HostRelayBindingError("canonical_turn_rebound", `Host Relay canonical turn is already bound to another Request: ${refusalFacts([{ name: "candidate_request_ref", value: candidate.request_ref, expected: existing.request_ref }, { name: "candidate_prompt_ref", value: candidate.prompt_ref, expected: existing.prompt_ref }, { name: "candidate_context_expires_at", value: candidate.context_expires_at, expected: String(existing.context_expires_at) }, { name: "candidate_local_turn_aliases", value: candidate.local_turn_aliases, expected: JSON.stringify(existing.local_turn_aliases) }])}.`);
2311
- return existing;
2312
- }
2313
- if (state.bindings.length >= MAX_BINDINGS) throw new HostRelayBindingError("binding_capacity_exhausted", `Host Relay request binding capacity is exhausted: ${refusalFact("binding_count", state.bindings.length, `below ${MAX_BINDINGS}`)}.`);
2314
- const next = { schema_version: STATE_SCHEMA, bindings: [...state.bindings, candidate] };
2315
- await this.write(next);
2316
- this.resolveBindingWaiters(candidate);
2317
- return structuredClone(candidate);
2318
- }
2319
- resolveBindingWaiters(binding) {
2320
- for (const waiter of this.bindingWaiters) {
2321
- if (relayRequestBindingMatchesQuery(waiter.query, binding)) waiter.resolve({ status: "ready", binding: structuredClone(binding) });
2322
- }
2323
- }
2324
- async queryLocked(input) {
2325
- validateQuery(input);
2326
- const state = await this.read();
2327
- const matches = state.bindings.filter((binding2) => relayRequestBindingMatchesQuery(input, binding2));
2328
- if (matches.length === 0) return { status: "no_binding" };
2329
- const binding = matches[0];
2330
- if (binding === void 0) throw new TypeError("Host Relay binding query result is invalid.");
2331
- return { status: "ready", binding: structuredClone(binding) };
2332
- }
2333
- async read() {
2334
- let handle;
2335
- try {
2336
- handle = await open3(join2(this.root, "request-bindings.json"), O_RDONLY2 | O_NOFOLLOW);
2337
- const stats = await handle.stat();
2338
- if (!stats.isFile() || !ownerPrivate2(stats)) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFacts([{ name: "state_is_file", value: stats.isFile(), expected: "true" }, { name: "state_uid", value: stats.uid, expected: String(process.geteuid?.() ?? stats.uid) }, { name: "state_mode", value: (stats.mode & 511).toString(8), expected: "no group or other permission bits" }])}.`);
2339
- if (stats.size > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFact("state_bytes", stats.size, `at most ${MAX_STATE_BYTES}`)}.`);
2340
- return decodeState(JSON.parse(await handle.readFile("utf8")));
2341
- } catch (error) {
2342
- if (errorCode4(error) === "ENOENT") return { schema_version: STATE_SCHEMA, bindings: [] };
2343
- throw error;
2344
- } finally {
2345
- await handle?.close();
2346
- }
2347
- }
2348
- async write(state) {
2349
- const encoded = JSON.stringify(state);
2350
- if (Buffer.byteLength(encoded, "utf8") > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is too large: ${refusalFact("state_bytes", Buffer.byteLength(encoded, "utf8"), `at most ${MAX_STATE_BYTES}`)}.`);
2351
- await durableAtomicReplace(join2(this.root, "request-bindings.json"), encoded);
2352
- }
2353
- };
2354
- function relayRequestWorkContextFromBinding(binding) {
2355
- return createRelayRequestWorkContext({ request_ref: binding.request_ref, host_binding: {
2356
- host_source_ref: binding.host_source_ref,
2357
- host_session_ref: binding.host_session_ref,
2358
- host_turn_ref: binding.host_turn_ref,
2359
- host_input_ref: binding.host_input_ref,
2360
- prompt_ref: binding.prompt_ref
2361
- } });
2362
- }
2363
- function createRelayRequestWorkContext(input) {
2364
- if (!record2(input) || !exactKeys3(input, ["request_ref", "host_binding"]) || !safeRef2(input.request_ref) || !record2(input.host_binding)) {
2365
- throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
2366
- }
2367
- const hostBinding = input.host_binding;
2368
- if (!exactKeys3(hostBinding, ["host_source_ref", "host_session_ref", "host_turn_ref", "host_input_ref", "prompt_ref"]) || ![hostBinding.host_source_ref, hostBinding.host_session_ref, hostBinding.host_turn_ref, hostBinding.host_input_ref, hostBinding.prompt_ref].every(safeRef2)) {
2369
- throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
2370
- }
2371
- const hostSourceRef = requiredRef(hostBinding.host_source_ref);
2372
- const hostSessionRef = requiredRef(hostBinding.host_session_ref);
2373
- const hostTurnRef = requiredRef(hostBinding.host_turn_ref);
2374
- const hostInputRef = requiredRef(hostBinding.host_input_ref);
2375
- const promptRef = requiredRef(hostBinding.prompt_ref);
2376
- return Object.freeze({ request_ref: input.request_ref, host_binding: Object.freeze({
2377
- host_source_ref: hostSourceRef,
2378
- host_session_ref: hostSessionRef,
2379
- host_turn_ref: hostTurnRef,
2380
- host_input_ref: hostInputRef,
2381
- prompt_ref: promptRef
2382
- }) });
2383
- }
2384
- function bindingFromGateway(input, now) {
2385
- const payload = input.frame.payload;
2386
- const candidate = {
2387
- schema_version: HOST_RELAY_REQUEST_BINDING_SCHEMA,
2388
- producer_id: input.frame.producer_id,
2389
- producer_installation_ref: input.frame.producer_installation_ref,
2390
- local_session_ref: requiredRef(payload.local_session_ref),
2391
- local_turn_ref: requiredRef(payload.local_turn_ref),
2392
- local_turn_aliases: optionalRefs2(payload.local_turn_aliases),
2393
- prompt_ref: requiredRef(input.prompt_ref),
2394
- request_ref: requiredRef(input.request_ref),
2395
- bound_at: timestamp2(now.toISOString()),
2396
- ...payload.context_expires_at === void 0 ? {} : { context_expires_at: timestamp2(payload.context_expires_at) },
2397
- host_source_ref: requiredRef(input.host_binding.host_source_ref),
2398
- host_session_ref: requiredRef(input.host_binding.host_session_ref),
2399
- host_turn_ref: requiredRef(input.host_binding.host_turn_ref),
2400
- host_input_ref: requiredRef(input.host_binding.host_input_ref)
2401
- };
2402
- if (input.host_binding.prompt_ref !== input.prompt_ref) throw new HostRelayBindingError("context_identity_mismatch", "Host Relay binding prompt reference does not match the acknowledged submission.");
2403
- validateBinding(candidate);
2404
- return candidate;
2405
- }
2406
- function decodeState(value) {
2407
- if (!record2(value) || !exactKeys3(value, ["schema_version", "bindings"])) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("state_keys", record2(value) ? Object.keys(value) : [], "exactly schema_version and bindings")}.`);
2408
- if (value.schema_version !== STATE_SCHEMA) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("schema_version", value.schema_version, STATE_SCHEMA)}.`);
2409
- if (!Array.isArray(value.bindings)) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("bindings", value.bindings, "an array")}.`);
2410
- const bindings = value.bindings.map((item) => {
2411
- validateBinding(item);
2412
- return item;
2413
- });
2414
- const keys = bindings.map(bindingKey);
2415
- if (new Set(keys).size !== keys.length) throw new TypeError("Host Relay binding state contains duplicate canonical turns.");
2416
- return { schema_version: STATE_SCHEMA, bindings };
2417
- }
2418
- function validateBinding(value) {
2419
- if (!record2(value) || !exactOptionalKeys(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "local_turn_aliases", "prompt_ref", "request_ref", "bound_at", "host_source_ref", "host_session_ref", "host_turn_ref", "host_input_ref"], ["context_expires_at"]) || !validBindingIdentity(value) || !validBindingRequest(value)) throw new TypeError("Host Relay request binding is invalid.");
2420
- }
2421
- function validBindingIdentity(value) {
2422
- return value.schema_version === HOST_RELAY_REQUEST_BINDING_SCHEMA && producer(value.producer_id) && [value.producer_installation_ref, value.local_session_ref, value.local_turn_ref].every(safeRef2) && refList(value.local_turn_aliases);
2423
- }
2424
- function validBindingRequest(value) {
2425
- const tuple = [value.host_source_ref, value.host_session_ref, value.host_turn_ref, value.host_input_ref];
2426
- return safeRef2(value.prompt_ref) && safeRef2(value.request_ref) && validTimestamp(value.bound_at) && (value.context_expires_at === void 0 || validTimestamp(value.context_expires_at)) && tuple.every(safeRef2);
2427
- }
2428
- function validateQuery(value) {
2429
- if (!record2(value) || !exactKeys3(value, ["producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref"]) || !producer(value.producer_id) || !safeRef2(value.producer_installation_ref) || !safeRef2(value.local_session_ref) || !safeRef2(value.local_turn_ref)) throw new TypeError("Host Relay binding query is invalid.");
2430
- }
2431
- function relayRequestBindingMatchesQuery(query, binding) {
2432
- return query.producer_id === binding.producer_id && query.local_session_ref === binding.local_session_ref && (query.local_turn_ref === binding.local_turn_ref || binding.local_turn_aliases.includes(query.local_turn_ref)) && query.producer_installation_ref === binding.producer_installation_ref;
2433
- }
2434
- function sameCanonicalIdentity(left, right) {
2435
- return bindingKey(left) === bindingKey(right);
2436
- }
2437
- function sameBinding(left, right) {
2438
- return left.request_ref === right.request_ref && left.prompt_ref === right.prompt_ref && left.context_expires_at === right.context_expires_at && left.host_source_ref === right.host_source_ref && left.host_session_ref === right.host_session_ref && left.host_turn_ref === right.host_turn_ref && left.host_input_ref === right.host_input_ref && JSON.stringify(left.local_turn_aliases) === JSON.stringify(right.local_turn_aliases);
2439
- }
2440
- function bindingKey(value) {
2441
- return `${value.producer_id}\0${value.producer_installation_ref}\0${value.local_session_ref}\0${value.local_turn_ref}`;
2442
- }
2443
- function requiredRef(value) {
2444
- if (!safeRef2(value)) throw new TypeError("Host Relay binding reference is invalid.");
2445
- return value;
2446
- }
2447
- function optionalRefs2(value) {
2448
- if (value === void 0) return [];
2449
- if (!refList(value)) throw new TypeError("Host Relay binding aliases are invalid.");
2450
- return [...value];
2451
- }
2452
- function timestamp2(value) {
2453
- if (!validTimestamp(value)) throw new TypeError("Host Relay binding timestamp is invalid.");
2454
- return value;
2455
- }
2456
- function producer(value) {
2457
- return value === "codex" || value === "claude";
2458
- }
2459
- function safeRef2(value) {
2460
- return typeof value === "string" && SAFE_REF3.test(value);
2461
- }
2462
- function refList(value) {
2463
- return Array.isArray(value) && value.length <= 16 && value.every(safeRef2) && new Set(value).size === value.length;
2464
- }
2465
- function validTimestamp(value) {
2466
- return typeof value === "string" && Number.isFinite(Date.parse(value));
2467
- }
2468
- function record2(value) {
2469
- return value !== null && typeof value === "object" && !Array.isArray(value);
2470
- }
2471
- function exactKeys3(value, keys) {
2472
- return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
2473
- }
2474
- function exactOptionalKeys(value, required, optional) {
2475
- const keys = Object.keys(value);
2476
- return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
2477
- }
2478
- function errorCode4(error) {
2479
- return record2(error) && typeof error.code === "string" ? error.code : null;
2480
- }
2481
- function ownerPrivate2(stats) {
2482
- if (process.platform === "win32") return true;
2483
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
2484
- return stats.uid === uid && (stats.mode & 63) === 0;
2485
- }
2486
-
2487
2783
  // host/relay/src/relay-quarantine-store.ts
2488
- import { createHash as createHash4 } from "node:crypto";
2784
+ import { createHash as createHash5 } from "node:crypto";
2489
2785
  import { readFile } from "node:fs/promises";
2490
- import { join as join3 } from "node:path";
2786
+ import { join as join2 } from "node:path";
2491
2787
  var RELAY_QUARANTINE_SCHEMA = "ceal.host_relay_quarantine_state.v1";
2492
2788
  var RELAY_QUARANTINE_MAX_RECORDS = 512;
2493
2789
  var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
@@ -2507,8 +2803,8 @@ var RelayQuarantineStore = class {
2507
2803
  if (!Number.isSafeInteger(this.maxRecords) || this.maxRecords < 1 || this.maxRecords > RELAY_QUARANTINE_MAX_RECORDS) throw new TypeError(`Relay quarantine record cap is invalid: ${refusalFact("max_records", this.maxRecords, `between 1 and ${RELAY_QUARANTINE_MAX_RECORDS}`)}.`);
2508
2804
  if (!Number.isSafeInteger(this.maxBytes) || this.maxBytes < 1 || this.maxBytes > RELAY_QUARANTINE_MAX_BYTES) throw new TypeError(`Relay quarantine byte cap is invalid: ${refusalFact("max_bytes", this.maxBytes, `between 1 and ${RELAY_QUARANTINE_MAX_BYTES}`)}.`);
2509
2805
  }
2510
- async retainFrame(frame, reason, attribution, capturedAt = (/* @__PURE__ */ new Date()).toISOString()) {
2511
- await this.retain(encodeHostProducerFrame(frame), frame.producer_id, reason, capturedAt, attribution);
2806
+ async retainFrame(frame, reason, capturedAt = (/* @__PURE__ */ new Date()).toISOString()) {
2807
+ await this.retain(encodeHostProducerFrame(frame), frame.producer_id, reason, capturedAt);
2512
2808
  }
2513
2809
  async retainMalformed(rawFrame, reason, capturedAt = (/* @__PURE__ */ new Date()).toISOString()) {
2514
2810
  await this.retain(rawFrame, inferProducer(rawFrame), reason, capturedAt);
@@ -2520,53 +2816,37 @@ var RelayQuarantineStore = class {
2520
2816
  const records = state.producers[producerId].slice(-Math.min(limit, this.maxRecords)).map((record6) => structuredClone(record6));
2521
2817
  return { producer_id: producerId, records, total_bytes: records.reduce((total, record6) => total + record6.charged_bytes, 0) };
2522
2818
  }
2523
- /**
2524
- * The newest quarantined record whose exact-turn attribution matches the
2525
- * query, or null when evicted, unattributed, or for another turn. The
2526
- * file-backed state survives drain, restart, and overwrite.
2527
- */
2528
- async findNewestAttributedMatch(query) {
2529
- await this.tail;
2530
- const state = await this.readState();
2531
- const bucket = query.producer_id === "codex" || query.producer_id === "claude" ? state.producers[query.producer_id] : [];
2532
- for (let index = bucket.length - 1; index >= 0; index -= 1) {
2533
- const record6 = bucket[index];
2534
- if (record6 !== void 0 && attributedRecordMatchesQuery(record6, query)) return structuredClone(record6);
2535
- }
2536
- return null;
2537
- }
2538
2819
  async snapshot() {
2539
2820
  await this.tail;
2540
2821
  return this.readState();
2541
2822
  }
2542
- async retain(rawFrame, producerId, reason, capturedAt, attribution) {
2823
+ async retain(rawFrame, producerId, reason, capturedAt) {
2543
2824
  const run = this.tail.then(async () => {
2544
2825
  if (!PRODUCERS2.includes(producerId) || !Number.isFinite(Date.parse(capturedAt))) throw new TypeError("Relay quarantine record identity is invalid.");
2545
2826
  const raw = Buffer.from(rawFrame).subarray(0, MAX_CAPTURE_BYTES);
2546
2827
  const state = await this.readState();
2547
2828
  const record6 = {
2548
- quarantine_ref: `quarantine:${createHash4("sha256").update(`${producerId}\0${reason}\0${capturedAt}\0`).update(raw).digest("hex")}`,
2829
+ quarantine_ref: `quarantine:${createHash5("sha256").update(`${producerId}\0${reason}\0${capturedAt}\0`).update(raw).digest("hex")}`,
2549
2830
  producer_id: producerId,
2550
2831
  reason,
2551
2832
  captured_at: capturedAt,
2552
- frame_sha256: createHash4("sha256").update(raw).digest("hex"),
2833
+ frame_sha256: createHash5("sha256").update(raw).digest("hex"),
2553
2834
  charged_bytes: raw.byteLength,
2554
- raw_frame_base64: raw.toString("base64"),
2555
- ...attribution === void 0 ? {} : { attribution: structuredClone(attribution) }
2835
+ raw_frame_base64: raw.toString("base64")
2556
2836
  };
2557
2837
  const records = state.producers[producerId];
2558
2838
  records.push(record6);
2559
2839
  while (records.length > this.maxRecords || totalBytes(records) > this.maxBytes) records.shift();
2560
- await durableAtomicReplace(join3(this.root, "quarantine.json"), JSON.stringify(state));
2840
+ await durableAtomicReplace(join2(this.root, "quarantine.json"), JSON.stringify(state));
2561
2841
  });
2562
2842
  this.tail = run.then(() => void 0, () => void 0);
2563
2843
  await run;
2564
2844
  }
2565
2845
  async readState() {
2566
2846
  try {
2567
- return decodeState2(JSON.parse(await readFile(join3(this.root, "quarantine.json"), "utf8")));
2847
+ return decodeState(JSON.parse(await readFile(join2(this.root, "quarantine.json"), "utf8")));
2568
2848
  } catch (error) {
2569
- if (errorCode5(error) === "ENOENT") return emptyState();
2849
+ if (errorCode4(error) === "ENOENT") return emptyState();
2570
2850
  throw error;
2571
2851
  }
2572
2852
  }
@@ -2577,9 +2857,9 @@ function emptyState() {
2577
2857
  function totalBytes(records) {
2578
2858
  return records.reduce((total, record6) => total + record6.charged_bytes, 0);
2579
2859
  }
2580
- function decodeState2(value) {
2581
- if (!record3(value) || value.schema_version !== RELAY_QUARANTINE_SCHEMA) throw new TypeError(`Host Relay quarantine state is invalid: ${refusalFact("schema_version", record3(value) ? value.schema_version : void 0, RELAY_QUARANTINE_SCHEMA)}.`);
2582
- if (!record3(value.producers)) throw new TypeError(`Host Relay quarantine state is invalid: ${refusalFact("producer_ids", record3(value.producers) ? Object.keys(value.producers) : [], `the ${PRODUCERS2.length} quarantine producer buckets`)}.`);
2860
+ function decodeState(value) {
2861
+ if (!record2(value) || value.schema_version !== RELAY_QUARANTINE_SCHEMA) throw new TypeError(`Host Relay quarantine state is invalid: ${refusalFact("schema_version", record2(value) ? value.schema_version : void 0, RELAY_QUARANTINE_SCHEMA)}.`);
2862
+ if (!record2(value.producers)) throw new TypeError(`Host Relay quarantine state is invalid: ${refusalFact("producer_ids", record2(value.producers) ? Object.keys(value.producers) : [], `the ${PRODUCERS2.length} quarantine producer buckets`)}.`);
2583
2863
  const state = emptyState();
2584
2864
  for (const producerId of PRODUCERS2) {
2585
2865
  const records = value.producers[producerId];
@@ -2589,8 +2869,7 @@ function decodeState2(value) {
2589
2869
  return state;
2590
2870
  }
2591
2871
  function validRecord(value) {
2592
- if (!record3(value) || !validRecordIdentity(value)) return false;
2593
- return value.attribution === void 0 || validAttribution(value.attribution);
2872
+ return record2(value) && validRecordIdentity(value);
2594
2873
  }
2595
2874
  function validRecordIdentity(value) {
2596
2875
  return typeof value.quarantine_ref === "string" && quarantineProducer(value.producer_id) && validReason(value.reason) && Number.isFinite(Date.parse(String(value.captured_at))) && typeof value.frame_sha256 === "string" && typeof value.raw_frame_base64 === "string" && validChargedBytes(value.charged_bytes);
@@ -2601,52 +2880,22 @@ function validReason(value) {
2601
2880
  function validChargedBytes(value) {
2602
2881
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
2603
2882
  }
2604
- var ATTRIBUTION_REQUIRED_KEYS = ["producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "local_turn_aliases", "sequence", "frame_sha256"];
2605
- var ATTRIBUTION_OPTIONAL_KEYS = ["diagnostic"];
2606
- function validAttribution(value) {
2607
- if (!record3(value) || !hasExactKeys2(value, ATTRIBUTION_REQUIRED_KEYS, ATTRIBUTION_OPTIONAL_KEYS)) return false;
2608
- return validAttributionIdentity(value) && validAttributionAliases(value.local_turn_aliases) && validAttributionEnvelope(value) && (value.diagnostic === void 0 || validAttributionDiagnostic(value.diagnostic));
2609
- }
2610
- function validAttributionIdentity(value) {
2611
- return (value.producer_id === "codex" || value.producer_id === "claude") && isHostSafeRef(value.producer_installation_ref) && isHostSafeRef(value.local_session_ref) && isHostSafeRef(value.local_turn_ref);
2612
- }
2613
- function validAttributionAliases(value) {
2614
- return Array.isArray(value) && value.length <= 16 && value.every(isHostSafeRef);
2615
- }
2616
- function validAttributionEnvelope(value) {
2617
- return typeof value.sequence === "number" && Number.isSafeInteger(value.sequence) && value.sequence > 0 && typeof value.frame_sha256 === "string";
2618
- }
2619
- function validAttributionDiagnostic(value) {
2620
- if (!record3(value) || !hasExactKeys2(value, [], RELAY_DELIVERY_DIAGNOSTIC_KEYS)) return false;
2621
- return isUndefinedOr(value.relayCode, isRelayGatewayErrorCode) && isUndefinedOr(value.gatewayStatus, isSafeInteger) && isUndefinedOr(value.gatewayCode, isBoundedRefusalWord) && isUndefinedOr(value.gatewayReason, isBoundedRefusalWord) && isUndefinedOr(value.exhausted, isBoolean);
2622
- }
2623
- function isSafeInteger(value) {
2624
- return Number.isSafeInteger(value);
2625
- }
2626
- function isBoolean(value) {
2627
- return typeof value === "boolean";
2628
- }
2629
- function attributedRecordMatchesQuery(recordValue, query) {
2630
- const attribution = recordValue.attribution;
2631
- if (attribution === void 0) return false;
2632
- return attribution.producer_id === query.producer_id && attribution.producer_installation_ref === query.producer_installation_ref && attribution.local_session_ref === query.local_session_ref && (attribution.local_turn_ref === query.local_turn_ref || attribution.local_turn_aliases.includes(query.local_turn_ref));
2633
- }
2634
2883
  function quarantineProducer(value) {
2635
2884
  return value === "codex" || value === "claude" || value === "unknown";
2636
2885
  }
2637
2886
  function inferProducer(rawFrame) {
2638
2887
  try {
2639
2888
  const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.from(rawFrame).subarray(4)));
2640
- return record3(value) && (value.producer_id === "codex" || value.producer_id === "claude") ? value.producer_id : "unknown";
2889
+ return record2(value) && (value.producer_id === "codex" || value.producer_id === "claude") ? value.producer_id : "unknown";
2641
2890
  } catch {
2642
2891
  return "unknown";
2643
2892
  }
2644
2893
  }
2645
- function record3(value) {
2894
+ function record2(value) {
2646
2895
  return value !== null && typeof value === "object" && !Array.isArray(value);
2647
2896
  }
2648
- function errorCode5(error) {
2649
- return record3(error) && typeof error.code === "string" ? error.code : null;
2897
+ function errorCode4(error) {
2898
+ return record2(error) && typeof error.code === "string" ? error.code : null;
2650
2899
  }
2651
2900
 
2652
2901
  // host/relay/src/relay-admission-store.ts
@@ -2656,12 +2905,11 @@ var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
2656
2905
  var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
2657
2906
  var RelayAdmissionStore = class {
2658
2907
  tail = Promise.resolve();
2659
- drainWaiters = /* @__PURE__ */ new Set();
2660
2908
  root;
2661
2909
  quarantine;
2662
2910
  limits;
2663
2911
  faultHooks;
2664
- constructor(root, limits = DEFAULT_LIMITS, faultHooks = {}, quarantine = new RelayQuarantineStore(join4(root, "quarantine"))) {
2912
+ constructor(root, limits = DEFAULT_LIMITS, faultHooks = {}, quarantine = new RelayQuarantineStore(join3(root, "quarantine"))) {
2665
2913
  this.root = root;
2666
2914
  this.quarantine = quarantine;
2667
2915
  this.limits = limits;
@@ -2676,36 +2924,10 @@ var RelayAdmissionStore = class {
2676
2924
  await this.tail;
2677
2925
  return this.read();
2678
2926
  }
2679
- async hasPendingRequestBinding(query) {
2680
- const state = await this.read();
2681
- return state.entries.some((entry2) => pendingFrameMatchesBindingQuery(entry2, query));
2682
- }
2683
2927
  async drainRound(deliver, options) {
2684
2928
  const run = this.tail.then(() => this.drainLocked(deliver, options));
2685
2929
  this.tail = run.then(() => void 0, () => void 0);
2686
- const result2 = await run;
2687
- if (result2.delivered.length > 0 || result2.refused.length > 0 || result2.retained.length > 0) this.resolveDrainWaiters();
2688
- return result2;
2689
- }
2690
- waitForDrainChange(signal) {
2691
- if (signal.aborted) return Promise.reject(new Error("Host Relay admission wait cancelled."));
2692
- return new Promise((resolve, reject) => {
2693
- let settled = false;
2694
- const finish = (error) => {
2695
- if (settled) return;
2696
- settled = true;
2697
- this.drainWaiters.delete(waiter);
2698
- signal.removeEventListener("abort", waiter.abort);
2699
- if (error !== void 0) reject(error);
2700
- else resolve();
2701
- };
2702
- const waiter = { resolve: () => finish(), abort: () => finish(new Error("Host Relay admission wait cancelled.")) };
2703
- this.drainWaiters.add(waiter);
2704
- signal.addEventListener("abort", waiter.abort, { once: true });
2705
- });
2706
- }
2707
- resolveDrainWaiters() {
2708
- for (const waiter of this.drainWaiters) waiter.resolve();
2930
+ return run;
2709
2931
  }
2710
2932
  async admitLocked(frame) {
2711
2933
  const state = await this.read();
@@ -2717,11 +2939,11 @@ var RelayAdmissionStore = class {
2717
2939
  const hash = hostProducerFrameHash(frame);
2718
2940
  const replay = replayCode(existing, hash, frame.sequence, installation2);
2719
2941
  if (replay !== null) {
2720
- if (replay === "frame_identity_conflict") await this.quarantine.retainFrame(frame, replay, attributionForFrame(frame, hash, frame.sequence));
2942
+ if (replay === "frame_identity_conflict") await this.quarantine.retainFrame(frame, replay);
2721
2943
  return result(replay, installation2);
2722
2944
  }
2723
2945
  if (frame.sequence !== installation2.admitted_through_sequence + 1) {
2724
- await this.quarantine.retainFrame(frame, "sequence_gap", attributionForFrame(frame, hash, frame.sequence));
2946
+ await this.quarantine.retainFrame(frame, "sequence_gap");
2725
2947
  return result("sequence_gap", installation2);
2726
2948
  }
2727
2949
  const chargedBytes = 4 + Buffer.byteLength(JSON.stringify(frame), "utf8");
@@ -2768,7 +2990,7 @@ var RelayAdmissionStore = class {
2768
2990
  if (!installation2) throw new TypeError("Host Relay delivery installation is unavailable.");
2769
2991
  if (outcome.status === "refused" || exhausted) {
2770
2992
  const diagnostic = exhaustedDiagnostic(outcome, exhausted);
2771
- await this.quarantine.retainFrame(head.frame, "gateway_refused", attributionForFrame(head.frame, head.hash, head.sequence, diagnostic));
2993
+ await this.quarantine.retainFrame(head.frame, "gateway_refused");
2772
2994
  installation2.last_refusal = { sequence: head.sequence, submission_ref: input.submission_ref, ...refusalCause(outcome, head), ...diagnostic === void 0 ? {} : { diagnostic }, refused_at_ms: options.now_ms };
2773
2995
  refused.push(input.submission_ref);
2774
2996
  } else {
@@ -2799,31 +3021,19 @@ var RelayAdmissionStore = class {
2799
3021
  }
2800
3022
  async read() {
2801
3023
  try {
2802
- return decodeState3(JSON.parse(await readFile2(join4(this.root, "admission.json"), "utf8")));
3024
+ return decodeState2(JSON.parse(await readFile2(join3(this.root, "admission.json"), "utf8")));
2803
3025
  } catch (error) {
2804
- if (errorCode6(error) === "ENOENT") return { schema_version: "ceal.host_relay_admission_state.v1", installations: {}, entries: [] };
3026
+ if (errorCode5(error) === "ENOENT") return { schema_version: "ceal.host_relay_admission_state.v1", installations: {}, entries: [] };
2805
3027
  throw error;
2806
3028
  }
2807
3029
  }
2808
3030
  async write(state) {
2809
- await durableAtomicReplace(join4(this.root, "admission.json"), JSON.stringify(state));
3031
+ await durableAtomicReplace(join3(this.root, "admission.json"), JSON.stringify(state));
2810
3032
  }
2811
3033
  };
2812
3034
  function installationKey(frame) {
2813
3035
  return `${frame.producer_id}\0${frame.producer_installation_ref}`;
2814
3036
  }
2815
- function pendingFrameMatchesBindingQuery(entry2, query) {
2816
- const selector = extractHostBindingSelector(entry2.frame);
2817
- if (selector === null) return false;
2818
- return relayRequestBindingMatchesQuery(query, selector);
2819
- }
2820
- function attributionForFrame(frame, hash, sequence, diagnostic) {
2821
- const selector = extractHostBindingSelector(frame);
2822
- if (selector === null) return void 0;
2823
- if (frame.producer_id !== selector.producer_id || frame.producer_installation_ref !== selector.producer_installation_ref || frame.sequence !== sequence) return void 0;
2824
- if (hostProducerFrameHash(frame) !== hash) return void 0;
2825
- return { ...selector, sequence, frame_sha256: hash, ...diagnostic === void 0 ? {} : { diagnostic } };
2826
- }
2827
3037
  function exhaustedDiagnostic(outcome, exhausted) {
2828
3038
  if (outcome.status !== "retryable" && outcome.status !== "refused") return void 0;
2829
3039
  if (outcome.diagnostic !== void 0) return exhausted ? { ...outcome.diagnostic, exhausted: true } : outcome.diagnostic;
@@ -2841,6 +3051,7 @@ function retryExhausted(outcome, entry2) {
2841
3051
  return outcome.status === "retryable" && entry2.retry_attempts >= MAX_RETRYABLE_ATTEMPTS;
2842
3052
  }
2843
3053
  function refusalCause(outcome, entry2) {
3054
+ if (outcome.status === "refused" && outcome.code === "no_binding") return { code: "gateway_refused", message: "Gateway refused the Host producer frame." };
2844
3055
  if (outcome.status === "refused") return { code: outcome.code, message: outcome.message };
2845
3056
  const reason = outcome.status === "retryable" ? outcome.reason ?? "no reason reported" : "no reason reported";
2846
3057
  return { code: "retry_exhausted", message: `Gateway delivery stayed retryable through ${entry2.retry_attempts} attempts: ${reason}` };
@@ -2854,10 +3065,10 @@ function scheduleRetry(entry2, nowMs, reason) {
2854
3065
  function result(code, state) {
2855
3066
  return { code, admitted_through_sequence: state.admitted_through_sequence, delivered_through_sequence: state.delivered_through_sequence };
2856
3067
  }
2857
- function errorCode6(error) {
3068
+ function errorCode5(error) {
2858
3069
  return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2859
3070
  }
2860
- function decodeState3(value) {
3071
+ function decodeState2(value) {
2861
3072
  if (!stateRecord(value)) throw new TypeError("Host Relay admission state is invalid.");
2862
3073
  const installations = {};
2863
3074
  for (const [key, item] of Object.entries(value.installations)) {
@@ -2884,18 +3095,18 @@ function refusalRecord(value) {
2884
3095
  }
2885
3096
  function validDiagnostic(value) {
2886
3097
  if (!object(value) || !hasExactKeys2(value, [], RELAY_DELIVERY_DIAGNOSTIC_KEYS)) return false;
2887
- return isUndefinedOr(value.relayCode, isRelayGatewayErrorCode) && isUndefinedOr(value.gatewayStatus, isSafeInteger2) && isUndefinedOr(value.gatewayCode, isBoundedRefusalWord) && isUndefinedOr(value.gatewayReason, isBoundedRefusalWord) && isUndefinedOr(value.exhausted, isBoolean2);
3098
+ return isUndefinedOr(value.relayCode, isRelayGatewayErrorCode) && isUndefinedOr(value.gatewayStatus, isSafeInteger) && isUndefinedOr(value.gatewayCode, isBoundedRefusalWord) && isUndefinedOr(value.gatewayReason, isBoundedRefusalWord) && isUndefinedOr(value.exhausted, isBoolean);
2888
3099
  }
2889
- function isSafeInteger2(value) {
3100
+ function isSafeInteger(value) {
2890
3101
  return Number.isSafeInteger(value);
2891
3102
  }
2892
- function isBoolean2(value) {
3103
+ function isBoolean(value) {
2893
3104
  return typeof value === "boolean";
2894
3105
  }
2895
3106
  function entry(value) {
2896
- return object(value) && producer2(value.producer_id) && typeof value.producer_installation_ref === "string" && Number.isSafeInteger(value.sequence) && typeof value.hash === "string" && Number.isSafeInteger(value.charged_bytes) && object(value.frame) && Number.isSafeInteger(value.retry_attempts) && Number.isSafeInteger(value.retry_at_ms);
3107
+ return object(value) && producer(value.producer_id) && typeof value.producer_installation_ref === "string" && Number.isSafeInteger(value.sequence) && typeof value.hash === "string" && Number.isSafeInteger(value.charged_bytes) && object(value.frame) && Number.isSafeInteger(value.retry_attempts) && Number.isSafeInteger(value.retry_at_ms);
2897
3108
  }
2898
- function producer2(value) {
3109
+ function producer(value) {
2899
3110
  return value === "codex" || value === "claude";
2900
3111
  }
2901
3112
  function object(value) {
@@ -2925,10 +3136,10 @@ function poisonEnvelopeMatches(entryValue, installation2) {
2925
3136
  return poisonEnvelopeSchema(frame) && poisonEnvelopeProducer(frame, entryValue) && poisonEnvelopeInstallation(frame, entryValue) && poisonEnvelopeEpoch(frame, installation2) && poisonEnvelopeSequence(frame, entryValue) && poisonEnvelopeKind(frame) && poisonEnvelopeTimestamp(frame);
2926
3137
  }
2927
3138
  function poisonEnvelopeSchema(frame) {
2928
- return frame.schema_version === "ceal.host_producer_frame.v1";
3139
+ return frame.schema_version === HOST_PRODUCER_FRAME_SCHEMA;
2929
3140
  }
2930
3141
  function poisonEnvelopeProducer(frame, entryValue) {
2931
- return producer2(frame.producer_id) && frame.producer_id === entryValue.producer_id;
3142
+ return producer(frame.producer_id) && frame.producer_id === entryValue.producer_id;
2932
3143
  }
2933
3144
  function poisonEnvelopeInstallation(frame, entryValue) {
2934
3145
  return opaqueHostSourceRef(frame.producer_installation_ref) && frame.producer_installation_ref === entryValue.producer_installation_ref;
@@ -2956,713 +3167,275 @@ function poisonSequenceInRange(entryValue, installation2) {
2956
3167
  return entryValue.sequence <= installation2.admitted_through_sequence;
2957
3168
  }
2958
3169
 
2959
- // host/relay/src/relay-gateway-bridge.ts
2960
- import { readFile as readFile3 } from "node:fs/promises";
2961
- import { join as join5 } from "node:path";
2962
- var STATE_SCHEMA2 = "ceal.host_relay_gateway_state.v1";
2963
- var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2964
- var HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS = ["created", "adopted", "replayed", "approval_approved", "approval_denied", "approval_replayed"];
2965
- function isHostGatewayAcknowledgementDisposition(value) {
2966
- return typeof value === "string" && HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS.some((disposition) => disposition === value);
2967
- }
2968
- var RelayGatewayBridge = class {
2969
- statePath;
2970
- hostSourceRef;
2971
- gateway;
2972
- timeoutMs;
2973
- onRequestBound;
2974
- tail = Promise.resolve();
2975
- constructor(options) {
2976
- if (typeof options.state_root !== "string" || options.state_root.length === 0 || !opaqueHostSourceRef(options.host_source_ref) || typeof options.gateway?.submitHostTurn !== "function") throw new TypeError("Host Relay Gateway bridge configuration is invalid.");
2977
- if (!validOptionalTimeout(options.timeout_ms)) throw new TypeError("Host Relay Gateway timeout is invalid.");
2978
- this.statePath = join5(options.state_root, "gateway-state.json");
2979
- this.hostSourceRef = options.host_source_ref;
2980
- this.gateway = options.gateway;
2981
- this.timeoutMs = options.timeout_ms ?? 5e3;
2982
- this.onRequestBound = options.on_request_bound ?? (() => void 0);
2983
- }
2984
- deliver(input) {
2985
- const run = this.tail.then(() => this.deliverLocked(input));
2986
- this.tail = run.then(() => void 0, () => void 0);
2987
- return run;
2988
- }
2989
- async deliverLocked(input) {
2990
- if (input.host_source_ref !== this.hostSourceRef) throw new TypeError(`Host Relay source binding does not match the Gateway source: ${refusalFacts([{ name: "input_host_source_ref", value: input.host_source_ref }, { name: "configured_host_source_ref", value: this.hostSourceRef }])}.`);
2991
- if (input.frame.frame_kind === "producer_status") return { status: "created" };
2992
- const state = await this.readState();
2993
- if (input.frame.frame_kind === "session_started") return this.stageSession(state, input);
2994
- if (input.frame.frame_kind === "turn_started") return this.stageTurn(state, input);
2995
- return input.frame.frame_kind === "user_input" ? this.deliverInput(state, input) : this.deliverTerminal(state, input);
2996
- }
2997
- async stageSession(state, input) {
2998
- const localSessionRef = payloadRef(input.frame, "local_session_ref");
2999
- const hostSessionRef = hostProducerSessionRef(this.hostSourceRef, input.frame, localSessionRef);
3000
- const next = { ...state, sessions: { ...state.sessions, [hostSessionRef]: { observations: payloadObservations(input.frame), submission_ref: input.submission_ref } } };
3001
- await this.writeState(next);
3002
- return { status: "created" };
3003
- }
3004
- async stageTurn(state, input) {
3005
- const refs = frameRefs(input.frame, this.hostSourceRef);
3006
- const existing = state.turns[refs.host_turn_ref];
3007
- const nextTurn = {
3008
- host_session_ref: refs.host_session_ref,
3009
- observations: payloadObservations(input.frame),
3010
- submission_ref: input.submission_ref,
3011
- ...existing?.request_ref === void 0 ? {} : { request_ref: existing.request_ref },
3012
- ...existing?.prompt_ref === void 0 ? {} : { prompt_ref: existing.prompt_ref },
3013
- ...existing?.origin_host_input_ref === void 0 ? {} : { origin_host_input_ref: existing.origin_host_input_ref },
3014
- ...existing?.input_refused === void 0 ? {} : { input_refused: existing.input_refused }
3015
- };
3016
- await this.writeState({ ...state, turns: { ...state.turns, [refs.host_turn_ref]: nextTurn } });
3017
- return { status: "created" };
3018
- }
3019
- async deliverInput(state, input) {
3020
- const prepared = prepareInput(state, input, this.hostSourceRef);
3021
- const priorRefusal = refusedUnboundInput(prepared.turn);
3022
- if (priorRefusal !== void 0) return priorRefusal;
3023
- const outcome = await this.submit(prepared.submission);
3024
- if ("refusal" in outcome) {
3025
- if (outcome.refusal.definitive && prepared.turn !== void 0) await this.writeState({ ...state, turns: { ...state.turns, [prepared.refs.host_turn_ref]: { ...prepared.turn, input_refused: { code: outcome.refusal.code, message: outcome.refusal.message } } } });
3026
- return deliveryRefusal(outcome.refusal);
3027
- }
3028
- const acknowledgement2 = outcome.acknowledgement;
3029
- const nextTurn = bindInput(prepared, acknowledgement2.request_ref, input.submission_ref);
3030
- await this.writeState({ ...state, turns: { ...state.turns, [prepared.refs.host_turn_ref]: nextTurn } });
3031
- if (!bindsOriginInput(prepared)) return { status: "created" };
3032
- const bindingRequestRef = nextTurn.request_ref ?? acknowledgement2.request_ref;
3033
- const bindingPromptRef = nextTurn.prompt_ref ?? prepared.promptRef;
3034
- const bindingInputRef = nextTurn.origin_host_input_ref ?? prepared.hostInputRef;
3035
- const binding = { frame: structuredClone(input.frame), request_ref: bindingRequestRef, prompt_ref: bindingPromptRef, host_binding: {
3036
- host_source_ref: prepared.submission.identity.host_source_ref,
3037
- host_session_ref: prepared.submission.identity.host_session_ref,
3038
- host_turn_ref: prepared.submission.identity.host_turn_ref,
3039
- host_input_ref: bindingInputRef,
3040
- prompt_ref: bindingPromptRef
3041
- } };
3042
- const context = createRelayRequestWorkContext({ request_ref: bindingRequestRef, host_binding: binding.host_binding });
3043
- try {
3044
- await this.onRequestBound(binding, context);
3045
- } catch (error) {
3046
- if (error instanceof HostRelayBindingError) return { status: "refused", code: error.code, message: error.message };
3047
- return { status: "retryable", reason: `binding_store: ${errorMessage(error)}` };
3048
- }
3049
- return { status: "created" };
3170
+ // host/relay/src/relay-pipe-handshake.ts
3171
+ import { createHmac, randomBytes } from "node:crypto";
3172
+ import { constants as constants2 } from "node:fs";
3173
+ import { link, unlink as unlink2 } from "node:fs/promises";
3174
+ import { dirname as dirname4, join as join4 } from "node:path";
3175
+ var RELAY_PIPE_HANDSHAKE_TIMEOUT_MS = 5e3;
3176
+ var RELAY_PIPE_KEY_BYTES = 32;
3177
+ var RELAY_PIPE_KEY_FILENAME = "relay-pipe.key";
3178
+ var RELAY_PIPE_NONCE_BYTES = 32;
3179
+ var RELAY_PIPE_NONCE_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
3180
+ var RELAY_PIPE_HELLO_SCHEMA = "ceal.host_relay_pipe_hello.v1";
3181
+ var RELAY_PIPE_CHALLENGE_SCHEMA = "ceal.host_relay_pipe_challenge.v1";
3182
+ var RELAY_PIPE_RESPONSE_SCHEMA = "ceal.host_relay_pipe_response.v1";
3183
+ var RELAY_PIPE_FRAME_BYTES = Uint32Array.BYTES_PER_ELEMENT;
3184
+ var RELAY_PIPE_FRAME_MAX_BYTES = 1024;
3185
+ var RelayPipeHandshakeError = class extends Error {
3186
+ name = "RelayPipeHandshakeError";
3187
+ code;
3188
+ constructor(code, message) {
3189
+ super(message);
3190
+ this.code = code;
3050
3191
  }
3051
- async deliverTerminal(state, input) {
3052
- const refs = frameRefs(input.frame, this.hostSourceRef);
3053
- const turn = state.turns[refs.host_turn_ref];
3054
- if (turn === void 0) return { status: "refused", code: "origin_turn_unknown", message: "The terminal frame names a turn whose start was never admitted." };
3055
- if (turn.request_ref === void 0 || turn.prompt_ref === void 0 || turn.origin_host_input_ref === void 0) {
3056
- if (turn.input_refused !== void 0) return { status: "refused", code: "origin_input_refused", message: `The terminal frame names a turn whose input was refused (${turn.input_refused.code}: ${turn.input_refused.message}).` };
3057
- return { status: "retryable", reason: "origin_turn_unbound" };
3058
- }
3059
- const outcome = await this.submit({
3060
- identity: { host_source_ref: this.hostSourceRef, host_session_ref: refs.host_session_ref, host_turn_ref: refs.host_turn_ref },
3061
- host_input_ref: turn.origin_host_input_ref,
3062
- input_kind: "host_automatic",
3063
- prompt_ref: turn.prompt_ref,
3064
- observations: payloadObservations(input.frame),
3065
- request_ref: turn.request_ref,
3066
- submission_ref: input.submission_ref
3067
- });
3068
- return "refusal" in outcome ? deliveryRefusal(outcome.refusal) : { status: "created" };
3192
+ };
3193
+ function relayPipeKeyPath(relayStateRoot) {
3194
+ return join4(relayStateRoot, RELAY_PIPE_KEY_FILENAME);
3195
+ }
3196
+ async function loadRelayPipeKey(relayStateRoot, platform = process.platform, custody = {}) {
3197
+ const path = relayPipeKeyPath(relayStateRoot);
3198
+ if (platform === "win32") return await loadWindowsRelayPipeKey(path, custody);
3199
+ const existing = await readRelayPipeKey(path, platform, custody);
3200
+ if (existing !== null) return existing;
3201
+ return await createPosixRelayPipeKey(path, platform, custody);
3202
+ }
3203
+ async function loadWindowsRelayPipeKey(path, custody) {
3204
+ const existing = await readRelayPipeKey(path, "win32", custody);
3205
+ if (existing !== null) return existing;
3206
+ try {
3207
+ await createRandomPrivateFileWithSecurefs(path, RELAY_PIPE_KEY_BYTES, custody);
3208
+ } catch (error) {
3209
+ if (errorCode6(error) === "EEXIST") return await readWinnerPipeKey(path, "win32", custody);
3210
+ throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}; delete it and restart the Host Relay.`);
3069
3211
  }
3070
- async submit(input) {
3071
- const controller2 = new AbortController();
3072
- const timeout = setTimeout(() => controller2.abort(), this.timeoutMs);
3073
- try {
3074
- const acknowledgement2 = await this.gateway.submitHostTurn(input, controller2.signal);
3075
- return SAFE_REF4.test(acknowledgement2.request_ref) && isHostGatewayAcknowledgementDisposition(acknowledgement2.disposition) ? { acknowledgement: acknowledgement2 } : { refusal: { code: "invalid_response", message: "Gateway acknowledgement identity is invalid.", definitive: false } };
3076
- } catch (error) {
3077
- return { refusal: submissionRefusal(error) };
3078
- } finally {
3079
- clearTimeout(timeout);
3080
- }
3212
+ return await readWinnerPipeKey(path, "win32", custody);
3213
+ }
3214
+ async function createPosixRelayPipeKey(path, platform, custody) {
3215
+ const key = randomBytes(RELAY_PIPE_KEY_BYTES);
3216
+ const staging = `${path}.${randomBytes(8).toString("hex")}.tmp`;
3217
+ let handle;
3218
+ try {
3219
+ handle = await openNoFollow(staging, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL, 384);
3220
+ } catch (error) {
3221
+ if (errorCode6(error) !== "EEXIST") throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
3222
+ return await readWinnerPipeKey(path, platform, custody);
3081
3223
  }
3082
- async readState() {
3083
- try {
3084
- const state = decodeState4(JSON.parse(await readFile3(this.statePath, "utf8")));
3085
- if (state.host_source_ref !== this.hostSourceRef) throw new TypeError(`Host Relay Gateway source binding changed: ${refusalFacts([{ name: "stored_host_source_ref", value: state.host_source_ref }, { name: "configured_host_source_ref", value: this.hostSourceRef }])}.`);
3086
- return state;
3087
- } catch (error) {
3088
- if (errorCode7(error) === "ENOENT") return { schema_version: STATE_SCHEMA2, host_source_ref: this.hostSourceRef, sessions: {}, turns: {} };
3089
- throw error;
3090
- }
3224
+ try {
3225
+ const stats = await handle.stat();
3226
+ 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.`);
3227
+ await handle.writeFile(key);
3228
+ await handle.sync();
3229
+ } finally {
3230
+ await handle.close();
3091
3231
  }
3092
- async writeState(state) {
3093
- await durableAtomicReplace(this.statePath, JSON.stringify(state));
3232
+ try {
3233
+ await link(staging, path);
3234
+ } catch (error) {
3235
+ if (errorCode6(error) !== "EEXIST") throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
3236
+ return await readWinnerPipeKey(path, platform, custody);
3237
+ } finally {
3238
+ await unlink2(staging).catch(() => void 0);
3094
3239
  }
3095
- };
3096
- var RETRYABLE_HTTP_STATUSES = /* @__PURE__ */ new Set([401, 403, 408, 429]);
3097
- function submissionRefusal(error) {
3098
- const status = isRecord(error) && typeof error.status === "number" ? error.status : void 0;
3099
- const code = errorCode7(error) ?? "gateway_unavailable";
3100
- const definitive = code === "payload_rejected" || status !== void 0 && status >= 400 && status < 500 && !RETRYABLE_HTTP_STATUSES.has(status);
3101
- return { code, message: errorMessage(error), definitive, diagnostic: typedDiagnostic(error, status) };
3102
- }
3103
- function typedDiagnostic(error, status) {
3104
- if (!isRecord(error)) return status === void 0 ? {} : { gatewayStatus: status };
3105
- const gatewayCode = isBoundedRefusalWord(error.error_code) ? error.error_code : void 0;
3106
- const gatewayReason = isBoundedRefusalWord(error.refusal_reason) ? error.refusal_reason : void 0;
3107
- return {
3108
- ...typeof error.code === "string" ? { relayCode: error.code } : {},
3109
- ...status === void 0 ? {} : { gatewayStatus: status },
3110
- ...gatewayCode === void 0 ? {} : { gatewayCode },
3111
- ...gatewayReason === void 0 ? {} : { gatewayReason }
3112
- };
3240
+ return Buffer.from(key);
3113
3241
  }
3114
- function deliveryRefusal(refusal) {
3115
- const diagnostic = refusal.diagnostic === void 0 ? {} : { diagnostic: refusal.diagnostic };
3116
- return refusal.definitive ? { status: "refused", code: refusal.code, message: refusal.message, ...diagnostic } : { status: "retryable", reason: `${refusal.code}: ${refusal.message}`, ...diagnostic };
3117
- }
3118
- function errorMessage(error) {
3119
- return error instanceof Error ? error.message : String(error);
3120
- }
3121
- function frameRefs(frame, hostSourceRef) {
3122
- const localSessionRef = payloadRef(frame, "local_session_ref");
3123
- const localTurnRef = payloadRef(frame, "local_turn_ref");
3124
- const hostSessionRef = hostProducerSessionRef(hostSourceRef, frame, localSessionRef);
3125
- return { local_session_ref: localSessionRef, local_turn_ref: localTurnRef, host_session_ref: hostSessionRef, host_turn_ref: hostProducerTurnRef(hostSourceRef, frame, localSessionRef, localTurnRef) };
3126
- }
3127
- function prepareInput(state, input, hostSourceRef) {
3128
- const refs = frameRefs(input.frame, hostSourceRef);
3129
- const turn = state.turns[refs.host_turn_ref];
3130
- const promptRef = turn?.prompt_ref ?? hostProducerPromptRef(payloadPromptDigest(input.frame));
3131
- const hostInputRef = hostProducerInputRef(hostSourceRef, input.frame, refs.local_session_ref, refs.local_turn_ref, payloadRef(input.frame, "local_input_ref"));
3132
- const observations = uniqueObservations([...state.sessions[refs.host_session_ref]?.observations ?? [], ...turn?.observations ?? [], ...payloadObservations(input.frame)]);
3133
- const submission = compactSubmission({
3134
- identity: { host_source_ref: hostSourceRef, host_session_ref: refs.host_session_ref, host_turn_ref: refs.host_turn_ref },
3135
- host_input_ref: hostInputRef,
3136
- input_kind: payloadInputKind(input.frame),
3137
- prompt_ref: promptRef,
3138
- observations,
3139
- request_ref: turn?.request_ref,
3140
- submission_ref: input.submission_ref
3141
- });
3142
- return { refs, turn, promptRef, hostInputRef, submission };
3143
- }
3144
- function bindInput(prepared, requestRef, submissionRef) {
3145
- return {
3146
- host_session_ref: prepared.refs.host_session_ref,
3147
- observations: prepared.turn?.observations ?? [],
3148
- submission_ref: prepared.turn?.submission_ref ?? submissionRef,
3149
- request_ref: prepared.turn?.request_ref ?? requestRef,
3150
- prompt_ref: prepared.turn?.prompt_ref ?? prepared.promptRef,
3151
- origin_host_input_ref: prepared.turn?.origin_host_input_ref ?? prepared.hostInputRef
3152
- };
3242
+ async function readWinnerPipeKey(path, platform, custody) {
3243
+ const winner = await readRelayPipeKey(path, platform, custody);
3244
+ if (winner === null) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
3245
+ return winner;
3153
3246
  }
3154
- function refusedUnboundInput(turn) {
3155
- if (turn?.request_ref !== void 0 || turn?.input_refused === void 0) return void 0;
3156
- return { status: "refused", code: "origin_input_refused", message: `The input frame names a turn whose origin input was refused (${turn.input_refused.code}: ${turn.input_refused.message}).` };
3247
+ async function readRelayPipeKey(path, platform, custody) {
3248
+ if (platform === "win32") return await readWindowsRelayPipeKey(path, custody);
3249
+ return await readPosixRelayPipeKey(path);
3157
3250
  }
3158
- function bindsOriginInput(prepared) {
3159
- return prepared.turn?.request_ref === void 0 || prepared.turn.origin_host_input_ref === prepared.hostInputRef;
3251
+ async function readWindowsRelayPipeKey(path, custody) {
3252
+ let key;
3253
+ try {
3254
+ key = await readPrivateFileWithSecurefs(path, RELAY_PIPE_KEY_BYTES, custody);
3255
+ } catch (error) {
3256
+ if (errorCode6(error) === "ENOENT" || errorCode6(error) === "ELOOP") return null;
3257
+ throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}; delete it and restart the Host Relay.`);
3258
+ }
3259
+ 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.`);
3260
+ return Buffer.from(key);
3160
3261
  }
3161
- function payloadRef(frame, key) {
3162
- const value = frame.payload[key];
3163
- if (typeof value !== "string" || !SAFE_REF4.test(value)) throw new TypeError("Host Relay frame reference is invalid.");
3164
- return value;
3262
+ async function readPosixRelayPipeKey(path) {
3263
+ let handle;
3264
+ try {
3265
+ handle = await openNoFollow(path);
3266
+ } catch (error) {
3267
+ if (errorCode6(error) === "ENOENT" || errorCode6(error) === "ELOOP") return null;
3268
+ throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
3269
+ }
3270
+ try {
3271
+ const stats = await handle.stat();
3272
+ 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.`);
3273
+ 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.`);
3274
+ 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.`);
3275
+ return await handle.readFile();
3276
+ } finally {
3277
+ await handle.close();
3278
+ }
3165
3279
  }
3166
- function payloadPromptDigest(frame) {
3167
- const prompt = payloadObservations(frame).find((observation) => observation.observation_kind === "prompt");
3168
- if (prompt === void 0 || !isCealHostPromptObservationValue(prompt.value)) throw new TypeError("Host Relay prompt observation is invalid.");
3169
- return prompt.value.body_sha256;
3280
+ function createPipeClientNonce() {
3281
+ return randomBytes(RELAY_PIPE_NONCE_BYTES).toString("base64url");
3170
3282
  }
3171
- function payloadObservations(frame) {
3172
- return Array.isArray(frame.payload.observations) ? frame.payload.observations.filter(isObservation) : [];
3283
+ function pipeProof(key, role, firstNonce, secondNonce) {
3284
+ return createHmac("sha256", key).update(`${role}\0${firstNonce}\0${secondNonce}`, "utf8").digest("hex");
3173
3285
  }
3174
- function payloadInputKind(frame) {
3175
- const value = frame.payload.input_kind;
3176
- if (!isCealHostInputKind(value)) throw new TypeError("Host Relay input kind is invalid.");
3177
- return value;
3286
+ function createPipeServerProof(key, clientNonce, serverNonce) {
3287
+ return pipeProof(key, "server", clientNonce, serverNonce);
3178
3288
  }
3179
- function uniqueObservations(values) {
3180
- const seen = /* @__PURE__ */ new Set();
3181
- return values.filter((value) => !seen.has(value.observation_ref) && Boolean(seen.add(value.observation_ref)));
3289
+ function createPipeClientProof(key, serverNonce, clientNonce) {
3290
+ return pipeProof(key, "client", serverNonce, clientNonce);
3182
3291
  }
3183
- function compactSubmission(value) {
3184
- return { identity: value.identity, host_input_ref: value.host_input_ref, input_kind: value.input_kind, prompt_ref: value.prompt_ref, observations: value.observations, submission_ref: value.submission_ref, ...value.request_ref === void 0 ? {} : { request_ref: value.request_ref } };
3292
+ function encodePipeFrame(value) {
3293
+ const body = Buffer.from(JSON.stringify(value), "utf8");
3294
+ const output = Buffer.allocUnsafe(body.length + RELAY_PIPE_FRAME_BYTES);
3295
+ output.writeUInt32BE(body.length, 0);
3296
+ body.copy(output, RELAY_PIPE_FRAME_BYTES);
3297
+ return output;
3185
3298
  }
3186
- function isObservation(value) {
3299
+ function decodePipeFrame(buffer) {
3300
+ if (buffer.length < RELAY_PIPE_FRAME_BYTES) return null;
3301
+ const length = buffer.readUInt32BE(0);
3302
+ 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}.`);
3303
+ if (buffer.length < length + RELAY_PIPE_FRAME_BYTES) return null;
3304
+ let value;
3187
3305
  try {
3188
- validateCealHostObservationDraft(value);
3189
- return true;
3306
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(RELAY_PIPE_FRAME_BYTES, length + RELAY_PIPE_FRAME_BYTES)));
3190
3307
  } catch {
3191
- return false;
3308
+ throw new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is not JSON.");
3192
3309
  }
3310
+ return { value, rest: buffer.subarray(length + RELAY_PIPE_FRAME_BYTES) };
3193
3311
  }
3194
- function isRecord(value) {
3195
- return value !== null && typeof value === "object" && !Array.isArray(value);
3196
- }
3197
- function validOptionalTimeout(value) {
3198
- return value === void 0 || Number.isSafeInteger(value) && Number(value) >= 1 && Number(value) <= 6e4;
3199
- }
3200
- function errorCode7(error) {
3201
- return isRecord(error) && typeof error.code === "string" ? error.code : null;
3202
- }
3203
- function decodeState4(value) {
3204
- if (!isRecord(value) || value.schema_version !== STATE_SCHEMA2) throw new TypeError(`Host Relay Gateway state is invalid: ${refusalFact("schema_version", isRecord(value) ? value.schema_version : void 0, STATE_SCHEMA2)}.`);
3205
- if (!opaqueHostSourceRef(value.host_source_ref)) throw new TypeError(`Host Relay Gateway state is invalid: ${refusalFact("host_source_ref", value.host_source_ref, "an opaque Host source reference")}.`);
3206
- if (!isRecord(value.sessions) || !isRecord(value.turns)) throw new TypeError(`Host Relay Gateway state is invalid: ${refusalFacts([{ name: "sessions_is_object", value: isRecord(value.sessions), expected: "true" }, { name: "turns_is_object", value: isRecord(value.turns), expected: "true" }])}.`);
3207
- return { schema_version: STATE_SCHEMA2, host_source_ref: value.host_source_ref, sessions: decodeSessions(value.sessions), turns: decodeTurns(value.turns) };
3208
- }
3209
- function decodeSessions(value) {
3210
- const result2 = {};
3211
- for (const [key, item] of Object.entries(value)) {
3212
- if (!SAFE_REF4.test(key) || !isRecord(item) || !Array.isArray(item.observations) || !item.observations.every(isObservation) || typeof item.submission_ref !== "string") throw new TypeError("Host Relay staged Session is invalid.");
3213
- result2[key] = { observations: item.observations, submission_ref: item.submission_ref };
3214
- }
3215
- return result2;
3312
+ function validNonce(value) {
3313
+ return typeof value === "string" && RELAY_PIPE_NONCE_PATTERN.test(value);
3216
3314
  }
3217
- function decodeTurns(value) {
3218
- const result2 = {};
3219
- for (const [key, item] of Object.entries(value)) {
3220
- if (!SAFE_REF4.test(key)) throw new TypeError("Host Relay staged turn is invalid.");
3221
- result2[key] = decodeTurn(item);
3222
- }
3223
- return result2;
3315
+ function writePipeFrame(socket, frame) {
3316
+ if (socket.write(frame)) return Promise.resolve();
3317
+ return new Promise((resolve, reject) => {
3318
+ socket.once("drain", resolve);
3319
+ socket.once("error", reject);
3320
+ socket.once("close", () => reject(new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake connection closed.")));
3321
+ });
3224
3322
  }
3225
- function decodeTurn(item) {
3226
- if (!isRecord(item) || typeof item.host_session_ref !== "string" || !Array.isArray(item.observations) || !item.observations.every(isObservation) || typeof item.submission_ref !== "string") throw new TypeError("Host Relay staged turn is invalid.");
3227
- return {
3228
- host_session_ref: item.host_session_ref,
3229
- observations: item.observations,
3230
- submission_ref: item.submission_ref,
3231
- ...decodeTurnBinding(item),
3232
- ...isInputRefusal(item.input_refused) ? { input_refused: { code: item.input_refused.code, message: item.input_refused.message } } : {}
3323
+ function readPipeFrames() {
3324
+ let buffer = Buffer.alloc(0);
3325
+ let pending = null;
3326
+ const feed = (chunk) => {
3327
+ buffer = Buffer.concat([buffer, chunk]);
3328
+ if (pending === null) return;
3329
+ let frame;
3330
+ try {
3331
+ frame = decodePipeFrame(buffer);
3332
+ } catch (error) {
3333
+ const waiting2 = pending;
3334
+ pending = null;
3335
+ waiting2.reject(error instanceof Error ? error : new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is invalid."));
3336
+ return;
3337
+ }
3338
+ if (frame === null) return;
3339
+ buffer = frame.rest;
3340
+ const waiting = pending;
3341
+ pending = null;
3342
+ waiting.resolve({ value: frame.value, rest: Buffer.alloc(0) });
3233
3343
  };
3234
- }
3235
- function decodeTurnBinding(item) {
3236
- return { ...typeof item.request_ref === "string" ? { request_ref: item.request_ref } : {}, ...typeof item.prompt_ref === "string" ? { prompt_ref: item.prompt_ref } : {}, ...typeof item.origin_host_input_ref === "string" ? { origin_host_input_ref: item.origin_host_input_ref } : {} };
3237
- }
3238
- function isInputRefusal(value) {
3239
- return isRecord(value) && typeof value.code === "string" && typeof value.message === "string";
3240
- }
3241
-
3242
- // host/relay/src/relay-gateway-http.ts
3243
- import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
3244
- import { lstat as lstat2, open as open4 } from "node:fs/promises";
3245
- import { dirname as dirname2 } from "node:path";
3246
-
3247
- // host/gateway-http.ts
3248
- var SAFE_HOST_REF = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3249
- function hostIngressBody(input, invalidEventTime) {
3250
- if (!isCealHostInputKind(input.input_kind)) throw new TypeError("Host ingress input kind is invalid.");
3251
- const prompt = input.observations.find((observation) => observation.observation_kind === "prompt");
3252
- const observedAt = prompt?.observed_at ?? input.observations[0]?.observed_at;
3253
- if (observedAt === void 0 || !Number.isFinite(Date.parse(observedAt))) throw invalidEventTime();
3254
- return {
3255
- submission_ref: input.submission_ref,
3256
- host_source_ref: input.identity.host_source_ref,
3257
- host_session_ref: input.identity.host_session_ref,
3258
- host_turn_ref: input.identity.host_turn_ref,
3259
- host_input_ref: input.host_input_ref,
3260
- input_kind: input.input_kind,
3261
- prompt_ref: input.prompt_ref,
3262
- observed_at: observedAt,
3263
- observations: input.observations,
3264
- ...input.request_ref === void 0 ? {} : { request_ref: input.request_ref }
3265
- };
3266
- }
3267
- async function responseJson(response2, invalidResponse2) {
3268
- try {
3269
- return await response2.json();
3270
- } catch {
3271
- throw invalidResponse2();
3272
- }
3273
- }
3274
- async function submitHostIngress(options) {
3275
- if (options.input.identity.host_source_ref !== options.expected_host_source_ref) throw options.sourceMismatch();
3276
- const body = JSON.stringify(hostIngressBody(options.input, options.invalidEventTime));
3277
- let response2;
3278
- try {
3279
- response2 = await options.fetcher(options.endpoint, {
3280
- method: "POST",
3281
- ...options.redirect === void 0 ? {} : { redirect: options.redirect },
3282
- headers: { authorization: `Bearer ${options.credential}`, "content-type": "application/json" },
3283
- body,
3284
- signal: options.signal
3285
- });
3286
- } catch {
3287
- throw options.unavailable();
3288
- }
3289
- const responseBody = await responseJson(response2, options.invalidResponse);
3290
- if (!response2.ok) throw options.refused(response2.status, responseBody);
3291
- return options.acknowledgement(responseBody);
3292
- }
3293
-
3294
- // host/relay/src/relay-gateway-http.ts
3295
- var HOST_REGISTRATION_STATE_SCHEMA = "ceal.host_registration_state.v1";
3296
- var HOST_REGISTRATION_STATE_KEYS = Object.freeze([
3297
- "credential_generation",
3298
- "gateway_request_origin",
3299
- "host_access_token",
3300
- "host_installation_ref",
3301
- "host_source_ref",
3302
- "registration_attempt_ref",
3303
- "schema_version",
3304
- "status"
3305
- ]);
3306
- var HOST_REGISTRATION_STATE_MAX_BYTES = 65536;
3307
- var HostRelayGatewayError = class extends Error {
3308
- name = "HostRelayGatewayError";
3309
- code;
3310
- /** HTTP status of a Gateway refusal, when one answered. */
3311
- status;
3312
- /** The Gateway's own refusal word, when it named one; a bounded token, never its message. */
3313
- error_code;
3314
- /** The Gateway mint refusal's bounded continuity reason, when it named one; a fixed token, never its message. */
3315
- refusal_reason;
3316
- constructor(code, message, status, errorCode9, refusalReason) {
3317
- super(message);
3318
- this.code = code;
3319
- if (status !== void 0) this.status = status;
3320
- if (errorCode9 !== void 0) this.error_code = errorCode9;
3321
- if (refusalReason !== void 0) this.refusal_reason = refusalReason;
3322
- }
3323
- };
3324
- var GATEWAY_REFUSAL_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
3325
- async function gatewayRefusal(response2) {
3326
- try {
3327
- const body = await response2.json();
3328
- if (!isCealJsonRecord(body)) return {};
3329
- const code = typeof body.error_code === "string" && GATEWAY_REFUSAL_CODE_PATTERN.test(body.error_code) ? body.error_code : void 0;
3330
- const reason = code === "request_context_refused" && typeof body.reason === "string" && GATEWAY_REFUSAL_CODE_PATTERN.test(body.reason) && isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, body.reason) ? body.reason : void 0;
3331
- return {
3332
- ...code === void 0 ? {} : { code },
3333
- ...reason === void 0 ? {} : { reason }
3334
- };
3335
- } catch {
3336
- return {};
3337
- }
3338
- }
3339
- function submitRefusalCode(body) {
3340
- if (!isCealJsonRecord(body) || typeof body.error_code !== "string" || !GATEWAY_REFUSAL_CODE_PATTERN.test(body.error_code)) return void 0;
3341
- return body.error_code;
3342
- }
3343
- function refusalMessage(status, body) {
3344
- const record6 = isCealJsonRecord(body) ? body : {};
3345
- const code = typeof record6.error_code === "string" ? record6.error_code : "unknown";
3346
- const message = typeof record6.message === "string" ? `: ${record6.message}` : "";
3347
- return `Gateway Host ingress refused the submission (${status} ${code}${message}).`;
3348
- }
3349
- function createHostRelayGatewayHttpPort(options) {
3350
- if (!SAFE_HOST_REF.test(options.host_source_ref) || options.host_source_ref.startsWith("ceal-host-source:") || typeof options.registration_state_file !== "string" || options.registration_state_file.length === 0) {
3351
- throw new HostRelayGatewayError("configuration", "Host Relay Gateway binding is invalid.");
3352
- }
3353
- return Object.freeze({
3354
- async submitHostTurn(input, signal) {
3355
- if (input.identity.host_source_ref !== options.host_source_ref) throw new HostRelayGatewayError("payload_rejected", `Host Relay source binding changed: ${refusalFacts([{ name: "input_host_source_ref", value: input.identity.host_source_ref }, { name: "configured_host_source_ref", value: options.host_source_ref }])}.`);
3356
- const registration = await readHostRegistrationAuthority(options.registration_state_file, options.host_source_ref);
3357
- const endpoint = hostRelayGatewayEndpoint(registration.gateway_request_origin);
3358
- return await submitHostIngress({
3359
- input,
3360
- expected_host_source_ref: options.host_source_ref,
3361
- endpoint,
3362
- credential: registration.host_access_token,
3363
- fetcher: options.fetch ?? fetch,
3364
- signal,
3365
- redirect: "error",
3366
- sourceMismatch: () => new HostRelayGatewayError("payload_rejected", "Host Relay source binding changed."),
3367
- invalidEventTime: () => new HostRelayGatewayError("payload_rejected", "Host Relay submission has no event time."),
3368
- unavailable: () => new HostRelayGatewayError("gateway_unavailable", "Gateway Host ingress is unavailable."),
3369
- invalidResponse: () => new HostRelayGatewayError("invalid_response", "Gateway Host ingress response is not JSON."),
3370
- refused: (status, body) => new HostRelayGatewayError("gateway_refused", refusalMessage(status, body), status, submitRefusalCode(body)),
3371
- acknowledgement
3372
- });
3373
- },
3374
- async mintInvocationContext(input, signal) {
3375
- const registration = await readHostRegistrationAuthority(options.registration_state_file, options.host_source_ref);
3376
- let response2;
3344
+ const waiter = {
3345
+ wait: () => new Promise((resolve, reject) => {
3346
+ let frame;
3377
3347
  try {
3378
- response2 = await (options.fetch ?? fetch)(hostRelayGatewayRoute(registration.gateway_request_origin, CEAL_HOST_INVOCATION_CONTEXT_PATH), {
3379
- method: "POST",
3380
- redirect: "error",
3381
- signal,
3382
- headers: { authorization: `Bearer ${registration.host_access_token}`, "content-type": "application/json" },
3383
- body: JSON.stringify({ schema_version: "ceal.gateway_host_invocation_context_request.v1", ...input })
3384
- });
3385
- } catch {
3386
- throw new HostRelayGatewayError("gateway_unavailable", "Gateway Host invocation context is unavailable.");
3348
+ frame = decodePipeFrame(buffer);
3349
+ } catch (error) {
3350
+ reject(error instanceof Error ? error : new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is invalid."));
3351
+ return;
3387
3352
  }
3388
- if (!response2.ok) {
3389
- const refusal = await gatewayRefusal(response2);
3390
- throw new HostRelayGatewayError(response2.status === 401 ? "credential" : "gateway_refused", `Gateway Host invocation context was refused (${response2.status}).`, response2.status, refusal.code, refusal.reason);
3353
+ if (frame !== null) {
3354
+ buffer = frame.rest;
3355
+ resolve({ value: frame.value, rest: Buffer.alloc(0) });
3356
+ return;
3391
3357
  }
3392
- let value;
3393
- try {
3394
- value = await response2.json();
3395
- } catch {
3396
- throw new HostRelayGatewayError("invalid_response", "Gateway Host invocation context response is not JSON.");
3397
- }
3398
- try {
3399
- return decodeCealHostInvocationAuthenticationResult(value);
3400
- } catch {
3401
- throw new HostRelayGatewayError("invalid_response", "Gateway Host invocation context response is invalid.");
3358
+ pending = { resolve, reject };
3359
+ }),
3360
+ cancel: (error) => {
3361
+ if (pending !== null) {
3362
+ const waiting = pending;
3363
+ pending = null;
3364
+ waiting.reject(error);
3402
3365
  }
3403
3366
  }
3404
- });
3405
- }
3406
- function hostRelayGatewayEndpoint(value) {
3407
- return hostRelayGatewayRoute(value, CEAL_HOST_INGRESS_PATH);
3408
- }
3409
- function hostRelayGatewayRoute(value, path) {
3410
- let parsed;
3411
- try {
3412
- parsed = new URL(value);
3413
- } catch {
3414
- throw new HostRelayGatewayError("configuration", "Host Relay Gateway origin is invalid.");
3415
- }
3416
- const hostname = parsed.hostname.toLowerCase().replace(/\.+$/u, "");
3417
- if (/\s|\0/u.test(value) || hostname === "" || !validPort(parsed) || !isExplicitLoopbackHttp(parsed, hostname) && !isSecureHttps(parsed)) {
3418
- throw new HostRelayGatewayError("configuration", `Host Relay Gateway origin is invalid: ${refusalFacts([{ name: "protocol", value: parsed.protocol, expected: "https:, or http: with an explicit loopback port" }, { name: "hostname", value: hostname }, { name: "port", value: parsed.port }])}.`);
3419
- }
3420
- if (hasOriginDecoration(value, parsed)) {
3421
- throw new HostRelayGatewayError("configuration", `Host Relay Gateway origin is invalid: ${refusalFacts([{ name: "origin", value: parsed.origin }, { name: "has_userinfo", value: parsed.username !== "" || parsed.password !== "", expected: "false" }, { name: "pathname", value: parsed.pathname, expected: "/" }, { name: "search", value: parsed.search, expected: "an empty query" }, { name: "hash", value: parsed.hash, expected: "an empty fragment" }])}.`);
3422
- }
3423
- return `${parsed.origin}${path}`;
3424
- }
3425
- function isExplicitLoopbackHttp(parsed, hostname) {
3426
- return parsed.protocol === "http:" && parsed.port !== "" && (hostname === "127.0.0.1" || hostname === "[::1]");
3427
- }
3428
- function isSecureHttps(parsed) {
3429
- return parsed.protocol === "https:";
3430
- }
3431
- function validPort(parsed) {
3432
- if (parsed.port === "") return true;
3433
- const port = Number(parsed.port);
3434
- return Number.isInteger(port) && port >= 1 && port <= 65535;
3367
+ };
3368
+ return { waiter, feed, remainder: () => buffer };
3369
+ }
3370
+ function attachHandshakeReader(socket, waiter, feed) {
3371
+ const onData = (chunk) => feed(Buffer.from(chunk));
3372
+ const onError = (error) => waiter.cancel(error);
3373
+ const onClose = () => waiter.cancel(new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake connection closed."));
3374
+ socket.on("data", onData);
3375
+ socket.once("error", onError);
3376
+ socket.once("close", onClose);
3377
+ return () => {
3378
+ socket.removeListener("data", onData);
3379
+ socket.removeListener("error", onError);
3380
+ socket.removeListener("close", onClose);
3381
+ };
3435
3382
  }
3436
- function hasOriginDecoration(value, parsed) {
3437
- const schemeEnd = value.indexOf("://");
3438
- const authority = schemeEnd < 0 ? "" : value.slice(schemeEnd + 3);
3439
- const suffixOffset = authority.search(/[\\/?#]/u);
3440
- const rawSuffix = suffixOffset < 0 ? "" : authority.slice(suffixOffset);
3441
- return parsed.username !== "" || parsed.password !== "" || parsed.pathname !== "/" || parsed.search !== "" || parsed.hash !== "" || rawSuffix !== "" && rawSuffix !== "/";
3383
+ function handshakeTimeout(timeoutMs) {
3384
+ let timer;
3385
+ const done = new Promise((_resolve, reject) => {
3386
+ timer = setTimeout(() => reject(new RelayPipeHandshakeError("pipe_handshake_timeout", `Host Relay pipe handshake stalled past ${timeoutMs} ms; dropping the connection.`)), timeoutMs);
3387
+ timer.unref?.();
3388
+ });
3389
+ return { done, clear: () => clearTimeout(timer) };
3442
3390
  }
3443
- async function readHostRegistrationAuthority(path, expectedHostSourceRef) {
3444
- let handle;
3391
+ async function conductPipeServerHandshake(socket, key, timeoutMs = RELAY_PIPE_HANDSHAKE_TIMEOUT_MS) {
3392
+ const bound = handshakeTimeout(timeoutMs);
3393
+ const { waiter, feed, remainder } = readPipeFrames();
3394
+ const detach = attachHandshakeReader(socket, waiter, feed);
3445
3395
  try {
3446
- await assertOwnerPrivateDirectory(dirname2(path));
3447
- handle = await open4(path, O_RDONLY3 | O_NOFOLLOW2);
3448
- const stats = await handle.stat();
3449
- if (!stats.isFile() || !ownerPrivate3(stats)) throw new HostRelayGatewayError("credential", "Host Relay credential file is not owner-private.");
3450
- const raw = await handle.readFile("utf8");
3451
- if (Buffer.byteLength(raw, "utf8") > HOST_REGISTRATION_STATE_MAX_BYTES) throw new HostRelayGatewayError("credential", `Host registration state is invalid: ${refusalFact("registration_state_bytes", Buffer.byteLength(raw, "utf8"), `at most ${HOST_REGISTRATION_STATE_MAX_BYTES}`)}.`);
3452
- let value;
3453
- try {
3454
- value = JSON.parse(raw);
3455
- } catch {
3456
- throw new HostRelayGatewayError("credential", "Host registration state is invalid.");
3396
+ const hello = await Promise.race([waiter.wait(), bound.done]);
3397
+ if (!isRecord2(hello.value) || hello.value.schema_version !== RELAY_PIPE_HELLO_SCHEMA || !validNonce(hello.value.client_nonce)) {
3398
+ throw new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe client hello is invalid; dropping the connection without an answer.");
3457
3399
  }
3458
- return decodeHostRegistrationAuthority(value, expectedHostSourceRef);
3459
- } catch (error) {
3460
- if (error instanceof HostRelayGatewayError) throw error;
3461
- throw new HostRelayGatewayError("credential", "Host Relay credential file is unavailable.");
3400
+ const clientNonce = hello.value.client_nonce;
3401
+ const serverNonce = createPipeClientNonce();
3402
+ const serverProof = createPipeServerProof(key, clientNonce, serverNonce);
3403
+ await Promise.race([writePipeFrame(socket, encodePipeFrame({ schema_version: RELAY_PIPE_CHALLENGE_SCHEMA, server_nonce: serverNonce, server_proof: serverProof })), bound.done]);
3404
+ const response2 = await Promise.race([waiter.wait(), bound.done]);
3405
+ if (!isRecord2(response2.value) || response2.value.schema_version !== RELAY_PIPE_RESPONSE_SCHEMA || typeof response2.value.client_proof !== "string") {
3406
+ throw new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe client proof is invalid; dropping the connection without an answer.");
3407
+ }
3408
+ const expected = createPipeClientProof(key, serverNonce, clientNonce);
3409
+ if (!timingSafeEqualHex(expected, response2.value.client_proof)) {
3410
+ throw new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe client proof failed; dropping the connection without an answer.");
3411
+ }
3412
+ return remainder();
3462
3413
  } finally {
3463
- await handle?.close();
3414
+ bound.clear();
3415
+ detach();
3464
3416
  }
3465
3417
  }
3466
- function decodeHostRegistrationAuthority(value, expectedHostSourceRef) {
3467
- const keys = HOST_REGISTRATION_STATE_KEYS;
3468
- const fieldCount = isCealJsonRecord(value) ? Object.keys(value).length : 0;
3469
- const missing = isCealJsonRecord(value) ? keys.filter((key) => !Object.hasOwn(value, key)).length : keys.length;
3470
- if (!isCealJsonRecord(value) || fieldCount !== keys.length || missing !== 0) throw new HostRelayGatewayError("credential", `Host registration state is invalid or does not match this Relay: ${refusalFacts([{ name: "registration_field_count", value: fieldCount, expected: `exactly ${keys.length}` }, { name: "missing_registration_field_count", value: missing, expected: "0" }])}.`);
3471
- if (!validRegistrationIdentity(value, expectedHostSourceRef)) throw new HostRelayGatewayError("credential", `Host registration state is invalid or does not match this Relay: ${refusalFacts([{ name: "schema_version", value: value.schema_version, expected: "ceal.host_registration_state.v1" }, { name: "status", value: value.status, expected: "active" }, { name: "host_source_ref", value: value.host_source_ref, expected: expectedHostSourceRef }, { name: "registration_attempt_ref", value: value.registration_attempt_ref, expected: "host-registration-attempt:<uuid>" }, { name: "host_installation_ref", value: value.host_installation_ref, expected: "host-installation:<uuid>" }])}.`);
3472
- if (!validRegistrationCredential(value)) throw new HostRelayGatewayError("credential", `Host registration state is invalid or does not match this Relay: ${refusalFacts([{ name: "credential_generation", value: value.credential_generation, expected: "a positive safe integer" }, { name: "host_access_token_matches_registered_shape", value: validHostAccessToken(value.host_access_token), expected: "true" }])}.`);
3473
- if (!validGatewayOrigin(value.gateway_request_origin)) throw new HostRelayGatewayError("credential", `Host registration state is invalid or does not match this Relay: ${refusalFact("gateway_request_origin", value.gateway_request_origin, "an HTTPS origin or explicit-port loopback HTTP origin")}.`);
3474
- return { gateway_request_origin: value.gateway_request_origin, host_access_token: value.host_access_token };
3475
- }
3476
- function validRegistrationIdentity(value, expectedHostSourceRef) {
3477
- return value.schema_version === HOST_REGISTRATION_STATE_SCHEMA && value.status === "active" && value.host_source_ref === expectedHostSourceRef && typeof value.registration_attempt_ref === "string" && CEAL_HOST_REGISTRATION_ATTEMPT_REF_PATTERN.test(value.registration_attempt_ref) && typeof value.host_installation_ref === "string" && CEAL_HOST_INSTALLATION_REF_PATTERN.test(value.host_installation_ref) && typeof value.host_source_ref === "string" && hostSourceUuid(value.host_source_ref) !== null;
3478
- }
3479
- function validRegistrationCredential(value) {
3480
- return Number.isSafeInteger(value.credential_generation) && Number(value.credential_generation) > 0 && validHostAccessToken(value.host_access_token);
3418
+ function timingSafeEqualHex(left, right) {
3419
+ if (left.length !== right.length) return false;
3420
+ let diff = 0;
3421
+ for (let index = 0; index < left.length; index += 1) diff |= left.charCodeAt(index) ^ right.charCodeAt(index);
3422
+ return diff === 0;
3481
3423
  }
3482
- function validHostAccessToken(value) {
3483
- return typeof value === "string" && CEAL_HOST_ACCESS_TOKEN_PATTERN.test(value);
3484
- }
3485
- function validGatewayOrigin(value) {
3486
- try {
3487
- if (typeof value !== "string") return false;
3488
- hostRelayGatewayEndpoint(value);
3489
- return true;
3490
- } catch {
3491
- return false;
3492
- }
3493
- }
3494
- async function assertOwnerPrivateDirectory(path) {
3495
- const stats = await lstat2(path);
3496
- if (!stats.isDirectory() || stats.isSymbolicLink() || !ownerPrivate3(stats)) throw new HostRelayGatewayError("credential", "Host Relay credential directory is not owner-private.");
3497
- }
3498
- function ownerPrivate3(stats) {
3499
- if (process.platform === "win32") return true;
3500
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
3501
- return stats.uid === uid && (stats.mode & 63) === 0;
3424
+ function isRecord2(value) {
3425
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3502
3426
  }
3503
- function acknowledgement(value) {
3504
- const fields = isCealJsonRecord(value) ? value : {};
3505
- if (!isCealJsonRecord(value) || value.ok !== true || value.status !== "accepted" || typeof value.request_ref !== "string" || !SAFE_HOST_REF.test(value.request_ref) || !isHostGatewayAcknowledgementDisposition(value.disposition)) {
3506
- throw new HostRelayGatewayError("invalid_response", `Gateway Host ingress acknowledgement is invalid: ${refusalFacts([{ name: "ok", value: fields.ok, expected: "true" }, { name: "status", value: fields.status, expected: "accepted" }, { name: "request_ref", value: fields.request_ref, expected: "a safe Host reference" }, { name: "disposition", value: fields.disposition, expected: "a Gateway acknowledgement disposition" }])}.`);
3507
- }
3508
- return { request_ref: value.request_ref, disposition: value.disposition };
3427
+ function errorCode6(error) {
3428
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
3509
3429
  }
3510
3430
 
3511
3431
  // host/relay/src/relay-ipc.ts
3512
- import { chmod, lstat as lstat3, rename as rename2, unlink as unlink2 } from "node:fs/promises";
3432
+ import { chmod, lstat as lstat3, rename as rename2, unlink as unlink3 } from "node:fs/promises";
3513
3433
  import { createConnection, createServer } from "node:net";
3514
- import { dirname as dirname3, join as join6 } from "node:path";
3515
-
3516
- // host/relay/src/current-prompt-custody.ts
3517
- import { createHash as createHash5 } from "node:crypto";
3518
- var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
3519
- var MAX_CURRENT_PROMPTS = 1024;
3520
- var MAX_CONSUMED_PROMPTS = 1024;
3521
- var HostCurrentPromptCapacityError = class extends Error {
3522
- report_diagnostic;
3523
- constructor(reportDiagnostic) {
3524
- super("current_prompt_unavailable");
3525
- this.name = "HostCurrentPromptCapacityError";
3526
- this.report_diagnostic = reportDiagnostic;
3527
- }
3528
- };
3529
- var HostCurrentPromptCustody = class {
3530
- #records = /* @__PURE__ */ new Map();
3531
- #consumed = /* @__PURE__ */ new Map();
3532
- #now;
3533
- #capacityDiagnosticReported = false;
3534
- constructor(now = Date.now) {
3535
- this.#now = now;
3536
- }
3537
- register(identity, prompt, expiresAt) {
3538
- const bytes2 = Buffer.byteLength(prompt, "utf8");
3539
- const expiry = Date.parse(expiresAt);
3540
- if (bytes2 === 0 || bytes2 > HOST_CURRENT_PROMPT_MAX_BYTES || !Number.isFinite(expiry) || expiry <= this.#now()) throw new TypeError("current_prompt_unavailable");
3541
- if (hostProducerPromptRef(createHash5("sha256").update(prompt, "utf8").digest("hex")) !== identity.prompt_ref) throw new TypeError("current_prompt_unavailable");
3542
- this.#purge();
3543
- const key = promptKey(identity);
3544
- if (this.#consumed.has(key)) return;
3545
- if (!this.#records.has(key) && this.#records.size >= MAX_CURRENT_PROMPTS) {
3546
- const reportDiagnostic = !this.#capacityDiagnosticReported;
3547
- this.#capacityDiagnosticReported = true;
3548
- throw new HostCurrentPromptCapacityError(reportDiagnostic);
3549
- }
3550
- this.#records.set(key, { prompt, expires_at_ms: expiry });
3551
- }
3552
- /** Transfer this turn's prompt once while its bounded recent replay guard retains the identity. */
3553
- take(identity) {
3554
- this.#purge();
3555
- const key = promptKey(identity);
3556
- const prompt = this.#records.get(key)?.prompt;
3557
- if (prompt !== void 0) {
3558
- const expiresAt = this.#records.get(key)?.expires_at_ms;
3559
- this.#records.delete(key);
3560
- if (expiresAt !== void 0) this.#rememberConsumed(key, expiresAt);
3561
- this.#resetCapacityDiagnosticIfAvailable();
3562
- }
3563
- return prompt;
3564
- }
3565
- /** Release a prompt after its turn ends while retaining the same bounded replay guard. */
3566
- release(identity) {
3567
- this.#purge();
3568
- const key = promptKey(identity);
3569
- const record6 = this.#records.get(key);
3570
- if (record6 === void 0) return false;
3571
- this.#records.delete(key);
3572
- this.#rememberConsumed(key, record6.expires_at_ms);
3573
- this.#resetCapacityDiagnosticIfAvailable();
3574
- return true;
3575
- }
3576
- #rememberConsumed(key, expiresAt) {
3577
- this.#consumed.set(key, expiresAt);
3578
- while (this.#consumed.size > MAX_CONSUMED_PROMPTS) {
3579
- const oldest = this.#consumed.keys().next().value;
3580
- if (oldest === void 0) return;
3581
- this.#consumed.delete(oldest);
3582
- }
3583
- }
3584
- #purge() {
3585
- const now = this.#now();
3586
- for (const [key, value] of this.#records) if (value.expires_at_ms <= now) this.#records.delete(key);
3587
- for (const [key, expiresAt] of this.#consumed) if (expiresAt <= now) this.#consumed.delete(key);
3588
- this.#resetCapacityDiagnosticIfAvailable();
3589
- }
3590
- #resetCapacityDiagnosticIfAvailable() {
3591
- if (this.#records.size < MAX_CURRENT_PROMPTS) this.#capacityDiagnosticReported = false;
3592
- }
3593
- };
3594
- function promptKey(value) {
3595
- return [value.producer_id, value.producer_installation_ref, value.local_session_ref, value.local_turn_ref, value.prompt_ref].join("\0");
3596
- }
3597
-
3598
- // host/relay/src/relay-ipc.ts
3599
- var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
3600
- "schema_version",
3601
- "producer_id",
3602
- "producer_installation_ref",
3603
- "local_session_ref",
3604
- "local_turn_ref",
3605
- "local_turn_aliases",
3606
- "prompt_ref",
3607
- "request_ref",
3608
- "bound_at",
3609
- "host_source_ref",
3610
- "host_session_ref",
3611
- "host_turn_ref",
3612
- "host_input_ref"
3613
- ]);
3434
+ import { dirname as dirname5, join as join5 } from "node:path";
3614
3435
  var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
3615
- var HOST_RELAY_BINDING_QUERY_SCHEMA = "ceal.host_relay_binding_query.v1";
3616
- var HOST_RELAY_BINDING_RESULT_SCHEMA = "ceal.host_relay_binding_result.v1";
3617
- var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v1";
3618
- var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v1";
3619
- var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
3620
- var HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA = "ceal.host_relay_current_prompt_release.v1";
3621
- var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
3436
+ var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v2";
3437
+ var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v2";
3622
3438
  var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
3623
- var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
3624
- "context_stale",
3625
- "context_expired",
3626
- "host_source_refused",
3627
- "host_credential_refused",
3628
- "gateway_unavailable",
3629
- "gateway_refused",
3630
- "gateway_response_invalid",
3631
- "gateway_rate_limited",
3632
- "producer_frame_invalid",
3633
- "deadline",
3634
- "cancelled",
3635
- "mint_failed",
3636
- "relay_failed",
3637
- "request_authority_unavailable",
3638
- "request_missing",
3639
- "authority_mismatch",
3640
- "host_source_mismatch",
3641
- "host_session_mismatch",
3642
- "host_turn_mismatch",
3643
- "host_input_mismatch",
3644
- "host_prompt_mismatch"
3645
- ]);
3646
- var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
3647
- "context_stale",
3648
- "context_expired",
3649
- "host_source_refused",
3650
- "host_credential_refused",
3651
- "gateway_unavailable",
3652
- "gateway_rate_limited",
3653
- "gateway_refused",
3654
- "gateway_response_invalid",
3655
- "producer_frame_invalid"
3656
- ]);
3657
- var HostRelayInvocationMintRefusal = class extends Error {
3658
- reason;
3659
- constructor(reason) {
3660
- super(reason);
3661
- this.name = "HostRelayInvocationMintRefusal";
3662
- this.reason = reason;
3663
- }
3664
- };
3665
- var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
3666
3439
  var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
3667
3440
  "permission_denied",
3668
3441
  "socket_missing",
@@ -3685,26 +3458,14 @@ var HostRelayIpcError = class extends Error {
3685
3458
  };
3686
3459
  async function startHostRelayIpcServer(options) {
3687
3460
  return withRelayEndpointLock(options.endpoint, async () => {
3688
- if (process.platform === "win32") {
3689
- await assertEndpointVacant(options.endpoint);
3690
- } else {
3691
- await assertTrustedEndpointParent(options.endpoint);
3692
- await removeStaleRelayEndpoint(options.endpoint, options.testHooks);
3693
- }
3461
+ const lastSeenRequestBySession = /* @__PURE__ */ new Map();
3462
+ if (process.platform === "win32") return startWindowsPipeServer(options, lastSeenRequestBySession);
3463
+ await assertTrustedEndpointParent(options.endpoint);
3464
+ await removeStaleRelayEndpoint(options.endpoint, options.testHooks);
3694
3465
  const active = /* @__PURE__ */ new Map();
3695
3466
  const sockets = /* @__PURE__ */ new Set();
3696
3467
  const quarantine = options.quarantine ?? options.admission.quarantine;
3697
- const currentPrompts = options.currentPrompts ?? new HostCurrentPromptCustody();
3698
- const server = createServer((socket) => serveSocket(socket, options, currentPrompts, quarantine, active, sockets));
3699
- if (process.platform === "win32") {
3700
- try {
3701
- await listen(server, options.endpoint);
3702
- } catch (error) {
3703
- await abandonRelayServer(server, sockets);
3704
- throw error;
3705
- }
3706
- return { endpoint: options.endpoint, close: () => closeServer(server, sockets, options.endpoint) };
3707
- }
3468
+ const server = createServer((socket) => serveSocket(socket, options, quarantine, active, sockets, Buffer.alloc(0), lastSeenRequestBySession));
3708
3469
  const staging = stagingRelayEndpoint(options.endpoint);
3709
3470
  await listenStagingRelayEndpoint(server, sockets, staging);
3710
3471
  let owned;
@@ -3728,39 +3489,105 @@ function encodeHostRelayIpcResponse(response2) {
3728
3489
  body.copy(result2, HOST_RELAY_IPC_LENGTH_BYTES);
3729
3490
  return result2;
3730
3491
  }
3731
- function serveSocket(socket, options, currentPrompts, quarantine, active, sockets) {
3732
- const state = { buffer: Buffer.alloc(0), closed: false, tail: Promise.resolve(), abort: new AbortController() };
3733
- sockets.add(socket);
3734
- socket.on("data", (chunk) => {
3735
- state.buffer = Buffer.concat([state.buffer, Buffer.from(chunk)]);
3736
- state.tail = state.tail.then(() => drainFrames(socket, state, options, currentPrompts, quarantine, active)).catch(() => {
3737
- writeResponse(socket, errorResponse("relay_unavailable"));
3738
- socket.end();
3492
+ async function startWindowsPipeServer(options, lastSeenRequestBySession) {
3493
+ if (options.pipeKey === void 0) throw new HostRelayIpcError("relay_unavailable", "Host Relay pipe key is unavailable; refusing to serve Windows pipe clients it cannot admit.");
3494
+ const active = /* @__PURE__ */ new Map();
3495
+ const sockets = /* @__PURE__ */ new Set();
3496
+ const quarantine = options.quarantine ?? options.admission.quarantine;
3497
+ const pipeKey = options.pipeKey;
3498
+ const server = createServer((socket) => {
3499
+ void admitWindowsPipeSocket(socket, options, quarantine, active, sockets, pipeKey, lastSeenRequestBySession);
3500
+ });
3501
+ try {
3502
+ await listen(server, options.endpoint);
3503
+ } catch (error) {
3504
+ await abandonRelayServer(server, sockets);
3505
+ throw await resolveWindowsPipeListenOutcome(options.endpoint, error);
3506
+ }
3507
+ return { endpoint: options.endpoint, close: () => closeServer(server, sockets, options.endpoint) };
3508
+ }
3509
+ async function admitWindowsPipeSocket(socket, options, quarantine, active, sockets, pipeKey, lastSeenRequestBySession) {
3510
+ let leftover;
3511
+ try {
3512
+ leftover = await conductPipeServerHandshake(socket, pipeKey, RELAY_PIPE_HANDSHAKE_TIMEOUT_MS);
3513
+ } catch {
3514
+ socket.destroy();
3515
+ return;
3516
+ }
3517
+ if (socket.destroyed) return;
3518
+ serveSocket(socket, options, quarantine, active, sockets, leftover, lastSeenRequestBySession);
3519
+ }
3520
+ var HostRelayAlreadyRunningError = class extends Error {
3521
+ constructor(endpoint) {
3522
+ super(`another Host Relay already serves ${endpoint}; this instance exits without opening a second pipe.`);
3523
+ this.name = "HostRelayAlreadyRunningError";
3524
+ }
3525
+ };
3526
+ async function resolveWindowsPipeListenOutcome(endpoint, error, probe = windowsPipeHolderIsLive) {
3527
+ if (pipeNameHeldError(error) && await probe(endpoint)) return new HostRelayAlreadyRunningError(endpoint);
3528
+ return mapWindowsPipeListenError(endpoint, error);
3529
+ }
3530
+ function pipeNameHeldError(error) {
3531
+ const code = errorCode7(error);
3532
+ return code === "EADDRINUSE" || code === "EACCES";
3533
+ }
3534
+ function windowsPipeHolderIsLive(endpoint) {
3535
+ return new Promise((resolve) => {
3536
+ const timer = setTimeout(() => {
3537
+ probe.destroy();
3538
+ resolve(false);
3539
+ }, RELAY_PIPE_HANDSHAKE_TIMEOUT_MS);
3540
+ const probe = createConnection(endpoint);
3541
+ probe.once("connect", () => {
3542
+ clearTimeout(timer);
3543
+ probe.destroy();
3544
+ resolve(true);
3545
+ });
3546
+ probe.once("error", () => {
3547
+ clearTimeout(timer);
3548
+ probe.destroy();
3549
+ resolve(false);
3739
3550
  });
3740
3551
  });
3552
+ }
3553
+ function mapWindowsPipeListenError(endpoint, error) {
3554
+ const code = errorCode7(error);
3555
+ if (pipeNameHeldError(error)) {
3556
+ return new HostRelayIpcError("relay_unavailable", `relay pipe name held by another process: the Host Relay pipe name ${endpoint} is already served (error ${code}); stop the process holding it, then restart the Host Relay.`);
3557
+ }
3558
+ return error instanceof Error ? error : new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is unavailable.");
3559
+ }
3560
+ function serveSocket(socket, options, quarantine, active, sockets, initial = Buffer.alloc(0), lastSeenRequestBySession = /* @__PURE__ */ new Map()) {
3561
+ const state = { buffer: initial, closed: false, tail: Promise.resolve(), abort: new AbortController(), last_seen_request_by_session: lastSeenRequestBySession };
3562
+ sockets.add(socket);
3563
+ socket.on("data", (chunk) => receiveRelaySocketBytes(socket, state, options, quarantine, active, Buffer.from(chunk)));
3741
3564
  socket.once("error", () => closeConnection(socket, state, active, sockets));
3742
3565
  socket.once("close", () => closeConnection(socket, state, active, sockets));
3743
3566
  socket.once("end", () => {
3744
- if (state.buffer.length > 0 && isCurrentPromptWire(state.buffer)) {
3745
- writeCurrentPromptResponse(socket, "current_prompt_unavailable");
3746
- socket.end();
3747
- } else if (state.buffer.length > 0) void quarantine.retainMalformed(state.buffer, "frame_truncated").finally(() => {
3567
+ if (state.buffer.length > 0) void quarantine.retainMalformed(state.buffer, "frame_truncated").finally(() => {
3748
3568
  writeResponse(socket, errorResponse("frame_truncated"));
3749
3569
  socket.end();
3750
3570
  });
3751
3571
  else socket.end();
3752
3572
  });
3573
+ if (initial.length > 0) receiveRelaySocketBytes(socket, state, options, quarantine, active, Buffer.alloc(0));
3574
+ }
3575
+ function receiveRelaySocketBytes(socket, state, options, quarantine, active, chunk) {
3576
+ state.buffer = Buffer.concat([state.buffer, chunk]);
3577
+ state.tail = state.tail.then(() => drainFrames(socket, state, options, quarantine, active)).catch(() => {
3578
+ writeResponse(socket, errorResponse("relay_unavailable"));
3579
+ socket.end();
3580
+ });
3753
3581
  }
3754
- async function drainFrames(socket, state, options, currentPrompts, quarantine, active) {
3755
- while (!state.closed && state.buffer.length >= HOST_RELAY_IPC_LENGTH_BYTES && await processBufferedRequest(socket, state, options, currentPrompts, quarantine, active)) {
3582
+ async function drainFrames(socket, state, options, quarantine, active) {
3583
+ while (!state.closed && state.buffer.length >= HOST_RELAY_IPC_LENGTH_BYTES && await processBufferedRequest(socket, state, options, quarantine, active)) {
3756
3584
  }
3757
3585
  }
3758
- async function processBufferedRequest(socket, state, options, currentPrompts, quarantine, active) {
3586
+ async function processBufferedRequest(socket, state, options, quarantine, active) {
3759
3587
  const length = state.buffer.readUInt32BE(0);
3760
- const promptWire = isCurrentPromptWire(state.buffer);
3761
- const maximum = promptWire ? HOST_CURRENT_PROMPT_MAX_BYTES + 4096 : HOST_PRODUCER_MAX_JSON_BYTES;
3588
+ const maximum = HOST_PRODUCER_MAX_JSON_BYTES;
3762
3589
  if (length === 0 || length > maximum) {
3763
- await refuseInvalidBufferedRequest(socket, state.buffer, length, promptWire, quarantine);
3590
+ await refuseInvalidBufferedRequest(socket, state.buffer, length, quarantine);
3764
3591
  state.buffer = Buffer.alloc(0);
3765
3592
  socket.end();
3766
3593
  return false;
@@ -3772,52 +3599,20 @@ async function processBufferedRequest(socket, state, options, currentPrompts, qu
3772
3599
  try {
3773
3600
  request2 = decodeIpcRequest(encoded);
3774
3601
  } catch (error) {
3775
- await refuseMalformedRequest(socket, encoded, promptWire, error, quarantine);
3776
- return true;
3777
- }
3778
- if (request2.schema_version === HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA || request2.schema_version === HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA) {
3779
- answerCurrentPromptRequest(socket, request2, currentPrompts, options.diagnostic_sink);
3602
+ await refuseMalformedRequest(socket, encoded, error, quarantine);
3780
3603
  return true;
3781
3604
  }
3782
- const answered = await processHostQuery(socket, request2, options, currentPrompts, state.abort.signal);
3783
- if (answered === "closed") {
3784
- socket.end();
3785
- return false;
3786
- }
3605
+ const answered = await processHostQuery(socket, state, options, request2);
3787
3606
  if (answered === "answered") return true;
3788
3607
  await processProducerRequest(socket, state, request2, encoded, options.admission, quarantine, active);
3789
3608
  return true;
3790
3609
  }
3791
- function answerCurrentPromptRequest(socket, request2, currentPrompts, sink) {
3792
- if (request2.schema_version === HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA) {
3793
- currentPrompts.release(request2);
3794
- writeCurrentPromptResponse(socket, "released");
3795
- return;
3796
- }
3797
- try {
3798
- currentPrompts.register(request2, request2.current_prompt, request2.context_expires_at);
3799
- writeCurrentPromptResponse(socket, "registered");
3800
- } catch (error) {
3801
- if (error instanceof HostCurrentPromptCapacityError) {
3802
- if (error.report_diagnostic) writeHostServiceDiagnostic(request2.producer_id, "current_prompt_capacity_exhausted", void 0, void 0, sink);
3803
- writeCurrentPromptResponse(socket, "capacity_exhausted");
3804
- } else writeCurrentPromptResponse(socket, "current_prompt_unavailable");
3805
- }
3806
- }
3807
- async function refuseInvalidBufferedRequest(socket, rawBuffer, length, promptWire, quarantine) {
3808
- if (promptWire) {
3809
- writeCurrentPromptResponse(socket, "current_prompt_unavailable");
3810
- return;
3811
- }
3610
+ async function refuseInvalidBufferedRequest(socket, rawBuffer, length, quarantine) {
3812
3611
  const code = length === 0 ? "invalid_frame_length" : "frame_too_large";
3813
3612
  await quarantine.retainMalformed(rawBuffer.subarray(0, Math.min(rawBuffer.length, length + HOST_RELAY_IPC_LENGTH_BYTES)), code);
3814
3613
  writeResponse(socket, errorResponse(code));
3815
3614
  }
3816
- async function refuseMalformedRequest(socket, encoded, promptWire, error, quarantine) {
3817
- if (promptWire) {
3818
- writeCurrentPromptResponse(socket, "current_prompt_unavailable");
3819
- return;
3820
- }
3615
+ async function refuseMalformedRequest(socket, encoded, error, quarantine) {
3821
3616
  const code = error instanceof HostProducerFrameError ? error.code : "invalid_frame_shape";
3822
3617
  await quarantine.retainMalformed(encoded, code);
3823
3618
  writeResponse(socket, errorResponse(code));
@@ -3833,29 +3628,19 @@ async function processProducerRequest(socket, state, request2, encoded, admissio
3833
3628
  }
3834
3629
  writeResponse(socket, admissionResponse(await admission.admit(request2)));
3835
3630
  }
3836
- async function processHostQuery(socket, request2, options, currentPrompts, signal) {
3837
- if (request2.schema_version === HOST_RELAY_BINDING_QUERY_SCHEMA) {
3838
- let result2;
3839
- try {
3840
- result2 = await options.bindings.query(bindingQueryFromRequest(request2));
3841
- } catch {
3842
- return "closed";
3843
- }
3844
- writeBindingResponse(socket, result2);
3845
- return "answered";
3846
- }
3847
- if (request2.schema_version === HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) {
3848
- let response2;
3849
- const resolve = (invocationSignal) => resolveInvocationContext(request2, options.admission, options.bindings, currentPrompts, options.mintInvocationContext, invocationSignal);
3850
- try {
3851
- response2 = options.invocationMintTimeoutMs === void 0 && options.scheduleInvocationMintDeadline === void 0 && options.observeInvocationMint === void 0 ? await resolve(signal) : await observeInvocationMint(resolve, signal, options.invocationMintTimeoutMs, options.scheduleInvocationMintDeadline, options.observeInvocationMint);
3852
- } catch {
3853
- response2 = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: "relay_failed" };
3854
- }
3855
- writeInvocationContextResponse(socket, response2);
3856
- return "answered";
3631
+ async function processHostQuery(socket, state, options, request2) {
3632
+ if (request2.schema_version !== HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) return "not_a_query";
3633
+ const sessionKey = `${request2.host_binding.host_source_ref}\0${request2.host_binding.host_session_ref}`;
3634
+ const lastSeen = state.last_seen_request_by_session.get(sessionKey);
3635
+ let response2;
3636
+ try {
3637
+ response2 = options.mintInvocationContext === void 0 ? { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: "gateway_unavailable" } : await options.mintInvocationContext({ ...request2, ...lastSeen === void 0 ? {} : { last_seen_request_ref: lastSeen } }, state.abort.signal);
3638
+ } catch {
3639
+ response2 = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: "gateway_unavailable" };
3857
3640
  }
3858
- return "not_a_query";
3641
+ if (response2.status === "ready") state.last_seen_request_by_session.set(sessionKey, response2.request_ref);
3642
+ writeInvocationContextResponse(socket, response2);
3643
+ return "answered";
3859
3644
  }
3860
3645
  function claimProducer(socket, state, frame, active) {
3861
3646
  const key = producerKey(frame);
@@ -3879,154 +3664,9 @@ function closeConnection(socket, state, active, sockets) {
3879
3664
  function writeResponse(socket, response2) {
3880
3665
  if (!socket.destroyed) socket.write(encodeHostRelayIpcResponse(response2));
3881
3666
  }
3882
- function writeBindingResponse(socket, result2) {
3883
- const response2 = { schema_version: HOST_RELAY_BINDING_RESULT_SCHEMA, ...result2 };
3884
- if (!socket.destroyed) socket.write(encodeLengthPrefixed(response2));
3885
- }
3886
3667
  function writeInvocationContextResponse(socket, response2) {
3887
3668
  if (!socket.destroyed) socket.write(encodeLengthPrefixed(response2));
3888
3669
  }
3889
- function writeCurrentPromptResponse(socket, status) {
3890
- if (!socket.destroyed) socket.write(encodeLengthPrefixed({ schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status }));
3891
- }
3892
- async function resolveInvocationContext(request2, admission, bindings, currentPrompts, mint, signal) {
3893
- if (mint === void 0) throw new HostRelayIpcError("relay_unavailable");
3894
- const query = bindingQueryFromRequest(request2);
3895
- const expectedSequences = pendingAdmissionSequences(await admission.snapshot(), query);
3896
- const binding = await waitForRequestBinding(query, admission, bindings, signal);
3897
- if (binding.status !== "ready") return await unboundInvocationContextRefusal(admission, await admission.snapshot(), query, expectedSequences) ?? { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "no_binding" };
3898
- const currentPrompt = currentPrompts.take({ producer_id: request2.producer_id, producer_installation_ref: request2.producer_installation_ref, local_session_ref: request2.local_session_ref, local_turn_ref: request2.local_turn_ref, prompt_ref: binding.binding.prompt_ref });
3899
- let result2;
3900
- try {
3901
- result2 = await mint({ client_nonce: request2.client_nonce, context_expires_at: binding.binding.context_expires_at ?? binding.binding.bound_at, request_work_context: relayRequestWorkContextFromBinding(binding.binding), ...currentPrompt === void 0 ? {} : { current_prompt: currentPrompt }, ...request2.visible_output_max_bytes === void 0 ? {} : { visible_output_max_bytes: request2.visible_output_max_bytes } }, signal);
3902
- } catch (error) {
3903
- return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: error instanceof HostRelayInvocationMintRefusal ? error.reason : "mint_failed" };
3904
- }
3905
- return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "ready", result: result2 };
3906
- }
3907
- async function unboundInvocationContextRefusal(admission, snapshot, query, expectedSequences) {
3908
- if (expectedSequences.length > 0) {
3909
- const refusal = snapshot.installations[`${query.producer_id}\0${query.producer_installation_ref}`]?.last_refusal;
3910
- if (refusal !== void 0 && expectedSequences.includes(refusal.sequence)) {
3911
- const reason2 = mintReasonForDeliveryRefusal(refusal.code, refusal.diagnostic);
3912
- if (reason2 !== void 0) return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: reason2 };
3913
- }
3914
- }
3915
- const selector = Object.freeze({
3916
- producer_id: query.producer_id,
3917
- producer_installation_ref: query.producer_installation_ref,
3918
- local_session_ref: query.local_session_ref,
3919
- local_turn_ref: query.local_turn_ref,
3920
- local_turn_aliases: Object.freeze([])
3921
- });
3922
- const match = await admission.quarantine.findNewestAttributedMatch(selector);
3923
- if (match === null) return null;
3924
- const reason = mintReasonForQuarantinedMatch(match);
3925
- if (reason === void 0) return null;
3926
- return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason };
3927
- }
3928
- function mintReasonForDeliveryRefusal(code, diagnostic) {
3929
- if (code !== "gateway_refused") return void 0;
3930
- if (diagnostic === void 0) return "gateway_refused";
3931
- return mintReasonForWireCause(projectCealHostAuthenticationCause(diagnostic));
3932
- }
3933
- function mintReasonForQuarantinedMatch(match) {
3934
- if (match === null) return void 0;
3935
- if (match.reason !== "gateway_refused") return "producer_frame_invalid";
3936
- const diagnostic = match.attribution?.diagnostic;
3937
- if (diagnostic === void 0) return void 0;
3938
- return mintReasonForWireCause(projectCealHostAuthenticationCause(diagnostic));
3939
- }
3940
- function mintReasonForWireCause(cause) {
3941
- if (isDirectWireCause(cause)) return cause;
3942
- if (cause === "relay_failed" || cause === "relay_unavailable" || cause === "options_invalid") return "relay_failed";
3943
- return "mint_failed";
3944
- }
3945
- function isDirectWireCause(cause) {
3946
- return isVocabularyWord(HOST_RELAY_DIRECT_WIRE_MINT_REASONS, cause);
3947
- }
3948
- function pendingAdmissionSequences(snapshot, query) {
3949
- const sequences = [];
3950
- for (const entry2 of snapshot.entries) {
3951
- const selector = extractHostBindingSelector(entry2.frame);
3952
- if (selector === null || !relayRequestBindingMatchesQuery(query, selector)) continue;
3953
- sequences.push(entry2.sequence);
3954
- }
3955
- return sequences;
3956
- }
3957
- async function waitForRequestBinding(query, admission, bindings, signal) {
3958
- let binding = await bindings.query(query);
3959
- if (binding.status === "ready") return binding;
3960
- while (await admission.hasPendingRequestBinding(query)) {
3961
- const cycle = new AbortController();
3962
- const cancelCycle = () => cycle.abort(signal.reason);
3963
- if (signal.aborted) cancelCycle();
3964
- else signal.addEventListener("abort", cancelCycle, { once: true });
3965
- try {
3966
- const changed = Promise.race([
3967
- bindings.waitForBinding(query, cycle.signal).then((result3) => ({ kind: "bound", result: result3 })),
3968
- admission.waitForDrainChange(cycle.signal).then(() => ({ kind: "drained" }))
3969
- ]).then((result3) => result3, (error) => ({ kind: "cancelled", error }));
3970
- binding = await bindings.query(query);
3971
- if (binding.status === "ready") return binding;
3972
- if (!await admission.hasPendingRequestBinding(query)) return await bindings.query(query);
3973
- const result2 = await changed;
3974
- if (result2.kind === "cancelled") throw result2.error;
3975
- } finally {
3976
- signal.removeEventListener("abort", cancelCycle);
3977
- cycle.abort("wait_cycle_complete");
3978
- }
3979
- binding = await bindings.query(query);
3980
- if (binding.status === "ready") return binding;
3981
- }
3982
- return await bindings.query(query);
3983
- }
3984
- async function observeInvocationMint(resolve, callerSignal, timeoutMs, scheduleDeadline, observe) {
3985
- const started = process.hrtime.bigint();
3986
- const controller2 = new AbortController();
3987
- let cancellation = null;
3988
- let outcome = "failed";
3989
- let reason;
3990
- const cancel = (cause) => {
3991
- if (cancellation === null) {
3992
- cancellation = cause;
3993
- controller2.abort(cause);
3994
- }
3995
- };
3996
- const callerCancelled = () => cancel("caller_cancelled");
3997
- const cancelled = new Promise((_resolve, reject) => controller2.signal.addEventListener("abort", () => reject(new Error("invocation mint cancelled")), { once: true }));
3998
- const clearDeadline = timeoutMs === void 0 ? () => void 0 : (scheduleDeadline ?? scheduleInvocationMintDeadline)(() => cancel("deadline"), timeoutMs);
3999
- if (callerSignal.aborted) callerCancelled();
4000
- else callerSignal.addEventListener("abort", callerCancelled, { once: true });
4001
- try {
4002
- const response2 = await Promise.race([resolve(controller2.signal), cancelled]);
4003
- ({ outcome, reason } = invocationMintObservationForResponse(response2));
4004
- return response2;
4005
- } catch {
4006
- ({ outcome, reason } = invocationMintObservationForCancellation(cancellation));
4007
- return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: reason ?? "relay_failed" };
4008
- } finally {
4009
- clearDeadline();
4010
- callerSignal.removeEventListener("abort", callerCancelled);
4011
- observe?.({ outcome, elapsed_ms: Number((process.hrtime.bigint() - started) / 1000000n), ...reason === void 0 ? {} : { reason } });
4012
- }
4013
- }
4014
- function invocationMintObservationForResponse(response2) {
4015
- if (response2.status === "no_binding") return { outcome: "no_binding" };
4016
- if (response2.status === "ready") return { outcome: "completed" };
4017
- if (response2.reason === "deadline") return { outcome: "deadline", reason: "deadline" };
4018
- if (response2.reason === "cancelled") return { outcome: "caller_cancelled", reason: "cancelled" };
4019
- return { outcome: "failed", reason: response2.reason };
4020
- }
4021
- function invocationMintObservationForCancellation(cancellation) {
4022
- if (cancellation === "deadline") return { outcome: "deadline", reason: "deadline" };
4023
- if (cancellation === "caller_cancelled") return { outcome: "caller_cancelled", reason: "cancelled" };
4024
- return { outcome: "failed", reason: "relay_failed" };
4025
- }
4026
- function scheduleInvocationMintDeadline(expire, milliseconds) {
4027
- const timeout = setTimeout(expire, milliseconds);
4028
- return () => clearTimeout(timeout);
4029
- }
4030
3670
  function admissionResponse(result2) {
4031
3671
  return { schema_version: HOST_RELAY_IPC_RESPONSE_SCHEMA, ...result2 };
4032
3672
  }
@@ -4036,52 +3676,45 @@ function errorResponse(code) {
4036
3676
  function producerKey(frame) {
4037
3677
  return `${frame.producer_id}\0${frame.producer_installation_ref}`;
4038
3678
  }
4039
- function record4(value) {
3679
+ function record3(value) {
4040
3680
  return value !== null && typeof value === "object" && !Array.isArray(value);
4041
3681
  }
4042
3682
  function decodeIpcRequest(input) {
4043
- const value = decodeLengthPrefixedJson(input, Math.max(HOST_PRODUCER_MAX_JSON_BYTES, HOST_CURRENT_PROMPT_MAX_BYTES + 4096));
3683
+ const value = decodeLengthPrefixedJson(input, HOST_PRODUCER_MAX_JSON_BYTES);
4044
3684
  const local = decodeLocalRequest(value);
4045
3685
  if (local !== void 0) return local;
4046
3686
  return decodeHostProducerFrame(input);
4047
3687
  }
4048
3688
  function decodeLocalRequest(value) {
4049
- if (!record4(value)) return void 0;
4050
- if (value.schema_version === HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA) {
4051
- if (!validCurrentPromptRegisterRequest(value)) throw new HostProducerFrameError("invalid_frame_shape");
4052
- return value;
4053
- }
4054
- if (value.schema_version === HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA) {
4055
- if (!validCurrentPromptReleaseRequest(value)) throw new HostProducerFrameError("invalid_frame_shape");
4056
- return value;
4057
- }
4058
- if (value.schema_version === HOST_RELAY_BINDING_QUERY_SCHEMA) {
4059
- if (!validBindingQueryRequest(value)) throw new HostProducerFrameError("invalid_frame_shape");
4060
- return value;
4061
- }
3689
+ if (!record3(value)) return void 0;
4062
3690
  if (value.schema_version === HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) {
4063
3691
  if (!validInvocationContextQueryRequest(value)) throw new HostProducerFrameError("invalid_frame_shape");
4064
3692
  return value;
4065
3693
  }
4066
3694
  return void 0;
4067
3695
  }
4068
- function validCurrentPromptRegisterRequest(value) {
4069
- return validQueryIdentity(value, HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA) && exactKeys4(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "prompt_ref", "current_prompt", "context_expires_at"]) && safeRef3(value.prompt_ref) && typeof value.current_prompt === "string" && Buffer.byteLength(value.current_prompt, "utf8") > 0 && Buffer.byteLength(value.current_prompt, "utf8") <= HOST_CURRENT_PROMPT_MAX_BYTES && validTimestamp2(value.context_expires_at);
4070
- }
4071
- function validCurrentPromptReleaseRequest(value) {
4072
- return validQueryIdentity(value, HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA) && exactKeys4(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "prompt_ref"]) && safeRef3(value.prompt_ref);
4073
- }
4074
- function validBindingQueryRequest(value) {
4075
- return validQueryIdentity(value, HOST_RELAY_BINDING_QUERY_SCHEMA) && exactKeys4(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref"]);
4076
- }
4077
- function bindingQueryFromRequest(value) {
4078
- return { producer_id: value.producer_id, producer_installation_ref: value.producer_installation_ref, local_session_ref: value.local_session_ref, local_turn_ref: value.local_turn_ref };
4079
- }
4080
3696
  function validInvocationContextQueryRequest(value) {
4081
- return validQueryIdentity(value, HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) && exactKeys4(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));
3697
+ if (!validQueryEnvelope(value)) return false;
3698
+ try {
3699
+ decodeCealHostInvocationContextRequest({
3700
+ schema_version: CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA,
3701
+ client_nonce: value.client_nonce,
3702
+ context_expires_at: value.context_expires_at,
3703
+ host_binding: value.host_binding,
3704
+ human_message_window: value.human_message_window,
3705
+ ...value.visible_output_max_bytes === void 0 ? {} : { visible_output_max_bytes: value.visible_output_max_bytes }
3706
+ });
3707
+ return true;
3708
+ } catch {
3709
+ return false;
3710
+ }
3711
+ }
3712
+ function validQueryEnvelope(value) {
3713
+ if (!validQueryIdentity(value, HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) || !exactKeys3(value, ["schema_version", "producer_id", "producer_installation_ref", "client_nonce", "context_expires_at", "host_binding", "human_message_window"], ["visible_output_max_bytes"])) return false;
3714
+ 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));
4082
3715
  }
4083
3716
  function validQueryIdentity(value, schema) {
4084
- return record4(value) && value.schema_version === schema && (value.producer_id === "codex" || value.producer_id === "claude") && safeRef3(value.producer_installation_ref) && safeRef3(value.local_session_ref) && safeRef3(value.local_turn_ref);
3717
+ return record3(value) && value.schema_version === schema && (value.producer_id === "codex" || value.producer_id === "claude") && safeRef2(value.producer_installation_ref);
4085
3718
  }
4086
3719
  function validVisibleOutputBudget(value) {
4087
3720
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
@@ -4105,33 +3738,13 @@ function encodeLengthPrefixed(value) {
4105
3738
  body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
4106
3739
  return output;
4107
3740
  }
4108
- function isCurrentPromptWire(value) {
4109
- return currentPromptRegisterWire(value) || currentPromptReleaseWire(value);
4110
- }
4111
- function currentPromptRegisterWire(value) {
4112
- return value.subarray(HOST_RELAY_IPC_LENGTH_BYTES, Math.min(value.length, HOST_RELAY_IPC_LENGTH_BYTES + 128)).includes(Buffer.from(`"schema_version":"${HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA}"`, "utf8"));
4113
- }
4114
- function currentPromptReleaseWire(value) {
4115
- return value.subarray(HOST_RELAY_IPC_LENGTH_BYTES, Math.min(value.length, HOST_RELAY_IPC_LENGTH_BYTES + 128)).includes(Buffer.from(`"schema_version":"${HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA}"`, "utf8"));
4116
- }
4117
- function exactKeys4(value, required, optional = []) {
3741
+ function exactKeys3(value, required, optional = []) {
4118
3742
  const keys = Object.keys(value);
4119
3743
  return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
4120
3744
  }
4121
- function safeRef3(value) {
3745
+ function safeRef2(value) {
4122
3746
  return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
4123
3747
  }
4124
- function validTimestamp2(value) {
4125
- return typeof value === "string" && Number.isFinite(Date.parse(value));
4126
- }
4127
- async function assertEndpointVacant(endpoint) {
4128
- try {
4129
- await lstat3(endpoint);
4130
- throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is already present.");
4131
- } catch (error) {
4132
- if (errorCode8(error) !== "ENOENT") throw error;
4133
- }
4134
- }
4135
3748
  function listen(server, endpoint) {
4136
3749
  return new Promise((resolve, reject) => {
4137
3750
  server.once("error", reject);
@@ -4144,7 +3757,7 @@ function listen(server, endpoint) {
4144
3757
  async function closeServer(server, sockets, endpoint) {
4145
3758
  for (const socket of sockets) socket.destroy();
4146
3759
  await new Promise((resolve) => server.close(() => resolve()));
4147
- if (process.platform !== "win32") await unlink2(endpoint).catch(() => void 0);
3760
+ if (process.platform !== "win32") await unlink3(endpoint).catch(() => void 0);
4148
3761
  }
4149
3762
  var relayEndpointLocks = /* @__PURE__ */ new Map();
4150
3763
  function withRelayEndpointLock(endpoint, run) {
@@ -4164,7 +3777,7 @@ function currentUid(fallback) {
4164
3777
  return typeof process.geteuid === "function" ? process.geteuid() : fallback;
4165
3778
  }
4166
3779
  async function assertTrustedEndpointParent(endpoint) {
4167
- const parent = dirname3(endpoint);
3780
+ const parent = dirname5(endpoint);
4168
3781
  let stats;
4169
3782
  try {
4170
3783
  stats = await lstat3(parent);
@@ -4189,7 +3802,7 @@ function probeRelayEndpoint(endpoint) {
4189
3802
  timer.unref?.();
4190
3803
  socket.once("connect", () => finish("live"));
4191
3804
  socket.once("error", (error) => {
4192
- const code = errorCode8(error);
3805
+ const code = errorCode7(error);
4193
3806
  if (code === "ECONNREFUSED") finish("stale");
4194
3807
  else if (code === "ENOENT") finish("missing");
4195
3808
  else finish("ambiguous");
@@ -4224,7 +3837,7 @@ async function readEndpointStats(endpoint) {
4224
3837
  try {
4225
3838
  return await lstat3(endpoint);
4226
3839
  } catch (error) {
4227
- if (errorCode8(error) === "ENOENT") return null;
3840
+ if (errorCode7(error) === "ENOENT") return null;
4228
3841
  throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint identity is ambiguous; leaving it untouched.");
4229
3842
  }
4230
3843
  }
@@ -4233,17 +3846,17 @@ function requireSameEndpointIdentity(current, owned, message) {
4233
3846
  }
4234
3847
  async function unlinkRelayEndpoint(endpoint) {
4235
3848
  try {
4236
- await unlink2(endpoint);
3849
+ await unlink3(endpoint);
4237
3850
  } catch (error) {
4238
- if (errorCode8(error) !== "ENOENT") throw error;
3851
+ if (errorCode7(error) !== "ENOENT") throw error;
4239
3852
  }
4240
3853
  }
4241
3854
  var relayStagingSequence = 0;
4242
3855
  function stagingRelayEndpoint(canonical) {
4243
- const parent = dirname3(canonical);
3856
+ const parent = dirname5(canonical);
4244
3857
  const pid = process.pid.toString(36);
4245
3858
  const sequence = (relayStagingSequence++ % (36 * 36)).toString(36);
4246
- return join6(parent, `.r${pid}${sequence}`);
3859
+ return join5(parent, `.r${pid}${sequence}`);
4247
3860
  }
4248
3861
  async function readStagingRelayEndpointIdentity(staging) {
4249
3862
  let created;
@@ -4300,8 +3913,121 @@ async function abandonRelayServer(server, sockets) {
4300
3913
  server.close(() => resolve());
4301
3914
  });
4302
3915
  }
4303
- function errorCode8(error) {
4304
- return record4(error) && typeof error.code === "string" ? error.code : null;
3916
+ function errorCode7(error) {
3917
+ return record3(error) && typeof error.code === "string" ? error.code : null;
3918
+ }
3919
+
3920
+ // host/relay/src/host-registration-state.ts
3921
+ import { constants as constants3 } from "node:fs";
3922
+ import { dirname as dirname6 } from "node:path";
3923
+ var HOST_REGISTRATION_STATE_SCHEMA = "ceal.host_registration_state.v1";
3924
+ var HOST_REGISTRATION_STATE_KEYS = Object.freeze([
3925
+ "credential_generation",
3926
+ "gateway_request_origin",
3927
+ "host_access_token",
3928
+ "host_installation_ref",
3929
+ "host_source_ref",
3930
+ "registration_attempt_ref",
3931
+ "schema_version",
3932
+ "status"
3933
+ ]);
3934
+ var HOST_REGISTRATION_STATE_MAX_BYTES = 65536;
3935
+ var HostRegistrationStateError = class extends Error {
3936
+ name = "HostRegistrationStateError";
3937
+ code = "credential";
3938
+ };
3939
+ async function readHostRegistrationAuthority(path, expectedHostSourceRef, platform = process.platform, custody = {}) {
3940
+ let raw;
3941
+ try {
3942
+ await assertPrivateRegistrationDirectory(dirname6(path), platform, custody);
3943
+ raw = platform === "win32" ? await readWindowsRegistration(path, custody) : await readPosixRegistration(path, platform, custody);
3944
+ } catch (error) {
3945
+ throw registrationReadError(error);
3946
+ }
3947
+ if (Buffer.byteLength(raw, "utf8") > HOST_REGISTRATION_STATE_MAX_BYTES) throw new HostRegistrationStateError(`Host registration state is invalid: ${refusalFact("registration_state_bytes", Buffer.byteLength(raw, "utf8"), `at most ${HOST_REGISTRATION_STATE_MAX_BYTES}`)}.`);
3948
+ let value;
3949
+ try {
3950
+ value = JSON.parse(raw);
3951
+ } catch {
3952
+ throw new HostRegistrationStateError("Host registration state is invalid.");
3953
+ }
3954
+ return decodeHostRegistrationAuthority(value, expectedHostSourceRef);
3955
+ }
3956
+ async function readWindowsRegistration(path, custody) {
3957
+ const bytes2 = await readPrivateFileWithSecurefs(path, HOST_REGISTRATION_STATE_MAX_BYTES, custody);
3958
+ try {
3959
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
3960
+ } catch {
3961
+ throw new HostRegistrationStateError("Host registration state is invalid.");
3962
+ }
3963
+ }
3964
+ async function readPosixRegistration(path, platform, custody) {
3965
+ const handle = await openNoFollow(path, constants3.O_RDONLY, void 0, platform, custody);
3966
+ try {
3967
+ const stats = await handle.stat();
3968
+ if (!stats.isFile() || !isSelfOwned(stats.uid, platform, handle) || !isPrivateFileMode(stats.mode, platform, handle)) throw new HostRegistrationStateError("Host registration file is not private to its owner.");
3969
+ return await handle.readFile("utf8");
3970
+ } finally {
3971
+ await handle.close();
3972
+ }
3973
+ }
3974
+ function registrationReadError(error) {
3975
+ if (error instanceof HostRegistrationStateError) return error;
3976
+ if (error instanceof SecureFileCustodyError && error.reason === "not_private") return new HostRegistrationStateError("Host registration file is not private to its owner.");
3977
+ return new HostRegistrationStateError("Host registration file is unavailable.");
3978
+ }
3979
+ function decodeHostRegistrationAuthority(value, expectedHostSourceRef) {
3980
+ const keys = HOST_REGISTRATION_STATE_KEYS;
3981
+ const fieldCount = record4(value) ? Object.keys(value).length : 0;
3982
+ const missing = record4(value) ? keys.filter((key) => !Object.hasOwn(value, key)).length : keys.length;
3983
+ if (!record4(value) || fieldCount !== keys.length || missing !== 0) throw new HostRegistrationStateError(`Host registration state is invalid or does not match this Relay: ${refusalFacts([{ name: "registration_field_count", value: fieldCount, expected: `exactly ${keys.length}` }, { name: "missing_registration_field_count", value: missing, expected: "0" }])}.`);
3984
+ if (!validRegistrationIdentity(value, expectedHostSourceRef)) throw new HostRegistrationStateError(`Host registration state is invalid or does not match this Relay: ${refusalFacts([{ name: "schema_version", value: value.schema_version, expected: HOST_REGISTRATION_STATE_SCHEMA }, { name: "status", value: value.status, expected: "active" }, { name: "host_source_ref", value: value.host_source_ref, expected: expectedHostSourceRef }, { name: "registration_attempt_ref", value: value.registration_attempt_ref, expected: "host-registration-attempt:<uuid>" }, { name: "host_installation_ref", value: value.host_installation_ref, expected: "host-installation:<uuid>" }])}.`);
3985
+ if (!validRegistrationCredential(value)) throw new HostRegistrationStateError(`Host registration state is invalid or does not match this Relay: ${refusalFacts([{ name: "credential_generation", value: value.credential_generation, expected: "a positive safe integer" }, { name: "host_access_token_matches_registered_shape", value: validHostAccessToken(value.host_access_token), expected: "true" }])}.`);
3986
+ if (!validGatewayOrigin(value.gateway_request_origin)) throw new HostRegistrationStateError(`Host registration state is invalid or does not match this Relay: ${refusalFact("gateway_request_origin", value.gateway_request_origin, "an HTTPS origin or explicit-port loopback HTTP origin")}.`);
3987
+ return { gateway_request_origin: value.gateway_request_origin, host_access_token: value.host_access_token };
3988
+ }
3989
+ function validRegistrationIdentity(value, expectedHostSourceRef) {
3990
+ return value.schema_version === HOST_REGISTRATION_STATE_SCHEMA && value.status === "active" && value.host_source_ref === expectedHostSourceRef && typeof value.registration_attempt_ref === "string" && CEAL_HOST_REGISTRATION_ATTEMPT_REF_PATTERN.test(value.registration_attempt_ref) && typeof value.host_installation_ref === "string" && CEAL_HOST_INSTALLATION_REF_PATTERN.test(value.host_installation_ref) && typeof value.host_source_ref === "string" && hostSourceUuid(value.host_source_ref) !== null;
3991
+ }
3992
+ function validRegistrationCredential(value) {
3993
+ return Number.isSafeInteger(value.credential_generation) && Number(value.credential_generation) > 0 && validHostAccessToken(value.host_access_token);
3994
+ }
3995
+ function validHostAccessToken(value) {
3996
+ return typeof value === "string" && CEAL_HOST_ACCESS_TOKEN_PATTERN.test(value);
3997
+ }
3998
+ function validGatewayOrigin(value) {
3999
+ try {
4000
+ if (typeof value !== "string" || /\s|\0/u.test(value)) return false;
4001
+ const parsed = new URL(value);
4002
+ const hostname = parsed.hostname.toLowerCase().replace(/\.+$/u, "");
4003
+ return hostname !== "" && validGatewayPort(parsed.port) && (loopbackHttpOrigin(parsed, hostname) || parsed.protocol === "https:") && cleanOriginPath(parsed) && bareOriginSuffix(value);
4004
+ } catch {
4005
+ return false;
4006
+ }
4007
+ }
4008
+ function loopbackHttpOrigin(parsed, hostname) {
4009
+ return parsed.protocol === "http:" && parsed.port !== "" && (hostname === "127.0.0.1" || hostname === "[::1]");
4010
+ }
4011
+ function validGatewayPort(port) {
4012
+ if (port === "") return true;
4013
+ const numeric = Number(port);
4014
+ return Number.isInteger(numeric) && numeric >= 1 && numeric <= 65535;
4015
+ }
4016
+ function cleanOriginPath(parsed) {
4017
+ return parsed.username === "" && parsed.password === "" && parsed.pathname === "/" && parsed.search === "" && parsed.hash === "";
4018
+ }
4019
+ function bareOriginSuffix(value) {
4020
+ const schemeEnd = value.indexOf("://");
4021
+ const authority = schemeEnd < 0 ? "" : value.slice(schemeEnd + 3);
4022
+ const suffixOffset = authority.search(/[\\/?#]/u);
4023
+ const rawSuffix = suffixOffset < 0 ? "" : authority.slice(suffixOffset);
4024
+ return rawSuffix === "" || rawSuffix === "/";
4025
+ }
4026
+ async function assertPrivateRegistrationDirectory(path, platform, custody) {
4027
+ if (!await isPrivateDirectory(path, platform, custody)) throw new HostRegistrationStateError("Host registration directory is not private to its owner.");
4028
+ }
4029
+ function record4(value) {
4030
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4305
4031
  }
4306
4032
 
4307
4033
  // host/relay/src/host-relay-runtime.ts
@@ -4317,16 +4043,24 @@ var HostRelayConfigError = class extends Error {
4317
4043
  this.code = code;
4318
4044
  }
4319
4045
  };
4320
- async function readHostRelayConfig(path) {
4046
+ async function readHostRelayConfig(path, platform = process.platform, custody = {}) {
4321
4047
  if (!isAbsoluteNormalizedNonRootPath(path, process.platform, true)) throw new HostRelayConfigError("invalid_config_path", `invalid_config_path: Host Relay config path is invalid: ${refusalFact("config_path", path, "absolute, normalized, and non-root")}.`);
4322
4048
  let handle;
4323
4049
  try {
4324
- await assertOwnerPrivateDirectory2(dirname4(path));
4325
- handle = await open5(path, O_RDONLY4 | O_NOFOLLOW3);
4326
- const stats = await handle.stat();
4327
- if (!stats.isFile() || !ownerPrivate4(stats)) throw new HostRelayConfigError("config_not_private", "Host Relay config file is not owner-private.");
4328
- if (stats.size < HOST_RELAY_CONFIG_MIN_BYTES || stats.size > HOST_RELAY_CONFIG_MAX_BYTES) throw new HostRelayConfigError("invalid_config", `invalid_config: Host Relay config size is invalid: ${refusalFact("config_bytes", stats.size, `between ${HOST_RELAY_CONFIG_MIN_BYTES} and ${HOST_RELAY_CONFIG_MAX_BYTES}`)}.`);
4329
- const text = await handle.readFile("utf8");
4050
+ await assertPrivateRelayConfigDirectory(dirname7(path), platform, custody);
4051
+ let text;
4052
+ if (platform === "win32") {
4053
+ const bytes2 = await readPrivateFileWithSecurefs(path, HOST_RELAY_CONFIG_MAX_BYTES, custody);
4054
+ if (bytes2.byteLength < HOST_RELAY_CONFIG_MIN_BYTES) throw new HostRelayConfigError("invalid_config", `invalid_config: Host Relay config size is invalid: ${refusalFact("config_bytes", bytes2.byteLength, `between ${HOST_RELAY_CONFIG_MIN_BYTES} and ${HOST_RELAY_CONFIG_MAX_BYTES}`)}.`);
4055
+ try {
4056
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes2);
4057
+ } catch {
4058
+ throw new HostRelayConfigError("invalid_json", "Host Relay config is not valid UTF-8 JSON.");
4059
+ }
4060
+ } else {
4061
+ handle = await openPrivateRelayConfigFile(path, platform, custody);
4062
+ text = await handle.readFile("utf8");
4063
+ }
4330
4064
  assertNoDuplicateJsonKeys(text);
4331
4065
  let value;
4332
4066
  try {
@@ -4334,114 +4068,236 @@ async function readHostRelayConfig(path) {
4334
4068
  } catch {
4335
4069
  throw new HostRelayConfigError("invalid_json", "Host Relay config is not JSON.");
4336
4070
  }
4337
- const config = decodeHostRelayConfig(value);
4071
+ const decoded = decodeHostRelayConfig(value);
4072
+ const config = resolveRelayIpcEndpoint(decoded);
4338
4073
  validateHostRelaySourcePaths(config);
4339
4074
  return config;
4340
4075
  } catch (error) {
4341
- if (error instanceof HostRelayConfigError) throw error;
4342
- throw new HostRelayConfigError("config_unavailable", "Host Relay config is unavailable.");
4076
+ throw relayConfigReadError(error);
4343
4077
  } finally {
4344
4078
  await handle?.close();
4345
4079
  }
4346
4080
  }
4081
+ async function openPrivateRelayConfigFile(path, platform, custody) {
4082
+ const handle = await openNoFollow(path, constants4.O_RDONLY, void 0, platform, custody);
4083
+ try {
4084
+ const stats = await handle.stat();
4085
+ if (!stats.isFile() || !isSelfOwned(stats.uid, platform, handle) || !isPrivateFileMode(stats.mode, platform, handle)) throw new HostRelayConfigError("config_not_private", "Host Relay config file is not private to its owner.");
4086
+ if (stats.size < HOST_RELAY_CONFIG_MIN_BYTES || stats.size > HOST_RELAY_CONFIG_MAX_BYTES) throw new HostRelayConfigError("invalid_config", `invalid_config: Host Relay config size is invalid: ${refusalFact("config_bytes", stats.size, `between ${HOST_RELAY_CONFIG_MIN_BYTES} and ${HOST_RELAY_CONFIG_MAX_BYTES}`)}.`);
4087
+ return handle;
4088
+ } catch (error) {
4089
+ await handle.close();
4090
+ throw error;
4091
+ }
4092
+ }
4093
+ function relayConfigReadError(error) {
4094
+ if (error instanceof HostRelayConfigError) return error;
4095
+ if (error instanceof SecureFileCustodyError && error.reason === "not_private") return new HostRelayConfigError("config_not_private", "Host Relay config file is not private to its owner.");
4096
+ if (error instanceof SecureFileCustodyError && error.reason === "too_large") return new HostRelayConfigError("invalid_config", `invalid_config: Host Relay config exceeds ${HOST_RELAY_CONFIG_MAX_BYTES} bytes.`);
4097
+ return new HostRelayConfigError("config_unavailable", "Host Relay config is unavailable.");
4098
+ }
4099
+ async function assertPrivateRelayConfigDirectory(path, platform, custody) {
4100
+ try {
4101
+ if (!await isPrivateDirectory(path, platform, custody)) throw new HostRelayConfigError("config_not_private", "Host Relay config directory is not private to its owner.");
4102
+ } catch (error) {
4103
+ if (error instanceof HostRelayConfigError) throw error;
4104
+ throw new HostRelayConfigError("config_unavailable", "Host Relay config directory is unavailable.");
4105
+ }
4106
+ }
4107
+ function resolveRelayIpcEndpoint(decoded) {
4108
+ if (process.platform !== "win32") return decoded;
4109
+ const endpoint = assertHostRelaySourcePaths(dirname7(decoded.registration_state_file), decoded);
4110
+ return endpoint === decoded.ipc_endpoint ? decoded : { ...decoded, ipc_endpoint: endpoint };
4111
+ }
4347
4112
  function validateHostRelaySourcePaths(config) {
4348
4113
  try {
4349
- assertHostRelaySourcePaths(dirname4(config.registration_state_file), config);
4114
+ assertHostRelaySourcePaths(dirname7(config.registration_state_file), config);
4350
4115
  } catch (error) {
4351
4116
  throw new HostRelayConfigError("invalid_config", error instanceof Error ? error.message : "Host Relay source tuple is invalid.");
4352
4117
  }
4353
4118
  }
4354
- function decodeHostRelayConfig(value) {
4355
- const required = ["schema_version", "registration_state_file", "host_source_ref", "state_root", "ipc_endpoint"];
4356
- const optional = ["drain_interval_ms", "gateway_timeout_ms"];
4357
- if (!record5(value) || !exactOptionalKeys2(value, required, optional) || !validConfigIdentity(value) || !validConfigRuntime(value)) {
4358
- throw new HostRelayConfigError("invalid_config", "Host Relay config shape is invalid.");
4119
+ function decodeHostRelayConfig(value, platform = process.platform) {
4120
+ const ignoreEndpoint = platform === "win32";
4121
+ const required = ignoreEndpoint ? ["schema_version", "registration_state_file", "host_source_ref", "state_root"] : ["schema_version", "registration_state_file", "host_source_ref", "state_root", "ipc_endpoint"];
4122
+ const optional = ignoreEndpoint ? ["ipc_endpoint", "drain_interval_ms"] : ["drain_interval_ms"];
4123
+ if (!record5(value) || !exactOptionalKeys2(value, required, optional) || !validConfigIdentity(value, platform) || !validConfigRuntime(value, platform)) {
4124
+ throw new HostRelayConfigError("invalid_config", `Host Relay config shape is invalid: ${refusalFacts(relayShapeFacts(value, required, optional, platform))}.`);
4359
4125
  }
4360
4126
  return {
4361
4127
  schema_version: HOST_RELAY_CONFIG_SCHEMA,
4362
4128
  registration_state_file: value.registration_state_file,
4363
4129
  host_source_ref: value.host_source_ref,
4364
4130
  state_root: value.state_root,
4365
- ipc_endpoint: value.ipc_endpoint,
4366
- drain_interval_ms: value.drain_interval_ms ?? 250,
4367
- gateway_timeout_ms: value.gateway_timeout_ms ?? 5e3
4131
+ ipc_endpoint: typeof value.ipc_endpoint === "string" ? value.ipc_endpoint : "",
4132
+ drain_interval_ms: value.drain_interval_ms ?? 250
4368
4133
  };
4369
4134
  }
4370
- function validConfigIdentity(value) {
4371
- return value.schema_version === HOST_RELAY_CONFIG_SCHEMA && isAbsoluteNormalizedNonRootPath(value.registration_state_file, process.platform, true) && opaqueHostSourceRef(value.host_source_ref);
4135
+ function validConfigIdentity(value, platform) {
4136
+ return value.schema_version === HOST_RELAY_CONFIG_SCHEMA && isAbsoluteNormalizedNonRootPath(value.registration_state_file, platform, true) && opaqueHostSourceRef(value.host_source_ref);
4372
4137
  }
4373
- function validConfigRuntime(value) {
4374
- return isAbsoluteNormalizedNonRootPath(value.state_root, process.platform, true) && isAbsoluteNormalizedNonRootPath(value.ipc_endpoint, process.platform, true) && boundedInteger(value.drain_interval_ms, 50, 3e4, true) && boundedInteger(value.gateway_timeout_ms, 100, 6e4, true);
4138
+ function relayShapeFacts(value, required, optional, platform) {
4139
+ if (!record5(value)) return [{ name: "config", value: "wrong_type", expected: "a record" }];
4140
+ const facts = [
4141
+ ...Object.keys(value).filter((key) => !required.includes(key) && !optional.includes(key)).map((key) => ({ name: key, value: "unknown", expected: `one of ${[...required, ...optional].join(", ")}` })),
4142
+ ...required.filter((key) => !Object.hasOwn(value, key)).map((key) => ({ name: key, value: "missing", expected: "present" }))
4143
+ ];
4144
+ if (Object.hasOwn(value, "schema_version") && value.schema_version !== HOST_RELAY_CONFIG_SCHEMA) facts.push({ name: "schema_version", value: "wrong_type", expected: HOST_RELAY_CONFIG_SCHEMA });
4145
+ for (const fact of relayValueFacts(value, platform)) facts.push(fact);
4146
+ return facts;
4147
+ }
4148
+ function relayValueFacts(value, platform) {
4149
+ const facts = [];
4150
+ for (const key of platform === "win32" ? ["registration_state_file", "state_root"] : ["registration_state_file", "state_root", "ipc_endpoint"]) {
4151
+ if (Object.hasOwn(value, key) && !isAbsoluteNormalizedNonRootPath(value[key], platform, true)) facts.push({ name: key, value: "wrong_type", expected: "an absolute normalized non-root path" });
4152
+ }
4153
+ if (Object.hasOwn(value, "host_source_ref") && !opaqueHostSourceRef(value.host_source_ref)) facts.push({ name: "host_source_ref", value: "wrong_type", expected: "a host source reference" });
4154
+ if (Object.hasOwn(value, "drain_interval_ms") && !boundedInteger(value.drain_interval_ms, 50, 3e4, true)) facts.push({ name: "drain_interval_ms", value: "wrong_type", expected: "between 50 and 30000" });
4155
+ return facts;
4156
+ }
4157
+ function validConfigRuntime(value, platform) {
4158
+ return isAbsoluteNormalizedNonRootPath(value.state_root, platform, true) && (platform === "win32" || isAbsoluteNormalizedNonRootPath(value.ipc_endpoint, platform, true)) && boundedInteger(value.drain_interval_ms, 50, 3e4, true);
4375
4159
  }
4376
4160
  var HOST_RELAY_INVOCATION_MINT_LOG_TAG = "[host-relay-invocation-mint]";
4377
- var HOST_RELAY_INVOCATION_MINT_LINE = new RegExp(`^${HOST_RELAY_INVOCATION_MINT_LOG_TAG.replace(/[[\]]/gu, "\\$&")} outcome=([a-z_]+)(?: reason=([a-z][a-z0-9_]{0,63}))? elapsed_ms=[0-9]+$`, "u");
4161
+ function formatHostRelayInvocationMintLine(result2) {
4162
+ return `${HOST_RELAY_INVOCATION_MINT_LOG_TAG} outcome=${result2.status === "ready" ? "completed" : "failed"}${result2.status === "refused" ? ` reason=${result2.reason}` : ""}`;
4163
+ }
4164
+ function reportInvocationMintLine(line) {
4165
+ try {
4166
+ process.stderr.write(line);
4167
+ } catch {
4168
+ }
4169
+ writeHostServiceLogLine(line);
4170
+ }
4378
4171
  async function serveHostRelay(config, signal, dependencies = {}) {
4379
- const admission = new RelayAdmissionStore(join7(config.state_root, "admission"));
4380
- const bindings = new RelayRequestBindingStore(join7(config.state_root, "bindings"));
4381
- const gateway = (dependencies.createGateway ?? createHostRelayGatewayHttpPort)({ registration_state_file: config.registration_state_file, host_source_ref: config.host_source_ref });
4382
- const bridge = new RelayGatewayBridge({ state_root: join7(config.state_root, "gateway"), host_source_ref: config.host_source_ref, gateway, timeout_ms: config.gateway_timeout_ms, on_request_bound: async (binding) => {
4383
- await bindings.bind(binding);
4384
- } });
4172
+ const admission = new RelayAdmissionStore(join6(config.state_root, "admission"));
4385
4173
  let server;
4386
4174
  try {
4387
- server = await startHostRelayRuntimeIpc(config, admission, bindings, gateway, dependencies);
4388
- while (!signal.aborted) {
4389
- const round = await admission.drainRound((input) => bridge.deliver(input), { now_ms: (dependencies.now ?? Date.now)(), host_source_ref: config.host_source_ref });
4390
- if (round.delivered.length === 0 && round.refused.length === 0) await (dependencies.wait ?? ((milliseconds, waitSignal) => waitForAbort(waitSignal, milliseconds)))(config.drain_interval_ms, signal);
4391
- }
4175
+ const pipeKey = process.platform === "win32" ? await loadRelayPipeKey(config.state_root) : void 0;
4176
+ server = await startHostRelayRuntimeIpc(config, admission, dependencies, pipeKey);
4177
+ await waitForRelayShutdown(config, signal, dependencies);
4178
+ } catch (error) {
4179
+ if (error instanceof HostRelayAlreadyRunningError) return;
4180
+ throw error;
4392
4181
  } finally {
4393
4182
  await server?.close();
4394
4183
  }
4395
4184
  }
4396
- async function startHostRelayRuntimeIpc(config, admission, bindings, gateway, dependencies) {
4185
+ async function waitForRelayShutdown(config, signal, dependencies) {
4186
+ while (!signal.aborted) await (dependencies.wait ?? ((milliseconds, waitSignal) => waitForAbort(waitSignal, milliseconds)))(config.drain_interval_ms, signal);
4187
+ }
4188
+ async function startHostRelayRuntimeIpc(config, admission, dependencies, pipeKey) {
4397
4189
  return (dependencies.startIpc ?? startHostRelayIpcServer)({
4398
4190
  endpoint: config.ipc_endpoint,
4399
4191
  admission,
4400
- bindings,
4401
- mintInvocationContext: (input, invocationSignal) => mintInvocationContext(gateway, input, invocationSignal),
4402
- invocationMintTimeoutMs: config.gateway_timeout_ms,
4403
- scheduleInvocationMintDeadline: dependencies.scheduleInvocationMintDeadline ?? scheduleInvocationMintDeadline2,
4404
- observeInvocationMint: dependencies.observeInvocationMint ?? reportInvocationMint
4192
+ ...pipeKey === void 0 ? {} : { pipeKey },
4193
+ mintInvocationContext: async (input, signal) => {
4194
+ let result2;
4195
+ try {
4196
+ result2 = await mintInvocationContextAtGateway(config, input, signal, dependencies.fetch ?? fetch);
4197
+ } catch (error) {
4198
+ const refused = { schema_version: "ceal.host_relay_invocation_context_result.v2", status: "refused", reason: "gateway_unavailable" };
4199
+ (dependencies.logInvocationMint ?? reportInvocationMintLine)(`${formatHostRelayInvocationMintLine(refused)}
4200
+ `);
4201
+ throw error;
4202
+ }
4203
+ (dependencies.logInvocationMint ?? reportInvocationMintLine)(`${formatHostRelayInvocationMintLine(result2)}
4204
+ `);
4205
+ return result2;
4206
+ }
4405
4207
  });
4406
4208
  }
4407
- async function mintInvocationContext(gateway, input, signal) {
4209
+ async function mintInvocationContextAtGateway(config, input, signal, fetchGateway) {
4408
4210
  try {
4409
- return await gateway.mintInvocationContext(input, signal);
4211
+ const registration = await readHostRegistrationAuthority(config.registration_state_file, config.host_source_ref);
4212
+ return gatewayMintPost(registration, input, signal, fetchGateway);
4410
4213
  } catch (error) {
4411
- const cancellation = signal.reason === "deadline" ? "deadline" : signal.reason === "caller_cancelled" ? "caller_cancelled" : null;
4412
- throw new HostRelayInvocationMintRefusal(invocationMintRefusalReason(error, cancellation));
4214
+ return gatewayMintFailure(signal, error);
4413
4215
  }
4414
4216
  }
4415
- function invocationMintRefusalReason(error, cancellation) {
4416
- if (cancellation === "deadline") return "deadline";
4417
- if (cancellation === "caller_cancelled") return "cancelled";
4418
- if (!(error instanceof HostRelayGatewayError)) return "mint_failed";
4419
- if (error.code === "credential") return "host_credential_refused";
4420
- if (error.code === "gateway_unavailable") return "gateway_unavailable";
4421
- if (error.code === "invalid_response") return "gateway_response_invalid";
4422
- if (error.code !== "gateway_refused") return "mint_failed";
4423
- return gatewayMintRefusalReason(error);
4424
- }
4425
- function gatewayMintRefusalReason(error) {
4426
- if (error.error_code === "request_context_refused" && isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, error.refusal_reason)) return error.refusal_reason;
4427
- return GATEWAY_MINT_REFUSAL_REASONS.get(error.error_code ?? "") ?? "gateway_refused";
4428
- }
4429
- var GATEWAY_MINT_REFUSAL_REASONS = /* @__PURE__ */ new Map([
4430
- ["request_context_refused", "context_stale"],
4431
- ["context_expired", "context_expired"],
4432
- ["host_source_refused", "host_source_refused"],
4433
- ["host_invocation_context_unavailable", "gateway_unavailable"]
4434
- ]);
4435
- function scheduleInvocationMintDeadline2(expire, milliseconds) {
4436
- const timeout = setTimeout(expire, milliseconds);
4437
- return () => clearTimeout(timeout);
4217
+ async function gatewayMintPost(registration, input, signal, fetchGateway) {
4218
+ const serialized = JSON.stringify(gatewayMintBody(input));
4219
+ if (Buffer.byteLength(serialized, "utf8") > CEAL_HOST_INVOCATION_CONTEXT_REQUEST_MAX_BYTES) return mintRefusal("producer_frame_invalid");
4220
+ const response2 = await fetchGateway(`${registration.gateway_request_origin}${CEAL_HOST_INVOCATION_CONTEXT_PATH}`, {
4221
+ method: "POST",
4222
+ headers: { authorization: `Bearer ${registration.host_access_token}`, "content-type": "application/json" },
4223
+ body: serialized,
4224
+ signal
4225
+ });
4226
+ const raw = await readBoundedResponseText(response2, 8192);
4227
+ return decodeGatewayMintResponse(response2, raw);
4438
4228
  }
4439
- function reportInvocationMint(observation) {
4440
- process.stderr.write(`${formatHostRelayInvocationMintLine(observation)}
4441
- `);
4229
+ function gatewayMintBody(input) {
4230
+ return {
4231
+ schema_version: CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA,
4232
+ client_nonce: input.client_nonce,
4233
+ context_expires_at: input.context_expires_at,
4234
+ host_binding: input.host_binding,
4235
+ human_message_window: input.human_message_window,
4236
+ ...input.last_seen_request_ref === void 0 ? {} : { last_seen_request_ref: input.last_seen_request_ref },
4237
+ ...input.visible_output_max_bytes === void 0 ? {} : { visible_output_max_bytes: input.visible_output_max_bytes }
4238
+ };
4442
4239
  }
4443
- function formatHostRelayInvocationMintLine(observation) {
4444
- return `${HOST_RELAY_INVOCATION_MINT_LOG_TAG} outcome=${observation.outcome}${observation.reason === void 0 ? "" : ` reason=${observation.reason}`} elapsed_ms=${observation.elapsed_ms}`;
4240
+ function decodeGatewayMintResponse(response2, raw) {
4241
+ if (raw === null) return mintRefusal("gateway_response_invalid");
4242
+ let value;
4243
+ try {
4244
+ value = JSON.parse(raw);
4245
+ } catch {
4246
+ value = null;
4247
+ }
4248
+ if (!response2.ok) return mintRefusal(gatewayMintRefusal(response2.status, value));
4249
+ if (!isMintResult(value)) return mintRefusal("gateway_response_invalid");
4250
+ const requestRef = response2.headers.get("ceal-request-ref");
4251
+ if (requestRef === null || !/^ceal-request:[a-f0-9-]{36}$/u.test(requestRef)) return mintRefusal("gateway_response_invalid");
4252
+ const result2 = decodeCealHostInvocationAuthenticationResult(value, /* @__PURE__ */ new Date());
4253
+ return { schema_version: "ceal.host_relay_invocation_context_result.v2", status: "ready", request_ref: requestRef, result: result2 };
4254
+ }
4255
+ function gatewayMintFailure(signal, _error) {
4256
+ return mintRefusal(signal.aborted ? "transient_retryable" : "gateway_unavailable");
4257
+ }
4258
+ function mintRefusal(reason) {
4259
+ return { schema_version: "ceal.host_relay_invocation_context_result.v2", status: "refused", reason };
4260
+ }
4261
+ async function readBoundedResponseText(response2, maximumBytes) {
4262
+ if (response2.body === null) return null;
4263
+ const reader = response2.body.getReader();
4264
+ const chunks = [];
4265
+ let total = 0;
4266
+ try {
4267
+ while (true) {
4268
+ const { done, value } = await reader.read();
4269
+ if (done) break;
4270
+ total += value.byteLength;
4271
+ if (total > maximumBytes) {
4272
+ await reader.cancel();
4273
+ return null;
4274
+ }
4275
+ chunks.push(value);
4276
+ }
4277
+ const output = new Uint8Array(total);
4278
+ let offset = 0;
4279
+ for (const chunk of chunks) {
4280
+ output.set(chunk, offset);
4281
+ offset += chunk.byteLength;
4282
+ }
4283
+ return new TextDecoder("utf-8", { fatal: true }).decode(output);
4284
+ } catch {
4285
+ return null;
4286
+ }
4287
+ }
4288
+ function isMintResult(value) {
4289
+ return record5(value) && Object.keys(value).length === 3 && value.schema_version === CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA && typeof value.context_token === "string" && typeof value.expires_at === "string";
4290
+ }
4291
+ function gatewayMintRefusal(status, value) {
4292
+ if (status === 401) return "host_credential_refused";
4293
+ if (status === 403 && record5(value)) {
4294
+ if (value.error_code === "host_source_refused") return "host_source_refused";
4295
+ if (value.error_code === "context_expired") return "context_expired";
4296
+ if (value.error_code === "request_context_refused") return "context_stale";
4297
+ }
4298
+ if (status === 429) return "gateway_rate_limited";
4299
+ if (status >= 500) return "gateway_unavailable";
4300
+ return "gateway_refused";
4445
4301
  }
4446
4302
  async function runHostRelayCli(args, signal, dependencies = {}) {
4447
4303
  try {
@@ -4460,20 +4316,6 @@ function parseServeArgs(args) {
4460
4316
  }
4461
4317
  return args[2];
4462
4318
  }
4463
- async function assertOwnerPrivateDirectory2(path) {
4464
- try {
4465
- const stats = await lstat4(path);
4466
- if (!stats.isDirectory() || stats.isSymbolicLink() || !ownerPrivate4(stats)) throw new HostRelayConfigError("config_not_private", "Host Relay config directory is not owner-private.");
4467
- } catch (error) {
4468
- if (error instanceof HostRelayConfigError) throw error;
4469
- throw new HostRelayConfigError("config_unavailable", "Host Relay config directory is unavailable.");
4470
- }
4471
- }
4472
- function ownerPrivate4(stats) {
4473
- if (process.platform === "win32") return true;
4474
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
4475
- return stats.uid === uid && (stats.mode & 63) === 0;
4476
- }
4477
4319
  function assertNoDuplicateJsonKeys(text) {
4478
4320
  rejectDuplicateJsonKeys(text, {
4479
4321
  invalid: () => new HostRelayConfigError("invalid_json", "Host Relay config is not JSON."),
@@ -4492,12 +4334,14 @@ function exactOptionalKeys2(value, required, optional) {
4492
4334
  }
4493
4335
 
4494
4336
  // host/relay/bin/ceal-host-relay.ts
4337
+ var serviceArguments = consumeHostServiceLogArgument(process.argv.slice(2));
4338
+ if (serviceArguments.path !== void 0) process.env[HOST_SERVICE_LOG_ENVIRONMENT] = serviceArguments.path;
4495
4339
  var controller = new AbortController();
4496
4340
  var stop = () => controller.abort();
4497
4341
  process.once("SIGINT", stop);
4498
4342
  process.once("SIGTERM", stop);
4499
4343
  var stopLogBound = boundHostServiceLog();
4500
- void runHostRelayCli(process.argv.slice(2), controller.signal).then((result2) => {
4344
+ void runHostRelayCli(serviceArguments.args, controller.signal).then((result2) => {
4501
4345
  process.stdout.write(`${JSON.stringify({ schema_version: "ceal.host_relay_command.v1", ...result2 })}
4502
4346
  `);
4503
4347
  process.exitCode = result2.ok ? 0 : 1;