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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,11 +1222,22 @@ 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
1240
+ var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
983
1241
  var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
984
1242
  var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
985
1243
  var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "since", "until", "authored_by_me", "authored_by", "include_replies", "kinds", "container_ref", "container_refs", "container_names", "addressed_to_me", "unread"]);
@@ -992,6 +1250,8 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
992
1250
  "resource_created",
993
1251
  "review_submitted",
994
1252
  "comment_created",
1253
+ "reaction_created",
1254
+ "ceal_write_completed",
995
1255
  "event_start",
996
1256
  "all_day_start",
997
1257
  "file_created",
@@ -1026,6 +1286,10 @@ var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
1026
1286
  "github.review.submitted",
1027
1287
  "github.issue_comment.created",
1028
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",
1029
1293
  "calendar.organized",
1030
1294
  "calendar.accepted_invite",
1031
1295
  "calendar.recorded",
@@ -1047,6 +1311,10 @@ var CEAL_ACTIVITY_EVENT_TIMESTAMP_BASIS_MAP = Object.freeze({
1047
1311
  "github.pull_request.opened": "resource_created",
1048
1312
  "github.issue.edited": "resource_modified",
1049
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",
1050
1318
  "calendar.organized": "resource_created",
1051
1319
  "drive.file.created": "file_created",
1052
1320
  "drive.file.modified": "file_modified_by_me",
@@ -1063,12 +1331,14 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1063
1331
  next: { required: true, timestamp: false },
1064
1332
  kind: { required: true, timestamp: false },
1065
1333
  matched_kinds: { required: false, timestamp: false },
1334
+ title_match: { required: false, timestamp: false },
1066
1335
  title: { required: true, timestamp: false },
1067
1336
  container_label: { required: true, timestamp: false },
1068
1337
  container_ref: { required: false, timestamp: false },
1069
1338
  source_url: { required: false, timestamp: false },
1070
1339
  source: { required: true, timestamp: false },
1071
1340
  owner: { required: false, timestamp: false },
1341
+ actor_kind: { required: false, timestamp: false },
1072
1342
  counterparts: { required: false, timestamp: false },
1073
1343
  attachments: { required: false, timestamp: false },
1074
1344
  created_at: { required: false, timestamp: true },
@@ -1076,9 +1346,18 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
1076
1346
  modified_at: { required: false, timestamp: true },
1077
1347
  modified_at_local: { required: false, timestamp: true },
1078
1348
  viewed_by_me_at: { required: false, timestamp: true },
1349
+ modified_by_me_at: { required: false, timestamp: true },
1079
1350
  own_ceal_write: { required: false, timestamp: false },
1080
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 },
1081
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 },
1082
1361
  start: { required: false, timestamp: false },
1083
1362
  end: { required: false, timestamp: false },
1084
1363
  status: { required: false, timestamp: false },
@@ -1151,18 +1430,29 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
1151
1430
  // The serving gate answers this while the Gateway is still starting and the
1152
1431
  // refresh was never attempted; the same attempt stays retryable. It is never
1153
1432
  // stored by the session owner, only answered at the serving edge.
1154
- "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"
1155
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");
1156
1446
 
1157
1447
  // packages/ceal-client-protocol/src/request-validation-primitives.ts
1158
- import { createHash } from "node:crypto";
1448
+ import { createHash as createHash2 } from "node:crypto";
1159
1449
  var DIGEST = /^[a-f0-9]{64}$/u;
1160
1450
  var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
1161
1451
  function isCealDigest(value) {
1162
1452
  return typeof value === "string" && DIGEST.test(value);
1163
1453
  }
1164
1454
  function cealRequestSha256(value) {
1165
- return createHash("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
1455
+ return createHash2("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
1166
1456
  }
1167
1457
  function operationRecord(value, message) {
1168
1458
  requireOperationRecord(value, message);
@@ -1178,40 +1468,71 @@ function requirePrefixedRef(value, prefix, message) {
1178
1468
  // packages/ceal-client-protocol/src/request-authority.ts
1179
1469
  var CEAL_REQUEST_SCHEMA_VERSION = "ceal.request.v1";
1180
1470
  var CEAL_REQUEST_ARTIFACT_OWNERS = vocabulary(["gateway", "host", "connector"]);
1181
- 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"]);
1182
1472
  var CEAL_REQUEST_ARTIFACT_RETENTION_CLASSES = vocabulary(["request", "source", "result"]);
1183
1473
  var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
1184
- 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"]);
1185
1510
  var CEAL_REQUEST_JSON_SCHEMA = {
1186
1511
  type: "object",
1187
- 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,
1188
1514
  additionalProperties: false,
1189
1515
  properties: {
1190
- schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
1191
- ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
1192
- created_at: { type: "string", minLength: 1, maxLength: 64 },
1193
- 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
+ {
1194
1524
  type: "object",
1195
- maxProperties: 5,
1196
- additionalProperties: false,
1197
- properties: {
1198
- instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1199
- access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1200
- principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1201
- execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1202
- authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
1203
- },
1204
- 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
1205
1528
  },
1206
- prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1207
- host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1208
- host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1209
- host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1210
- host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
1211
- requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
1212
- 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." }
1213
- },
1214
- 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
+ ]
1215
1536
  };
1216
1537
 
1217
1538
  // packages/ceal-client-protocol/src/request-operands.ts
@@ -1322,10 +1643,6 @@ var CEAL_REQUEST_RUN_BATCH_JSON_SCHEMA = {
1322
1643
  };
1323
1644
 
1324
1645
  // packages/ceal-client-protocol/src/host-observation.ts
1325
- var CEAL_HOST_INPUT_KINDS = vocabulary(["human", "host_automatic"]);
1326
- function isCealHostInputKind(value) {
1327
- return typeof value === "string" && CEAL_HOST_INPUT_KINDS.some((kind) => kind === value);
1328
- }
1329
1646
  var CEAL_HOST_OBSERVATION_REF_PREFIX = "host-observation:";
1330
1647
  var HOST_TOKEN_OBSERVATION_BREAKDOWN_KEYS = vocabulary(["cached_input", "cache_write_input", "reasoning_output"]);
1331
1648
  function validateCealHostObservationDraft(value) {
@@ -1336,7 +1653,7 @@ function validateCealHostObservationDraft(value) {
1336
1653
  assertHostObservationValue(record6.value, record6.observation_kind);
1337
1654
  }
1338
1655
  var CEAL_REQUEST_MAX_OBSERVATION_VALUE_BYTES = 64 * 1024;
1339
- 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"]);
1340
1657
  function isCealHostPromptObservationValue(value) {
1341
1658
  return isOperationRecord(value) && Object.keys(value).length === 1 && isCealDigest(value.body_sha256);
1342
1659
  }
@@ -1363,10 +1680,6 @@ function inspectHostKeys(value) {
1363
1680
  // packages/ceal-client-protocol/src/host-prompt-ref.ts
1364
1681
  var CEAL_HOST_PROMPT_DIGEST_PATTERN = "^[a-f0-9]{64}$";
1365
1682
  var PROMPT_DIGEST = new RegExp(CEAL_HOST_PROMPT_DIGEST_PATTERN, "u");
1366
- function hostProducerPromptRef(promptSha256) {
1367
- if (!PROMPT_DIGEST.test(promptSha256)) throw new TypeError("Canonical Host prompt digest is invalid.");
1368
- return `ceal-host-prompt:${promptSha256}`;
1369
- }
1370
1683
 
1371
1684
  // packages/ceal-client-protocol/src/request-effect-approval.ts
1372
1685
  var CEAL_EFFECT_APPROVAL_DECISION_CHECK_PHRASES = Object.freeze([
@@ -1459,18 +1772,115 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
1459
1772
  };
1460
1773
 
1461
1774
  // packages/ceal-client-protocol/src/host-invocation-authentication.ts
1462
- 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");
1463
1782
  var CEAL_HOST_INVOCATION_CONTEXT_PATH = "/api/ceal/v2/host-invocation-context";
1464
- var CEAL_HOST_INGRESS_PATH = "/api/ceal/v2/host-ingress";
1465
- var CEAL_HOST_NONCE_ENCODED_CHARACTERS = 43;
1466
- var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(`^[A-Za-z0-9_-]{${CEAL_HOST_NONCE_ENCODED_CHARACTERS}}$`, "u");
1467
- var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(`^ceal_context_[A-Za-z0-9_-]{${CEAL_HOST_NONCE_ENCODED_CHARACTERS}}$`, "u");
1468
- var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = new TextEncoder().encode(`${JSON.stringify({
1469
- schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA,
1470
- context_token: `ceal_context_${"A".repeat(CEAL_HOST_NONCE_ENCODED_CHARACTERS)}`,
1471
- expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
1472
- })}
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]])))}
1473
1882
  `).byteLength;
1883
+ }
1474
1884
  var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
1475
1885
  "request_authority_unavailable",
1476
1886
  "request_missing",
@@ -1494,6 +1904,7 @@ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
1494
1904
  "host_credential_refused",
1495
1905
  "gateway_unavailable",
1496
1906
  "gateway_rate_limited",
1907
+ "contract_mismatch",
1497
1908
  "gateway_refused",
1498
1909
  "gateway_response_invalid",
1499
1910
  "producer_frame_invalid",
@@ -1508,76 +1919,43 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
1508
1919
  invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
1509
1920
  options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
1510
1921
  session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
1511
- context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn; run `ceal-host install` to repair the Host hook registration, then retry this command on a fresh Host turn" }),
1512
- context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1513
- no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
1514
- context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
1515
- 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" }),
1516
1930
  host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
1517
1931
  host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
1518
1932
  gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
1519
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" }),
1520
1935
  gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
1521
1936
  gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
1522
- 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" }),
1523
- 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" }),
1524
1939
  relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
1525
1940
  transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
1526
1941
  mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
1527
1942
  relay_failed: Object.freeze({ classification: "host_relay_failed", next_action: "answer_with_gap" }),
1528
1943
  host_refusal_invalid: Object.freeze({ classification: "host_refusal_invalid", next_action: "answer_with_gap" })
1529
1944
  });
1530
- function projectCealHostAuthenticationCause(diagnostic) {
1531
- return projectRelayCodeCause(diagnostic) ?? projectGatewayCodeCause(diagnostic) ?? projectGatewayStatusCause(diagnostic) ?? "mint_failed";
1532
- }
1533
- function projectRelayCodeCause(diagnostic) {
1534
- switch (diagnostic.relayCode) {
1535
- case "credential":
1536
- return "host_credential_refused";
1537
- case "configuration":
1538
- return "options_invalid";
1539
- case "payload_rejected":
1540
- return "producer_frame_invalid";
1541
- case "invalid_response":
1542
- return "gateway_response_invalid";
1543
- case "gateway_refused":
1544
- return projectGatewayRefusedCause(diagnostic);
1545
- case "gateway_unavailable":
1546
- return diagnostic.exhausted === true ? void 0 : "gateway_unavailable";
1547
- case void 0:
1548
- case "":
1549
- return void 0;
1550
- default:
1551
- return "mint_failed";
1552
- }
1553
- }
1554
- function projectGatewayRefusedCause(diagnostic) {
1555
- if (diagnostic.gatewayStatus === 401 || diagnostic.gatewayStatus === 403) return "host_credential_refused";
1556
- const stale = projectGatewayCodeCause(diagnostic);
1557
- if (stale !== void 0) return stale;
1558
- if (diagnostic.exhausted === true && (diagnostic.gatewayStatus === 408 || diagnostic.gatewayStatus === 429)) return "gateway_rate_limited";
1559
- return "gateway_refused";
1560
- }
1561
- function projectGatewayCodeCause(diagnostic) {
1562
- if (diagnostic.gatewayCode === "request_context_refused" && diagnostic.gatewayReason !== void 0 && isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, diagnostic.gatewayReason)) return "context_stale";
1563
- if (diagnostic.gatewayCode === "context_expired") return "context_expired";
1564
- if (diagnostic.gatewayCode === "host_source_refused") return "host_source_refused";
1565
- return void 0;
1566
- }
1567
- function projectGatewayStatusCause(diagnostic) {
1568
- if (diagnostic.gatewayStatus === 401 || diagnostic.gatewayStatus === 403) return "host_credential_refused";
1569
- if (diagnostic.exhausted === true && (diagnostic.gatewayStatus === 408 || diagnostic.gatewayStatus === 429)) return "gateway_rate_limited";
1570
- return void 0;
1571
- }
1572
1945
  function decodeCealHostInvocationAuthenticationResult(value, now = /* @__PURE__ */ new Date()) {
1573
- 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)) {
1574
1952
  throw new TypeError("The Host invocation authentication result is invalid.");
1575
1953
  }
1576
1954
  return Object.freeze({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA, context_token: value.context_token, expires_at: value.expires_at });
1577
1955
  }
1578
1956
  function validFutureTimestamp(value, now) {
1579
- const timestamp3 = Date.parse(value);
1580
- 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();
1581
1959
  }
1582
1960
  function exactKeys(value, expected) {
1583
1961
  const keys = Object.keys(value);
@@ -1590,9 +1968,6 @@ var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
1590
1968
  "hook_invoked",
1591
1969
  "registration_written",
1592
1970
  "hook_failed",
1593
- "watcher_turn_observed",
1594
- "current_prompt_registered",
1595
- "current_prompt_registration_failed",
1596
1971
  "activation_recorded",
1597
1972
  "activation_failed",
1598
1973
  "watcher_scan_failed",
@@ -1615,6 +1990,9 @@ var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "un
1615
1990
  var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
1616
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"]);
1617
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
+
1618
1996
  // packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
1619
1997
  var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
1620
1998
 
@@ -1673,6 +2051,7 @@ var ADMIN_UNAVAILABLE_REASONS = vocabulary([
1673
2051
  "legacy",
1674
2052
  "invalid_observation",
1675
2053
  "redacted",
2054
+ "organization_setting_disabled",
1676
2055
  "owner_unavailable",
1677
2056
  "unit_unknown",
1678
2057
  "retention_gap",
@@ -1702,15 +2081,13 @@ var ADMIN_APPROVAL_DISPOSITIONS = CEAL_OPERATION_APPROVAL_DISPOSITIONS;
1702
2081
  var ADMIN_APPROVAL_POSTURES = vocabulary([...ADMIN_APPROVAL_DISPOSITIONS, "not_applicable"]);
1703
2082
  var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "approval", "effect", "readback"]);
1704
2083
 
1705
- // packages/ceal-client-protocol/src/actor-kind.ts
1706
- var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
1707
-
1708
2084
  // packages/ceal-client-protocol/src/device-enrollment.ts
1709
2085
  var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
1710
2086
  var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
1711
2087
 
1712
2088
  // packages/ceal-client-protocol/src/personal-client-session.ts
1713
2089
  var REFRESH_FAILURE_CODES = new Set(CEAL_REFRESH_FAILURE_CODES);
2090
+ var REVOKE_FAILURE_CODES = new Set(CEAL_CLIENT_REVOKE_FAILURE_CODES);
1714
2091
 
1715
2092
  // packages/ceal-client-protocol/src/index.ts
1716
2093
  var MAX_REQUEST_BYTES = 32 * 1024;
@@ -1746,60 +2123,251 @@ function errorCode(error) {
1746
2123
  }
1747
2124
 
1748
2125
  // host/service-log.ts
1749
- import { copyFile, open, stat, truncate } from "node:fs/promises";
1750
- import { isAbsolute } from "node:path";
1751
- var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
1752
- var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
1753
- var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
1754
- function hostServiceLogPreviousPath(path) {
1755
- return `${path}.1`;
1756
- }
1757
- async function rotateHostServiceLog(path, maxBytes = HOST_SERVICE_LOG_MAX_BYTES) {
1758
- let size;
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);
1759
2166
  try {
1760
- size = (await stat(path)).size;
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;
1761
2172
  } catch (error) {
1762
- if (isNotFound(error)) return false;
2173
+ await handle.close();
1763
2174
  throw error;
1764
2175
  }
1765
- if (size <= maxBytes) return false;
1766
- await copyFile(path, hostServiceLogPreviousPath(path));
1767
- await truncate(path, 0);
1768
- return true;
1769
2176
  }
1770
- function boundHostServiceLog(environment = process.env) {
1771
- const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
1772
- if (path === void 0 || !isAbsolute(path)) return () => void 0;
1773
- let reported = false;
1774
- const check = () => {
1775
- rotateHostServiceLog(path).catch((error) => {
1776
- if (reported) return;
1777
- reported = true;
1778
- process.stderr.write(`[host-service-log] rotation_failed code=${errorCode2(error)}
1779
- `);
1780
- });
1781
- };
1782
- check();
1783
- const timer = setInterval(check, HOST_SERVICE_LOG_CHECK_INTERVAL_MS);
1784
- timer.unref();
1785
- return () => clearInterval(timer);
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
+ }
1786
2182
  }
1787
- var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
1788
- function isNotFound(error) {
1789
- return errorCode2(error) === "ENOENT";
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;
2240
+ try {
2241
+ value = JSON.parse(stderr.toString("utf8"));
2242
+ } catch {
2243
+ return new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an unreadable refusal.");
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;
2270
+ }
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 };
2295
+ }
2296
+ var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
2297
+ var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
2298
+ function hostServiceLogPreviousPath(path) {
2299
+ return `${path}.1`;
2300
+ }
2301
+ async function rotateHostServiceLog(path, maxBytes = HOST_SERVICE_LOG_MAX_BYTES) {
2302
+ let size;
2303
+ try {
2304
+ size = (await stat(path)).size;
2305
+ } catch (error) {
2306
+ if (isNotFound(error)) return false;
2307
+ throw error;
2308
+ }
2309
+ if (size <= maxBytes) return false;
2310
+ await copyFile(path, hostServiceLogPreviousPath(path));
2311
+ await truncate(path, 0);
2312
+ return true;
2313
+ }
2314
+ function boundHostServiceLog(environment = process.env) {
2315
+ const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
2316
+ if (path === void 0 || !isAbsolute(path)) return () => void 0;
2317
+ let reported = false;
2318
+ const ready = ensureHostServiceLogDirectory(path);
2319
+ const check = () => {
2320
+ ready.then(() => rotateHostServiceLog(path)).catch((error) => {
2321
+ if (reported) return;
2322
+ reported = true;
2323
+ process.stderr.write(`[host-service-log] rotation_failed code=${errorCode2(error)}
2324
+ `);
2325
+ });
2326
+ };
2327
+ check();
2328
+ const timer = setInterval(check, HOST_SERVICE_LOG_CHECK_INTERVAL_MS);
2329
+ timer.unref();
2330
+ return () => clearInterval(timer);
2331
+ }
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) {
2357
+ return errorCode2(error) === "ENOENT";
1790
2358
  }
1791
2359
  function errorCode2(error) {
1792
2360
  return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "unknown";
1793
2361
  }
1794
2362
 
1795
2363
  // host/runtime-source-paths.ts
1796
- import { createHash as createHash2 } from "node:crypto";
1797
- 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";
1798
2366
 
1799
2367
  // host/absolute-path.ts
1800
- import { posix, win32 } from "node:path";
2368
+ import { posix as posix2, win32 as win323 } from "node:path";
1801
2369
  function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allowRoot = false) {
1802
- const pathApi = flavor === "win32" || flavor === "windows" ? win32 : posix;
2370
+ const pathApi = flavor === "win32" || flavor === "windows" ? win323 : posix2;
1803
2371
  return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
1804
2372
  }
1805
2373
 
@@ -1811,28 +2379,22 @@ function hostSourceUuid(sourceRef) {
1811
2379
  function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
1812
2380
  const sourceId = hostSourceUuid(sourceRef);
1813
2381
  if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
1814
- const path = platform === "win32" ? win322 : posix2;
2382
+ const path = platform === "win32" ? win324 : posix3;
1815
2383
  const sourcesRoot = path.join(stateRoot, "sources");
1816
2384
  const sourceRoot = path.join(sourcesRoot, sourceId);
1817
2385
  const runtimeRoot = path.join(stateRoot, "run");
1818
- const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash2("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
1819
- 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`);
1820
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 };
1821
2388
  }
1822
- function assertHostRuntimeIpcLength(endpoint, platform) {
1823
- if (platform === "win32") return;
1824
- const maximum = platform === "linux" ? 107 : 103;
1825
- 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" });
1826
- }
1827
2389
  function assertHostRelaySourcePaths(stateRoot, config, platform = process.platform) {
1828
2390
  const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
1829
- 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;
1830
2393
  }
1831
2394
 
1832
2395
  // host/relay/src/host-relay-runtime.ts
1833
- import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
1834
- import { lstat as lstat4, open as open5 } from "node:fs/promises";
1835
- 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";
1836
2398
 
1837
2399
  // host/abortable-wait.ts
1838
2400
  function waitForAbort(signal, milliseconds) {
@@ -1918,13 +2480,13 @@ function skipWhitespace(text, start) {
1918
2480
  }
1919
2481
 
1920
2482
  // host/relay/src/producer-frame.ts
1921
- import { createHash as createHash3 } from "node:crypto";
2483
+ import { createHash as createHash4 } from "node:crypto";
1922
2484
  var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
1923
2485
  var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
1924
2486
  var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
1925
2487
  var SAFE_REF2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
1926
2488
  var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
1927
- 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"];
1928
2490
  var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
1929
2491
  function isHostProducerFrameKind(value) {
1930
2492
  return typeof value === "string" && FRAME_KINDS.has(value);
@@ -1976,58 +2538,15 @@ function decodeHostProducerFrame(input) {
1976
2538
  return value;
1977
2539
  }
1978
2540
  function hostProducerFrameHash(frame) {
1979
- return createHash3("sha256").update(JSON.stringify(frame), "utf8").digest("hex");
1980
- }
1981
- function extractHostBindingSelector(frame) {
1982
- if (frame.frame_kind !== "user_input") return null;
1983
- if (!PRODUCERS.has(frame.producer_id) || !safeRef(frame.producer_installation_ref)) return null;
1984
- const payload = frame.payload;
1985
- if (!safeRef(payload.local_session_ref) || !safeRef(payload.local_turn_ref)) return null;
1986
- const aliases = payload.local_turn_aliases;
1987
- if (aliases === void 0) {
1988
- return Object.freeze({
1989
- producer_id: frame.producer_id,
1990
- producer_installation_ref: frame.producer_installation_ref,
1991
- local_session_ref: payload.local_session_ref,
1992
- local_turn_ref: payload.local_turn_ref,
1993
- local_turn_aliases: Object.freeze([])
1994
- });
1995
- }
1996
- if (!Array.isArray(aliases) || aliases.length > 16 || !aliases.every(safeRef) || new Set(aliases).size !== aliases.length) return null;
1997
- return Object.freeze({
1998
- producer_id: frame.producer_id,
1999
- producer_installation_ref: frame.producer_installation_ref,
2000
- local_session_ref: payload.local_session_ref,
2001
- local_turn_ref: payload.local_turn_ref,
2002
- local_turn_aliases: Object.freeze([...aliases])
2003
- });
2004
- }
2005
- function hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef) {
2006
- if (!opaqueHostSourceRef(hostSourceRef)) throw new TypeError("Gateway host source reference is invalid.");
2007
- 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 })}`;
2008
- 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 })}`;
2009
- 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 })}`;
2010
- 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 })}`;
2011
- 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 };
2012
- }
2013
- function hostProducerSessionRef(hostSourceRef, frame, localSessionRef) {
2014
- return requiredIdentity(hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef).host_session_ref);
2015
- }
2016
- function hostProducerTurnRef(hostSourceRef, frame, localSessionRef, localTurnRef) {
2017
- return requiredIdentity(hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef).host_turn_ref);
2018
- }
2019
- function hostProducerInputRef(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef) {
2020
- return requiredIdentity(hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef).host_input_ref);
2541
+ return createHash4("sha256").update(JSON.stringify(frame), "utf8").digest("hex");
2021
2542
  }
2022
2543
  function hostProducerSubmissionRef(hostSourceRef, frame) {
2023
- 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 })}`;
2024
2546
  }
2025
2547
  function opaqueHostSourceRef(value) {
2026
2548
  return typeof value === "string" && SAFE_REF2.test(value) && !value.startsWith("ceal-host-source:");
2027
2549
  }
2028
- function isHostSafeRef(value) {
2029
- return safeRef(value);
2030
- }
2031
2550
  var BOUNDED_REFUSAL_WORD = /^[a-z][a-z0-9_]{0,63}$/u;
2032
2551
  function isBoundedRefusalWord(value) {
2033
2552
  return typeof value === "string" && BOUNDED_REFUSAL_WORD.test(value);
@@ -2044,16 +2563,13 @@ var HOST_RELAY_GATEWAY_ERROR_CODES = [
2044
2563
  "gateway_unavailable",
2045
2564
  "gateway_refused",
2046
2565
  "invalid_response",
2566
+ "contract_mismatch",
2047
2567
  "payload_rejected"
2048
2568
  ];
2049
2569
  function isRelayGatewayErrorCode(value) {
2050
2570
  return isVocabularyWord(HOST_RELAY_GATEWAY_ERROR_CODES, value);
2051
2571
  }
2052
2572
  var RELAY_DELIVERY_DIAGNOSTIC_KEYS = ["relayCode", "gatewayStatus", "gatewayCode", "gatewayReason", "exhausted"];
2053
- function requiredIdentity(value) {
2054
- if (value === void 0) throw new TypeError("Canonical Host identity is incomplete.");
2055
- return value;
2056
- }
2057
2573
  function validateFrame(value) {
2058
2574
  if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
2059
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)}`);
@@ -2063,7 +2579,7 @@ function validateFrame(value) {
2063
2579
  validatePayload(value.frame_kind, value.payload);
2064
2580
  }
2065
2581
  function validatePayload(kind, value) {
2066
- 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);
2067
2583
  if (!valid) throw new HostProducerFrameError("invalid_frame_shape");
2068
2584
  }
2069
2585
  function validSession(value) {
@@ -2072,15 +2588,6 @@ function validSession(value) {
2072
2588
  function validTurn(value) {
2073
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);
2074
2590
  }
2075
- function validInput(value) {
2076
- 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);
2077
- }
2078
- function validInputObservations(value) {
2079
- return validObservations(value) && Array.isArray(value) && value.filter((item) => record(item) && item.observation_kind === "prompt").length === 1;
2080
- }
2081
- function validInputOptionals(value) {
2082
- return optionalRefs(value.local_turn_aliases) && (value.context_expires_at === void 0 || timestamp(value.context_expires_at));
2083
- }
2084
2591
  function validTerminal(value) {
2085
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);
2086
2593
  }
@@ -2096,9 +2603,6 @@ function optionalText(value, maxLength) {
2096
2603
  function optionalNonNegative(value) {
2097
2604
  return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
2098
2605
  }
2099
- function optionalRefs(value) {
2100
- return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef);
2101
- }
2102
2606
  function validObservations(value) {
2103
2607
  if (value === void 0) return true;
2104
2608
  if (!Array.isArray(value) || value.length > 32) return false;
@@ -2143,27 +2647,78 @@ function exactOptional(value, required, optional) {
2143
2647
 
2144
2648
  // host/relay/src/relay-admission-store.ts
2145
2649
  import { readFile as readFile2 } from "node:fs/promises";
2146
- 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
+ }
2147
2703
 
2148
2704
  // host/relay/src/durable-atomic-replacement.ts
2149
- import { O_RDONLY } from "node:constants";
2150
- import { lstat, mkdir, open as open2, rename, unlink } from "node:fs/promises";
2151
- import { dirname } from "node:path";
2152
2705
  var DEFAULT_PORT = {
2153
2706
  platform: process.platform,
2154
- mkdir: (path, options) => mkdir(path, options),
2155
- lstat: (path) => lstat(path),
2707
+ mkdir: (path, options) => mkdir2(path, options),
2708
+ lstat: (path) => lstat2(path),
2156
2709
  open_file: (path) => open2(path, "wx", 384),
2157
2710
  rename,
2158
2711
  unlink,
2159
- open_directory: (path) => open2(path, O_RDONLY),
2712
+ open_directory: (path) => open2(path),
2160
2713
  unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
2161
2714
  };
2162
2715
  async function durableAtomicReplace(target, data, port = DEFAULT_PORT) {
2163
- const parent = dirname(target);
2716
+ const parent = dirname3(target);
2164
2717
  await port.mkdir(parent, { recursive: true, mode: 448 });
2165
2718
  const parentStats = await port.lstat(parent);
2166
- 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 }])}.`);
2167
2722
  const temporary = `${target}.${port.unique_ref()}.new`;
2168
2723
  let staged;
2169
2724
  const failures = [];
@@ -2174,7 +2729,7 @@ async function durableAtomicReplace(target, data, port = DEFAULT_PORT) {
2174
2729
  const closing = staged;
2175
2730
  staged = void 0;
2176
2731
  await closing.close();
2177
- await port.rename(temporary, target);
2732
+ await renameWithWindowsRetry(() => port.rename(temporary, target), { ...port.rename_retry, platform: port.platform, target });
2178
2733
  await syncParentDirectory(parent, port);
2179
2734
  } catch (error) {
2180
2735
  failures.push(error);
@@ -2200,11 +2755,6 @@ async function syncParentDirectory(path, port) {
2200
2755
  }
2201
2756
  if (failure !== void 0) throw failure;
2202
2757
  }
2203
- function ownerPrivate(stats, platform) {
2204
- if (platform === "win32") return true;
2205
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
2206
- return stats.uid === uid && (stats.mode & 63) === 0;
2207
- }
2208
2758
  async function closeStaged(staged, failures) {
2209
2759
  if (staged !== void 0) try {
2210
2760
  await staged.close();
@@ -2230,249 +2780,10 @@ function errorCode3(error) {
2230
2780
  return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2231
2781
  }
2232
2782
 
2233
- // host/relay/src/relay-request-binding-store.ts
2234
- import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
2235
- import { open as open3 } from "node:fs/promises";
2236
- import { join as join2 } from "node:path";
2237
- var HOST_RELAY_REQUEST_BINDING_SCHEMA = "ceal.host_relay_request_binding.v1";
2238
- var STATE_SCHEMA = "ceal.host_relay_request_binding_state.v1";
2239
- var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2240
- var MAX_BINDINGS = 8192;
2241
- var MAX_STATE_BYTES = 8 * 1024 * 1024;
2242
- var HostRelayBindingError = class extends TypeError {
2243
- name = "HostRelayBindingError";
2244
- code;
2245
- constructor(code, message) {
2246
- super(message);
2247
- this.code = code;
2248
- }
2249
- };
2250
- var RelayRequestBindingStore = class {
2251
- tail = Promise.resolve();
2252
- bindingWaiters = /* @__PURE__ */ new Set();
2253
- root;
2254
- constructor(root) {
2255
- if (typeof root !== "string" || root.length === 0) throw new TypeError("Host Relay binding root is invalid.");
2256
- this.root = root;
2257
- }
2258
- bind(input, now = /* @__PURE__ */ new Date()) {
2259
- const run = this.tail.then(() => this.bindLocked(input, now));
2260
- this.tail = run.then(() => void 0, () => void 0);
2261
- return run;
2262
- }
2263
- query(input) {
2264
- const run = this.tail.then(() => this.queryLocked(input));
2265
- this.tail = run.then(() => void 0, () => void 0);
2266
- return run;
2267
- }
2268
- waitForBinding(input, signal) {
2269
- validateQuery(input);
2270
- if (signal.aborted) return Promise.reject(new Error("Host Relay binding wait cancelled."));
2271
- return new Promise((resolve, reject) => {
2272
- let settled = false;
2273
- const finish = (result2, error) => {
2274
- if (settled) return;
2275
- settled = true;
2276
- this.bindingWaiters.delete(waiter);
2277
- signal.removeEventListener("abort", waiter.abort);
2278
- if (error !== void 0) reject(error);
2279
- else if (result2?.status === "ready") resolve(result2);
2280
- };
2281
- const abort = () => finish(void 0, new Error("Host Relay binding wait cancelled."));
2282
- const waiter = { query: input, resolve: (result2) => finish(result2), abort };
2283
- this.bindingWaiters.add(waiter);
2284
- signal.addEventListener("abort", abort, { once: true });
2285
- void this.query(input).then((result2) => {
2286
- if (result2.status === "ready") finish(result2);
2287
- }, (error) => finish(void 0, error));
2288
- });
2289
- }
2290
- async bindLocked(input, now) {
2291
- const candidate = bindingFromGateway(input, now);
2292
- const state = await this.read();
2293
- const existing = state.bindings.find((item) => sameCanonicalIdentity(item, candidate));
2294
- if (existing !== void 0) {
2295
- 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) }])}.`);
2296
- return existing;
2297
- }
2298
- 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}`)}.`);
2299
- const next = { schema_version: STATE_SCHEMA, bindings: [...state.bindings, candidate] };
2300
- await this.write(next);
2301
- this.resolveBindingWaiters(candidate);
2302
- return structuredClone(candidate);
2303
- }
2304
- resolveBindingWaiters(binding) {
2305
- for (const waiter of this.bindingWaiters) {
2306
- if (relayRequestBindingMatchesQuery(waiter.query, binding)) waiter.resolve({ status: "ready", binding: structuredClone(binding) });
2307
- }
2308
- }
2309
- async queryLocked(input) {
2310
- validateQuery(input);
2311
- const state = await this.read();
2312
- const matches = state.bindings.filter((binding2) => relayRequestBindingMatchesQuery(input, binding2));
2313
- if (matches.length === 0) return { status: "no_binding" };
2314
- const binding = matches[0];
2315
- if (binding === void 0) throw new TypeError("Host Relay binding query result is invalid.");
2316
- return { status: "ready", binding: structuredClone(binding) };
2317
- }
2318
- async read() {
2319
- let handle;
2320
- try {
2321
- handle = await open3(join2(this.root, "request-bindings.json"), O_RDONLY2 | O_NOFOLLOW);
2322
- const stats = await handle.stat();
2323
- 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" }])}.`);
2324
- 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}`)}.`);
2325
- return decodeState(JSON.parse(await handle.readFile("utf8")));
2326
- } catch (error) {
2327
- if (errorCode4(error) === "ENOENT") return { schema_version: STATE_SCHEMA, bindings: [] };
2328
- throw error;
2329
- } finally {
2330
- await handle?.close();
2331
- }
2332
- }
2333
- async write(state) {
2334
- const encoded = JSON.stringify(state);
2335
- 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}`)}.`);
2336
- await durableAtomicReplace(join2(this.root, "request-bindings.json"), encoded);
2337
- }
2338
- };
2339
- function relayRequestWorkContextFromBinding(binding) {
2340
- return createRelayRequestWorkContext({ request_ref: binding.request_ref, host_binding: {
2341
- host_source_ref: binding.host_source_ref,
2342
- host_session_ref: binding.host_session_ref,
2343
- host_turn_ref: binding.host_turn_ref,
2344
- host_input_ref: binding.host_input_ref,
2345
- prompt_ref: binding.prompt_ref
2346
- } });
2347
- }
2348
- function createRelayRequestWorkContext(input) {
2349
- if (!record2(input) || !exactKeys3(input, ["request_ref", "host_binding"]) || !safeRef2(input.request_ref) || !record2(input.host_binding)) {
2350
- throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
2351
- }
2352
- const hostBinding = input.host_binding;
2353
- 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)) {
2354
- throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
2355
- }
2356
- const hostSourceRef = requiredRef(hostBinding.host_source_ref);
2357
- const hostSessionRef = requiredRef(hostBinding.host_session_ref);
2358
- const hostTurnRef = requiredRef(hostBinding.host_turn_ref);
2359
- const hostInputRef = requiredRef(hostBinding.host_input_ref);
2360
- const promptRef = requiredRef(hostBinding.prompt_ref);
2361
- return Object.freeze({ request_ref: input.request_ref, host_binding: Object.freeze({
2362
- host_source_ref: hostSourceRef,
2363
- host_session_ref: hostSessionRef,
2364
- host_turn_ref: hostTurnRef,
2365
- host_input_ref: hostInputRef,
2366
- prompt_ref: promptRef
2367
- }) });
2368
- }
2369
- function bindingFromGateway(input, now) {
2370
- const payload = input.frame.payload;
2371
- const candidate = {
2372
- schema_version: HOST_RELAY_REQUEST_BINDING_SCHEMA,
2373
- producer_id: input.frame.producer_id,
2374
- producer_installation_ref: input.frame.producer_installation_ref,
2375
- local_session_ref: requiredRef(payload.local_session_ref),
2376
- local_turn_ref: requiredRef(payload.local_turn_ref),
2377
- local_turn_aliases: optionalRefs2(payload.local_turn_aliases),
2378
- prompt_ref: requiredRef(input.prompt_ref),
2379
- request_ref: requiredRef(input.request_ref),
2380
- bound_at: timestamp2(now.toISOString()),
2381
- ...payload.context_expires_at === void 0 ? {} : { context_expires_at: timestamp2(payload.context_expires_at) },
2382
- host_source_ref: requiredRef(input.host_binding.host_source_ref),
2383
- host_session_ref: requiredRef(input.host_binding.host_session_ref),
2384
- host_turn_ref: requiredRef(input.host_binding.host_turn_ref),
2385
- host_input_ref: requiredRef(input.host_binding.host_input_ref)
2386
- };
2387
- 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.");
2388
- validateBinding(candidate);
2389
- return candidate;
2390
- }
2391
- function decodeState(value) {
2392
- 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")}.`);
2393
- if (value.schema_version !== STATE_SCHEMA) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("schema_version", value.schema_version, STATE_SCHEMA)}.`);
2394
- if (!Array.isArray(value.bindings)) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("bindings", value.bindings, "an array")}.`);
2395
- const bindings = value.bindings.map((item) => {
2396
- validateBinding(item);
2397
- return item;
2398
- });
2399
- const keys = bindings.map(bindingKey);
2400
- if (new Set(keys).size !== keys.length) throw new TypeError("Host Relay binding state contains duplicate canonical turns.");
2401
- return { schema_version: STATE_SCHEMA, bindings };
2402
- }
2403
- function validateBinding(value) {
2404
- 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.");
2405
- }
2406
- function validBindingIdentity(value) {
2407
- 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);
2408
- }
2409
- function validBindingRequest(value) {
2410
- const tuple = [value.host_source_ref, value.host_session_ref, value.host_turn_ref, value.host_input_ref];
2411
- 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);
2412
- }
2413
- function validateQuery(value) {
2414
- 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.");
2415
- }
2416
- function relayRequestBindingMatchesQuery(query, binding) {
2417
- 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;
2418
- }
2419
- function sameCanonicalIdentity(left, right) {
2420
- return bindingKey(left) === bindingKey(right);
2421
- }
2422
- function sameBinding(left, right) {
2423
- 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);
2424
- }
2425
- function bindingKey(value) {
2426
- return `${value.producer_id}\0${value.producer_installation_ref}\0${value.local_session_ref}\0${value.local_turn_ref}`;
2427
- }
2428
- function requiredRef(value) {
2429
- if (!safeRef2(value)) throw new TypeError("Host Relay binding reference is invalid.");
2430
- return value;
2431
- }
2432
- function optionalRefs2(value) {
2433
- if (value === void 0) return [];
2434
- if (!refList(value)) throw new TypeError("Host Relay binding aliases are invalid.");
2435
- return [...value];
2436
- }
2437
- function timestamp2(value) {
2438
- if (!validTimestamp(value)) throw new TypeError("Host Relay binding timestamp is invalid.");
2439
- return value;
2440
- }
2441
- function producer(value) {
2442
- return value === "codex" || value === "claude";
2443
- }
2444
- function safeRef2(value) {
2445
- return typeof value === "string" && SAFE_REF3.test(value);
2446
- }
2447
- function refList(value) {
2448
- return Array.isArray(value) && value.length <= 16 && value.every(safeRef2) && new Set(value).size === value.length;
2449
- }
2450
- function validTimestamp(value) {
2451
- return typeof value === "string" && Number.isFinite(Date.parse(value));
2452
- }
2453
- function record2(value) {
2454
- return value !== null && typeof value === "object" && !Array.isArray(value);
2455
- }
2456
- function exactKeys3(value, keys) {
2457
- return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
2458
- }
2459
- function exactOptionalKeys(value, required, optional) {
2460
- const keys = Object.keys(value);
2461
- return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
2462
- }
2463
- function errorCode4(error) {
2464
- return record2(error) && typeof error.code === "string" ? error.code : null;
2465
- }
2466
- function ownerPrivate2(stats) {
2467
- if (process.platform === "win32") return true;
2468
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
2469
- return stats.uid === uid && (stats.mode & 63) === 0;
2470
- }
2471
-
2472
2783
  // host/relay/src/relay-quarantine-store.ts
2473
- import { createHash as createHash4 } from "node:crypto";
2784
+ import { createHash as createHash5 } from "node:crypto";
2474
2785
  import { readFile } from "node:fs/promises";
2475
- import { join as join3 } from "node:path";
2786
+ import { join as join2 } from "node:path";
2476
2787
  var RELAY_QUARANTINE_SCHEMA = "ceal.host_relay_quarantine_state.v1";
2477
2788
  var RELAY_QUARANTINE_MAX_RECORDS = 512;
2478
2789
  var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
@@ -2492,8 +2803,8 @@ var RelayQuarantineStore = class {
2492
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}`)}.`);
2493
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}`)}.`);
2494
2805
  }
2495
- async retainFrame(frame, reason, attribution, capturedAt = (/* @__PURE__ */ new Date()).toISOString()) {
2496
- 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);
2497
2808
  }
2498
2809
  async retainMalformed(rawFrame, reason, capturedAt = (/* @__PURE__ */ new Date()).toISOString()) {
2499
2810
  await this.retain(rawFrame, inferProducer(rawFrame), reason, capturedAt);
@@ -2505,53 +2816,37 @@ var RelayQuarantineStore = class {
2505
2816
  const records = state.producers[producerId].slice(-Math.min(limit, this.maxRecords)).map((record6) => structuredClone(record6));
2506
2817
  return { producer_id: producerId, records, total_bytes: records.reduce((total, record6) => total + record6.charged_bytes, 0) };
2507
2818
  }
2508
- /**
2509
- * The newest quarantined record whose exact-turn attribution matches the
2510
- * query, or null when evicted, unattributed, or for another turn. The
2511
- * file-backed state survives drain, restart, and overwrite.
2512
- */
2513
- async findNewestAttributedMatch(query) {
2514
- await this.tail;
2515
- const state = await this.readState();
2516
- const bucket = query.producer_id === "codex" || query.producer_id === "claude" ? state.producers[query.producer_id] : [];
2517
- for (let index = bucket.length - 1; index >= 0; index -= 1) {
2518
- const record6 = bucket[index];
2519
- if (record6 !== void 0 && attributedRecordMatchesQuery(record6, query)) return structuredClone(record6);
2520
- }
2521
- return null;
2522
- }
2523
2819
  async snapshot() {
2524
2820
  await this.tail;
2525
2821
  return this.readState();
2526
2822
  }
2527
- async retain(rawFrame, producerId, reason, capturedAt, attribution) {
2823
+ async retain(rawFrame, producerId, reason, capturedAt) {
2528
2824
  const run = this.tail.then(async () => {
2529
2825
  if (!PRODUCERS2.includes(producerId) || !Number.isFinite(Date.parse(capturedAt))) throw new TypeError("Relay quarantine record identity is invalid.");
2530
2826
  const raw = Buffer.from(rawFrame).subarray(0, MAX_CAPTURE_BYTES);
2531
2827
  const state = await this.readState();
2532
2828
  const record6 = {
2533
- 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")}`,
2534
2830
  producer_id: producerId,
2535
2831
  reason,
2536
2832
  captured_at: capturedAt,
2537
- frame_sha256: createHash4("sha256").update(raw).digest("hex"),
2833
+ frame_sha256: createHash5("sha256").update(raw).digest("hex"),
2538
2834
  charged_bytes: raw.byteLength,
2539
- raw_frame_base64: raw.toString("base64"),
2540
- ...attribution === void 0 ? {} : { attribution: structuredClone(attribution) }
2835
+ raw_frame_base64: raw.toString("base64")
2541
2836
  };
2542
2837
  const records = state.producers[producerId];
2543
2838
  records.push(record6);
2544
2839
  while (records.length > this.maxRecords || totalBytes(records) > this.maxBytes) records.shift();
2545
- await durableAtomicReplace(join3(this.root, "quarantine.json"), JSON.stringify(state));
2840
+ await durableAtomicReplace(join2(this.root, "quarantine.json"), JSON.stringify(state));
2546
2841
  });
2547
2842
  this.tail = run.then(() => void 0, () => void 0);
2548
2843
  await run;
2549
2844
  }
2550
2845
  async readState() {
2551
2846
  try {
2552
- 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")));
2553
2848
  } catch (error) {
2554
- if (errorCode5(error) === "ENOENT") return emptyState();
2849
+ if (errorCode4(error) === "ENOENT") return emptyState();
2555
2850
  throw error;
2556
2851
  }
2557
2852
  }
@@ -2562,9 +2857,9 @@ function emptyState() {
2562
2857
  function totalBytes(records) {
2563
2858
  return records.reduce((total, record6) => total + record6.charged_bytes, 0);
2564
2859
  }
2565
- function decodeState2(value) {
2566
- 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)}.`);
2567
- 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`)}.`);
2568
2863
  const state = emptyState();
2569
2864
  for (const producerId of PRODUCERS2) {
2570
2865
  const records = value.producers[producerId];
@@ -2574,8 +2869,7 @@ function decodeState2(value) {
2574
2869
  return state;
2575
2870
  }
2576
2871
  function validRecord(value) {
2577
- if (!record3(value) || !validRecordIdentity(value)) return false;
2578
- return value.attribution === void 0 || validAttribution(value.attribution);
2872
+ return record2(value) && validRecordIdentity(value);
2579
2873
  }
2580
2874
  function validRecordIdentity(value) {
2581
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);
@@ -2586,52 +2880,22 @@ function validReason(value) {
2586
2880
  function validChargedBytes(value) {
2587
2881
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
2588
2882
  }
2589
- var ATTRIBUTION_REQUIRED_KEYS = ["producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "local_turn_aliases", "sequence", "frame_sha256"];
2590
- var ATTRIBUTION_OPTIONAL_KEYS = ["diagnostic"];
2591
- function validAttribution(value) {
2592
- if (!record3(value) || !hasExactKeys2(value, ATTRIBUTION_REQUIRED_KEYS, ATTRIBUTION_OPTIONAL_KEYS)) return false;
2593
- return validAttributionIdentity(value) && validAttributionAliases(value.local_turn_aliases) && validAttributionEnvelope(value) && (value.diagnostic === void 0 || validAttributionDiagnostic(value.diagnostic));
2594
- }
2595
- function validAttributionIdentity(value) {
2596
- return (value.producer_id === "codex" || value.producer_id === "claude") && isHostSafeRef(value.producer_installation_ref) && isHostSafeRef(value.local_session_ref) && isHostSafeRef(value.local_turn_ref);
2597
- }
2598
- function validAttributionAliases(value) {
2599
- return Array.isArray(value) && value.length <= 16 && value.every(isHostSafeRef);
2600
- }
2601
- function validAttributionEnvelope(value) {
2602
- return typeof value.sequence === "number" && Number.isSafeInteger(value.sequence) && value.sequence > 0 && typeof value.frame_sha256 === "string";
2603
- }
2604
- function validAttributionDiagnostic(value) {
2605
- if (!record3(value) || !hasExactKeys2(value, [], RELAY_DELIVERY_DIAGNOSTIC_KEYS)) return false;
2606
- return isUndefinedOr(value.relayCode, isRelayGatewayErrorCode) && isUndefinedOr(value.gatewayStatus, isSafeInteger) && isUndefinedOr(value.gatewayCode, isBoundedRefusalWord) && isUndefinedOr(value.gatewayReason, isBoundedRefusalWord) && isUndefinedOr(value.exhausted, isBoolean);
2607
- }
2608
- function isSafeInteger(value) {
2609
- return Number.isSafeInteger(value);
2610
- }
2611
- function isBoolean(value) {
2612
- return typeof value === "boolean";
2613
- }
2614
- function attributedRecordMatchesQuery(recordValue, query) {
2615
- const attribution = recordValue.attribution;
2616
- if (attribution === void 0) return false;
2617
- 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));
2618
- }
2619
2883
  function quarantineProducer(value) {
2620
2884
  return value === "codex" || value === "claude" || value === "unknown";
2621
2885
  }
2622
2886
  function inferProducer(rawFrame) {
2623
2887
  try {
2624
2888
  const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.from(rawFrame).subarray(4)));
2625
- 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";
2626
2890
  } catch {
2627
2891
  return "unknown";
2628
2892
  }
2629
2893
  }
2630
- function record3(value) {
2894
+ function record2(value) {
2631
2895
  return value !== null && typeof value === "object" && !Array.isArray(value);
2632
2896
  }
2633
- function errorCode5(error) {
2634
- 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;
2635
2899
  }
2636
2900
 
2637
2901
  // host/relay/src/relay-admission-store.ts
@@ -2641,12 +2905,11 @@ var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
2641
2905
  var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
2642
2906
  var RelayAdmissionStore = class {
2643
2907
  tail = Promise.resolve();
2644
- drainWaiters = /* @__PURE__ */ new Set();
2645
2908
  root;
2646
2909
  quarantine;
2647
2910
  limits;
2648
2911
  faultHooks;
2649
- 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"))) {
2650
2913
  this.root = root;
2651
2914
  this.quarantine = quarantine;
2652
2915
  this.limits = limits;
@@ -2661,36 +2924,10 @@ var RelayAdmissionStore = class {
2661
2924
  await this.tail;
2662
2925
  return this.read();
2663
2926
  }
2664
- async hasPendingRequestBinding(query) {
2665
- const state = await this.read();
2666
- return state.entries.some((entry2) => pendingFrameMatchesBindingQuery(entry2, query));
2667
- }
2668
2927
  async drainRound(deliver, options) {
2669
2928
  const run = this.tail.then(() => this.drainLocked(deliver, options));
2670
2929
  this.tail = run.then(() => void 0, () => void 0);
2671
- const result2 = await run;
2672
- if (result2.delivered.length > 0 || result2.refused.length > 0 || result2.retained.length > 0) this.resolveDrainWaiters();
2673
- return result2;
2674
- }
2675
- waitForDrainChange(signal) {
2676
- if (signal.aborted) return Promise.reject(new Error("Host Relay admission wait cancelled."));
2677
- return new Promise((resolve, reject) => {
2678
- let settled = false;
2679
- const finish = (error) => {
2680
- if (settled) return;
2681
- settled = true;
2682
- this.drainWaiters.delete(waiter);
2683
- signal.removeEventListener("abort", waiter.abort);
2684
- if (error !== void 0) reject(error);
2685
- else resolve();
2686
- };
2687
- const waiter = { resolve: () => finish(), abort: () => finish(new Error("Host Relay admission wait cancelled.")) };
2688
- this.drainWaiters.add(waiter);
2689
- signal.addEventListener("abort", waiter.abort, { once: true });
2690
- });
2691
- }
2692
- resolveDrainWaiters() {
2693
- for (const waiter of this.drainWaiters) waiter.resolve();
2930
+ return run;
2694
2931
  }
2695
2932
  async admitLocked(frame) {
2696
2933
  const state = await this.read();
@@ -2702,11 +2939,11 @@ var RelayAdmissionStore = class {
2702
2939
  const hash = hostProducerFrameHash(frame);
2703
2940
  const replay = replayCode(existing, hash, frame.sequence, installation2);
2704
2941
  if (replay !== null) {
2705
- 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);
2706
2943
  return result(replay, installation2);
2707
2944
  }
2708
2945
  if (frame.sequence !== installation2.admitted_through_sequence + 1) {
2709
- await this.quarantine.retainFrame(frame, "sequence_gap", attributionForFrame(frame, hash, frame.sequence));
2946
+ await this.quarantine.retainFrame(frame, "sequence_gap");
2710
2947
  return result("sequence_gap", installation2);
2711
2948
  }
2712
2949
  const chargedBytes = 4 + Buffer.byteLength(JSON.stringify(frame), "utf8");
@@ -2753,7 +2990,7 @@ var RelayAdmissionStore = class {
2753
2990
  if (!installation2) throw new TypeError("Host Relay delivery installation is unavailable.");
2754
2991
  if (outcome.status === "refused" || exhausted) {
2755
2992
  const diagnostic = exhaustedDiagnostic(outcome, exhausted);
2756
- 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");
2757
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 };
2758
2995
  refused.push(input.submission_ref);
2759
2996
  } else {
@@ -2784,31 +3021,19 @@ var RelayAdmissionStore = class {
2784
3021
  }
2785
3022
  async read() {
2786
3023
  try {
2787
- 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")));
2788
3025
  } catch (error) {
2789
- 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: [] };
2790
3027
  throw error;
2791
3028
  }
2792
3029
  }
2793
3030
  async write(state) {
2794
- await durableAtomicReplace(join4(this.root, "admission.json"), JSON.stringify(state));
3031
+ await durableAtomicReplace(join3(this.root, "admission.json"), JSON.stringify(state));
2795
3032
  }
2796
3033
  };
2797
3034
  function installationKey(frame) {
2798
3035
  return `${frame.producer_id}\0${frame.producer_installation_ref}`;
2799
3036
  }
2800
- function pendingFrameMatchesBindingQuery(entry2, query) {
2801
- const selector = extractHostBindingSelector(entry2.frame);
2802
- if (selector === null) return false;
2803
- return relayRequestBindingMatchesQuery(query, selector);
2804
- }
2805
- function attributionForFrame(frame, hash, sequence, diagnostic) {
2806
- const selector = extractHostBindingSelector(frame);
2807
- if (selector === null) return void 0;
2808
- if (frame.producer_id !== selector.producer_id || frame.producer_installation_ref !== selector.producer_installation_ref || frame.sequence !== sequence) return void 0;
2809
- if (hostProducerFrameHash(frame) !== hash) return void 0;
2810
- return { ...selector, sequence, frame_sha256: hash, ...diagnostic === void 0 ? {} : { diagnostic } };
2811
- }
2812
3037
  function exhaustedDiagnostic(outcome, exhausted) {
2813
3038
  if (outcome.status !== "retryable" && outcome.status !== "refused") return void 0;
2814
3039
  if (outcome.diagnostic !== void 0) return exhausted ? { ...outcome.diagnostic, exhausted: true } : outcome.diagnostic;
@@ -2826,6 +3051,7 @@ function retryExhausted(outcome, entry2) {
2826
3051
  return outcome.status === "retryable" && entry2.retry_attempts >= MAX_RETRYABLE_ATTEMPTS;
2827
3052
  }
2828
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." };
2829
3055
  if (outcome.status === "refused") return { code: outcome.code, message: outcome.message };
2830
3056
  const reason = outcome.status === "retryable" ? outcome.reason ?? "no reason reported" : "no reason reported";
2831
3057
  return { code: "retry_exhausted", message: `Gateway delivery stayed retryable through ${entry2.retry_attempts} attempts: ${reason}` };
@@ -2839,10 +3065,10 @@ function scheduleRetry(entry2, nowMs, reason) {
2839
3065
  function result(code, state) {
2840
3066
  return { code, admitted_through_sequence: state.admitted_through_sequence, delivered_through_sequence: state.delivered_through_sequence };
2841
3067
  }
2842
- function errorCode6(error) {
3068
+ function errorCode5(error) {
2843
3069
  return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
2844
3070
  }
2845
- function decodeState3(value) {
3071
+ function decodeState2(value) {
2846
3072
  if (!stateRecord(value)) throw new TypeError("Host Relay admission state is invalid.");
2847
3073
  const installations = {};
2848
3074
  for (const [key, item] of Object.entries(value.installations)) {
@@ -2869,18 +3095,18 @@ function refusalRecord(value) {
2869
3095
  }
2870
3096
  function validDiagnostic(value) {
2871
3097
  if (!object(value) || !hasExactKeys2(value, [], RELAY_DELIVERY_DIAGNOSTIC_KEYS)) return false;
2872
- 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);
2873
3099
  }
2874
- function isSafeInteger2(value) {
3100
+ function isSafeInteger(value) {
2875
3101
  return Number.isSafeInteger(value);
2876
3102
  }
2877
- function isBoolean2(value) {
3103
+ function isBoolean(value) {
2878
3104
  return typeof value === "boolean";
2879
3105
  }
2880
3106
  function entry(value) {
2881
- 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);
2882
3108
  }
2883
- function producer2(value) {
3109
+ function producer(value) {
2884
3110
  return value === "codex" || value === "claude";
2885
3111
  }
2886
3112
  function object(value) {
@@ -2910,10 +3136,10 @@ function poisonEnvelopeMatches(entryValue, installation2) {
2910
3136
  return poisonEnvelopeSchema(frame) && poisonEnvelopeProducer(frame, entryValue) && poisonEnvelopeInstallation(frame, entryValue) && poisonEnvelopeEpoch(frame, installation2) && poisonEnvelopeSequence(frame, entryValue) && poisonEnvelopeKind(frame) && poisonEnvelopeTimestamp(frame);
2911
3137
  }
2912
3138
  function poisonEnvelopeSchema(frame) {
2913
- return frame.schema_version === "ceal.host_producer_frame.v1";
3139
+ return frame.schema_version === HOST_PRODUCER_FRAME_SCHEMA;
2914
3140
  }
2915
3141
  function poisonEnvelopeProducer(frame, entryValue) {
2916
- return producer2(frame.producer_id) && frame.producer_id === entryValue.producer_id;
3142
+ return producer(frame.producer_id) && frame.producer_id === entryValue.producer_id;
2917
3143
  }
2918
3144
  function poisonEnvelopeInstallation(frame, entryValue) {
2919
3145
  return opaqueHostSourceRef(frame.producer_installation_ref) && frame.producer_installation_ref === entryValue.producer_installation_ref;
@@ -2941,674 +3167,275 @@ function poisonSequenceInRange(entryValue, installation2) {
2941
3167
  return entryValue.sequence <= installation2.admitted_through_sequence;
2942
3168
  }
2943
3169
 
2944
- // host/relay/src/relay-gateway-bridge.ts
2945
- import { readFile as readFile3 } from "node:fs/promises";
2946
- import { join as join5 } from "node:path";
2947
- var STATE_SCHEMA2 = "ceal.host_relay_gateway_state.v1";
2948
- var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
2949
- var HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS = ["created", "adopted", "replayed", "approval_approved", "approval_denied", "approval_replayed"];
2950
- function isHostGatewayAcknowledgementDisposition(value) {
2951
- return typeof value === "string" && HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS.some((disposition) => disposition === value);
2952
- }
2953
- var RelayGatewayBridge = class {
2954
- statePath;
2955
- hostSourceRef;
2956
- gateway;
2957
- timeoutMs;
2958
- onRequestBound;
2959
- tail = Promise.resolve();
2960
- constructor(options) {
2961
- 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.");
2962
- if (!validOptionalTimeout(options.timeout_ms)) throw new TypeError("Host Relay Gateway timeout is invalid.");
2963
- this.statePath = join5(options.state_root, "gateway-state.json");
2964
- this.hostSourceRef = options.host_source_ref;
2965
- this.gateway = options.gateway;
2966
- this.timeoutMs = options.timeout_ms ?? 5e3;
2967
- this.onRequestBound = options.on_request_bound ?? (() => void 0);
2968
- }
2969
- deliver(input) {
2970
- const run = this.tail.then(() => this.deliverLocked(input));
2971
- this.tail = run.then(() => void 0, () => void 0);
2972
- return run;
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;
2973
3191
  }
2974
- async deliverLocked(input) {
2975
- 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 }])}.`);
2976
- if (input.frame.frame_kind === "producer_status") return { status: "created" };
2977
- const state = await this.readState();
2978
- if (input.frame.frame_kind === "session_started") return this.stageSession(state, input);
2979
- if (input.frame.frame_kind === "turn_started") return this.stageTurn(state, input);
2980
- return input.frame.frame_kind === "user_input" ? this.deliverInput(state, input) : this.deliverTerminal(state, input);
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.`);
2981
3211
  }
2982
- async stageSession(state, input) {
2983
- const localSessionRef = payloadRef(input.frame, "local_session_ref");
2984
- const hostSessionRef = hostProducerSessionRef(this.hostSourceRef, input.frame, localSessionRef);
2985
- const next = { ...state, sessions: { ...state.sessions, [hostSessionRef]: { observations: payloadObservations(input.frame), submission_ref: input.submission_ref } } };
2986
- await this.writeState(next);
2987
- return { status: "created" };
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);
2988
3223
  }
2989
- async stageTurn(state, input) {
2990
- const refs = frameRefs(input.frame, this.hostSourceRef);
2991
- const existing = state.turns[refs.host_turn_ref];
2992
- const nextTurn = {
2993
- host_session_ref: refs.host_session_ref,
2994
- observations: payloadObservations(input.frame),
2995
- submission_ref: input.submission_ref,
2996
- ...existing?.request_ref === void 0 ? {} : { request_ref: existing.request_ref },
2997
- ...existing?.prompt_ref === void 0 ? {} : { prompt_ref: existing.prompt_ref },
2998
- ...existing?.origin_host_input_ref === void 0 ? {} : { origin_host_input_ref: existing.origin_host_input_ref },
2999
- ...existing?.input_refused === void 0 ? {} : { input_refused: existing.input_refused }
3000
- };
3001
- await this.writeState({ ...state, turns: { ...state.turns, [refs.host_turn_ref]: nextTurn } });
3002
- return { status: "created" };
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();
3003
3231
  }
3004
- async deliverInput(state, input) {
3005
- const prepared = prepareInput(state, input, this.hostSourceRef);
3006
- const priorRefusal = refusedUnboundInput(prepared.turn);
3007
- if (priorRefusal !== void 0) return priorRefusal;
3008
- const outcome = await this.submit(prepared.submission);
3009
- if ("refusal" in outcome) {
3010
- 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 } } } });
3011
- return deliveryRefusal(outcome.refusal);
3012
- }
3013
- const acknowledgement2 = outcome.acknowledgement;
3014
- const nextTurn = bindInput(prepared, acknowledgement2.request_ref, input.submission_ref);
3015
- await this.writeState({ ...state, turns: { ...state.turns, [prepared.refs.host_turn_ref]: nextTurn } });
3016
- if (!bindsOriginInput(prepared)) return { status: "created" };
3017
- const bindingRequestRef = nextTurn.request_ref ?? acknowledgement2.request_ref;
3018
- const bindingPromptRef = nextTurn.prompt_ref ?? prepared.promptRef;
3019
- const bindingInputRef = nextTurn.origin_host_input_ref ?? prepared.hostInputRef;
3020
- const binding = { frame: structuredClone(input.frame), request_ref: bindingRequestRef, prompt_ref: bindingPromptRef, host_binding: {
3021
- host_source_ref: prepared.submission.identity.host_source_ref,
3022
- host_session_ref: prepared.submission.identity.host_session_ref,
3023
- host_turn_ref: prepared.submission.identity.host_turn_ref,
3024
- host_input_ref: bindingInputRef,
3025
- prompt_ref: bindingPromptRef
3026
- } };
3027
- const context = createRelayRequestWorkContext({ request_ref: bindingRequestRef, host_binding: binding.host_binding });
3028
- try {
3029
- await this.onRequestBound(binding, context);
3030
- } catch (error) {
3031
- if (error instanceof HostRelayBindingError) return { status: "refused", code: error.code, message: error.message };
3032
- return { status: "retryable", reason: `binding_store: ${errorMessage(error)}` };
3033
- }
3034
- return { status: "created" };
3035
- }
3036
- async deliverTerminal(state, input) {
3037
- const refs = frameRefs(input.frame, this.hostSourceRef);
3038
- const turn = state.turns[refs.host_turn_ref];
3039
- if (turn === void 0) return { status: "refused", code: "origin_turn_unknown", message: "The terminal frame names a turn whose start was never admitted." };
3040
- if (turn.request_ref === void 0 || turn.prompt_ref === void 0 || turn.origin_host_input_ref === void 0) {
3041
- 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}).` };
3042
- return { status: "retryable", reason: "origin_turn_unbound" };
3043
- }
3044
- const outcome = await this.submit({
3045
- identity: { host_source_ref: this.hostSourceRef, host_session_ref: refs.host_session_ref, host_turn_ref: refs.host_turn_ref },
3046
- host_input_ref: turn.origin_host_input_ref,
3047
- input_kind: "host_automatic",
3048
- prompt_ref: turn.prompt_ref,
3049
- observations: payloadObservations(input.frame),
3050
- request_ref: turn.request_ref,
3051
- submission_ref: input.submission_ref
3052
- });
3053
- return "refusal" in outcome ? deliveryRefusal(outcome.refusal) : { status: "created" };
3054
- }
3055
- async submit(input) {
3056
- const controller2 = new AbortController();
3057
- const timeout = setTimeout(() => controller2.abort(), this.timeoutMs);
3058
- try {
3059
- const acknowledgement2 = await this.gateway.submitHostTurn(input, controller2.signal);
3060
- return SAFE_REF4.test(acknowledgement2.request_ref) && isHostGatewayAcknowledgementDisposition(acknowledgement2.disposition) ? { acknowledgement: acknowledgement2 } : { refusal: { code: "invalid_response", message: "Gateway acknowledgement identity is invalid.", definitive: false } };
3061
- } catch (error) {
3062
- return { refusal: submissionRefusal(error) };
3063
- } finally {
3064
- clearTimeout(timeout);
3065
- }
3066
- }
3067
- async readState() {
3068
- try {
3069
- const state = decodeState4(JSON.parse(await readFile3(this.statePath, "utf8")));
3070
- 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 }])}.`);
3071
- return state;
3072
- } catch (error) {
3073
- if (errorCode7(error) === "ENOENT") return { schema_version: STATE_SCHEMA2, host_source_ref: this.hostSourceRef, sessions: {}, turns: {} };
3074
- throw error;
3075
- }
3076
- }
3077
- async writeState(state) {
3078
- 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);
3079
3239
  }
3080
- };
3081
- var RETRYABLE_HTTP_STATUSES = /* @__PURE__ */ new Set([401, 403, 408, 429]);
3082
- function submissionRefusal(error) {
3083
- const status = isRecord(error) && typeof error.status === "number" ? error.status : void 0;
3084
- const code = errorCode7(error) ?? "gateway_unavailable";
3085
- const definitive = code === "payload_rejected" || status !== void 0 && status >= 400 && status < 500 && !RETRYABLE_HTTP_STATUSES.has(status);
3086
- return { code, message: errorMessage(error), definitive, diagnostic: typedDiagnostic(error, status) };
3087
- }
3088
- function typedDiagnostic(error, status) {
3089
- if (!isRecord(error)) return status === void 0 ? {} : { gatewayStatus: status };
3090
- const gatewayCode = isBoundedRefusalWord(error.error_code) ? error.error_code : void 0;
3091
- const gatewayReason = isBoundedRefusalWord(error.refusal_reason) ? error.refusal_reason : void 0;
3092
- return {
3093
- ...typeof error.code === "string" ? { relayCode: error.code } : {},
3094
- ...status === void 0 ? {} : { gatewayStatus: status },
3095
- ...gatewayCode === void 0 ? {} : { gatewayCode },
3096
- ...gatewayReason === void 0 ? {} : { gatewayReason }
3097
- };
3240
+ return Buffer.from(key);
3098
3241
  }
3099
- function deliveryRefusal(refusal) {
3100
- const diagnostic = refusal.diagnostic === void 0 ? {} : { diagnostic: refusal.diagnostic };
3101
- return refusal.definitive ? { status: "refused", code: refusal.code, message: refusal.message, ...diagnostic } : { status: "retryable", reason: `${refusal.code}: ${refusal.message}`, ...diagnostic };
3102
- }
3103
- function errorMessage(error) {
3104
- return error instanceof Error ? error.message : String(error);
3105
- }
3106
- function frameRefs(frame, hostSourceRef) {
3107
- const localSessionRef = payloadRef(frame, "local_session_ref");
3108
- const localTurnRef = payloadRef(frame, "local_turn_ref");
3109
- const hostSessionRef = hostProducerSessionRef(hostSourceRef, frame, localSessionRef);
3110
- return { local_session_ref: localSessionRef, local_turn_ref: localTurnRef, host_session_ref: hostSessionRef, host_turn_ref: hostProducerTurnRef(hostSourceRef, frame, localSessionRef, localTurnRef) };
3111
- }
3112
- function prepareInput(state, input, hostSourceRef) {
3113
- const refs = frameRefs(input.frame, hostSourceRef);
3114
- const turn = state.turns[refs.host_turn_ref];
3115
- const promptRef = turn?.prompt_ref ?? hostProducerPromptRef(payloadPromptDigest(input.frame));
3116
- const hostInputRef = hostProducerInputRef(hostSourceRef, input.frame, refs.local_session_ref, refs.local_turn_ref, payloadRef(input.frame, "local_input_ref"));
3117
- const observations = uniqueObservations([...state.sessions[refs.host_session_ref]?.observations ?? [], ...turn?.observations ?? [], ...payloadObservations(input.frame)]);
3118
- const submission = compactSubmission({
3119
- identity: { host_source_ref: hostSourceRef, host_session_ref: refs.host_session_ref, host_turn_ref: refs.host_turn_ref },
3120
- host_input_ref: hostInputRef,
3121
- input_kind: payloadInputKind(input.frame),
3122
- prompt_ref: promptRef,
3123
- observations,
3124
- request_ref: turn?.request_ref,
3125
- submission_ref: input.submission_ref
3126
- });
3127
- return { refs, turn, promptRef, hostInputRef, submission };
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;
3128
3246
  }
3129
- function bindInput(prepared, requestRef, submissionRef) {
3130
- return {
3131
- host_session_ref: prepared.refs.host_session_ref,
3132
- observations: prepared.turn?.observations ?? [],
3133
- submission_ref: prepared.turn?.submission_ref ?? submissionRef,
3134
- request_ref: prepared.turn?.request_ref ?? requestRef,
3135
- prompt_ref: prepared.turn?.prompt_ref ?? prepared.promptRef,
3136
- origin_host_input_ref: prepared.turn?.origin_host_input_ref ?? prepared.hostInputRef
3137
- };
3247
+ async function readRelayPipeKey(path, platform, custody) {
3248
+ if (platform === "win32") return await readWindowsRelayPipeKey(path, custody);
3249
+ return await readPosixRelayPipeKey(path);
3138
3250
  }
3139
- function refusedUnboundInput(turn) {
3140
- if (turn?.request_ref !== void 0 || turn?.input_refused === void 0) return void 0;
3141
- 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}).` };
3142
- }
3143
- function bindsOriginInput(prepared) {
3144
- return prepared.turn?.request_ref === void 0 || prepared.turn.origin_host_input_ref === prepared.hostInputRef;
3145
- }
3146
- function payloadRef(frame, key) {
3147
- const value = frame.payload[key];
3148
- if (typeof value !== "string" || !SAFE_REF4.test(value)) throw new TypeError("Host Relay frame reference is invalid.");
3149
- return value;
3150
- }
3151
- function payloadPromptDigest(frame) {
3152
- const prompt = payloadObservations(frame).find((observation) => observation.observation_kind === "prompt");
3153
- if (prompt === void 0 || !isCealHostPromptObservationValue(prompt.value)) throw new TypeError("Host Relay prompt observation is invalid.");
3154
- return prompt.value.body_sha256;
3155
- }
3156
- function payloadObservations(frame) {
3157
- return Array.isArray(frame.payload.observations) ? frame.payload.observations.filter(isObservation) : [];
3158
- }
3159
- function payloadInputKind(frame) {
3160
- const value = frame.payload.input_kind;
3161
- if (!isCealHostInputKind(value)) throw new TypeError("Host Relay input kind is invalid.");
3162
- return value;
3163
- }
3164
- function uniqueObservations(values) {
3165
- const seen = /* @__PURE__ */ new Set();
3166
- return values.filter((value) => !seen.has(value.observation_ref) && Boolean(seen.add(value.observation_ref)));
3167
- }
3168
- function compactSubmission(value) {
3169
- 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 } };
3170
- }
3171
- function isObservation(value) {
3251
+ async function readWindowsRelayPipeKey(path, custody) {
3252
+ let key;
3172
3253
  try {
3173
- validateCealHostObservationDraft(value);
3174
- return true;
3175
- } catch {
3176
- return false;
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.`);
3177
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);
3178
3261
  }
3179
- function isRecord(value) {
3180
- return value !== null && typeof value === "object" && !Array.isArray(value);
3181
- }
3182
- function validOptionalTimeout(value) {
3183
- return value === void 0 || Number.isSafeInteger(value) && Number(value) >= 1 && Number(value) <= 6e4;
3184
- }
3185
- function errorCode7(error) {
3186
- return isRecord(error) && typeof error.code === "string" ? error.code : null;
3187
- }
3188
- function decodeState4(value) {
3189
- 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)}.`);
3190
- 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")}.`);
3191
- 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" }])}.`);
3192
- return { schema_version: STATE_SCHEMA2, host_source_ref: value.host_source_ref, sessions: decodeSessions(value.sessions), turns: decodeTurns(value.turns) };
3193
- }
3194
- function decodeSessions(value) {
3195
- const result2 = {};
3196
- for (const [key, item] of Object.entries(value)) {
3197
- 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.");
3198
- result2[key] = { observations: item.observations, submission_ref: item.submission_ref };
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}.`);
3199
3269
  }
3200
- return result2;
3201
- }
3202
- function decodeTurns(value) {
3203
- const result2 = {};
3204
- for (const [key, item] of Object.entries(value)) {
3205
- if (!SAFE_REF4.test(key)) throw new TypeError("Host Relay staged turn is invalid.");
3206
- result2[key] = decodeTurn(item);
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();
3207
3278
  }
3208
- return result2;
3209
3279
  }
3210
- function decodeTurn(item) {
3211
- 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.");
3212
- return {
3213
- host_session_ref: item.host_session_ref,
3214
- observations: item.observations,
3215
- submission_ref: item.submission_ref,
3216
- ...decodeTurnBinding(item),
3217
- ...isInputRefusal(item.input_refused) ? { input_refused: { code: item.input_refused.code, message: item.input_refused.message } } : {}
3218
- };
3280
+ function createPipeClientNonce() {
3281
+ return randomBytes(RELAY_PIPE_NONCE_BYTES).toString("base64url");
3219
3282
  }
3220
- function decodeTurnBinding(item) {
3221
- 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 } : {} };
3283
+ function pipeProof(key, role, firstNonce, secondNonce) {
3284
+ return createHmac("sha256", key).update(`${role}\0${firstNonce}\0${secondNonce}`, "utf8").digest("hex");
3222
3285
  }
3223
- function isInputRefusal(value) {
3224
- return isRecord(value) && typeof value.code === "string" && typeof value.message === "string";
3286
+ function createPipeServerProof(key, clientNonce, serverNonce) {
3287
+ return pipeProof(key, "server", clientNonce, serverNonce);
3225
3288
  }
3226
-
3227
- // host/relay/src/relay-gateway-http.ts
3228
- import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
3229
- import { lstat as lstat2, open as open4 } from "node:fs/promises";
3230
- import { dirname as dirname2 } from "node:path";
3231
-
3232
- // host/gateway-http.ts
3233
- var SAFE_HOST_REF = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
3234
- function hostIngressBody(input, invalidEventTime) {
3235
- if (!isCealHostInputKind(input.input_kind)) throw new TypeError("Host ingress input kind is invalid.");
3236
- const prompt = input.observations.find((observation) => observation.observation_kind === "prompt");
3237
- const observedAt = prompt?.observed_at ?? input.observations[0]?.observed_at;
3238
- if (observedAt === void 0 || !Number.isFinite(Date.parse(observedAt))) throw invalidEventTime();
3239
- return {
3240
- submission_ref: input.submission_ref,
3241
- host_source_ref: input.identity.host_source_ref,
3242
- host_session_ref: input.identity.host_session_ref,
3243
- host_turn_ref: input.identity.host_turn_ref,
3244
- host_input_ref: input.host_input_ref,
3245
- input_kind: input.input_kind,
3246
- prompt_ref: input.prompt_ref,
3247
- observed_at: observedAt,
3248
- observations: input.observations,
3249
- ...input.request_ref === void 0 ? {} : { request_ref: input.request_ref }
3250
- };
3251
- }
3252
- async function responseJson(response2, invalidResponse2) {
3253
- try {
3254
- return await response2.json();
3255
- } catch {
3256
- throw invalidResponse2();
3257
- }
3289
+ function createPipeClientProof(key, serverNonce, clientNonce) {
3290
+ return pipeProof(key, "client", serverNonce, clientNonce);
3258
3291
  }
3259
- async function submitHostIngress(options) {
3260
- if (options.input.identity.host_source_ref !== options.expected_host_source_ref) throw options.sourceMismatch();
3261
- const body = JSON.stringify(hostIngressBody(options.input, options.invalidEventTime));
3262
- let response2;
3263
- try {
3264
- response2 = await options.fetcher(options.endpoint, {
3265
- method: "POST",
3266
- ...options.redirect === void 0 ? {} : { redirect: options.redirect },
3267
- headers: { authorization: `Bearer ${options.credential}`, "content-type": "application/json" },
3268
- body,
3269
- signal: options.signal
3270
- });
3271
- } catch {
3272
- throw options.unavailable();
3273
- }
3274
- const responseBody = await responseJson(response2, options.invalidResponse);
3275
- if (!response2.ok) throw options.refused(response2.status, responseBody);
3276
- return options.acknowledgement(responseBody);
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;
3277
3298
  }
3278
-
3279
- // host/relay/src/relay-gateway-http.ts
3280
- var HOST_REGISTRATION_STATE_SCHEMA = "ceal.host_registration_state.v1";
3281
- var HOST_REGISTRATION_STATE_KEYS = Object.freeze([
3282
- "credential_generation",
3283
- "gateway_request_origin",
3284
- "host_access_token",
3285
- "host_installation_ref",
3286
- "host_source_ref",
3287
- "registration_attempt_ref",
3288
- "schema_version",
3289
- "status"
3290
- ]);
3291
- var HOST_REGISTRATION_STATE_MAX_BYTES = 65536;
3292
- var HostRelayGatewayError = class extends Error {
3293
- name = "HostRelayGatewayError";
3294
- code;
3295
- /** HTTP status of a Gateway refusal, when one answered. */
3296
- status;
3297
- /** The Gateway's own refusal word, when it named one; a bounded token, never its message. */
3298
- error_code;
3299
- /** The Gateway mint refusal's bounded continuity reason, when it named one; a fixed token, never its message. */
3300
- refusal_reason;
3301
- constructor(code, message, status, errorCode9, refusalReason) {
3302
- super(message);
3303
- this.code = code;
3304
- if (status !== void 0) this.status = status;
3305
- if (errorCode9 !== void 0) this.error_code = errorCode9;
3306
- if (refusalReason !== void 0) this.refusal_reason = refusalReason;
3307
- }
3308
- };
3309
- var GATEWAY_REFUSAL_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
3310
- async function gatewayRefusal(response2) {
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;
3311
3305
  try {
3312
- const body = await response2.json();
3313
- if (!isCealJsonRecord(body)) return {};
3314
- const code = typeof body.error_code === "string" && GATEWAY_REFUSAL_CODE_PATTERN.test(body.error_code) ? body.error_code : void 0;
3315
- 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;
3316
- return {
3317
- ...code === void 0 ? {} : { code },
3318
- ...reason === void 0 ? {} : { reason }
3319
- };
3306
+ value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(RELAY_PIPE_FRAME_BYTES, length + RELAY_PIPE_FRAME_BYTES)));
3320
3307
  } catch {
3321
- return {};
3308
+ throw new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is not JSON.");
3322
3309
  }
3310
+ return { value, rest: buffer.subarray(length + RELAY_PIPE_FRAME_BYTES) };
3323
3311
  }
3324
- function submitRefusalCode(body) {
3325
- if (!isCealJsonRecord(body) || typeof body.error_code !== "string" || !GATEWAY_REFUSAL_CODE_PATTERN.test(body.error_code)) return void 0;
3326
- return body.error_code;
3312
+ function validNonce(value) {
3313
+ return typeof value === "string" && RELAY_PIPE_NONCE_PATTERN.test(value);
3327
3314
  }
3328
- function refusalMessage(status, body) {
3329
- const record6 = isCealJsonRecord(body) ? body : {};
3330
- const code = typeof record6.error_code === "string" ? record6.error_code : "unknown";
3331
- const message = typeof record6.message === "string" ? `: ${record6.message}` : "";
3332
- return `Gateway Host ingress refused the submission (${status} ${code}${message}).`;
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
+ });
3333
3322
  }
3334
- function createHostRelayGatewayHttpPort(options) {
3335
- 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) {
3336
- throw new HostRelayGatewayError("configuration", "Host Relay Gateway binding is invalid.");
3337
- }
3338
- return Object.freeze({
3339
- async submitHostTurn(input, signal) {
3340
- 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 }])}.`);
3341
- const registration = await readHostRegistrationAuthority(options.registration_state_file, options.host_source_ref);
3342
- const endpoint = hostRelayGatewayEndpoint(registration.gateway_request_origin);
3343
- return await submitHostIngress({
3344
- input,
3345
- expected_host_source_ref: options.host_source_ref,
3346
- endpoint,
3347
- credential: registration.host_access_token,
3348
- fetcher: options.fetch ?? fetch,
3349
- signal,
3350
- redirect: "error",
3351
- sourceMismatch: () => new HostRelayGatewayError("payload_rejected", "Host Relay source binding changed."),
3352
- invalidEventTime: () => new HostRelayGatewayError("payload_rejected", "Host Relay submission has no event time."),
3353
- unavailable: () => new HostRelayGatewayError("gateway_unavailable", "Gateway Host ingress is unavailable."),
3354
- invalidResponse: () => new HostRelayGatewayError("invalid_response", "Gateway Host ingress response is not JSON."),
3355
- refused: (status, body) => new HostRelayGatewayError("gateway_refused", refusalMessage(status, body), status, submitRefusalCode(body)),
3356
- acknowledgement
3357
- });
3358
- },
3359
- async mintInvocationContext(input, signal) {
3360
- const registration = await readHostRegistrationAuthority(options.registration_state_file, options.host_source_ref);
3361
- let response2;
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) });
3343
+ };
3344
+ const waiter = {
3345
+ wait: () => new Promise((resolve, reject) => {
3346
+ let frame;
3362
3347
  try {
3363
- response2 = await (options.fetch ?? fetch)(hostRelayGatewayRoute(registration.gateway_request_origin, CEAL_HOST_INVOCATION_CONTEXT_PATH), {
3364
- method: "POST",
3365
- redirect: "error",
3366
- signal,
3367
- headers: { authorization: `Bearer ${registration.host_access_token}`, "content-type": "application/json" },
3368
- body: JSON.stringify({ schema_version: "ceal.gateway_host_invocation_context_request.v1", ...input })
3369
- });
3370
- } catch {
3371
- throw new HostRelayGatewayError("gateway_unavailable", "Gateway Host invocation context is unavailable.");
3372
- }
3373
- if (!response2.ok) {
3374
- const refusal = await gatewayRefusal(response2);
3375
- throw new HostRelayGatewayError(response2.status === 401 ? "credential" : "gateway_refused", `Gateway Host invocation context was refused (${response2.status}).`, response2.status, refusal.code, refusal.reason);
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;
3376
3352
  }
3377
- let value;
3378
- try {
3379
- value = await response2.json();
3380
- } catch {
3381
- throw new HostRelayGatewayError("invalid_response", "Gateway Host invocation context response is not JSON.");
3353
+ if (frame !== null) {
3354
+ buffer = frame.rest;
3355
+ resolve({ value: frame.value, rest: Buffer.alloc(0) });
3356
+ return;
3382
3357
  }
3383
- try {
3384
- return decodeCealHostInvocationAuthenticationResult(value);
3385
- } catch {
3386
- 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);
3387
3365
  }
3388
3366
  }
3389
- });
3390
- }
3391
- function hostRelayGatewayEndpoint(value) {
3392
- return hostRelayGatewayRoute(value, CEAL_HOST_INGRESS_PATH);
3393
- }
3394
- function hostRelayGatewayRoute(value, path) {
3395
- let parsed;
3396
- try {
3397
- parsed = new URL(value);
3398
- } catch {
3399
- throw new HostRelayGatewayError("configuration", "Host Relay Gateway origin is invalid.");
3400
- }
3401
- const hostname = parsed.hostname.toLowerCase().replace(/\.+$/u, "");
3402
- if (/\s|\0/u.test(value) || hostname === "" || !validPort(parsed) || !isExplicitLoopbackHttp(parsed, hostname) && !isSecureHttps(parsed)) {
3403
- 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 }])}.`);
3404
- }
3405
- if (hasOriginDecoration(value, parsed)) {
3406
- 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" }])}.`);
3407
- }
3408
- return `${parsed.origin}${path}`;
3409
- }
3410
- function isExplicitLoopbackHttp(parsed, hostname) {
3411
- return parsed.protocol === "http:" && parsed.port !== "" && (hostname === "127.0.0.1" || hostname === "[::1]");
3412
- }
3413
- function isSecureHttps(parsed) {
3414
- return parsed.protocol === "https:";
3415
- }
3416
- function validPort(parsed) {
3417
- if (parsed.port === "") return true;
3418
- const port = Number(parsed.port);
3419
- 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
+ };
3420
3382
  }
3421
- function hasOriginDecoration(value, parsed) {
3422
- const schemeEnd = value.indexOf("://");
3423
- const authority = schemeEnd < 0 ? "" : value.slice(schemeEnd + 3);
3424
- const suffixOffset = authority.search(/[\\/?#]/u);
3425
- const rawSuffix = suffixOffset < 0 ? "" : authority.slice(suffixOffset);
3426
- 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) };
3427
3390
  }
3428
- async function readHostRegistrationAuthority(path, expectedHostSourceRef) {
3429
- 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);
3430
3395
  try {
3431
- await assertOwnerPrivateDirectory(dirname2(path));
3432
- handle = await open4(path, O_RDONLY3 | O_NOFOLLOW2);
3433
- const stats = await handle.stat();
3434
- if (!stats.isFile() || !ownerPrivate3(stats)) throw new HostRelayGatewayError("credential", "Host Relay credential file is not owner-private.");
3435
- const raw = await handle.readFile("utf8");
3436
- 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}`)}.`);
3437
- let value;
3438
- try {
3439
- value = JSON.parse(raw);
3440
- } catch {
3441
- 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.");
3442
3399
  }
3443
- return decodeHostRegistrationAuthority(value, expectedHostSourceRef);
3444
- } catch (error) {
3445
- if (error instanceof HostRelayGatewayError) throw error;
3446
- 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();
3447
3413
  } finally {
3448
- await handle?.close();
3414
+ bound.clear();
3415
+ detach();
3449
3416
  }
3450
3417
  }
3451
- function decodeHostRegistrationAuthority(value, expectedHostSourceRef) {
3452
- const keys = HOST_REGISTRATION_STATE_KEYS;
3453
- const fieldCount = isCealJsonRecord(value) ? Object.keys(value).length : 0;
3454
- const missing = isCealJsonRecord(value) ? keys.filter((key) => !Object.hasOwn(value, key)).length : keys.length;
3455
- 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" }])}.`);
3456
- 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>" }])}.`);
3457
- 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" }])}.`);
3458
- 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")}.`);
3459
- return { gateway_request_origin: value.gateway_request_origin, host_access_token: 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;
3460
3423
  }
3461
- function validRegistrationIdentity(value, expectedHostSourceRef) {
3462
- 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;
3463
- }
3464
- function validRegistrationCredential(value) {
3465
- return Number.isSafeInteger(value.credential_generation) && Number(value.credential_generation) > 0 && validHostAccessToken(value.host_access_token);
3466
- }
3467
- function validHostAccessToken(value) {
3468
- return typeof value === "string" && CEAL_HOST_ACCESS_TOKEN_PATTERN.test(value);
3469
- }
3470
- function validGatewayOrigin(value) {
3471
- try {
3472
- if (typeof value !== "string") return false;
3473
- hostRelayGatewayEndpoint(value);
3474
- return true;
3475
- } catch {
3476
- return false;
3477
- }
3478
- }
3479
- async function assertOwnerPrivateDirectory(path) {
3480
- const stats = await lstat2(path);
3481
- if (!stats.isDirectory() || stats.isSymbolicLink() || !ownerPrivate3(stats)) throw new HostRelayGatewayError("credential", "Host Relay credential directory is not owner-private.");
3482
- }
3483
- function ownerPrivate3(stats) {
3484
- if (process.platform === "win32") return true;
3485
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
3486
- return stats.uid === uid && (stats.mode & 63) === 0;
3424
+ function isRecord2(value) {
3425
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3487
3426
  }
3488
- function acknowledgement(value) {
3489
- const fields = isCealJsonRecord(value) ? value : {};
3490
- if (!isCealJsonRecord(value) || value.ok !== true || value.status !== "accepted" || typeof value.request_ref !== "string" || !SAFE_HOST_REF.test(value.request_ref) || !isHostGatewayAcknowledgementDisposition(value.disposition)) {
3491
- 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" }])}.`);
3492
- }
3493
- 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;
3494
3429
  }
3495
3430
 
3496
3431
  // host/relay/src/relay-ipc.ts
3497
- 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";
3498
3433
  import { createConnection, createServer } from "node:net";
3499
- import { dirname as dirname3, join as join6 } from "node:path";
3500
-
3501
- // host/relay/src/current-prompt-custody.ts
3502
- import { createHash as createHash5 } from "node:crypto";
3503
- var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
3504
- var MAX_CURRENT_PROMPTS = 1024;
3505
- var HostCurrentPromptCustody = class {
3506
- #records = /* @__PURE__ */ new Map();
3507
- #consumed = /* @__PURE__ */ new Map();
3508
- #now;
3509
- constructor(now = Date.now) {
3510
- this.#now = now;
3511
- }
3512
- register(identity, prompt, expiresAt) {
3513
- const bytes2 = Buffer.byteLength(prompt, "utf8");
3514
- const expiry = Date.parse(expiresAt);
3515
- if (bytes2 === 0 || bytes2 > HOST_CURRENT_PROMPT_MAX_BYTES || !Number.isFinite(expiry) || expiry <= this.#now()) throw new TypeError("current_prompt_unavailable");
3516
- if (hostProducerPromptRef(createHash5("sha256").update(prompt, "utf8").digest("hex")) !== identity.prompt_ref) throw new TypeError("current_prompt_unavailable");
3517
- this.#purge();
3518
- const key = promptKey(identity);
3519
- if (this.#consumed.has(key)) return;
3520
- if (!this.#records.has(key) && this.#records.size + this.#consumed.size >= MAX_CURRENT_PROMPTS) throw new TypeError("current_prompt_unavailable");
3521
- this.#records.set(key, { prompt, expires_at_ms: expiry });
3522
- }
3523
- /** Transfer this turn's prompt into at most one authenticated invocation. */
3524
- take(identity) {
3525
- this.#purge();
3526
- const key = promptKey(identity);
3527
- const prompt = this.#records.get(key)?.prompt;
3528
- if (prompt !== void 0) {
3529
- const expiresAt = this.#records.get(key)?.expires_at_ms;
3530
- this.#records.delete(key);
3531
- if (expiresAt !== void 0) this.#consumed.set(key, expiresAt);
3532
- }
3533
- return prompt;
3534
- }
3535
- #purge() {
3536
- const now = this.#now();
3537
- for (const [key, value] of this.#records) if (value.expires_at_ms <= now) this.#records.delete(key);
3538
- for (const [key, expiresAt] of this.#consumed) if (expiresAt <= now) this.#consumed.delete(key);
3539
- }
3540
- };
3541
- function promptKey(value) {
3542
- return [value.producer_id, value.producer_installation_ref, value.local_session_ref, value.local_turn_ref, value.prompt_ref].join("\0");
3543
- }
3544
-
3545
- // host/relay/src/relay-ipc.ts
3546
- var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
3547
- "schema_version",
3548
- "producer_id",
3549
- "producer_installation_ref",
3550
- "local_session_ref",
3551
- "local_turn_ref",
3552
- "local_turn_aliases",
3553
- "prompt_ref",
3554
- "request_ref",
3555
- "bound_at",
3556
- "host_source_ref",
3557
- "host_session_ref",
3558
- "host_turn_ref",
3559
- "host_input_ref"
3560
- ]);
3434
+ import { dirname as dirname5, join as join5 } from "node:path";
3561
3435
  var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
3562
- var HOST_RELAY_BINDING_QUERY_SCHEMA = "ceal.host_relay_binding_query.v1";
3563
- var HOST_RELAY_BINDING_RESULT_SCHEMA = "ceal.host_relay_binding_result.v1";
3564
- var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v1";
3565
- var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v1";
3566
- var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
3567
- 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";
3568
3438
  var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
3569
- var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
3570
- "context_stale",
3571
- "context_expired",
3572
- "host_source_refused",
3573
- "host_credential_refused",
3574
- "gateway_unavailable",
3575
- "gateway_refused",
3576
- "gateway_response_invalid",
3577
- "gateway_rate_limited",
3578
- "producer_frame_invalid",
3579
- "deadline",
3580
- "cancelled",
3581
- "mint_failed",
3582
- "relay_failed",
3583
- "request_authority_unavailable",
3584
- "request_missing",
3585
- "authority_mismatch",
3586
- "host_source_mismatch",
3587
- "host_session_mismatch",
3588
- "host_turn_mismatch",
3589
- "host_input_mismatch",
3590
- "host_prompt_mismatch"
3591
- ]);
3592
- var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
3593
- "context_stale",
3594
- "context_expired",
3595
- "host_source_refused",
3596
- "host_credential_refused",
3597
- "gateway_unavailable",
3598
- "gateway_rate_limited",
3599
- "gateway_refused",
3600
- "gateway_response_invalid",
3601
- "producer_frame_invalid"
3602
- ]);
3603
- var HostRelayInvocationMintRefusal = class extends Error {
3604
- reason;
3605
- constructor(reason) {
3606
- super(reason);
3607
- this.name = "HostRelayInvocationMintRefusal";
3608
- this.reason = reason;
3609
- }
3610
- };
3611
- var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
3612
3439
  var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
3613
3440
  "permission_denied",
3614
3441
  "socket_missing",
@@ -3631,26 +3458,14 @@ var HostRelayIpcError = class extends Error {
3631
3458
  };
3632
3459
  async function startHostRelayIpcServer(options) {
3633
3460
  return withRelayEndpointLock(options.endpoint, async () => {
3634
- if (process.platform === "win32") {
3635
- await assertEndpointVacant(options.endpoint);
3636
- } else {
3637
- await assertTrustedEndpointParent(options.endpoint);
3638
- await removeStaleRelayEndpoint(options.endpoint, options.testHooks);
3639
- }
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);
3640
3465
  const active = /* @__PURE__ */ new Map();
3641
3466
  const sockets = /* @__PURE__ */ new Set();
3642
3467
  const quarantine = options.quarantine ?? options.admission.quarantine;
3643
- const currentPrompts = options.currentPrompts ?? new HostCurrentPromptCustody();
3644
- const server = createServer((socket) => serveSocket(socket, options, currentPrompts, quarantine, active, sockets));
3645
- if (process.platform === "win32") {
3646
- try {
3647
- await listen(server, options.endpoint);
3648
- } catch (error) {
3649
- await abandonRelayServer(server, sockets);
3650
- throw error;
3651
- }
3652
- return { endpoint: options.endpoint, close: () => closeServer(server, sockets, options.endpoint) };
3653
- }
3468
+ const server = createServer((socket) => serveSocket(socket, options, quarantine, active, sockets, Buffer.alloc(0), lastSeenRequestBySession));
3654
3469
  const staging = stagingRelayEndpoint(options.endpoint);
3655
3470
  await listenStagingRelayEndpoint(server, sockets, staging);
3656
3471
  let owned;
@@ -3674,39 +3489,105 @@ function encodeHostRelayIpcResponse(response2) {
3674
3489
  body.copy(result2, HOST_RELAY_IPC_LENGTH_BYTES);
3675
3490
  return result2;
3676
3491
  }
3677
- function serveSocket(socket, options, currentPrompts, quarantine, active, sockets) {
3678
- const state = { buffer: Buffer.alloc(0), closed: false, tail: Promise.resolve(), abort: new AbortController() };
3679
- sockets.add(socket);
3680
- socket.on("data", (chunk) => {
3681
- state.buffer = Buffer.concat([state.buffer, Buffer.from(chunk)]);
3682
- state.tail = state.tail.then(() => drainFrames(socket, state, options, currentPrompts, quarantine, active)).catch(() => {
3683
- writeResponse(socket, errorResponse("relay_unavailable"));
3684
- 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);
3685
3550
  });
3686
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)));
3687
3564
  socket.once("error", () => closeConnection(socket, state, active, sockets));
3688
3565
  socket.once("close", () => closeConnection(socket, state, active, sockets));
3689
3566
  socket.once("end", () => {
3690
- if (state.buffer.length > 0 && currentPromptWire(state.buffer)) {
3691
- writeCurrentPromptResponse(socket, "current_prompt_unavailable");
3692
- socket.end();
3693
- } 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(() => {
3694
3568
  writeResponse(socket, errorResponse("frame_truncated"));
3695
3569
  socket.end();
3696
3570
  });
3697
3571
  else socket.end();
3698
3572
  });
3573
+ if (initial.length > 0) receiveRelaySocketBytes(socket, state, options, quarantine, active, Buffer.alloc(0));
3699
3574
  }
3700
- async function drainFrames(socket, state, options, currentPrompts, quarantine, active) {
3701
- while (!state.closed && state.buffer.length >= HOST_RELAY_IPC_LENGTH_BYTES && await processBufferedRequest(socket, state, options, currentPrompts, quarantine, active)) {
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
+ });
3581
+ }
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)) {
3702
3584
  }
3703
3585
  }
3704
- async function processBufferedRequest(socket, state, options, currentPrompts, quarantine, active) {
3586
+ async function processBufferedRequest(socket, state, options, quarantine, active) {
3705
3587
  const length = state.buffer.readUInt32BE(0);
3706
- const promptWire = currentPromptWire(state.buffer);
3707
- const maximum = promptWire ? HOST_CURRENT_PROMPT_MAX_BYTES + 4096 : HOST_PRODUCER_MAX_JSON_BYTES;
3588
+ const maximum = HOST_PRODUCER_MAX_JSON_BYTES;
3708
3589
  if (length === 0 || length > maximum) {
3709
- await refuseInvalidBufferedRequest(socket, state.buffer, length, promptWire, quarantine);
3590
+ await refuseInvalidBufferedRequest(socket, state.buffer, length, quarantine);
3710
3591
  state.buffer = Buffer.alloc(0);
3711
3592
  socket.end();
3712
3593
  return false;
@@ -3718,41 +3599,20 @@ async function processBufferedRequest(socket, state, options, currentPrompts, qu
3718
3599
  try {
3719
3600
  request2 = decodeIpcRequest(encoded);
3720
3601
  } catch (error) {
3721
- await refuseMalformedRequest(socket, encoded, promptWire, error, quarantine);
3722
- return true;
3723
- }
3724
- if (request2.schema_version === HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA) {
3725
- try {
3726
- currentPrompts.register(request2, request2.current_prompt, request2.context_expires_at);
3727
- writeCurrentPromptResponse(socket, "registered");
3728
- } catch {
3729
- writeCurrentPromptResponse(socket, "current_prompt_unavailable");
3730
- }
3602
+ await refuseMalformedRequest(socket, encoded, error, quarantine);
3731
3603
  return true;
3732
3604
  }
3733
- const answered = await processHostQuery(socket, request2, options, currentPrompts, state.abort.signal);
3734
- if (answered === "closed") {
3735
- socket.end();
3736
- return false;
3737
- }
3605
+ const answered = await processHostQuery(socket, state, options, request2);
3738
3606
  if (answered === "answered") return true;
3739
3607
  await processProducerRequest(socket, state, request2, encoded, options.admission, quarantine, active);
3740
3608
  return true;
3741
3609
  }
3742
- async function refuseInvalidBufferedRequest(socket, rawBuffer, length, promptWire, quarantine) {
3743
- if (promptWire) {
3744
- writeCurrentPromptResponse(socket, "current_prompt_unavailable");
3745
- return;
3746
- }
3610
+ async function refuseInvalidBufferedRequest(socket, rawBuffer, length, quarantine) {
3747
3611
  const code = length === 0 ? "invalid_frame_length" : "frame_too_large";
3748
3612
  await quarantine.retainMalformed(rawBuffer.subarray(0, Math.min(rawBuffer.length, length + HOST_RELAY_IPC_LENGTH_BYTES)), code);
3749
3613
  writeResponse(socket, errorResponse(code));
3750
3614
  }
3751
- async function refuseMalformedRequest(socket, encoded, promptWire, error, quarantine) {
3752
- if (promptWire) {
3753
- writeCurrentPromptResponse(socket, "current_prompt_unavailable");
3754
- return;
3755
- }
3615
+ async function refuseMalformedRequest(socket, encoded, error, quarantine) {
3756
3616
  const code = error instanceof HostProducerFrameError ? error.code : "invalid_frame_shape";
3757
3617
  await quarantine.retainMalformed(encoded, code);
3758
3618
  writeResponse(socket, errorResponse(code));
@@ -3768,29 +3628,19 @@ async function processProducerRequest(socket, state, request2, encoded, admissio
3768
3628
  }
3769
3629
  writeResponse(socket, admissionResponse(await admission.admit(request2)));
3770
3630
  }
3771
- async function processHostQuery(socket, request2, options, currentPrompts, signal) {
3772
- if (request2.schema_version === HOST_RELAY_BINDING_QUERY_SCHEMA) {
3773
- let result2;
3774
- try {
3775
- result2 = await options.bindings.query(bindingQueryFromRequest(request2));
3776
- } catch {
3777
- return "closed";
3778
- }
3779
- writeBindingResponse(socket, result2);
3780
- return "answered";
3781
- }
3782
- if (request2.schema_version === HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) {
3783
- let response2;
3784
- const resolve = (invocationSignal) => resolveInvocationContext(request2, options.admission, options.bindings, currentPrompts, options.mintInvocationContext, invocationSignal);
3785
- try {
3786
- 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);
3787
- } catch {
3788
- response2 = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: "relay_failed" };
3789
- }
3790
- writeInvocationContextResponse(socket, response2);
3791
- 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" };
3792
3640
  }
3793
- 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";
3794
3644
  }
3795
3645
  function claimProducer(socket, state, frame, active) {
3796
3646
  const key = producerKey(frame);
@@ -3814,154 +3664,9 @@ function closeConnection(socket, state, active, sockets) {
3814
3664
  function writeResponse(socket, response2) {
3815
3665
  if (!socket.destroyed) socket.write(encodeHostRelayIpcResponse(response2));
3816
3666
  }
3817
- function writeBindingResponse(socket, result2) {
3818
- const response2 = { schema_version: HOST_RELAY_BINDING_RESULT_SCHEMA, ...result2 };
3819
- if (!socket.destroyed) socket.write(encodeLengthPrefixed(response2));
3820
- }
3821
3667
  function writeInvocationContextResponse(socket, response2) {
3822
3668
  if (!socket.destroyed) socket.write(encodeLengthPrefixed(response2));
3823
3669
  }
3824
- function writeCurrentPromptResponse(socket, status) {
3825
- if (!socket.destroyed) socket.write(encodeLengthPrefixed({ schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status }));
3826
- }
3827
- async function resolveInvocationContext(request2, admission, bindings, currentPrompts, mint, signal) {
3828
- if (mint === void 0) throw new HostRelayIpcError("relay_unavailable");
3829
- const query = bindingQueryFromRequest(request2);
3830
- const expectedSequences = pendingAdmissionSequences(await admission.snapshot(), query);
3831
- const binding = await waitForRequestBinding(query, admission, bindings, signal);
3832
- if (binding.status !== "ready") return await unboundInvocationContextRefusal(admission, await admission.snapshot(), query, expectedSequences) ?? { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "no_binding" };
3833
- 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 });
3834
- let result2;
3835
- try {
3836
- 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);
3837
- } catch (error) {
3838
- return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: error instanceof HostRelayInvocationMintRefusal ? error.reason : "mint_failed" };
3839
- }
3840
- return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "ready", result: result2 };
3841
- }
3842
- async function unboundInvocationContextRefusal(admission, snapshot, query, expectedSequences) {
3843
- if (expectedSequences.length > 0) {
3844
- const refusal = snapshot.installations[`${query.producer_id}\0${query.producer_installation_ref}`]?.last_refusal;
3845
- if (refusal !== void 0 && expectedSequences.includes(refusal.sequence)) {
3846
- const reason2 = mintReasonForDeliveryRefusal(refusal.code, refusal.diagnostic);
3847
- if (reason2 !== void 0) return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: reason2 };
3848
- }
3849
- }
3850
- const selector = Object.freeze({
3851
- producer_id: query.producer_id,
3852
- producer_installation_ref: query.producer_installation_ref,
3853
- local_session_ref: query.local_session_ref,
3854
- local_turn_ref: query.local_turn_ref,
3855
- local_turn_aliases: Object.freeze([])
3856
- });
3857
- const match = await admission.quarantine.findNewestAttributedMatch(selector);
3858
- if (match === null) return null;
3859
- const reason = mintReasonForQuarantinedMatch(match);
3860
- if (reason === void 0) return null;
3861
- return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason };
3862
- }
3863
- function mintReasonForDeliveryRefusal(code, diagnostic) {
3864
- if (code !== "gateway_refused") return void 0;
3865
- if (diagnostic === void 0) return "gateway_refused";
3866
- return mintReasonForWireCause(projectCealHostAuthenticationCause(diagnostic));
3867
- }
3868
- function mintReasonForQuarantinedMatch(match) {
3869
- if (match === null) return void 0;
3870
- if (match.reason !== "gateway_refused") return "producer_frame_invalid";
3871
- const diagnostic = match.attribution?.diagnostic;
3872
- if (diagnostic === void 0) return void 0;
3873
- return mintReasonForWireCause(projectCealHostAuthenticationCause(diagnostic));
3874
- }
3875
- function mintReasonForWireCause(cause) {
3876
- if (isDirectWireCause(cause)) return cause;
3877
- if (cause === "relay_failed" || cause === "relay_unavailable" || cause === "options_invalid") return "relay_failed";
3878
- return "mint_failed";
3879
- }
3880
- function isDirectWireCause(cause) {
3881
- return isVocabularyWord(HOST_RELAY_DIRECT_WIRE_MINT_REASONS, cause);
3882
- }
3883
- function pendingAdmissionSequences(snapshot, query) {
3884
- const sequences = [];
3885
- for (const entry2 of snapshot.entries) {
3886
- const selector = extractHostBindingSelector(entry2.frame);
3887
- if (selector === null || !relayRequestBindingMatchesQuery(query, selector)) continue;
3888
- sequences.push(entry2.sequence);
3889
- }
3890
- return sequences;
3891
- }
3892
- async function waitForRequestBinding(query, admission, bindings, signal) {
3893
- let binding = await bindings.query(query);
3894
- if (binding.status === "ready") return binding;
3895
- while (await admission.hasPendingRequestBinding(query)) {
3896
- const cycle = new AbortController();
3897
- const cancelCycle = () => cycle.abort(signal.reason);
3898
- if (signal.aborted) cancelCycle();
3899
- else signal.addEventListener("abort", cancelCycle, { once: true });
3900
- try {
3901
- const changed = Promise.race([
3902
- bindings.waitForBinding(query, cycle.signal).then((result3) => ({ kind: "bound", result: result3 })),
3903
- admission.waitForDrainChange(cycle.signal).then(() => ({ kind: "drained" }))
3904
- ]).then((result3) => result3, (error) => ({ kind: "cancelled", error }));
3905
- binding = await bindings.query(query);
3906
- if (binding.status === "ready") return binding;
3907
- if (!await admission.hasPendingRequestBinding(query)) return await bindings.query(query);
3908
- const result2 = await changed;
3909
- if (result2.kind === "cancelled") throw result2.error;
3910
- } finally {
3911
- signal.removeEventListener("abort", cancelCycle);
3912
- cycle.abort("wait_cycle_complete");
3913
- }
3914
- binding = await bindings.query(query);
3915
- if (binding.status === "ready") return binding;
3916
- }
3917
- return await bindings.query(query);
3918
- }
3919
- async function observeInvocationMint(resolve, callerSignal, timeoutMs, scheduleDeadline, observe) {
3920
- const started = process.hrtime.bigint();
3921
- const controller2 = new AbortController();
3922
- let cancellation = null;
3923
- let outcome = "failed";
3924
- let reason;
3925
- const cancel = (cause) => {
3926
- if (cancellation === null) {
3927
- cancellation = cause;
3928
- controller2.abort(cause);
3929
- }
3930
- };
3931
- const callerCancelled = () => cancel("caller_cancelled");
3932
- const cancelled = new Promise((_resolve, reject) => controller2.signal.addEventListener("abort", () => reject(new Error("invocation mint cancelled")), { once: true }));
3933
- const clearDeadline = timeoutMs === void 0 ? () => void 0 : (scheduleDeadline ?? scheduleInvocationMintDeadline)(() => cancel("deadline"), timeoutMs);
3934
- if (callerSignal.aborted) callerCancelled();
3935
- else callerSignal.addEventListener("abort", callerCancelled, { once: true });
3936
- try {
3937
- const response2 = await Promise.race([resolve(controller2.signal), cancelled]);
3938
- ({ outcome, reason } = invocationMintObservationForResponse(response2));
3939
- return response2;
3940
- } catch {
3941
- ({ outcome, reason } = invocationMintObservationForCancellation(cancellation));
3942
- return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: reason ?? "relay_failed" };
3943
- } finally {
3944
- clearDeadline();
3945
- callerSignal.removeEventListener("abort", callerCancelled);
3946
- observe?.({ outcome, elapsed_ms: Number((process.hrtime.bigint() - started) / 1000000n), ...reason === void 0 ? {} : { reason } });
3947
- }
3948
- }
3949
- function invocationMintObservationForResponse(response2) {
3950
- if (response2.status === "no_binding") return { outcome: "no_binding" };
3951
- if (response2.status === "ready") return { outcome: "completed" };
3952
- if (response2.reason === "deadline") return { outcome: "deadline", reason: "deadline" };
3953
- if (response2.reason === "cancelled") return { outcome: "caller_cancelled", reason: "cancelled" };
3954
- return { outcome: "failed", reason: response2.reason };
3955
- }
3956
- function invocationMintObservationForCancellation(cancellation) {
3957
- if (cancellation === "deadline") return { outcome: "deadline", reason: "deadline" };
3958
- if (cancellation === "caller_cancelled") return { outcome: "caller_cancelled", reason: "cancelled" };
3959
- return { outcome: "failed", reason: "relay_failed" };
3960
- }
3961
- function scheduleInvocationMintDeadline(expire, milliseconds) {
3962
- const timeout = setTimeout(expire, milliseconds);
3963
- return () => clearTimeout(timeout);
3964
- }
3965
3670
  function admissionResponse(result2) {
3966
3671
  return { schema_version: HOST_RELAY_IPC_RESPONSE_SCHEMA, ...result2 };
3967
3672
  }
@@ -3971,45 +3676,45 @@ function errorResponse(code) {
3971
3676
  function producerKey(frame) {
3972
3677
  return `${frame.producer_id}\0${frame.producer_installation_ref}`;
3973
3678
  }
3974
- function record4(value) {
3679
+ function record3(value) {
3975
3680
  return value !== null && typeof value === "object" && !Array.isArray(value);
3976
3681
  }
3977
3682
  function decodeIpcRequest(input) {
3978
- 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);
3979
3684
  const local = decodeLocalRequest(value);
3980
3685
  if (local !== void 0) return local;
3981
3686
  return decodeHostProducerFrame(input);
3982
3687
  }
3983
3688
  function decodeLocalRequest(value) {
3984
- if (!record4(value)) return void 0;
3985
- if (value.schema_version === HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA) {
3986
- if (!validCurrentPromptRegisterRequest(value)) throw new HostProducerFrameError("invalid_frame_shape");
3987
- return value;
3988
- }
3989
- if (value.schema_version === HOST_RELAY_BINDING_QUERY_SCHEMA) {
3990
- if (!validBindingQueryRequest(value)) throw new HostProducerFrameError("invalid_frame_shape");
3991
- return value;
3992
- }
3689
+ if (!record3(value)) return void 0;
3993
3690
  if (value.schema_version === HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) {
3994
3691
  if (!validInvocationContextQueryRequest(value)) throw new HostProducerFrameError("invalid_frame_shape");
3995
3692
  return value;
3996
3693
  }
3997
3694
  return void 0;
3998
3695
  }
3999
- function validCurrentPromptRegisterRequest(value) {
4000
- 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);
4001
- }
4002
- function validBindingQueryRequest(value) {
4003
- return validQueryIdentity(value, HOST_RELAY_BINDING_QUERY_SCHEMA) && exactKeys4(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref"]);
4004
- }
4005
- function bindingQueryFromRequest(value) {
4006
- 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 };
4007
- }
4008
3696
  function validInvocationContextQueryRequest(value) {
4009
- 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));
4010
3715
  }
4011
3716
  function validQueryIdentity(value, schema) {
4012
- 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);
4013
3718
  }
4014
3719
  function validVisibleOutputBudget(value) {
4015
3720
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
@@ -4033,27 +3738,13 @@ function encodeLengthPrefixed(value) {
4033
3738
  body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
4034
3739
  return output;
4035
3740
  }
4036
- function currentPromptWire(value) {
4037
- 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"));
4038
- }
4039
- function exactKeys4(value, required, optional = []) {
3741
+ function exactKeys3(value, required, optional = []) {
4040
3742
  const keys = Object.keys(value);
4041
3743
  return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
4042
3744
  }
4043
- function safeRef3(value) {
3745
+ function safeRef2(value) {
4044
3746
  return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
4045
3747
  }
4046
- function validTimestamp2(value) {
4047
- return typeof value === "string" && Number.isFinite(Date.parse(value));
4048
- }
4049
- async function assertEndpointVacant(endpoint) {
4050
- try {
4051
- await lstat3(endpoint);
4052
- throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is already present.");
4053
- } catch (error) {
4054
- if (errorCode8(error) !== "ENOENT") throw error;
4055
- }
4056
- }
4057
3748
  function listen(server, endpoint) {
4058
3749
  return new Promise((resolve, reject) => {
4059
3750
  server.once("error", reject);
@@ -4066,7 +3757,7 @@ function listen(server, endpoint) {
4066
3757
  async function closeServer(server, sockets, endpoint) {
4067
3758
  for (const socket of sockets) socket.destroy();
4068
3759
  await new Promise((resolve) => server.close(() => resolve()));
4069
- if (process.platform !== "win32") await unlink2(endpoint).catch(() => void 0);
3760
+ if (process.platform !== "win32") await unlink3(endpoint).catch(() => void 0);
4070
3761
  }
4071
3762
  var relayEndpointLocks = /* @__PURE__ */ new Map();
4072
3763
  function withRelayEndpointLock(endpoint, run) {
@@ -4086,7 +3777,7 @@ function currentUid(fallback) {
4086
3777
  return typeof process.geteuid === "function" ? process.geteuid() : fallback;
4087
3778
  }
4088
3779
  async function assertTrustedEndpointParent(endpoint) {
4089
- const parent = dirname3(endpoint);
3780
+ const parent = dirname5(endpoint);
4090
3781
  let stats;
4091
3782
  try {
4092
3783
  stats = await lstat3(parent);
@@ -4111,7 +3802,7 @@ function probeRelayEndpoint(endpoint) {
4111
3802
  timer.unref?.();
4112
3803
  socket.once("connect", () => finish("live"));
4113
3804
  socket.once("error", (error) => {
4114
- const code = errorCode8(error);
3805
+ const code = errorCode7(error);
4115
3806
  if (code === "ECONNREFUSED") finish("stale");
4116
3807
  else if (code === "ENOENT") finish("missing");
4117
3808
  else finish("ambiguous");
@@ -4146,7 +3837,7 @@ async function readEndpointStats(endpoint) {
4146
3837
  try {
4147
3838
  return await lstat3(endpoint);
4148
3839
  } catch (error) {
4149
- if (errorCode8(error) === "ENOENT") return null;
3840
+ if (errorCode7(error) === "ENOENT") return null;
4150
3841
  throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint identity is ambiguous; leaving it untouched.");
4151
3842
  }
4152
3843
  }
@@ -4155,17 +3846,17 @@ function requireSameEndpointIdentity(current, owned, message) {
4155
3846
  }
4156
3847
  async function unlinkRelayEndpoint(endpoint) {
4157
3848
  try {
4158
- await unlink2(endpoint);
3849
+ await unlink3(endpoint);
4159
3850
  } catch (error) {
4160
- if (errorCode8(error) !== "ENOENT") throw error;
3851
+ if (errorCode7(error) !== "ENOENT") throw error;
4161
3852
  }
4162
3853
  }
4163
3854
  var relayStagingSequence = 0;
4164
3855
  function stagingRelayEndpoint(canonical) {
4165
- const parent = dirname3(canonical);
3856
+ const parent = dirname5(canonical);
4166
3857
  const pid = process.pid.toString(36);
4167
3858
  const sequence = (relayStagingSequence++ % (36 * 36)).toString(36);
4168
- return join6(parent, `.r${pid}${sequence}`);
3859
+ return join5(parent, `.r${pid}${sequence}`);
4169
3860
  }
4170
3861
  async function readStagingRelayEndpointIdentity(staging) {
4171
3862
  let created;
@@ -4222,8 +3913,121 @@ async function abandonRelayServer(server, sockets) {
4222
3913
  server.close(() => resolve());
4223
3914
  });
4224
3915
  }
4225
- function errorCode8(error) {
4226
- 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);
4227
4031
  }
4228
4032
 
4229
4033
  // host/relay/src/host-relay-runtime.ts
@@ -4239,16 +4043,24 @@ var HostRelayConfigError = class extends Error {
4239
4043
  this.code = code;
4240
4044
  }
4241
4045
  };
4242
- async function readHostRelayConfig(path) {
4046
+ async function readHostRelayConfig(path, platform = process.platform, custody = {}) {
4243
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")}.`);
4244
4048
  let handle;
4245
4049
  try {
4246
- await assertOwnerPrivateDirectory2(dirname4(path));
4247
- handle = await open5(path, O_RDONLY4 | O_NOFOLLOW3);
4248
- const stats = await handle.stat();
4249
- if (!stats.isFile() || !ownerPrivate4(stats)) throw new HostRelayConfigError("config_not_private", "Host Relay config file is not owner-private.");
4250
- 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}`)}.`);
4251
- 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
+ }
4252
4064
  assertNoDuplicateJsonKeys(text);
4253
4065
  let value;
4254
4066
  try {
@@ -4256,114 +4068,236 @@ async function readHostRelayConfig(path) {
4256
4068
  } catch {
4257
4069
  throw new HostRelayConfigError("invalid_json", "Host Relay config is not JSON.");
4258
4070
  }
4259
- const config = decodeHostRelayConfig(value);
4071
+ const decoded = decodeHostRelayConfig(value);
4072
+ const config = resolveRelayIpcEndpoint(decoded);
4260
4073
  validateHostRelaySourcePaths(config);
4261
4074
  return config;
4262
4075
  } catch (error) {
4263
- if (error instanceof HostRelayConfigError) throw error;
4264
- throw new HostRelayConfigError("config_unavailable", "Host Relay config is unavailable.");
4076
+ throw relayConfigReadError(error);
4265
4077
  } finally {
4266
4078
  await handle?.close();
4267
4079
  }
4268
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
+ }
4269
4112
  function validateHostRelaySourcePaths(config) {
4270
4113
  try {
4271
- assertHostRelaySourcePaths(dirname4(config.registration_state_file), config);
4114
+ assertHostRelaySourcePaths(dirname7(config.registration_state_file), config);
4272
4115
  } catch (error) {
4273
4116
  throw new HostRelayConfigError("invalid_config", error instanceof Error ? error.message : "Host Relay source tuple is invalid.");
4274
4117
  }
4275
4118
  }
4276
- function decodeHostRelayConfig(value) {
4277
- const required = ["schema_version", "registration_state_file", "host_source_ref", "state_root", "ipc_endpoint"];
4278
- const optional = ["drain_interval_ms", "gateway_timeout_ms"];
4279
- if (!record5(value) || !exactOptionalKeys2(value, required, optional) || !validConfigIdentity(value) || !validConfigRuntime(value)) {
4280
- 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))}.`);
4281
4125
  }
4282
4126
  return {
4283
4127
  schema_version: HOST_RELAY_CONFIG_SCHEMA,
4284
4128
  registration_state_file: value.registration_state_file,
4285
4129
  host_source_ref: value.host_source_ref,
4286
4130
  state_root: value.state_root,
4287
- ipc_endpoint: value.ipc_endpoint,
4288
- drain_interval_ms: value.drain_interval_ms ?? 250,
4289
- 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
4290
4133
  };
4291
4134
  }
4292
- function validConfigIdentity(value) {
4293
- 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);
4137
+ }
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;
4294
4156
  }
4295
- function validConfigRuntime(value) {
4296
- 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);
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);
4297
4159
  }
4298
4160
  var HOST_RELAY_INVOCATION_MINT_LOG_TAG = "[host-relay-invocation-mint]";
4299
- 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
+ }
4300
4171
  async function serveHostRelay(config, signal, dependencies = {}) {
4301
- const admission = new RelayAdmissionStore(join7(config.state_root, "admission"));
4302
- const bindings = new RelayRequestBindingStore(join7(config.state_root, "bindings"));
4303
- const gateway = (dependencies.createGateway ?? createHostRelayGatewayHttpPort)({ registration_state_file: config.registration_state_file, host_source_ref: config.host_source_ref });
4304
- 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) => {
4305
- await bindings.bind(binding);
4306
- } });
4172
+ const admission = new RelayAdmissionStore(join6(config.state_root, "admission"));
4307
4173
  let server;
4308
4174
  try {
4309
- server = await startHostRelayRuntimeIpc(config, admission, bindings, gateway, dependencies);
4310
- while (!signal.aborted) {
4311
- const round = await admission.drainRound((input) => bridge.deliver(input), { now_ms: (dependencies.now ?? Date.now)(), host_source_ref: config.host_source_ref });
4312
- if (round.delivered.length === 0 && round.refused.length === 0) await (dependencies.wait ?? ((milliseconds, waitSignal) => waitForAbort(waitSignal, milliseconds)))(config.drain_interval_ms, signal);
4313
- }
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;
4314
4181
  } finally {
4315
4182
  await server?.close();
4316
4183
  }
4317
4184
  }
4318
- 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) {
4319
4189
  return (dependencies.startIpc ?? startHostRelayIpcServer)({
4320
4190
  endpoint: config.ipc_endpoint,
4321
4191
  admission,
4322
- bindings,
4323
- mintInvocationContext: (input, invocationSignal) => mintInvocationContext(gateway, input, invocationSignal),
4324
- invocationMintTimeoutMs: config.gateway_timeout_ms,
4325
- scheduleInvocationMintDeadline: dependencies.scheduleInvocationMintDeadline ?? scheduleInvocationMintDeadline2,
4326
- 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
+ }
4327
4207
  });
4328
4208
  }
4329
- async function mintInvocationContext(gateway, input, signal) {
4209
+ async function mintInvocationContextAtGateway(config, input, signal, fetchGateway) {
4330
4210
  try {
4331
- 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);
4332
4213
  } catch (error) {
4333
- const cancellation = signal.reason === "deadline" ? "deadline" : signal.reason === "caller_cancelled" ? "caller_cancelled" : null;
4334
- throw new HostRelayInvocationMintRefusal(invocationMintRefusalReason(error, cancellation));
4214
+ return gatewayMintFailure(signal, error);
4335
4215
  }
4336
4216
  }
4337
- function invocationMintRefusalReason(error, cancellation) {
4338
- if (cancellation === "deadline") return "deadline";
4339
- if (cancellation === "caller_cancelled") return "cancelled";
4340
- if (!(error instanceof HostRelayGatewayError)) return "mint_failed";
4341
- if (error.code === "credential") return "host_credential_refused";
4342
- if (error.code === "gateway_unavailable") return "gateway_unavailable";
4343
- if (error.code === "invalid_response") return "gateway_response_invalid";
4344
- if (error.code !== "gateway_refused") return "mint_failed";
4345
- return gatewayMintRefusalReason(error);
4346
- }
4347
- function gatewayMintRefusalReason(error) {
4348
- if (error.error_code === "request_context_refused" && isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, error.refusal_reason)) return error.refusal_reason;
4349
- return GATEWAY_MINT_REFUSAL_REASONS.get(error.error_code ?? "") ?? "gateway_refused";
4350
- }
4351
- var GATEWAY_MINT_REFUSAL_REASONS = /* @__PURE__ */ new Map([
4352
- ["request_context_refused", "context_stale"],
4353
- ["context_expired", "context_expired"],
4354
- ["host_source_refused", "host_source_refused"],
4355
- ["host_invocation_context_unavailable", "gateway_unavailable"]
4356
- ]);
4357
- function scheduleInvocationMintDeadline2(expire, milliseconds) {
4358
- const timeout = setTimeout(expire, milliseconds);
4359
- 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);
4360
4228
  }
4361
- function reportInvocationMint(observation) {
4362
- process.stderr.write(`${formatHostRelayInvocationMintLine(observation)}
4363
- `);
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
+ };
4239
+ }
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
+ }
4364
4287
  }
4365
- function formatHostRelayInvocationMintLine(observation) {
4366
- return `${HOST_RELAY_INVOCATION_MINT_LOG_TAG} outcome=${observation.outcome}${observation.reason === void 0 ? "" : ` reason=${observation.reason}`} elapsed_ms=${observation.elapsed_ms}`;
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";
4367
4301
  }
4368
4302
  async function runHostRelayCli(args, signal, dependencies = {}) {
4369
4303
  try {
@@ -4382,20 +4316,6 @@ function parseServeArgs(args) {
4382
4316
  }
4383
4317
  return args[2];
4384
4318
  }
4385
- async function assertOwnerPrivateDirectory2(path) {
4386
- try {
4387
- const stats = await lstat4(path);
4388
- if (!stats.isDirectory() || stats.isSymbolicLink() || !ownerPrivate4(stats)) throw new HostRelayConfigError("config_not_private", "Host Relay config directory is not owner-private.");
4389
- } catch (error) {
4390
- if (error instanceof HostRelayConfigError) throw error;
4391
- throw new HostRelayConfigError("config_unavailable", "Host Relay config directory is unavailable.");
4392
- }
4393
- }
4394
- function ownerPrivate4(stats) {
4395
- if (process.platform === "win32") return true;
4396
- const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
4397
- return stats.uid === uid && (stats.mode & 63) === 0;
4398
- }
4399
4319
  function assertNoDuplicateJsonKeys(text) {
4400
4320
  rejectDuplicateJsonKeys(text, {
4401
4321
  invalid: () => new HostRelayConfigError("invalid_json", "Host Relay config is not JSON."),
@@ -4414,12 +4334,14 @@ function exactOptionalKeys2(value, required, optional) {
4414
4334
  }
4415
4335
 
4416
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;
4417
4339
  var controller = new AbortController();
4418
4340
  var stop = () => controller.abort();
4419
4341
  process.once("SIGINT", stop);
4420
4342
  process.once("SIGTERM", stop);
4421
4343
  var stopLogBound = boundHostServiceLog();
4422
- void runHostRelayCli(process.argv.slice(2), controller.signal).then((result2) => {
4344
+ void runHostRelayCli(serviceArguments.args, controller.signal).then((result2) => {
4423
4345
  process.stdout.write(`${JSON.stringify({ schema_version: "ceal.host_relay_command.v1", ...result2 })}
4424
4346
  `);
4425
4347
  process.exitCode = result2.ok ? 0 : 1;