@corca-org/ceal-host-win32-x64 1.0.19 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ceal-host.exe +0 -0
- package/libexec/ceal-host/claude-hook.mjs +1327 -1211
- package/libexec/ceal-host/claude-invocation-authentication.mjs +1986 -1272
- package/libexec/ceal-host/claude-producer.mjs +1376 -1313
- package/libexec/ceal-host/codex-hook.mjs +1215 -1392
- package/libexec/ceal-host/codex-invocation-authentication.mjs +1924 -1364
- package/libexec/ceal-host/codex-producer.mjs +1222 -1286
- package/libexec/ceal-host/control.mjs +2818 -2076
- package/libexec/ceal-host/relay.mjs +1733 -1811
- 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,11 +1198,22 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
|
|
|
972
1198
|
var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "block", "object"]);
|
|
973
1199
|
var CEAL_OPAQUE_REF_KINDS = vocabulary([...CEAL_OPAQUE_HANDLE_KINDS, "result", "operation-receipt", "reply-receipt", "field", "notion-data-source"]);
|
|
974
1200
|
|
|
1201
|
+
// packages/ceal-client-protocol/src/client-environment-generated.ts
|
|
1202
|
+
var CEAL_CLIENT_CONTEXT_OVERRIDE_ENVIRONMENTS = Object.freeze(["CEAL_GATEWAY_ORIGIN", "CEAL_GATEWAY_TRANSPORT_ORIGIN", "CEAL_ACCESS_TOKEN", "CEAL_CONTEXT_STORE_PATH"]);
|
|
1203
|
+
|
|
1204
|
+
// packages/ceal-client-protocol/src/client-process-contract.ts
|
|
1205
|
+
var CEAL_CLIENT_VERSION_SCHEMA_SUFFIX = ".version.v1";
|
|
1206
|
+
var CEAL_CLIENT_VERSION_SCHEMA_VERSION = `ceal${CEAL_CLIENT_VERSION_SCHEMA_SUFFIX}`;
|
|
1207
|
+
|
|
975
1208
|
// packages/ceal-client-protocol/src/bare-document.ts
|
|
976
1209
|
var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_fields;
|
|
977
1210
|
var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
|
|
978
1211
|
|
|
1212
|
+
// packages/ceal-client-protocol/src/actor-kind.ts
|
|
1213
|
+
var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
|
|
1214
|
+
|
|
979
1215
|
// packages/ceal-client-protocol/src/resource-row.ts
|
|
1216
|
+
var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
|
|
980
1217
|
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
981
1218
|
var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
|
|
982
1219
|
var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "since", "until", "authored_by_me", "authored_by", "include_replies", "kinds", "container_ref", "container_refs", "container_names", "addressed_to_me", "unread"]);
|
|
@@ -989,6 +1226,8 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
|
989
1226
|
"resource_created",
|
|
990
1227
|
"review_submitted",
|
|
991
1228
|
"comment_created",
|
|
1229
|
+
"reaction_created",
|
|
1230
|
+
"ceal_write_completed",
|
|
992
1231
|
"event_start",
|
|
993
1232
|
"all_day_start",
|
|
994
1233
|
"file_created",
|
|
@@ -1023,6 +1262,10 @@ var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
|
1023
1262
|
"github.review.submitted",
|
|
1024
1263
|
"github.issue_comment.created",
|
|
1025
1264
|
"github.review_comment.created",
|
|
1265
|
+
"github.reaction.created",
|
|
1266
|
+
"github.reaction.deleted",
|
|
1267
|
+
"github.pull_request.review_requested",
|
|
1268
|
+
"github.pull_request.review_request_removed",
|
|
1026
1269
|
"calendar.organized",
|
|
1027
1270
|
"calendar.accepted_invite",
|
|
1028
1271
|
"calendar.recorded",
|
|
@@ -1044,6 +1287,10 @@ var CEAL_ACTIVITY_EVENT_TIMESTAMP_BASIS_MAP = Object.freeze({
|
|
|
1044
1287
|
"github.pull_request.opened": "resource_created",
|
|
1045
1288
|
"github.issue.edited": "resource_modified",
|
|
1046
1289
|
"github.pull_request.edited": "resource_modified",
|
|
1290
|
+
"github.reaction.created": "reaction_created",
|
|
1291
|
+
"github.reaction.deleted": "ceal_write_completed",
|
|
1292
|
+
"github.pull_request.review_requested": "ceal_write_completed",
|
|
1293
|
+
"github.pull_request.review_request_removed": "ceal_write_completed",
|
|
1047
1294
|
"calendar.organized": "resource_created",
|
|
1048
1295
|
"drive.file.created": "file_created",
|
|
1049
1296
|
"drive.file.modified": "file_modified_by_me",
|
|
@@ -1060,12 +1307,14 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
1060
1307
|
next: { required: true, timestamp: false },
|
|
1061
1308
|
kind: { required: true, timestamp: false },
|
|
1062
1309
|
matched_kinds: { required: false, timestamp: false },
|
|
1310
|
+
title_match: { required: false, timestamp: false },
|
|
1063
1311
|
title: { required: true, timestamp: false },
|
|
1064
1312
|
container_label: { required: true, timestamp: false },
|
|
1065
1313
|
container_ref: { required: false, timestamp: false },
|
|
1066
1314
|
source_url: { required: false, timestamp: false },
|
|
1067
1315
|
source: { required: true, timestamp: false },
|
|
1068
1316
|
owner: { required: false, timestamp: false },
|
|
1317
|
+
actor_kind: { required: false, timestamp: false },
|
|
1069
1318
|
counterparts: { required: false, timestamp: false },
|
|
1070
1319
|
attachments: { required: false, timestamp: false },
|
|
1071
1320
|
created_at: { required: false, timestamp: true },
|
|
@@ -1073,9 +1322,18 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
1073
1322
|
modified_at: { required: false, timestamp: true },
|
|
1074
1323
|
modified_at_local: { required: false, timestamp: true },
|
|
1075
1324
|
viewed_by_me_at: { required: false, timestamp: true },
|
|
1325
|
+
modified_by_me_at: { required: false, timestamp: true },
|
|
1076
1326
|
own_ceal_write: { required: false, timestamp: false },
|
|
1077
1327
|
owned_by_me: { required: false, timestamp: false },
|
|
1328
|
+
mime_type: { required: false, timestamp: false },
|
|
1329
|
+
size_bytes: { required: false, timestamp: false },
|
|
1330
|
+
version: { required: false, timestamp: false },
|
|
1331
|
+
trashed: { required: false, timestamp: false },
|
|
1078
1332
|
visibility: { required: false, timestamp: false },
|
|
1333
|
+
archived: { required: false, timestamp: false },
|
|
1334
|
+
tag: { required: false, timestamp: false },
|
|
1335
|
+
labels: { required: false, timestamp: false },
|
|
1336
|
+
labels_incomplete: { required: false, timestamp: false },
|
|
1079
1337
|
start: { required: false, timestamp: false },
|
|
1080
1338
|
end: { required: false, timestamp: false },
|
|
1081
1339
|
status: { required: false, timestamp: false },
|
|
@@ -1148,18 +1406,29 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
|
1148
1406
|
// The serving gate answers this while the Gateway is still starting and the
|
|
1149
1407
|
// refresh was never attempted; the same attempt stays retryable. It is never
|
|
1150
1408
|
// stored by the session owner, only answered at the serving edge.
|
|
1151
|
-
|
|
1409
|
+
CEAL_GATEWAY_STARTING_FAILURE_CODE
|
|
1410
|
+
]);
|
|
1411
|
+
var CEAL_CLIENT_REVOKE_FAILURE_CODES = vocabulary([
|
|
1412
|
+
"refresh_invalid",
|
|
1413
|
+
"refresh_expired",
|
|
1414
|
+
"refresh_replayed",
|
|
1415
|
+
"refresh_revoked"
|
|
1152
1416
|
]);
|
|
1417
|
+
var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX = "ceal_personal_";
|
|
1418
|
+
var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX = "ceal_refresh_";
|
|
1419
|
+
var PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN = "[A-Za-z0-9_-]{43}";
|
|
1420
|
+
var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
|
|
1421
|
+
var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
|
|
1153
1422
|
|
|
1154
1423
|
// packages/ceal-client-protocol/src/request-validation-primitives.ts
|
|
1155
|
-
import { createHash } from "node:crypto";
|
|
1424
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1156
1425
|
var DIGEST = /^[a-f0-9]{64}$/u;
|
|
1157
1426
|
var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
|
|
1158
1427
|
function isCealDigest(value) {
|
|
1159
1428
|
return typeof value === "string" && DIGEST.test(value);
|
|
1160
1429
|
}
|
|
1161
1430
|
function cealRequestSha256(value) {
|
|
1162
|
-
return
|
|
1431
|
+
return createHash2("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
|
|
1163
1432
|
}
|
|
1164
1433
|
function operationRecord(value, message) {
|
|
1165
1434
|
requireOperationRecord(value, message);
|
|
@@ -1175,40 +1444,71 @@ function requirePrefixedRef(value, prefix, message) {
|
|
|
1175
1444
|
// packages/ceal-client-protocol/src/request-authority.ts
|
|
1176
1445
|
var CEAL_REQUEST_SCHEMA_VERSION = "ceal.request.v1";
|
|
1177
1446
|
var CEAL_REQUEST_ARTIFACT_OWNERS = vocabulary(["gateway", "host", "connector"]);
|
|
1178
|
-
var
|
|
1447
|
+
var CEAL_HOST_INVOCATION_IDENTITY_KEYS = vocabulary(["host_session_ref", "host_turn_ref", "host_source_ref"]);
|
|
1179
1448
|
var CEAL_REQUEST_ARTIFACT_RETENTION_CLASSES = vocabulary(["request", "source", "result"]);
|
|
1180
1449
|
var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
|
|
1181
|
-
var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority"
|
|
1450
|
+
var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority"]);
|
|
1451
|
+
var CEAL_REQUEST_COMMON_PROPERTIES = Object.freeze({
|
|
1452
|
+
schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
|
|
1453
|
+
ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
|
|
1454
|
+
created_at: { type: "string", minLength: 1, maxLength: 64 },
|
|
1455
|
+
authority: {
|
|
1456
|
+
type: "object",
|
|
1457
|
+
maxProperties: 5,
|
|
1458
|
+
additionalProperties: false,
|
|
1459
|
+
properties: {
|
|
1460
|
+
instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1461
|
+
access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1462
|
+
principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1463
|
+
execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1464
|
+
authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
1465
|
+
},
|
|
1466
|
+
required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
|
|
1467
|
+
}
|
|
1468
|
+
});
|
|
1469
|
+
var CEAL_REQUEST_HOST_PROPERTIES = Object.freeze({
|
|
1470
|
+
prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1471
|
+
host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1472
|
+
host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1473
|
+
host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1474
|
+
host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
1475
|
+
});
|
|
1476
|
+
var CEAL_REQUEST_CALLER_PROPERTIES = Object.freeze({
|
|
1477
|
+
caller_call_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source, description: "Gateway-minted call-group identity for a non-Host caller." },
|
|
1478
|
+
caller_call_input_sha256: { type: "string", minLength: 64, maxLength: 64, pattern: "^[a-f0-9]{64}$", description: "Commitment to the exact input for active non-Host identity-reuse checks; a migrated legacy call group commits to its legacy group key because the original input may no longer exist." }
|
|
1479
|
+
});
|
|
1480
|
+
var CEAL_REQUEST_OPTIONAL_PROPERTIES = Object.freeze({
|
|
1481
|
+
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
|
|
1482
|
+
device_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source, description: "Device that submitted this Request through its client session, including Host turns when the authenticated Host registration carries the device; absent for older records." }
|
|
1483
|
+
});
|
|
1484
|
+
var CEAL_REQUEST_HOST_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, ...CEAL_HOST_INVOCATION_IDENTITY_KEYS, "prompt_ref"]);
|
|
1485
|
+
var CEAL_REQUEST_CALLER_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, "caller_call_group_ref", "caller_call_input_sha256"]);
|
|
1182
1486
|
var CEAL_REQUEST_JSON_SCHEMA = {
|
|
1183
1487
|
type: "object",
|
|
1184
|
-
|
|
1488
|
+
description: "A Request has exactly one creation identity: the complete Host tuple and prompt reference, or a caller call-group identity. Caller Requests retain no prompt reference or prompt body.",
|
|
1489
|
+
maxProperties: 13,
|
|
1185
1490
|
additionalProperties: false,
|
|
1186
1491
|
properties: {
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1492
|
+
...CEAL_REQUEST_COMMON_PROPERTIES,
|
|
1493
|
+
...CEAL_REQUEST_HOST_PROPERTIES,
|
|
1494
|
+
...CEAL_REQUEST_CALLER_PROPERTIES,
|
|
1495
|
+
...CEAL_REQUEST_OPTIONAL_PROPERTIES
|
|
1496
|
+
},
|
|
1497
|
+
required: CEAL_REQUEST_KEYS,
|
|
1498
|
+
anyOf: [
|
|
1499
|
+
{
|
|
1191
1500
|
type: "object",
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1196
|
-
access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1197
|
-
principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1198
|
-
execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1199
|
-
authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
1200
|
-
},
|
|
1201
|
-
required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
|
|
1501
|
+
required: CEAL_REQUEST_HOST_REQUIRED_KEYS,
|
|
1502
|
+
properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_HOST_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
|
|
1503
|
+
additionalProperties: false
|
|
1202
1504
|
},
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
},
|
|
1211
|
-
required: CEAL_REQUEST_KEYS
|
|
1505
|
+
{
|
|
1506
|
+
type: "object",
|
|
1507
|
+
required: CEAL_REQUEST_CALLER_REQUIRED_KEYS,
|
|
1508
|
+
properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_CALLER_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
|
|
1509
|
+
additionalProperties: false
|
|
1510
|
+
}
|
|
1511
|
+
]
|
|
1212
1512
|
};
|
|
1213
1513
|
|
|
1214
1514
|
// packages/ceal-client-protocol/src/request-operands.ts
|
|
@@ -1319,10 +1619,6 @@ var CEAL_REQUEST_RUN_BATCH_JSON_SCHEMA = {
|
|
|
1319
1619
|
};
|
|
1320
1620
|
|
|
1321
1621
|
// packages/ceal-client-protocol/src/host-observation.ts
|
|
1322
|
-
var CEAL_HOST_INPUT_KINDS = vocabulary(["human", "host_automatic"]);
|
|
1323
|
-
function isCealHostInputKind(value) {
|
|
1324
|
-
return typeof value === "string" && CEAL_HOST_INPUT_KINDS.some((kind) => kind === value);
|
|
1325
|
-
}
|
|
1326
1622
|
var CEAL_HOST_OBSERVATION_REF_PREFIX = "host-observation:";
|
|
1327
1623
|
var HOST_OBSERVATION_PRODUCER = /^[a-z][a-z0-9]{0,31}$/u;
|
|
1328
1624
|
var HOST_OBSERVATION_DIGEST = /^[a-f0-9]{8,64}$/u;
|
|
@@ -1368,14 +1664,14 @@ function safeCount(value) {
|
|
|
1368
1664
|
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
1369
1665
|
}
|
|
1370
1666
|
function validateCealHostObservationDraft(value) {
|
|
1371
|
-
const
|
|
1372
|
-
requirePrefixedRef(
|
|
1373
|
-
requireClosedValue(
|
|
1374
|
-
requireIsoTimestamp(
|
|
1375
|
-
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);
|
|
1376
1672
|
}
|
|
1377
1673
|
var CEAL_REQUEST_MAX_OBSERVATION_VALUE_BYTES = 64 * 1024;
|
|
1378
|
-
var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "
|
|
1674
|
+
var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "timing"]);
|
|
1379
1675
|
function isCealHostPromptObservationValue(value) {
|
|
1380
1676
|
return isOperationRecord(value) && Object.keys(value).length === 1 && isCealDigest(value.body_sha256);
|
|
1381
1677
|
}
|
|
@@ -1498,16 +1794,41 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
|
|
|
1498
1794
|
};
|
|
1499
1795
|
|
|
1500
1796
|
// packages/ceal-client-protocol/src/host-invocation-authentication.ts
|
|
1501
|
-
var
|
|
1502
|
-
var
|
|
1503
|
-
var
|
|
1504
|
-
var
|
|
1505
|
-
var
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1797
|
+
var hostInvocationAuthenticationContract = CEAL_CLIENT_WIRE_CONTRACT.host_invocation_authentication;
|
|
1798
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.request_schema_name, fields: hostInvocationAuthenticationContract.request_fields });
|
|
1799
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.request_fields]);
|
|
1800
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.result_schema_name, fields: hostInvocationAuthenticationContract.result_fields });
|
|
1801
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.result_fields]);
|
|
1802
|
+
var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE = "^(?:0|[1-9][0-9]{0,8})$";
|
|
1803
|
+
var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN = new RegExp(CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE, "u");
|
|
1804
|
+
var CEAL_HOST_INVOCATION_CONTEXT_REQUEST_MAX_BYTES = 16 * 1024;
|
|
1805
|
+
var CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES = 2 * 1024;
|
|
1806
|
+
var CEAL_HOST_CONTEXT_TOKEN_PREFIX = "ceal_context_";
|
|
1807
|
+
function deriveCealHostInvocationNonceContract(contract = CEAL_CLIENT_WIRE_CONTRACT) {
|
|
1808
|
+
const clientNonce = contract.host_invocation_authentication.client_nonce;
|
|
1809
|
+
return {
|
|
1810
|
+
encoded_characters: clientNonce.encoded_characters,
|
|
1811
|
+
client_nonce_pattern_source: `^[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`,
|
|
1812
|
+
context_token_pattern_source: `^${CEAL_HOST_CONTEXT_TOKEN_PREFIX}[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
var hostNonceContract = deriveCealHostInvocationNonceContract();
|
|
1816
|
+
var CEAL_HOST_NONCE_ENCODED_CHARACTERS = hostNonceContract.encoded_characters;
|
|
1817
|
+
var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(hostNonceContract.client_nonce_pattern_source, "u");
|
|
1818
|
+
var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(hostNonceContract.context_token_pattern_source, "u");
|
|
1819
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = deriveCealHostInvocationAuthenticationResultMaxBytes();
|
|
1820
|
+
function deriveCealHostInvocationAuthenticationResultMaxBytes(contract = CEAL_CLIENT_WIRE_CONTRACT) {
|
|
1821
|
+
const hostAuthentication = contract.host_invocation_authentication;
|
|
1822
|
+
const values = {
|
|
1823
|
+
schema_version: deriveCealClientWireSchemaVersion({ schema_name: hostAuthentication.result_schema_name, fields: hostAuthentication.result_fields }),
|
|
1824
|
+
context_token: `${CEAL_HOST_CONTEXT_TOKEN_PREFIX}${"A".repeat(hostAuthentication.client_nonce.encoded_characters)}`,
|
|
1825
|
+
expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
|
|
1826
|
+
};
|
|
1827
|
+
const fields = hostAuthentication.result_fields;
|
|
1828
|
+
if (fields.some((field) => !Object.hasOwn(values, field))) throw new TypeError("Host invocation result fields have no bounded sample value.");
|
|
1829
|
+
return new TextEncoder().encode(`${JSON.stringify(Object.fromEntries(fields.map((field) => [field, values[field]])))}
|
|
1510
1830
|
`).byteLength;
|
|
1831
|
+
}
|
|
1511
1832
|
var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
|
|
1512
1833
|
"request_authority_unavailable",
|
|
1513
1834
|
"request_missing",
|
|
@@ -1531,6 +1852,7 @@ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
|
|
|
1531
1852
|
"host_credential_refused",
|
|
1532
1853
|
"gateway_unavailable",
|
|
1533
1854
|
"gateway_rate_limited",
|
|
1855
|
+
"contract_mismatch",
|
|
1534
1856
|
"gateway_refused",
|
|
1535
1857
|
"gateway_response_invalid",
|
|
1536
1858
|
"producer_frame_invalid",
|
|
@@ -1545,19 +1867,23 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
1545
1867
|
invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
|
|
1546
1868
|
options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
|
|
1547
1869
|
session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
1548
|
-
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "
|
|
1549
|
-
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "
|
|
1550
|
-
no_binding
|
|
1551
|
-
|
|
1552
|
-
|
|
1870
|
+
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "retry" }),
|
|
1871
|
+
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
|
|
1872
|
+
// `no_binding` now means the producer has no real current Host session. The
|
|
1873
|
+
// Host Relay never uses it for an uncreated Gateway Request; there is no
|
|
1874
|
+
// useful retry until the consumer is running inside a Host session.
|
|
1875
|
+
no_binding: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
1876
|
+
context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
|
|
1877
|
+
context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "retry" }),
|
|
1553
1878
|
host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
|
|
1554
1879
|
host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
|
|
1555
1880
|
gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
|
|
1556
1881
|
gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
|
|
1882
|
+
contract_mismatch: Object.freeze({ classification: "contract_mismatch", next_action: "upgrade_ceal_client" }),
|
|
1557
1883
|
gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
|
|
1558
1884
|
gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
|
|
1559
|
-
producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin"
|
|
1560
|
-
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" }),
|
|
1561
1887
|
relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
|
|
1562
1888
|
transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
|
|
1563
1889
|
mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
|
|
@@ -1566,14 +1892,12 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
1566
1892
|
});
|
|
1567
1893
|
|
|
1568
1894
|
// packages/ceal-client-protocol/src/host-service-diagnostic.ts
|
|
1895
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA = "ceal.host_service_diagnostic.v1";
|
|
1569
1896
|
var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
|
|
1570
1897
|
var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
1571
1898
|
"hook_invoked",
|
|
1572
1899
|
"registration_written",
|
|
1573
1900
|
"hook_failed",
|
|
1574
|
-
"watcher_turn_observed",
|
|
1575
|
-
"current_prompt_registered",
|
|
1576
|
-
"current_prompt_registration_failed",
|
|
1577
1901
|
"activation_recorded",
|
|
1578
1902
|
"activation_failed",
|
|
1579
1903
|
"watcher_scan_failed",
|
|
@@ -1596,6 +1920,9 @@ var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "un
|
|
|
1596
1920
|
var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
|
|
1597
1921
|
var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "items_path", "row_count", "result_complete", "coverage", "local_result_root", "model_disclosure", "continue_argv", "how_to_read"]);
|
|
1598
1922
|
|
|
1923
|
+
// packages/ceal-client-protocol/src/discovery-manifest.ts
|
|
1924
|
+
var DISCOVERY_MANIFEST_KEYS = CEAL_CLIENT_WIRE_CONTRACT.command_response.discovery_manifest_fields;
|
|
1925
|
+
|
|
1599
1926
|
// packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
|
|
1600
1927
|
var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
1601
1928
|
|
|
@@ -1654,6 +1981,7 @@ var ADMIN_UNAVAILABLE_REASONS = vocabulary([
|
|
|
1654
1981
|
"legacy",
|
|
1655
1982
|
"invalid_observation",
|
|
1656
1983
|
"redacted",
|
|
1984
|
+
"organization_setting_disabled",
|
|
1657
1985
|
"owner_unavailable",
|
|
1658
1986
|
"unit_unknown",
|
|
1659
1987
|
"retention_gap",
|
|
@@ -1683,15 +2011,13 @@ var ADMIN_APPROVAL_DISPOSITIONS = CEAL_OPERATION_APPROVAL_DISPOSITIONS;
|
|
|
1683
2011
|
var ADMIN_APPROVAL_POSTURES = vocabulary([...ADMIN_APPROVAL_DISPOSITIONS, "not_applicable"]);
|
|
1684
2012
|
var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "approval", "effect", "readback"]);
|
|
1685
2013
|
|
|
1686
|
-
// packages/ceal-client-protocol/src/actor-kind.ts
|
|
1687
|
-
var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
|
|
1688
|
-
|
|
1689
2014
|
// packages/ceal-client-protocol/src/device-enrollment.ts
|
|
1690
2015
|
var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
|
|
1691
2016
|
var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
|
|
1692
2017
|
|
|
1693
2018
|
// packages/ceal-client-protocol/src/personal-client-session.ts
|
|
1694
2019
|
var REFRESH_FAILURE_CODES = new Set(CEAL_REFRESH_FAILURE_CODES);
|
|
2020
|
+
var REVOKE_FAILURE_CODES = new Set(CEAL_CLIENT_REVOKE_FAILURE_CODES);
|
|
1695
2021
|
|
|
1696
2022
|
// packages/ceal-client-protocol/src/index.ts
|
|
1697
2023
|
var MAX_REQUEST_BYTES = 32 * 1024;
|
|
@@ -1701,12 +2027,11 @@ var AUDIT_IDENTITY_KEYS = Object.freeze(["event_ref", "membership_ref", "registr
|
|
|
1701
2027
|
var FAILURE_RESPONSE_KEYS = Object.freeze(["error", "ok", "proof_ref_or_unavailable", "protocol_version", "request_id"]);
|
|
1702
2028
|
|
|
1703
2029
|
// host/codex/src/codex-decoder.ts
|
|
1704
|
-
import { createHash as
|
|
2030
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
1705
2031
|
import { isAbsolute, resolve } from "node:path";
|
|
1706
2032
|
|
|
1707
2033
|
// host/codex/src/codex-types.ts
|
|
1708
2034
|
var CODEX_OUTBOX_SCHEMA_VERSION = "ceal.codex_observation_outbox.v1";
|
|
1709
|
-
var CODEX_TURN_BINDING_SCHEMA_VERSION = "ceal.codex_turn_binding.v1";
|
|
1710
2035
|
var CODEX_SESSION_SCHEMA_VERSION = "ceal.codex_session.v1";
|
|
1711
2036
|
var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1";
|
|
1712
2037
|
var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
|
|
@@ -1734,8 +2059,8 @@ function decodeCodexHookPayload(event, raw) {
|
|
|
1734
2059
|
if (event === "SessionStart") return decodeSession(input, event);
|
|
1735
2060
|
return decodeUserPromptSubmit(input, event);
|
|
1736
2061
|
}
|
|
1737
|
-
function observationRef(event, identity, value) {
|
|
1738
|
-
return cealHostObservationRef("codex", shortDigest({ event, identity, value }));
|
|
2062
|
+
function observationRef(hostSourceRef, event, identity, value) {
|
|
2063
|
+
return cealHostObservationRef("codex", shortDigest({ host_source_ref: hostSourceRef, event, identity, value }));
|
|
1739
2064
|
}
|
|
1740
2065
|
function hostSessionReference(sourceRef, sessionId) {
|
|
1741
2066
|
return `codex-session:${shortDigest({ source_ref: sourceRef, session_id: sessionId })}`;
|
|
@@ -1830,7 +2155,7 @@ function isRecord(value) {
|
|
|
1830
2155
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1831
2156
|
}
|
|
1832
2157
|
function sha256(value) {
|
|
1833
|
-
return
|
|
2158
|
+
return createHash3("sha256").update(value, "utf8").digest("hex");
|
|
1834
2159
|
}
|
|
1835
2160
|
function shortDigest(value) {
|
|
1836
2161
|
return cealRequestSha256(value);
|
|
@@ -1840,18 +2165,17 @@ function reject(message) {
|
|
|
1840
2165
|
}
|
|
1841
2166
|
|
|
1842
2167
|
// host/codex/src/codex-outbox.ts
|
|
1843
|
-
import {
|
|
1844
|
-
import {
|
|
1845
|
-
import { dirname, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
2168
|
+
import { mkdir as mkdir2, open as open3, readdir, rename as rename2, stat, unlink as unlink2 } from "node:fs/promises";
|
|
2169
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2 } from "node:path";
|
|
1846
2170
|
|
|
1847
2171
|
// host/invocation-context.ts
|
|
1848
|
-
var
|
|
2172
|
+
var HOST_INVOCATION_TURN_SELECTION_SCHEMA = "ceal.host_invocation_turn_selection.v1";
|
|
1849
2173
|
var HOST_INVOCATION_CONTEXT_REQUIRED_KEYS = ["schema_version", "identity", "prompt_ref", "expires_at"];
|
|
1850
2174
|
function invocationContextKey(identity) {
|
|
1851
2175
|
return cealRequestSha256({ host_source_ref: identity.host_source_ref, host_session_ref: identity.host_session_ref });
|
|
1852
2176
|
}
|
|
1853
2177
|
function hasHostInvocationIdentityKeys(value) {
|
|
1854
|
-
return exactKeys2(value,
|
|
2178
|
+
return exactKeys2(value, CEAL_HOST_INVOCATION_IDENTITY_KEYS);
|
|
1855
2179
|
}
|
|
1856
2180
|
function hasHostInvocationContextKeys(value, optional = []) {
|
|
1857
2181
|
return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
|
|
@@ -1872,432 +2196,163 @@ function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor) {
|
|
|
1872
2196
|
expires_at: expiresAt,
|
|
1873
2197
|
...descriptor.turn_generation_ref === void 0 ? {} : { turn_generation_ref: descriptor.turn_generation_ref },
|
|
1874
2198
|
...descriptor.working_directory === void 0 ? {} : { working_directory: descriptor.working_directory }
|
|
1875
|
-
};
|
|
1876
|
-
descriptor.validate(context);
|
|
1877
|
-
return Object.freeze(context);
|
|
1878
|
-
}
|
|
1879
|
-
function isHostInvocationTurnSelection(value) {
|
|
1880
|
-
return isRecord2(value) && Object.keys(value).length === 5 && value.schema_version ===
|
|
1881
|
-
}
|
|
1882
|
-
function
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
return
|
|
1893
|
-
}
|
|
1894
|
-
function
|
|
1895
|
-
return
|
|
1896
|
-
}
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
var
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
var
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
this.code = code;
|
|
1926
|
-
}
|
|
1927
|
-
};
|
|
1928
|
-
function encodeHostProducerFrame(frame) {
|
|
1929
|
-
validateFrame(frame);
|
|
1930
|
-
const json = Buffer.from(JSON.stringify(frame), "utf8");
|
|
1931
|
-
if (json.byteLength > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("frame_bytes", json.byteLength, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
|
|
1932
|
-
const result = Buffer.allocUnsafe(json.byteLength + HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1933
|
-
result.writeUInt32BE(json.byteLength, 0);
|
|
1934
|
-
json.copy(result, HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1935
|
-
return result;
|
|
1936
|
-
}
|
|
1937
|
-
function decodeHostProducerFrame(input) {
|
|
1938
|
-
if (input.byteLength < HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError("frame_truncated", `frame_truncated: ${refusalFact("frame_bytes", input.byteLength, `at least ${HOST_PRODUCER_FRAME_LENGTH_BYTES}`)}`);
|
|
1939
|
-
const length = input.readUInt32BE(0);
|
|
1940
|
-
if (length > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("declared_bytes", length, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
|
|
1941
|
-
if (length === 0) throw new HostProducerFrameError("invalid_frame_length");
|
|
1942
|
-
if (input.byteLength !== length + HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError(input.byteLength < length + HOST_PRODUCER_FRAME_LENGTH_BYTES ? "frame_truncated" : "invalid_frame_length");
|
|
1943
|
-
const bytes = input.subarray(HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1944
|
-
const text = new TextDecoder("utf-8", { fatal: true });
|
|
1945
|
-
let value;
|
|
1946
|
-
try {
|
|
1947
|
-
value = JSON.parse(text.decode(bytes));
|
|
1948
|
-
} catch (error) {
|
|
1949
|
-
if (error instanceof TypeError) throw new HostProducerFrameError("invalid_frame_encoding");
|
|
1950
|
-
throw new HostProducerFrameError("frame_truncated");
|
|
1951
|
-
}
|
|
1952
|
-
validateFrame(value);
|
|
1953
|
-
return value;
|
|
1954
|
-
}
|
|
1955
|
-
function isHostSafeRef(value) {
|
|
1956
|
-
return safeRef2(value);
|
|
1957
|
-
}
|
|
1958
|
-
function validateFrame(value) {
|
|
1959
|
-
if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1960
|
-
if (value.schema_version !== HOST_PRODUCER_FRAME_SCHEMA) throw new HostProducerFrameError("unsupported_frame_schema", `unsupported_frame_schema: ${refusalFact("schema_version", value.schema_version, HOST_PRODUCER_FRAME_SCHEMA)}`);
|
|
1961
|
-
if (!exactKeys3(value, ["schema_version", "producer_id", "producer_installation_ref", "producer_epoch", "sequence", "frame_kind", "occurred_at", "payload"])) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1962
|
-
if (!PRODUCERS.has(String(value.producer_id)) || !safeRef2(value.producer_installation_ref)) throw new HostProducerFrameError("invalid_producer_identity");
|
|
1963
|
-
if (!positive(value.producer_epoch) || !positive(value.sequence) || !hostProducerFrameKind(value.frame_kind) || !timestamp(value.occurred_at) || !record(value.payload)) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1964
|
-
validatePayload(value.frame_kind, value.payload);
|
|
1965
|
-
}
|
|
1966
|
-
function validatePayload(kind, value) {
|
|
1967
|
-
const valid = kind === "session_started" ? validSession(value) : kind === "turn_started" ? validTurn(value) : kind === "user_input" ? validInput(value) : kind === "turn_terminal" ? validTerminal(value) : validStatus(value);
|
|
1968
|
-
if (!valid) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1969
|
-
}
|
|
1970
|
-
function validSession(value) {
|
|
1971
|
-
return exactOptional(value, ["local_session_ref", "working_directory_ref"], ["model", "host_version", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.working_directory_ref) && optionalText(value.model, 256) && optionalText(value.host_version, 256) && validObservations(value.observations);
|
|
1972
|
-
}
|
|
1973
|
-
function validTurn(value) {
|
|
1974
|
-
return exactOptional(value, ["local_session_ref", "local_turn_ref"], ["model", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && optionalText(value.model, 256) && validObservations(value.observations);
|
|
1975
|
-
}
|
|
1976
|
-
function validInput(value) {
|
|
1977
|
-
return exactOptional(value, ["local_session_ref", "local_turn_ref", "local_input_ref", "input_kind", "origin_input", "observations"], ["local_turn_aliases", "context_expires_at"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && safeRef2(value.local_input_ref) && validInputObservations(value.observations) && isCealHostInputKind(value.input_kind) && typeof value.origin_input === "boolean" && validInputOptionals(value);
|
|
1978
|
-
}
|
|
1979
|
-
function validInputObservations(value) {
|
|
1980
|
-
return validObservations(value) && Array.isArray(value) && value.filter((item) => record(item) && item.observation_kind === "prompt").length === 1;
|
|
1981
|
-
}
|
|
1982
|
-
function validInputOptionals(value) {
|
|
1983
|
-
return optionalRefs(value.local_turn_aliases) && (value.context_expires_at === void 0 || timestamp(value.context_expires_at));
|
|
1984
|
-
}
|
|
1985
|
-
function validTerminal(value) {
|
|
1986
|
-
return exactOptional(value, ["local_session_ref", "local_turn_ref", "terminal_state"], ["abort_reason", "duration_ms", "time_to_first_token_ms", "usage", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && validTerminalDisposition(value.terminal_state, value.abort_reason) && optionalNonNegative(value.duration_ms) && optionalNonNegative(value.time_to_first_token_ms) && (value.usage === void 0 || jsonValue(value.usage)) && validObservations(value.observations);
|
|
1987
|
-
}
|
|
1988
|
-
function validTerminalDisposition(state, reason) {
|
|
1989
|
-
return (state === "completed" || state === "aborted") && (reason === void 0 || state === "aborted" && safeRef2(reason));
|
|
1990
|
-
}
|
|
1991
|
-
function validStatus(value) {
|
|
1992
|
-
return exactOptional(value, ["status", "reason_code"], ["detail"]) && ["ready", "degraded", "stopping"].includes(String(value.status)) && safeRef2(value.reason_code) && optionalText(value.detail, 4096);
|
|
1993
|
-
}
|
|
1994
|
-
function optionalText(value, maxLength) {
|
|
1995
|
-
return value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maxLength;
|
|
1996
|
-
}
|
|
1997
|
-
function optionalNonNegative(value) {
|
|
1998
|
-
return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1999
|
-
}
|
|
2000
|
-
function optionalRefs(value) {
|
|
2001
|
-
return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef2);
|
|
2002
|
-
}
|
|
2003
|
-
function validObservations(value) {
|
|
2004
|
-
if (value === void 0) return true;
|
|
2005
|
-
if (!Array.isArray(value) || value.length > 32) return false;
|
|
2006
|
-
return value.every((item) => {
|
|
2007
|
-
try {
|
|
2008
|
-
validateCealHostObservationDraft(item);
|
|
2009
|
-
return true;
|
|
2010
|
-
} catch {
|
|
2011
|
-
return false;
|
|
2012
|
-
}
|
|
2013
|
-
});
|
|
2014
|
-
}
|
|
2015
|
-
function jsonValue(value, depth = 0) {
|
|
2016
|
-
if (depth > 16) return false;
|
|
2017
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
2018
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
2019
|
-
if (Array.isArray(value)) return value.length <= 256 && value.every((item) => jsonValue(item, depth + 1));
|
|
2020
|
-
return record(value) && Object.keys(value).length <= 256 && Object.entries(value).every(([key, item]) => key.length > 0 && key.length <= 128 && jsonValue(item, depth + 1));
|
|
2021
|
-
}
|
|
2022
|
-
function record(value) {
|
|
2023
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2024
|
-
}
|
|
2025
|
-
function safeRef2(value) {
|
|
2026
|
-
return typeof value === "string" && SAFE_REF3.test(value);
|
|
2027
|
-
}
|
|
2028
|
-
function positive(value) {
|
|
2029
|
-
return Number.isSafeInteger(value) && Number(value) > 0;
|
|
2030
|
-
}
|
|
2031
|
-
function timestamp(value) {
|
|
2032
|
-
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
2033
|
-
}
|
|
2034
|
-
function hostProducerFrameKind(value) {
|
|
2035
|
-
return isHostProducerFrameKind(value);
|
|
2036
|
-
}
|
|
2037
|
-
function exactKeys3(value, keys) {
|
|
2038
|
-
return JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
|
|
2039
|
-
}
|
|
2040
|
-
function exactOptional(value, required, optional) {
|
|
2041
|
-
const keys = Object.keys(value);
|
|
2042
|
-
return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
// host/relay/src/host-producer-core.ts
|
|
2046
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
2047
|
-
import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
|
|
2048
|
-
import { mkdir as mkdir2, open as open2, readFile, rename as rename2, writeFile } from "node:fs/promises";
|
|
2049
|
-
import { join as join2 } from "node:path";
|
|
2050
|
-
|
|
2051
|
-
// host/relay/src/relay-ipc.ts
|
|
2052
|
-
import { createConnection, createServer } from "node:net";
|
|
2053
|
-
|
|
2054
|
-
// host/relay/src/durable-atomic-replacement.ts
|
|
2055
|
-
import { O_RDONLY } from "node:constants";
|
|
2056
|
-
import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
2057
|
-
var DEFAULT_PORT = {
|
|
2058
|
-
platform: process.platform,
|
|
2059
|
-
mkdir: (path, options) => mkdir(path, options),
|
|
2060
|
-
lstat: (path) => lstat(path),
|
|
2061
|
-
open_file: (path) => open(path, "wx", 384),
|
|
2062
|
-
rename,
|
|
2063
|
-
unlink,
|
|
2064
|
-
open_directory: (path) => open(path, O_RDONLY),
|
|
2065
|
-
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
2066
|
-
};
|
|
2067
|
-
|
|
2068
|
-
// host/relay/src/relay-request-binding-store.ts
|
|
2069
|
-
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
2070
|
-
|
|
2071
|
-
// host/relay/src/current-prompt-custody.ts
|
|
2072
|
-
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
2073
|
-
|
|
2074
|
-
// host/relay/src/relay-ipc.ts
|
|
2075
|
-
var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
2076
|
-
"schema_version",
|
|
2077
|
-
"producer_id",
|
|
2078
|
-
"producer_installation_ref",
|
|
2079
|
-
"local_session_ref",
|
|
2080
|
-
"local_turn_ref",
|
|
2081
|
-
"local_turn_aliases",
|
|
2082
|
-
"prompt_ref",
|
|
2083
|
-
"request_ref",
|
|
2084
|
-
"bound_at",
|
|
2085
|
-
"host_source_ref",
|
|
2086
|
-
"host_session_ref",
|
|
2087
|
-
"host_turn_ref",
|
|
2088
|
-
"host_input_ref"
|
|
2089
|
-
]);
|
|
2090
|
-
var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
|
|
2091
|
-
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
2092
|
-
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
2093
|
-
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
2094
|
-
var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
|
|
2095
|
-
var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
2096
|
-
"context_stale",
|
|
2097
|
-
"context_expired",
|
|
2098
|
-
"host_source_refused",
|
|
2099
|
-
"host_credential_refused",
|
|
2100
|
-
"gateway_unavailable",
|
|
2101
|
-
"gateway_refused",
|
|
2102
|
-
"gateway_response_invalid",
|
|
2103
|
-
"gateway_rate_limited",
|
|
2104
|
-
"producer_frame_invalid",
|
|
2105
|
-
"deadline",
|
|
2106
|
-
"cancelled",
|
|
2107
|
-
"mint_failed",
|
|
2108
|
-
"relay_failed",
|
|
2109
|
-
"request_authority_unavailable",
|
|
2110
|
-
"request_missing",
|
|
2111
|
-
"authority_mismatch",
|
|
2112
|
-
"host_source_mismatch",
|
|
2113
|
-
"host_session_mismatch",
|
|
2114
|
-
"host_turn_mismatch",
|
|
2115
|
-
"host_input_mismatch",
|
|
2116
|
-
"host_prompt_mismatch"
|
|
2117
|
-
]);
|
|
2118
|
-
var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
|
|
2119
|
-
"context_stale",
|
|
2120
|
-
"context_expired",
|
|
2121
|
-
"host_source_refused",
|
|
2122
|
-
"host_credential_refused",
|
|
2123
|
-
"gateway_unavailable",
|
|
2124
|
-
"gateway_rate_limited",
|
|
2125
|
-
"gateway_refused",
|
|
2126
|
-
"gateway_response_invalid",
|
|
2127
|
-
"producer_frame_invalid"
|
|
2128
|
-
]);
|
|
2129
|
-
var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
|
|
2130
|
-
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
2131
|
-
"permission_denied",
|
|
2132
|
-
"socket_missing",
|
|
2133
|
-
"connection_refused",
|
|
2134
|
-
"closed_early",
|
|
2135
|
-
"response_invalid",
|
|
2136
|
-
"timed_out",
|
|
2137
|
-
"cancelled",
|
|
2138
|
-
"unavailable"
|
|
2139
|
-
]);
|
|
2140
|
-
var HostRelayIpcError = class extends Error {
|
|
2141
|
-
code;
|
|
2142
|
-
/** Set only when the client-side socket exchange itself failed. */
|
|
2143
|
-
failure;
|
|
2144
|
-
constructor(code, message = code, failure) {
|
|
2199
|
+
};
|
|
2200
|
+
descriptor.validate(context);
|
|
2201
|
+
return Object.freeze(context);
|
|
2202
|
+
}
|
|
2203
|
+
function isHostInvocationTurnSelection(value) {
|
|
2204
|
+
return isRecord2(value) && Object.keys(value).length === 5 && value.schema_version === HOST_INVOCATION_TURN_SELECTION_SCHEMA && ["host_source_ref", "host_session_ref", "prompt_ref", "turn_generation_ref"].every((key) => safeRef(value[key]));
|
|
2205
|
+
}
|
|
2206
|
+
function exactKeys2(value, keys) {
|
|
2207
|
+
const actual = Object.keys(value);
|
|
2208
|
+
return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
|
|
2209
|
+
}
|
|
2210
|
+
function requiredKeys(value, required, optional) {
|
|
2211
|
+
const actual = Object.keys(value);
|
|
2212
|
+
const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
|
|
2213
|
+
return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
|
|
2214
|
+
}
|
|
2215
|
+
function isRecord2(value) {
|
|
2216
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2217
|
+
}
|
|
2218
|
+
function safeRef(value) {
|
|
2219
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
// host/relay/src/producer-frame.ts
|
|
2223
|
+
var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
2224
|
+
var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "turn_terminal", "producer_status"];
|
|
2225
|
+
var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
|
|
2226
|
+
var HOST_PRODUCER_FRAME_ERROR_CODES = [
|
|
2227
|
+
"invalid_frame_encoding",
|
|
2228
|
+
"invalid_frame_length",
|
|
2229
|
+
"frame_truncated",
|
|
2230
|
+
"unsupported_frame_schema",
|
|
2231
|
+
"invalid_frame_shape",
|
|
2232
|
+
"invalid_producer_identity",
|
|
2233
|
+
"frame_too_large"
|
|
2234
|
+
];
|
|
2235
|
+
|
|
2236
|
+
// host/relay/src/host-producer-core.ts
|
|
2237
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
2238
|
+
|
|
2239
|
+
// host/secure-file.ts
|
|
2240
|
+
import { execFile } from "node:child_process";
|
|
2241
|
+
import { constants } from "node:fs";
|
|
2242
|
+
import { lstat, open } from "node:fs/promises";
|
|
2243
|
+
import { dirname, win32 } from "node:path";
|
|
2244
|
+
import { fileURLToPath } from "node:url";
|
|
2245
|
+
var SecureFileCustodyError = class extends Error {
|
|
2246
|
+
name = "SecureFileCustodyError";
|
|
2247
|
+
reason;
|
|
2248
|
+
constructor(reason, message) {
|
|
2145
2249
|
super(message);
|
|
2146
|
-
this.
|
|
2147
|
-
this.failure = failure;
|
|
2250
|
+
this.reason = reason;
|
|
2148
2251
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
case "EACCES":
|
|
2156
|
-
case "EPERM":
|
|
2157
|
-
return "permission_denied";
|
|
2158
|
-
case "ENOENT":
|
|
2159
|
-
return "socket_missing";
|
|
2160
|
-
case "ECONNREFUSED":
|
|
2161
|
-
return "connection_refused";
|
|
2162
|
-
default:
|
|
2163
|
-
return "unavailable";
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
var HostRelayIpcAdmissionPort = class {
|
|
2167
|
-
endpoint;
|
|
2168
|
-
timeout_ms;
|
|
2169
|
-
constructor(endpoint, timeoutMs = 5e3) {
|
|
2170
|
-
this.endpoint = endpoint;
|
|
2171
|
-
this.timeout_ms = timeoutMs;
|
|
2172
|
-
}
|
|
2173
|
-
async admit(frame) {
|
|
2174
|
-
const response2 = await submitHostProducerFrame(this.endpoint, frame, this.timeout_ms);
|
|
2175
|
-
if (!relayAdmissionCode(response2.code)) throw new HostRelayIpcError(response2.code);
|
|
2176
|
-
return { code: response2.code, admitted_through_sequence: response2.admitted_through_sequence, delivered_through_sequence: response2.delivered_through_sequence };
|
|
2252
|
+
get code() {
|
|
2253
|
+
if (this.reason === "reparse_point") return "ELOOP";
|
|
2254
|
+
if (this.reason === "not_found") return "ENOENT";
|
|
2255
|
+
if (this.reason === "too_large") return "secure_file_too_large";
|
|
2256
|
+
if (this.reason === "already_exists") return "EEXIST";
|
|
2257
|
+
return this.reason === "not_private" ? "secure_file_not_private" : "secure_file_unavailable";
|
|
2177
2258
|
}
|
|
2178
2259
|
};
|
|
2179
|
-
var
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2260
|
+
var WINDOWS_SECUREFS_REFUSAL_SCHEMA = "ceal.host_securefs_refusal.v1";
|
|
2261
|
+
var WINDOWS_SECUREFS_TIMEOUT_MS = 1e4;
|
|
2262
|
+
var WINDOWS_SECUREFS_DIAGNOSTIC_BYTES = 4096;
|
|
2263
|
+
function secureOpenFlags(flags, platform) {
|
|
2264
|
+
if (platform === "win32") return flags;
|
|
2265
|
+
const nofollow = constants.O_NOFOLLOW;
|
|
2266
|
+
if (typeof nofollow === "number" && nofollow !== 0) return flags | nofollow;
|
|
2267
|
+
throw new Error("Secure file reads require O_NOFOLLOW on this platform.");
|
|
2268
|
+
}
|
|
2269
|
+
async function openNoFollow(path, flags = constants.O_RDONLY, mode, platform = process.platform, _custody = {}) {
|
|
2270
|
+
if (platform !== "win32") return await open(path, secureOpenFlags(flags, platform), mode);
|
|
2271
|
+
return await openWindowsIdentityScreened(path, flags, mode);
|
|
2272
|
+
}
|
|
2273
|
+
async function openWindowsIdentityScreened(path, flags, mode) {
|
|
2274
|
+
const handle = await open(path, flags, mode);
|
|
2275
|
+
try {
|
|
2276
|
+
const first = await handle.stat();
|
|
2277
|
+
if (!first.isFile()) throw new SecureFileCustodyError("not_private", "The Windows Host file is not a regular file.");
|
|
2278
|
+
await assertWindowsLinkFree(path, first);
|
|
2279
|
+
await assertWindowsIdentityStable(handle, first, path);
|
|
2280
|
+
return handle;
|
|
2281
|
+
} catch (error) {
|
|
2282
|
+
await handle.close();
|
|
2283
|
+
throw error;
|
|
2190
2284
|
}
|
|
2191
|
-
}
|
|
2192
|
-
function
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
} catch (error) {
|
|
2226
|
-
finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
|
|
2285
|
+
}
|
|
2286
|
+
async function assertWindowsLinkFree(path, first) {
|
|
2287
|
+
const entry = await lstat(path);
|
|
2288
|
+
if (entry.isSymbolicLink() || entry.dev !== first.dev || entry.ino !== first.ino) {
|
|
2289
|
+
throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
async function assertWindowsIdentityStable(handle, first, path) {
|
|
2293
|
+
const second = await handle.stat();
|
|
2294
|
+
if (second.dev !== first.dev || second.ino !== first.ino || second.size !== first.size || second.mtimeMs !== first.mtimeMs) {
|
|
2295
|
+
throw new SecureFileCustodyError("unavailable", "The Windows Host file changed while its identity was checked.");
|
|
2296
|
+
}
|
|
2297
|
+
const entry = await lstat(path);
|
|
2298
|
+
if (entry.isSymbolicLink()) throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
|
|
2299
|
+
if (entry.dev !== second.dev || entry.ino !== second.ino) throw new SecureFileCustodyError("unavailable", "The Windows Host file path no longer names the opened file.");
|
|
2300
|
+
}
|
|
2301
|
+
async function readPrivateFileWithSecurefs(path, maxBytes, custody = {}) {
|
|
2302
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) throw new TypeError("The securefs read bound must be a positive safe integer.");
|
|
2303
|
+
const result = await runWindowsSecurefs(["internal", "securefs", "read-private-file", path, String(maxBytes)], maxBytes, custody);
|
|
2304
|
+
if (result.exit_code === 0 && result.stderr.length === 0 && result.stdout.length <= maxBytes) return result.stdout;
|
|
2305
|
+
if (result.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned bytes with a refusal.");
|
|
2306
|
+
if (result.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command exceeded its output contract.");
|
|
2307
|
+
throw securefsRefusal(result.stderr);
|
|
2308
|
+
}
|
|
2309
|
+
async function runWindowsSecurefs(args, maxBytes, custody) {
|
|
2310
|
+
if (custody.run_securefs_command !== void 0) return await custody.run_securefs_command(args);
|
|
2311
|
+
const executable = custody.securefs_executable ?? resolveInstalledHostExecutable();
|
|
2312
|
+
const processArgs = [...custody.securefs_args_prefix ?? [], ...args];
|
|
2313
|
+
return await new Promise((resolve3, reject3) => {
|
|
2314
|
+
execFile(executable, processArgs, { encoding: "buffer", timeout: WINDOWS_SECUREFS_TIMEOUT_MS, maxBuffer: maxBytes + WINDOWS_SECUREFS_DIAGNOSTIC_BYTES }, (error, stdout, stderr) => {
|
|
2315
|
+
const exitCode = error === null ? 0 : typeof error.code === "number" ? error.code : null;
|
|
2316
|
+
if (exitCode === null) {
|
|
2317
|
+
reject3(new SecureFileCustodyError("unavailable", "The installed Windows Host securefs command could not run."));
|
|
2318
|
+
return;
|
|
2227
2319
|
}
|
|
2228
|
-
|
|
2229
|
-
socket.once("end", () => {
|
|
2230
|
-
if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
|
|
2320
|
+
resolve3({ exit_code: exitCode, stdout: Buffer.from(stdout), stderr: Buffer.from(stderr) });
|
|
2231
2321
|
});
|
|
2232
2322
|
});
|
|
2233
2323
|
}
|
|
2234
|
-
function
|
|
2235
|
-
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response is truncated: ${refusalFact("response_bytes", input.byteLength, `at least ${HOST_RELAY_IPC_LENGTH_BYTES}`)}.`);
|
|
2236
|
-
const length = input.readUInt32BE(0);
|
|
2237
|
-
if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES || input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2324
|
+
function securefsRefusal(stderr) {
|
|
2238
2325
|
let value;
|
|
2239
2326
|
try {
|
|
2240
|
-
value = JSON.parse(
|
|
2327
|
+
value = JSON.parse(stderr.toString("utf8"));
|
|
2241
2328
|
} catch {
|
|
2242
|
-
|
|
2329
|
+
return new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an unreadable refusal.");
|
|
2243
2330
|
}
|
|
2244
|
-
if (!
|
|
2245
|
-
return
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
return record2(value) && value.schema_version === HOST_RELAY_IPC_RESPONSE_SCHEMA && hostRelayIpcCode(value.code) && nonnegative(value.admitted_through_sequence) && nonnegative(value.delivered_through_sequence) && Object.keys(value).length === 4;
|
|
2331
|
+
if (!isRecord3(value) || value.schema_version !== WINDOWS_SECUREFS_REFUSAL_SCHEMA || typeof value.code !== "string") return new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an unreadable refusal.");
|
|
2332
|
+
if (value.code === "reparse_point") return new SecureFileCustodyError("reparse_point", "The Windows Host securefs file is a reparse point.");
|
|
2333
|
+
if (value.code === "not_found") return new SecureFileCustodyError("not_found", "The Windows Host securefs file was not found.");
|
|
2334
|
+
if (value.code === "too_large") return new SecureFileCustodyError("too_large", "The Windows Host securefs file exceeds its read bound.");
|
|
2335
|
+
if (value.code === "already_exists") return new SecureFileCustodyError("already_exists", "The Windows Host securefs target already exists.");
|
|
2336
|
+
if (value.code === "not_private") return new SecureFileCustodyError("not_private", "The Windows Host securefs file is not private to the current user.");
|
|
2337
|
+
return new SecureFileCustodyError("unavailable", "The Windows Host securefs command refused the file read.");
|
|
2252
2338
|
}
|
|
2253
|
-
function
|
|
2254
|
-
|
|
2339
|
+
function resolveInstalledHostExecutable() {
|
|
2340
|
+
const privateRuntimeRoot = dirname(fileURLToPath(import.meta.url));
|
|
2341
|
+
return win32.join(privateRuntimeRoot, "..", "..", "bin", "ceal-host.exe");
|
|
2255
2342
|
}
|
|
2256
|
-
function
|
|
2257
|
-
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
2258
|
-
}
|
|
2259
|
-
function record2(value) {
|
|
2343
|
+
function isRecord3(value) {
|
|
2260
2344
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2261
2345
|
}
|
|
2262
|
-
function
|
|
2263
|
-
|
|
2264
|
-
const length = buffer.readUInt32BE(0);
|
|
2265
|
-
if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFact("declared_bytes", length, `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}`)}.`);
|
|
2266
|
-
if (buffer.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2267
|
-
if (buffer.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC returned trailing bytes: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `the received message length minus ${HOST_RELAY_IPC_LENGTH_BYTES}` }, { name: "received_bytes", value: buffer.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2268
|
-
return decodeHostRelayIpcResponse(buffer);
|
|
2269
|
-
}
|
|
2270
|
-
function decodeCurrentPromptResponseBuffer(buffer) {
|
|
2271
|
-
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
|
|
2272
|
-
if (value === null) return null;
|
|
2273
|
-
if (!record2(value) || !exactKeys4(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "current_prompt_unavailable") throw new HostRelayIpcError("relay_unavailable");
|
|
2274
|
-
return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
|
|
2275
|
-
}
|
|
2276
|
-
function decodeLengthPrefixedJson(input, maximum) {
|
|
2277
|
-
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2278
|
-
const length = input.readUInt32BE(0);
|
|
2279
|
-
if (length === 0 || length > maximum) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC message length is invalid: ${refusalFact("declared_bytes", length, `between 1 and ${maximum}`)}.`);
|
|
2280
|
-
if (input.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2281
|
-
if (input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC message has trailing bytes: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `the received message length minus ${HOST_RELAY_IPC_LENGTH_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2282
|
-
try {
|
|
2283
|
-
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
|
|
2284
|
-
} catch {
|
|
2285
|
-
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC message is invalid.");
|
|
2286
|
-
}
|
|
2287
|
-
}
|
|
2288
|
-
function encodeLengthPrefixed(value) {
|
|
2289
|
-
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
2290
|
-
const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
|
|
2291
|
-
output.writeUInt32BE(body.length, 0);
|
|
2292
|
-
body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
|
|
2293
|
-
return output;
|
|
2346
|
+
function isOwnedBy(uid, expectedUid) {
|
|
2347
|
+
return Number.isSafeInteger(uid) && Number.isSafeInteger(expectedUid) && uid === expectedUid;
|
|
2294
2348
|
}
|
|
2295
|
-
function
|
|
2296
|
-
|
|
2297
|
-
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());
|
|
2298
2352
|
}
|
|
2299
|
-
function
|
|
2300
|
-
|
|
2353
|
+
function isPrivateFileMode(mode, platform = process.platform, _handle) {
|
|
2354
|
+
if (platform === "win32") return false;
|
|
2355
|
+
return (mode & 63) === 0;
|
|
2301
2356
|
}
|
|
2302
2357
|
|
|
2303
2358
|
// host/relay/src/host-producer-core.ts
|
|
@@ -2306,6 +2361,20 @@ function isHostTranscriptCursorReason(value) {
|
|
|
2306
2361
|
return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
|
|
2307
2362
|
}
|
|
2308
2363
|
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
2364
|
+
var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
|
|
2365
|
+
var HOST_INVOCATION_TRANSCRIPT_TAIL_MAX_BYTES = 1024 * 1024;
|
|
2366
|
+
function transcriptScanFailureKind(error, adapterFailureKind) {
|
|
2367
|
+
if (transcriptErrorCode(error) === "ELOOP") return "symlink";
|
|
2368
|
+
const adapter = adapterFailureKind(error);
|
|
2369
|
+
if (adapter !== void 0) return adapter;
|
|
2370
|
+
const code = transcriptErrorCode(error);
|
|
2371
|
+
if (code === "EFBIG") return "size_budget";
|
|
2372
|
+
if (code !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code)) return "read_error";
|
|
2373
|
+
return "other";
|
|
2374
|
+
}
|
|
2375
|
+
function transcriptErrorCode(error) {
|
|
2376
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
2377
|
+
}
|
|
2309
2378
|
var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
|
|
2310
2379
|
async function readTranscriptCompleteChunk(handle, offset, size) {
|
|
2311
2380
|
const available = Math.max(0, size - offset);
|
|
@@ -2350,10 +2419,10 @@ async function transcriptCursorBoundsReason(handle, cursor, size) {
|
|
|
2350
2419
|
return null;
|
|
2351
2420
|
}
|
|
2352
2421
|
function transcriptSha256(value) {
|
|
2353
|
-
return
|
|
2422
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
2354
2423
|
}
|
|
2355
2424
|
function transcriptObjectSha256(value) {
|
|
2356
|
-
return
|
|
2425
|
+
return createHash4("sha256").update(JSON.stringify(value), "utf8").digest("hex");
|
|
2357
2426
|
}
|
|
2358
2427
|
function isTranscriptRecord(value) {
|
|
2359
2428
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -2422,11 +2491,10 @@ var HostTranscriptWatcher = class {
|
|
|
2422
2491
|
async openTranscript(registration, scan, handlers) {
|
|
2423
2492
|
let handle;
|
|
2424
2493
|
try {
|
|
2425
|
-
handle = await
|
|
2494
|
+
handle = await openNoFollow(this.transcriptPathOf(registration));
|
|
2426
2495
|
return await scan(handle);
|
|
2427
2496
|
} catch (error) {
|
|
2428
2497
|
if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
|
|
2429
|
-
if (hostRelayExchangeFailure(error) !== void 0) throw error;
|
|
2430
2498
|
return handlers.failed(handle, error);
|
|
2431
2499
|
} finally {
|
|
2432
2500
|
await handle?.close();
|
|
@@ -2472,29 +2540,12 @@ var HostTranscriptWatcher = class {
|
|
|
2472
2540
|
return transcriptCursorBoundsReason(handle, cursor, size);
|
|
2473
2541
|
}
|
|
2474
2542
|
};
|
|
2475
|
-
var HOST_PRODUCER_STATE_IDENTITY = {
|
|
2476
|
-
codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
|
|
2477
|
-
claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
|
|
2478
|
-
};
|
|
2479
2543
|
var HostProducerDrainCore = class {
|
|
2480
2544
|
host;
|
|
2481
|
-
relay;
|
|
2482
|
-
statePath;
|
|
2483
|
-
stateSchema;
|
|
2484
|
-
installationRef;
|
|
2485
|
-
epoch;
|
|
2486
2545
|
now;
|
|
2487
2546
|
tail = Promise.resolve();
|
|
2488
2547
|
constructor(host, runtime) {
|
|
2489
|
-
if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
|
|
2490
|
-
const epoch = runtime.producer_epoch ?? 1;
|
|
2491
|
-
if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
|
|
2492
2548
|
this.host = host;
|
|
2493
|
-
this.relay = runtime.relay;
|
|
2494
|
-
this.statePath = join2(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
|
|
2495
|
-
this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
|
|
2496
|
-
this.installationRef = runtime.producer_installation_ref;
|
|
2497
|
-
this.epoch = epoch;
|
|
2498
2549
|
this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
|
|
2499
2550
|
}
|
|
2500
2551
|
drainOutbox() {
|
|
@@ -2504,118 +2555,29 @@ var HostProducerDrainCore = class {
|
|
|
2504
2555
|
}
|
|
2505
2556
|
async drainLocked() {
|
|
2506
2557
|
const read = await this.host.listEnvelopes();
|
|
2507
|
-
let
|
|
2558
|
+
let removed = 0;
|
|
2508
2559
|
let expired = 0;
|
|
2509
|
-
let retained = 0;
|
|
2510
2560
|
for (const envelope of read.entries) {
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
await this.host.removeEnvelope(envelope);
|
|
2514
|
-
expired += 1;
|
|
2515
|
-
continue;
|
|
2516
|
-
}
|
|
2517
|
-
const frames = await this.reserve(envelope);
|
|
2518
|
-
let complete = true;
|
|
2519
|
-
for (const frame of frames) {
|
|
2520
|
-
const outcome = await this.relay.admit(frame);
|
|
2521
|
-
if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
|
|
2522
|
-
complete = false;
|
|
2523
|
-
break;
|
|
2524
|
-
}
|
|
2525
|
-
}
|
|
2526
|
-
if (!complete) {
|
|
2527
|
-
retained += 1;
|
|
2528
|
-
break;
|
|
2529
|
-
}
|
|
2561
|
+
if (Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) expired += 1;
|
|
2562
|
+
else removed += 1;
|
|
2530
2563
|
await this.host.removeEnvelope(envelope);
|
|
2531
|
-
await this.release(this.host.submissionRefOf(envelope));
|
|
2532
|
-
admitted += 1;
|
|
2533
|
-
}
|
|
2534
|
-
return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
|
|
2535
|
-
}
|
|
2536
|
-
async hasReservation(submissionRef) {
|
|
2537
|
-
return (await this.readState()).pending[submissionRef] !== void 0;
|
|
2538
|
-
}
|
|
2539
|
-
async reserve(envelope) {
|
|
2540
|
-
const state = await this.readState();
|
|
2541
|
-
const submissionRef = this.host.submissionRefOf(envelope);
|
|
2542
|
-
const existing = state.pending[submissionRef];
|
|
2543
|
-
if (existing !== void 0) return existing;
|
|
2544
|
-
const drafts = await this.host.mapEnvelope(envelope);
|
|
2545
|
-
const frames = drafts.map((draft, offset) => ({
|
|
2546
|
-
schema_version: "ceal.host_producer_frame.v1",
|
|
2547
|
-
producer_id: this.host.producer_id,
|
|
2548
|
-
producer_installation_ref: this.installationRef,
|
|
2549
|
-
producer_epoch: this.epoch,
|
|
2550
|
-
sequence: state.next_sequence + offset,
|
|
2551
|
-
frame_kind: draft.frame_kind,
|
|
2552
|
-
occurred_at: this.host.createdAtOf(envelope),
|
|
2553
|
-
payload: draft.payload
|
|
2554
|
-
}));
|
|
2555
|
-
await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [submissionRef]: frames } });
|
|
2556
|
-
return frames;
|
|
2557
|
-
}
|
|
2558
|
-
async release(submissionRef) {
|
|
2559
|
-
const state = await this.readState();
|
|
2560
|
-
if (state.pending[submissionRef] === void 0) return;
|
|
2561
|
-
const pending = { ...state.pending };
|
|
2562
|
-
delete pending[submissionRef];
|
|
2563
|
-
await this.writeState({ ...state, pending });
|
|
2564
|
-
}
|
|
2565
|
-
async readState() {
|
|
2566
|
-
try {
|
|
2567
|
-
const value = JSON.parse(await readFile(this.statePath, "utf8"));
|
|
2568
|
-
return this.decodeState(value);
|
|
2569
|
-
} catch (error) {
|
|
2570
|
-
if (errorCode2(error) !== "ENOENT") throw error;
|
|
2571
|
-
return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
async writeState(state) {
|
|
2575
|
-
await mkdir2(this.host.outbox_directory, { recursive: true, mode: 448 });
|
|
2576
|
-
const temporary = `${this.statePath}.${process.pid}.new`;
|
|
2577
|
-
await writeFile(temporary, JSON.stringify(state), { mode: 384 });
|
|
2578
|
-
await rename2(temporary, this.statePath);
|
|
2579
|
-
}
|
|
2580
|
-
decodeState(value) {
|
|
2581
|
-
const label = this.host.producer_label;
|
|
2582
|
-
const fail = (code, message) => this.host.fail(code, message);
|
|
2583
|
-
if (!isDrainStateHeader(value, this.stateSchema, this.installationRef)) {
|
|
2584
|
-
throw fail("storage", `${label} producer state header is malformed or belongs to another installation (${refusalFacts([
|
|
2585
|
-
{ name: "schema_version", value: isTranscriptRecord(value) ? value.schema_version : void 0, expected: this.stateSchema },
|
|
2586
|
-
{ name: "producer_installation_ref", value: isTranscriptRecord(value) ? value.producer_installation_ref : void 0, expected: this.installationRef }
|
|
2587
|
-
])}).`);
|
|
2588
|
-
}
|
|
2589
|
-
if (Number(value.producer_epoch) > this.epoch) throw fail("storage", `${label} producer state epoch is ahead of this installation (${refusalFact("producer_epoch", value.producer_epoch, `at most ${this.epoch}`)}).`);
|
|
2590
|
-
const pending = {};
|
|
2591
|
-
const storedEpoch = Number(value.producer_epoch);
|
|
2592
|
-
for (const [submissionRef, frames] of Object.entries(value.pending)) {
|
|
2593
|
-
if (!isHostSafeRef(submissionRef) || !Array.isArray(frames) || frames.length === 0) throw fail("storage", `${label} producer pending reservation is malformed.`);
|
|
2594
|
-
pending[submissionRef] = frames.map((frame) => this.decodeStoredFrame(frame, storedEpoch));
|
|
2595
2564
|
}
|
|
2596
|
-
return {
|
|
2597
|
-
}
|
|
2598
|
-
decodeStoredFrame(value, storedEpoch) {
|
|
2599
|
-
const label = this.host.producer_label;
|
|
2600
|
-
const json = Buffer.from(JSON.stringify(value), "utf8");
|
|
2601
|
-
const encoded = Buffer.allocUnsafe(json.length + 4);
|
|
2602
|
-
encoded.writeUInt32BE(json.length, 0);
|
|
2603
|
-
json.copy(encoded, 4);
|
|
2604
|
-
const frame = decodeHostProducerFrame(encoded);
|
|
2605
|
-
if (frame.producer_id !== this.host.producer_id || frame.producer_installation_ref !== this.installationRef || frame.producer_epoch > storedEpoch) throw this.host.fail("storage", `${label} producer pending frame identity is malformed (${refusalFacts([
|
|
2606
|
-
{ name: "producer_id", value: frame.producer_id, expected: this.host.producer_id },
|
|
2607
|
-
{ name: "producer_installation_ref", value: frame.producer_installation_ref, expected: this.installationRef },
|
|
2608
|
-
{ name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${storedEpoch}` }
|
|
2609
|
-
])}).`);
|
|
2610
|
-
return frame;
|
|
2565
|
+
return { removed, expired, malformed: read.malformed };
|
|
2611
2566
|
}
|
|
2612
2567
|
};
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2568
|
+
|
|
2569
|
+
// host/relay/src/durable-atomic-replacement.ts
|
|
2570
|
+
import { lstat as lstat2, mkdir, open as open2, rename, unlink } from "node:fs/promises";
|
|
2571
|
+
var DEFAULT_PORT = {
|
|
2572
|
+
platform: process.platform,
|
|
2573
|
+
mkdir: (path, options) => mkdir(path, options),
|
|
2574
|
+
lstat: (path) => lstat2(path),
|
|
2575
|
+
open_file: (path) => open2(path, "wx", 384),
|
|
2576
|
+
rename,
|
|
2577
|
+
unlink,
|
|
2578
|
+
open_directory: (path) => open2(path),
|
|
2579
|
+
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
2580
|
+
};
|
|
2619
2581
|
|
|
2620
2582
|
// host/relay/src/relay-quarantine-store.ts
|
|
2621
2583
|
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
@@ -2627,11 +2589,21 @@ var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, produ
|
|
|
2627
2589
|
var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
2628
2590
|
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
2629
2591
|
|
|
2630
|
-
// host/relay/src/relay-
|
|
2631
|
-
var
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2592
|
+
// host/relay/src/relay-pipe-handshake.ts
|
|
2593
|
+
var RELAY_PIPE_FRAME_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
2594
|
+
|
|
2595
|
+
// host/relay/src/relay-ipc.ts
|
|
2596
|
+
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
2597
|
+
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
2598
|
+
"permission_denied",
|
|
2599
|
+
"socket_missing",
|
|
2600
|
+
"connection_refused",
|
|
2601
|
+
"closed_early",
|
|
2602
|
+
"response_invalid",
|
|
2603
|
+
"timed_out",
|
|
2604
|
+
"cancelled",
|
|
2605
|
+
"unavailable"
|
|
2606
|
+
]);
|
|
2635
2607
|
|
|
2636
2608
|
// host/codex/src/codex-invocation-context.ts
|
|
2637
2609
|
function codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef) {
|
|
@@ -2645,59 +2617,40 @@ function validateCodexHostInvocationContext(value) {
|
|
|
2645
2617
|
validateContextReferences(value);
|
|
2646
2618
|
validateContextTimes(value);
|
|
2647
2619
|
}
|
|
2648
|
-
function invocationContextFromBinding(binding, turnGenerationRef) {
|
|
2649
|
-
const context = {
|
|
2650
|
-
schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
|
|
2651
|
-
identity: binding.identity,
|
|
2652
|
-
prompt_ref: binding.prompt_ref,
|
|
2653
|
-
request_ref: binding.request_ref,
|
|
2654
|
-
bound_at: binding.bound_at,
|
|
2655
|
-
expires_at: binding.expires_at,
|
|
2656
|
-
...turnGenerationRef === void 0 ? {} : { turn_generation_ref: turnGenerationRef }
|
|
2657
|
-
};
|
|
2658
|
-
validateCodexHostInvocationContext(context);
|
|
2659
|
-
return Object.freeze(context);
|
|
2660
|
-
}
|
|
2661
2620
|
function validateIdentity(value) {
|
|
2662
|
-
if (!
|
|
2621
|
+
if (!isRecord4(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef2)) {
|
|
2663
2622
|
throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
|
|
2664
2623
|
}
|
|
2665
2624
|
}
|
|
2666
2625
|
function isContextShape(value) {
|
|
2667
|
-
return
|
|
2626
|
+
return isRecord4(value) && hasHostInvocationContextKeys(value, ["turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
|
|
2668
2627
|
}
|
|
2669
2628
|
function validateContextReferences(value) {
|
|
2670
|
-
if (!
|
|
2629
|
+
if (!safeRef2(value.prompt_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2671
2630
|
}
|
|
2672
2631
|
function validateContextTimes(value) {
|
|
2673
2632
|
const expiresAt = value.expires_at;
|
|
2674
|
-
|
|
2675
|
-
if (!timestamp2(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2676
|
-
if (typeof boundAt === "string" && Date.parse(boundAt) > Date.parse(expiresAt)) throw new CodexHostAdapterError("storage", `Codex Host invocation context has invalid binding time (${refusalFact("bound_at", boundAt, `at or before ${expiresAt}`)}).`);
|
|
2633
|
+
if (!timestamp(expiresAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2677
2634
|
}
|
|
2678
2635
|
function optionalSafeRef(value) {
|
|
2679
|
-
return value === void 0 ||
|
|
2680
|
-
}
|
|
2681
|
-
function optionalTimestamp(value) {
|
|
2682
|
-
return value === void 0 || timestamp2(value);
|
|
2636
|
+
return value === void 0 || safeRef2(value);
|
|
2683
2637
|
}
|
|
2684
|
-
function
|
|
2638
|
+
function safeRef2(value) {
|
|
2685
2639
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
2686
2640
|
}
|
|
2687
|
-
function
|
|
2641
|
+
function timestamp(value) {
|
|
2688
2642
|
return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
2689
2643
|
}
|
|
2690
|
-
function
|
|
2644
|
+
function isRecord4(value) {
|
|
2691
2645
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2692
2646
|
}
|
|
2693
2647
|
|
|
2694
2648
|
// host/codex/src/codex-outbox.ts
|
|
2695
|
-
var
|
|
2649
|
+
var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2696
2650
|
var CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS = 4096;
|
|
2697
2651
|
var CodexOutboxStore = class {
|
|
2698
2652
|
directory;
|
|
2699
2653
|
entriesDirectory;
|
|
2700
|
-
bindingsDirectory;
|
|
2701
2654
|
sessionsDirectory;
|
|
2702
2655
|
activeContextsDirectory;
|
|
2703
2656
|
turnSelectionsDirectory;
|
|
@@ -2706,17 +2659,16 @@ var CodexOutboxStore = class {
|
|
|
2706
2659
|
constructor(directory) {
|
|
2707
2660
|
if (typeof directory !== "string" || directory.length === 0) throw new CodexHostAdapterError("configuration", "Codex outbox directory is required.");
|
|
2708
2661
|
this.directory = directory;
|
|
2709
|
-
this.entriesDirectory =
|
|
2710
|
-
this.
|
|
2711
|
-
this.
|
|
2712
|
-
this.
|
|
2713
|
-
this.
|
|
2714
|
-
this.
|
|
2715
|
-
this.cursorsDirectory = join3(directory, "transcript-cursors");
|
|
2662
|
+
this.entriesDirectory = join2(directory, "entries");
|
|
2663
|
+
this.sessionsDirectory = join2(directory, "sessions");
|
|
2664
|
+
this.activeContextsDirectory = join2(directory, "active-contexts");
|
|
2665
|
+
this.turnSelectionsDirectory = join2(directory, "turn-selections");
|
|
2666
|
+
this.registrationsDirectory = join2(directory, "transcript-registrations");
|
|
2667
|
+
this.cursorsDirectory = join2(directory, "transcript-cursors");
|
|
2716
2668
|
}
|
|
2717
2669
|
async prepare() {
|
|
2718
|
-
for (const path of [this.directory, this.entriesDirectory, this.
|
|
2719
|
-
await
|
|
2670
|
+
for (const path of [this.directory, this.entriesDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
|
|
2671
|
+
await ensureOwnerDirectory(path);
|
|
2720
2672
|
}
|
|
2721
2673
|
}
|
|
2722
2674
|
async put(entry) {
|
|
@@ -2731,7 +2683,7 @@ var CodexOutboxStore = class {
|
|
|
2731
2683
|
for (const name of await readdir(this.entriesDirectory)) {
|
|
2732
2684
|
if (!name.endsWith(".json")) continue;
|
|
2733
2685
|
try {
|
|
2734
|
-
const value = JSON.parse(await
|
|
2686
|
+
const value = JSON.parse(await readOwnerStateFile(join2(this.entriesDirectory, name)));
|
|
2735
2687
|
validateEnvelope(value);
|
|
2736
2688
|
entries.push(value);
|
|
2737
2689
|
} catch {
|
|
@@ -2749,11 +2701,6 @@ var CodexOutboxStore = class {
|
|
|
2749
2701
|
if (!isNotFound(error)) throw storageError("Unable to remove the Codex outbox entry.");
|
|
2750
2702
|
}
|
|
2751
2703
|
}
|
|
2752
|
-
async writeBinding(binding) {
|
|
2753
|
-
validateBinding(binding);
|
|
2754
|
-
await this.prepare();
|
|
2755
|
-
await writeAtomicJson(this.bindingPath(binding.identity), binding);
|
|
2756
|
-
}
|
|
2757
2704
|
/**
|
|
2758
2705
|
* Select the exact Host turn that model-reachable commands may use. A new
|
|
2759
2706
|
* prompt replaces this projection, so it cannot inherit the prior turn's
|
|
@@ -2780,7 +2727,7 @@ var CodexOutboxStore = class {
|
|
|
2780
2727
|
await writeAtomicJson(this.turnSelectionPath(selection.host_source_ref, selection.host_session_ref), selection);
|
|
2781
2728
|
}
|
|
2782
2729
|
async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
|
|
2783
|
-
if (!
|
|
2730
|
+
if (!SAFE_REF3.test(hostSourceRef) || !SAFE_REF3.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
|
|
2784
2731
|
await this.prepare();
|
|
2785
2732
|
return readOptionalJson(this.turnSelectionPath(hostSourceRef, hostSessionRef), validateTurnSelection);
|
|
2786
2733
|
}
|
|
@@ -2793,23 +2740,11 @@ var CodexOutboxStore = class {
|
|
|
2793
2740
|
await writeAtomicJson(path, { ...active, turn_generation_ref: turnGenerationRef });
|
|
2794
2741
|
return true;
|
|
2795
2742
|
}
|
|
2796
|
-
/**
|
|
2797
|
-
* Publish a Request reference only while the acknowledged binding is still
|
|
2798
|
-
* the active turn. A delayed relay response therefore cannot overwrite a
|
|
2799
|
-
* newer prompt's context.
|
|
2800
|
-
*/
|
|
2801
|
-
async bindInvocationContext(binding, now = /* @__PURE__ */ new Date()) {
|
|
2802
|
-
validateBinding(binding);
|
|
2803
|
-
const active = await this.readInvocationContext(binding.identity.host_source_ref, binding.identity.host_session_ref, now);
|
|
2804
|
-
if (active === null || !sameIdentity(active.identity, binding.identity)) return false;
|
|
2805
|
-
await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding, active.turn_generation_ref));
|
|
2806
|
-
return true;
|
|
2807
|
-
}
|
|
2808
2743
|
/** Return only the live session projection; never fall back to another turn. */
|
|
2809
2744
|
async readInvocationContext(hostSourceRef, hostSessionRef, now = /* @__PURE__ */ new Date()) {
|
|
2810
2745
|
if (!(now instanceof Date) || !Number.isFinite(now.getTime())) throw new CodexHostAdapterError("invalid_input", "Codex Host context clock is invalid.");
|
|
2811
2746
|
const context = await this.readRawInvocationContext(hostSourceRef, hostSessionRef);
|
|
2812
|
-
return isLiveSessionProjection(context, now)
|
|
2747
|
+
return isLiveSessionProjection(context, now) ? context : null;
|
|
2813
2748
|
}
|
|
2814
2749
|
/**
|
|
2815
2750
|
* The latest Codex session projection even when past expiry, so the
|
|
@@ -2817,10 +2752,10 @@ var CodexOutboxStore = class {
|
|
|
2817
2752
|
* a missing one. Every other caller keeps the fail-closed read above.
|
|
2818
2753
|
*/
|
|
2819
2754
|
async readRawInvocationContext(hostSourceRef, hostSessionRef) {
|
|
2820
|
-
if (!
|
|
2755
|
+
if (!SAFE_REF3.test(hostSourceRef) || !SAFE_REF3.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
|
|
2821
2756
|
await this.prepare();
|
|
2822
2757
|
const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, (file) => readOptionalJson(file, validateCodexHostInvocationContext));
|
|
2823
|
-
return
|
|
2758
|
+
return context;
|
|
2824
2759
|
}
|
|
2825
2760
|
/** Clear the projection only if the caller still owns its exact active turn. */
|
|
2826
2761
|
async clearInvocationContext(identity, now = /* @__PURE__ */ new Date()) {
|
|
@@ -2834,11 +2769,6 @@ var CodexOutboxStore = class {
|
|
|
2834
2769
|
}
|
|
2835
2770
|
return true;
|
|
2836
2771
|
}
|
|
2837
|
-
async readBinding(identity) {
|
|
2838
|
-
validateIdentity2(identity);
|
|
2839
|
-
await this.prepare();
|
|
2840
|
-
return readOptionalJson(this.bindingPath(identity), validateBinding);
|
|
2841
|
-
}
|
|
2842
2772
|
async saveSession(snapshot) {
|
|
2843
2773
|
validateSession(snapshot);
|
|
2844
2774
|
await this.prepare();
|
|
@@ -2846,7 +2776,7 @@ var CodexOutboxStore = class {
|
|
|
2846
2776
|
}
|
|
2847
2777
|
async readSession(sourceRef, sessionRef) {
|
|
2848
2778
|
const identity = { host_source_ref: sourceRef, host_session_ref: sessionRef };
|
|
2849
|
-
if (!
|
|
2779
|
+
if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex session identity is invalid.");
|
|
2850
2780
|
await this.prepare();
|
|
2851
2781
|
return readOptionalJson(this.sessionPath(identity.host_source_ref, identity.host_session_ref), validateSession);
|
|
2852
2782
|
}
|
|
@@ -2879,7 +2809,7 @@ var CodexOutboxStore = class {
|
|
|
2879
2809
|
for (const name of await readdir(this.registrationsDirectory)) {
|
|
2880
2810
|
if (!name.endsWith(".json")) continue;
|
|
2881
2811
|
try {
|
|
2882
|
-
const value = JSON.parse(await
|
|
2812
|
+
const value = JSON.parse(await readOwnerStateFile(join2(this.registrationsDirectory, name)));
|
|
2883
2813
|
validateRegistration(value);
|
|
2884
2814
|
registrations.push(value);
|
|
2885
2815
|
} catch {
|
|
@@ -2898,48 +2828,37 @@ var CodexOutboxStore = class {
|
|
|
2898
2828
|
await writeAtomicJson(this.cursorPath(cursor.host_source_ref, cursor.host_session_ref), cursor);
|
|
2899
2829
|
}
|
|
2900
2830
|
async findByAlias(sourceRef, sessionRef, alias) {
|
|
2901
|
-
if (!
|
|
2831
|
+
if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef) || !SAFE_REF3.test(alias)) return null;
|
|
2902
2832
|
const read = await this.list();
|
|
2903
2833
|
const envelope = findEnvelopeByAlias(read.entries, sourceRef, sessionRef, alias);
|
|
2904
2834
|
if (envelope !== null) return envelopeMatch(envelope);
|
|
2905
2835
|
await this.prepare();
|
|
2906
|
-
const binding = await findBindingByAlias(this.bindingsDirectory, sourceRef, sessionRef, alias);
|
|
2907
|
-
if (binding !== null) return bindingMatch(binding);
|
|
2908
2836
|
return null;
|
|
2909
2837
|
}
|
|
2910
2838
|
async hasPriorTurn(sourceRef, sessionRef) {
|
|
2911
|
-
if (!
|
|
2839
|
+
if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef)) return false;
|
|
2912
2840
|
const read = await this.list();
|
|
2913
2841
|
if (read.entries.some((entry) => entry.identity.host_source_ref === sourceRef && entry.identity.host_session_ref === sessionRef)) return true;
|
|
2914
|
-
await this.prepare();
|
|
2915
|
-
for (const name of await readdir(this.bindingsDirectory)) {
|
|
2916
|
-
if (!name.endsWith(".json")) continue;
|
|
2917
|
-
const binding = await readBindingCandidate(this.bindingsDirectory, name);
|
|
2918
|
-
if (binding?.identity.host_source_ref === sourceRef && binding.identity.host_session_ref === sessionRef) return true;
|
|
2919
|
-
}
|
|
2920
2842
|
return false;
|
|
2921
2843
|
}
|
|
2922
2844
|
entryPath(submissionRef) {
|
|
2923
|
-
if (!
|
|
2924
|
-
return
|
|
2925
|
-
}
|
|
2926
|
-
bindingPath(identity) {
|
|
2927
|
-
return join3(this.bindingsDirectory, `${identityKey(identity)}.json`);
|
|
2845
|
+
if (!SAFE_REF3.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
|
|
2846
|
+
return join2(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
|
|
2928
2847
|
}
|
|
2929
2848
|
sessionPath(sourceRef, sessionRef) {
|
|
2930
|
-
return
|
|
2849
|
+
return join2(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
|
|
2931
2850
|
}
|
|
2932
2851
|
activeContextPath(identity) {
|
|
2933
|
-
return
|
|
2852
|
+
return join2(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
|
|
2934
2853
|
}
|
|
2935
2854
|
turnSelectionPath(sourceRef, sessionRef) {
|
|
2936
|
-
return
|
|
2855
|
+
return join2(this.turnSelectionsDirectory, `${invocationContextKey({ host_source_ref: sourceRef, host_session_ref: sessionRef })}.json`);
|
|
2937
2856
|
}
|
|
2938
2857
|
registrationPath(sourceRef, sessionRef) {
|
|
2939
|
-
return
|
|
2858
|
+
return join2(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
|
|
2940
2859
|
}
|
|
2941
2860
|
cursorPath(sourceRef, sessionRef) {
|
|
2942
|
-
return
|
|
2861
|
+
return join2(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
|
|
2943
2862
|
}
|
|
2944
2863
|
};
|
|
2945
2864
|
async function removeOwnerState(path, what) {
|
|
@@ -2950,14 +2869,11 @@ async function removeOwnerState(path, what) {
|
|
|
2950
2869
|
}
|
|
2951
2870
|
}
|
|
2952
2871
|
function sessionStateKey(sourceRef, sessionRef) {
|
|
2953
|
-
if (!
|
|
2872
|
+
if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(sessionRef)) throw storageError("Codex transcript session identity is malformed.");
|
|
2954
2873
|
return cealRequestSha256({ host_source_ref: sourceRef, host_session_ref: sessionRef });
|
|
2955
2874
|
}
|
|
2956
2875
|
function envelopeMatch(envelope) {
|
|
2957
|
-
return { identity: envelope.identity, prompt_ref: envelope.prompt_ref,
|
|
2958
|
-
}
|
|
2959
|
-
function bindingMatch(binding) {
|
|
2960
|
-
return { identity: binding.identity, prompt_ref: binding.prompt_ref, request_ref: binding.request_ref, binding };
|
|
2876
|
+
return { identity: envelope.identity, prompt_ref: envelope.prompt_ref, envelope };
|
|
2961
2877
|
}
|
|
2962
2878
|
function findEnvelopeByAlias(entries, sourceRef, sessionRef, alias) {
|
|
2963
2879
|
for (const envelope of entries) {
|
|
@@ -2966,24 +2882,6 @@ function findEnvelopeByAlias(entries, sourceRef, sessionRef, alias) {
|
|
|
2966
2882
|
}
|
|
2967
2883
|
return null;
|
|
2968
2884
|
}
|
|
2969
|
-
async function findBindingByAlias(directory, sourceRef, sessionRef, alias) {
|
|
2970
|
-
for (const name of await readdir(directory)) {
|
|
2971
|
-
if (!name.endsWith(".json")) continue;
|
|
2972
|
-
const binding = await readBindingCandidate(directory, name);
|
|
2973
|
-
if (binding === null || binding.identity.host_source_ref !== sourceRef || binding.identity.host_session_ref !== sessionRef) continue;
|
|
2974
|
-
if (binding.codex_turn_ids.includes(alias)) return binding;
|
|
2975
|
-
}
|
|
2976
|
-
return null;
|
|
2977
|
-
}
|
|
2978
|
-
async function readBindingCandidate(directory, name) {
|
|
2979
|
-
try {
|
|
2980
|
-
const value = JSON.parse(await readPrivateFile(join3(directory, name)));
|
|
2981
|
-
validateBinding(value);
|
|
2982
|
-
return value;
|
|
2983
|
-
} catch {
|
|
2984
|
-
return null;
|
|
2985
|
-
}
|
|
2986
|
-
}
|
|
2987
2885
|
function identityKey(identity) {
|
|
2988
2886
|
validateIdentity2(identity);
|
|
2989
2887
|
return cealRequestSha256(identity);
|
|
@@ -2996,15 +2894,15 @@ async function writeAtomicJson(path, value) {
|
|
|
2996
2894
|
if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
|
|
2997
2895
|
const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
2998
2896
|
try {
|
|
2999
|
-
const handle = await open3(temp, "wx"
|
|
2897
|
+
const handle = await open3(temp, "wx");
|
|
3000
2898
|
try {
|
|
3001
2899
|
await handle.writeFile(text, "utf8");
|
|
3002
2900
|
await handle.sync();
|
|
3003
2901
|
} finally {
|
|
3004
2902
|
await handle.close();
|
|
3005
2903
|
}
|
|
3006
|
-
await
|
|
3007
|
-
await syncDirectory(
|
|
2904
|
+
await rename2(temp, path);
|
|
2905
|
+
await syncDirectory(dirname2(path));
|
|
3008
2906
|
} catch (error) {
|
|
3009
2907
|
try {
|
|
3010
2908
|
await unlink2(temp);
|
|
@@ -3027,7 +2925,7 @@ async function syncDirectory(path) {
|
|
|
3027
2925
|
}
|
|
3028
2926
|
async function readOptionalJson(path, validate) {
|
|
3029
2927
|
try {
|
|
3030
|
-
const value = JSON.parse(await
|
|
2928
|
+
const value = JSON.parse(await readOwnerStateFile(path));
|
|
3031
2929
|
validate(value);
|
|
3032
2930
|
return value;
|
|
3033
2931
|
} catch (error) {
|
|
@@ -3036,36 +2934,30 @@ async function readOptionalJson(path, validate) {
|
|
|
3036
2934
|
throw storageError("Codex owner state is malformed.");
|
|
3037
2935
|
}
|
|
3038
2936
|
}
|
|
3039
|
-
async function
|
|
2937
|
+
async function ensureOwnerDirectory(path) {
|
|
3040
2938
|
try {
|
|
3041
|
-
await
|
|
2939
|
+
await stat(path);
|
|
3042
2940
|
} catch (error) {
|
|
3043
2941
|
if (!isNotFound(error)) throw storageError("Codex owner state directory is unavailable.");
|
|
3044
|
-
await
|
|
2942
|
+
await mkdir2(path, { recursive: true });
|
|
3045
2943
|
}
|
|
3046
|
-
const stats = await
|
|
3047
|
-
|
|
3048
|
-
if (!stats.isDirectory() || stats.isSymbolicLink()) throw storageError(`Codex owner state path is not a directory (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a directory mode (0o40xxx)")}).`);
|
|
3049
|
-
if ((stats.mode & 63) !== 0) throw storageError(`Codex owner state directory is readable beyond its owner (${refusalFact("permission_bits", `0o${(stats.mode & 511).toString(8)}`, "no group or other bits")}).`);
|
|
3050
|
-
if (stats.uid !== uid) throw storageError(`Codex owner state directory is owned by another user (${refusalFact("uid", stats.uid, `expected ${uid}`)}).`);
|
|
2944
|
+
const stats = await stat(path);
|
|
2945
|
+
if (!stats.isDirectory()) throw storageError("Codex owner state path is not a directory.");
|
|
3051
2946
|
}
|
|
3052
|
-
async function
|
|
2947
|
+
async function readOwnerStateFile(path) {
|
|
3053
2948
|
let handle;
|
|
3054
2949
|
try {
|
|
3055
|
-
handle = await open3(path
|
|
2950
|
+
handle = await open3(path);
|
|
3056
2951
|
const stats = await handle.stat();
|
|
3057
|
-
|
|
3058
|
-
if (!stats.isFile()) throw storageError(`Codex owner state path is not a regular file (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a regular-file mode (0o100xxx)")}).`);
|
|
3059
|
-
if ((stats.mode & 63) !== 0) throw storageError(`Codex owner state file is readable beyond its owner (${refusalFact("permission_bits", `0o${(stats.mode & 511).toString(8)}`, "no group or other bits")}).`);
|
|
3060
|
-
if (stats.uid !== uid) throw storageError(`Codex owner state file is owned by another user (${refusalFact("uid", stats.uid, `expected ${uid}`)}).`);
|
|
2952
|
+
if (!stats.isFile()) throw storageError("Codex owner state path is not a regular file.");
|
|
3061
2953
|
return await handle.readFile("utf8");
|
|
3062
2954
|
} finally {
|
|
3063
2955
|
await handle?.close();
|
|
3064
2956
|
}
|
|
3065
2957
|
}
|
|
3066
2958
|
function validateEnvelope(value) {
|
|
3067
|
-
if (!
|
|
3068
|
-
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")}).`);
|
|
3069
2961
|
if (value.schema_version !== CODEX_OUTBOX_SCHEMA_VERSION) throw storageError(`Codex outbox entry is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_OUTBOX_SCHEMA_VERSION}`)}).`);
|
|
3070
2962
|
validateIdentity2(value.identity);
|
|
3071
2963
|
validateEnvelopeReferences(value);
|
|
@@ -3073,22 +2965,20 @@ function validateEnvelope(value) {
|
|
|
3073
2965
|
validateEnvelopeTimestamps(value);
|
|
3074
2966
|
}
|
|
3075
2967
|
function validateEnvelopeReferences(value) {
|
|
3076
|
-
if (!
|
|
3077
|
-
if (!
|
|
3078
|
-
if (!
|
|
3079
|
-
if (!
|
|
3080
|
-
if (value.request_ref !== void 0 && !SAFE_REF4.test(String(value.request_ref))) throw storageError("Codex outbox Request reference is malformed.");
|
|
3081
|
-
if (value.codex_turn_id !== void 0 && !SAFE_REF4.test(String(value.codex_turn_id))) throw storageError("Codex outbox turn identity is malformed.");
|
|
2968
|
+
if (!SAFE_REF3.test(String(value.host_input_ref))) throw storageError("Codex outbox input reference is malformed.");
|
|
2969
|
+
if (!SAFE_REF3.test(String(value.prompt_ref))) throw storageError("Codex outbox references are malformed.");
|
|
2970
|
+
if (!SAFE_REF3.test(String(value.submission_ref))) throw storageError("Codex outbox references are malformed.");
|
|
2971
|
+
if (value.codex_turn_id !== void 0 && !SAFE_REF3.test(String(value.codex_turn_id))) throw storageError("Codex outbox turn identity is malformed.");
|
|
3082
2972
|
if (typeof value.origin_input !== "boolean") throw storageError("Codex outbox origin disposition is malformed.");
|
|
3083
2973
|
}
|
|
3084
2974
|
function validateRegistration(value) {
|
|
3085
|
-
if (!
|
|
2975
|
+
if (!isRecord5(value)) throw storageError("Codex transcript registration is malformed.");
|
|
3086
2976
|
if (!sameKeys(value, ["schema_version", "host_source_ref", "host_session_ref", "codex_session_id", "transcript_path", "registered_at", "expires_at"], ["cwd"])) throw storageError(`Codex transcript registration key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the registration key set")}).`);
|
|
3087
2977
|
if (value.schema_version !== CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION) throw storageError(`Codex transcript registration is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION}`)}).`);
|
|
3088
2978
|
validateRegistrationFields(value);
|
|
3089
2979
|
}
|
|
3090
2980
|
function validateRegistrationFields(value) {
|
|
3091
|
-
if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" &&
|
|
2981
|
+
if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate))) throw storageError(`Codex transcript registration references are malformed (${refusalFacts([{ name: "host_source_ref", value: value.host_source_ref }, { name: "host_session_ref", value: value.host_session_ref }, { name: "codex_session_id", value: value.codex_session_id }])}).`);
|
|
3092
2982
|
if (!validRegistrationCwd(value.cwd)) throw storageError(`Codex transcript registration working directory is malformed (${refusalFact("cwd_characters", typeof value.cwd === "string" ? value.cwd.length : 0, `an absolute path of at most ${CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS} characters`)}).`);
|
|
3093
2983
|
if (typeof value.transcript_path !== "string") throw storageError(`Codex transcript registration path is malformed (${refusalFact("kind", typeof value.transcript_path, "string")}).`);
|
|
3094
2984
|
if (value.transcript_path.length === 0 || value.transcript_path.length > CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS) throw storageError(`Codex transcript registration path is malformed (${refusalFact("characters", value.transcript_path.length, `from 1 through ${CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS}`)}).`);
|
|
@@ -3098,7 +2988,7 @@ function validRegistrationCwd(value) {
|
|
|
3098
2988
|
return value === void 0 || typeof value === "string" && isAbsolute2(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
|
|
3099
2989
|
}
|
|
3100
2990
|
function validateCursor(value) {
|
|
3101
|
-
if (!
|
|
2991
|
+
if (!isRecord5(value)) throw storageError("Codex transcript cursor is malformed.");
|
|
3102
2992
|
if (!sameKeys(value, ["schema_version", "host_source_ref", "host_session_ref", "transcript_path", "device", "inode", "offset", "last_ordinal", "anchor_start", "anchor_sha256", "header_validated", "status", "updated_at"], ["active_task", "reason"])) throw storageError(`Codex transcript cursor key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the cursor key set")}).`);
|
|
3103
2993
|
if (value.schema_version !== CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION) throw storageError(`Codex transcript cursor is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION}`)}).`);
|
|
3104
2994
|
if (!validCursorSource(value) || !validCursorPosition(value) || !validCursorStatus(value)) throw storageError("Codex transcript cursor fields are malformed.");
|
|
@@ -3106,7 +2996,7 @@ function validateCursor(value) {
|
|
|
3106
2996
|
if (value.reason !== void 0 && !isHostTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
|
|
3107
2997
|
}
|
|
3108
2998
|
function validCursorSource(value) {
|
|
3109
|
-
return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" &&
|
|
2999
|
+
return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate)) && typeof value.transcript_path === "string" && value.transcript_path.length > 0 && typeof value.device === "string" && typeof value.inode === "string";
|
|
3110
3000
|
}
|
|
3111
3001
|
function validCursorPosition(value) {
|
|
3112
3002
|
return nonNegativeInteger(value.offset) && nonNegativeInteger(value.last_ordinal) && nonNegativeInteger(value.anchor_start) && typeof value.anchor_sha256 === "string" && /^[a-f0-9]{64}$/u.test(value.anchor_sha256) && typeof value.header_validated === "boolean";
|
|
@@ -3115,23 +3005,23 @@ function validCursorStatus(value) {
|
|
|
3115
3005
|
return (value.status === "active" || value.status === "quarantined") && isTimestamp(value.updated_at);
|
|
3116
3006
|
}
|
|
3117
3007
|
function validActiveTask(value) {
|
|
3118
|
-
if (!
|
|
3008
|
+
if (!isRecord5(value) || !sameKeys(value, ["codex_turn_id", "host_turn_ref", "started_at"], ["origin_host_input_ref", "prompt_ref", "model", "token_usage", "last_total_usage", "token_usage_gap_reason"])) return false;
|
|
3119
3009
|
return validActiveTaskIdentity(value) && validActiveTaskReferences(value) && validActiveTaskUsage(value);
|
|
3120
3010
|
}
|
|
3121
3011
|
function validActiveTaskIdentity(value) {
|
|
3122
|
-
return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" &&
|
|
3012
|
+
return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate)) && isTimestamp(value.started_at);
|
|
3123
3013
|
}
|
|
3124
3014
|
function validActiveTaskReferences(value) {
|
|
3125
3015
|
return optionalSafeRef2(value.origin_host_input_ref) && optionalSafeRef2(value.prompt_ref) && (value.model === void 0 || typeof value.model === "string" && value.model.length > 0 && value.model.length <= 256);
|
|
3126
3016
|
}
|
|
3127
3017
|
function optionalSafeRef2(value) {
|
|
3128
|
-
return value === void 0 || typeof value === "string" &&
|
|
3018
|
+
return value === void 0 || typeof value === "string" && SAFE_REF3.test(value);
|
|
3129
3019
|
}
|
|
3130
3020
|
function validActiveTaskUsage(value) {
|
|
3131
3021
|
return (value.token_usage === void 0 || validTokenUsage(value.token_usage)) && (value.last_total_usage === void 0 || validTokenUsage(value.last_total_usage)) && (value.token_usage_gap_reason === void 0 || value.token_usage_gap_reason === "token_count_shape_not_attributable");
|
|
3132
3022
|
}
|
|
3133
3023
|
function validTokenUsage(value) {
|
|
3134
|
-
if (!
|
|
3024
|
+
if (!isRecord5(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
|
|
3135
3025
|
if (![value.input_tokens, value.cached_input_tokens, value.cache_write_input_tokens, value.output_tokens, value.reasoning_output_tokens, value.total_tokens].every(nonNegativeInteger)) return false;
|
|
3136
3026
|
return value.total_tokens === Number(value.input_tokens) + Number(value.output_tokens) && Number(value.cached_input_tokens) + Number(value.cache_write_input_tokens) <= Number(value.input_tokens) && Number(value.reasoning_output_tokens) <= Number(value.output_tokens);
|
|
3137
3027
|
}
|
|
@@ -3141,7 +3031,7 @@ function nonNegativeInteger(value) {
|
|
|
3141
3031
|
function validateEnvelopeObservations(value) {
|
|
3142
3032
|
if (!Array.isArray(value.observations)) throw storageError("Codex outbox observations are malformed.");
|
|
3143
3033
|
try {
|
|
3144
|
-
for (const
|
|
3034
|
+
for (const observation of value.observations) validateCealHostObservationDraft(observation);
|
|
3145
3035
|
} catch {
|
|
3146
3036
|
throw storageError("Codex outbox observations are malformed.");
|
|
3147
3037
|
}
|
|
@@ -3150,34 +3040,23 @@ function validateEnvelopeTimestamps(value) {
|
|
|
3150
3040
|
if (!isTimestamp(value.created_at)) throw storageError("Codex outbox timestamps are malformed.");
|
|
3151
3041
|
if (!isTimestamp(value.expires_at)) throw storageError("Codex outbox timestamps are malformed.");
|
|
3152
3042
|
}
|
|
3153
|
-
function validateBinding(value) {
|
|
3154
|
-
if (!isRecord4(value)) throw storageError("Codex turn binding is malformed.");
|
|
3155
|
-
if (!sameKeys(value, ["schema_version", "identity", "prompt_ref", "request_ref", "codex_turn_ids", "bound_at", "expires_at"])) throw storageError(`Codex turn binding key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the turn binding key set")}).`);
|
|
3156
|
-
if (value.schema_version !== CODEX_TURN_BINDING_SCHEMA_VERSION) throw storageError(`Codex turn binding is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_TURN_BINDING_SCHEMA_VERSION}`)}).`);
|
|
3157
|
-
validateIdentity2(value.identity);
|
|
3158
|
-
if (!SAFE_REF4.test(String(value.prompt_ref)) || !SAFE_REF4.test(String(value.request_ref))) throw storageError("Codex turn binding references are malformed.");
|
|
3159
|
-
if (!isStringList(value.codex_turn_ids) || !isTimestamp(value.bound_at) || !isTimestamp(value.expires_at)) throw storageError("Codex turn binding fields are malformed.");
|
|
3160
|
-
}
|
|
3161
3043
|
function validateSession(value) {
|
|
3162
|
-
if (!
|
|
3044
|
+
if (!isRecord5(value)) throw storageError("Codex session snapshot is malformed.");
|
|
3163
3045
|
if (!sameKeys(value, ["schema_version", "host_session_ref", "host_source_ref", "observed_at", "value", "expires_at"])) throw storageError(`Codex session snapshot key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the session snapshot key set")}).`);
|
|
3164
3046
|
if (value.schema_version !== CODEX_SESSION_SCHEMA_VERSION) throw storageError(`Codex session snapshot is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_SESSION_SCHEMA_VERSION}`)}).`);
|
|
3165
|
-
if (!
|
|
3047
|
+
if (!SAFE_REF3.test(String(value.host_session_ref)) || !SAFE_REF3.test(String(value.host_source_ref)) || !isTimestamp(value.observed_at) || !isTimestamp(value.expires_at) || !isJsonValue(value.value)) throw storageError("Codex session snapshot fields are malformed.");
|
|
3166
3048
|
}
|
|
3167
3049
|
function validateTurnSelection(value) {
|
|
3168
3050
|
if (!isHostInvocationTurnSelection(value)) throw storageError("Codex Host turn selection is malformed.");
|
|
3169
3051
|
}
|
|
3170
3052
|
function validateIdentity2(value) {
|
|
3171
|
-
if (!
|
|
3053
|
+
if (!isRecord5(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every((candidate) => typeof candidate === "string" && SAFE_REF3.test(candidate))) throw storageError("Codex Host identity is malformed.");
|
|
3172
3054
|
}
|
|
3173
3055
|
function isJsonValue(value) {
|
|
3174
3056
|
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
3175
3057
|
if (typeof value === "number") return Number.isFinite(value);
|
|
3176
3058
|
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
3177
|
-
return
|
|
3178
|
-
}
|
|
3179
|
-
function isStringList(value) {
|
|
3180
|
-
return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate));
|
|
3059
|
+
return isRecord5(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
|
|
3181
3060
|
}
|
|
3182
3061
|
function isTimestamp(value) {
|
|
3183
3062
|
return typeof value === "string" && value.length <= 64 && !Number.isNaN(Date.parse(value));
|
|
@@ -3187,23 +3066,22 @@ function sameKeys(value, required, optional = []) {
|
|
|
3187
3066
|
const keys = Object.keys(value);
|
|
3188
3067
|
return keys.length >= required.length && keys.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
|
|
3189
3068
|
}
|
|
3190
|
-
function
|
|
3069
|
+
function isRecord5(value) {
|
|
3191
3070
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3192
3071
|
}
|
|
3193
3072
|
function isNotFound(error) {
|
|
3194
|
-
return
|
|
3073
|
+
return isRecord5(error) && error.code === "ENOENT";
|
|
3195
3074
|
}
|
|
3196
3075
|
function storageError(message) {
|
|
3197
3076
|
return new CodexHostAdapterError("storage", message);
|
|
3198
3077
|
}
|
|
3199
3078
|
|
|
3200
3079
|
// host/codex/src/codex-transcript-baseline.ts
|
|
3201
|
-
import { createHash as
|
|
3202
|
-
import {
|
|
3203
|
-
import { open as open4 } from "node:fs/promises";
|
|
3080
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
3081
|
+
import { constants as constants2 } from "node:fs";
|
|
3204
3082
|
|
|
3205
3083
|
// host/codex/src/codex-transcript-decoder.ts
|
|
3206
|
-
var
|
|
3084
|
+
var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
|
|
3207
3085
|
var MAX_PROMPT_BYTES = 64 * 1024;
|
|
3208
3086
|
function decodeCodexTranscriptLine(line) {
|
|
3209
3087
|
let value;
|
|
@@ -3212,40 +3090,40 @@ function decodeCodexTranscriptLine(line) {
|
|
|
3212
3090
|
} catch {
|
|
3213
3091
|
reject2("Codex transcript record is not valid JSON.");
|
|
3214
3092
|
}
|
|
3215
|
-
if (!
|
|
3093
|
+
if (!isRecord6(value)) reject2("Codex transcript record envelope is malformed.");
|
|
3216
3094
|
const ordinal = safeOrdinal(value.ordinal);
|
|
3217
|
-
const
|
|
3218
|
-
if (!
|
|
3219
|
-
if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal,
|
|
3220
|
-
if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal,
|
|
3221
|
-
if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal,
|
|
3222
|
-
if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal,
|
|
3223
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
3224
|
-
}
|
|
3225
|
-
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) {
|
|
3226
3104
|
return {
|
|
3227
3105
|
kind: "turn_context",
|
|
3228
3106
|
ordinal,
|
|
3229
|
-
timestamp:
|
|
3230
|
-
turn_id:
|
|
3107
|
+
timestamp: timestamp4,
|
|
3108
|
+
turn_id: safeRef3(payload.turn_id, "Codex turn context identity is invalid."),
|
|
3231
3109
|
model: boundedNonEmptyText(payload.model, 256, "Codex turn context model is invalid.")
|
|
3232
3110
|
};
|
|
3233
3111
|
}
|
|
3234
|
-
function decodeSessionMeta(payload, ordinal,
|
|
3235
|
-
const sessionId =
|
|
3112
|
+
function decodeSessionMeta(payload, ordinal, timestamp4) {
|
|
3113
|
+
const sessionId = safeRef3(payload.id, "Codex transcript session identity is invalid.");
|
|
3236
3114
|
const cliVersion = boundedNonEmptyText(payload.cli_version, 64, "Codex transcript version is invalid.");
|
|
3237
|
-
return { kind: "session_meta", ordinal, timestamp:
|
|
3115
|
+
return { kind: "session_meta", ordinal, timestamp: timestamp4, session_id: sessionId, cli_version: cliVersion };
|
|
3238
3116
|
}
|
|
3239
|
-
function decodeEventMessage(payload, ordinal,
|
|
3117
|
+
function decodeEventMessage(payload, ordinal, timestamp4) {
|
|
3240
3118
|
if (payload.type === "task_started") {
|
|
3241
|
-
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) };
|
|
3242
3120
|
}
|
|
3243
3121
|
if (payload.type === "task_complete") {
|
|
3244
3122
|
return {
|
|
3245
3123
|
kind: "task_complete",
|
|
3246
3124
|
ordinal,
|
|
3247
|
-
timestamp:
|
|
3248
|
-
turn_id:
|
|
3125
|
+
timestamp: timestamp4,
|
|
3126
|
+
turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."),
|
|
3249
3127
|
started_at: safeNumber(payload.started_at),
|
|
3250
3128
|
completed_at: safeNumber(payload.completed_at),
|
|
3251
3129
|
duration_ms: safeNumber(payload.duration_ms),
|
|
@@ -3256,20 +3134,20 @@ function decodeEventMessage(payload, ordinal, timestamp5) {
|
|
|
3256
3134
|
return {
|
|
3257
3135
|
kind: "turn_aborted",
|
|
3258
3136
|
ordinal,
|
|
3259
|
-
timestamp:
|
|
3260
|
-
turn_id:
|
|
3137
|
+
timestamp: timestamp4,
|
|
3138
|
+
turn_id: safeRef3(payload.turn_id, "Codex task identity is invalid."),
|
|
3261
3139
|
started_at: safeNumber(payload.started_at),
|
|
3262
3140
|
completed_at: safeNumber(payload.completed_at),
|
|
3263
3141
|
duration_ms: safeNumber(payload.duration_ms),
|
|
3264
3142
|
reason: boundedText(payload.reason, 256, "Codex task abort reason is invalid.")
|
|
3265
3143
|
};
|
|
3266
3144
|
}
|
|
3267
|
-
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal,
|
|
3268
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
3145
|
+
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp4);
|
|
3146
|
+
return { kind: "ignored", ordinal, timestamp: timestamp4 };
|
|
3269
3147
|
}
|
|
3270
|
-
function decodeTokenCount(payload, ordinal,
|
|
3271
|
-
if (payload.info === null) return { kind: "token_count", ordinal, timestamp:
|
|
3272
|
-
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" };
|
|
3273
3151
|
const lastUsage = optionalTokenUsage(payload.info.last_token_usage);
|
|
3274
3152
|
const totalUsage = optionalTokenUsage(payload.info.total_token_usage);
|
|
3275
3153
|
const gap = lastUsage.status === "invalid" || totalUsage.status === "invalid";
|
|
@@ -3277,7 +3155,7 @@ function decodeTokenCount(payload, ordinal, timestamp5) {
|
|
|
3277
3155
|
return {
|
|
3278
3156
|
kind: "token_count",
|
|
3279
3157
|
ordinal,
|
|
3280
|
-
timestamp:
|
|
3158
|
+
timestamp: timestamp4,
|
|
3281
3159
|
...lastUsage.status === "valid" ? { last_usage: lastUsage.value } : {},
|
|
3282
3160
|
...totalUsage.status === "valid" ? { total_usage: totalUsage.value } : {},
|
|
3283
3161
|
...usageGapReason === void 0 ? {} : { usage_gap_reason: usageGapReason }
|
|
@@ -3293,7 +3171,7 @@ function optionalTokenUsage(value) {
|
|
|
3293
3171
|
}
|
|
3294
3172
|
}
|
|
3295
3173
|
function decodeTokenUsage(value) {
|
|
3296
|
-
if (!
|
|
3174
|
+
if (!isRecord6(value)) reject2("Codex token usage is malformed.");
|
|
3297
3175
|
const usage = {
|
|
3298
3176
|
input_tokens: safeTokenCount(value.input_tokens),
|
|
3299
3177
|
cached_input_tokens: safeTokenCount(value.cached_input_tokens),
|
|
@@ -3305,35 +3183,33 @@ function decodeTokenUsage(value) {
|
|
|
3305
3183
|
if (usage.total_tokens !== usage.input_tokens + usage.output_tokens || usage.cached_input_tokens + usage.cache_write_input_tokens > usage.input_tokens || usage.reasoning_output_tokens > usage.output_tokens) reject2("Codex token usage buckets are inconsistent.");
|
|
3306
3184
|
return usage;
|
|
3307
3185
|
}
|
|
3308
|
-
function decodeResponseItem(payload, ordinal,
|
|
3309
|
-
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 };
|
|
3310
3188
|
const metadata = userMessageMetadata(payload.internal_chat_message_metadata_passthrough);
|
|
3311
|
-
const
|
|
3312
|
-
if (
|
|
3189
|
+
const contentItemKind = userTextContentKind(metadata.content_item_kinds);
|
|
3190
|
+
if (contentItemKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp4 };
|
|
3313
3191
|
const prompt = userTextContent(payload.content);
|
|
3314
3192
|
return {
|
|
3315
3193
|
kind: "user_text",
|
|
3316
3194
|
ordinal,
|
|
3317
|
-
timestamp:
|
|
3195
|
+
timestamp: timestamp4,
|
|
3318
3196
|
prompt,
|
|
3319
|
-
|
|
3320
|
-
message_id:
|
|
3321
|
-
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.")
|
|
3322
3200
|
};
|
|
3323
3201
|
}
|
|
3324
|
-
function
|
|
3325
|
-
|
|
3326
|
-
if (kinds[0] === "user.text") return "human";
|
|
3327
|
-
return kinds[0] === "goal.internal_context" ? "host_automatic" : void 0;
|
|
3202
|
+
function userTextContentKind(kinds) {
|
|
3203
|
+
return kinds.length === 1 && (kinds[0] === "user.text" || kinds[0] === "goal.internal_context") ? kinds[0] : void 0;
|
|
3328
3204
|
}
|
|
3329
3205
|
function userMessageMetadata(value) {
|
|
3330
|
-
if (!
|
|
3206
|
+
if (!isRecord6(value) || !Array.isArray(value.content_item_kinds)) reject2("Codex user message provenance is malformed.");
|
|
3331
3207
|
return { ...value, content_item_kinds: value.content_item_kinds };
|
|
3332
3208
|
}
|
|
3333
3209
|
function userTextContent(value) {
|
|
3334
3210
|
if (!Array.isArray(value)) reject2(`Codex user text content is malformed (${refusalFact("kind", typeof value, "array")}).`);
|
|
3335
3211
|
if (value.length !== 1) reject2(`Codex user text content is malformed (${refusalFact("items", value.length, "exactly one")}).`);
|
|
3336
|
-
if (!
|
|
3212
|
+
if (!isRecord6(value[0]) || value[0].type !== "input_text") reject2(`Codex user text content is malformed (${refusalFact("item_type", isRecord6(value[0]) ? value[0].type : typeof value[0], "input_text")}).`);
|
|
3337
3213
|
return boundedText(value[0].text, MAX_PROMPT_BYTES, "Codex user text is invalid.");
|
|
3338
3214
|
}
|
|
3339
3215
|
function safeOrdinal(value) {
|
|
@@ -3354,8 +3230,8 @@ function safeTimestamp(value) {
|
|
|
3354
3230
|
if (!Number.isFinite(Date.parse(value))) reject2(`Codex transcript timestamp is invalid (${refusalFact("parsed_ms", Date.parse(value), "a finite epoch milliseconds value")}).`);
|
|
3355
3231
|
return value;
|
|
3356
3232
|
}
|
|
3357
|
-
function
|
|
3358
|
-
if (typeof value !== "string" || !
|
|
3233
|
+
function safeRef3(value, message) {
|
|
3234
|
+
if (typeof value !== "string" || !SAFE_REF4.test(value)) reject2(message);
|
|
3359
3235
|
return value;
|
|
3360
3236
|
}
|
|
3361
3237
|
function boundedText(value, maximum, message) {
|
|
@@ -3369,7 +3245,7 @@ function boundedNonEmptyText(value, maximum, message) {
|
|
|
3369
3245
|
if (result.length === 0) reject2(message);
|
|
3370
3246
|
return result;
|
|
3371
3247
|
}
|
|
3372
|
-
function
|
|
3248
|
+
function isRecord6(value) {
|
|
3373
3249
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3374
3250
|
}
|
|
3375
3251
|
function reject2(message) {
|
|
@@ -3379,11 +3255,11 @@ function reject2(message) {
|
|
|
3379
3255
|
// host/codex/src/codex-transcript-baseline.ts
|
|
3380
3256
|
var BASELINE_READ_CHUNK_BYTES = 256 * 1024;
|
|
3381
3257
|
var CODEX_TRANSCRIPT_ANCHOR_BYTES = 256;
|
|
3382
|
-
async function captureCodexTranscriptBaseline(registration, now = () => /* @__PURE__ */ new Date()) {
|
|
3258
|
+
async function captureCodexTranscriptBaseline(registration, now = () => /* @__PURE__ */ new Date(), platform = process.platform, custody = {}) {
|
|
3383
3259
|
let handle;
|
|
3384
3260
|
try {
|
|
3385
|
-
handle = await
|
|
3386
|
-
const { size, device, inode } = await ownedCodexTranscriptFile(handle);
|
|
3261
|
+
handle = await openNoFollow(registration.transcript_path, constants2.O_RDONLY, void 0, platform, custody);
|
|
3262
|
+
const { size, device, inode } = await ownedCodexTranscriptFile(handle, platform);
|
|
3387
3263
|
const empty = codexTranscriptInitialCursor(registration, device, inode, now);
|
|
3388
3264
|
const headerLine = await firstCompleteLine(handle, size);
|
|
3389
3265
|
if (headerLine === null) throw new CodexHostAdapterError("payload_rejected", "Codex transcript header is incomplete.");
|
|
@@ -3402,7 +3278,7 @@ async function captureCodexTranscriptBaseline(registration, now = () => /* @__PU
|
|
|
3402
3278
|
anchor_start: anchorStart,
|
|
3403
3279
|
anchor_sha256: digest(anchor),
|
|
3404
3280
|
header_validated: true,
|
|
3405
|
-
updated_at:
|
|
3281
|
+
updated_at: timestamp2(now)
|
|
3406
3282
|
};
|
|
3407
3283
|
} finally {
|
|
3408
3284
|
await handle?.close();
|
|
@@ -3422,23 +3298,19 @@ function codexTranscriptInitialCursor(registration, device, inode, now) {
|
|
|
3422
3298
|
anchor_sha256: digest(Buffer.alloc(0)),
|
|
3423
3299
|
header_validated: false,
|
|
3424
3300
|
status: "active",
|
|
3425
|
-
updated_at:
|
|
3301
|
+
updated_at: timestamp2(now)
|
|
3426
3302
|
};
|
|
3427
3303
|
}
|
|
3428
|
-
async function ownedCodexTranscriptFile(handle) {
|
|
3304
|
+
async function ownedCodexTranscriptFile(handle, platform = process.platform) {
|
|
3429
3305
|
const stats = await handle.stat();
|
|
3430
|
-
|
|
3306
|
+
if (!stats.isFile()) throw new CodexHostAdapterError("storage", "Codex transcript source is not a regular file.");
|
|
3307
|
+
if (platform !== "win32" && !isSelfOwned(stats.uid, platform, handle)) throw new CodexHostAdapterError("storage", "Codex transcript source is owned by another user.");
|
|
3431
3308
|
return { size: stats.size, device: String(stats.dev), inode: String(stats.ino) };
|
|
3432
3309
|
}
|
|
3433
|
-
function
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
}
|
|
3438
|
-
function assertCodexTranscriptHeader(registration, record3) {
|
|
3439
|
-
if (record3.kind !== "session_meta" || record3.session_id !== registration.codex_session_id) throw new CodexHostAdapterError("payload_rejected", `Codex transcript header does not match its SessionStart registration (${refusalFacts([
|
|
3440
|
-
{ name: "kind", value: record3.kind, expected: "session_meta" },
|
|
3441
|
-
{ name: "session_id", value: record3.kind === "session_meta" ? record3.session_id : "unavailable", expected: registration.codex_session_id }
|
|
3310
|
+
function assertCodexTranscriptHeader(registration, record) {
|
|
3311
|
+
if (record.kind !== "session_meta" || record.session_id !== registration.codex_session_id) throw new CodexHostAdapterError("payload_rejected", `Codex transcript header does not match its SessionStart registration (${refusalFacts([
|
|
3312
|
+
{ name: "kind", value: record.kind, expected: "session_meta" },
|
|
3313
|
+
{ name: "session_id", value: record.kind === "session_meta" ? record.session_id : "unavailable", expected: registration.codex_session_id }
|
|
3442
3314
|
])}).`);
|
|
3443
3315
|
}
|
|
3444
3316
|
async function firstCompleteLine(handle, size) {
|
|
@@ -3524,24 +3396,32 @@ async function* completeLinesBackward(handle, end) {
|
|
|
3524
3396
|
yield { start: 0, bytes: Buffer.concat(carry) };
|
|
3525
3397
|
}
|
|
3526
3398
|
function digest(value) {
|
|
3527
|
-
return
|
|
3399
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
3528
3400
|
}
|
|
3529
|
-
function
|
|
3401
|
+
function timestamp2(now) {
|
|
3530
3402
|
const value = now();
|
|
3531
3403
|
if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
|
|
3532
3404
|
return value.toISOString();
|
|
3533
3405
|
}
|
|
3534
3406
|
|
|
3407
|
+
// host/codex/src/codex-runtime-paths.ts
|
|
3408
|
+
import { join as join3 } from "node:path";
|
|
3409
|
+
var CODEX_HOME_ENVIRONMENT = "CODEX_HOME";
|
|
3410
|
+
var CODEX_HOME_DEFAULT_DIRECTORY = ".codex";
|
|
3411
|
+
var CODEX_SESSIONS_DIRECTORY = "sessions";
|
|
3412
|
+
function resolveCodexHome(environment, home) {
|
|
3413
|
+
return environment[CODEX_HOME_ENVIRONMENT] || join3(home, CODEX_HOME_DEFAULT_DIRECTORY);
|
|
3414
|
+
}
|
|
3415
|
+
function resolveCodexSessionsRoot(environment, home) {
|
|
3416
|
+
return join3(resolveCodexHome(environment, home), CODEX_SESSIONS_DIRECTORY);
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3535
3419
|
// host/codex/src/codex-adapter.ts
|
|
3536
|
-
var
|
|
3537
|
-
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
3538
|
-
var ORIGIN_DELIVERY_CONCURRENCY = 8;
|
|
3539
|
-
var CODEX_RELAY_TIMEOUT_MAX_MS = 1e4;
|
|
3420
|
+
var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
3540
3421
|
function createCodexHostObservationAdapter(options) {
|
|
3541
3422
|
validateOptions(options);
|
|
3542
3423
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
3543
3424
|
const retentionMs = options.retention_ms ?? CODEX_DEFAULT_RETENTION_MS;
|
|
3544
|
-
const timeoutMs = options.timeout_ms ?? DEFAULT_TIMEOUT_MS;
|
|
3545
3425
|
const store = new CodexOutboxStore(options.outbox_dir);
|
|
3546
3426
|
return Object.freeze({
|
|
3547
3427
|
handle: async (event, payload) => {
|
|
@@ -3550,15 +3430,14 @@ function createCodexHostObservationAdapter(options) {
|
|
|
3550
3430
|
} catch (error) {
|
|
3551
3431
|
return { event, disposition: "rejected", reason: safeReason(error) };
|
|
3552
3432
|
}
|
|
3553
|
-
}
|
|
3554
|
-
drainOutbox: (signal) => drainOutbox(options, store, now, timeoutMs, signal)
|
|
3433
|
+
}
|
|
3555
3434
|
});
|
|
3556
3435
|
}
|
|
3557
3436
|
async function registerSession(event, options, store, now, retentionMs) {
|
|
3558
|
-
const defaultRoot =
|
|
3437
|
+
const defaultRoot = resolveCodexSessionsRoot(process.env, homedir());
|
|
3559
3438
|
const transcriptPath = sourceBoundTranscriptPath(event.transcript_path, options.codex_sessions_root ?? defaultRoot);
|
|
3560
3439
|
const hostSessionRef = hostSessionReference(options.host_source_ref, event.session_id);
|
|
3561
|
-
const observedAt =
|
|
3440
|
+
const observedAt = timestamp3(now);
|
|
3562
3441
|
const expiresAt = new Date(Date.parse(observedAt) + retentionMs).toISOString();
|
|
3563
3442
|
const registration = {
|
|
3564
3443
|
schema_version: "ceal.codex_transcript_registration.v1",
|
|
@@ -3576,6 +3455,9 @@ async function registerSession(event, options, store, now, retentionMs) {
|
|
|
3576
3455
|
await store.writeTranscriptCursor(await captureCodexTranscriptBaseline(registration, now));
|
|
3577
3456
|
}
|
|
3578
3457
|
await store.registerTranscript(registration);
|
|
3458
|
+
if (existing !== void 0 && currentTurn !== void 0 && (await store.readTranscriptCursor(registration))?.status === "quarantined") {
|
|
3459
|
+
await store.writeTranscriptCursor(await captureCodexTranscriptBaseline(registration, now));
|
|
3460
|
+
}
|
|
3579
3461
|
if (event.event === "SessionStart") {
|
|
3580
3462
|
await store.saveSession({
|
|
3581
3463
|
schema_version: "ceal.codex_session.v1",
|
|
@@ -3592,13 +3474,16 @@ async function registerSession(event, options, store, now, retentionMs) {
|
|
|
3592
3474
|
}
|
|
3593
3475
|
async function selectCurrentTurn(event, options, store, hostSessionRef) {
|
|
3594
3476
|
if (event.event !== "UserPromptSubmit") return void 0;
|
|
3477
|
+
const host_turn_ref = hostTurnReference(options.host_source_ref, hostSessionRef, event.turn_id);
|
|
3478
|
+
const steered = await liveSteeredTurn(options, store, hostSessionRef, host_turn_ref);
|
|
3479
|
+
if (steered !== void 0) return steered;
|
|
3595
3480
|
const currentTurn = {
|
|
3596
|
-
host_turn_ref
|
|
3481
|
+
host_turn_ref,
|
|
3597
3482
|
prompt_ref: hostProducerPromptRef(promptObservationValue(event.prompt).body_sha256),
|
|
3598
3483
|
turn_generation_ref: options.random_uuid?.() ?? randomUUID()
|
|
3599
3484
|
};
|
|
3600
3485
|
await store.writeInvocationTurnSelection({
|
|
3601
|
-
schema_version:
|
|
3486
|
+
schema_version: HOST_INVOCATION_TURN_SELECTION_SCHEMA,
|
|
3602
3487
|
host_source_ref: options.host_source_ref,
|
|
3603
3488
|
host_session_ref: hostSessionRef,
|
|
3604
3489
|
prompt_ref: currentTurn.prompt_ref,
|
|
@@ -3606,6 +3491,18 @@ async function selectCurrentTurn(event, options, store, hostSessionRef) {
|
|
|
3606
3491
|
});
|
|
3607
3492
|
return currentTurn;
|
|
3608
3493
|
}
|
|
3494
|
+
async function liveSteeredTurn(options, store, hostSessionRef, hostTurnRef) {
|
|
3495
|
+
let live;
|
|
3496
|
+
try {
|
|
3497
|
+
live = await store.readInvocationContext(options.host_source_ref, hostSessionRef, (options.now ?? (() => /* @__PURE__ */ new Date()))());
|
|
3498
|
+
} catch {
|
|
3499
|
+
return void 0;
|
|
3500
|
+
}
|
|
3501
|
+
if (live === null || live.identity.host_turn_ref !== hostTurnRef) return void 0;
|
|
3502
|
+
const selection = await store.readInvocationTurnSelection(options.host_source_ref, hostSessionRef).catch(() => null);
|
|
3503
|
+
if (selection === null || selection.prompt_ref !== live.prompt_ref || selection.turn_generation_ref !== live.turn_generation_ref) return void 0;
|
|
3504
|
+
return { host_turn_ref: hostTurnRef, prompt_ref: selection.prompt_ref, turn_generation_ref: selection.turn_generation_ref };
|
|
3505
|
+
}
|
|
3609
3506
|
function sourceBoundTranscriptPath(path, sessionsRoot) {
|
|
3610
3507
|
if (!isAbsolute3(path) || !isAbsolute3(sessionsRoot)) throw new CodexHostAdapterError("configuration", "Codex transcript registration paths must be absolute.");
|
|
3611
3508
|
const transcript = resolve2(path);
|
|
@@ -3616,164 +3513,47 @@ function sourceBoundTranscriptPath(path, sessionsRoot) {
|
|
|
3616
3513
|
}
|
|
3617
3514
|
return transcript;
|
|
3618
3515
|
}
|
|
3619
|
-
async function drainOutbox(options, store, now, timeoutMs, signal) {
|
|
3620
|
-
const read = await store.list();
|
|
3621
|
-
const { live, expired } = await liveOutboxEntries(read.entries, store, now, signal);
|
|
3622
|
-
const origins = live.filter((entry) => entry.origin_input === true).sort((left, right) => right.created_at.localeCompare(left.created_at));
|
|
3623
|
-
const originResult = await deliverOriginEntries(origins, options, store, now, timeoutMs, signal);
|
|
3624
|
-
const boundResult = await deliverBoundEntries(live.filter((entry) => entry.origin_input !== true), options, store, now, timeoutMs, signal);
|
|
3625
|
-
return { delivered: originResult.delivered + boundResult.delivered, retained: originResult.retained + boundResult.retained, expired, malformed: read.malformed };
|
|
3626
|
-
}
|
|
3627
|
-
async function liveOutboxEntries(entries, store, now, signal) {
|
|
3628
|
-
const live = [];
|
|
3629
|
-
let expired = 0;
|
|
3630
|
-
for (const entry of entries) {
|
|
3631
|
-
if (signal?.aborted === true) break;
|
|
3632
|
-
if (Date.parse(entry.expires_at) > now().getTime()) live.push(entry);
|
|
3633
|
-
else {
|
|
3634
|
-
await store.remove(entry);
|
|
3635
|
-
expired += 1;
|
|
3636
|
-
}
|
|
3637
|
-
}
|
|
3638
|
-
return { live, expired };
|
|
3639
|
-
}
|
|
3640
|
-
async function deliverOriginEntries(entries, options, store, now, timeoutMs, signal) {
|
|
3641
|
-
const counts = { delivered: 0, retained: 0 };
|
|
3642
|
-
for (let offset = 0; offset < entries.length; offset += ORIGIN_DELIVERY_CONCURRENCY) {
|
|
3643
|
-
if (signal?.aborted === true) break;
|
|
3644
|
-
const outcomes = await Promise.allSettled(entries.slice(offset, offset + ORIGIN_DELIVERY_CONCURRENCY).map((entry) => deliverPreparedEntry(entry, entry, options, store, now, timeoutMs, signal)));
|
|
3645
|
-
for (const outcome of outcomes) {
|
|
3646
|
-
if (outcome.status === "rejected") throw outcome.reason;
|
|
3647
|
-
incrementDelivery(counts, outcome.value);
|
|
3648
|
-
}
|
|
3649
|
-
}
|
|
3650
|
-
return counts;
|
|
3651
|
-
}
|
|
3652
|
-
async function deliverBoundEntries(entries, options, store, now, timeoutMs, signal) {
|
|
3653
|
-
const counts = { delivered: 0, retained: 0 };
|
|
3654
|
-
for (const entry of entries) {
|
|
3655
|
-
if (signal?.aborted === true) break;
|
|
3656
|
-
const prepared = await prepareForDelivery(entry, store);
|
|
3657
|
-
if (prepared === null) {
|
|
3658
|
-
counts.retained += 1;
|
|
3659
|
-
continue;
|
|
3660
|
-
}
|
|
3661
|
-
const outcome = await deliverPreparedEntry(entry, prepared, options, store, now, timeoutMs, signal);
|
|
3662
|
-
incrementDelivery(counts, outcome);
|
|
3663
|
-
}
|
|
3664
|
-
return counts;
|
|
3665
|
-
}
|
|
3666
|
-
function incrementDelivery(counts, outcome) {
|
|
3667
|
-
counts[outcome] += 1;
|
|
3668
|
-
}
|
|
3669
|
-
async function deliverPreparedEntry(entry, prepared, options, store, now, timeoutMs, signal) {
|
|
3670
|
-
try {
|
|
3671
|
-
const acknowledgement = await submitWithDeadline(options.gateway, gatewaySubmission(prepared), timeoutMs, signal);
|
|
3672
|
-
if (prepared.request_ref === void 0) {
|
|
3673
|
-
const binding = bindingFor(prepared, acknowledgement, now);
|
|
3674
|
-
await store.writeBinding(binding);
|
|
3675
|
-
await store.bindInvocationContext(binding, now());
|
|
3676
|
-
}
|
|
3677
|
-
await store.remove(entry);
|
|
3678
|
-
return "delivered";
|
|
3679
|
-
} catch (error) {
|
|
3680
|
-
if (error instanceof CodexHostAdapterError && error.code === "payload_rejected") throw error;
|
|
3681
|
-
return "retained";
|
|
3682
|
-
}
|
|
3683
|
-
}
|
|
3684
|
-
async function prepareForDelivery(entry, store) {
|
|
3685
|
-
const binding = await store.readBinding(entry.identity);
|
|
3686
|
-
if (!entry.origin_input && binding === null) return null;
|
|
3687
|
-
if (binding === null) return entry;
|
|
3688
|
-
return { ...entry, prompt_ref: binding.prompt_ref, request_ref: binding.request_ref };
|
|
3689
|
-
}
|
|
3690
|
-
function gatewaySubmission(envelope) {
|
|
3691
|
-
return {
|
|
3692
|
-
identity: envelope.identity,
|
|
3693
|
-
host_input_ref: envelope.host_input_ref,
|
|
3694
|
-
input_kind: envelope.input_kind,
|
|
3695
|
-
prompt_ref: envelope.prompt_ref,
|
|
3696
|
-
observations: envelope.observations,
|
|
3697
|
-
...envelope.request_ref === void 0 ? {} : { request_ref: envelope.request_ref },
|
|
3698
|
-
submission_ref: envelope.submission_ref
|
|
3699
|
-
};
|
|
3700
|
-
}
|
|
3701
|
-
async function submitWithDeadline(gateway, submission, timeoutMs, signal) {
|
|
3702
|
-
if (signal?.aborted === true) throw new CodexHostAdapterError("storage", "Codex relay delivery was cancelled.");
|
|
3703
|
-
const controller = new AbortController();
|
|
3704
|
-
const abort = () => controller.abort();
|
|
3705
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
3706
|
-
let timeoutHandle;
|
|
3707
|
-
const timeout = new Promise((_resolve, reject3) => {
|
|
3708
|
-
timeoutHandle = setTimeout(() => {
|
|
3709
|
-
controller.abort();
|
|
3710
|
-
reject3(new CodexHostAdapterError("storage", "Codex relay delivery timed out."));
|
|
3711
|
-
}, timeoutMs);
|
|
3712
|
-
});
|
|
3713
|
-
try {
|
|
3714
|
-
const acknowledgement = await Promise.race([gateway.submitHostTurn(submission, controller.signal), timeout]);
|
|
3715
|
-
if (!isRecord6(acknowledgement) || !SAFE_REF6.test(String(acknowledgement.request_ref)) || !isHostGatewayAcknowledgementDisposition(acknowledgement.disposition)) {
|
|
3716
|
-
throw new CodexHostAdapterError("payload_rejected", "Gateway Host acknowledgement is invalid.");
|
|
3717
|
-
}
|
|
3718
|
-
return acknowledgement;
|
|
3719
|
-
} finally {
|
|
3720
|
-
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
3721
|
-
signal?.removeEventListener("abort", abort);
|
|
3722
|
-
}
|
|
3723
|
-
}
|
|
3724
|
-
function bindingFor(envelope, acknowledgement, now) {
|
|
3725
|
-
return {
|
|
3726
|
-
schema_version: "ceal.codex_turn_binding.v1",
|
|
3727
|
-
identity: envelope.identity,
|
|
3728
|
-
prompt_ref: envelope.prompt_ref,
|
|
3729
|
-
request_ref: acknowledgement.request_ref,
|
|
3730
|
-
codex_turn_ids: envelope.codex_turn_id === void 0 ? [] : [envelope.codex_turn_id],
|
|
3731
|
-
bound_at: timestamp4(now),
|
|
3732
|
-
expires_at: envelope.expires_at
|
|
3733
|
-
};
|
|
3734
|
-
}
|
|
3735
3516
|
function codexSubmissionReference(hostInputRef, event, ordinal) {
|
|
3736
|
-
if (!
|
|
3517
|
+
if (!SAFE_REF5.test(hostInputRef) || !Number.isSafeInteger(ordinal) || ordinal < 0) throw new CodexHostAdapterError("invalid_input", "Codex submission identity is invalid.");
|
|
3737
3518
|
return `codex-submission:${cealRequestSha256({ host_input_ref: hostInputRef, event, ordinal })}`;
|
|
3738
3519
|
}
|
|
3739
|
-
function
|
|
3520
|
+
function timestamp3(now) {
|
|
3740
3521
|
const value = now();
|
|
3741
3522
|
if (!(value instanceof Date) || !Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex adapter clock is invalid.");
|
|
3742
3523
|
return value.toISOString();
|
|
3743
3524
|
}
|
|
3744
3525
|
function validateOptions(options) {
|
|
3745
|
-
if (!
|
|
3526
|
+
if (!isRecord7(options)) throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
|
|
3746
3527
|
validateAdapterOwner(options);
|
|
3747
3528
|
validateAdapterBehavior(options);
|
|
3748
3529
|
}
|
|
3749
3530
|
function validateAdapterOwner(options) {
|
|
3750
|
-
if (!
|
|
3531
|
+
if (!SAFE_REF5.test(options.host_source_ref) || options.outbox_dir.length === 0) {
|
|
3751
3532
|
throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
|
|
3752
3533
|
}
|
|
3753
3534
|
if (options.codex_sessions_root !== void 0 && !isAbsolute3(options.codex_sessions_root)) throw new CodexHostAdapterError("configuration", "Codex sessions root is invalid.");
|
|
3754
3535
|
}
|
|
3755
3536
|
function validateAdapterBehavior(options) {
|
|
3756
|
-
if (options.timeout_ms !== void 0 && (!Number.isSafeInteger(options.timeout_ms) || options.timeout_ms < 1 || options.timeout_ms > CODEX_RELAY_TIMEOUT_MAX_MS)) throw new CodexHostAdapterError("configuration", `Codex relay timeout is invalid (${refusalFact("timeout_ms", options.timeout_ms, `an integer from 1 through ${CODEX_RELAY_TIMEOUT_MAX_MS}`)}).`);
|
|
3757
3537
|
if (options.retention_ms !== void 0 && (!Number.isSafeInteger(options.retention_ms) || options.retention_ms < 1)) throw new CodexHostAdapterError("configuration", `Codex retention is invalid (${refusalFact("retention_ms", options.retention_ms, "a positive integer")}).`);
|
|
3758
3538
|
}
|
|
3759
3539
|
function safeReason(error) {
|
|
3760
3540
|
return error instanceof CodexHostAdapterError ? error.message.slice(0, 128) : "codex_session_registration_rejected";
|
|
3761
3541
|
}
|
|
3762
|
-
function
|
|
3542
|
+
function isRecord7(value) {
|
|
3763
3543
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3764
3544
|
}
|
|
3765
3545
|
|
|
3766
3546
|
// host/producer-service.ts
|
|
3767
|
-
import { posix as posix3, win32 as
|
|
3547
|
+
import { posix as posix3, win32 as win324 } from "node:path";
|
|
3768
3548
|
|
|
3769
3549
|
// host/runtime-source-paths.ts
|
|
3770
|
-
import { createHash as
|
|
3771
|
-
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";
|
|
3772
3552
|
|
|
3773
3553
|
// host/absolute-path.ts
|
|
3774
|
-
import { posix, win32 } from "node:path";
|
|
3554
|
+
import { posix, win32 as win322 } from "node:path";
|
|
3775
3555
|
function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allowRoot = false) {
|
|
3776
|
-
const pathApi = flavor === "win32" || flavor === "windows" ?
|
|
3556
|
+
const pathApi = flavor === "win32" || flavor === "windows" ? win322 : posix;
|
|
3777
3557
|
return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
|
|
3778
3558
|
}
|
|
3779
3559
|
|
|
@@ -3785,28 +3565,23 @@ function hostSourceUuid(sourceRef) {
|
|
|
3785
3565
|
function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
|
|
3786
3566
|
const sourceId = hostSourceUuid(sourceRef);
|
|
3787
3567
|
if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
|
|
3788
|
-
const path = platform === "win32" ?
|
|
3568
|
+
const path = platform === "win32" ? win323 : posix2;
|
|
3789
3569
|
const sourcesRoot = path.join(stateRoot, "sources");
|
|
3790
3570
|
const sourceRoot = path.join(sourcesRoot, sourceId);
|
|
3791
3571
|
const runtimeRoot = path.join(stateRoot, "run");
|
|
3792
|
-
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${
|
|
3793
|
-
assertHostRuntimeIpcLength(endpoint, platform);
|
|
3572
|
+
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash6("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
|
|
3794
3573
|
return { sourceId, sourcesRoot, sourceRoot, relayRoot: path.join(sourceRoot, "relay"), codexOutbox: path.join(sourceRoot, "producers", "codex", "outbox"), claudeOutbox: path.join(sourceRoot, "producers", "claude", "outbox"), endpoint, runtimeRoot };
|
|
3795
3574
|
}
|
|
3796
|
-
function assertHostRuntimeIpcLength(endpoint, platform) {
|
|
3797
|
-
if (platform === "win32") return;
|
|
3798
|
-
const maximum = platform === "linux" ? 107 : 103;
|
|
3799
|
-
if (Buffer.byteLength(endpoint, "utf8") > maximum) throw Object.assign(new TypeError(`ipc_endpoint_too_long: Host IPC path exceeds ${maximum} bytes; use a shorter Host state root.`), { code: "ipc_endpoint_too_long" });
|
|
3800
|
-
}
|
|
3801
3575
|
function assertHostProducerSourcePaths(stateRoot, config, producer, platform = process.platform) {
|
|
3802
3576
|
const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
|
|
3803
|
-
if (config.outbox_dir !== (producer === "codex" ? paths.codexOutbox : paths.claudeOutbox) || config.relay_endpoint !== paths.endpoint) throw new TypeError("Host producer outbox and IPC must belong to its configured source.");
|
|
3577
|
+
if (config.outbox_dir !== (producer === "codex" ? paths.codexOutbox : paths.claudeOutbox) || platform !== "win32" && config.relay_endpoint !== paths.endpoint) throw new TypeError("Host producer outbox and IPC must belong to its configured source.");
|
|
3578
|
+
return paths.endpoint;
|
|
3804
3579
|
}
|
|
3805
3580
|
|
|
3806
3581
|
// host/producer-service.ts
|
|
3807
|
-
import { constants } from "node:fs";
|
|
3808
|
-
import { lstat as lstat3
|
|
3809
|
-
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;
|
|
3810
3585
|
var HOST_PRODUCER_CONFIG_FLAG = "--ceal-host-config";
|
|
3811
3586
|
var HostProducerServiceError = class extends Error {
|
|
3812
3587
|
code;
|
|
@@ -3817,18 +3592,19 @@ var HostProducerServiceError = class extends Error {
|
|
|
3817
3592
|
}
|
|
3818
3593
|
};
|
|
3819
3594
|
var HOST_PRODUCER_CONFIG_KEYS = ["schema_version", "host_source_ref", "producer_installation_ref", "producer_epoch", "outbox_dir", "relay_endpoint"];
|
|
3595
|
+
var HOST_PRODUCER_CONFIG_ENDPOINT_KEY = "relay_endpoint";
|
|
3820
3596
|
var HOST_PRODUCER_TIMING_DEFAULTS = Object.freeze({ reconcile_interval_ms: 250, retry_backoff_ms: 1e3, ipc_timeout_ms: 5e3 });
|
|
3821
3597
|
function hostProducerExtraPath(extras, key) {
|
|
3822
3598
|
const value = extras[key];
|
|
3823
|
-
if (value === void 0) throw hostProducerError("invalid_config");
|
|
3599
|
+
if (value === void 0) throw hostProducerError("invalid_config", refusalFact(key, "missing", "an absolute path"));
|
|
3824
3600
|
return value;
|
|
3825
3601
|
}
|
|
3826
3602
|
function parseHostProducerConfigFlag(args, platform = process.platform) {
|
|
3827
3603
|
return parseProducerHostConfigFlag(args, HOST_PRODUCER_CONFIG_FLAG, platform, hostProducerError);
|
|
3828
3604
|
}
|
|
3829
|
-
async function readHostProducerConfig(descriptor, configPath, platform = process.platform) {
|
|
3830
|
-
const config = decodeHostProducerConfigText(descriptor, await readProducerConfigText(configPath, platform, hostProducerError), platform);
|
|
3831
|
-
const path = platform === "win32" ?
|
|
3605
|
+
async function readHostProducerConfig(descriptor, configPath, platform = process.platform, custody = {}) {
|
|
3606
|
+
const config = decodeHostProducerConfigText(descriptor, await readProducerConfigText(configPath, platform, hostProducerError, custody), platform);
|
|
3607
|
+
const path = platform === "win32" ? win324 : posix3;
|
|
3832
3608
|
const root = path.resolve(config.outbox_dir, "../../../../..");
|
|
3833
3609
|
try {
|
|
3834
3610
|
assertHostProducerSourcePaths(root, config, descriptor.producer_id, platform);
|
|
@@ -3854,15 +3630,31 @@ function parseProducerHostConfigFlag(args, flag, platform, error) {
|
|
|
3854
3630
|
assertProducerAbsolutePath(value, platform, "config", error);
|
|
3855
3631
|
return value;
|
|
3856
3632
|
}
|
|
3857
|
-
async function readProducerConfigText(configPath, platform, error) {
|
|
3858
|
-
assertProducerAbsolutePath(configPath, platform, "config", error);
|
|
3633
|
+
async function readProducerConfigText(configPath, platform, error, custody) {
|
|
3634
|
+
assertProducerAbsolutePath(configPath, process.platform, "config", error);
|
|
3635
|
+
if (platform === "win32") {
|
|
3636
|
+
let bytes;
|
|
3637
|
+
try {
|
|
3638
|
+
bytes = await readPrivateFileWithSecurefs(configPath, CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES, custody);
|
|
3639
|
+
} catch (cause) {
|
|
3640
|
+
if (cause instanceof SecureFileCustodyError && cause.reason === "not_private") throw error("config_not_private");
|
|
3641
|
+
if (cause instanceof SecureFileCustodyError && cause.reason === "too_large") throw error("invalid_config", refusalFact("config_bytes", "over_limit", `at most ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
|
|
3642
|
+
if (cause instanceof SecureFileCustodyError && cause.reason === "reparse_point") throw error("invalid_config");
|
|
3643
|
+
throw error("config_unavailable");
|
|
3644
|
+
}
|
|
3645
|
+
if (bytes.byteLength === 0) throw error("invalid_config", refusalFact("config_bytes", 0, `between 1 and ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
|
|
3646
|
+
try {
|
|
3647
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
3648
|
+
} catch {
|
|
3649
|
+
throw error("invalid_config");
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3859
3652
|
const pathStat = await configPathStat(configPath, error);
|
|
3860
3653
|
validateConfigFile(pathStat, platform, error);
|
|
3861
|
-
const
|
|
3862
|
-
const handle = await openConfigFile(configPath, constants.O_RDONLY | noFollow, error);
|
|
3654
|
+
const handle = await openConfigFile(configPath, platform, error, custody);
|
|
3863
3655
|
try {
|
|
3864
3656
|
const opened = await handle.stat();
|
|
3865
|
-
validateConfigFile(opened, platform, error);
|
|
3657
|
+
validateConfigFile(opened, platform, error, handle);
|
|
3866
3658
|
validateOpenedFile(opened, pathStat, platform, error);
|
|
3867
3659
|
return await readBoundedConfig(handle, opened.size, error);
|
|
3868
3660
|
} finally {
|
|
@@ -3870,8 +3662,9 @@ async function readProducerConfigText(configPath, platform, error) {
|
|
|
3870
3662
|
}
|
|
3871
3663
|
}
|
|
3872
3664
|
function decodeProducerConfigCore(text, options) {
|
|
3665
|
+
const ownership = producerConfigKeyOwnership(options.keys, options.platform);
|
|
3873
3666
|
const value = parseCanonicalJson(text, options.error);
|
|
3874
|
-
if (!
|
|
3667
|
+
if (!isRecord8(value) || !hasConfigKeys(value, ownership.required, ownership.tolerated) || value.schema_version !== options.schema) throw options.error("invalid_config", refusalFacts(producerShapeFacts(value, { schema: options.schema, keys: ownership.required, tolerated: ownership.tolerated })));
|
|
3875
3668
|
const hostSourceRef = typeof value.host_source_ref === "string" ? value.host_source_ref : "";
|
|
3876
3669
|
const installationRef = typeof value.producer_installation_ref === "string" ? value.producer_installation_ref : "";
|
|
3877
3670
|
const producerEpoch = validateIdentity3(hostSourceRef, installationRef, value.producer_epoch, options.error);
|
|
@@ -3889,14 +3682,21 @@ function decodeProducerConfigCore(text, options) {
|
|
|
3889
3682
|
producer_installation_ref: installationRef,
|
|
3890
3683
|
producer_epoch: producerEpoch,
|
|
3891
3684
|
outbox_dir: value.outbox_dir,
|
|
3892
|
-
relay_endpoint: value
|
|
3685
|
+
relay_endpoint: producerConfigEndpoint(value),
|
|
3893
3686
|
reconcile_interval_ms: intervals[0],
|
|
3894
3687
|
retry_backoff_ms: intervals[1],
|
|
3895
3688
|
ipc_timeout_ms: intervals[2]
|
|
3896
3689
|
};
|
|
3897
3690
|
}
|
|
3691
|
+
function producerConfigKeyOwnership(keys, platform) {
|
|
3692
|
+
if (platform !== "win32") return { required: keys, tolerated: [] };
|
|
3693
|
+
return { required: keys.filter((key) => key !== HOST_PRODUCER_CONFIG_ENDPOINT_KEY), tolerated: [HOST_PRODUCER_CONFIG_ENDPOINT_KEY] };
|
|
3694
|
+
}
|
|
3695
|
+
function producerConfigEndpoint(value) {
|
|
3696
|
+
return typeof value.relay_endpoint === "string" ? value.relay_endpoint : "";
|
|
3697
|
+
}
|
|
3898
3698
|
function assertProducerRelayEndpoint(value, platform, error) {
|
|
3899
|
-
if (platform === "win32"
|
|
3699
|
+
if (platform === "win32") return;
|
|
3900
3700
|
assertProducerAbsolutePath(value, platform, "runtime", error);
|
|
3901
3701
|
}
|
|
3902
3702
|
function assertProducerAbsolutePath(value, platform, kind, error) {
|
|
@@ -3909,21 +3709,22 @@ async function configPathStat(configPath, error) {
|
|
|
3909
3709
|
throw error("config_unavailable");
|
|
3910
3710
|
}
|
|
3911
3711
|
}
|
|
3912
|
-
function validateConfigFile(value, platform, error) {
|
|
3712
|
+
function validateConfigFile(value, platform, error, handle) {
|
|
3913
3713
|
if (!value.isFile() || value.isSymbolicLink()) throw error("invalid_config", refusalFacts([{ name: "config_is_file", value: value.isFile(), expected: "true" }, { name: "config_is_symlink", value: value.isSymbolicLink(), expected: "false" }]));
|
|
3914
3714
|
if (value.size <= 0 || value.size > CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES) throw error("invalid_config", refusalFact("config_bytes", value.size, `between 1 and ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
|
|
3915
|
-
if (
|
|
3715
|
+
if (!isSelfOwned(value.uid, platform, handle) || !isPrivateFileMode(value.mode, platform, handle)) throw error("config_not_private", refusalFacts([{ name: "platform", value: platform, expected: "a platform with verifiable owner and private-mode facts" }, { name: "config_uid", value: value.uid, expected: "the current user" }, { name: "config_mode", value: value.mode, expected: "private to its owner" }]));
|
|
3916
3716
|
}
|
|
3917
|
-
async function openConfigFile(configPath,
|
|
3717
|
+
async function openConfigFile(configPath, platform, error, custody) {
|
|
3918
3718
|
try {
|
|
3919
|
-
return await
|
|
3920
|
-
} catch {
|
|
3719
|
+
return await openNoFollow(configPath, constants3.O_RDONLY, void 0, platform, custody);
|
|
3720
|
+
} catch (error_) {
|
|
3721
|
+
if (error_ instanceof SecureFileCustodyError) throw error(error_.reason === "not_private" ? "config_not_private" : "invalid_config");
|
|
3921
3722
|
throw error("config_unavailable");
|
|
3922
3723
|
}
|
|
3923
3724
|
}
|
|
3924
3725
|
function validateOpenedFile(opened, pathStat, platform, error) {
|
|
3925
3726
|
if (!opened.isFile()) throw error("config_unavailable", refusalFacts([{ name: "opened_is_file", value: opened.isFile(), expected: "true" }, { name: "opened_mode", value: opened.mode.toString(8), expected: "a regular file mode" }]));
|
|
3926
|
-
if (
|
|
3727
|
+
if (opened.dev !== pathStat.dev || opened.ino !== pathStat.ino) throw error("config_unavailable", refusalFacts([{ name: "platform", value: platform, expected: "the same file the path named" }, { name: "opened_device", value: opened.dev, expected: String(pathStat.dev) }, { name: "opened_inode", value: opened.ino, expected: String(pathStat.ino) }]));
|
|
3927
3728
|
}
|
|
3928
3729
|
async function readBoundedConfig(handle, expectedBytes, error) {
|
|
3929
3730
|
const buffer = Buffer.allocUnsafe(expectedBytes + 1);
|
|
@@ -3955,152 +3756,120 @@ function firstDifferenceIndex(left, right) {
|
|
|
3955
3756
|
for (let index = 0; index < shared; index += 1) if (left[index] !== right[index]) return index;
|
|
3956
3757
|
return shared;
|
|
3957
3758
|
}
|
|
3759
|
+
function producerShapeFacts(value, options) {
|
|
3760
|
+
if (!isRecord8(value)) return [{ name: "config", value: "wrong_type", expected: "a record" }];
|
|
3761
|
+
const tolerated = options.tolerated ?? [];
|
|
3762
|
+
const allowed = [...options.keys, ...tolerated, ...Object.keys(HOST_PRODUCER_TIMING_DEFAULTS)];
|
|
3763
|
+
const facts = [
|
|
3764
|
+
...Object.keys(value).filter((key) => !allowed.includes(key)).map((key) => ({ name: key, value: "unknown", expected: `one of ${[...options.keys, ...tolerated].join(", ")}` })),
|
|
3765
|
+
...options.keys.filter((key) => !Object.hasOwn(value, key)).map((key) => ({ name: key, value: "missing", expected: "present" }))
|
|
3766
|
+
];
|
|
3767
|
+
if (Object.hasOwn(value, "schema_version") && value.schema_version !== options.schema) facts.push({ name: "schema_version", value: "wrong_type", expected: options.schema });
|
|
3768
|
+
return facts;
|
|
3769
|
+
}
|
|
3958
3770
|
function validateIdentity3(sourceRef, installationRef, epoch, error) {
|
|
3959
|
-
if (!
|
|
3771
|
+
if (!SAFE_REF6.test(sourceRef) || !SAFE_REF6.test(installationRef) || !positive(epoch, 2147483647)) throw error("invalid_config", refusalFacts(producerIdentityFacts(sourceRef, installationRef, epoch)));
|
|
3960
3772
|
return epoch;
|
|
3961
3773
|
}
|
|
3774
|
+
function producerIdentityFacts(sourceRef, installationRef, epoch) {
|
|
3775
|
+
const facts = [];
|
|
3776
|
+
if (!SAFE_REF6.test(sourceRef)) facts.push({ name: "host_source_ref", value: "wrong_type", expected: "a safe reference" });
|
|
3777
|
+
if (!SAFE_REF6.test(installationRef)) facts.push({ name: "producer_installation_ref", value: "wrong_type", expected: "a safe reference" });
|
|
3778
|
+
if (!positive(epoch, 2147483647)) facts.push({ name: "producer_epoch", value: "wrong_type", expected: "a positive integer" });
|
|
3779
|
+
return facts;
|
|
3780
|
+
}
|
|
3962
3781
|
function validateIntervals(reconcile, retry, timeout, error) {
|
|
3963
|
-
if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config");
|
|
3782
|
+
if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config", refusalFacts(producerIntervalFacts(reconcile, retry, timeout)));
|
|
3964
3783
|
return [reconcile, retry, timeout];
|
|
3965
3784
|
}
|
|
3966
|
-
function
|
|
3967
|
-
|
|
3785
|
+
function producerIntervalFacts(reconcile, retry, timeout) {
|
|
3786
|
+
const facts = [];
|
|
3787
|
+
if (!boundedMilliseconds(reconcile)) facts.push({ name: "reconcile_interval_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
|
|
3788
|
+
if (!boundedMilliseconds(retry)) facts.push({ name: "retry_backoff_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
|
|
3789
|
+
if (!boundedMilliseconds(timeout)) facts.push({ name: "ipc_timeout_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
|
|
3790
|
+
return facts;
|
|
3968
3791
|
}
|
|
3969
|
-
function
|
|
3792
|
+
function hasConfigKeys(value, required, tolerated = []) {
|
|
3793
|
+
return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || tolerated.includes(key) || Object.hasOwn(HOST_PRODUCER_TIMING_DEFAULTS, key));
|
|
3794
|
+
}
|
|
3795
|
+
function isRecord8(value) {
|
|
3970
3796
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3971
3797
|
}
|
|
3972
|
-
function
|
|
3798
|
+
function positive(value, maximum) {
|
|
3973
3799
|
return Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= maximum;
|
|
3974
3800
|
}
|
|
3975
3801
|
function boundedMilliseconds(value) {
|
|
3976
|
-
return
|
|
3802
|
+
return positive(value, 6e4) && Number(value) >= 25;
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
// host/service-log.ts
|
|
3806
|
+
import { copyFile, mkdir as mkdir3, stat as stat2, truncate, appendFile } from "node:fs/promises";
|
|
3807
|
+
import { dirname as dirname3, isAbsolute as isAbsolute4, posix as posix4, win32 as win325 } from "node:path";
|
|
3808
|
+
var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
|
|
3809
|
+
function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
|
|
3810
|
+
const line = `${JSON.stringify({ schema_version: CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA, producer_id, event, ...event === "watcher_scan_failed" && failure_kind !== void 0 ? { failure_kind } : {}, ...event === "watcher_scan_failed" && cause !== void 0 ? { cause } : {} })}
|
|
3811
|
+
`;
|
|
3812
|
+
try {
|
|
3813
|
+
(sink ?? defaultHostServiceDiagnosticSink)(line);
|
|
3814
|
+
} catch {
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
function defaultHostServiceDiagnosticSink(line) {
|
|
3818
|
+
writeHostServiceLogLine(line);
|
|
3819
|
+
process.stderr.write(line);
|
|
3820
|
+
}
|
|
3821
|
+
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
3822
|
+
function writeHostServiceLogLine(line, environment = process.env) {
|
|
3823
|
+
if (process.platform !== "win32") return;
|
|
3824
|
+
const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
|
|
3825
|
+
if (path === void 0 || !win325.isAbsolute(path) || win325.normalize(path) !== path) return;
|
|
3826
|
+
void appendHostServiceLogLine(path, line).catch(() => void 0);
|
|
3827
|
+
}
|
|
3828
|
+
async function appendHostServiceLogLine(path, line) {
|
|
3829
|
+
if (!isAbsolute4(path)) return;
|
|
3830
|
+
await ensureHostServiceLogDirectory(path);
|
|
3831
|
+
await appendFile(path, line);
|
|
3832
|
+
}
|
|
3833
|
+
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
3834
|
+
var hostServiceLogDirectories = /* @__PURE__ */ new Map();
|
|
3835
|
+
function ensureHostServiceLogDirectory(path) {
|
|
3836
|
+
const directory = dirname3(path);
|
|
3837
|
+
const existing = hostServiceLogDirectories.get(directory);
|
|
3838
|
+
if (existing !== void 0) return existing;
|
|
3839
|
+
const pending = mkdir3(directory, { recursive: true, mode: 448 }).then(() => void 0).catch((error) => {
|
|
3840
|
+
hostServiceLogDirectories.delete(directory);
|
|
3841
|
+
throw error;
|
|
3842
|
+
});
|
|
3843
|
+
hostServiceLogDirectories.set(directory, pending);
|
|
3844
|
+
return pending;
|
|
3977
3845
|
}
|
|
3978
3846
|
|
|
3979
3847
|
// host/codex/src/codex-producer.ts
|
|
3980
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
3981
|
-
var SAFE_REF8 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
3982
3848
|
var CodexProducer = class {
|
|
3983
|
-
producer_id = "codex";
|
|
3984
|
-
producer_label = "Codex";
|
|
3985
|
-
outbox_directory;
|
|
3986
3849
|
store;
|
|
3987
3850
|
core;
|
|
3988
3851
|
constructor(options) {
|
|
3989
3852
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
3990
|
-
this.outbox_directory = this.store.directory;
|
|
3991
3853
|
this.core = new HostProducerDrainCore(this, options);
|
|
3992
3854
|
}
|
|
3993
3855
|
drainOutbox() {
|
|
3994
3856
|
return this.core.drainOutbox();
|
|
3995
3857
|
}
|
|
3996
|
-
fail(code, message) {
|
|
3997
|
-
return new CodexHostAdapterError(code, message);
|
|
3998
|
-
}
|
|
3999
3858
|
listEnvelopes() {
|
|
4000
3859
|
return this.store.list();
|
|
4001
3860
|
}
|
|
4002
3861
|
removeEnvelope(envelope) {
|
|
4003
3862
|
return this.store.remove(envelope);
|
|
4004
3863
|
}
|
|
4005
|
-
submissionRefOf(envelope) {
|
|
4006
|
-
return envelope.submission_ref;
|
|
4007
|
-
}
|
|
4008
3864
|
expiresAtOf(envelope) {
|
|
4009
3865
|
return envelope.expires_at;
|
|
4010
3866
|
}
|
|
4011
|
-
createdAtOf(envelope) {
|
|
4012
|
-
return envelope.created_at;
|
|
4013
|
-
}
|
|
4014
|
-
async mapEnvelope(envelope) {
|
|
4015
|
-
const prompt = observation(envelope, "prompt");
|
|
4016
|
-
if (prompt !== void 0) return this.mapInput(envelope, prompt);
|
|
4017
|
-
return [mapTerminal(envelope)];
|
|
4018
|
-
}
|
|
4019
|
-
async mapInput(envelope, prompt) {
|
|
4020
|
-
const drafts = [];
|
|
4021
|
-
const session = observation(envelope, "session");
|
|
4022
|
-
if (session !== void 0) {
|
|
4023
|
-
const registrations = await this.store.listTranscriptRegistrations();
|
|
4024
|
-
const registration = registrations.find((item) => item.host_source_ref === envelope.identity.host_source_ref && item.host_session_ref === envelope.identity.host_session_ref);
|
|
4025
|
-
const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
|
|
4026
|
-
drafts.push({ frame_kind: "session_started", payload: compact({
|
|
4027
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
4028
|
-
working_directory_ref: `workspace:${createHash6("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
|
|
4029
|
-
model: recordText(session.value, "model"),
|
|
4030
|
-
host_version: nestedText(session.value, "host_version"),
|
|
4031
|
-
observations: [session]
|
|
4032
|
-
}) });
|
|
4033
|
-
}
|
|
4034
|
-
const turn = observation(envelope, "turn");
|
|
4035
|
-
if (turn !== void 0) drafts.push({ frame_kind: "turn_started", payload: compact({
|
|
4036
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
4037
|
-
local_turn_ref: localTurnRef(envelope),
|
|
4038
|
-
observations: [turn]
|
|
4039
|
-
}) });
|
|
4040
|
-
if (!isCealHostPromptObservationValue(prompt.value)) throw new CodexHostAdapterError("storage", "Codex prompt observation is malformed.");
|
|
4041
|
-
drafts.push({ frame_kind: "user_input", payload: compact({
|
|
4042
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
4043
|
-
local_turn_ref: localTurnRef(envelope),
|
|
4044
|
-
local_input_ref: envelope.host_input_ref,
|
|
4045
|
-
input_kind: envelope.input_kind,
|
|
4046
|
-
origin_input: envelope.origin_input,
|
|
4047
|
-
observations: [prompt],
|
|
4048
|
-
local_turn_aliases: envelope.codex_turn_id === void 0 ? void 0 : [envelope.codex_turn_id],
|
|
4049
|
-
context_expires_at: envelope.expires_at
|
|
4050
|
-
}) });
|
|
4051
|
-
return drafts;
|
|
4052
|
-
}
|
|
4053
3867
|
};
|
|
4054
|
-
function mapTerminal(envelope) {
|
|
4055
|
-
const timing = observation(envelope, "timing");
|
|
4056
|
-
const timingValue = asRecord(timing?.value);
|
|
4057
|
-
const aborted = timingValue?.terminal === "aborted";
|
|
4058
|
-
const usage = envelope.observations.filter((item) => ["token", "cost", "model"].includes(item.observation_kind));
|
|
4059
|
-
return { frame_kind: "turn_terminal", payload: compact({
|
|
4060
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
4061
|
-
local_turn_ref: localTurnRef(envelope),
|
|
4062
|
-
terminal_state: aborted ? "aborted" : "completed",
|
|
4063
|
-
abort_reason: aborted && typeof timingValue?.reason === "string" ? safeReasonRef(timingValue.reason) : void 0,
|
|
4064
|
-
duration_ms: nonNegativeNumber(timingValue?.duration_ms),
|
|
4065
|
-
time_to_first_token_ms: nonNegativeNumber(timingValue?.time_to_first_token_ms),
|
|
4066
|
-
usage: usage.length === 0 ? void 0 : Object.fromEntries(usage.map((item) => [item.observation_kind, item.value])),
|
|
4067
|
-
observations: envelope.observations
|
|
4068
|
-
}) };
|
|
4069
|
-
}
|
|
4070
|
-
function observation(envelope, kind) {
|
|
4071
|
-
return envelope.observations.find((item) => item.observation_kind === kind);
|
|
4072
|
-
}
|
|
4073
|
-
function localTurnRef(envelope) {
|
|
4074
|
-
return envelope.identity.host_turn_ref;
|
|
4075
|
-
}
|
|
4076
|
-
function compact(value) {
|
|
4077
|
-
return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
|
|
4078
|
-
}
|
|
4079
|
-
function asRecord(value) {
|
|
4080
|
-
return isRecord8(value) ? value : void 0;
|
|
4081
|
-
}
|
|
4082
|
-
function recordText(value, key) {
|
|
4083
|
-
const record3 = asRecord(value);
|
|
4084
|
-
return typeof record3?.[key] === "string" ? record3[key] : void 0;
|
|
4085
|
-
}
|
|
4086
|
-
function nestedText(value, key) {
|
|
4087
|
-
const nested = asRecord(asRecord(value)?.[key]);
|
|
4088
|
-
return typeof nested?.value === "string" ? nested.value : void 0;
|
|
4089
|
-
}
|
|
4090
|
-
function nonNegativeNumber(value) {
|
|
4091
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
4092
|
-
}
|
|
4093
|
-
function safeReasonRef(value) {
|
|
4094
|
-
return SAFE_REF8.test(value) ? value : `reason:${createHash6("sha256").update(value, "utf8").digest("hex")}`;
|
|
4095
|
-
}
|
|
4096
|
-
function isRecord8(value) {
|
|
4097
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4098
|
-
}
|
|
4099
3868
|
|
|
4100
3869
|
// host/codex/src/codex-transcript-watcher.ts
|
|
4101
3870
|
var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
4102
3871
|
store;
|
|
4103
|
-
|
|
3872
|
+
diagnostic;
|
|
4104
3873
|
constructor(options) {
|
|
4105
3874
|
super(options, {
|
|
4106
3875
|
default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
|
|
@@ -4108,7 +3877,7 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
4108
3877
|
clock_failure: () => new CodexHostAdapterError("storage", "Codex watcher clock is invalid.")
|
|
4109
3878
|
});
|
|
4110
3879
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
4111
|
-
this.
|
|
3880
|
+
this.diagnostic = options.diagnostic ?? (() => void 0);
|
|
4112
3881
|
}
|
|
4113
3882
|
transcriptPathOf(registration) {
|
|
4114
3883
|
return registration.transcript_path;
|
|
@@ -4124,7 +3893,8 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
4124
3893
|
if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
|
|
4125
3894
|
return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
|
|
4126
3895
|
}
|
|
4127
|
-
async scanFailed(handle, registration,
|
|
3896
|
+
async scanFailed(handle, registration, error) {
|
|
3897
|
+
this.diagnostic("watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
4128
3898
|
await this.quarantineFailure(registration, handle);
|
|
4129
3899
|
return { records: 0, enqueued: 0, quarantined: true };
|
|
4130
3900
|
}
|
|
@@ -4134,7 +3904,9 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
4134
3904
|
async preflight(registration, handle, size, device, inode) {
|
|
4135
3905
|
const existing = await this.store.readTranscriptCursor(registration);
|
|
4136
3906
|
let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
|
|
4137
|
-
if (cursor.status === "quarantined")
|
|
3907
|
+
if (cursor.status === "quarantined") {
|
|
3908
|
+
return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
3909
|
+
}
|
|
4138
3910
|
const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
|
|
4139
3911
|
if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
|
|
4140
3912
|
await this.quarantine(registration, cursor, device, inode, reason);
|
|
@@ -4146,22 +3918,22 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
4146
3918
|
let records = 0;
|
|
4147
3919
|
let enqueued = 0;
|
|
4148
3920
|
for (const line of complete.toString("utf8").split("\n").filter((value) => value.length > 0)) {
|
|
4149
|
-
const
|
|
4150
|
-
assertRecordSequence(registration, cursor, records, lastOrdinal,
|
|
4151
|
-
await this.observeSessionMetadata(registration,
|
|
4152
|
-
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);
|
|
4153
3925
|
activeTask = outcome.activeTask;
|
|
4154
3926
|
enqueued += outcome.enqueued;
|
|
4155
|
-
lastOrdinal =
|
|
3927
|
+
lastOrdinal = record.ordinal;
|
|
4156
3928
|
records += 1;
|
|
4157
3929
|
}
|
|
4158
3930
|
await this.commitCursor(registration, handle, cursor, complete.length, device, inode, lastOrdinal, activeTask);
|
|
4159
3931
|
return { records, enqueued, quarantined: false };
|
|
4160
3932
|
}
|
|
4161
|
-
async observeSessionMetadata(registration,
|
|
4162
|
-
if (
|
|
3933
|
+
async observeSessionMetadata(registration, record) {
|
|
3934
|
+
if (record.kind !== "session_meta") return;
|
|
4163
3935
|
const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
|
|
4164
|
-
if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version:
|
|
3936
|
+
if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record.cli_version } });
|
|
4165
3937
|
}
|
|
4166
3938
|
async commitCursor(registration, handle, cursor, consumedBytes, device, inode, lastOrdinal, activeTask) {
|
|
4167
3939
|
const offset = cursor.offset + consumedBytes;
|
|
@@ -4173,75 +3945,132 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
4173
3945
|
const stats = await handle.stat().catch(() => null);
|
|
4174
3946
|
if (stats === null) return;
|
|
4175
3947
|
const existing = await this.store.readTranscriptCursor(registration).catch(() => null);
|
|
4176
|
-
|
|
3948
|
+
const quarantined = {
|
|
3949
|
+
...existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now),
|
|
3950
|
+
transcript_path: registration.transcript_path,
|
|
3951
|
+
device: String(stats.dev),
|
|
3952
|
+
inode: String(stats.ino),
|
|
3953
|
+
status: "quarantined",
|
|
3954
|
+
reason: "transcript_malformed",
|
|
3955
|
+
updated_at: this.stamp()
|
|
3956
|
+
};
|
|
3957
|
+
await this.store.writeTranscriptCursor(quarantined);
|
|
4177
3958
|
}
|
|
4178
|
-
async processRecord(registration, activeTask,
|
|
4179
|
-
if (
|
|
4180
|
-
|
|
4181
|
-
}
|
|
4182
|
-
if (
|
|
4183
|
-
if (
|
|
4184
|
-
if (record3.kind === "user_text") return this.processUserInput(registration, activeTask, record3);
|
|
4185
|
-
if (record3.kind === "task_complete" || record3.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record3);
|
|
3959
|
+
async processRecord(registration, activeTask, record) {
|
|
3960
|
+
if (record.kind === "task_started") return this.processTaskStarted(registration, record);
|
|
3961
|
+
if (record.kind === "turn_context") return { ...activeTask === void 0 ? {} : { activeTask: record.turn_id === activeTask.codex_turn_id ? { ...activeTask, model: record.model } : activeTask }, enqueued: 0 };
|
|
3962
|
+
if (record.kind === "token_count") return { ...activeTask === void 0 ? {} : { activeTask: accumulateTokenCount(activeTask, record) }, enqueued: 0 };
|
|
3963
|
+
if (record.kind === "user_text") return this.processUserInput(registration, activeTask, record);
|
|
3964
|
+
if (record.kind === "task_complete" || record.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record);
|
|
4186
3965
|
return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
4187
3966
|
}
|
|
4188
|
-
async
|
|
4189
|
-
|
|
4190
|
-
|
|
3967
|
+
async processTaskStarted(registration, record) {
|
|
3968
|
+
return { activeTask: { codex_turn_id: record.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record.turn_id), started_at: record.timestamp }, enqueued: 0 };
|
|
3969
|
+
}
|
|
3970
|
+
async processUserInput(registration, activeTask, record) {
|
|
3971
|
+
if (activeTask === void 0 || activeTask.codex_turn_id !== record.turn_id) {
|
|
3972
|
+
const adopted = await this.adoptHookCorrelatedTurn(registration, record);
|
|
3973
|
+
if (adopted === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
3974
|
+
activeTask = adopted;
|
|
3975
|
+
}
|
|
3976
|
+
const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record.turn_id, record.message_id, record.ordinal, record.prompt);
|
|
4191
3977
|
const origin = activeTask.origin_host_input_ref === void 0;
|
|
4192
|
-
const promptValue = promptObservationValue(
|
|
3978
|
+
const promptValue = promptObservationValue(record.prompt);
|
|
4193
3979
|
const currentPromptRef = hostProducerPromptRef(promptValue.body_sha256);
|
|
4194
3980
|
const promptRef = activeTask.prompt_ref ?? currentPromptRef;
|
|
3981
|
+
const steeredPromptRef = await this.steeredPromptRef(registration, activeTask, origin, currentPromptRef);
|
|
4195
3982
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
4196
3983
|
const observations = [{
|
|
4197
|
-
observation_ref: observationRef("transcript_user_text", hostInputRef,
|
|
3984
|
+
observation_ref: observationRef(registration.host_source_ref, "transcript_user_text", hostInputRef, record.prompt),
|
|
4198
3985
|
observation_kind: "prompt",
|
|
4199
|
-
observed_at:
|
|
3986
|
+
observed_at: record.timestamp,
|
|
4200
3987
|
value: promptValue
|
|
4201
3988
|
}];
|
|
4202
3989
|
if (origin) {
|
|
4203
|
-
observations.push({ observation_ref: observationRef("transcript_task_started", activeTask.host_turn_ref,
|
|
3990
|
+
observations.push({ observation_ref: observationRef(registration.host_source_ref, "transcript_task_started", activeTask.host_turn_ref, record.turn_id), observation_kind: "turn", observed_at: record.timestamp, value: turnObservationValue(registration.codex_session_id, activeTask.host_turn_ref, "started") });
|
|
4204
3991
|
const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
|
|
4205
|
-
if (session !== null) observations.push({ observation_ref: observationRef("transcript_session", registration.host_session_ref, session.value), observation_kind: "session", observed_at:
|
|
3992
|
+
if (session !== null) observations.push({ observation_ref: observationRef(registration.host_source_ref, "transcript_session", registration.host_session_ref, session.value), observation_kind: "session", observed_at: record.timestamp, value: session.value });
|
|
4206
3993
|
}
|
|
4207
3994
|
const envelope = {
|
|
4208
3995
|
schema_version: "ceal.codex_observation_outbox.v1",
|
|
4209
3996
|
identity,
|
|
4210
3997
|
host_input_ref: hostInputRef,
|
|
4211
|
-
input_kind: record3.input_kind,
|
|
4212
3998
|
prompt_ref: promptRef,
|
|
4213
3999
|
observations,
|
|
4214
|
-
codex_turn_id:
|
|
4000
|
+
codex_turn_id: record.turn_id,
|
|
4215
4001
|
origin_input: origin,
|
|
4216
|
-
submission_ref: codexSubmissionReference(hostInputRef, "input",
|
|
4217
|
-
created_at:
|
|
4002
|
+
submission_ref: codexSubmissionReference(hostInputRef, "input", record.ordinal),
|
|
4003
|
+
created_at: record.timestamp,
|
|
4218
4004
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
4219
4005
|
};
|
|
4220
|
-
await this.
|
|
4006
|
+
const persisted = await this.persistUserInputTurn(activeTask, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope);
|
|
4007
|
+
if (steeredPromptRef !== null) await this.activateTranscriptTurnContext(identity, steeredPromptRef, envelope.expires_at);
|
|
4008
|
+
return persisted;
|
|
4009
|
+
}
|
|
4010
|
+
/**
|
|
4011
|
+
* The steered prompt to re-activate under the running task, or null when
|
|
4012
|
+
* there is nothing to repair. A steered message joins the running turn
|
|
4013
|
+
* with new prompt bytes but no task_started. When the steer hook already
|
|
4014
|
+
* replaced the turn selection (a turn id the hook did not recognize as
|
|
4015
|
+
* the running one, or a hook that ran before any context was live), the
|
|
4016
|
+
* running task's context no longer matches and the next invocation would
|
|
4017
|
+
* wait for a boundary that never comes. When the hook kept the live
|
|
4018
|
+
* selection, the context still matches and this stays null.
|
|
4019
|
+
*/
|
|
4020
|
+
async steeredPromptRef(registration, activeTask, origin, currentPromptRef) {
|
|
4021
|
+
if (origin || activeTask.prompt_ref === void 0 || activeTask.prompt_ref === currentPromptRef) return null;
|
|
4022
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref).catch(() => null);
|
|
4023
|
+
return selection?.prompt_ref === currentPromptRef ? currentPromptRef : null;
|
|
4024
|
+
}
|
|
4025
|
+
/**
|
|
4026
|
+
* Adopt the live hooked turn when its transcript message arrives before
|
|
4027
|
+
* (or without) its task_started, as an interrupted turn's follow-up can.
|
|
4028
|
+
* The UserPromptSubmit hook names the exact hooked prompt, so only a
|
|
4029
|
+
* message carrying those bytes adopts; inherited history and other turns
|
|
4030
|
+
* stay ignored. A stale active task for another turn releases first, the
|
|
4031
|
+
* same as a task_started supersession.
|
|
4032
|
+
*/
|
|
4033
|
+
async adoptHookCorrelatedTurn(registration, record) {
|
|
4034
|
+
const currentPromptRef = hostProducerPromptRef(promptObservationValue(record.prompt).body_sha256);
|
|
4035
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref);
|
|
4036
|
+
if (selection?.prompt_ref !== currentPromptRef) return void 0;
|
|
4037
|
+
return {
|
|
4038
|
+
codex_turn_id: record.turn_id,
|
|
4039
|
+
host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record.turn_id),
|
|
4040
|
+
started_at: record.timestamp
|
|
4041
|
+
};
|
|
4042
|
+
}
|
|
4043
|
+
async persistUserInputTurn(activeTask, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope) {
|
|
4221
4044
|
await this.store.put(envelope);
|
|
4222
|
-
if (origin) await activateTranscriptTurnContext(
|
|
4045
|
+
if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
|
|
4223
4046
|
return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
|
|
4224
4047
|
}
|
|
4225
|
-
async
|
|
4226
|
-
|
|
4227
|
-
|
|
4048
|
+
async activateTranscriptTurnContext(identity, promptRef, expiresAt) {
|
|
4049
|
+
try {
|
|
4050
|
+
const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
4051
|
+
const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
|
|
4052
|
+
await this.store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
|
|
4053
|
+
this.diagnostic("activation_recorded");
|
|
4054
|
+
} catch (error) {
|
|
4055
|
+
this.diagnostic("activation_failed");
|
|
4056
|
+
throw error;
|
|
4057
|
+
}
|
|
4228
4058
|
}
|
|
4229
|
-
async processTerminal(registration, activeTask,
|
|
4230
|
-
if (activeTask === void 0 || activeTask.codex_turn_id !==
|
|
4059
|
+
async processTerminal(registration, activeTask, record) {
|
|
4060
|
+
if (activeTask === void 0 || activeTask.codex_turn_id !== record.turn_id || activeTask.origin_host_input_ref === void 0 || activeTask.prompt_ref === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
4231
4061
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
4232
|
-
const timing =
|
|
4233
|
-
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);
|
|
4234
4064
|
await this.store.put({
|
|
4235
4065
|
schema_version: "ceal.codex_observation_outbox.v1",
|
|
4236
4066
|
identity,
|
|
4237
4067
|
host_input_ref: activeTask.origin_host_input_ref,
|
|
4238
|
-
input_kind: "host_automatic",
|
|
4239
4068
|
prompt_ref: activeTask.prompt_ref,
|
|
4240
4069
|
observations,
|
|
4241
|
-
codex_turn_id:
|
|
4070
|
+
codex_turn_id: record.turn_id,
|
|
4242
4071
|
origin_input: false,
|
|
4243
|
-
submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal",
|
|
4244
|
-
created_at:
|
|
4072
|
+
submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record.ordinal),
|
|
4073
|
+
created_at: record.timestamp,
|
|
4245
4074
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
4246
4075
|
});
|
|
4247
4076
|
await this.store.clearInvocationContext(identity, this.now());
|
|
@@ -4252,15 +4081,18 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
4252
4081
|
await this.store.writeTranscriptCursor(quarantined);
|
|
4253
4082
|
}
|
|
4254
4083
|
};
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4084
|
+
function codexTranscriptScanFailureKind(error) {
|
|
4085
|
+
return transcriptScanFailureKind(error, (candidate) => {
|
|
4086
|
+
if (!(candidate instanceof CodexHostAdapterError)) return void 0;
|
|
4087
|
+
if (candidate.code === "payload_rejected") return "payload_rejected";
|
|
4088
|
+
if (candidate.code === "storage") return "read_error";
|
|
4089
|
+
return void 0;
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
function accumulateTokenCount(activeTask, record) {
|
|
4093
|
+
if (record.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record.usage_gap_reason };
|
|
4094
|
+
const last = record.last_usage;
|
|
4095
|
+
const total = record.total_usage;
|
|
4264
4096
|
if (last === void 0 || ignoreTokenCount(activeTask.last_total_usage, total, last)) return activeTask;
|
|
4265
4097
|
const tokenUsage = addUsage(activeTask.token_usage, last);
|
|
4266
4098
|
return {
|
|
@@ -4274,21 +4106,16 @@ function ignoreTokenCount(previous, total, last) {
|
|
|
4274
4106
|
if (total === void 0 || previous === void 0) return false;
|
|
4275
4107
|
return sameUsage(total, previous) || regressedUsage(total, previous) && looksLikeStaleRegression(total, previous, last);
|
|
4276
4108
|
}
|
|
4277
|
-
function terminalObservations(registration, activeTask,
|
|
4109
|
+
function terminalObservations(registration, activeTask, record, timing) {
|
|
4278
4110
|
const tokenValue = activeTask.token_usage_gap_reason !== void 0 ? notRecorded(activeTask.token_usage_gap_reason) : activeTask.token_usage === void 0 ? notRecorded("token_count_not_observed_for_active_turn") : tokenObservationValue(activeTask.token_usage);
|
|
4279
|
-
const costValue = terminalCostValue();
|
|
4280
4111
|
const observations = [
|
|
4281
|
-
{ observation_ref: observationRef("transcript_terminal", activeTask.host_turn_ref,
|
|
4282
|
-
{ observation_ref: observationRef("transcript_timing", activeTask.host_turn_ref, timing), observation_kind: "timing", observed_at:
|
|
4283
|
-
{ observation_ref: observationRef("transcript_token", activeTask.host_turn_ref, tokenValue), observation_kind: "token", observed_at:
|
|
4284
|
-
{ observation_ref: observationRef("transcript_cost", activeTask.host_turn_ref, costValue), observation_kind: "cost", observed_at: record3.timestamp, value: costValue }
|
|
4112
|
+
{ observation_ref: observationRef(registration.host_source_ref, "transcript_terminal", activeTask.host_turn_ref, record), observation_kind: "turn", observed_at: record.timestamp, value: turnObservationValue(registration.codex_session_id, activeTask.host_turn_ref, "stopped") },
|
|
4113
|
+
{ observation_ref: observationRef(registration.host_source_ref, "transcript_timing", activeTask.host_turn_ref, timing), observation_kind: "timing", observed_at: record.timestamp, value: timing },
|
|
4114
|
+
{ observation_ref: observationRef(registration.host_source_ref, "transcript_token", activeTask.host_turn_ref, tokenValue), observation_kind: "token", observed_at: record.timestamp, value: tokenValue }
|
|
4285
4115
|
];
|
|
4286
|
-
if (activeTask.model !== void 0) observations.push({ observation_ref: observationRef("transcript_model", activeTask.host_turn_ref, activeTask.model), observation_kind: "model", observed_at:
|
|
4116
|
+
if (activeTask.model !== void 0) observations.push({ observation_ref: observationRef(registration.host_source_ref, "transcript_model", activeTask.host_turn_ref, activeTask.model), observation_kind: "model", observed_at: record.timestamp, value: { status: "available", value: activeTask.model, source: "turn_context.model" } });
|
|
4287
4117
|
return observations;
|
|
4288
4118
|
}
|
|
4289
|
-
function terminalCostValue() {
|
|
4290
|
-
return notRecorded("provider_cost_not_observed");
|
|
4291
|
-
}
|
|
4292
4119
|
function notRecorded(reason) {
|
|
4293
4120
|
return hostObservationNotRecorded(reason);
|
|
4294
4121
|
}
|
|
@@ -4335,16 +4162,21 @@ function looksLikeStaleRegression(current, previous, last) {
|
|
|
4335
4162
|
if (previous.total_tokens <= 0 || current.total_tokens <= 0 || last.total_tokens <= 0) return false;
|
|
4336
4163
|
return current.total_tokens * 100 >= previous.total_tokens * 98 || current.total_tokens + last.total_tokens * 2 >= previous.total_tokens;
|
|
4337
4164
|
}
|
|
4338
|
-
function assertRecordSequence(registration, cursor, records, lastOrdinal,
|
|
4339
|
-
if ((cursor.header_validated || records > 0) &&
|
|
4340
|
-
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);
|
|
4341
4168
|
}
|
|
4342
4169
|
|
|
4343
4170
|
// host/codex/src/codex-producer-runtime.ts
|
|
4344
4171
|
async function drainCodexProducerOnce(options) {
|
|
4345
|
-
const
|
|
4346
|
-
|
|
4347
|
-
|
|
4172
|
+
const scan = await new CodexTranscriptWatcher({
|
|
4173
|
+
outbox_dir: options.outbox_dir,
|
|
4174
|
+
diagnostic: (event, failure_kind) => writeHostServiceDiagnostic("codex", event, failure_kind)
|
|
4175
|
+
}).scanOnce().catch((error) => {
|
|
4176
|
+
writeHostServiceDiagnostic("codex", "watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
4177
|
+
throw error;
|
|
4178
|
+
});
|
|
4179
|
+
const producer = new CodexProducer({ outbox_dir: options.outbox_dir, ...options.now === void 0 ? {} : { now: options.now } });
|
|
4348
4180
|
return { scanned: scan.enqueued, ...await producer.drainOutbox() };
|
|
4349
4181
|
}
|
|
4350
4182
|
|
|
@@ -4357,11 +4189,7 @@ var CODEX_PRODUCER_DESCRIPTOR = {
|
|
|
4357
4189
|
extra_paths: [CODEX_SESSIONS_ROOT],
|
|
4358
4190
|
config: (shared, extras) => ({ ...shared, codex_sessions_root: hostProducerExtraPath(extras, CODEX_SESSIONS_ROOT) }),
|
|
4359
4191
|
runtime_options: (config) => Object.freeze({
|
|
4360
|
-
outbox_dir: config.outbox_dir
|
|
4361
|
-
producer_installation_ref: config.producer_installation_ref,
|
|
4362
|
-
producer_epoch: config.producer_epoch,
|
|
4363
|
-
relay_endpoint: config.relay_endpoint,
|
|
4364
|
-
timeout_ms: config.ipc_timeout_ms
|
|
4192
|
+
outbox_dir: config.outbox_dir
|
|
4365
4193
|
}),
|
|
4366
4194
|
drain: drainCodexProducerOnce
|
|
4367
4195
|
};
|
|
@@ -4408,18 +4236,13 @@ async function readStdin(stdin) {
|
|
|
4408
4236
|
return Buffer.concat(chunks).toString("utf8");
|
|
4409
4237
|
}
|
|
4410
4238
|
async function runCodexHookCli() {
|
|
4411
|
-
const unavailableGateway = {
|
|
4412
|
-
submitHostTurn: async () => {
|
|
4413
|
-
throw new Error("Codex Gateway adapter is not configured");
|
|
4414
|
-
}
|
|
4415
|
-
};
|
|
4416
4239
|
const args = process.argv.slice(2);
|
|
4417
|
-
const owner = await installedCodexHookRunnerOptions(args
|
|
4240
|
+
const owner = await installedCodexHookRunnerOptions(args);
|
|
4418
4241
|
return runCodexHookProcess(args, process.stdin, owner);
|
|
4419
4242
|
}
|
|
4420
|
-
async function installedCodexHookRunnerOptions(args
|
|
4243
|
+
async function installedCodexHookRunnerOptions(args) {
|
|
4421
4244
|
const config = await readHostProducerConfig(CODEX_PRODUCER_DESCRIPTOR, parseHostProducerConfigFlag(args));
|
|
4422
|
-
return {
|
|
4245
|
+
return { host_source_ref: config.host_source_ref, outbox_dir: config.outbox_dir, codex_sessions_root: config.codex_sessions_root };
|
|
4423
4246
|
}
|
|
4424
4247
|
|
|
4425
4248
|
// host/codex/bin/ceal-codex-hook.ts
|