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