@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
|
@@ -2059,16 +2059,34 @@ function isPlainRecord(value) {
|
|
|
2059
2059
|
return prototype === Object.prototype || prototype === null;
|
|
2060
2060
|
}
|
|
2061
2061
|
|
|
2062
|
+
// packages/ceal-client-protocol/src/client-wire-contract.ts
|
|
2063
|
+
import { createHash } from "node:crypto";
|
|
2064
|
+
|
|
2062
2065
|
// packages/ceal-client-protocol/client-wire-contract.json
|
|
2063
2066
|
var client_wire_contract_default = {
|
|
2064
2067
|
schema_version: "ceal.client_wire_contract.v1",
|
|
2068
|
+
doctor: {
|
|
2069
|
+
schema_version: "ceal.doctor.v1"
|
|
2070
|
+
},
|
|
2071
|
+
client_commands: {
|
|
2072
|
+
doctor: ["doctor"],
|
|
2073
|
+
version: ["version"],
|
|
2074
|
+
access_group_list: ["access-group", "list"]
|
|
2075
|
+
},
|
|
2076
|
+
client_environment: {
|
|
2077
|
+
environment_prefix: "CEAL",
|
|
2078
|
+
context_override_suffixes: ["GATEWAY_ORIGIN", "GATEWAY_TRANSPORT_ORIGIN", "ACCESS_TOKEN", "CONTEXT_STORE_PATH"]
|
|
2079
|
+
},
|
|
2065
2080
|
canonical_json: {
|
|
2066
2081
|
ordering: "utf16_code_unit_ascending"
|
|
2067
2082
|
},
|
|
2068
2083
|
artifact_descriptor: {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2084
|
+
operation_contract_bundle_schema_name: "ceal.operation_contract_bundle",
|
|
2085
|
+
operation_contract_bundle_fields: ["schema_version", "catalog_revision", "operations", "failure_guidance"],
|
|
2086
|
+
schema_name: "ceal.verified_artifact_descriptor",
|
|
2087
|
+
fields: ["schema_version", "kind", "media_type", "byte_count", "sha256", "binding"],
|
|
2088
|
+
binding_schema_name: "ceal.verified_artifact_binding",
|
|
2089
|
+
binding_fields: ["schema_version", "kind", "catalog_revision"],
|
|
2072
2090
|
kind: "verified_artifact",
|
|
2073
2091
|
binding_kind: "catalog",
|
|
2074
2092
|
media_type: "application/json",
|
|
@@ -2084,14 +2102,16 @@ var client_wire_contract_default = {
|
|
|
2084
2102
|
length: 72,
|
|
2085
2103
|
pattern: "^catalog:[a-f0-9]{64}$"
|
|
2086
2104
|
},
|
|
2087
|
-
envelope_max_bytes:
|
|
2105
|
+
envelope_max_bytes: 407
|
|
2088
2106
|
},
|
|
2089
2107
|
command_request: {
|
|
2090
|
-
|
|
2108
|
+
schema_name: "ceal.command_request",
|
|
2109
|
+
fields: ["schema_version", "argv", "operation", "client_nonce", "context_token"],
|
|
2091
2110
|
absolute_transport_max_bytes: 65536
|
|
2092
2111
|
},
|
|
2093
2112
|
command_resolution: {
|
|
2094
|
-
|
|
2113
|
+
schema_name: "ceal.gateway_command_resolution",
|
|
2114
|
+
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"],
|
|
2095
2115
|
authority: {
|
|
2096
2116
|
anonymous: {
|
|
2097
2117
|
state: "anonymous",
|
|
@@ -2131,7 +2151,8 @@ var client_wire_contract_default = {
|
|
|
2131
2151
|
}
|
|
2132
2152
|
},
|
|
2133
2153
|
command_response: {
|
|
2134
|
-
|
|
2154
|
+
schema_name: "ceal.command_response",
|
|
2155
|
+
fields: ["schema_version", "exit_code", "document", "operation_id", "visible_output_max_bytes", "local_delivery", "verified_artifact"],
|
|
2135
2156
|
descriptor_field: "verified_artifact",
|
|
2136
2157
|
required_fields: [
|
|
2137
2158
|
"schema_version",
|
|
@@ -2155,29 +2176,41 @@ var client_wire_contract_default = {
|
|
|
2155
2176
|
"required_action_sequence",
|
|
2156
2177
|
"withheld"
|
|
2157
2178
|
],
|
|
2158
|
-
prompt_field: "prompt",
|
|
2159
|
-
prompt_schema_version: "ceal.prompt.v1",
|
|
2160
|
-
focused_prompt_document_fields: [
|
|
2161
|
-
"catalog_revision",
|
|
2162
|
-
"prompt"
|
|
2163
|
-
],
|
|
2164
|
-
focused_prompt_continuation_document_fields: [
|
|
2165
|
-
"catalog_revision",
|
|
2166
|
-
"remaining_work",
|
|
2167
|
-
"prompt"
|
|
2168
|
-
],
|
|
2169
2179
|
discovery_command: "discovery",
|
|
2170
|
-
|
|
2180
|
+
discovery_manifest_schema_name: "ceal.discovery_manifest",
|
|
2181
|
+
discovery_manifest_fields: ["schema_version", "skill", "authority_scope", "connector_kinds", "personal_connector_kinds", "guide_markdown", "renderer_version", "digest"],
|
|
2171
2182
|
discovery_manifest_document_fields: [
|
|
2172
2183
|
"discovery_manifest"
|
|
2173
2184
|
],
|
|
2174
2185
|
exit_code_max: 125,
|
|
2175
2186
|
bare_document_max_bytes: 65536,
|
|
2176
|
-
complete_max_bytes:
|
|
2187
|
+
complete_max_bytes: 66046
|
|
2188
|
+
},
|
|
2189
|
+
host_invocation_authentication: {
|
|
2190
|
+
request_schema_name: "ceal.host_invocation_authentication_request",
|
|
2191
|
+
request_fields: [
|
|
2192
|
+
"schema_version",
|
|
2193
|
+
"client_nonce"
|
|
2194
|
+
],
|
|
2195
|
+
result_schema_name: "ceal.host_invocation_authentication_result",
|
|
2196
|
+
result_fields: [
|
|
2197
|
+
"schema_version",
|
|
2198
|
+
"context_token",
|
|
2199
|
+
"expires_at"
|
|
2200
|
+
],
|
|
2201
|
+
client_nonce: {
|
|
2202
|
+
character_class_source: "A-Za-z0-9_-",
|
|
2203
|
+
encoded_characters: 43
|
|
2204
|
+
}
|
|
2205
|
+
},
|
|
2206
|
+
gateway_host_invocation_context_request: {
|
|
2207
|
+
schema_name: "ceal.gateway_host_invocation_context_request",
|
|
2208
|
+
fields: ["schema_version", "client_nonce", "context_expires_at", "request_work_context", "current_prompt", "visible_output_max_bytes"]
|
|
2177
2209
|
},
|
|
2178
2210
|
resource_stream: {
|
|
2179
2211
|
path: "/api/ceal/v2/resources",
|
|
2180
|
-
|
|
2212
|
+
request_schema_name: "ceal.resource_stream_request",
|
|
2213
|
+
request_fields: ["schema_version", "resource_ref", "client_nonce", "context_token"],
|
|
2181
2214
|
ref_pattern: "^resource:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
2182
2215
|
media_type_pattern: "^[a-z0-9][a-z0-9!#$&^_.+-]{0,63}/[a-z0-9][a-z0-9!#$&^_.+-]{0,63}$",
|
|
2183
2216
|
filename_max_bytes: 512,
|
|
@@ -2194,92 +2227,6 @@ var client_wire_contract_default = {
|
|
|
2194
2227
|
}
|
|
2195
2228
|
};
|
|
2196
2229
|
|
|
2197
|
-
// packages/ceal-client-protocol/src/client-wire-contract.ts
|
|
2198
|
-
var CEAL_CLIENT_WIRE_CONTRACT = Object.freeze(client_wire_contract_default);
|
|
2199
|
-
var commandResolution = CEAL_CLIENT_WIRE_CONTRACT.command_resolution;
|
|
2200
|
-
var commandResolutionAuthority = commandResolution.authority;
|
|
2201
|
-
var commandResolutionSemanticOptions = commandResolution.semantic_options;
|
|
2202
|
-
var CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION = commandResolution.schema_version;
|
|
2203
|
-
var CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES = CEAL_CLIENT_WIRE_CONTRACT.encoded_json_document.max_bytes;
|
|
2204
|
-
var artifact = CEAL_CLIENT_WIRE_CONTRACT.artifact_descriptor;
|
|
2205
|
-
var request = CEAL_CLIENT_WIRE_CONTRACT.command_request;
|
|
2206
|
-
var response = CEAL_CLIENT_WIRE_CONTRACT.command_response;
|
|
2207
|
-
var CEAL_OPERATION_CONTRACT_BUNDLE_SCHEMA_VERSION = artifact.operation_contract_bundle_schema_version;
|
|
2208
|
-
var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_SCHEMA_VERSION = artifact.schema_version;
|
|
2209
|
-
var CEAL_VERIFIED_ARTIFACT_BINDING_SCHEMA_VERSION = artifact.binding_schema_version;
|
|
2210
|
-
var CEAL_VERIFIED_ARTIFACT_KIND = artifact.kind;
|
|
2211
|
-
var CEAL_VERIFIED_ARTIFACT_BINDING_KIND = artifact.binding_kind;
|
|
2212
|
-
var CEAL_VERIFIED_ARTIFACT_MEDIA_TYPE = artifact.media_type;
|
|
2213
|
-
var CEAL_VERIFIED_ARTIFACT_DIGEST_LENGTH = artifact.digest.length;
|
|
2214
|
-
var CEAL_VERIFIED_ARTIFACT_DIGEST_PATTERN = artifact.digest.pattern;
|
|
2215
|
-
var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_LENGTH = artifact.catalog_revision.length;
|
|
2216
|
-
var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_PATTERN = artifact.catalog_revision.pattern;
|
|
2217
|
-
var CEAL_VERIFIED_ARTIFACT_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
|
|
2218
|
-
var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_MAX_BYTES = artifact.envelope_max_bytes;
|
|
2219
|
-
var CEAL_COMMAND_REQUEST_SCHEMA_VERSION = request.schema_version;
|
|
2220
|
-
var CEAL_COMMAND_REQUEST_MAX_BYTES = request.absolute_transport_max_bytes;
|
|
2221
|
-
var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_MAX_BYTES = response.bare_document_max_bytes;
|
|
2222
|
-
var CEAL_COMMAND_RESPONSE_PROMPT_FIELD = response.prompt_field;
|
|
2223
|
-
var CEAL_COMMAND_RESPONSE_PROMPT_SCHEMA_VERSION = response.prompt_schema_version;
|
|
2224
|
-
var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_OPTIONAL_FIELDS = Object.freeze([...response.bare_document_optional_fields]);
|
|
2225
|
-
var CEAL_COMMAND_RESPONSE_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.bare_document_fields, response.prompt_field]);
|
|
2226
|
-
var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_document_fields]);
|
|
2227
|
-
var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_CONTINUATION_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_continuation_document_fields]);
|
|
2228
|
-
var CEAL_COMMAND_RESPONSE_DISCOVERY_COMMAND = response.discovery_command;
|
|
2229
|
-
var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_SCHEMA_VERSION = response.discovery_manifest_schema_version;
|
|
2230
|
-
var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_DOCUMENT_FIELDS = Object.freeze([...response.discovery_manifest_document_fields]);
|
|
2231
|
-
var CEAL_COMMAND_RESPONSE_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
|
|
2232
|
-
|
|
2233
|
-
// packages/ceal-client-protocol/src/vocabulary.ts
|
|
2234
|
-
function vocabulary(words) {
|
|
2235
|
-
return Object.freeze(words);
|
|
2236
|
-
}
|
|
2237
|
-
function isVocabularyWord(words, value) {
|
|
2238
|
-
return words.some((word) => word === value);
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
// packages/ceal-client-protocol/src/gateway-response-types.ts
|
|
2242
|
-
var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
|
|
2243
|
-
var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
|
|
2244
|
-
var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
|
|
2245
|
-
var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
|
|
2246
|
-
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"]);
|
|
2247
|
-
var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
|
|
2248
|
-
var CEAL_GATEWAY_DERIVED_STORE_FAILURE_CLASSES = vocabulary(["storage_unavailable", "decode_mismatch", "write_conflict", "quota", "store_error"]);
|
|
2249
|
-
var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
|
|
2250
|
-
var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
|
|
2251
|
-
var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
|
|
2252
|
-
var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "provider_authenticated_event"]);
|
|
2253
|
-
var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
|
|
2254
|
-
var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
|
|
2255
|
-
var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
|
|
2256
|
-
var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
|
|
2257
|
-
var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
|
|
2258
|
-
var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verified", "not_dispatched"]);
|
|
2259
|
-
var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
|
|
2260
|
-
var CEAL_PROTOCOL_VERSION = "1.4.0";
|
|
2261
|
-
var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
|
|
2262
|
-
var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
|
|
2263
|
-
var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
|
|
2264
|
-
"audited_call_pending",
|
|
2265
|
-
"continuation_read_pending",
|
|
2266
|
-
"continuation_write_pending",
|
|
2267
|
-
"provider_pacing_pending",
|
|
2268
|
-
"corpus_replay_pending",
|
|
2269
|
-
"corpus_append_pending",
|
|
2270
|
-
"corpus_fence_pending",
|
|
2271
|
-
"corpus_content_pending",
|
|
2272
|
-
"corpus_transaction_pending"
|
|
2273
|
-
]);
|
|
2274
|
-
var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
|
|
2275
|
-
var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
|
|
2276
|
-
var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
|
|
2277
|
-
var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
|
|
2278
|
-
"connector_ref",
|
|
2279
|
-
"connector_revision",
|
|
2280
|
-
"policy_decision_revision"
|
|
2281
|
-
]);
|
|
2282
|
-
|
|
2283
2230
|
// packages/ceal-client-protocol/src/operation-next-actions.ts
|
|
2284
2231
|
var CEAL_REPEAT_REQUEST_NEXT_ACTION = "repeat_request";
|
|
2285
2232
|
var CEAL_INSPECT_RECEIPT_NEXT_ACTION = "inspect_receipt";
|
|
@@ -2346,7 +2293,10 @@ var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
|
2346
2293
|
"follow_required_action_sequence",
|
|
2347
2294
|
// The command needs caller-owned local materialization (for example an
|
|
2348
2295
|
// @path file) before the Gateway can receive the argument.
|
|
2349
|
-
"materialize_local_argument"
|
|
2296
|
+
"materialize_local_argument",
|
|
2297
|
+
// A peer's derived schema identifier changed with its field set; install or
|
|
2298
|
+
// upgrade the CLI and Host Relay to the Gateway's release before retrying.
|
|
2299
|
+
"upgrade_ceal_client"
|
|
2350
2300
|
];
|
|
2351
2301
|
var CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS = Object.freeze(CALLER_RECOVERABLE_NEXT_ACTION_VALUES);
|
|
2352
2302
|
var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
@@ -2459,47 +2409,56 @@ var CEAL_OPERATION_WITHHELD_NEXT_ACTIONS = Object.freeze({
|
|
|
2459
2409
|
personal_authorization_required: "connect_personal_account"
|
|
2460
2410
|
});
|
|
2461
2411
|
|
|
2462
|
-
// packages/ceal-client-protocol/src/
|
|
2463
|
-
|
|
2412
|
+
// packages/ceal-client-protocol/src/vocabulary.ts
|
|
2413
|
+
function vocabulary(words) {
|
|
2414
|
+
return Object.freeze(words);
|
|
2415
|
+
}
|
|
2416
|
+
function isVocabularyWord(words, value) {
|
|
2417
|
+
return words.some((word) => word === value);
|
|
2418
|
+
}
|
|
2464
2419
|
|
|
2465
|
-
// packages/ceal-client-protocol/src/
|
|
2466
|
-
var
|
|
2467
|
-
var
|
|
2468
|
-
var
|
|
2469
|
-
var
|
|
2470
|
-
var
|
|
2471
|
-
var
|
|
2472
|
-
var
|
|
2473
|
-
var
|
|
2474
|
-
var
|
|
2475
|
-
var
|
|
2476
|
-
var
|
|
2477
|
-
var
|
|
2478
|
-
var
|
|
2479
|
-
var
|
|
2480
|
-
var
|
|
2481
|
-
var
|
|
2482
|
-
var
|
|
2483
|
-
var
|
|
2484
|
-
var
|
|
2485
|
-
var
|
|
2486
|
-
var
|
|
2487
|
-
var
|
|
2488
|
-
var
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
"
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2420
|
+
// packages/ceal-client-protocol/src/gateway-response-types.ts
|
|
2421
|
+
var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
|
|
2422
|
+
var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
|
|
2423
|
+
var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
|
|
2424
|
+
var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
|
|
2425
|
+
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"]);
|
|
2426
|
+
var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
|
|
2427
|
+
var CEAL_GATEWAY_DERIVED_STORE_FAILURE_CLASSES = vocabulary(["storage_unavailable", "decode_mismatch", "write_conflict", "quota", "store_error"]);
|
|
2428
|
+
var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
|
|
2429
|
+
var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
|
|
2430
|
+
var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
|
|
2431
|
+
var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "provider_authenticated_event"]);
|
|
2432
|
+
var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
|
|
2433
|
+
var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
|
|
2434
|
+
var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
|
|
2435
|
+
var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
|
|
2436
|
+
var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
|
|
2437
|
+
var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verified", "not_dispatched"]);
|
|
2438
|
+
var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
|
|
2439
|
+
var CEAL_PROTOCOL_VERSION = "1.4.0";
|
|
2440
|
+
var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
|
|
2441
|
+
var CEAL_GATEWAY_CALL_RESULT_SCHEMA_VERSION = "ceal.gateway_call_result.v1";
|
|
2442
|
+
var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
|
|
2443
|
+
var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
|
|
2444
|
+
"audited_call_pending",
|
|
2445
|
+
"continuation_read_pending",
|
|
2446
|
+
"continuation_write_pending",
|
|
2447
|
+
"provider_pacing_pending",
|
|
2448
|
+
"corpus_replay_pending",
|
|
2449
|
+
"corpus_append_pending",
|
|
2450
|
+
"corpus_fence_pending",
|
|
2451
|
+
"corpus_content_pending",
|
|
2452
|
+
"corpus_transaction_pending"
|
|
2453
|
+
]);
|
|
2454
|
+
var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
|
|
2455
|
+
var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
|
|
2456
|
+
var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
|
|
2457
|
+
var CEAL_GATEWAY_AUDIT_CALL_DETAIL_SCHEMA_VERSION = "ceal.gateway_audit_call_detail.v1";
|
|
2458
|
+
var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
|
|
2459
|
+
"connector_ref",
|
|
2460
|
+
"connector_revision",
|
|
2461
|
+
"policy_decision_revision"
|
|
2503
2462
|
]);
|
|
2504
2463
|
|
|
2505
2464
|
// packages/ceal-client-protocol/src/gateway-validation-primitives.ts
|
|
@@ -2541,10 +2500,10 @@ function assertSafeJsonArray(value, options, depth, count) {
|
|
|
2541
2500
|
if (value.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
2542
2501
|
for (const item of value) assertSafeJsonValue(item, options, depth + 1, count);
|
|
2543
2502
|
}
|
|
2544
|
-
function assertSafeJsonRecord(
|
|
2545
|
-
const entries = Object.entries(
|
|
2503
|
+
function assertSafeJsonRecord(record2, options, depth, count) {
|
|
2504
|
+
const entries = Object.entries(record2);
|
|
2546
2505
|
if (entries.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
2547
|
-
const sourceUrlColumn = options.allowHttpsUrl ? compactSourceUrlColumn(
|
|
2506
|
+
const sourceUrlColumn = options.allowHttpsUrl ? compactSourceUrlColumn(record2.fields) : null;
|
|
2548
2507
|
for (const [key, child] of entries) {
|
|
2549
2508
|
if (key === "credential_material_included" && child !== false) invalidByContext(options);
|
|
2550
2509
|
if (!isSafeNegativeMaterialAssertion(key, child)) assertSafeJsonKey(key, options);
|
|
@@ -2639,6 +2598,11 @@ function isCealJsonRecord(value) {
|
|
|
2639
2598
|
}
|
|
2640
2599
|
var CEAL_VALIDATION_STATUSES = vocabulary(["not_applicable", "valid", "invalid"]);
|
|
2641
2600
|
var CEAL_PARSE_STATUSES = vocabulary(["not_applicable", "parsed", "parse_failed"]);
|
|
2601
|
+
function hasExactKeys(value, expected) {
|
|
2602
|
+
const actual = Object.keys(value).sort();
|
|
2603
|
+
const wanted = [...expected].sort();
|
|
2604
|
+
return actual.length === wanted.length && actual.every((key, index) => key === wanted[index]);
|
|
2605
|
+
}
|
|
2642
2606
|
function invalidByContext(options) {
|
|
2643
2607
|
if (options.forbidAuthorityKeys) invalidRequest();
|
|
2644
2608
|
invalidResponse();
|
|
@@ -2713,51 +2677,6 @@ function cut(text) {
|
|
|
2713
2677
|
return text.length <= REFUSAL_VALUE_MAX_CHARS ? text : `${text.slice(0, REFUSAL_VALUE_MAX_CHARS)}...(+${text.length - REFUSAL_VALUE_MAX_CHARS} chars)`;
|
|
2714
2678
|
}
|
|
2715
2679
|
|
|
2716
|
-
// packages/ceal-client-protocol/src/operation-access-class.ts
|
|
2717
|
-
var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
|
|
2718
|
-
|
|
2719
|
-
// packages/ceal-client-protocol/src/domain-vocabulary.ts
|
|
2720
|
-
var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
|
|
2721
|
-
var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
|
|
2722
|
-
var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
|
|
2723
|
-
var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
|
|
2724
|
-
var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
|
|
2725
|
-
var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
|
|
2726
|
-
var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
|
|
2727
|
-
var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
|
|
2728
|
-
var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
|
|
2729
|
-
var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
|
|
2730
|
-
var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
|
|
2731
|
-
var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
|
|
2732
|
-
|
|
2733
|
-
// packages/ceal-client-protocol/src/safe-json-budget.ts
|
|
2734
|
-
var SAFE_JSON_MIN_BYTES_PER_NODE = 4;
|
|
2735
|
-
function safeJsonNodeBudgetForBytes(maxBytes) {
|
|
2736
|
-
if (!Number.isSafeInteger(maxBytes) || maxBytes < SAFE_JSON_MIN_BYTES_PER_NODE) {
|
|
2737
|
-
throw new RangeError(`safe-JSON byte cap is invalid: ${refusalFact("maxBytes", maxBytes, `at least ${SAFE_JSON_MIN_BYTES_PER_NODE} bytes`)}`);
|
|
2738
|
-
}
|
|
2739
|
-
return Math.floor(maxBytes / SAFE_JSON_MIN_BYTES_PER_NODE);
|
|
2740
|
-
}
|
|
2741
|
-
|
|
2742
|
-
// packages/ceal-client-protocol/src/protocol-bounds.ts
|
|
2743
|
-
var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES = 64 * 1024;
|
|
2744
|
-
var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_NODES = safeJsonNodeBudgetForBytes(CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES);
|
|
2745
|
-
var OPERATION_SCHEMA_LITERAL_MAX_BYTES = CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES / 4;
|
|
2746
|
-
var CEAL_OPERATION_JSON_SCHEMA_BUDGET = Object.freeze({
|
|
2747
|
-
max_depth: 8,
|
|
2748
|
-
max_properties: 96,
|
|
2749
|
-
max_string_length: OPERATION_SCHEMA_LITERAL_MAX_BYTES / 4,
|
|
2750
|
-
max_alternatives: 8,
|
|
2751
|
-
max_enum_values: 64,
|
|
2752
|
-
max_literal_bytes: OPERATION_SCHEMA_LITERAL_MAX_BYTES,
|
|
2753
|
-
/** @basis our_policy -- bounds explanatory schema prose carried in one catalog. */
|
|
2754
|
-
max_description_characters: 1024,
|
|
2755
|
-
/** @basis our_policy -- bounds regular-expression compilation input from one schema. */
|
|
2756
|
-
max_pattern_characters: 256,
|
|
2757
|
-
/** @basis our_policy -- bounds traversal work inside one already byte-bounded schema literal. */
|
|
2758
|
-
max_literal_nodes: 512
|
|
2759
|
-
});
|
|
2760
|
-
|
|
2761
2680
|
// packages/ceal-client-protocol/src/operation-validation-primitives.ts
|
|
2762
2681
|
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;
|
|
2763
2682
|
var CEAL_CALENDAR_DATE_PATTERN_SOURCE = "^\\d{4}-\\d{2}-\\d{2}$";
|
|
@@ -2825,38 +2744,366 @@ function requireIsoTimestamp(value, message) {
|
|
|
2825
2744
|
if (!isCealIsoTimestamp(value)) throw new TypeError(message);
|
|
2826
2745
|
}
|
|
2827
2746
|
|
|
2828
|
-
// packages/ceal-client-protocol/src/
|
|
2829
|
-
var
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
var
|
|
2833
|
-
var
|
|
2834
|
-
var
|
|
2835
|
-
var
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2747
|
+
// packages/ceal-client-protocol/src/failure-guidance-table.ts
|
|
2748
|
+
var CEAL_FAILURE_REDACTION_CLASSES = vocabulary(["model_safe", "withheld"]);
|
|
2749
|
+
var CEAL_GATEWAY_STARTING_FAILURE_CODE = "gateway_starting";
|
|
2750
|
+
var CEAL_OWNER_BOOTSTRAP_ABSENT_FAILURE_CODE = "bootstrap_absent";
|
|
2751
|
+
var CEAL_OWNER_BOOTSTRAP_UNAVAILABLE_FAILURE_CODE = "bootstrap_unavailable";
|
|
2752
|
+
var LOCAL_COMMAND_INVALID = "The local command or argument is invalid.";
|
|
2753
|
+
var LOCAL_TRANSPORT_INCOMPLETE = "The local Ceal transport could not complete; report this as a gap.";
|
|
2754
|
+
var GATEWAY_TRANSPORT_UNAVAILABLE = "The Gateway transport is unavailable; ask an administrator to restore Gateway availability.";
|
|
2755
|
+
var HOST_AUTHENTICATION_UNAVAILABLE = "The Host could not authenticate this invocation; report this as a gap.";
|
|
2756
|
+
var SESSION_CONTEXT_UNAVAILABLE = "The local Gateway session context is unavailable.";
|
|
2757
|
+
function guidance(message, next_action, http_status, retryable = false, redaction_class = "model_safe") {
|
|
2758
|
+
return Object.freeze({ message, next_action, http_status, retryable, redaction_class });
|
|
2759
|
+
}
|
|
2760
|
+
var CEAL_FAILURE_GUIDANCE_TABLE = Object.freeze({
|
|
2761
|
+
invalid_command: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 400),
|
|
2762
|
+
invalid_argument: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 400),
|
|
2763
|
+
command_not_found: guidance(LOCAL_COMMAND_INVALID, "correct_argument", 404),
|
|
2764
|
+
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),
|
|
2765
|
+
local_route_unavailable: guidance("The requested local route is unavailable or its descriptor is invalid; report this as a gap.", "answer_with_gap", 404),
|
|
2766
|
+
local_route_ambiguous: guidance("The requested local route is ambiguous; no local executable was selected. Report this as a gap.", "answer_with_gap", 400),
|
|
2767
|
+
local_execution_unavailable: guidance("The selected local route executable is unavailable; report this as a gap.", "answer_with_gap", 500),
|
|
2768
|
+
local_pickup_failed: guidance("The Host Request pickup failed; the Gateway command was not sent. Report this as a gap.", "answer_with_gap", 502),
|
|
2769
|
+
invocation_authentication_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 401),
|
|
2770
|
+
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),
|
|
2771
|
+
gateway_origin_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2772
|
+
access_token_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2773
|
+
context_pair_required: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2774
|
+
context_not_selected: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2775
|
+
context_store_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2776
|
+
context_store_conflict: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2777
|
+
refresh_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2778
|
+
refresh_expired: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2779
|
+
refresh_replayed: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2780
|
+
refresh_revoked: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2781
|
+
refresh_recovery_unavailable: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2782
|
+
refresh_custody_invalid: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2783
|
+
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),
|
|
2784
|
+
local_output_exists: guidance("The selected local output path already exists; nothing was written to it.", "choose_new_output_path", 400),
|
|
2785
|
+
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),
|
|
2786
|
+
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),
|
|
2787
|
+
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),
|
|
2788
|
+
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),
|
|
2789
|
+
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),
|
|
2790
|
+
response_encoding_failed: guidance("The local response could not be encoded; report this as a gap.", "answer_with_gap", 500),
|
|
2791
|
+
request_timeout: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 504),
|
|
2792
|
+
request_failed: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 502),
|
|
2793
|
+
gateway_unavailable: guidance(GATEWAY_TRANSPORT_UNAVAILABLE, "ask_admin", 503, true),
|
|
2794
|
+
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),
|
|
2795
|
+
// `gateway_starting` is the one retryable envelope the serving gate answers on every route while starting
|
|
2796
|
+
// (`cli/internal/client/gateway_startup_wait.go` re-drives it); the sentence is the client default that renders
|
|
2797
|
+
// when the wait ends still unserved, moved from the `localFailureText` default branch.
|
|
2798
|
+
[CEAL_GATEWAY_STARTING_FAILURE_CODE]: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503, true),
|
|
2799
|
+
gateway_internal_error: guidance("The Gateway failed without exposing internal state; report this as a gap.", "answer_with_gap", 500),
|
|
2800
|
+
// The serving gate emits this code with no message; the sentence is the owner-normalized form of the
|
|
2801
|
+
// `gateway_internal_error` guidance (decided with the operator 2026-10-13).
|
|
2802
|
+
internal_error: guidance("The Gateway failed without exposing internal state.", "answer_with_gap", 500),
|
|
2803
|
+
// Owned by the Gateway canonical adapter (`gateway/core/src/gateway-canonical-operation-http-adapter.ts`):
|
|
2804
|
+
// the client variant of this sentence in `localFailureText` was retired when the client started reading this table.
|
|
2805
|
+
request_body_too_large: guidance("The Gateway request body exceeds its bounded limit.", "reduce_request_size", 413),
|
|
2806
|
+
// Restored verbatim from the retired `localFailureText` `unknown_outcome`
|
|
2807
|
+
// case: the outcome is unknown, so a read may be retried while a write
|
|
2808
|
+
// must receipt-check first. Collapsing both to "do not repeat the call"
|
|
2809
|
+
// forbids the safe read retry the sentence exists to allow.
|
|
2810
|
+
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),
|
|
2811
|
+
// Restored verbatim from the retired `localFailureText`
|
|
2812
|
+
// `refresh_unknown_outcome` case: a refresh that ends unknown leaves the
|
|
2813
|
+
// session context unavailable, so the caller repairs or adopts the
|
|
2814
|
+
// session instead of reporting a transport gap.
|
|
2815
|
+
refresh_unknown_outcome: guidance(SESSION_CONTEXT_UNAVAILABLE, "repair_or_adopt_session", 401),
|
|
2816
|
+
request_cancelled: guidance("The caller cancelled the request before it completed.", "retry", 400, true),
|
|
2817
|
+
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),
|
|
2818
|
+
denied: guidance("An administrator denied this device's activation request; ask an administrator whether to reconsider it.", "ask_admin", 403),
|
|
2819
|
+
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),
|
|
2820
|
+
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),
|
|
2821
|
+
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),
|
|
2822
|
+
recovery_required: guidance("The Gateway could not safely complete the activation transaction automatically; ask an administrator to recover this transaction.", "ask_admin", 500),
|
|
2823
|
+
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),
|
|
2824
|
+
// Activation commit codes rendered through the `localFailureText` default before phase 2b; `retryable` is moved from the
|
|
2825
|
+
// emitting sites in `cli/internal/client/gateway_installation_activation.go` and `activation.go:483`.
|
|
2826
|
+
activation_state_commit_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500, true),
|
|
2827
|
+
activation_response_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502, true),
|
|
2828
|
+
activation_rejected: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502),
|
|
2829
|
+
activation_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
|
|
2830
|
+
activation_binding: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
|
|
2831
|
+
activation_crypto: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
|
|
2832
|
+
activation_state_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
|
|
2833
|
+
// Long-tail local codes rendered through the `localFailureText` default before phase 2b; sentences moved from that branch.
|
|
2834
|
+
invalid_response: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
|
|
2835
|
+
invalid_configuration: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
|
|
2836
|
+
activation_state_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
|
|
2837
|
+
activation_code_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
|
|
2838
|
+
activation_code_file_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
|
|
2839
|
+
activation_code_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 404),
|
|
2840
|
+
bootstrap_file_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
|
|
2841
|
+
bootstrap_not_active: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 409),
|
|
2842
|
+
bootstrap_unauthorized: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
|
|
2843
|
+
// The Gateway owner-bootstrap store file was never created, so the Gateway
|
|
2844
|
+
// holds no owner bootstrap to exchange (`gateway/src/host/owner-bootstrap-http-composition.ts`
|
|
2845
|
+
// `absent`, stored at `gateway/src/shared/installation/owner-bootstrap-store.ts:162`);
|
|
2846
|
+
// the sentence is the owner-normalized form of that store error. Non-retryable:
|
|
2847
|
+
// repeating the exchange cannot create the store; only an administrator
|
|
2848
|
+
// placing a bootstrap (fresh gateway setup) changes the outcome.
|
|
2849
|
+
[CEAL_OWNER_BOOTSTRAP_ABSENT_FAILURE_CODE]: guidance("The Gateway holds no owner bootstrap to exchange; report this as a gap.", "answer_with_gap", 503),
|
|
2850
|
+
// The Gateway owner-bootstrap store file exists but cannot be read, so the
|
|
2851
|
+
// exchange cannot proceed (`gateway/src/host/owner-bootstrap-http-composition.ts`
|
|
2852
|
+
// `unavailable`, stored at `gateway/src/shared/installation/owner-bootstrap-store.ts:163`);
|
|
2853
|
+
// the sentence is the owner-normalized form of that store error. Retryable:
|
|
2854
|
+
// unlike `absent`, a later exchange may succeed once the store is readable.
|
|
2855
|
+
[CEAL_OWNER_BOOTSTRAP_UNAVAILABLE_FAILURE_CODE]: guidance("The Gateway owner bootstrap store is unavailable; retry the exchange.", "retry", 503, true),
|
|
2856
|
+
context_selection_ambiguous: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
|
|
2857
|
+
session_authority_unverified: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
|
|
2858
|
+
session_authority_mismatch: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 409),
|
|
2859
|
+
local_credential_lost: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 401),
|
|
2860
|
+
local_capability_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
|
|
2861
|
+
host_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503),
|
|
2862
|
+
host_activation_failed: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 500),
|
|
2863
|
+
doctor_unavailable: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 503),
|
|
2864
|
+
admin_sign_in_invalid: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 502),
|
|
2865
|
+
// `invalid_request` renders through the client default today (local encode/path faults); Gateway-answered
|
|
2866
|
+
// envelopes carry the Gateway message verbatim and never read this sentence.
|
|
2867
|
+
invalid_request: guidance(LOCAL_TRANSPORT_INCOMPLETE, "answer_with_gap", 400),
|
|
2868
|
+
// Gateway canonical-route codes, sentences moved from their minting adapters; statuses moved from the
|
|
2869
|
+
// `valid*Failure` decoder maps in `cli/internal/client/gateway_api.go` and `transport.go`.
|
|
2870
|
+
invalid_json: guidance("The Gateway request body must be valid JSON.", "answer_with_gap", 400),
|
|
2871
|
+
// Per-route variants name their route; the table holds the one stable guidance and the route's `allow`
|
|
2872
|
+
// header stays the measured fact (decided with the operator 2026-10-13).
|
|
2873
|
+
method_not_allowed: guidance("The HTTP method is not allowed for this Gateway route.", "answer_with_gap", 405),
|
|
2874
|
+
request_target_too_large: guidance("The Gateway request target exceeds its bounded limit.", "reduce_request_size", 414),
|
|
2875
|
+
route_not_found: guidance("The Gateway route is not present.", "answer_with_gap", 404),
|
|
2876
|
+
artifact_unavailable: guidance("The Gateway artifact is unavailable.", "answer_with_gap", 503),
|
|
2877
|
+
discovery_unavailable: guidance("The Gateway discovery manifest is unavailable.", "answer_with_gap", 503),
|
|
2878
|
+
serialization_failed: guidance("The Gateway response could not be serialized.", "answer_with_gap", 500),
|
|
2879
|
+
request_header_too_large: guidance("A consumed Gateway request header exceeds its bounded limit.", "answer_with_gap", 431),
|
|
2880
|
+
request_work_context_missing: guidance("The Gateway command requires a Request work context.", "answer_with_gap", 400),
|
|
2881
|
+
request_work_context_invalid: guidance("The Gateway Request work context encoding is invalid.", "answer_with_gap", 400),
|
|
2882
|
+
request_work_context_refused: guidance("The Host-only discovery manifest takes no Request work context.", "answer_with_gap", 400),
|
|
2883
|
+
// Moved from `gateway/src/operations/http-composition.ts` (`unauthorized`) and `command-runtime.ts:827`
|
|
2884
|
+
// (`artifact_stale`); both travel with Gateway messages verbatim online, so these sentences are the
|
|
2885
|
+
// owner fallback guidance. `unauthorized` re-authenticates through the session, hence its action.
|
|
2886
|
+
unauthorized: guidance("Unauthorized.", "repair_or_adopt_session", 401),
|
|
2887
|
+
artifact_stale: guidance("The requested contract artifact is not the current catalog artifact.", "answer_with_gap", 409),
|
|
2888
|
+
// Host mint classifications. The CLI emits `invocation_authentication_unavailable` with the classification
|
|
2889
|
+
// in its detail fact; rows without their own sentence share that code's moved sentence, while `next_action`
|
|
2890
|
+
// is moved from `canonicalHostAuthenticationNextActions` (generated from `CEAL_HOST_AUTHENTICATION_POLICY`)
|
|
2891
|
+
// and `retryable` from `hostAuthenticationFailureForCause` (`failure.nextAction == "retry"`). The three rows
|
|
2892
|
+
// with their own sentences carry the retired policy remedies as Gateway-owned guidance. Statuses are the
|
|
2893
|
+
// Gateway-owned mapping decided with the operator 2026-10-13. This covers every wire cause the maps at
|
|
2894
|
+
// `host/invocation-authentication-command.ts:217-242` emit, through their policy classifications.
|
|
2895
|
+
host_arguments_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 400),
|
|
2896
|
+
host_configuration_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500),
|
|
2897
|
+
host_session_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 401),
|
|
2898
|
+
// Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
|
|
2899
|
+
// missing exact-turn fact names the fresh Host turn, not a reinstall.
|
|
2900
|
+
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),
|
|
2901
|
+
host_invocation_context_stale: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 401, true),
|
|
2902
|
+
host_invocation_context_expired: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 401, true),
|
|
2903
|
+
host_source_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 403),
|
|
2904
|
+
host_registration_credential_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "ask_admin", 403),
|
|
2905
|
+
host_mint_gateway_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "ask_admin", 503),
|
|
2906
|
+
host_mint_gateway_rate_limited: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 429),
|
|
2907
|
+
host_mint_gateway_refused: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
|
|
2908
|
+
host_mint_gateway_response_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
|
|
2909
|
+
// Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
|
|
2910
|
+
// producer-frame fault names the administrator repair step.
|
|
2911
|
+
host_producer_frame_invalid: guidance("Ask an administrator to inspect and repair the producer frame, then start a fresh Host turn.", "ask_admin", 400),
|
|
2912
|
+
// Moved from the retired `CEAL_HOST_AUTHENTICATION_POLICY` remedy: the
|
|
2913
|
+
// sandboxed Relay socket names the run-alone retry.
|
|
2914
|
+
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),
|
|
2915
|
+
host_relay_unavailable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 503),
|
|
2916
|
+
host_mint_transient_retryable: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "retry", 503, true),
|
|
2917
|
+
host_mint_failed: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500),
|
|
2918
|
+
host_relay_failed: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 502),
|
|
2919
|
+
host_refusal_invalid: guidance(HOST_AUTHENTICATION_UNAVAILABLE, "answer_with_gap", 500)
|
|
2920
|
+
});
|
|
2921
|
+
function cealFailureGuidanceForCode(code) {
|
|
2922
|
+
const entry = CEAL_FAILURE_GUIDANCE_TABLE[code];
|
|
2923
|
+
if (entry === void 0) throw new TypeError(`Failure guidance code is not guided: ${code}.`);
|
|
2924
|
+
return entry;
|
|
2925
|
+
}
|
|
2926
|
+
var FAILURE_GUIDANCE_ENTRY_KEYS = Object.freeze(["message", "next_action", "http_status", "retryable", "redaction_class"]);
|
|
2841
2927
|
|
|
2842
|
-
// packages/ceal-client-protocol/src/
|
|
2843
|
-
var
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
"
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
"
|
|
2859
|
-
"
|
|
2928
|
+
// packages/ceal-client-protocol/src/client-wire-contract.ts
|
|
2929
|
+
var CEAL_CLIENT_WIRE_CONTRACT = Object.freeze(client_wire_contract_default);
|
|
2930
|
+
var CEAL_CLIENT_DOCTOR_SCHEMA_VERSION = CEAL_CLIENT_WIRE_CONTRACT.doctor.schema_version;
|
|
2931
|
+
var clientCommands = CEAL_CLIENT_WIRE_CONTRACT.client_commands;
|
|
2932
|
+
var CEAL_CLIENT_DOCTOR_COMMAND = Object.freeze([...clientCommands.doctor]);
|
|
2933
|
+
var CEAL_CLIENT_VERSION_COMMAND = Object.freeze([...clientCommands.version]);
|
|
2934
|
+
var CEAL_CLIENT_ACCESS_GROUP_LIST_COMMAND = Object.freeze([...clientCommands.access_group_list]);
|
|
2935
|
+
function deriveCealClientWireSchemaVersion(schema) {
|
|
2936
|
+
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))) {
|
|
2937
|
+
throw new TypeError("Client wire schema name or field set is invalid.");
|
|
2938
|
+
}
|
|
2939
|
+
const fields = schema.fields.filter((field) => field !== "schema_version").sort();
|
|
2940
|
+
const hash8 = createHash("sha256").update(cealCanonicalJson(fields), "utf8").digest("hex").slice(0, 8);
|
|
2941
|
+
return `${schema.schema_name}.v1.${hash8}`;
|
|
2942
|
+
}
|
|
2943
|
+
var CealClientWireContractMismatchError = class extends Error {
|
|
2944
|
+
code = "contract_mismatch";
|
|
2945
|
+
next_action = cealFailureGuidanceForCode("contract_mismatch").next_action;
|
|
2946
|
+
constructor(expected, actual) {
|
|
2947
|
+
super(`The peer wire contract ${actual} does not match this client contract ${expected}.`);
|
|
2948
|
+
this.name = "CealClientWireContractMismatchError";
|
|
2949
|
+
}
|
|
2950
|
+
};
|
|
2951
|
+
function requireCealClientWireSchemaVersion(value, expected) {
|
|
2952
|
+
if (typeof value === "string" && value !== expected) throw new CealClientWireContractMismatchError(expected, value);
|
|
2953
|
+
}
|
|
2954
|
+
var commandResolution = CEAL_CLIENT_WIRE_CONTRACT.command_resolution;
|
|
2955
|
+
var commandResolutionAuthority = commandResolution.authority;
|
|
2956
|
+
var commandResolutionSemanticOptions = commandResolution.semantic_options;
|
|
2957
|
+
var CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(commandResolution);
|
|
2958
|
+
var CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES = CEAL_CLIENT_WIRE_CONTRACT.encoded_json_document.max_bytes;
|
|
2959
|
+
var artifact = CEAL_CLIENT_WIRE_CONTRACT.artifact_descriptor;
|
|
2960
|
+
var request = CEAL_CLIENT_WIRE_CONTRACT.command_request;
|
|
2961
|
+
var response = CEAL_CLIENT_WIRE_CONTRACT.command_response;
|
|
2962
|
+
var CEAL_OPERATION_CONTRACT_BUNDLE_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: artifact.operation_contract_bundle_schema_name, fields: artifact.operation_contract_bundle_fields });
|
|
2963
|
+
var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(artifact);
|
|
2964
|
+
var CEAL_VERIFIED_ARTIFACT_BINDING_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: artifact.binding_schema_name, fields: artifact.binding_fields });
|
|
2965
|
+
var CEAL_VERIFIED_ARTIFACT_KIND = artifact.kind;
|
|
2966
|
+
var CEAL_VERIFIED_ARTIFACT_BINDING_KIND = artifact.binding_kind;
|
|
2967
|
+
var CEAL_VERIFIED_ARTIFACT_MEDIA_TYPE = artifact.media_type;
|
|
2968
|
+
var CEAL_VERIFIED_ARTIFACT_DIGEST_LENGTH = artifact.digest.length;
|
|
2969
|
+
var CEAL_VERIFIED_ARTIFACT_DIGEST_PATTERN = artifact.digest.pattern;
|
|
2970
|
+
var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_LENGTH = artifact.catalog_revision.length;
|
|
2971
|
+
var CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_PATTERN = artifact.catalog_revision.pattern;
|
|
2972
|
+
var CEAL_VERIFIED_ARTIFACT_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
|
|
2973
|
+
var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_MAX_BYTES = artifact.envelope_max_bytes;
|
|
2974
|
+
var CEAL_COMMAND_REQUEST_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(request);
|
|
2975
|
+
var CEAL_COMMAND_REQUEST_MAX_BYTES = request.absolute_transport_max_bytes;
|
|
2976
|
+
var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_MAX_BYTES = response.bare_document_max_bytes;
|
|
2977
|
+
var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_OPTIONAL_FIELDS = Object.freeze([...response.bare_document_optional_fields]);
|
|
2978
|
+
var CEAL_COMMAND_RESPONSE_DISCOVERY_COMMAND = response.discovery_command;
|
|
2979
|
+
var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_SCHEMA_VERSION = deriveCealClientWireSchemaVersion({ schema_name: response.discovery_manifest_schema_name, fields: response.discovery_manifest_fields });
|
|
2980
|
+
var CEAL_COMMAND_RESPONSE_SCHEMA_VERSION = deriveCealClientWireSchemaVersion(response);
|
|
2981
|
+
var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_DOCUMENT_FIELDS = Object.freeze([...response.discovery_manifest_document_fields]);
|
|
2982
|
+
var CEAL_COMMAND_RESPONSE_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
|
|
2983
|
+
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 });
|
|
2984
|
+
var CEAL_RESOURCE_STREAM_REQUEST_FIELDS = Object.freeze([...CEAL_CLIENT_WIRE_CONTRACT.resource_stream.request_fields]);
|
|
2985
|
+
|
|
2986
|
+
// packages/ceal-client-protocol/src/operation-withheld-cause.ts
|
|
2987
|
+
var CEAL_OPERATION_WITHHELD_CAUSES = vocabulary(["provider_permission", "access_level", "personal_authorization_required"]);
|
|
2988
|
+
|
|
2989
|
+
// packages/ceal-client-protocol/src/operation-contract-types.ts
|
|
2990
|
+
var CEAL_JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
|
|
2991
|
+
var CEAL_OPERATION_DESCRIPTOR_SCHEMA_VERSION = "ceal.operation_descriptor.v1";
|
|
2992
|
+
var CEAL_OPERATION_ERROR_SCHEMA_VERSION = "ceal.operation_error.v1";
|
|
2993
|
+
var CEAL_ARTIFACT_STAGE_TERMINALS = vocabulary(["chunk_accepted", "artifact_ready", "idempotency_replayed"]);
|
|
2994
|
+
var CEAL_RETRY_AFTER_MAX_MS = 60 * 60 * 1e3;
|
|
2995
|
+
var CEAL_JSON_SCHEMA_TYPES = vocabulary(["object", "array", "string", "integer", "number", "boolean", "null"]);
|
|
2996
|
+
var OBSERVATION_POSTURES = vocabulary(["direct_readback", "approved_no_readback", "operator_mediated", "excluded"]);
|
|
2997
|
+
var EFFECT_DOMAINS = vocabulary(["none", "gateway_state", "provider", "external_world"]);
|
|
2998
|
+
var SAFETY_CLASSES = vocabulary(["routine", "sensitive", "high_impact"]);
|
|
2999
|
+
var IDEMPOTENCY_CLASSES = vocabulary(["idempotent", "conditionally_idempotent", "non_idempotent"]);
|
|
3000
|
+
var REVERSIBILITY_CLASSES = vocabulary(["none", "reversible", "compensatable", "irreversible"]);
|
|
3001
|
+
var COMPENSATION_OWNERS = vocabulary(["none", "connector", "operator"]);
|
|
3002
|
+
var CANCELLATION_BOUNDARIES = vocabulary(["before_admission", "before_handoff", "after_handoff_reconcile"]);
|
|
3003
|
+
var HANDOFF_CLASSES = vocabulary(["not_applicable", "submission", "effect"]);
|
|
3004
|
+
var AUTHORITATIVE_READBACK_CLASSES = vocabulary(["required", "unavailable"]);
|
|
3005
|
+
var PROVIDER_READBACK_CLASSES = vocabulary(["required", "not_available", "not_applicable"]);
|
|
3006
|
+
var RECONCILIATION_CLASSES = vocabulary(["none", "automatic", "operator"]);
|
|
3007
|
+
var NO_READBACK_APPROVAL_CLASSES = vocabulary(["not_applicable", "explicit", "operator"]);
|
|
3008
|
+
var REPLAY_PREREQUISITES = vocabulary(["never", "fresh_admission", "reconciled_non_application", "operator_approval"]);
|
|
3009
|
+
var UNKNOWN_OUTCOMES = vocabulary(["reconcile", "operator_action", "excluded"]);
|
|
3010
|
+
var RECOVERY_OWNERS = vocabulary(["gateway", "connector", "operator"]);
|
|
3011
|
+
var CEAL_OPERATION_ADMISSION_STATES = vocabulary(["proposed", "refused", "admitted"]);
|
|
3012
|
+
var CEAL_OPERATION_EXECUTION_STATES = vocabulary(["not_started", "started", "completed"]);
|
|
3013
|
+
var CEAL_OPERATION_PROVIDER_HANDOFF_STATES = vocabulary(["not_offered", "offered", "accepted", "rejected", "unknown"]);
|
|
3014
|
+
var CEAL_OPERATION_EFFECT_STATES = vocabulary(["none", "applied", "failed", "unknown", "compensated"]);
|
|
3015
|
+
var CEAL_OPERATION_OBSERVATION_STATES = vocabulary(["not_started", "not_required", "pending", "observed", "unavailable"]);
|
|
3016
|
+
var CEAL_OPERATION_RECOVERY_KINDS = vocabulary(["none", "reconcile", "compensate", "operator_action"]);
|
|
3017
|
+
var CEAL_OPERATION_RECOVERY_STATUSES = vocabulary(["not_started", "active", "completed"]);
|
|
3018
|
+
var CEAL_OPERATION_PROVIDER_READBACK_AVAILABILITY = vocabulary(["not_started", "not_applicable", "not_available", "pending", "available"]);
|
|
3019
|
+
var CEAL_OPERATION_OFFERED_HANDOFF_STATES = vocabulary(["accepted", "rejected", "unknown"]);
|
|
3020
|
+
var CEAL_OPERATION_RECEIPT_AUTHORITY_KEYS = Object.freeze(["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]);
|
|
3021
|
+
var CEAL_OPERATION_APPROVAL_DISPOSITIONS = vocabulary([
|
|
3022
|
+
"approval_not_required_by_policy",
|
|
3023
|
+
"interactive_pending",
|
|
3024
|
+
"interactive_granted",
|
|
3025
|
+
"interactive_denied",
|
|
3026
|
+
"interactive_expired",
|
|
3027
|
+
"interactive_unavailable"
|
|
3028
|
+
]);
|
|
3029
|
+
|
|
3030
|
+
// packages/ceal-client-protocol/src/operation-access-class.ts
|
|
3031
|
+
var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
|
|
3032
|
+
|
|
3033
|
+
// packages/ceal-client-protocol/src/domain-vocabulary.ts
|
|
3034
|
+
var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
|
|
3035
|
+
var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
|
|
3036
|
+
var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
|
|
3037
|
+
var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
|
|
3038
|
+
var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
|
|
3039
|
+
var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
|
|
3040
|
+
var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
|
|
3041
|
+
var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
|
|
3042
|
+
var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
|
|
3043
|
+
var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
|
|
3044
|
+
var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
|
|
3045
|
+
var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
|
|
3046
|
+
|
|
3047
|
+
// packages/ceal-client-protocol/src/safe-json-budget.ts
|
|
3048
|
+
var SAFE_JSON_MIN_BYTES_PER_NODE = 4;
|
|
3049
|
+
function safeJsonNodeBudgetForBytes(maxBytes) {
|
|
3050
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < SAFE_JSON_MIN_BYTES_PER_NODE) {
|
|
3051
|
+
throw new RangeError(`safe-JSON byte cap is invalid: ${refusalFact("maxBytes", maxBytes, `at least ${SAFE_JSON_MIN_BYTES_PER_NODE} bytes`)}`);
|
|
3052
|
+
}
|
|
3053
|
+
return Math.floor(maxBytes / SAFE_JSON_MIN_BYTES_PER_NODE);
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
// packages/ceal-client-protocol/src/protocol-bounds.ts
|
|
3057
|
+
var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES = 64 * 1024;
|
|
3058
|
+
var CEAL_PROTOCOL_RESPONSE_VALUE_MAX_NODES = safeJsonNodeBudgetForBytes(CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES);
|
|
3059
|
+
var OPERATION_SCHEMA_LITERAL_MAX_BYTES = CEAL_PROTOCOL_RESPONSE_VALUE_MAX_BYTES / 4;
|
|
3060
|
+
var CEAL_OPERATION_JSON_SCHEMA_BUDGET = Object.freeze({
|
|
3061
|
+
max_depth: 8,
|
|
3062
|
+
max_properties: 96,
|
|
3063
|
+
max_string_length: OPERATION_SCHEMA_LITERAL_MAX_BYTES / 4,
|
|
3064
|
+
max_alternatives: 8,
|
|
3065
|
+
max_enum_values: 64,
|
|
3066
|
+
max_literal_bytes: OPERATION_SCHEMA_LITERAL_MAX_BYTES,
|
|
3067
|
+
/** @basis our_policy -- bounds explanatory schema prose carried in one catalog. */
|
|
3068
|
+
max_description_characters: 1024,
|
|
3069
|
+
/** @basis our_policy -- bounds regular-expression compilation input from one schema. */
|
|
3070
|
+
max_pattern_characters: 256,
|
|
3071
|
+
/** @basis our_policy -- bounds traversal work inside one already byte-bounded schema literal. */
|
|
3072
|
+
max_literal_nodes: 512
|
|
3073
|
+
});
|
|
3074
|
+
|
|
3075
|
+
// packages/ceal-client-protocol/src/operation-json-schema.ts
|
|
3076
|
+
var JSON_DESCRIPTION_CONTROL_CHARACTER = new RegExp("\\p{Cc}", "u");
|
|
3077
|
+
|
|
3078
|
+
// packages/ceal-client-protocol/src/operation-contract-validation.ts
|
|
3079
|
+
var CEAL_OPERATION_AUTHORITY_POLICY_KEYS = Object.freeze(["access_group", "instance_binding", "target", "admission_recheck"]);
|
|
3080
|
+
var MAX_OPERATION_INPUT_RESOLVER_BINDINGS = 96;
|
|
3081
|
+
var CEAL_OPERATION_DESCRIPTOR_REQUIRED_ENVELOPE_KEYS = Object.freeze(["schema_version", "id", "input_contract", "result_contract", "policy"]);
|
|
3082
|
+
var CEAL_OPERATION_DESCRIPTOR_OPTIONAL_ENVELOPE_KEYS = Object.freeze(["input_resolution", "summary"]);
|
|
3083
|
+
|
|
3084
|
+
// packages/ceal-client-protocol/src/operation-catalog.ts
|
|
3085
|
+
var CEAL_OPERATION_CATALOG_SCHEMA_VERSION = "ceal.catalog.v3";
|
|
3086
|
+
var CLIENT_AUDIENCES = Object.freeze(["ceal", "cealctl", "agent"]);
|
|
3087
|
+
var CLIENT_AUDIENCE_SET = new Set(CLIENT_AUDIENCES);
|
|
3088
|
+
|
|
3089
|
+
// packages/ceal-client-protocol/src/schema-literals.ts
|
|
3090
|
+
var CEAL_SCHEMA_LITERALS = [
|
|
3091
|
+
"ceal.activation_transport.v1",
|
|
3092
|
+
CEAL_OPERATION_ACCESS_SCHEMA_VERSION,
|
|
3093
|
+
CEAL_OPERATION_CATALOG_SCHEMA_VERSION,
|
|
3094
|
+
"ceal.client_refresh_request.v1",
|
|
3095
|
+
"ceal.client_refresh_result.v1",
|
|
3096
|
+
"ceal.client_revoke_request.v1",
|
|
3097
|
+
"ceal.client_revoke_result.v1",
|
|
3098
|
+
CEAL_COMMAND_REQUEST_SCHEMA_VERSION,
|
|
3099
|
+
"ceal.device_enrollment_challenge.v1",
|
|
3100
|
+
"ceal.device_enrollment_challenge_request.v1",
|
|
3101
|
+
"ceal.device_enrollment_hpke_aad.v1",
|
|
3102
|
+
"ceal.device_enrollment_hpke_info.v1",
|
|
3103
|
+
"ceal.device_enrollment_poll.v1",
|
|
3104
|
+
"ceal.device_enrollment_poll_result.v1",
|
|
3105
|
+
"ceal.device_enrollment_proof.v1",
|
|
3106
|
+
"ceal.device_enrollment_start.v1",
|
|
2860
3107
|
"ceal.device_enrollment_start_result.v1",
|
|
2861
3108
|
"ceal.effect_approval.v1",
|
|
2862
3109
|
"ceal.effect_approval_confirmation.v1",
|
|
@@ -2866,12 +3113,12 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
2866
3113
|
"ceal.enrollment_result.v1",
|
|
2867
3114
|
"ceal.first_activation_causal_join.v1",
|
|
2868
3115
|
"ceal.gateway_announcement_policy.v1",
|
|
2869
|
-
|
|
3116
|
+
CEAL_GATEWAY_AUDIT_CALL_DETAIL_SCHEMA_VERSION,
|
|
2870
3117
|
"ceal.gateway_audit_event.v1",
|
|
2871
3118
|
"ceal.gateway_audit_readback.v1",
|
|
2872
3119
|
"ceal.gateway_authorization_snapshot.v1",
|
|
2873
3120
|
"ceal.gateway_cache_origin.v1",
|
|
2874
|
-
|
|
3121
|
+
CEAL_GATEWAY_CALL_RESULT_SCHEMA_VERSION,
|
|
2875
3122
|
CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION,
|
|
2876
3123
|
"ceal.gateway_connector_route_failure.v1",
|
|
2877
3124
|
"ceal.gateway_discovery.v1",
|
|
@@ -2896,7 +3143,7 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
2896
3143
|
"ceal.host_observation.v1",
|
|
2897
3144
|
"ceal.host_registration_request.v1",
|
|
2898
3145
|
"ceal.host_registration_result.v1",
|
|
2899
|
-
|
|
3146
|
+
CEAL_OPERATION_DESCRIPTOR_SCHEMA_VERSION,
|
|
2900
3147
|
"ceal.operation_error.v1",
|
|
2901
3148
|
"ceal.operation_failure.v1",
|
|
2902
3149
|
"ceal.operation_failure.compact.v1",
|
|
@@ -3004,11 +3251,22 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
|
|
|
3004
3251
|
var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "block", "object"]);
|
|
3005
3252
|
var CEAL_OPAQUE_REF_KINDS = vocabulary([...CEAL_OPAQUE_HANDLE_KINDS, "result", "operation-receipt", "reply-receipt", "field", "notion-data-source"]);
|
|
3006
3253
|
|
|
3254
|
+
// packages/ceal-client-protocol/src/client-environment-generated.ts
|
|
3255
|
+
var CEAL_CLIENT_CONTEXT_OVERRIDE_ENVIRONMENTS = Object.freeze(["CEAL_GATEWAY_ORIGIN", "CEAL_GATEWAY_TRANSPORT_ORIGIN", "CEAL_ACCESS_TOKEN", "CEAL_CONTEXT_STORE_PATH"]);
|
|
3256
|
+
|
|
3257
|
+
// packages/ceal-client-protocol/src/client-process-contract.ts
|
|
3258
|
+
var CEAL_CLIENT_VERSION_SCHEMA_SUFFIX = ".version.v1";
|
|
3259
|
+
var CEAL_CLIENT_VERSION_SCHEMA_VERSION = `ceal${CEAL_CLIENT_VERSION_SCHEMA_SUFFIX}`;
|
|
3260
|
+
|
|
3007
3261
|
// packages/ceal-client-protocol/src/bare-document.ts
|
|
3008
3262
|
var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_fields;
|
|
3009
3263
|
var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
|
|
3010
3264
|
|
|
3265
|
+
// packages/ceal-client-protocol/src/actor-kind.ts
|
|
3266
|
+
var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
|
|
3267
|
+
|
|
3011
3268
|
// packages/ceal-client-protocol/src/resource-row.ts
|
|
3269
|
+
var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
|
|
3012
3270
|
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
3013
3271
|
var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
|
|
3014
3272
|
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"]);
|
|
@@ -3021,6 +3279,8 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
|
3021
3279
|
"resource_created",
|
|
3022
3280
|
"review_submitted",
|
|
3023
3281
|
"comment_created",
|
|
3282
|
+
"reaction_created",
|
|
3283
|
+
"ceal_write_completed",
|
|
3024
3284
|
"event_start",
|
|
3025
3285
|
"all_day_start",
|
|
3026
3286
|
"file_created",
|
|
@@ -3055,6 +3315,10 @@ var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
|
3055
3315
|
"github.review.submitted",
|
|
3056
3316
|
"github.issue_comment.created",
|
|
3057
3317
|
"github.review_comment.created",
|
|
3318
|
+
"github.reaction.created",
|
|
3319
|
+
"github.reaction.deleted",
|
|
3320
|
+
"github.pull_request.review_requested",
|
|
3321
|
+
"github.pull_request.review_request_removed",
|
|
3058
3322
|
"calendar.organized",
|
|
3059
3323
|
"calendar.accepted_invite",
|
|
3060
3324
|
"calendar.recorded",
|
|
@@ -3076,6 +3340,10 @@ var CEAL_ACTIVITY_EVENT_TIMESTAMP_BASIS_MAP = Object.freeze({
|
|
|
3076
3340
|
"github.pull_request.opened": "resource_created",
|
|
3077
3341
|
"github.issue.edited": "resource_modified",
|
|
3078
3342
|
"github.pull_request.edited": "resource_modified",
|
|
3343
|
+
"github.reaction.created": "reaction_created",
|
|
3344
|
+
"github.reaction.deleted": "ceal_write_completed",
|
|
3345
|
+
"github.pull_request.review_requested": "ceal_write_completed",
|
|
3346
|
+
"github.pull_request.review_request_removed": "ceal_write_completed",
|
|
3079
3347
|
"calendar.organized": "resource_created",
|
|
3080
3348
|
"drive.file.created": "file_created",
|
|
3081
3349
|
"drive.file.modified": "file_modified_by_me",
|
|
@@ -3092,12 +3360,14 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
3092
3360
|
next: { required: true, timestamp: false },
|
|
3093
3361
|
kind: { required: true, timestamp: false },
|
|
3094
3362
|
matched_kinds: { required: false, timestamp: false },
|
|
3363
|
+
title_match: { required: false, timestamp: false },
|
|
3095
3364
|
title: { required: true, timestamp: false },
|
|
3096
3365
|
container_label: { required: true, timestamp: false },
|
|
3097
3366
|
container_ref: { required: false, timestamp: false },
|
|
3098
3367
|
source_url: { required: false, timestamp: false },
|
|
3099
3368
|
source: { required: true, timestamp: false },
|
|
3100
3369
|
owner: { required: false, timestamp: false },
|
|
3370
|
+
actor_kind: { required: false, timestamp: false },
|
|
3101
3371
|
counterparts: { required: false, timestamp: false },
|
|
3102
3372
|
attachments: { required: false, timestamp: false },
|
|
3103
3373
|
created_at: { required: false, timestamp: true },
|
|
@@ -3105,9 +3375,18 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
3105
3375
|
modified_at: { required: false, timestamp: true },
|
|
3106
3376
|
modified_at_local: { required: false, timestamp: true },
|
|
3107
3377
|
viewed_by_me_at: { required: false, timestamp: true },
|
|
3378
|
+
modified_by_me_at: { required: false, timestamp: true },
|
|
3108
3379
|
own_ceal_write: { required: false, timestamp: false },
|
|
3109
3380
|
owned_by_me: { required: false, timestamp: false },
|
|
3381
|
+
mime_type: { required: false, timestamp: false },
|
|
3382
|
+
size_bytes: { required: false, timestamp: false },
|
|
3383
|
+
version: { required: false, timestamp: false },
|
|
3384
|
+
trashed: { required: false, timestamp: false },
|
|
3110
3385
|
visibility: { required: false, timestamp: false },
|
|
3386
|
+
archived: { required: false, timestamp: false },
|
|
3387
|
+
tag: { required: false, timestamp: false },
|
|
3388
|
+
labels: { required: false, timestamp: false },
|
|
3389
|
+
labels_incomplete: { required: false, timestamp: false },
|
|
3111
3390
|
start: { required: false, timestamp: false },
|
|
3112
3391
|
end: { required: false, timestamp: false },
|
|
3113
3392
|
status: { required: false, timestamp: false },
|
|
@@ -3180,18 +3459,29 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
|
3180
3459
|
// The serving gate answers this while the Gateway is still starting and the
|
|
3181
3460
|
// refresh was never attempted; the same attempt stays retryable. It is never
|
|
3182
3461
|
// stored by the session owner, only answered at the serving edge.
|
|
3183
|
-
|
|
3462
|
+
CEAL_GATEWAY_STARTING_FAILURE_CODE
|
|
3463
|
+
]);
|
|
3464
|
+
var CEAL_CLIENT_REVOKE_FAILURE_CODES = vocabulary([
|
|
3465
|
+
"refresh_invalid",
|
|
3466
|
+
"refresh_expired",
|
|
3467
|
+
"refresh_replayed",
|
|
3468
|
+
"refresh_revoked"
|
|
3184
3469
|
]);
|
|
3470
|
+
var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX = "ceal_personal_";
|
|
3471
|
+
var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX = "ceal_refresh_";
|
|
3472
|
+
var PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN = "[A-Za-z0-9_-]{43}";
|
|
3473
|
+
var CEAL_PERSONAL_CLIENT_ACCESS_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_ACCESS_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
|
|
3474
|
+
var CEAL_PERSONAL_CLIENT_REFRESH_TOKEN = new RegExp(`^${CEAL_PERSONAL_CLIENT_REFRESH_TOKEN_PREFIX}${PERSONAL_CLIENT_TOKEN_SUFFIX_PATTERN}$`, "u");
|
|
3185
3475
|
|
|
3186
3476
|
// packages/ceal-client-protocol/src/request-validation-primitives.ts
|
|
3187
|
-
import { createHash } from "node:crypto";
|
|
3477
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3188
3478
|
var DIGEST = /^[a-f0-9]{64}$/u;
|
|
3189
3479
|
var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
|
|
3190
3480
|
function isCealDigest(value) {
|
|
3191
3481
|
return typeof value === "string" && DIGEST.test(value);
|
|
3192
3482
|
}
|
|
3193
3483
|
function cealRequestSha256(value) {
|
|
3194
|
-
return
|
|
3484
|
+
return createHash2("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
|
|
3195
3485
|
}
|
|
3196
3486
|
function operationRecord(value, message) {
|
|
3197
3487
|
requireOperationRecord(value, message);
|
|
@@ -3207,40 +3497,71 @@ function requirePrefixedRef(value, prefix, message) {
|
|
|
3207
3497
|
// packages/ceal-client-protocol/src/request-authority.ts
|
|
3208
3498
|
var CEAL_REQUEST_SCHEMA_VERSION = "ceal.request.v1";
|
|
3209
3499
|
var CEAL_REQUEST_ARTIFACT_OWNERS = vocabulary(["gateway", "host", "connector"]);
|
|
3210
|
-
var
|
|
3500
|
+
var CEAL_HOST_INVOCATION_IDENTITY_KEYS = vocabulary(["host_session_ref", "host_turn_ref", "host_source_ref"]);
|
|
3211
3501
|
var CEAL_REQUEST_ARTIFACT_RETENTION_CLASSES = vocabulary(["request", "source", "result"]);
|
|
3212
3502
|
var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
|
|
3213
|
-
var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority"
|
|
3503
|
+
var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority"]);
|
|
3504
|
+
var CEAL_REQUEST_COMMON_PROPERTIES = Object.freeze({
|
|
3505
|
+
schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
|
|
3506
|
+
ceal_request_ref: { type: "string", minLength: 1, maxLength: 128, pattern: "^ceal-request:[A-Za-z0-9][A-Za-z0-9._:-]{0,114}$" },
|
|
3507
|
+
created_at: { type: "string", minLength: 1, maxLength: 64 },
|
|
3508
|
+
authority: {
|
|
3509
|
+
type: "object",
|
|
3510
|
+
maxProperties: 5,
|
|
3511
|
+
additionalProperties: false,
|
|
3512
|
+
properties: {
|
|
3513
|
+
instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3514
|
+
access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3515
|
+
principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3516
|
+
execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3517
|
+
authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
3518
|
+
},
|
|
3519
|
+
required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
|
|
3520
|
+
}
|
|
3521
|
+
});
|
|
3522
|
+
var CEAL_REQUEST_HOST_PROPERTIES = Object.freeze({
|
|
3523
|
+
prompt_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3524
|
+
host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3525
|
+
host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3526
|
+
host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3527
|
+
host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
3528
|
+
});
|
|
3529
|
+
var CEAL_REQUEST_CALLER_PROPERTIES = Object.freeze({
|
|
3530
|
+
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." },
|
|
3531
|
+
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." }
|
|
3532
|
+
});
|
|
3533
|
+
var CEAL_REQUEST_OPTIONAL_PROPERTIES = Object.freeze({
|
|
3534
|
+
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
|
|
3535
|
+
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." }
|
|
3536
|
+
});
|
|
3537
|
+
var CEAL_REQUEST_HOST_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, ...CEAL_HOST_INVOCATION_IDENTITY_KEYS, "prompt_ref"]);
|
|
3538
|
+
var CEAL_REQUEST_CALLER_REQUIRED_KEYS = Object.freeze([...CEAL_REQUEST_KEYS, "caller_call_group_ref", "caller_call_input_sha256"]);
|
|
3214
3539
|
var CEAL_REQUEST_JSON_SCHEMA = {
|
|
3215
3540
|
type: "object",
|
|
3216
|
-
|
|
3541
|
+
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.",
|
|
3542
|
+
maxProperties: 13,
|
|
3217
3543
|
additionalProperties: false,
|
|
3218
3544
|
properties: {
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3545
|
+
...CEAL_REQUEST_COMMON_PROPERTIES,
|
|
3546
|
+
...CEAL_REQUEST_HOST_PROPERTIES,
|
|
3547
|
+
...CEAL_REQUEST_CALLER_PROPERTIES,
|
|
3548
|
+
...CEAL_REQUEST_OPTIONAL_PROPERTIES
|
|
3549
|
+
},
|
|
3550
|
+
required: CEAL_REQUEST_KEYS,
|
|
3551
|
+
anyOf: [
|
|
3552
|
+
{
|
|
3223
3553
|
type: "object",
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3228
|
-
access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3229
|
-
principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3230
|
-
execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3231
|
-
authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
3232
|
-
},
|
|
3233
|
-
required: ["instance_ref", "access_group_ref", "principal_ref", "execution_subject_ref", "authority_revision_ref"]
|
|
3554
|
+
required: CEAL_REQUEST_HOST_REQUIRED_KEYS,
|
|
3555
|
+
properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_HOST_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
|
|
3556
|
+
additionalProperties: false
|
|
3234
3557
|
},
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
},
|
|
3243
|
-
required: CEAL_REQUEST_KEYS
|
|
3558
|
+
{
|
|
3559
|
+
type: "object",
|
|
3560
|
+
required: CEAL_REQUEST_CALLER_REQUIRED_KEYS,
|
|
3561
|
+
properties: { ...CEAL_REQUEST_COMMON_PROPERTIES, ...CEAL_REQUEST_CALLER_PROPERTIES, ...CEAL_REQUEST_OPTIONAL_PROPERTIES },
|
|
3562
|
+
additionalProperties: false
|
|
3563
|
+
}
|
|
3564
|
+
]
|
|
3244
3565
|
};
|
|
3245
3566
|
|
|
3246
3567
|
// packages/ceal-client-protocol/src/request-operands.ts
|
|
@@ -3351,10 +3672,6 @@ var CEAL_REQUEST_RUN_BATCH_JSON_SCHEMA = {
|
|
|
3351
3672
|
};
|
|
3352
3673
|
|
|
3353
3674
|
// packages/ceal-client-protocol/src/host-observation.ts
|
|
3354
|
-
var CEAL_HOST_INPUT_KINDS = vocabulary(["human", "host_automatic"]);
|
|
3355
|
-
function isCealHostInputKind(value) {
|
|
3356
|
-
return typeof value === "string" && CEAL_HOST_INPUT_KINDS.some((kind) => kind === value);
|
|
3357
|
-
}
|
|
3358
3675
|
var CEAL_HOST_OBSERVATION_REF_PREFIX = "host-observation:";
|
|
3359
3676
|
var HOST_OBSERVATION_PRODUCER = /^[a-z][a-z0-9]{0,31}$/u;
|
|
3360
3677
|
var HOST_OBSERVATION_DIGEST = /^[a-f0-9]{8,64}$/u;
|
|
@@ -3400,14 +3717,14 @@ function safeCount(value) {
|
|
|
3400
3717
|
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
3401
3718
|
}
|
|
3402
3719
|
function validateCealHostObservationDraft(value) {
|
|
3403
|
-
const
|
|
3404
|
-
requirePrefixedRef(
|
|
3405
|
-
requireClosedValue(
|
|
3406
|
-
requireIsoTimestamp(
|
|
3407
|
-
assertHostObservationValue(
|
|
3720
|
+
const record2 = operationRecord(value, "Ceal Host observation draft is invalid.");
|
|
3721
|
+
requirePrefixedRef(record2.observation_ref, CEAL_HOST_OBSERVATION_REF_PREFIX, "Ceal Host observation reference is invalid.");
|
|
3722
|
+
requireClosedValue(record2.observation_kind, CEAL_HOST_OBSERVATION_KINDS, "Ceal Host observation kind is invalid.");
|
|
3723
|
+
requireIsoTimestamp(record2.observed_at, "Ceal Host observation time is invalid.");
|
|
3724
|
+
assertHostObservationValue(record2.value, record2.observation_kind);
|
|
3408
3725
|
}
|
|
3409
3726
|
var CEAL_REQUEST_MAX_OBSERVATION_VALUE_BYTES = 64 * 1024;
|
|
3410
|
-
var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "
|
|
3727
|
+
var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "timing"]);
|
|
3411
3728
|
function isCealHostPromptObservationValue(value) {
|
|
3412
3729
|
return isOperationRecord(value) && Object.keys(value).length === 1 && isCealDigest(value.body_sha256);
|
|
3413
3730
|
}
|
|
@@ -3530,17 +3847,128 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
|
|
|
3530
3847
|
};
|
|
3531
3848
|
|
|
3532
3849
|
// packages/ceal-client-protocol/src/host-invocation-authentication.ts
|
|
3533
|
-
var
|
|
3534
|
-
var
|
|
3535
|
-
var
|
|
3536
|
-
var
|
|
3537
|
-
var
|
|
3538
|
-
var
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3850
|
+
var hostInvocationAuthenticationContract = CEAL_CLIENT_WIRE_CONTRACT.host_invocation_authentication;
|
|
3851
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.request_schema_name, fields: hostInvocationAuthenticationContract.request_fields });
|
|
3852
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.request_fields]);
|
|
3853
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = deriveCealClientWireSchemaVersion({ schema_name: hostInvocationAuthenticationContract.result_schema_name, fields: hostInvocationAuthenticationContract.result_fields });
|
|
3854
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_FIELDS = Object.freeze([...hostInvocationAuthenticationContract.result_fields]);
|
|
3855
|
+
var CEAL_HOST_AUTHENTICATION_BUDGET_ENVIRONMENT = "CEAL_HOST_AUTHENTICATION_BUDGET_MS";
|
|
3856
|
+
var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE = "^(?:0|[1-9][0-9]{0,8})$";
|
|
3857
|
+
var CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN = new RegExp(CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN_SOURCE, "u");
|
|
3858
|
+
var CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA = "ceal.gateway_host_invocation_context_request.v2";
|
|
3859
|
+
var CEAL_HOST_INVOCATION_CONTEXT_REQUEST_MAX_BYTES = 16 * 1024;
|
|
3860
|
+
var CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES = 2 * 1024;
|
|
3861
|
+
var CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES = 32;
|
|
3862
|
+
var HOST_CONTEXT_EXPIRY_MAX_CHARS = 64;
|
|
3863
|
+
var CEAL_HOST_INVOCATION_CONTEXT_VISIBLE_OUTPUT_MAX_BYTES = CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
|
|
3864
|
+
var CEAL_HOST_CONTEXT_TOKEN_PREFIX = "ceal_context_";
|
|
3865
|
+
function boundCealHostInvocationHumanMessageWindow(newestFirst, sourceTruncated = false) {
|
|
3866
|
+
const messages = [];
|
|
3867
|
+
let truncated = sourceTruncated || newestFirst.length > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES;
|
|
3868
|
+
for (const candidate of newestFirst.slice(0, CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES)) {
|
|
3869
|
+
const next = [...messages, candidate];
|
|
3870
|
+
if (new TextEncoder().encode(JSON.stringify(next)).byteLength > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES) {
|
|
3871
|
+
truncated = true;
|
|
3872
|
+
break;
|
|
3873
|
+
}
|
|
3874
|
+
messages.push(Object.freeze({ host_turn_ref: candidate.host_turn_ref, host_input_ref: candidate.host_input_ref, text: candidate.text }));
|
|
3875
|
+
}
|
|
3876
|
+
return Object.freeze({ messages: Object.freeze(messages), truncated });
|
|
3877
|
+
}
|
|
3878
|
+
function decodeCealHostInvocationContextRequest(value) {
|
|
3879
|
+
if (!isCealJsonRecord(value)) throw invalidInvocationContextRequest();
|
|
3880
|
+
requireCealClientWireSchemaVersion(value.schema_version, CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA);
|
|
3881
|
+
if (!hasInvocationContextRequestKeys(value)) throw invalidInvocationContextRequest();
|
|
3882
|
+
const clientNonce = requireClientNonce(value.client_nonce);
|
|
3883
|
+
const expiresAt = requireContextExpiration(value.context_expires_at);
|
|
3884
|
+
const hostBinding = requireHostBinding(value.host_binding);
|
|
3885
|
+
const messageWindow = requireHumanMessageWindow(value.human_message_window);
|
|
3886
|
+
const lastRequestRef = optionalSafeReference(value.last_seen_request_ref);
|
|
3887
|
+
const visibleOutputMaxBytes = optionalVisibleOutputBudget(value.visible_output_max_bytes);
|
|
3888
|
+
return Object.freeze({
|
|
3889
|
+
schema_version: CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA,
|
|
3890
|
+
client_nonce: clientNonce,
|
|
3891
|
+
context_expires_at: expiresAt,
|
|
3892
|
+
host_binding: Object.freeze({ ...hostBinding }),
|
|
3893
|
+
human_message_window: messageWindow,
|
|
3894
|
+
...lastRequestRef === void 0 ? {} : { last_seen_request_ref: lastRequestRef },
|
|
3895
|
+
...visibleOutputMaxBytes === void 0 ? {} : { visible_output_max_bytes: visibleOutputMaxBytes }
|
|
3896
|
+
});
|
|
3897
|
+
}
|
|
3898
|
+
function hasInvocationContextRequestKeys(value) {
|
|
3899
|
+
const required = ["schema_version", "client_nonce", "context_expires_at", "host_binding", "human_message_window"];
|
|
3900
|
+
const optional = ["last_seen_request_ref", "visible_output_max_bytes"];
|
|
3901
|
+
const optionalKeys = Object.keys(value).filter((key) => optional.includes(key));
|
|
3902
|
+
return hasExactKeys(value, [...required, ...optionalKeys]) && required.every((key) => Object.hasOwn(value, key)) && value.schema_version === CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA;
|
|
3903
|
+
}
|
|
3904
|
+
function requireClientNonce(value) {
|
|
3905
|
+
if (typeof value !== "string" || !CEAL_HOST_CLIENT_NONCE_PATTERN.test(value)) throw invalidInvocationContextRequest();
|
|
3906
|
+
return value;
|
|
3907
|
+
}
|
|
3908
|
+
function requireContextExpiration(value) {
|
|
3909
|
+
if (typeof value !== "string" || value.length > HOST_CONTEXT_EXPIRY_MAX_CHARS || !Number.isFinite(Date.parse(value))) throw invalidInvocationContextRequest();
|
|
3910
|
+
return value;
|
|
3911
|
+
}
|
|
3912
|
+
function requireHostBinding(value) {
|
|
3913
|
+
if (!isCealJsonRecord(value) || !exactOptionalKeys(value, ["host_source_ref", "host_session_ref", "host_turn_ref"], []) || !safeReference(value.host_source_ref) || !safeReference(value.host_session_ref) || !safeReference(value.host_turn_ref)) throw invalidInvocationContextRequest();
|
|
3914
|
+
return { host_source_ref: value.host_source_ref, host_session_ref: value.host_session_ref, host_turn_ref: value.host_turn_ref };
|
|
3915
|
+
}
|
|
3916
|
+
function requireHumanMessageWindow(value) {
|
|
3917
|
+
if (!isCealJsonRecord(value) || !exactOptionalKeys(value, ["messages", "truncated"], []) || !Array.isArray(value.messages) || value.messages.length > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_MESSAGES || typeof value.truncated !== "boolean") throw invalidInvocationContextRequest();
|
|
3918
|
+
const messages = value.messages.map(decodeHumanMessage);
|
|
3919
|
+
const bytes = new TextEncoder().encode(JSON.stringify(messages)).byteLength;
|
|
3920
|
+
if (bytes > CEAL_HOST_INVOCATION_HUMAN_MESSAGE_WINDOW_MAX_BYTES) throw invalidInvocationContextRequest();
|
|
3921
|
+
return Object.freeze({ messages: Object.freeze(messages), truncated: value.truncated });
|
|
3922
|
+
}
|
|
3923
|
+
function decodeHumanMessage(candidate) {
|
|
3924
|
+
if (!isCealJsonRecord(candidate) || !exactOptionalKeys(candidate, ["host_turn_ref", "host_input_ref", "text"], []) || !safeReference(candidate.host_turn_ref) || !safeReference(candidate.host_input_ref) || typeof candidate.text !== "string") throw invalidInvocationContextRequest();
|
|
3925
|
+
return Object.freeze({ host_turn_ref: candidate.host_turn_ref, host_input_ref: candidate.host_input_ref, text: candidate.text });
|
|
3926
|
+
}
|
|
3927
|
+
function optionalSafeReference(value) {
|
|
3928
|
+
if (value === void 0) return void 0;
|
|
3929
|
+
if (!safeReference(value)) throw invalidInvocationContextRequest();
|
|
3930
|
+
return value;
|
|
3931
|
+
}
|
|
3932
|
+
function safeReference(value) {
|
|
3933
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
3934
|
+
}
|
|
3935
|
+
function exactOptionalKeys(value, required, optional = []) {
|
|
3936
|
+
const keys = Object.keys(value);
|
|
3937
|
+
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
3938
|
+
}
|
|
3939
|
+
function optionalVisibleOutputBudget(value) {
|
|
3940
|
+
if (value === void 0) return void 0;
|
|
3941
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > CEAL_HOST_INVOCATION_CONTEXT_VISIBLE_OUTPUT_MAX_BYTES) throw invalidInvocationContextRequest();
|
|
3942
|
+
return value;
|
|
3943
|
+
}
|
|
3944
|
+
function invalidInvocationContextRequest() {
|
|
3945
|
+
return new TypeError("The Host invocation context request is invalid.");
|
|
3946
|
+
}
|
|
3947
|
+
function deriveCealHostInvocationNonceContract(contract = CEAL_CLIENT_WIRE_CONTRACT) {
|
|
3948
|
+
const clientNonce = contract.host_invocation_authentication.client_nonce;
|
|
3949
|
+
return {
|
|
3950
|
+
encoded_characters: clientNonce.encoded_characters,
|
|
3951
|
+
client_nonce_pattern_source: `^[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`,
|
|
3952
|
+
context_token_pattern_source: `^${CEAL_HOST_CONTEXT_TOKEN_PREFIX}[${clientNonce.character_class_source}]{${clientNonce.encoded_characters}}$`
|
|
3953
|
+
};
|
|
3954
|
+
}
|
|
3955
|
+
var hostNonceContract = deriveCealHostInvocationNonceContract();
|
|
3956
|
+
var CEAL_HOST_NONCE_ENCODED_CHARACTERS = hostNonceContract.encoded_characters;
|
|
3957
|
+
var CEAL_HOST_CLIENT_NONCE_PATTERN = new RegExp(hostNonceContract.client_nonce_pattern_source, "u");
|
|
3958
|
+
var CEAL_HOST_CONTEXT_TOKEN_PATTERN = new RegExp(hostNonceContract.context_token_pattern_source, "u");
|
|
3959
|
+
var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = deriveCealHostInvocationAuthenticationResultMaxBytes();
|
|
3960
|
+
function deriveCealHostInvocationAuthenticationResultMaxBytes(contract = CEAL_CLIENT_WIRE_CONTRACT) {
|
|
3961
|
+
const hostAuthentication = contract.host_invocation_authentication;
|
|
3962
|
+
const values = {
|
|
3963
|
+
schema_version: deriveCealClientWireSchemaVersion({ schema_name: hostAuthentication.result_schema_name, fields: hostAuthentication.result_fields }),
|
|
3964
|
+
context_token: `${CEAL_HOST_CONTEXT_TOKEN_PREFIX}${"A".repeat(hostAuthentication.client_nonce.encoded_characters)}`,
|
|
3965
|
+
expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
|
|
3966
|
+
};
|
|
3967
|
+
const fields = hostAuthentication.result_fields;
|
|
3968
|
+
if (fields.some((field) => !Object.hasOwn(values, field))) throw new TypeError("Host invocation result fields have no bounded sample value.");
|
|
3969
|
+
return new TextEncoder().encode(`${JSON.stringify(Object.fromEntries(fields.map((field) => [field, values[field]])))}
|
|
3543
3970
|
`).byteLength;
|
|
3971
|
+
}
|
|
3544
3972
|
var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
|
|
3545
3973
|
"request_authority_unavailable",
|
|
3546
3974
|
"request_missing",
|
|
@@ -3564,6 +3992,7 @@ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
|
|
|
3564
3992
|
"host_credential_refused",
|
|
3565
3993
|
"gateway_unavailable",
|
|
3566
3994
|
"gateway_rate_limited",
|
|
3995
|
+
"contract_mismatch",
|
|
3567
3996
|
"gateway_refused",
|
|
3568
3997
|
"gateway_response_invalid",
|
|
3569
3998
|
"producer_frame_invalid",
|
|
@@ -3578,19 +4007,23 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
3578
4007
|
invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
|
|
3579
4008
|
options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
|
|
3580
4009
|
session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
3581
|
-
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "
|
|
3582
|
-
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "
|
|
3583
|
-
no_binding
|
|
3584
|
-
|
|
3585
|
-
|
|
4010
|
+
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "retry" }),
|
|
4011
|
+
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
|
|
4012
|
+
// `no_binding` now means the producer has no real current Host session. The
|
|
4013
|
+
// Host Relay never uses it for an uncreated Gateway Request; there is no
|
|
4014
|
+
// useful retry until the consumer is running inside a Host session.
|
|
4015
|
+
no_binding: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
4016
|
+
context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "retry" }),
|
|
4017
|
+
context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "retry" }),
|
|
3586
4018
|
host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
|
|
3587
4019
|
host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
|
|
3588
4020
|
gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
|
|
3589
4021
|
gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
|
|
4022
|
+
contract_mismatch: Object.freeze({ classification: "contract_mismatch", next_action: "upgrade_ceal_client" }),
|
|
3590
4023
|
gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
|
|
3591
4024
|
gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
|
|
3592
|
-
producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin"
|
|
3593
|
-
relay_permission_denied: Object.freeze({ classification: "host_relay_permission_denied", next_action: "rerun_outside_agent_sandbox"
|
|
4025
|
+
producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin" }),
|
|
4026
|
+
relay_permission_denied: Object.freeze({ classification: "host_relay_permission_denied", next_action: "rerun_outside_agent_sandbox" }),
|
|
3594
4027
|
relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
|
|
3595
4028
|
transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
|
|
3596
4029
|
mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
|
|
@@ -3600,21 +4033,33 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
3600
4033
|
function isCealHostAuthenticationCause(value) {
|
|
3601
4034
|
return isVocabularyWord(CEAL_HOST_AUTHENTICATION_CAUSES, value);
|
|
3602
4035
|
}
|
|
4036
|
+
function cealHostAuthenticationCauseForExchangeFailure(failure2) {
|
|
4037
|
+
if (failure2 === "permission_denied") return "relay_permission_denied";
|
|
4038
|
+
if (failure2 === "timed_out") return "transient_retryable";
|
|
4039
|
+
return "relay_unavailable";
|
|
4040
|
+
}
|
|
3603
4041
|
function decodeCealHostInvocationAuthenticationRequest(value) {
|
|
3604
|
-
if (!isCealJsonRecord(value)
|
|
4042
|
+
if (!isCealJsonRecord(value)) throw new TypeError("The Host invocation authentication request is invalid.");
|
|
4043
|
+
requireCealClientWireSchemaVersion(value.schema_version, CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA);
|
|
4044
|
+
if (!exactKeys(value, CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_FIELDS) || typeof value.client_nonce !== "string" || !CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce)) {
|
|
3605
4045
|
throw new TypeError("The Host invocation authentication request is invalid.");
|
|
3606
4046
|
}
|
|
3607
4047
|
return Object.freeze({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA, client_nonce: value.client_nonce });
|
|
3608
4048
|
}
|
|
3609
4049
|
function decodeCealHostInvocationAuthenticationResult(value, now = /* @__PURE__ */ new Date()) {
|
|
3610
|
-
|
|
4050
|
+
return decodeHostInvocationAuthenticationResult(value, CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_FIELDS, now);
|
|
4051
|
+
}
|
|
4052
|
+
function decodeHostInvocationAuthenticationResult(value, fields, now) {
|
|
4053
|
+
if (!isCealJsonRecord(value)) throw new TypeError("The Host invocation authentication result is invalid.");
|
|
4054
|
+
requireCealClientWireSchemaVersion(value.schema_version, CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA);
|
|
4055
|
+
if (!exactKeys(value, fields) || typeof value.context_token !== "string" || !CEAL_HOST_CONTEXT_TOKEN_PATTERN.test(value.context_token) || typeof value.expires_at !== "string" || !validFutureTimestamp(value.expires_at, now)) {
|
|
3611
4056
|
throw new TypeError("The Host invocation authentication result is invalid.");
|
|
3612
4057
|
}
|
|
3613
4058
|
return Object.freeze({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA, context_token: value.context_token, expires_at: value.expires_at });
|
|
3614
4059
|
}
|
|
3615
4060
|
function validFutureTimestamp(value, now) {
|
|
3616
|
-
const
|
|
3617
|
-
return Number.isFinite(
|
|
4061
|
+
const timestamp3 = Date.parse(value);
|
|
4062
|
+
return Number.isFinite(timestamp3) && new Date(timestamp3).toISOString() === value && timestamp3 > now.getTime();
|
|
3618
4063
|
}
|
|
3619
4064
|
function exactKeys(value, expected) {
|
|
3620
4065
|
const keys = Object.keys(value);
|
|
@@ -3628,9 +4073,6 @@ var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
|
3628
4073
|
"hook_invoked",
|
|
3629
4074
|
"registration_written",
|
|
3630
4075
|
"hook_failed",
|
|
3631
|
-
"watcher_turn_observed",
|
|
3632
|
-
"current_prompt_registered",
|
|
3633
|
-
"current_prompt_registration_failed",
|
|
3634
4076
|
"activation_recorded",
|
|
3635
4077
|
"activation_failed",
|
|
3636
4078
|
"watcher_scan_failed",
|
|
@@ -3653,6 +4095,9 @@ var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "un
|
|
|
3653
4095
|
var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
|
|
3654
4096
|
var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "items_path", "row_count", "result_complete", "coverage", "local_result_root", "model_disclosure", "continue_argv", "how_to_read"]);
|
|
3655
4097
|
|
|
4098
|
+
// packages/ceal-client-protocol/src/discovery-manifest.ts
|
|
4099
|
+
var DISCOVERY_MANIFEST_KEYS = CEAL_CLIENT_WIRE_CONTRACT.command_response.discovery_manifest_fields;
|
|
4100
|
+
|
|
3656
4101
|
// packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
|
|
3657
4102
|
var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
3658
4103
|
|
|
@@ -3711,6 +4156,7 @@ var ADMIN_UNAVAILABLE_REASONS = vocabulary([
|
|
|
3711
4156
|
"legacy",
|
|
3712
4157
|
"invalid_observation",
|
|
3713
4158
|
"redacted",
|
|
4159
|
+
"organization_setting_disabled",
|
|
3714
4160
|
"owner_unavailable",
|
|
3715
4161
|
"unit_unknown",
|
|
3716
4162
|
"retention_gap",
|
|
@@ -3740,15 +4186,13 @@ var ADMIN_APPROVAL_DISPOSITIONS = CEAL_OPERATION_APPROVAL_DISPOSITIONS;
|
|
|
3740
4186
|
var ADMIN_APPROVAL_POSTURES = vocabulary([...ADMIN_APPROVAL_DISPOSITIONS, "not_applicable"]);
|
|
3741
4187
|
var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "approval", "effect", "readback"]);
|
|
3742
4188
|
|
|
3743
|
-
// packages/ceal-client-protocol/src/actor-kind.ts
|
|
3744
|
-
var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
|
|
3745
|
-
|
|
3746
4189
|
// packages/ceal-client-protocol/src/device-enrollment.ts
|
|
3747
4190
|
var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
|
|
3748
4191
|
var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
|
|
3749
4192
|
|
|
3750
4193
|
// packages/ceal-client-protocol/src/personal-client-session.ts
|
|
3751
4194
|
var REFRESH_FAILURE_CODES = new Set(CEAL_REFRESH_FAILURE_CODES);
|
|
4195
|
+
var REVOKE_FAILURE_CODES = new Set(CEAL_CLIENT_REVOKE_FAILURE_CODES);
|
|
3752
4196
|
|
|
3753
4197
|
// packages/ceal-client-protocol/src/index.ts
|
|
3754
4198
|
var MAX_REQUEST_BYTES = 32 * 1024;
|
|
@@ -3840,16 +4284,59 @@ function hostClientProbeRefusal(probe, subject) {
|
|
|
3840
4284
|
}
|
|
3841
4285
|
|
|
3842
4286
|
// host/client-doctor.ts
|
|
3843
|
-
import { createHash as
|
|
4287
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
3844
4288
|
import { execFile } from "node:child_process";
|
|
3845
4289
|
import { access, open, readFile, realpath, stat } from "node:fs/promises";
|
|
3846
4290
|
import { delimiter, isAbsolute, join as join2 } from "node:path";
|
|
3847
4291
|
import { promisify } from "node:util";
|
|
4292
|
+
|
|
4293
|
+
// host/command-vector.ts
|
|
4294
|
+
import { win32 } from "node:path";
|
|
4295
|
+
var MAX_COMMAND_BYTES = 4096;
|
|
4296
|
+
function formatHostCommand(vector, platform) {
|
|
4297
|
+
return formatWith(vector, platform === "windows" ? quoteWindowsArgument : quotePosixArgument);
|
|
4298
|
+
}
|
|
4299
|
+
function hostScriptLauncherInvocation(launcherPath, args, platform = process.platform, environment = process.env) {
|
|
4300
|
+
if (/\.(?:cjs|js|mjs)$/iu.test(launcherPath)) return { executable: process.execPath, args: [launcherPath, ...args] };
|
|
4301
|
+
if (platform === "win32" && /\.(?:bat|cmd)$/iu.test(launcherPath)) {
|
|
4302
|
+
return {
|
|
4303
|
+
executable: environment.ComSpec ?? "cmd.exe",
|
|
4304
|
+
args: ["/d", "/s", "/c", `call ${formatHostCommand({ executable: launcherPath, args }, "windows")}`],
|
|
4305
|
+
windowsVerbatimArguments: true
|
|
4306
|
+
};
|
|
4307
|
+
}
|
|
4308
|
+
if (platform === "win32" && /\.ps1$/iu.test(launcherPath)) {
|
|
4309
|
+
return {
|
|
4310
|
+
executable: win32.join(environment.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe"),
|
|
4311
|
+
args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-File", launcherPath, ...args]
|
|
4312
|
+
};
|
|
4313
|
+
}
|
|
4314
|
+
return { executable: launcherPath, args };
|
|
4315
|
+
}
|
|
4316
|
+
function formatWith(vector, quote) {
|
|
4317
|
+
const command = [vector.executable, ...vector.args].map(quote).join(" ");
|
|
4318
|
+
const commandBytes = Buffer.byteLength(command, "utf8");
|
|
4319
|
+
if (commandBytes > MAX_COMMAND_BYTES) throw new TypeError(`Host command vector is too large (${refusalFact("bytes", commandBytes, `at most ${MAX_COMMAND_BYTES} UTF-8 bytes`)}).`);
|
|
4320
|
+
return command;
|
|
4321
|
+
}
|
|
4322
|
+
function quotePosixArgument(value) {
|
|
4323
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
4324
|
+
}
|
|
4325
|
+
function quoteWindowsArgument(value) {
|
|
4326
|
+
if (value.includes("%") || value.includes("!")) throw new TypeError("Host command vector contains unsafe Windows expansion syntax.");
|
|
4327
|
+
return quoteWindowsExecArgument(value);
|
|
4328
|
+
}
|
|
4329
|
+
function quoteWindowsExecArgument(value) {
|
|
4330
|
+
const escaped = value.replace(/(\\*)"/gu, '$1$1\\"').replace(/(\\+)$/gu, "$1$1");
|
|
4331
|
+
return `"${escaped}"`;
|
|
4332
|
+
}
|
|
4333
|
+
|
|
4334
|
+
// host/client-doctor.ts
|
|
3848
4335
|
var execFileAsync = promisify(execFile);
|
|
3849
4336
|
var GIT_OID = /^[a-f0-9]{40}$/u;
|
|
3850
4337
|
var SHA2562 = /^[a-f0-9]{64}$/u;
|
|
3851
4338
|
var SAFE_REF2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
|
|
3852
|
-
var CEAL_ENVIRONMENT_OVERRIDES =
|
|
4339
|
+
var CEAL_ENVIRONMENT_OVERRIDES = CEAL_CLIENT_CONTEXT_OVERRIDE_ENVIRONMENTS;
|
|
3853
4340
|
async function doctorClient(options = {}, component) {
|
|
3854
4341
|
const environment = canonicalClientEnvironment(options.environment ?? process.env);
|
|
3855
4342
|
const run = options.run ?? executeCealCommand;
|
|
@@ -3886,7 +4373,7 @@ async function inspectExecutable(options, environment, component) {
|
|
|
3886
4373
|
return { ok: true, value: executable };
|
|
3887
4374
|
}
|
|
3888
4375
|
async function inspectVersion(options, executable, environment, run, component) {
|
|
3889
|
-
const probe = hostClientProbe(await safeRun(run, executable.path,
|
|
4376
|
+
const probe = hostClientProbe(await safeRun(run, executable.path, CEAL_CLIENT_VERSION_COMMAND, environment), (result) => versionIdentity(commandDocument(result)));
|
|
3890
4377
|
if (probe.kind !== "value") return probeFailure(probe, "version", component, options);
|
|
3891
4378
|
if (options.expected_source_commit !== void 0 && probe.value.source_commit !== options.expected_source_commit) return inspectedFailure("ceal_identity_mismatch", `The Ceal executable visible to the ${component} Host did not return the expected source identity (${refusalFact("source_commit", probe.value.source_commit, `expected ${options.expected_source_commit}`)}).`, `Install the exact candidate, restart ${component}, and verify \`ceal version\` from that Host.`);
|
|
3892
4379
|
return { ok: true, value: probe.value };
|
|
@@ -3903,8 +4390,8 @@ function probeCode(code) {
|
|
|
3903
4390
|
return "ceal_output_unreadable";
|
|
3904
4391
|
}
|
|
3905
4392
|
async function inspectCanonicalContext(options, executable, environment, run, component) {
|
|
3906
|
-
const probe = hostClientProbe(await safeRun(run, executable.path,
|
|
3907
|
-
if (probe.kind === "unreachable") return probeFailure(probe, "
|
|
4393
|
+
const probe = hostClientProbe(await safeRun(run, executable.path, CEAL_CLIENT_ACCESS_GROUP_LIST_COMMAND, environment), (result) => selectedContext(commandDocument(result)));
|
|
4394
|
+
if (probe.kind === "unreachable") return probeFailure(probe, CEAL_CLIENT_ACCESS_GROUP_LIST_COMMAND.join(" "), component, options);
|
|
3908
4395
|
if (probe.kind !== "value" || options.expected_instance_ref !== void 0 && probe.value.instance_ref !== options.expected_instance_ref) return inspectedFailure("canonical_context_unavailable", `The ${component} Host cannot select the intended authenticated context from Ceal's canonical user configuration (${refusalFacts([{ name: "probe_kind", value: probe.kind, expected: "value" }, { name: "instance_ref", value: probe.kind === "value" ? probe.value.instance_ref : "unavailable", expected: options.expected_instance_ref ?? "a selected context" }])}).`, `Run \`ceal session adopt --email <email>\` without CEAL context overrides, then restart ${component}.`);
|
|
3909
4396
|
return { ok: true, value: probe.value };
|
|
3910
4397
|
}
|
|
@@ -3925,7 +4412,7 @@ async function resolveCealExecutable(command, environment) {
|
|
|
3925
4412
|
const metadata = await stat(path);
|
|
3926
4413
|
if (!metadata.isFile()) continue;
|
|
3927
4414
|
if (await isLauncher(path)) return resolveLauncherBinary(path, environment);
|
|
3928
|
-
return { path, sha256:
|
|
4415
|
+
return { path, sha256: createHash3("sha256").update(await readFile(path)).digest("hex") };
|
|
3929
4416
|
} catch {
|
|
3930
4417
|
}
|
|
3931
4418
|
}
|
|
@@ -3944,18 +4431,19 @@ async function isLauncher(path) {
|
|
|
3944
4431
|
}
|
|
3945
4432
|
}
|
|
3946
4433
|
async function resolveLauncherBinary(launcherPath, environment) {
|
|
3947
|
-
const
|
|
4434
|
+
const invocation = hostScriptLauncherInvocation(launcherPath, CEAL_CLIENT_DOCTOR_COMMAND, process.platform, environment);
|
|
4435
|
+
const result = await execFileAsync(invocation.executable, [...invocation.args], { env: environment, timeout: 15e3, encoding: "utf8", windowsHide: false, ...invocation.windowsVerbatimArguments === void 0 ? {} : { windowsVerbatimArguments: invocation.windowsVerbatimArguments } });
|
|
3948
4436
|
let parsed;
|
|
3949
4437
|
try {
|
|
3950
4438
|
parsed = JSON.parse(result.stdout);
|
|
3951
4439
|
} catch {
|
|
3952
4440
|
throw new Error("launcher doctor returned unreadable output");
|
|
3953
4441
|
}
|
|
3954
|
-
if (!isRecord(parsed) || parsed.schema_version !==
|
|
4442
|
+
if (!isRecord(parsed) || parsed.schema_version !== CEAL_CLIENT_DOCTOR_SCHEMA_VERSION || parsed.ok !== true || typeof parsed.binary_path !== "string" || parsed.binary_path.length === 0) throw new Error("launcher doctor did not report a binary path");
|
|
3955
4443
|
const binaryPath = await realpath(parsed.binary_path);
|
|
3956
4444
|
const metadata = await stat(binaryPath);
|
|
3957
4445
|
if (!metadata.isFile()) throw new Error("launcher doctor reported a non-file binary path");
|
|
3958
|
-
return { path: binaryPath, sha256:
|
|
4446
|
+
return { path: binaryPath, sha256: createHash3("sha256").update(await readFile(binaryPath)).digest("hex") };
|
|
3959
4447
|
}
|
|
3960
4448
|
async function executeCealCommand(executable, args, environment) {
|
|
3961
4449
|
try {
|
|
@@ -3990,7 +4478,7 @@ function selectedContextRecord(item) {
|
|
|
3990
4478
|
return { name: item.name, instance_ref: item.instance_ref };
|
|
3991
4479
|
}
|
|
3992
4480
|
function versionIdentity(value) {
|
|
3993
|
-
if (value?.schema_version !==
|
|
4481
|
+
if (value?.schema_version !== CEAL_CLIENT_VERSION_SCHEMA_VERSION || value.ok !== true || typeof value.version !== "string" || typeof value.source_commit !== "string" || typeof value.source_tree !== "string") return null;
|
|
3994
4482
|
if (value.version.length === 0 || !GIT_OID.test(value.source_commit) || !GIT_OID.test(value.source_tree)) return null;
|
|
3995
4483
|
return { version: value.version, source_commit: value.source_commit, source_tree: value.source_tree };
|
|
3996
4484
|
}
|
|
@@ -4014,16 +4502,9 @@ async function doctorCodexClient(options = {}) {
|
|
|
4014
4502
|
import { performance } from "node:perf_hooks";
|
|
4015
4503
|
|
|
4016
4504
|
// host/relay/src/producer-frame.ts
|
|
4017
|
-
var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
|
|
4018
|
-
var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
|
|
4019
4505
|
var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
4020
|
-
var
|
|
4021
|
-
var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
|
|
4022
|
-
var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "user_input", "turn_terminal", "producer_status"];
|
|
4506
|
+
var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "turn_terminal", "producer_status"];
|
|
4023
4507
|
var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
|
|
4024
|
-
function isHostProducerFrameKind(value) {
|
|
4025
|
-
return typeof value === "string" && FRAME_KINDS.has(value);
|
|
4026
|
-
}
|
|
4027
4508
|
var HOST_PRODUCER_FRAME_ERROR_CODES = [
|
|
4028
4509
|
"invalid_frame_encoding",
|
|
4029
4510
|
"invalid_frame_length",
|
|
@@ -4033,444 +4514,217 @@ var HOST_PRODUCER_FRAME_ERROR_CODES = [
|
|
|
4033
4514
|
"invalid_producer_identity",
|
|
4034
4515
|
"frame_too_large"
|
|
4035
4516
|
];
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4517
|
+
|
|
4518
|
+
// host/relay/src/host-producer-core.ts
|
|
4519
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
4520
|
+
|
|
4521
|
+
// host/secure-file.ts
|
|
4522
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
4523
|
+
import { constants } from "node:fs";
|
|
4524
|
+
import { lstat, open as open2 } from "node:fs/promises";
|
|
4525
|
+
import { dirname, win32 as win322 } from "node:path";
|
|
4526
|
+
import { fileURLToPath } from "node:url";
|
|
4527
|
+
var SecureFileCustodyError = class extends Error {
|
|
4528
|
+
name = "SecureFileCustodyError";
|
|
4529
|
+
reason;
|
|
4530
|
+
constructor(reason, message) {
|
|
4039
4531
|
super(message);
|
|
4040
|
-
this.
|
|
4532
|
+
this.reason = reason;
|
|
4533
|
+
}
|
|
4534
|
+
get code() {
|
|
4535
|
+
if (this.reason === "reparse_point") return "ELOOP";
|
|
4536
|
+
if (this.reason === "not_found") return "ENOENT";
|
|
4537
|
+
if (this.reason === "too_large") return "secure_file_too_large";
|
|
4538
|
+
if (this.reason === "already_exists") return "EEXIST";
|
|
4539
|
+
return this.reason === "not_private" ? "secure_file_not_private" : "secure_file_unavailable";
|
|
4041
4540
|
}
|
|
4042
4541
|
};
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
return
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
const
|
|
4059
|
-
const text = new TextDecoder("utf-8", { fatal: true });
|
|
4060
|
-
let value;
|
|
4542
|
+
var WINDOWS_SECUREFS_REFUSAL_SCHEMA = "ceal.host_securefs_refusal.v1";
|
|
4543
|
+
var WINDOWS_SECUREFS_MAX_RANDOM_FILE_BYTES = 4096;
|
|
4544
|
+
var WINDOWS_SECUREFS_TIMEOUT_MS = 1e4;
|
|
4545
|
+
var WINDOWS_SECUREFS_DIAGNOSTIC_BYTES = 4096;
|
|
4546
|
+
function secureOpenFlags(flags, platform) {
|
|
4547
|
+
if (platform === "win32") return flags;
|
|
4548
|
+
const nofollow = constants.O_NOFOLLOW;
|
|
4549
|
+
if (typeof nofollow === "number" && nofollow !== 0) return flags | nofollow;
|
|
4550
|
+
throw new Error("Secure file reads require O_NOFOLLOW on this platform.");
|
|
4551
|
+
}
|
|
4552
|
+
async function openNoFollow(path, flags = constants.O_RDONLY, mode, platform = process.platform, _custody = {}) {
|
|
4553
|
+
if (platform !== "win32") return await open2(path, secureOpenFlags(flags, platform), mode);
|
|
4554
|
+
return await openWindowsIdentityScreened(path, flags, mode);
|
|
4555
|
+
}
|
|
4556
|
+
async function openWindowsIdentityScreened(path, flags, mode) {
|
|
4557
|
+
const handle = await open2(path, flags, mode);
|
|
4061
4558
|
try {
|
|
4062
|
-
|
|
4559
|
+
const first = await handle.stat();
|
|
4560
|
+
if (!first.isFile()) throw new SecureFileCustodyError("not_private", "The Windows Host file is not a regular file.");
|
|
4561
|
+
await assertWindowsLinkFree(path, first);
|
|
4562
|
+
await assertWindowsIdentityStable(handle, first, path);
|
|
4563
|
+
return handle;
|
|
4063
4564
|
} catch (error) {
|
|
4064
|
-
|
|
4065
|
-
throw
|
|
4565
|
+
await handle.close();
|
|
4566
|
+
throw error;
|
|
4066
4567
|
}
|
|
4067
|
-
validateFrame(value);
|
|
4068
|
-
return value;
|
|
4069
4568
|
}
|
|
4070
|
-
function
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
if (
|
|
4079
|
-
|
|
4080
|
-
}
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
if (
|
|
4084
|
-
}
|
|
4085
|
-
function
|
|
4086
|
-
|
|
4569
|
+
async function assertWindowsLinkFree(path, first) {
|
|
4570
|
+
const entry = await lstat(path);
|
|
4571
|
+
if (entry.isSymbolicLink() || entry.dev !== first.dev || entry.ino !== first.ino) {
|
|
4572
|
+
throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
async function assertWindowsIdentityStable(handle, first, path) {
|
|
4576
|
+
const second = await handle.stat();
|
|
4577
|
+
if (second.dev !== first.dev || second.ino !== first.ino || second.size !== first.size || second.mtimeMs !== first.mtimeMs) {
|
|
4578
|
+
throw new SecureFileCustodyError("unavailable", "The Windows Host file changed while its identity was checked.");
|
|
4579
|
+
}
|
|
4580
|
+
const entry = await lstat(path);
|
|
4581
|
+
if (entry.isSymbolicLink()) throw new SecureFileCustodyError("reparse_point", "The Windows Host file path resolves through a reparse point.");
|
|
4582
|
+
if (entry.dev !== second.dev || entry.ino !== second.ino) throw new SecureFileCustodyError("unavailable", "The Windows Host file path no longer names the opened file.");
|
|
4583
|
+
}
|
|
4584
|
+
async function readPrivateFileWithSecurefs(path, maxBytes, custody = {}) {
|
|
4585
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) throw new TypeError("The securefs read bound must be a positive safe integer.");
|
|
4586
|
+
const result = await runWindowsSecurefs(["internal", "securefs", "read-private-file", path, String(maxBytes)], maxBytes, custody);
|
|
4587
|
+
if (result.exit_code === 0 && result.stderr.length === 0 && result.stdout.length <= maxBytes) return result.stdout;
|
|
4588
|
+
if (result.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned bytes with a refusal.");
|
|
4589
|
+
if (result.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command exceeded its output contract.");
|
|
4590
|
+
throw securefsRefusal(result.stderr);
|
|
4591
|
+
}
|
|
4592
|
+
async function createRandomPrivateFileWithSecurefs(path, byteLength2, custody = {}) {
|
|
4593
|
+
if (!Number.isSafeInteger(byteLength2) || byteLength2 < 1 || byteLength2 > WINDOWS_SECUREFS_MAX_RANDOM_FILE_BYTES) throw new TypeError(`The securefs random-file size must be between 1 and ${WINDOWS_SECUREFS_MAX_RANDOM_FILE_BYTES} bytes.`);
|
|
4594
|
+
const result = await runWindowsSecurefs(["internal", "securefs", "create-random-private-file", path, String(byteLength2)], 0, custody);
|
|
4595
|
+
if (result.exit_code === 0 && result.stdout.length === 0 && result.stderr.length === 0) return;
|
|
4596
|
+
if (result.stdout.length !== 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned unexpected creation bytes.");
|
|
4597
|
+
if (result.exit_code === 0) throw new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an invalid creation result.");
|
|
4598
|
+
throw securefsRefusal(result.stderr);
|
|
4599
|
+
}
|
|
4600
|
+
async function runWindowsSecurefs(args, maxBytes, custody) {
|
|
4601
|
+
if (custody.run_securefs_command !== void 0) return await custody.run_securefs_command(args);
|
|
4602
|
+
const executable = custody.securefs_executable ?? resolveInstalledHostExecutable();
|
|
4603
|
+
const processArgs = [...custody.securefs_args_prefix ?? [], ...args];
|
|
4604
|
+
return await new Promise((resolve, reject2) => {
|
|
4605
|
+
execFile2(executable, processArgs, { encoding: "buffer", timeout: WINDOWS_SECUREFS_TIMEOUT_MS, maxBuffer: maxBytes + WINDOWS_SECUREFS_DIAGNOSTIC_BYTES }, (error, stdout, stderr) => {
|
|
4606
|
+
const exitCode = error === null ? 0 : typeof error.code === "number" ? error.code : null;
|
|
4607
|
+
if (exitCode === null) {
|
|
4608
|
+
reject2(new SecureFileCustodyError("unavailable", "The installed Windows Host securefs command could not run."));
|
|
4609
|
+
return;
|
|
4610
|
+
}
|
|
4611
|
+
resolve({ exit_code: exitCode, stdout: Buffer.from(stdout), stderr: Buffer.from(stderr) });
|
|
4612
|
+
});
|
|
4613
|
+
});
|
|
4087
4614
|
}
|
|
4088
|
-
function
|
|
4089
|
-
|
|
4615
|
+
function securefsRefusal(stderr) {
|
|
4616
|
+
let value;
|
|
4617
|
+
try {
|
|
4618
|
+
value = JSON.parse(stderr.toString("utf8"));
|
|
4619
|
+
} catch {
|
|
4620
|
+
return new SecureFileCustodyError("unavailable", "The Windows Host securefs command returned an unreadable refusal.");
|
|
4621
|
+
}
|
|
4622
|
+
if (!isRecord2(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.");
|
|
4623
|
+
if (value.code === "reparse_point") return new SecureFileCustodyError("reparse_point", "The Windows Host securefs file is a reparse point.");
|
|
4624
|
+
if (value.code === "not_found") return new SecureFileCustodyError("not_found", "The Windows Host securefs file was not found.");
|
|
4625
|
+
if (value.code === "too_large") return new SecureFileCustodyError("too_large", "The Windows Host securefs file exceeds its read bound.");
|
|
4626
|
+
if (value.code === "already_exists") return new SecureFileCustodyError("already_exists", "The Windows Host securefs target already exists.");
|
|
4627
|
+
if (value.code === "not_private") return new SecureFileCustodyError("not_private", "The Windows Host securefs file is not private to the current user.");
|
|
4628
|
+
return new SecureFileCustodyError("unavailable", "The Windows Host securefs command refused the file read.");
|
|
4090
4629
|
}
|
|
4091
|
-
function
|
|
4092
|
-
|
|
4630
|
+
function resolveInstalledHostExecutable() {
|
|
4631
|
+
const privateRuntimeRoot = dirname(fileURLToPath(import.meta.url));
|
|
4632
|
+
return win322.join(privateRuntimeRoot, "..", "..", "bin", "ceal-host.exe");
|
|
4093
4633
|
}
|
|
4094
|
-
function
|
|
4095
|
-
return
|
|
4634
|
+
function isRecord2(value) {
|
|
4635
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4096
4636
|
}
|
|
4097
|
-
function
|
|
4098
|
-
return
|
|
4637
|
+
function isOwnedBy(uid, expectedUid) {
|
|
4638
|
+
return Number.isSafeInteger(uid) && Number.isSafeInteger(expectedUid) && uid === expectedUid;
|
|
4099
4639
|
}
|
|
4100
|
-
function
|
|
4101
|
-
|
|
4640
|
+
function isSelfOwned(uid, platform = process.platform, _handle) {
|
|
4641
|
+
if (platform === "win32" || typeof process.geteuid !== "function") return false;
|
|
4642
|
+
return isOwnedBy(uid, process.geteuid());
|
|
4102
4643
|
}
|
|
4103
|
-
function
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
function validStatus(value) {
|
|
4107
|
-
return exactOptional(value, ["status", "reason_code"], ["detail"]) && ["ready", "degraded", "stopping"].includes(String(value.status)) && safeRef(value.reason_code) && optionalText(value.detail, 4096);
|
|
4108
|
-
}
|
|
4109
|
-
function optionalText(value, maxLength) {
|
|
4110
|
-
return value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maxLength;
|
|
4111
|
-
}
|
|
4112
|
-
function optionalNonNegative(value) {
|
|
4113
|
-
return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
4114
|
-
}
|
|
4115
|
-
function optionalRefs(value) {
|
|
4116
|
-
return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef);
|
|
4117
|
-
}
|
|
4118
|
-
function validObservations(value) {
|
|
4119
|
-
if (value === void 0) return true;
|
|
4120
|
-
if (!Array.isArray(value) || value.length > 32) return false;
|
|
4121
|
-
return value.every((item) => {
|
|
4122
|
-
try {
|
|
4123
|
-
validateCealHostObservationDraft(item);
|
|
4124
|
-
return true;
|
|
4125
|
-
} catch {
|
|
4126
|
-
return false;
|
|
4127
|
-
}
|
|
4128
|
-
});
|
|
4129
|
-
}
|
|
4130
|
-
function jsonValue(value, depth = 0) {
|
|
4131
|
-
if (depth > 16) return false;
|
|
4132
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
4133
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
4134
|
-
if (Array.isArray(value)) return value.length <= 256 && value.every((item) => jsonValue(item, depth + 1));
|
|
4135
|
-
return record(value) && Object.keys(value).length <= 256 && Object.entries(value).every(([key, item]) => key.length > 0 && key.length <= 128 && jsonValue(item, depth + 1));
|
|
4136
|
-
}
|
|
4137
|
-
function record(value) {
|
|
4138
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4139
|
-
}
|
|
4140
|
-
function safeRef(value) {
|
|
4141
|
-
return typeof value === "string" && SAFE_REF3.test(value);
|
|
4142
|
-
}
|
|
4143
|
-
function positive(value) {
|
|
4144
|
-
return Number.isSafeInteger(value) && Number(value) > 0;
|
|
4145
|
-
}
|
|
4146
|
-
function timestamp(value) {
|
|
4147
|
-
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
4148
|
-
}
|
|
4149
|
-
function hostProducerFrameKind(value) {
|
|
4150
|
-
return isHostProducerFrameKind(value);
|
|
4151
|
-
}
|
|
4152
|
-
function exactKeys2(value, keys) {
|
|
4153
|
-
return JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
|
|
4154
|
-
}
|
|
4155
|
-
function exactOptional(value, required, optional) {
|
|
4156
|
-
const keys = Object.keys(value);
|
|
4157
|
-
return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
4644
|
+
function isPrivateFileMode(mode, platform = process.platform, _handle) {
|
|
4645
|
+
if (platform === "win32") return false;
|
|
4646
|
+
return (mode & 63) === 0;
|
|
4158
4647
|
}
|
|
4159
4648
|
|
|
4160
4649
|
// host/relay/src/host-producer-core.ts
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
import { join as join3 } from "node:path";
|
|
4165
|
-
|
|
4166
|
-
// host/relay/src/relay-ipc.ts
|
|
4167
|
-
import { createConnection, createServer } from "node:net";
|
|
4168
|
-
|
|
4169
|
-
// host/relay/src/durable-atomic-replacement.ts
|
|
4170
|
-
import { O_RDONLY } from "node:constants";
|
|
4171
|
-
import { lstat, mkdir, open as open2, rename, unlink } from "node:fs/promises";
|
|
4172
|
-
var DEFAULT_PORT = {
|
|
4173
|
-
platform: process.platform,
|
|
4174
|
-
mkdir: (path, options) => mkdir(path, options),
|
|
4175
|
-
lstat: (path) => lstat(path),
|
|
4176
|
-
open_file: (path) => open2(path, "wx", 384),
|
|
4177
|
-
rename,
|
|
4178
|
-
unlink,
|
|
4179
|
-
open_directory: (path) => open2(path, O_RDONLY),
|
|
4180
|
-
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
4181
|
-
};
|
|
4182
|
-
|
|
4183
|
-
// host/relay/src/relay-request-binding-store.ts
|
|
4184
|
-
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
4185
|
-
|
|
4186
|
-
// host/relay/src/current-prompt-custody.ts
|
|
4187
|
-
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
4188
|
-
|
|
4189
|
-
// host/relay/src/relay-ipc.ts
|
|
4190
|
-
var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
4191
|
-
"schema_version",
|
|
4192
|
-
"producer_id",
|
|
4193
|
-
"producer_installation_ref",
|
|
4194
|
-
"local_session_ref",
|
|
4195
|
-
"local_turn_ref",
|
|
4196
|
-
"local_turn_aliases",
|
|
4197
|
-
"prompt_ref",
|
|
4198
|
-
"request_ref",
|
|
4199
|
-
"bound_at",
|
|
4200
|
-
"host_source_ref",
|
|
4201
|
-
"host_session_ref",
|
|
4202
|
-
"host_turn_ref",
|
|
4203
|
-
"host_input_ref"
|
|
4204
|
-
]);
|
|
4205
|
-
var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
|
|
4206
|
-
var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v1";
|
|
4207
|
-
var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v1";
|
|
4208
|
-
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
4209
|
-
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
4210
|
-
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
4211
|
-
var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
|
|
4212
|
-
var HOST_RELAY_QUERY_RESPONSE_MAX_BYTES = 8192;
|
|
4213
|
-
var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
4214
|
-
"context_stale",
|
|
4215
|
-
"context_expired",
|
|
4216
|
-
"host_source_refused",
|
|
4217
|
-
"host_credential_refused",
|
|
4218
|
-
"gateway_unavailable",
|
|
4219
|
-
"gateway_refused",
|
|
4220
|
-
"gateway_response_invalid",
|
|
4221
|
-
"gateway_rate_limited",
|
|
4222
|
-
"producer_frame_invalid",
|
|
4223
|
-
"deadline",
|
|
4224
|
-
"cancelled",
|
|
4225
|
-
"mint_failed",
|
|
4226
|
-
"relay_failed",
|
|
4227
|
-
"request_authority_unavailable",
|
|
4228
|
-
"request_missing",
|
|
4229
|
-
"authority_mismatch",
|
|
4230
|
-
"host_source_mismatch",
|
|
4231
|
-
"host_session_mismatch",
|
|
4232
|
-
"host_turn_mismatch",
|
|
4233
|
-
"host_input_mismatch",
|
|
4234
|
-
"host_prompt_mismatch"
|
|
4235
|
-
]);
|
|
4236
|
-
var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
|
|
4237
|
-
"context_stale",
|
|
4238
|
-
"context_expired",
|
|
4239
|
-
"host_source_refused",
|
|
4240
|
-
"host_credential_refused",
|
|
4241
|
-
"gateway_unavailable",
|
|
4242
|
-
"gateway_rate_limited",
|
|
4243
|
-
"gateway_refused",
|
|
4244
|
-
"gateway_response_invalid",
|
|
4245
|
-
"producer_frame_invalid"
|
|
4246
|
-
]);
|
|
4247
|
-
var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
|
|
4248
|
-
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
4249
|
-
"permission_denied",
|
|
4250
|
-
"socket_missing",
|
|
4251
|
-
"connection_refused",
|
|
4252
|
-
"closed_early",
|
|
4253
|
-
"response_invalid",
|
|
4254
|
-
"timed_out",
|
|
4255
|
-
"cancelled",
|
|
4256
|
-
"unavailable"
|
|
4257
|
-
]);
|
|
4258
|
-
var HostRelayIpcError = class extends Error {
|
|
4259
|
-
code;
|
|
4260
|
-
/** Set only when the client-side socket exchange itself failed. */
|
|
4261
|
-
failure;
|
|
4262
|
-
constructor(code, message = code, failure2) {
|
|
4263
|
-
super(message);
|
|
4264
|
-
this.code = code;
|
|
4265
|
-
this.failure = failure2;
|
|
4266
|
-
}
|
|
4267
|
-
};
|
|
4268
|
-
function hostRelayExchangeFailure(error) {
|
|
4269
|
-
return error instanceof HostRelayIpcError ? error.failure : void 0;
|
|
4270
|
-
}
|
|
4271
|
-
function socketErrorFailure(error) {
|
|
4272
|
-
switch (errorCode2(error)) {
|
|
4273
|
-
case "EACCES":
|
|
4274
|
-
case "EPERM":
|
|
4275
|
-
return "permission_denied";
|
|
4276
|
-
case "ENOENT":
|
|
4277
|
-
return "socket_missing";
|
|
4278
|
-
case "ECONNREFUSED":
|
|
4279
|
-
return "connection_refused";
|
|
4280
|
-
default:
|
|
4281
|
-
return "unavailable";
|
|
4282
|
-
}
|
|
4283
|
-
}
|
|
4284
|
-
var HostRelayInvocationContextPort = class {
|
|
4285
|
-
endpoint;
|
|
4286
|
-
constructor(endpoint) {
|
|
4287
|
-
this.endpoint = endpoint;
|
|
4288
|
-
}
|
|
4289
|
-
mintInvocationContext(input, signal) {
|
|
4290
|
-
return queryHostRelayInvocationContext(this.endpoint, input, signal);
|
|
4291
|
-
}
|
|
4292
|
-
};
|
|
4293
|
-
var HostRelayIpcAdmissionPort = class {
|
|
4294
|
-
endpoint;
|
|
4295
|
-
timeout_ms;
|
|
4296
|
-
constructor(endpoint, timeoutMs = 5e3) {
|
|
4297
|
-
this.endpoint = endpoint;
|
|
4298
|
-
this.timeout_ms = timeoutMs;
|
|
4299
|
-
}
|
|
4300
|
-
async admit(frame) {
|
|
4301
|
-
const response2 = await submitHostProducerFrame(this.endpoint, frame, this.timeout_ms);
|
|
4302
|
-
if (!relayAdmissionCode(response2.code)) throw new HostRelayIpcError(response2.code);
|
|
4303
|
-
return { code: response2.code, admitted_through_sequence: response2.admitted_through_sequence, delivered_through_sequence: response2.delivered_through_sequence };
|
|
4304
|
-
}
|
|
4305
|
-
};
|
|
4306
|
-
var HostRelayCurrentPromptPort = class {
|
|
4307
|
-
endpoint;
|
|
4308
|
-
timeout_ms;
|
|
4309
|
-
constructor(endpoint, timeoutMs = 5e3) {
|
|
4310
|
-
this.endpoint = endpoint;
|
|
4311
|
-
this.timeout_ms = timeoutMs;
|
|
4312
|
-
}
|
|
4313
|
-
async register(input) {
|
|
4314
|
-
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
|
|
4315
|
-
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
4316
|
-
if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
|
|
4317
|
-
}
|
|
4318
|
-
};
|
|
4319
|
-
function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
|
|
4320
|
-
return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
|
|
4321
|
-
}
|
|
4322
|
-
function queryHostRelayInvocationContext(endpoint, query, signal) {
|
|
4323
|
-
return exchange(endpoint, encodeInvocationContextQuery(query), void 0, decodeInvocationContextResponseBuffer, signal);
|
|
4324
|
-
}
|
|
4325
|
-
function exchange(endpoint, encoded, timeoutMs, decode, signal) {
|
|
4326
|
-
return new Promise((resolve, reject2) => {
|
|
4327
|
-
const socket = createConnection(endpoint);
|
|
4328
|
-
let buffer = Buffer.alloc(0);
|
|
4329
|
-
let settled = false;
|
|
4330
|
-
const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
|
|
4331
|
-
const finish = (error, response2) => {
|
|
4332
|
-
if (settled) return;
|
|
4333
|
-
settled = true;
|
|
4334
|
-
signal?.removeEventListener("abort", aborted);
|
|
4335
|
-
socket.destroy();
|
|
4336
|
-
if (error !== null) reject2(error);
|
|
4337
|
-
else if (response2 !== void 0) resolve(response2);
|
|
4338
|
-
};
|
|
4339
|
-
if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
|
|
4340
|
-
if (signal?.aborted === true) {
|
|
4341
|
-
aborted();
|
|
4342
|
-
return;
|
|
4343
|
-
}
|
|
4344
|
-
signal?.addEventListener("abort", aborted, { once: true });
|
|
4345
|
-
socket.once("error", (error) => {
|
|
4346
|
-
const failure2 = socketErrorFailure(error);
|
|
4347
|
-
finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure2}.`, failure2));
|
|
4348
|
-
});
|
|
4349
|
-
socket.once("connect", () => socket.write(encoded));
|
|
4350
|
-
socket.on("data", (chunk) => {
|
|
4351
|
-
try {
|
|
4352
|
-
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
|
4353
|
-
const decoded = decode(buffer);
|
|
4354
|
-
if (decoded !== null) finish(null, decoded);
|
|
4355
|
-
} catch (error) {
|
|
4356
|
-
finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
|
|
4357
|
-
}
|
|
4358
|
-
});
|
|
4359
|
-
socket.once("end", () => {
|
|
4360
|
-
if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
|
|
4361
|
-
});
|
|
4362
|
-
});
|
|
4650
|
+
var HOST_TRANSCRIPT_CURSOR_REASONS = vocabulary(["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"]);
|
|
4651
|
+
function isHostTranscriptCursorReason(value) {
|
|
4652
|
+
return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
|
|
4363
4653
|
}
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4654
|
+
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
4655
|
+
var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
|
|
4656
|
+
var HOST_INVOCATION_TRANSCRIPT_TAIL_MAX_BYTES = 1024 * 1024;
|
|
4657
|
+
async function readBoundedHostTranscriptTail(path, maximumBytes = HOST_INVOCATION_TRANSCRIPT_TAIL_MAX_BYTES) {
|
|
4658
|
+
if (!Number.isSafeInteger(maximumBytes) || maximumBytes < 1 || maximumBytes > HOST_INVOCATION_TRANSCRIPT_TAIL_MAX_BYTES) throw new TypeError("Host transcript window byte bound is invalid.");
|
|
4659
|
+
const handle = await openNoFollow(path);
|
|
4369
4660
|
try {
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4661
|
+
const stats = await handle.stat();
|
|
4662
|
+
if (!stats.isFile() || !isSelfOwned(stats.uid)) throw new TypeError("Host transcript source is not an owner-owned regular file.");
|
|
4663
|
+
const truncated = stats.size > maximumBytes;
|
|
4664
|
+
const start = Math.max(0, stats.size - maximumBytes);
|
|
4665
|
+
const bytes = await readTranscriptBytes(handle, start, stats.size - start);
|
|
4666
|
+
return { bytes: truncated ? dropPartialLeadingTranscriptRecord(bytes) : bytes, truncated };
|
|
4667
|
+
} finally {
|
|
4668
|
+
await handle.close();
|
|
4373
4669
|
}
|
|
4374
|
-
if (!validResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response shape is invalid.");
|
|
4375
|
-
return value;
|
|
4376
|
-
}
|
|
4377
|
-
function relayAdmissionCode(value) {
|
|
4378
|
-
return value === "admitted" || value === "already_admitted" || value === "producer_epoch_stale" || value === "sequence_gap" || value === "frame_identity_conflict" || value === "producer_queue_full" || value === "relay_queue_full" || value === "producer_already_connected" || value === "invalid_producer_identity";
|
|
4379
|
-
}
|
|
4380
|
-
function validResponse(value) {
|
|
4381
|
-
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;
|
|
4382
|
-
}
|
|
4383
|
-
function hostRelayIpcCode(value) {
|
|
4384
|
-
return typeof value === "string" && ["admitted", "already_admitted", "producer_epoch_stale", "sequence_gap", "frame_identity_conflict", "producer_queue_full", "relay_queue_full", "invalid_frame_encoding", "invalid_frame_length", "frame_truncated", "unsupported_frame_schema", "invalid_frame_shape", "invalid_producer_identity", "frame_too_large", "producer_already_connected", "relay_unavailable"].includes(value);
|
|
4385
|
-
}
|
|
4386
|
-
function nonnegative(value) {
|
|
4387
|
-
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
4388
|
-
}
|
|
4389
|
-
function record2(value) {
|
|
4390
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4391
|
-
}
|
|
4392
|
-
function decodeResponseBuffer(buffer) {
|
|
4393
|
-
if (buffer.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
4394
|
-
const length = buffer.readUInt32BE(0);
|
|
4395
|
-
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}`)}.`);
|
|
4396
|
-
if (buffer.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
4397
|
-
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}` }])}.`);
|
|
4398
|
-
return decodeHostRelayIpcResponse(buffer);
|
|
4399
|
-
}
|
|
4400
|
-
function decodeInvocationContextResponseBuffer(buffer) {
|
|
4401
|
-
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_QUERY_RESPONSE_MAX_BYTES);
|
|
4402
|
-
if (value === null) return null;
|
|
4403
|
-
if (!validInvocationContextResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay invocation context response is invalid.");
|
|
4404
|
-
return value;
|
|
4405
|
-
}
|
|
4406
|
-
function decodeCurrentPromptResponseBuffer(buffer) {
|
|
4407
|
-
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
|
|
4408
|
-
if (value === null) return null;
|
|
4409
|
-
if (!record2(value) || !exactKeys3(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");
|
|
4410
|
-
return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
|
|
4411
|
-
}
|
|
4412
|
-
function encodeInvocationContextQuery(query) {
|
|
4413
|
-
const value = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA, ...query };
|
|
4414
|
-
if (!validInvocationContextQueryRequest(value)) throw new HostRelayIpcError("invalid_frame_shape");
|
|
4415
|
-
return encodeLengthPrefixed(value);
|
|
4416
|
-
}
|
|
4417
|
-
function validInvocationContextQueryRequest(value) {
|
|
4418
|
-
return validQueryIdentity(value, HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) && exactKeys3(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "client_nonce"], ["visible_output_max_bytes"]) && typeof value.client_nonce === "string" && CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce) && (value.visible_output_max_bytes === void 0 || validVisibleOutputBudget(value.visible_output_max_bytes));
|
|
4419
|
-
}
|
|
4420
|
-
function validQueryIdentity(value, schema) {
|
|
4421
|
-
return record2(value) && value.schema_version === schema && (value.producer_id === "codex" || value.producer_id === "claude") && safeRef2(value.producer_installation_ref) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref);
|
|
4422
|
-
}
|
|
4423
|
-
function validVisibleOutputBudget(value) {
|
|
4424
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
|
|
4425
4670
|
}
|
|
4426
|
-
function
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
return true;
|
|
4434
|
-
} catch {
|
|
4435
|
-
return false;
|
|
4671
|
+
async function readTranscriptBytes(handle, start, length) {
|
|
4672
|
+
const output = Buffer.alloc(Math.max(0, length));
|
|
4673
|
+
let offset = 0;
|
|
4674
|
+
while (offset < output.length) {
|
|
4675
|
+
const { bytesRead } = await handle.read(output, offset, output.length - offset, start + offset);
|
|
4676
|
+
if (bytesRead === 0) break;
|
|
4677
|
+
offset += bytesRead;
|
|
4436
4678
|
}
|
|
4679
|
+
return output.subarray(0, offset);
|
|
4437
4680
|
}
|
|
4438
|
-
function
|
|
4439
|
-
if (
|
|
4440
|
-
const
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4681
|
+
function dropPartialLeadingTranscriptRecord(bytes) {
|
|
4682
|
+
if (bytes.length === 0) return bytes;
|
|
4683
|
+
const newline = bytes.indexOf(10);
|
|
4684
|
+
return newline < 0 ? Buffer.alloc(0) : bytes.subarray(newline + 1);
|
|
4685
|
+
}
|
|
4686
|
+
async function readRegisteredHostInvocationHumanMessageWindow(listRegistrations, identity, decodeLine) {
|
|
4444
4687
|
try {
|
|
4445
|
-
|
|
4688
|
+
const registration = (await listRegistrations()).find((candidate) => candidate.host_source_ref === identity.host_source_ref && candidate.host_session_ref === identity.host_session_ref && Date.parse(candidate.expires_at) > Date.now());
|
|
4689
|
+
if (registration === void 0) throw new TypeError("context_unavailable");
|
|
4690
|
+
return await readHostInvocationHumanMessageWindow(registration.transcript_path, (line) => decodeLine(line, registration));
|
|
4446
4691
|
} catch {
|
|
4447
|
-
throw new
|
|
4448
|
-
}
|
|
4449
|
-
}
|
|
4450
|
-
function
|
|
4451
|
-
const
|
|
4452
|
-
const
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
const
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4692
|
+
throw new TypeError("context_unavailable");
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
async function readHostInvocationHumanMessageWindow(path, decodeLine) {
|
|
4696
|
+
const tail = await readBoundedHostTranscriptTail(path);
|
|
4697
|
+
const lines = new TextDecoder("utf-8", { fatal: true }).decode(tail.bytes).split("\n");
|
|
4698
|
+
const incompleteTail = lines.at(-1) !== "";
|
|
4699
|
+
if (incompleteTail) lines.pop();
|
|
4700
|
+
let truncated = tail.truncated || incompleteTail;
|
|
4701
|
+
const newestFirst = [];
|
|
4702
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4703
|
+
for (const line of lines.reverse()) {
|
|
4704
|
+
if (line.length === 0) continue;
|
|
4705
|
+
const message = decodeLine(line);
|
|
4706
|
+
if (message === "malformed") {
|
|
4707
|
+
truncated = true;
|
|
4708
|
+
continue;
|
|
4709
|
+
}
|
|
4710
|
+
if (message === null || seen.has(message.host_input_ref)) continue;
|
|
4711
|
+
seen.add(message.host_input_ref);
|
|
4712
|
+
newestFirst.push(message);
|
|
4713
|
+
}
|
|
4714
|
+
return boundCealHostInvocationHumanMessageWindow(newestFirst, truncated);
|
|
4715
|
+
}
|
|
4716
|
+
function transcriptScanFailureKind(error, adapterFailureKind) {
|
|
4717
|
+
if (transcriptErrorCode(error) === "ELOOP") return "symlink";
|
|
4718
|
+
const adapter = adapterFailureKind(error);
|
|
4719
|
+
if (adapter !== void 0) return adapter;
|
|
4720
|
+
const code = transcriptErrorCode(error);
|
|
4721
|
+
if (code === "EFBIG") return "size_budget";
|
|
4722
|
+
if (code !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code)) return "read_error";
|
|
4723
|
+
return "other";
|
|
4724
|
+
}
|
|
4725
|
+
function transcriptErrorCode(error) {
|
|
4726
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
4472
4727
|
}
|
|
4473
|
-
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
4474
4728
|
var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
|
|
4475
4729
|
async function readTranscriptCompleteChunk(handle, offset, size) {
|
|
4476
4730
|
const available = Math.max(0, size - offset);
|
|
@@ -4515,10 +4769,10 @@ async function transcriptCursorBoundsReason(handle, cursor, size) {
|
|
|
4515
4769
|
return null;
|
|
4516
4770
|
}
|
|
4517
4771
|
function transcriptSha256(value) {
|
|
4518
|
-
return
|
|
4772
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
4519
4773
|
}
|
|
4520
4774
|
function transcriptObjectSha256(value) {
|
|
4521
|
-
return
|
|
4775
|
+
return createHash4("sha256").update(JSON.stringify(value), "utf8").digest("hex");
|
|
4522
4776
|
}
|
|
4523
4777
|
function isTranscriptRecord(value) {
|
|
4524
4778
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -4587,11 +4841,10 @@ var HostTranscriptWatcher = class {
|
|
|
4587
4841
|
async openTranscript(registration, scan, handlers) {
|
|
4588
4842
|
let handle;
|
|
4589
4843
|
try {
|
|
4590
|
-
handle = await
|
|
4844
|
+
handle = await openNoFollow(this.transcriptPathOf(registration));
|
|
4591
4845
|
return await scan(handle);
|
|
4592
4846
|
} catch (error) {
|
|
4593
4847
|
if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
|
|
4594
|
-
if (hostRelayExchangeFailure(error) !== void 0) throw error;
|
|
4595
4848
|
return handlers.failed(handle, error);
|
|
4596
4849
|
} finally {
|
|
4597
4850
|
await handle?.close();
|
|
@@ -4637,29 +4890,12 @@ var HostTranscriptWatcher = class {
|
|
|
4637
4890
|
return transcriptCursorBoundsReason(handle, cursor, size);
|
|
4638
4891
|
}
|
|
4639
4892
|
};
|
|
4640
|
-
var HOST_PRODUCER_STATE_IDENTITY = {
|
|
4641
|
-
codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
|
|
4642
|
-
claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
|
|
4643
|
-
};
|
|
4644
4893
|
var HostProducerDrainCore = class {
|
|
4645
4894
|
host;
|
|
4646
|
-
relay;
|
|
4647
|
-
statePath;
|
|
4648
|
-
stateSchema;
|
|
4649
|
-
installationRef;
|
|
4650
|
-
epoch;
|
|
4651
4895
|
now;
|
|
4652
4896
|
tail = Promise.resolve();
|
|
4653
4897
|
constructor(host, runtime) {
|
|
4654
|
-
if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
|
|
4655
|
-
const epoch = runtime.producer_epoch ?? 1;
|
|
4656
|
-
if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
|
|
4657
4898
|
this.host = host;
|
|
4658
|
-
this.relay = runtime.relay;
|
|
4659
|
-
this.statePath = join3(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
|
|
4660
|
-
this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
|
|
4661
|
-
this.installationRef = runtime.producer_installation_ref;
|
|
4662
|
-
this.epoch = epoch;
|
|
4663
4899
|
this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
|
|
4664
4900
|
}
|
|
4665
4901
|
drainOutbox() {
|
|
@@ -4669,156 +4905,563 @@ var HostProducerDrainCore = class {
|
|
|
4669
4905
|
}
|
|
4670
4906
|
async drainLocked() {
|
|
4671
4907
|
const read = await this.host.listEnvelopes();
|
|
4672
|
-
let
|
|
4908
|
+
let removed = 0;
|
|
4673
4909
|
let expired = 0;
|
|
4674
|
-
let retained = 0;
|
|
4675
4910
|
for (const envelope of read.entries) {
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4911
|
+
if (Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) expired += 1;
|
|
4912
|
+
else removed += 1;
|
|
4913
|
+
await this.host.removeEnvelope(envelope);
|
|
4914
|
+
}
|
|
4915
|
+
return { removed, expired, malformed: read.malformed };
|
|
4916
|
+
}
|
|
4917
|
+
};
|
|
4918
|
+
|
|
4919
|
+
// host/relay/src/durable-atomic-replacement.ts
|
|
4920
|
+
import { lstat as lstat2, mkdir, open as open3, rename, unlink } from "node:fs/promises";
|
|
4921
|
+
var DEFAULT_PORT = {
|
|
4922
|
+
platform: process.platform,
|
|
4923
|
+
mkdir: (path, options) => mkdir(path, options),
|
|
4924
|
+
lstat: (path) => lstat2(path),
|
|
4925
|
+
open_file: (path) => open3(path, "wx", 384),
|
|
4926
|
+
rename,
|
|
4927
|
+
unlink,
|
|
4928
|
+
open_directory: (path) => open3(path),
|
|
4929
|
+
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
4930
|
+
};
|
|
4931
|
+
|
|
4932
|
+
// host/relay/src/relay-quarantine-store.ts
|
|
4933
|
+
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
4934
|
+
var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
|
|
4935
|
+
var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
|
|
4936
|
+
|
|
4937
|
+
// host/relay/src/relay-admission-store.ts
|
|
4938
|
+
var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
|
|
4939
|
+
var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
4940
|
+
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
4941
|
+
|
|
4942
|
+
// host/relay/src/relay-ipc.ts
|
|
4943
|
+
import { createConnection, createServer } from "node:net";
|
|
4944
|
+
|
|
4945
|
+
// host/relay/src/relay-pipe-handshake.ts
|
|
4946
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
4947
|
+
import { constants as constants2 } from "node:fs";
|
|
4948
|
+
import { link, unlink as unlink2 } from "node:fs/promises";
|
|
4949
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
4950
|
+
var RELAY_PIPE_HANDSHAKE_TIMEOUT_MS = 5e3;
|
|
4951
|
+
var RELAY_PIPE_KEY_BYTES = 32;
|
|
4952
|
+
var RELAY_PIPE_KEY_FILENAME = "relay-pipe.key";
|
|
4953
|
+
var RELAY_PIPE_NONCE_BYTES = 32;
|
|
4954
|
+
var RELAY_PIPE_NONCE_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
|
|
4955
|
+
var RELAY_PIPE_HELLO_SCHEMA = "ceal.host_relay_pipe_hello.v1";
|
|
4956
|
+
var RELAY_PIPE_CHALLENGE_SCHEMA = "ceal.host_relay_pipe_challenge.v1";
|
|
4957
|
+
var RELAY_PIPE_RESPONSE_SCHEMA = "ceal.host_relay_pipe_response.v1";
|
|
4958
|
+
var RELAY_PIPE_FRAME_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
4959
|
+
var RELAY_PIPE_FRAME_MAX_BYTES = 1024;
|
|
4960
|
+
var RelayPipeHandshakeError = class extends Error {
|
|
4961
|
+
name = "RelayPipeHandshakeError";
|
|
4962
|
+
code;
|
|
4963
|
+
constructor(code, message) {
|
|
4964
|
+
super(message);
|
|
4965
|
+
this.code = code;
|
|
4966
|
+
}
|
|
4967
|
+
};
|
|
4968
|
+
function relayPipeStateRootForOutboxDir(outboxDir) {
|
|
4969
|
+
return join3(dirname2(dirname2(dirname2(outboxDir))), "relay");
|
|
4970
|
+
}
|
|
4971
|
+
function relayPipeKeyPath(relayStateRoot) {
|
|
4972
|
+
return join3(relayStateRoot, RELAY_PIPE_KEY_FILENAME);
|
|
4973
|
+
}
|
|
4974
|
+
async function loadRelayPipeKey(relayStateRoot, platform = process.platform, custody = {}) {
|
|
4975
|
+
const path = relayPipeKeyPath(relayStateRoot);
|
|
4976
|
+
if (platform === "win32") return await loadWindowsRelayPipeKey(path, custody);
|
|
4977
|
+
const existing = await readRelayPipeKey(path, platform, custody);
|
|
4978
|
+
if (existing !== null) return existing;
|
|
4979
|
+
return await createPosixRelayPipeKey(path, platform, custody);
|
|
4980
|
+
}
|
|
4981
|
+
async function loadWindowsRelayPipeKey(path, custody) {
|
|
4982
|
+
const existing = await readRelayPipeKey(path, "win32", custody);
|
|
4983
|
+
if (existing !== null) return existing;
|
|
4984
|
+
try {
|
|
4985
|
+
await createRandomPrivateFileWithSecurefs(path, RELAY_PIPE_KEY_BYTES, custody);
|
|
4986
|
+
} catch (error) {
|
|
4987
|
+
if (errorCode2(error) === "EEXIST") return await readWinnerPipeKey(path, "win32", custody);
|
|
4988
|
+
throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}; delete it and restart the Host Relay.`);
|
|
4989
|
+
}
|
|
4990
|
+
return await readWinnerPipeKey(path, "win32", custody);
|
|
4991
|
+
}
|
|
4992
|
+
async function createPosixRelayPipeKey(path, platform, custody) {
|
|
4993
|
+
const key = randomBytes(RELAY_PIPE_KEY_BYTES);
|
|
4994
|
+
const staging = `${path}.${randomBytes(8).toString("hex")}.tmp`;
|
|
4995
|
+
let handle;
|
|
4996
|
+
try {
|
|
4997
|
+
handle = await openNoFollow(staging, constants2.O_WRONLY | constants2.O_CREAT | constants2.O_EXCL, 384);
|
|
4998
|
+
} catch (error) {
|
|
4999
|
+
if (errorCode2(error) !== "EEXIST") throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
|
|
5000
|
+
return await readWinnerPipeKey(path, platform, custody);
|
|
5001
|
+
}
|
|
5002
|
+
try {
|
|
5003
|
+
const stats = await handle.stat();
|
|
5004
|
+
if (!stats.isFile() || !isSelfOwned(stats.uid) || !isPrivateFileMode(stats.mode)) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} is not a private regular file owned by the current user.`);
|
|
5005
|
+
await handle.writeFile(key);
|
|
5006
|
+
await handle.sync();
|
|
5007
|
+
} finally {
|
|
5008
|
+
await handle.close();
|
|
5009
|
+
}
|
|
5010
|
+
try {
|
|
5011
|
+
await link(staging, path);
|
|
5012
|
+
} catch (error) {
|
|
5013
|
+
if (errorCode2(error) !== "EEXIST") throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
|
|
5014
|
+
return await readWinnerPipeKey(path, platform, custody);
|
|
5015
|
+
} finally {
|
|
5016
|
+
await unlink2(staging).catch(() => void 0);
|
|
5017
|
+
}
|
|
5018
|
+
return Buffer.from(key);
|
|
5019
|
+
}
|
|
5020
|
+
async function readWinnerPipeKey(path, platform, custody) {
|
|
5021
|
+
const winner = await readRelayPipeKey(path, platform, custody);
|
|
5022
|
+
if (winner === null) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
|
|
5023
|
+
return winner;
|
|
5024
|
+
}
|
|
5025
|
+
async function readRelayPipeKey(path, platform, custody) {
|
|
5026
|
+
if (platform === "win32") return await readWindowsRelayPipeKey(path, custody);
|
|
5027
|
+
return await readPosixRelayPipeKey(path);
|
|
5028
|
+
}
|
|
5029
|
+
async function readWindowsRelayPipeKey(path, custody) {
|
|
5030
|
+
let key;
|
|
5031
|
+
try {
|
|
5032
|
+
key = await readPrivateFileWithSecurefs(path, RELAY_PIPE_KEY_BYTES, custody);
|
|
5033
|
+
} catch (error) {
|
|
5034
|
+
if (errorCode2(error) === "ENOENT" || errorCode2(error) === "ELOOP") return null;
|
|
5035
|
+
throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}; delete it and restart the Host Relay.`);
|
|
5036
|
+
}
|
|
5037
|
+
if (key.byteLength !== RELAY_PIPE_KEY_BYTES) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} has ${key.byteLength} bytes; want ${RELAY_PIPE_KEY_BYTES}. Delete the file and restart the Host Relay.`);
|
|
5038
|
+
return Buffer.from(key);
|
|
5039
|
+
}
|
|
5040
|
+
async function readPosixRelayPipeKey(path) {
|
|
5041
|
+
let handle;
|
|
5042
|
+
try {
|
|
5043
|
+
handle = await openNoFollow(path);
|
|
5044
|
+
} catch (error) {
|
|
5045
|
+
if (errorCode2(error) === "ENOENT" || errorCode2(error) === "ELOOP") return null;
|
|
5046
|
+
throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key is unavailable at ${path}.`);
|
|
5047
|
+
}
|
|
5048
|
+
try {
|
|
5049
|
+
const stats = await handle.stat();
|
|
5050
|
+
if (!stats.isFile()) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} is not a regular file; delete it and restart the Host Relay.`);
|
|
5051
|
+
if (!isSelfOwned(stats.uid) || !isPrivateFileMode(stats.mode)) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} is not a private file owned by the current user; delete it and restart the Host Relay.`);
|
|
5052
|
+
if (stats.size !== RELAY_PIPE_KEY_BYTES) throw new RelayPipeHandshakeError("pipe_key_unavailable", `Host Relay pipe key at ${path} has ${stats.size} bytes; want ${RELAY_PIPE_KEY_BYTES}. Delete the file and restart the Host Relay.`);
|
|
5053
|
+
return await handle.readFile();
|
|
5054
|
+
} finally {
|
|
5055
|
+
await handle.close();
|
|
5056
|
+
}
|
|
5057
|
+
}
|
|
5058
|
+
function createPipeClientNonce() {
|
|
5059
|
+
return randomBytes(RELAY_PIPE_NONCE_BYTES).toString("base64url");
|
|
5060
|
+
}
|
|
5061
|
+
function pipeProof(key, role, firstNonce, secondNonce) {
|
|
5062
|
+
return createHmac("sha256", key).update(`${role}\0${firstNonce}\0${secondNonce}`, "utf8").digest("hex");
|
|
5063
|
+
}
|
|
5064
|
+
function createPipeServerProof(key, clientNonce, serverNonce) {
|
|
5065
|
+
return pipeProof(key, "server", clientNonce, serverNonce);
|
|
5066
|
+
}
|
|
5067
|
+
function createPipeClientProof(key, serverNonce, clientNonce) {
|
|
5068
|
+
return pipeProof(key, "client", serverNonce, clientNonce);
|
|
5069
|
+
}
|
|
5070
|
+
function encodePipeFrame(value) {
|
|
5071
|
+
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
5072
|
+
const output = Buffer.allocUnsafe(body.length + RELAY_PIPE_FRAME_BYTES);
|
|
5073
|
+
output.writeUInt32BE(body.length, 0);
|
|
5074
|
+
body.copy(output, RELAY_PIPE_FRAME_BYTES);
|
|
5075
|
+
return output;
|
|
5076
|
+
}
|
|
5077
|
+
function decodePipeFrame(buffer) {
|
|
5078
|
+
if (buffer.length < RELAY_PIPE_FRAME_BYTES) return null;
|
|
5079
|
+
const length = buffer.readUInt32BE(0);
|
|
5080
|
+
if (length === 0 || length > RELAY_PIPE_FRAME_MAX_BYTES) throw new RelayPipeHandshakeError("pipe_handshake_invalid", `Host Relay pipe handshake frame declares ${length} bytes; want between 1 and ${RELAY_PIPE_FRAME_MAX_BYTES}.`);
|
|
5081
|
+
if (buffer.length < length + RELAY_PIPE_FRAME_BYTES) return null;
|
|
5082
|
+
let value;
|
|
5083
|
+
try {
|
|
5084
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(RELAY_PIPE_FRAME_BYTES, length + RELAY_PIPE_FRAME_BYTES)));
|
|
5085
|
+
} catch {
|
|
5086
|
+
throw new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is not JSON.");
|
|
5087
|
+
}
|
|
5088
|
+
return { value, rest: buffer.subarray(length + RELAY_PIPE_FRAME_BYTES) };
|
|
5089
|
+
}
|
|
5090
|
+
function validNonce(value) {
|
|
5091
|
+
return typeof value === "string" && RELAY_PIPE_NONCE_PATTERN.test(value);
|
|
5092
|
+
}
|
|
5093
|
+
function writePipeFrame(socket, frame) {
|
|
5094
|
+
if (socket.write(frame)) return Promise.resolve();
|
|
5095
|
+
return new Promise((resolve, reject2) => {
|
|
5096
|
+
socket.once("drain", resolve);
|
|
5097
|
+
socket.once("error", reject2);
|
|
5098
|
+
socket.once("close", () => reject2(new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake connection closed.")));
|
|
5099
|
+
});
|
|
5100
|
+
}
|
|
5101
|
+
function readPipeFrames() {
|
|
5102
|
+
let buffer = Buffer.alloc(0);
|
|
5103
|
+
let pending = null;
|
|
5104
|
+
const feed = (chunk) => {
|
|
5105
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
5106
|
+
if (pending === null) return;
|
|
5107
|
+
let frame;
|
|
5108
|
+
try {
|
|
5109
|
+
frame = decodePipeFrame(buffer);
|
|
5110
|
+
} catch (error) {
|
|
5111
|
+
const waiting2 = pending;
|
|
5112
|
+
pending = null;
|
|
5113
|
+
waiting2.reject(error instanceof Error ? error : new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is invalid."));
|
|
5114
|
+
return;
|
|
5115
|
+
}
|
|
5116
|
+
if (frame === null) return;
|
|
5117
|
+
buffer = frame.rest;
|
|
5118
|
+
const waiting = pending;
|
|
5119
|
+
pending = null;
|
|
5120
|
+
waiting.resolve({ value: frame.value, rest: Buffer.alloc(0) });
|
|
5121
|
+
};
|
|
5122
|
+
const waiter = {
|
|
5123
|
+
wait: () => new Promise((resolve, reject2) => {
|
|
5124
|
+
let frame;
|
|
5125
|
+
try {
|
|
5126
|
+
frame = decodePipeFrame(buffer);
|
|
5127
|
+
} catch (error) {
|
|
5128
|
+
reject2(error instanceof Error ? error : new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake frame is invalid."));
|
|
5129
|
+
return;
|
|
4681
5130
|
}
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
|
|
4687
|
-
complete = false;
|
|
4688
|
-
break;
|
|
4689
|
-
}
|
|
5131
|
+
if (frame !== null) {
|
|
5132
|
+
buffer = frame.rest;
|
|
5133
|
+
resolve({ value: frame.value, rest: Buffer.alloc(0) });
|
|
5134
|
+
return;
|
|
4690
5135
|
}
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
5136
|
+
pending = { resolve, reject: reject2 };
|
|
5137
|
+
}),
|
|
5138
|
+
cancel: (error) => {
|
|
5139
|
+
if (pending !== null) {
|
|
5140
|
+
const waiting = pending;
|
|
5141
|
+
pending = null;
|
|
5142
|
+
waiting.reject(error);
|
|
4694
5143
|
}
|
|
4695
|
-
await this.host.removeEnvelope(envelope);
|
|
4696
|
-
await this.release(this.host.submissionRefOf(envelope));
|
|
4697
|
-
admitted += 1;
|
|
4698
5144
|
}
|
|
4699
|
-
|
|
4700
|
-
}
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
5145
|
+
};
|
|
5146
|
+
return { waiter, feed, remainder: () => buffer };
|
|
5147
|
+
}
|
|
5148
|
+
function attachHandshakeReader(socket, waiter, feed) {
|
|
5149
|
+
const onData = (chunk) => feed(Buffer.from(chunk));
|
|
5150
|
+
const onError = (error) => waiter.cancel(error);
|
|
5151
|
+
const onClose = () => waiter.cancel(new RelayPipeHandshakeError("pipe_handshake_invalid", "Host Relay pipe handshake connection closed."));
|
|
5152
|
+
socket.on("data", onData);
|
|
5153
|
+
socket.once("error", onError);
|
|
5154
|
+
socket.once("close", onClose);
|
|
5155
|
+
return () => {
|
|
5156
|
+
socket.removeListener("data", onData);
|
|
5157
|
+
socket.removeListener("error", onError);
|
|
5158
|
+
socket.removeListener("close", onClose);
|
|
5159
|
+
};
|
|
5160
|
+
}
|
|
5161
|
+
function handshakeTimeout(timeoutMs) {
|
|
5162
|
+
let timer;
|
|
5163
|
+
const done = new Promise((_resolve, reject2) => {
|
|
5164
|
+
timer = setTimeout(() => reject2(new RelayPipeHandshakeError("pipe_handshake_timeout", `Host Relay pipe handshake stalled past ${timeoutMs} ms; dropping the connection.`)), timeoutMs);
|
|
5165
|
+
timer.unref?.();
|
|
5166
|
+
});
|
|
5167
|
+
return { done, clear: () => clearTimeout(timer) };
|
|
5168
|
+
}
|
|
5169
|
+
async function conductPipeClientHandshake(socket, key, timeoutMs = RELAY_PIPE_HANDSHAKE_TIMEOUT_MS) {
|
|
5170
|
+
const bound = handshakeTimeout(timeoutMs);
|
|
5171
|
+
const { waiter, feed } = readPipeFrames();
|
|
5172
|
+
const detach = attachHandshakeReader(socket, waiter, feed);
|
|
5173
|
+
try {
|
|
5174
|
+
const clientNonce = createPipeClientNonce();
|
|
5175
|
+
await Promise.race([writePipeFrame(socket, encodePipeFrame({ schema_version: RELAY_PIPE_HELLO_SCHEMA, client_nonce: clientNonce })), bound.done]);
|
|
5176
|
+
const challenge = await Promise.race([waiter.wait(), bound.done]);
|
|
5177
|
+
if (!isRecord3(challenge.value) || challenge.value.schema_version !== RELAY_PIPE_CHALLENGE_SCHEMA || !validNonce(challenge.value.server_nonce) || typeof challenge.value.server_proof !== "string") {
|
|
5178
|
+
throw new RelayPipeHandshakeError("pipe_server_unproven", "Host Relay pipe server proof is invalid; aborting before sending any request.");
|
|
5179
|
+
}
|
|
5180
|
+
const serverNonce = challenge.value.server_nonce;
|
|
5181
|
+
const expected = createPipeServerProof(key, clientNonce, serverNonce);
|
|
5182
|
+
if (!timingSafeEqualHex(expected, challenge.value.server_proof)) {
|
|
5183
|
+
throw new RelayPipeHandshakeError("pipe_server_unproven", "Host Relay pipe server proof failed; aborting before sending any request.");
|
|
5184
|
+
}
|
|
5185
|
+
const clientProof = createPipeClientProof(key, serverNonce, clientNonce);
|
|
5186
|
+
await Promise.race([writePipeFrame(socket, encodePipeFrame({ schema_version: RELAY_PIPE_RESPONSE_SCHEMA, client_proof: clientProof })), bound.done]);
|
|
5187
|
+
} finally {
|
|
5188
|
+
bound.clear();
|
|
5189
|
+
detach();
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
function timingSafeEqualHex(left, right) {
|
|
5193
|
+
if (left.length !== right.length) return false;
|
|
5194
|
+
let diff = 0;
|
|
5195
|
+
for (let index = 0; index < left.length; index += 1) diff |= left.charCodeAt(index) ^ right.charCodeAt(index);
|
|
5196
|
+
return diff === 0;
|
|
5197
|
+
}
|
|
5198
|
+
function isRecord3(value) {
|
|
5199
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5200
|
+
}
|
|
5201
|
+
function errorCode2(error) {
|
|
5202
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
5203
|
+
}
|
|
5204
|
+
|
|
5205
|
+
// host/relay/src/relay-ipc.ts
|
|
5206
|
+
var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v2";
|
|
5207
|
+
var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v2";
|
|
5208
|
+
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
5209
|
+
var HOST_RELAY_QUERY_RESPONSE_MAX_BYTES = 8192;
|
|
5210
|
+
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
5211
|
+
"permission_denied",
|
|
5212
|
+
"socket_missing",
|
|
5213
|
+
"connection_refused",
|
|
5214
|
+
"closed_early",
|
|
5215
|
+
"response_invalid",
|
|
5216
|
+
"timed_out",
|
|
5217
|
+
"cancelled",
|
|
5218
|
+
"unavailable"
|
|
5219
|
+
]);
|
|
5220
|
+
var HostRelayIpcError = class extends Error {
|
|
5221
|
+
code;
|
|
5222
|
+
/** Set only when the client-side socket exchange itself failed. */
|
|
5223
|
+
failure;
|
|
5224
|
+
constructor(code, message = code, failure2) {
|
|
5225
|
+
super(message);
|
|
5226
|
+
this.code = code;
|
|
5227
|
+
this.failure = failure2;
|
|
4722
5228
|
}
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
5229
|
+
};
|
|
5230
|
+
function hostRelayExchangeFailure(error) {
|
|
5231
|
+
return error instanceof HostRelayIpcError ? error.failure : void 0;
|
|
5232
|
+
}
|
|
5233
|
+
function socketErrorFailure(error) {
|
|
5234
|
+
switch (errorCode3(error)) {
|
|
5235
|
+
case "EACCES":
|
|
5236
|
+
case "EPERM":
|
|
5237
|
+
return "permission_denied";
|
|
5238
|
+
case "ENOENT":
|
|
5239
|
+
return "socket_missing";
|
|
5240
|
+
case "ECONNREFUSED":
|
|
5241
|
+
return "connection_refused";
|
|
5242
|
+
default:
|
|
5243
|
+
return "unavailable";
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
var HostRelayInvocationContextPort = class {
|
|
5247
|
+
endpoint;
|
|
5248
|
+
pipe_key;
|
|
5249
|
+
timeout_ms;
|
|
5250
|
+
constructor(endpoint, pipeKey, timeoutMs = 5e3) {
|
|
5251
|
+
this.endpoint = endpoint;
|
|
5252
|
+
this.pipe_key = pipeKey;
|
|
5253
|
+
this.timeout_ms = timeoutMs;
|
|
4729
5254
|
}
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
const value = JSON.parse(await readFile2(this.statePath, "utf8"));
|
|
4733
|
-
return this.decodeState(value);
|
|
4734
|
-
} catch (error) {
|
|
4735
|
-
if (errorCode3(error) !== "ENOENT") throw error;
|
|
4736
|
-
return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
|
|
4737
|
-
}
|
|
5255
|
+
mintInvocationContext(input, signal) {
|
|
5256
|
+
return queryHostRelayInvocationContext(this.endpoint, input, this.timeout_ms, signal, this.pipe_key);
|
|
4738
5257
|
}
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
5258
|
+
};
|
|
5259
|
+
function queryHostRelayInvocationContext(endpoint, query, timeoutMs, signal, pipeKey) {
|
|
5260
|
+
return exchange(endpoint, encodeInvocationContextQuery(query), timeoutMs, decodeInvocationContextResponseBuffer, signal, pipeKey);
|
|
5261
|
+
}
|
|
5262
|
+
function exchange(endpoint, encoded, timeoutMs, decode, signal, pipeKey) {
|
|
5263
|
+
return new Promise((resolve, reject2) => {
|
|
5264
|
+
const socket = createConnection(endpoint);
|
|
5265
|
+
let buffer = Buffer.alloc(0);
|
|
5266
|
+
let settled = false;
|
|
5267
|
+
const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
|
|
5268
|
+
const finish = (error, response2) => {
|
|
5269
|
+
if (settled) return;
|
|
5270
|
+
settled = true;
|
|
5271
|
+
signal?.removeEventListener("abort", aborted);
|
|
5272
|
+
socket.destroy();
|
|
5273
|
+
if (error !== null) reject2(error);
|
|
5274
|
+
else if (response2 !== void 0) resolve(response2);
|
|
5275
|
+
};
|
|
5276
|
+
if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
|
|
5277
|
+
if (signal?.aborted === true) {
|
|
5278
|
+
aborted();
|
|
5279
|
+
return;
|
|
4753
5280
|
}
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
5281
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
5282
|
+
socket.once("error", (error) => {
|
|
5283
|
+
const failure2 = socketErrorFailure(error);
|
|
5284
|
+
finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure2}.`, failure2));
|
|
5285
|
+
});
|
|
5286
|
+
socket.once("connect", () => {
|
|
5287
|
+
void beginExchange();
|
|
5288
|
+
});
|
|
5289
|
+
async function beginExchange() {
|
|
5290
|
+
try {
|
|
5291
|
+
if (process.platform === "win32") {
|
|
5292
|
+
if (pipeKey === void 0) throw new HostRelayIpcError("relay_unavailable", "Host Relay pipe key is unavailable; refusing to send any request.");
|
|
5293
|
+
await conductPipeClientHandshake(socket, pipeKey, RELAY_PIPE_HANDSHAKE_TIMEOUT_MS);
|
|
5294
|
+
}
|
|
5295
|
+
} catch (error) {
|
|
5296
|
+
finish(error instanceof HostRelayIpcError ? error : new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay pipe handshake failed.", "closed_early"));
|
|
5297
|
+
return;
|
|
5298
|
+
}
|
|
5299
|
+
if (settled) return;
|
|
5300
|
+
socket.once("error", (error) => {
|
|
5301
|
+
const failure2 = socketErrorFailure(error);
|
|
5302
|
+
finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure2}.`, failure2));
|
|
5303
|
+
});
|
|
5304
|
+
socket.on("data", (chunk) => {
|
|
5305
|
+
try {
|
|
5306
|
+
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
|
5307
|
+
const decoded = decode(buffer);
|
|
5308
|
+
if (decoded !== null) finish(null, decoded);
|
|
5309
|
+
} catch (error) {
|
|
5310
|
+
finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
|
|
5311
|
+
}
|
|
5312
|
+
});
|
|
5313
|
+
socket.once("end", () => {
|
|
5314
|
+
if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
|
|
5315
|
+
});
|
|
5316
|
+
socket.write(encoded);
|
|
4760
5317
|
}
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
5318
|
+
});
|
|
5319
|
+
}
|
|
5320
|
+
function record(value) {
|
|
5321
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5322
|
+
}
|
|
5323
|
+
function decodeInvocationContextResponseBuffer(buffer) {
|
|
5324
|
+
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_QUERY_RESPONSE_MAX_BYTES);
|
|
5325
|
+
if (value === null) return null;
|
|
5326
|
+
if (!validInvocationContextResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay invocation context response is invalid.");
|
|
5327
|
+
return value;
|
|
5328
|
+
}
|
|
5329
|
+
function encodeInvocationContextQuery(query) {
|
|
5330
|
+
const value = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA, ...query };
|
|
5331
|
+
if (!validInvocationContextQueryRequest(value)) throw new HostRelayIpcError("invalid_frame_shape");
|
|
5332
|
+
return encodeLengthPrefixed(value);
|
|
5333
|
+
}
|
|
5334
|
+
function validInvocationContextQueryRequest(value) {
|
|
5335
|
+
if (!validQueryEnvelope(value)) return false;
|
|
5336
|
+
try {
|
|
5337
|
+
decodeCealHostInvocationContextRequest({
|
|
5338
|
+
schema_version: CEAL_HOST_INVOCATION_CONTEXT_REQUEST_SCHEMA,
|
|
5339
|
+
client_nonce: value.client_nonce,
|
|
5340
|
+
context_expires_at: value.context_expires_at,
|
|
5341
|
+
host_binding: value.host_binding,
|
|
5342
|
+
human_message_window: value.human_message_window,
|
|
5343
|
+
...value.visible_output_max_bytes === void 0 ? {} : { visible_output_max_bytes: value.visible_output_max_bytes }
|
|
5344
|
+
});
|
|
5345
|
+
return true;
|
|
5346
|
+
} catch {
|
|
5347
|
+
return false;
|
|
4776
5348
|
}
|
|
4777
|
-
}
|
|
4778
|
-
function
|
|
4779
|
-
|
|
5349
|
+
}
|
|
5350
|
+
function validQueryEnvelope(value) {
|
|
5351
|
+
if (!validQueryIdentity(value, HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) || !exactKeys2(value, ["schema_version", "producer_id", "producer_installation_ref", "client_nonce", "context_expires_at", "host_binding", "human_message_window"], ["visible_output_max_bytes"])) return false;
|
|
5352
|
+
return typeof value.client_nonce === "string" && CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce) && typeof value.context_expires_at === "string" && (value.visible_output_max_bytes === void 0 || validVisibleOutputBudget(value.visible_output_max_bytes));
|
|
5353
|
+
}
|
|
5354
|
+
function validQueryIdentity(value, schema) {
|
|
5355
|
+
return record(value) && value.schema_version === schema && (value.producer_id === "codex" || value.producer_id === "claude") && safeRef(value.producer_installation_ref);
|
|
5356
|
+
}
|
|
5357
|
+
function validVisibleOutputBudget(value) {
|
|
5358
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
|
|
5359
|
+
}
|
|
5360
|
+
function validInvocationContextResponse(value) {
|
|
5361
|
+
if (!record(value) || value.schema_version !== HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA) return false;
|
|
5362
|
+
return value.status === "refused" ? validInvocationContextRefusal(value) : validReadyInvocationContext(value);
|
|
5363
|
+
}
|
|
5364
|
+
function validInvocationContextRefusal(value) {
|
|
5365
|
+
return exactKeys2(value, ["schema_version", "status", "reason"]) && isCealHostAuthenticationCause(value.reason) && value.reason !== "no_binding";
|
|
5366
|
+
}
|
|
5367
|
+
function validReadyInvocationContext(value) {
|
|
5368
|
+
if (value.status !== "ready" || !exactKeys2(value, ["schema_version", "status", "request_ref", "result"]) || !safeRef(value.request_ref) || !value.request_ref.startsWith("ceal-request:") || !record(value.result)) return false;
|
|
5369
|
+
const result = value.result;
|
|
5370
|
+
return exactKeys2(result, ["schema_version", "context_token", "expires_at"]) && result.schema_version === CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA && typeof result.context_token === "string" && CEAL_HOST_CONTEXT_TOKEN_PATTERN.test(result.context_token) && typeof result.expires_at === "string" && Number.isFinite(Date.parse(result.expires_at));
|
|
5371
|
+
}
|
|
5372
|
+
function decodeLengthPrefixedJson(input, maximum) {
|
|
5373
|
+
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
5374
|
+
const length = input.readUInt32BE(0);
|
|
5375
|
+
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}`)}.`);
|
|
5376
|
+
if (input.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
5377
|
+
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}` }])}.`);
|
|
5378
|
+
try {
|
|
5379
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
|
|
5380
|
+
} catch {
|
|
5381
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC message is invalid.");
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
function encodeLengthPrefixed(value) {
|
|
5385
|
+
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
5386
|
+
const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
|
|
5387
|
+
output.writeUInt32BE(body.length, 0);
|
|
5388
|
+
body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
|
|
5389
|
+
return output;
|
|
5390
|
+
}
|
|
5391
|
+
function exactKeys2(value, required, optional = []) {
|
|
5392
|
+
const keys = Object.keys(value);
|
|
5393
|
+
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
5394
|
+
}
|
|
5395
|
+
function safeRef(value) {
|
|
5396
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
4780
5397
|
}
|
|
4781
5398
|
function errorCode3(error) {
|
|
4782
|
-
return
|
|
5399
|
+
return record(error) && typeof error.code === "string" ? error.code : null;
|
|
4783
5400
|
}
|
|
4784
5401
|
|
|
4785
|
-
// host/relay/src/relay-quarantine-store.ts
|
|
4786
|
-
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
4787
|
-
var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
|
|
4788
|
-
var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
|
|
4789
|
-
|
|
4790
|
-
// host/relay/src/relay-admission-store.ts
|
|
4791
|
-
var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
|
|
4792
|
-
var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
4793
|
-
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
4794
|
-
|
|
4795
5402
|
// host/service-log.ts
|
|
4796
|
-
|
|
5403
|
+
import { copyFile, mkdir as mkdir2, stat as stat2, truncate, appendFile } from "node:fs/promises";
|
|
5404
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, posix, win32 as win323 } from "node:path";
|
|
5405
|
+
var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
|
|
5406
|
+
function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
|
|
5407
|
+
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 } : {} })}
|
|
5408
|
+
`;
|
|
4797
5409
|
try {
|
|
4798
|
-
|
|
4799
|
-
`);
|
|
5410
|
+
(sink ?? defaultHostServiceDiagnosticSink)(line);
|
|
4800
5411
|
} catch {
|
|
4801
5412
|
}
|
|
4802
5413
|
}
|
|
5414
|
+
function defaultHostServiceDiagnosticSink(line) {
|
|
5415
|
+
writeHostServiceLogLine(line);
|
|
5416
|
+
process.stderr.write(line);
|
|
5417
|
+
}
|
|
4803
5418
|
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
5419
|
+
function writeHostServiceLogLine(line, environment = process.env) {
|
|
5420
|
+
if (process.platform !== "win32") return;
|
|
5421
|
+
const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
|
|
5422
|
+
if (path === void 0 || !win323.isAbsolute(path) || win323.normalize(path) !== path) return;
|
|
5423
|
+
void appendHostServiceLogLine(path, line).catch(() => void 0);
|
|
5424
|
+
}
|
|
5425
|
+
async function appendHostServiceLogLine(path, line) {
|
|
5426
|
+
if (!isAbsolute2(path)) return;
|
|
5427
|
+
await ensureHostServiceLogDirectory(path);
|
|
5428
|
+
await appendFile(path, line);
|
|
5429
|
+
}
|
|
4804
5430
|
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
5431
|
+
var hostServiceLogDirectories = /* @__PURE__ */ new Map();
|
|
5432
|
+
function ensureHostServiceLogDirectory(path) {
|
|
5433
|
+
const directory = dirname3(path);
|
|
5434
|
+
const existing = hostServiceLogDirectories.get(directory);
|
|
5435
|
+
if (existing !== void 0) return existing;
|
|
5436
|
+
const pending = mkdir2(directory, { recursive: true, mode: 448 }).then(() => void 0).catch((error) => {
|
|
5437
|
+
hostServiceLogDirectories.delete(directory);
|
|
5438
|
+
throw error;
|
|
5439
|
+
});
|
|
5440
|
+
hostServiceLogDirectories.set(directory, pending);
|
|
5441
|
+
return pending;
|
|
5442
|
+
}
|
|
5443
|
+
|
|
5444
|
+
// host/local-capability-descriptor.ts
|
|
5445
|
+
var HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX = Object.freeze(["authenticate", "--client-nonce"]);
|
|
4805
5446
|
|
|
4806
5447
|
// host/invocation-authentication-command.ts
|
|
4807
|
-
var HOST_INVOCATION_AUTHENTICATION_COMMAND_PREFIX = ["authenticate", "--client-nonce"];
|
|
4808
|
-
var HOST_AUTHENTICATION_BUDGET_ENVIRONMENT = "CEAL_HOST_AUTHENTICATION_BUDGET_MS";
|
|
4809
5448
|
var HOST_CONTEXT_ACTIVATION_WAIT_MAX_MS = 2e4;
|
|
4810
5449
|
var CODEX_CONTEXT_ACTIVATION_WAIT_MAX_MS = HOST_CONTEXT_ACTIVATION_WAIT_MAX_MS;
|
|
4811
5450
|
var HOST_AUTHENTICATION_COMPLETION_RESERVE_MS = 5e3;
|
|
4812
5451
|
var INVOCATION_CONTEXT_POLL_INTERVAL_MS = 250;
|
|
4813
5452
|
var REFUSAL_REASON_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
4814
5453
|
function installedHostInvocationAuthenticationCommandOptions(config, contextBudgetMs, maxContextWaitMs) {
|
|
4815
|
-
|
|
5454
|
+
let pipeKey;
|
|
5455
|
+
const loadPipeKey = () => {
|
|
5456
|
+
if (pipeKey === void 0) pipeKey = process.platform === "win32" ? loadRelayPipeKey(relayPipeStateRootForOutboxDir(config.outbox_dir)) : Promise.resolve(void 0);
|
|
5457
|
+
return pipeKey;
|
|
5458
|
+
};
|
|
4816
5459
|
return {
|
|
4817
5460
|
host_source_ref: config.host_source_ref,
|
|
4818
5461
|
outbox_dir: config.outbox_dir,
|
|
4819
5462
|
producer_installation_ref: config.producer_installation_ref,
|
|
4820
5463
|
context_activation_wait_ms: boundedContextActivationWait(contextBudgetMs, config.ipc_timeout_ms, maxContextWaitMs),
|
|
4821
|
-
mint_invocation_context: (input) =>
|
|
5464
|
+
mint_invocation_context: async (input) => new HostRelayInvocationContextPort(config.relay_endpoint, await loadPipeKey(), config.ipc_timeout_ms).mintInvocationContext(input)
|
|
4822
5465
|
};
|
|
4823
5466
|
}
|
|
4824
5467
|
async function runHostInvocationAuthenticationCommand(descriptor2, args, options) {
|
|
@@ -4839,26 +5482,31 @@ function authenticationRefusalReason(error) {
|
|
|
4839
5482
|
return isCealHostAuthenticationCause(message) ? message : "host_refusal_invalid";
|
|
4840
5483
|
}
|
|
4841
5484
|
async function authenticateHostInvocation(descriptor2, input, options) {
|
|
4842
|
-
if (input.length !==
|
|
5485
|
+
if (input.length !== HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX.length + 1 || input[0] !== HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX[0] || input[1] !== HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX[1]) throw new TypeError("invalid_arguments");
|
|
4843
5486
|
const request2 = decodeCealHostInvocationAuthenticationRequest({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA, client_nonce: input[2] });
|
|
4844
5487
|
const config = validateOptions(options);
|
|
4845
5488
|
const now = config.now ?? (() => /* @__PURE__ */ new Date());
|
|
4846
5489
|
const { store, current } = await currentInvocation(descriptor2, config, now);
|
|
4847
5490
|
if (config.mint_invocation_context === void 0) throw new TypeError("relay_failed");
|
|
4848
|
-
const query =
|
|
4849
|
-
producer_id: descriptor2.producer_id,
|
|
4850
|
-
producer_installation_ref: config.producer_installation_ref,
|
|
4851
|
-
local_session_ref: current.identity.host_session_ref,
|
|
4852
|
-
local_turn_ref: current.identity.host_turn_ref,
|
|
4853
|
-
client_nonce: request2.client_nonce,
|
|
4854
|
-
...await visibleOutputBudget(descriptor2)
|
|
4855
|
-
};
|
|
5491
|
+
const query = await invocationMintQuery(descriptor2, store, current, request2.client_nonce, config);
|
|
4856
5492
|
const response2 = await mintedResult(config.mint_invocation_context, query);
|
|
4857
5493
|
const completedAt = now();
|
|
4858
5494
|
const rechecked = await store.readInvocationContext(current.identity.host_source_ref, current.identity.host_session_ref, completedAt);
|
|
4859
5495
|
if (rechecked === null || !sameTurn(current, rechecked)) throw new TypeError("context_replaced");
|
|
4860
5496
|
return decodeCealHostInvocationAuthenticationResult(response2.result, completedAt);
|
|
4861
5497
|
}
|
|
5498
|
+
async function invocationMintQuery(descriptor2, store, current, clientNonce, options) {
|
|
5499
|
+
const readWindow = options.read_human_message_window?.(current.identity) ?? descriptor2.read_human_message_window?.({ store, identity: current.identity }) ?? Promise.resolve({ messages: [], truncated: false });
|
|
5500
|
+
return {
|
|
5501
|
+
producer_id: descriptor2.producer_id,
|
|
5502
|
+
producer_installation_ref: options.producer_installation_ref,
|
|
5503
|
+
client_nonce: clientNonce,
|
|
5504
|
+
context_expires_at: current.expires_at,
|
|
5505
|
+
host_binding: { ...current.identity },
|
|
5506
|
+
human_message_window: await readWindow,
|
|
5507
|
+
...await visibleOutputBudget(descriptor2)
|
|
5508
|
+
};
|
|
5509
|
+
}
|
|
4862
5510
|
async function visibleOutputBudget(descriptor2) {
|
|
4863
5511
|
const bytes = await descriptor2.resolve_visible_output_max_bytes?.();
|
|
4864
5512
|
return bytes === void 0 ? {} : { visible_output_max_bytes: bytes };
|
|
@@ -4868,30 +5516,15 @@ async function mintedResult(mint, query) {
|
|
|
4868
5516
|
try {
|
|
4869
5517
|
response2 = await mint(query);
|
|
4870
5518
|
} catch (error) {
|
|
4871
|
-
throw new TypeError(
|
|
5519
|
+
throw new TypeError(cealHostAuthenticationCauseForExchangeFailure(hostRelayExchangeFailure(error) ?? "unavailable"));
|
|
4872
5520
|
}
|
|
4873
|
-
if (response2.status
|
|
4874
|
-
if (response2.status !== "ready") throw new TypeError("no_binding");
|
|
5521
|
+
if (response2.status !== "ready") throw new TypeError(isCealHostAuthenticationCause(response2.reason) && response2.reason !== "no_binding" ? response2.reason : "host_refusal_invalid");
|
|
4875
5522
|
return response2;
|
|
4876
5523
|
}
|
|
4877
|
-
function mintRefusalCause(reason) {
|
|
4878
|
-
if (isDirectMintRefusalCause(reason)) return reason;
|
|
4879
|
-
if (reason === "deadline" || reason === "cancelled") return "transient_retryable";
|
|
4880
|
-
if (reason === "mint_failed" || reason === "relay_failed") return reason;
|
|
4881
|
-
return isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, reason) ? "context_stale" : "host_refusal_invalid";
|
|
4882
|
-
}
|
|
4883
|
-
function isDirectMintRefusalCause(reason) {
|
|
4884
|
-
return isVocabularyWord(HOST_RELAY_DIRECT_WIRE_MINT_REASONS, reason);
|
|
4885
|
-
}
|
|
4886
|
-
function exchangeFailureCause(failure2) {
|
|
4887
|
-
if (failure2 === "permission_denied") return "relay_permission_denied";
|
|
4888
|
-
if (failure2 === "timed_out") return "transient_retryable";
|
|
4889
|
-
return "relay_unavailable";
|
|
4890
|
-
}
|
|
4891
5524
|
async function currentInvocation(descriptor2, config, now) {
|
|
4892
5525
|
const store = descriptor2.create_store(config.outbox_dir);
|
|
4893
5526
|
const identity = resolveIdentity(descriptor2, config);
|
|
4894
|
-
if (identity.status
|
|
5527
|
+
if (identity.status === "session_unavailable") throw new TypeError("no_binding");
|
|
4895
5528
|
const waitBudgetMs = config.context_activation_wait_ms ?? 0;
|
|
4896
5529
|
const monotonicNow = config.monotonic_now ?? (() => performance.now());
|
|
4897
5530
|
const deadline = monotonicNow() + waitBudgetMs;
|
|
@@ -4899,36 +5532,36 @@ async function currentInvocation(descriptor2, config, now) {
|
|
|
4899
5532
|
let waitEngaged = false;
|
|
4900
5533
|
while (true) {
|
|
4901
5534
|
if (!firstCheck && monotonicNow() >= deadline) {
|
|
4902
|
-
recordActivationWaitExhaustion(descriptor2.producer_id, waitEngaged, waitBudgetMs);
|
|
5535
|
+
recordActivationWaitExhaustion(descriptor2.producer_id, waitEngaged, waitBudgetMs, config.diagnostic_sink);
|
|
4903
5536
|
throw new TypeError("context_unavailable");
|
|
4904
5537
|
}
|
|
4905
5538
|
firstCheck = false;
|
|
4906
5539
|
const observedAt = now();
|
|
4907
5540
|
const current = await readLiveSessionContext(store, identity.identity.host_source_ref, identity.identity.host_session_ref, observedAt);
|
|
4908
5541
|
if (current !== null) {
|
|
4909
|
-
recordActivationWaitCompletion(descriptor2.producer_id, waitEngaged);
|
|
5542
|
+
recordActivationWaitCompletion(descriptor2.producer_id, waitEngaged, config.diagnostic_sink);
|
|
4910
5543
|
return { store, current };
|
|
4911
5544
|
}
|
|
4912
5545
|
const remainingMs = deadline - monotonicNow();
|
|
4913
5546
|
if (remainingMs <= 0) {
|
|
4914
|
-
recordActivationWaitExhaustion(descriptor2.producer_id, waitEngaged, waitBudgetMs);
|
|
5547
|
+
recordActivationWaitExhaustion(descriptor2.producer_id, waitEngaged, waitBudgetMs, config.diagnostic_sink);
|
|
4915
5548
|
throw new TypeError("context_unavailable");
|
|
4916
5549
|
}
|
|
4917
|
-
recordActivationWaitEngagement(descriptor2.producer_id, waitEngaged);
|
|
5550
|
+
recordActivationWaitEngagement(descriptor2.producer_id, waitEngaged, config.diagnostic_sink);
|
|
4918
5551
|
waitEngaged = true;
|
|
4919
5552
|
const intervalMs = Math.min(INVOCATION_CONTEXT_POLL_INTERVAL_MS, remainingMs);
|
|
4920
5553
|
await (config.wait ?? waitForContextActivation)(intervalMs);
|
|
4921
5554
|
}
|
|
4922
5555
|
}
|
|
4923
|
-
function recordActivationWaitExhaustion(producer, engaged, budgetMs) {
|
|
4924
|
-
if (engaged || budgetMs > 0) writeHostServiceDiagnostic(producer, "activation_wait_exhausted");
|
|
4925
|
-
else writeHostServiceDiagnostic(producer, "activation_wait_skipped");
|
|
5556
|
+
function recordActivationWaitExhaustion(producer, engaged, budgetMs, sink) {
|
|
5557
|
+
if (engaged || budgetMs > 0) writeHostServiceDiagnostic(producer, "activation_wait_exhausted", void 0, void 0, sink);
|
|
5558
|
+
else writeHostServiceDiagnostic(producer, "activation_wait_skipped", void 0, void 0, sink);
|
|
4926
5559
|
}
|
|
4927
|
-
function recordActivationWaitCompletion(producer, engaged) {
|
|
4928
|
-
if (engaged) writeHostServiceDiagnostic(producer, "activation_wait_completed");
|
|
5560
|
+
function recordActivationWaitCompletion(producer, engaged, sink) {
|
|
5561
|
+
if (engaged) writeHostServiceDiagnostic(producer, "activation_wait_completed", void 0, void 0, sink);
|
|
4929
5562
|
}
|
|
4930
|
-
function recordActivationWaitEngagement(producer, engaged) {
|
|
4931
|
-
if (!engaged) writeHostServiceDiagnostic(producer, "activation_wait_engaged");
|
|
5563
|
+
function recordActivationWaitEngagement(producer, engaged, sink) {
|
|
5564
|
+
if (!engaged) writeHostServiceDiagnostic(producer, "activation_wait_engaged", void 0, void 0, sink);
|
|
4932
5565
|
}
|
|
4933
5566
|
async function readLiveSessionContext(store, hostSourceRef, hostSessionRef, now) {
|
|
4934
5567
|
const current = await store.readInvocationContext(hostSourceRef, hostSessionRef, now);
|
|
@@ -4955,9 +5588,9 @@ async function resolveOptions(descriptor2, args, options) {
|
|
|
4955
5588
|
};
|
|
4956
5589
|
}
|
|
4957
5590
|
function authenticationBudgetMsFromEnvironment() {
|
|
4958
|
-
const value = process.env[
|
|
5591
|
+
const value = process.env[CEAL_HOST_AUTHENTICATION_BUDGET_ENVIRONMENT];
|
|
4959
5592
|
if (value === void 0) return 0;
|
|
4960
|
-
if (
|
|
5593
|
+
if (!CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN.test(value)) throw new TypeError("invalid_arguments");
|
|
4961
5594
|
return Number(value);
|
|
4962
5595
|
}
|
|
4963
5596
|
function boundedContextActivationWait(contextBudgetMs, relayTimeoutMs, maxContextWaitMs) {
|
|
@@ -4994,16 +5627,16 @@ function writeResult(value, sink) {
|
|
|
4994
5627
|
}
|
|
4995
5628
|
|
|
4996
5629
|
// host/producer-service.ts
|
|
4997
|
-
import { posix as
|
|
5630
|
+
import { posix as posix4, win32 as win326 } from "node:path";
|
|
4998
5631
|
|
|
4999
5632
|
// host/runtime-source-paths.ts
|
|
5000
|
-
import { createHash as
|
|
5001
|
-
import { posix as
|
|
5633
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
5634
|
+
import { posix as posix3, win32 as win325 } from "node:path";
|
|
5002
5635
|
|
|
5003
5636
|
// host/absolute-path.ts
|
|
5004
|
-
import { posix, win32 } from "node:path";
|
|
5637
|
+
import { posix as posix2, win32 as win324 } from "node:path";
|
|
5005
5638
|
function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allowRoot = false) {
|
|
5006
|
-
const pathApi = flavor === "win32" || flavor === "windows" ?
|
|
5639
|
+
const pathApi = flavor === "win32" || flavor === "windows" ? win324 : posix2;
|
|
5007
5640
|
return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
|
|
5008
5641
|
}
|
|
5009
5642
|
|
|
@@ -5015,28 +5648,23 @@ function hostSourceUuid(sourceRef) {
|
|
|
5015
5648
|
function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
|
|
5016
5649
|
const sourceId = hostSourceUuid(sourceRef);
|
|
5017
5650
|
if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
|
|
5018
|
-
const path = platform === "win32" ?
|
|
5651
|
+
const path = platform === "win32" ? win325 : posix3;
|
|
5019
5652
|
const sourcesRoot = path.join(stateRoot, "sources");
|
|
5020
5653
|
const sourceRoot = path.join(sourcesRoot, sourceId);
|
|
5021
5654
|
const runtimeRoot = path.join(stateRoot, "run");
|
|
5022
|
-
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${
|
|
5023
|
-
assertHostRuntimeIpcLength(endpoint, platform);
|
|
5655
|
+
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash5("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
|
|
5024
5656
|
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 };
|
|
5025
5657
|
}
|
|
5026
|
-
function assertHostRuntimeIpcLength(endpoint, platform) {
|
|
5027
|
-
if (platform === "win32") return;
|
|
5028
|
-
const maximum = platform === "linux" ? 107 : 103;
|
|
5029
|
-
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" });
|
|
5030
|
-
}
|
|
5031
5658
|
function assertHostProducerSourcePaths(stateRoot, config, producer, platform = process.platform) {
|
|
5032
5659
|
const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
|
|
5033
|
-
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.");
|
|
5660
|
+
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.");
|
|
5661
|
+
return paths.endpoint;
|
|
5034
5662
|
}
|
|
5035
5663
|
|
|
5036
5664
|
// host/producer-service.ts
|
|
5037
|
-
import { constants } from "node:fs";
|
|
5038
|
-
import { lstat as
|
|
5039
|
-
var
|
|
5665
|
+
import { constants as constants3 } from "node:fs";
|
|
5666
|
+
import { lstat as lstat3 } from "node:fs/promises";
|
|
5667
|
+
var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
5040
5668
|
var HOST_PRODUCER_CONFIG_FLAG = "--ceal-host-config";
|
|
5041
5669
|
var HostProducerServiceError = class extends Error {
|
|
5042
5670
|
code;
|
|
@@ -5047,18 +5675,19 @@ var HostProducerServiceError = class extends Error {
|
|
|
5047
5675
|
}
|
|
5048
5676
|
};
|
|
5049
5677
|
var HOST_PRODUCER_CONFIG_KEYS = ["schema_version", "host_source_ref", "producer_installation_ref", "producer_epoch", "outbox_dir", "relay_endpoint"];
|
|
5678
|
+
var HOST_PRODUCER_CONFIG_ENDPOINT_KEY = "relay_endpoint";
|
|
5050
5679
|
var HOST_PRODUCER_TIMING_DEFAULTS = Object.freeze({ reconcile_interval_ms: 250, retry_backoff_ms: 1e3, ipc_timeout_ms: 5e3 });
|
|
5051
5680
|
function hostProducerExtraPath(extras, key) {
|
|
5052
5681
|
const value = extras[key];
|
|
5053
|
-
if (value === void 0) throw hostProducerError("invalid_config");
|
|
5682
|
+
if (value === void 0) throw hostProducerError("invalid_config", refusalFact(key, "missing", "an absolute path"));
|
|
5054
5683
|
return value;
|
|
5055
5684
|
}
|
|
5056
5685
|
function parseHostProducerConfigFlag(args, platform = process.platform) {
|
|
5057
5686
|
return parseProducerHostConfigFlag(args, HOST_PRODUCER_CONFIG_FLAG, platform, hostProducerError);
|
|
5058
5687
|
}
|
|
5059
|
-
async function readHostProducerConfig(descriptor2, configPath, platform = process.platform) {
|
|
5060
|
-
const config = decodeHostProducerConfigText(descriptor2, await readProducerConfigText(configPath, platform, hostProducerError), platform);
|
|
5061
|
-
const path = platform === "win32" ?
|
|
5688
|
+
async function readHostProducerConfig(descriptor2, configPath, platform = process.platform, custody = {}) {
|
|
5689
|
+
const config = decodeHostProducerConfigText(descriptor2, await readProducerConfigText(configPath, platform, hostProducerError, custody), platform);
|
|
5690
|
+
const path = platform === "win32" ? win326 : posix4;
|
|
5062
5691
|
const root = path.resolve(config.outbox_dir, "../../../../..");
|
|
5063
5692
|
try {
|
|
5064
5693
|
assertHostProducerSourcePaths(root, config, descriptor2.producer_id, platform);
|
|
@@ -5084,15 +5713,31 @@ function parseProducerHostConfigFlag(args, flag, platform, error) {
|
|
|
5084
5713
|
assertProducerAbsolutePath(value, platform, "config", error);
|
|
5085
5714
|
return value;
|
|
5086
5715
|
}
|
|
5087
|
-
async function readProducerConfigText(configPath, platform, error) {
|
|
5088
|
-
assertProducerAbsolutePath(configPath, platform, "config", error);
|
|
5716
|
+
async function readProducerConfigText(configPath, platform, error, custody) {
|
|
5717
|
+
assertProducerAbsolutePath(configPath, process.platform, "config", error);
|
|
5718
|
+
if (platform === "win32") {
|
|
5719
|
+
let bytes;
|
|
5720
|
+
try {
|
|
5721
|
+
bytes = await readPrivateFileWithSecurefs(configPath, CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES, custody);
|
|
5722
|
+
} catch (cause) {
|
|
5723
|
+
if (cause instanceof SecureFileCustodyError && cause.reason === "not_private") throw error("config_not_private");
|
|
5724
|
+
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}`));
|
|
5725
|
+
if (cause instanceof SecureFileCustodyError && cause.reason === "reparse_point") throw error("invalid_config");
|
|
5726
|
+
throw error("config_unavailable");
|
|
5727
|
+
}
|
|
5728
|
+
if (bytes.byteLength === 0) throw error("invalid_config", refusalFact("config_bytes", 0, `between 1 and ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
|
|
5729
|
+
try {
|
|
5730
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
5731
|
+
} catch {
|
|
5732
|
+
throw error("invalid_config");
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5089
5735
|
const pathStat = await configPathStat(configPath, error);
|
|
5090
5736
|
validateConfigFile(pathStat, platform, error);
|
|
5091
|
-
const
|
|
5092
|
-
const handle = await openConfigFile(configPath, constants.O_RDONLY | noFollow, error);
|
|
5737
|
+
const handle = await openConfigFile(configPath, platform, error, custody);
|
|
5093
5738
|
try {
|
|
5094
5739
|
const opened = await handle.stat();
|
|
5095
|
-
validateConfigFile(opened, platform, error);
|
|
5740
|
+
validateConfigFile(opened, platform, error, handle);
|
|
5096
5741
|
validateOpenedFile(opened, pathStat, platform, error);
|
|
5097
5742
|
return await readBoundedConfig(handle, opened.size, error);
|
|
5098
5743
|
} finally {
|
|
@@ -5100,8 +5745,9 @@ async function readProducerConfigText(configPath, platform, error) {
|
|
|
5100
5745
|
}
|
|
5101
5746
|
}
|
|
5102
5747
|
function decodeProducerConfigCore(text, options) {
|
|
5748
|
+
const ownership = producerConfigKeyOwnership(options.keys, options.platform);
|
|
5103
5749
|
const value = parseCanonicalJson(text, options.error);
|
|
5104
|
-
if (!
|
|
5750
|
+
if (!isRecord4(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 })));
|
|
5105
5751
|
const hostSourceRef = typeof value.host_source_ref === "string" ? value.host_source_ref : "";
|
|
5106
5752
|
const installationRef = typeof value.producer_installation_ref === "string" ? value.producer_installation_ref : "";
|
|
5107
5753
|
const producerEpoch = validateIdentity(hostSourceRef, installationRef, value.producer_epoch, options.error);
|
|
@@ -5119,14 +5765,21 @@ function decodeProducerConfigCore(text, options) {
|
|
|
5119
5765
|
producer_installation_ref: installationRef,
|
|
5120
5766
|
producer_epoch: producerEpoch,
|
|
5121
5767
|
outbox_dir: value.outbox_dir,
|
|
5122
|
-
relay_endpoint: value
|
|
5768
|
+
relay_endpoint: producerConfigEndpoint(value),
|
|
5123
5769
|
reconcile_interval_ms: intervals[0],
|
|
5124
5770
|
retry_backoff_ms: intervals[1],
|
|
5125
5771
|
ipc_timeout_ms: intervals[2]
|
|
5126
5772
|
};
|
|
5127
5773
|
}
|
|
5774
|
+
function producerConfigKeyOwnership(keys, platform) {
|
|
5775
|
+
if (platform !== "win32") return { required: keys, tolerated: [] };
|
|
5776
|
+
return { required: keys.filter((key) => key !== HOST_PRODUCER_CONFIG_ENDPOINT_KEY), tolerated: [HOST_PRODUCER_CONFIG_ENDPOINT_KEY] };
|
|
5777
|
+
}
|
|
5778
|
+
function producerConfigEndpoint(value) {
|
|
5779
|
+
return typeof value.relay_endpoint === "string" ? value.relay_endpoint : "";
|
|
5780
|
+
}
|
|
5128
5781
|
function assertProducerRelayEndpoint(value, platform, error) {
|
|
5129
|
-
if (platform === "win32"
|
|
5782
|
+
if (platform === "win32") return;
|
|
5130
5783
|
assertProducerAbsolutePath(value, platform, "runtime", error);
|
|
5131
5784
|
}
|
|
5132
5785
|
function assertProducerAbsolutePath(value, platform, kind, error) {
|
|
@@ -5134,26 +5787,27 @@ function assertProducerAbsolutePath(value, platform, kind, error) {
|
|
|
5134
5787
|
}
|
|
5135
5788
|
async function configPathStat(configPath, error) {
|
|
5136
5789
|
try {
|
|
5137
|
-
return await
|
|
5790
|
+
return await lstat3(configPath);
|
|
5138
5791
|
} catch {
|
|
5139
5792
|
throw error("config_unavailable");
|
|
5140
5793
|
}
|
|
5141
5794
|
}
|
|
5142
|
-
function validateConfigFile(value, platform, error) {
|
|
5795
|
+
function validateConfigFile(value, platform, error, handle) {
|
|
5143
5796
|
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" }]));
|
|
5144
5797
|
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}`));
|
|
5145
|
-
if (
|
|
5798
|
+
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" }]));
|
|
5146
5799
|
}
|
|
5147
|
-
async function openConfigFile(configPath,
|
|
5800
|
+
async function openConfigFile(configPath, platform, error, custody) {
|
|
5148
5801
|
try {
|
|
5149
|
-
return await
|
|
5150
|
-
} catch {
|
|
5802
|
+
return await openNoFollow(configPath, constants3.O_RDONLY, void 0, platform, custody);
|
|
5803
|
+
} catch (error_) {
|
|
5804
|
+
if (error_ instanceof SecureFileCustodyError) throw error(error_.reason === "not_private" ? "config_not_private" : "invalid_config");
|
|
5151
5805
|
throw error("config_unavailable");
|
|
5152
5806
|
}
|
|
5153
5807
|
}
|
|
5154
5808
|
function validateOpenedFile(opened, pathStat, platform, error) {
|
|
5155
5809
|
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" }]));
|
|
5156
|
-
if (
|
|
5810
|
+
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) }]));
|
|
5157
5811
|
}
|
|
5158
5812
|
async function readBoundedConfig(handle, expectedBytes, error) {
|
|
5159
5813
|
const buffer = Buffer.allocUnsafe(expectedBytes + 1);
|
|
@@ -5185,34 +5839,58 @@ function firstDifferenceIndex(left, right) {
|
|
|
5185
5839
|
for (let index = 0; index < shared; index += 1) if (left[index] !== right[index]) return index;
|
|
5186
5840
|
return shared;
|
|
5187
5841
|
}
|
|
5842
|
+
function producerShapeFacts(value, options) {
|
|
5843
|
+
if (!isRecord4(value)) return [{ name: "config", value: "wrong_type", expected: "a record" }];
|
|
5844
|
+
const tolerated = options.tolerated ?? [];
|
|
5845
|
+
const allowed = [...options.keys, ...tolerated, ...Object.keys(HOST_PRODUCER_TIMING_DEFAULTS)];
|
|
5846
|
+
const facts = [
|
|
5847
|
+
...Object.keys(value).filter((key) => !allowed.includes(key)).map((key) => ({ name: key, value: "unknown", expected: `one of ${[...options.keys, ...tolerated].join(", ")}` })),
|
|
5848
|
+
...options.keys.filter((key) => !Object.hasOwn(value, key)).map((key) => ({ name: key, value: "missing", expected: "present" }))
|
|
5849
|
+
];
|
|
5850
|
+
if (Object.hasOwn(value, "schema_version") && value.schema_version !== options.schema) facts.push({ name: "schema_version", value: "wrong_type", expected: options.schema });
|
|
5851
|
+
return facts;
|
|
5852
|
+
}
|
|
5188
5853
|
function validateIdentity(sourceRef, installationRef, epoch, error) {
|
|
5189
|
-
if (!
|
|
5854
|
+
if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(installationRef) || !positive(epoch, 2147483647)) throw error("invalid_config", refusalFacts(producerIdentityFacts(sourceRef, installationRef, epoch)));
|
|
5190
5855
|
return epoch;
|
|
5191
5856
|
}
|
|
5857
|
+
function producerIdentityFacts(sourceRef, installationRef, epoch) {
|
|
5858
|
+
const facts = [];
|
|
5859
|
+
if (!SAFE_REF3.test(sourceRef)) facts.push({ name: "host_source_ref", value: "wrong_type", expected: "a safe reference" });
|
|
5860
|
+
if (!SAFE_REF3.test(installationRef)) facts.push({ name: "producer_installation_ref", value: "wrong_type", expected: "a safe reference" });
|
|
5861
|
+
if (!positive(epoch, 2147483647)) facts.push({ name: "producer_epoch", value: "wrong_type", expected: "a positive integer" });
|
|
5862
|
+
return facts;
|
|
5863
|
+
}
|
|
5192
5864
|
function validateIntervals(reconcile, retry, timeout, error) {
|
|
5193
|
-
if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config");
|
|
5865
|
+
if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config", refusalFacts(producerIntervalFacts(reconcile, retry, timeout)));
|
|
5194
5866
|
return [reconcile, retry, timeout];
|
|
5195
5867
|
}
|
|
5196
|
-
function
|
|
5197
|
-
|
|
5868
|
+
function producerIntervalFacts(reconcile, retry, timeout) {
|
|
5869
|
+
const facts = [];
|
|
5870
|
+
if (!boundedMilliseconds(reconcile)) facts.push({ name: "reconcile_interval_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
|
|
5871
|
+
if (!boundedMilliseconds(retry)) facts.push({ name: "retry_backoff_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
|
|
5872
|
+
if (!boundedMilliseconds(timeout)) facts.push({ name: "ipc_timeout_ms", value: "wrong_type", expected: "25..60000 milliseconds" });
|
|
5873
|
+
return facts;
|
|
5198
5874
|
}
|
|
5199
|
-
function
|
|
5875
|
+
function hasConfigKeys(value, required, tolerated = []) {
|
|
5876
|
+
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));
|
|
5877
|
+
}
|
|
5878
|
+
function isRecord4(value) {
|
|
5200
5879
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5201
5880
|
}
|
|
5202
|
-
function
|
|
5881
|
+
function positive(value, maximum) {
|
|
5203
5882
|
return Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= maximum;
|
|
5204
5883
|
}
|
|
5205
5884
|
function boundedMilliseconds(value) {
|
|
5206
|
-
return
|
|
5885
|
+
return positive(value, 6e4) && Number(value) >= 25;
|
|
5207
5886
|
}
|
|
5208
5887
|
|
|
5209
5888
|
// host/codex/src/codex-decoder.ts
|
|
5210
|
-
import { createHash as
|
|
5889
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
5211
5890
|
|
|
5212
5891
|
// host/codex/src/codex-types.ts
|
|
5213
5892
|
var CODEX_THREAD_ID_ENVIRONMENT = "CODEX_THREAD_ID";
|
|
5214
5893
|
var CODEX_OUTBOX_SCHEMA_VERSION = "ceal.codex_observation_outbox.v1";
|
|
5215
|
-
var CODEX_TURN_BINDING_SCHEMA_VERSION = "ceal.codex_turn_binding.v1";
|
|
5216
5894
|
var CODEX_SESSION_SCHEMA_VERSION = "ceal.codex_session.v1";
|
|
5217
5895
|
var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1";
|
|
5218
5896
|
var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
|
|
@@ -5228,20 +5906,20 @@ var CodexHostAdapterError = class extends Error {
|
|
|
5228
5906
|
};
|
|
5229
5907
|
|
|
5230
5908
|
// host/codex/src/codex-decoder.ts
|
|
5231
|
-
var
|
|
5909
|
+
var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
5232
5910
|
var MAX_PAYLOAD_BYTES = 512 * 1024;
|
|
5233
|
-
function observationRef(event, identity, value) {
|
|
5234
|
-
return cealHostObservationRef("codex", shortDigest({ event, identity, value }));
|
|
5911
|
+
function observationRef(hostSourceRef, event, identity, value) {
|
|
5912
|
+
return cealHostObservationRef("codex", shortDigest({ host_source_ref: hostSourceRef, event, identity, value }));
|
|
5235
5913
|
}
|
|
5236
5914
|
function hostSessionReference(sourceRef, sessionId) {
|
|
5237
5915
|
return `codex-session:${shortDigest({ source_ref: sourceRef, session_id: sessionId })}`;
|
|
5238
5916
|
}
|
|
5239
5917
|
function hostTurnReference(sourceRef, sessionRef, nativeTurnId) {
|
|
5240
|
-
if (![sourceRef, sessionRef, nativeTurnId].every((value) =>
|
|
5918
|
+
if (![sourceRef, sessionRef, nativeTurnId].every((value) => SAFE_REF4.test(value))) throw new CodexHostAdapterError("invalid_input", "Codex turn identity is invalid.");
|
|
5241
5919
|
return `codex-turn:${shortDigest({ source_ref: sourceRef, session_ref: sessionRef, native_turn_id: nativeTurnId })}`;
|
|
5242
5920
|
}
|
|
5243
5921
|
function hostInputReference(sourceRef, sessionRef, nativeTurnId, messageId, ordinal, prompt) {
|
|
5244
|
-
if (![sourceRef, sessionRef, nativeTurnId, messageId].every((value) =>
|
|
5922
|
+
if (![sourceRef, sessionRef, nativeTurnId, messageId].every((value) => SAFE_REF4.test(value)) || !Number.isSafeInteger(ordinal) || ordinal < 0) {
|
|
5245
5923
|
throw new CodexHostAdapterError("invalid_input", "Codex input identity is invalid.");
|
|
5246
5924
|
}
|
|
5247
5925
|
return `codex-input:${shortDigest({ source_ref: sourceRef, session_ref: sessionRef, native_turn_id: nativeTurnId, message_id: messageId, ordinal, prompt_sha256: sha256(prompt) })}`;
|
|
@@ -5253,25 +5931,24 @@ function turnObservationValue(sessionId, turnRef, status) {
|
|
|
5253
5931
|
return { status, host_session_id_sha256: sha256(sessionId), host_turn_ref: turnRef };
|
|
5254
5932
|
}
|
|
5255
5933
|
function sha256(value) {
|
|
5256
|
-
return
|
|
5934
|
+
return createHash6("sha256").update(value, "utf8").digest("hex");
|
|
5257
5935
|
}
|
|
5258
5936
|
function shortDigest(value) {
|
|
5259
5937
|
return cealRequestSha256(value);
|
|
5260
5938
|
}
|
|
5261
5939
|
|
|
5262
5940
|
// host/codex/src/codex-outbox.ts
|
|
5263
|
-
import {
|
|
5264
|
-
import {
|
|
5265
|
-
import { dirname, isAbsolute as isAbsolute2, join as join4 } from "node:path";
|
|
5941
|
+
import { mkdir as mkdir3, open as open4, readdir, rename as rename2, stat as stat3, unlink as unlink3 } from "node:fs/promises";
|
|
5942
|
+
import { dirname as dirname4, isAbsolute as isAbsolute3, join as join4 } from "node:path";
|
|
5266
5943
|
|
|
5267
5944
|
// host/invocation-context.ts
|
|
5268
|
-
var
|
|
5945
|
+
var HOST_INVOCATION_TURN_SELECTION_SCHEMA = "ceal.host_invocation_turn_selection.v1";
|
|
5269
5946
|
var HOST_INVOCATION_CONTEXT_REQUIRED_KEYS = ["schema_version", "identity", "prompt_ref", "expires_at"];
|
|
5270
5947
|
function invocationContextKey(identity) {
|
|
5271
5948
|
return cealRequestSha256({ host_source_ref: identity.host_source_ref, host_session_ref: identity.host_session_ref });
|
|
5272
5949
|
}
|
|
5273
5950
|
function hasHostInvocationIdentityKeys(value) {
|
|
5274
|
-
return
|
|
5951
|
+
return exactKeys3(value, CEAL_HOST_INVOCATION_IDENTITY_KEYS);
|
|
5275
5952
|
}
|
|
5276
5953
|
function hasHostInvocationContextKeys(value, optional = []) {
|
|
5277
5954
|
return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
|
|
@@ -5297,12 +5974,9 @@ function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor2)
|
|
|
5297
5974
|
return Object.freeze(context);
|
|
5298
5975
|
}
|
|
5299
5976
|
function isHostInvocationTurnSelection(value) {
|
|
5300
|
-
return
|
|
5977
|
+
return isRecord5(value) && Object.keys(value).length === 5 && value.schema_version === HOST_INVOCATION_TURN_SELECTION_SCHEMA && ["host_source_ref", "host_session_ref", "prompt_ref", "turn_generation_ref"].every((key) => safeRef2(value[key]));
|
|
5301
5978
|
}
|
|
5302
|
-
function
|
|
5303
|
-
return selection === null || context !== null && selection.host_source_ref === context.identity.host_source_ref && selection.host_session_ref === context.identity.host_session_ref && selection.prompt_ref === context.prompt_ref && selection.turn_generation_ref === context.turn_generation_ref;
|
|
5304
|
-
}
|
|
5305
|
-
function exactKeys4(value, keys) {
|
|
5979
|
+
function exactKeys3(value, keys) {
|
|
5306
5980
|
const actual = Object.keys(value);
|
|
5307
5981
|
return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
|
|
5308
5982
|
}
|
|
@@ -5311,10 +5985,10 @@ function requiredKeys(value, required, optional) {
|
|
|
5311
5985
|
const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
|
|
5312
5986
|
return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
|
|
5313
5987
|
}
|
|
5314
|
-
function
|
|
5988
|
+
function isRecord5(value) {
|
|
5315
5989
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5316
5990
|
}
|
|
5317
|
-
function
|
|
5991
|
+
function safeRef2(value) {
|
|
5318
5992
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
5319
5993
|
}
|
|
5320
5994
|
|
|
@@ -5330,59 +6004,40 @@ function validateCodexHostInvocationContext(value) {
|
|
|
5330
6004
|
validateContextReferences(value);
|
|
5331
6005
|
validateContextTimes(value);
|
|
5332
6006
|
}
|
|
5333
|
-
function invocationContextFromBinding(binding, turnGenerationRef) {
|
|
5334
|
-
const context = {
|
|
5335
|
-
schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
|
|
5336
|
-
identity: binding.identity,
|
|
5337
|
-
prompt_ref: binding.prompt_ref,
|
|
5338
|
-
request_ref: binding.request_ref,
|
|
5339
|
-
bound_at: binding.bound_at,
|
|
5340
|
-
expires_at: binding.expires_at,
|
|
5341
|
-
...turnGenerationRef === void 0 ? {} : { turn_generation_ref: turnGenerationRef }
|
|
5342
|
-
};
|
|
5343
|
-
validateCodexHostInvocationContext(context);
|
|
5344
|
-
return Object.freeze(context);
|
|
5345
|
-
}
|
|
5346
6007
|
function validateIdentity2(value) {
|
|
5347
|
-
if (!
|
|
6008
|
+
if (!isRecord6(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef3)) {
|
|
5348
6009
|
throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
|
|
5349
6010
|
}
|
|
5350
6011
|
}
|
|
5351
6012
|
function isContextShape(value) {
|
|
5352
|
-
return
|
|
6013
|
+
return isRecord6(value) && hasHostInvocationContextKeys(value, ["turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
|
|
5353
6014
|
}
|
|
5354
6015
|
function validateContextReferences(value) {
|
|
5355
|
-
if (!
|
|
6016
|
+
if (!safeRef3(value.prompt_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
5356
6017
|
}
|
|
5357
6018
|
function validateContextTimes(value) {
|
|
5358
6019
|
const expiresAt = value.expires_at;
|
|
5359
|
-
|
|
5360
|
-
if (!timestamp2(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
5361
|
-
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}`)}).`);
|
|
6020
|
+
if (!timestamp(expiresAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
5362
6021
|
}
|
|
5363
6022
|
function optionalSafeRef(value) {
|
|
5364
|
-
return value === void 0 ||
|
|
5365
|
-
}
|
|
5366
|
-
function optionalTimestamp(value) {
|
|
5367
|
-
return value === void 0 || timestamp2(value);
|
|
6023
|
+
return value === void 0 || safeRef3(value);
|
|
5368
6024
|
}
|
|
5369
|
-
function
|
|
6025
|
+
function safeRef3(value) {
|
|
5370
6026
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
5371
6027
|
}
|
|
5372
|
-
function
|
|
6028
|
+
function timestamp(value) {
|
|
5373
6029
|
return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
5374
6030
|
}
|
|
5375
|
-
function
|
|
6031
|
+
function isRecord6(value) {
|
|
5376
6032
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5377
6033
|
}
|
|
5378
6034
|
|
|
5379
6035
|
// host/codex/src/codex-outbox.ts
|
|
5380
|
-
var
|
|
6036
|
+
var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
5381
6037
|
var CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS = 4096;
|
|
5382
6038
|
var CodexOutboxStore = class {
|
|
5383
6039
|
directory;
|
|
5384
6040
|
entriesDirectory;
|
|
5385
|
-
bindingsDirectory;
|
|
5386
6041
|
sessionsDirectory;
|
|
5387
6042
|
activeContextsDirectory;
|
|
5388
6043
|
turnSelectionsDirectory;
|
|
@@ -5392,7 +6047,6 @@ var CodexOutboxStore = class {
|
|
|
5392
6047
|
if (typeof directory !== "string" || directory.length === 0) throw new CodexHostAdapterError("configuration", "Codex outbox directory is required.");
|
|
5393
6048
|
this.directory = directory;
|
|
5394
6049
|
this.entriesDirectory = join4(directory, "entries");
|
|
5395
|
-
this.bindingsDirectory = join4(directory, "bindings");
|
|
5396
6050
|
this.sessionsDirectory = join4(directory, "sessions");
|
|
5397
6051
|
this.activeContextsDirectory = join4(directory, "active-contexts");
|
|
5398
6052
|
this.turnSelectionsDirectory = join4(directory, "turn-selections");
|
|
@@ -5400,8 +6054,8 @@ var CodexOutboxStore = class {
|
|
|
5400
6054
|
this.cursorsDirectory = join4(directory, "transcript-cursors");
|
|
5401
6055
|
}
|
|
5402
6056
|
async prepare() {
|
|
5403
|
-
for (const path of [this.directory, this.entriesDirectory, this.
|
|
5404
|
-
await
|
|
6057
|
+
for (const path of [this.directory, this.entriesDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
|
|
6058
|
+
await ensureOwnerDirectory(path);
|
|
5405
6059
|
}
|
|
5406
6060
|
}
|
|
5407
6061
|
async put(entry) {
|
|
@@ -5416,7 +6070,7 @@ var CodexOutboxStore = class {
|
|
|
5416
6070
|
for (const name of await readdir(this.entriesDirectory)) {
|
|
5417
6071
|
if (!name.endsWith(".json")) continue;
|
|
5418
6072
|
try {
|
|
5419
|
-
const value = JSON.parse(await
|
|
6073
|
+
const value = JSON.parse(await readOwnerStateFile(join4(this.entriesDirectory, name)));
|
|
5420
6074
|
validateEnvelope(value);
|
|
5421
6075
|
entries.push(value);
|
|
5422
6076
|
} catch {
|
|
@@ -5429,16 +6083,11 @@ var CodexOutboxStore = class {
|
|
|
5429
6083
|
async remove(entry) {
|
|
5430
6084
|
validateEnvelope(entry);
|
|
5431
6085
|
try {
|
|
5432
|
-
await
|
|
6086
|
+
await unlink3(this.entryPath(entry.submission_ref));
|
|
5433
6087
|
} catch (error) {
|
|
5434
6088
|
if (!isNotFound(error)) throw storageError("Unable to remove the Codex outbox entry.");
|
|
5435
6089
|
}
|
|
5436
6090
|
}
|
|
5437
|
-
async writeBinding(binding) {
|
|
5438
|
-
validateBinding(binding);
|
|
5439
|
-
await this.prepare();
|
|
5440
|
-
await writeAtomicJson(this.bindingPath(binding.identity), binding);
|
|
5441
|
-
}
|
|
5442
6091
|
/**
|
|
5443
6092
|
* Select the exact Host turn that model-reachable commands may use. A new
|
|
5444
6093
|
* prompt replaces this projection, so it cannot inherit the prior turn's
|
|
@@ -5452,7 +6101,7 @@ var CodexOutboxStore = class {
|
|
|
5452
6101
|
await writeAtomicJson(path, context);
|
|
5453
6102
|
} catch (error) {
|
|
5454
6103
|
try {
|
|
5455
|
-
await
|
|
6104
|
+
await unlink3(path);
|
|
5456
6105
|
} catch (unlinkError) {
|
|
5457
6106
|
if (!isNotFound(unlinkError)) throw storageError("Unable to clear a failed Codex Host context replacement.");
|
|
5458
6107
|
}
|
|
@@ -5465,7 +6114,7 @@ var CodexOutboxStore = class {
|
|
|
5465
6114
|
await writeAtomicJson(this.turnSelectionPath(selection.host_source_ref, selection.host_session_ref), selection);
|
|
5466
6115
|
}
|
|
5467
6116
|
async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
|
|
5468
|
-
if (!
|
|
6117
|
+
if (!SAFE_REF5.test(hostSourceRef) || !SAFE_REF5.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
|
|
5469
6118
|
await this.prepare();
|
|
5470
6119
|
return readOptionalJson(this.turnSelectionPath(hostSourceRef, hostSessionRef), validateTurnSelection);
|
|
5471
6120
|
}
|
|
@@ -5478,23 +6127,11 @@ var CodexOutboxStore = class {
|
|
|
5478
6127
|
await writeAtomicJson(path, { ...active, turn_generation_ref: turnGenerationRef });
|
|
5479
6128
|
return true;
|
|
5480
6129
|
}
|
|
5481
|
-
/**
|
|
5482
|
-
* Publish a Request reference only while the acknowledged binding is still
|
|
5483
|
-
* the active turn. A delayed relay response therefore cannot overwrite a
|
|
5484
|
-
* newer prompt's context.
|
|
5485
|
-
*/
|
|
5486
|
-
async bindInvocationContext(binding, now = /* @__PURE__ */ new Date()) {
|
|
5487
|
-
validateBinding(binding);
|
|
5488
|
-
const active = await this.readInvocationContext(binding.identity.host_source_ref, binding.identity.host_session_ref, now);
|
|
5489
|
-
if (active === null || !sameIdentity(active.identity, binding.identity)) return false;
|
|
5490
|
-
await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding, active.turn_generation_ref));
|
|
5491
|
-
return true;
|
|
5492
|
-
}
|
|
5493
6130
|
/** Return only the live session projection; never fall back to another turn. */
|
|
5494
6131
|
async readInvocationContext(hostSourceRef, hostSessionRef, now = /* @__PURE__ */ new Date()) {
|
|
5495
6132
|
if (!(now instanceof Date) || !Number.isFinite(now.getTime())) throw new CodexHostAdapterError("invalid_input", "Codex Host context clock is invalid.");
|
|
5496
6133
|
const context = await this.readRawInvocationContext(hostSourceRef, hostSessionRef);
|
|
5497
|
-
return isLiveSessionProjection(context, now)
|
|
6134
|
+
return isLiveSessionProjection(context, now) ? context : null;
|
|
5498
6135
|
}
|
|
5499
6136
|
/**
|
|
5500
6137
|
* The latest Codex session projection even when past expiry, so the
|
|
@@ -5502,10 +6139,10 @@ var CodexOutboxStore = class {
|
|
|
5502
6139
|
* a missing one. Every other caller keeps the fail-closed read above.
|
|
5503
6140
|
*/
|
|
5504
6141
|
async readRawInvocationContext(hostSourceRef, hostSessionRef) {
|
|
5505
|
-
if (!
|
|
6142
|
+
if (!SAFE_REF5.test(hostSourceRef) || !SAFE_REF5.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
|
|
5506
6143
|
await this.prepare();
|
|
5507
6144
|
const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, (file) => readOptionalJson(file, validateCodexHostInvocationContext));
|
|
5508
|
-
return
|
|
6145
|
+
return context;
|
|
5509
6146
|
}
|
|
5510
6147
|
/** Clear the projection only if the caller still owns its exact active turn. */
|
|
5511
6148
|
async clearInvocationContext(identity, now = /* @__PURE__ */ new Date()) {
|
|
@@ -5513,17 +6150,12 @@ var CodexOutboxStore = class {
|
|
|
5513
6150
|
const active = await this.readInvocationContext(identity.host_source_ref, identity.host_session_ref, now);
|
|
5514
6151
|
if (active === null || !sameIdentity(active.identity, identity)) return false;
|
|
5515
6152
|
try {
|
|
5516
|
-
await
|
|
6153
|
+
await unlink3(this.activeContextPath(identity));
|
|
5517
6154
|
} catch (error) {
|
|
5518
6155
|
if (!isNotFound(error)) throw storageError("Unable to clear the Codex Host invocation context.");
|
|
5519
6156
|
}
|
|
5520
6157
|
return true;
|
|
5521
6158
|
}
|
|
5522
|
-
async readBinding(identity) {
|
|
5523
|
-
validateIdentity3(identity);
|
|
5524
|
-
await this.prepare();
|
|
5525
|
-
return readOptionalJson(this.bindingPath(identity), validateBinding);
|
|
5526
|
-
}
|
|
5527
6159
|
async saveSession(snapshot) {
|
|
5528
6160
|
validateSession(snapshot);
|
|
5529
6161
|
await this.prepare();
|
|
@@ -5531,7 +6163,7 @@ var CodexOutboxStore = class {
|
|
|
5531
6163
|
}
|
|
5532
6164
|
async readSession(sourceRef, sessionRef) {
|
|
5533
6165
|
const identity = { host_source_ref: sourceRef, host_session_ref: sessionRef };
|
|
5534
|
-
if (!
|
|
6166
|
+
if (!SAFE_REF5.test(sourceRef) || !SAFE_REF5.test(sessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex session identity is invalid.");
|
|
5535
6167
|
await this.prepare();
|
|
5536
6168
|
return readOptionalJson(this.sessionPath(identity.host_source_ref, identity.host_session_ref), validateSession);
|
|
5537
6169
|
}
|
|
@@ -5564,7 +6196,7 @@ var CodexOutboxStore = class {
|
|
|
5564
6196
|
for (const name of await readdir(this.registrationsDirectory)) {
|
|
5565
6197
|
if (!name.endsWith(".json")) continue;
|
|
5566
6198
|
try {
|
|
5567
|
-
const value = JSON.parse(await
|
|
6199
|
+
const value = JSON.parse(await readOwnerStateFile(join4(this.registrationsDirectory, name)));
|
|
5568
6200
|
validateRegistration(value);
|
|
5569
6201
|
registrations.push(value);
|
|
5570
6202
|
} catch {
|
|
@@ -5583,34 +6215,23 @@ var CodexOutboxStore = class {
|
|
|
5583
6215
|
await writeAtomicJson(this.cursorPath(cursor.host_source_ref, cursor.host_session_ref), cursor);
|
|
5584
6216
|
}
|
|
5585
6217
|
async findByAlias(sourceRef, sessionRef, alias) {
|
|
5586
|
-
if (!
|
|
6218
|
+
if (!SAFE_REF5.test(sourceRef) || !SAFE_REF5.test(sessionRef) || !SAFE_REF5.test(alias)) return null;
|
|
5587
6219
|
const read = await this.list();
|
|
5588
6220
|
const envelope = findEnvelopeByAlias(read.entries, sourceRef, sessionRef, alias);
|
|
5589
6221
|
if (envelope !== null) return envelopeMatch(envelope);
|
|
5590
6222
|
await this.prepare();
|
|
5591
|
-
const binding = await findBindingByAlias(this.bindingsDirectory, sourceRef, sessionRef, alias);
|
|
5592
|
-
if (binding !== null) return bindingMatch(binding);
|
|
5593
6223
|
return null;
|
|
5594
6224
|
}
|
|
5595
6225
|
async hasPriorTurn(sourceRef, sessionRef) {
|
|
5596
|
-
if (!
|
|
6226
|
+
if (!SAFE_REF5.test(sourceRef) || !SAFE_REF5.test(sessionRef)) return false;
|
|
5597
6227
|
const read = await this.list();
|
|
5598
6228
|
if (read.entries.some((entry) => entry.identity.host_source_ref === sourceRef && entry.identity.host_session_ref === sessionRef)) return true;
|
|
5599
|
-
await this.prepare();
|
|
5600
|
-
for (const name of await readdir(this.bindingsDirectory)) {
|
|
5601
|
-
if (!name.endsWith(".json")) continue;
|
|
5602
|
-
const binding = await readBindingCandidate(this.bindingsDirectory, name);
|
|
5603
|
-
if (binding?.identity.host_source_ref === sourceRef && binding.identity.host_session_ref === sessionRef) return true;
|
|
5604
|
-
}
|
|
5605
6229
|
return false;
|
|
5606
6230
|
}
|
|
5607
6231
|
entryPath(submissionRef) {
|
|
5608
|
-
if (!
|
|
6232
|
+
if (!SAFE_REF5.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
|
|
5609
6233
|
return join4(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
|
|
5610
6234
|
}
|
|
5611
|
-
bindingPath(identity) {
|
|
5612
|
-
return join4(this.bindingsDirectory, `${identityKey(identity)}.json`);
|
|
5613
|
-
}
|
|
5614
6235
|
sessionPath(sourceRef, sessionRef) {
|
|
5615
6236
|
return join4(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
|
|
5616
6237
|
}
|
|
@@ -5629,20 +6250,17 @@ var CodexOutboxStore = class {
|
|
|
5629
6250
|
};
|
|
5630
6251
|
async function removeOwnerState(path, what) {
|
|
5631
6252
|
try {
|
|
5632
|
-
await
|
|
6253
|
+
await unlink3(path);
|
|
5633
6254
|
} catch (error) {
|
|
5634
6255
|
if (!isNotFound(error)) throw storageError(`Unable to retire the Codex transcript ${what}.`);
|
|
5635
6256
|
}
|
|
5636
6257
|
}
|
|
5637
6258
|
function sessionStateKey(sourceRef, sessionRef) {
|
|
5638
|
-
if (!
|
|
6259
|
+
if (!SAFE_REF5.test(sourceRef) || !SAFE_REF5.test(sessionRef)) throw storageError("Codex transcript session identity is malformed.");
|
|
5639
6260
|
return cealRequestSha256({ host_source_ref: sourceRef, host_session_ref: sessionRef });
|
|
5640
6261
|
}
|
|
5641
6262
|
function envelopeMatch(envelope) {
|
|
5642
|
-
return { identity: envelope.identity, prompt_ref: envelope.prompt_ref,
|
|
5643
|
-
}
|
|
5644
|
-
function bindingMatch(binding) {
|
|
5645
|
-
return { identity: binding.identity, prompt_ref: binding.prompt_ref, request_ref: binding.request_ref, binding };
|
|
6263
|
+
return { identity: envelope.identity, prompt_ref: envelope.prompt_ref, envelope };
|
|
5646
6264
|
}
|
|
5647
6265
|
function findEnvelopeByAlias(entries, sourceRef, sessionRef, alias) {
|
|
5648
6266
|
for (const envelope of entries) {
|
|
@@ -5651,24 +6269,6 @@ function findEnvelopeByAlias(entries, sourceRef, sessionRef, alias) {
|
|
|
5651
6269
|
}
|
|
5652
6270
|
return null;
|
|
5653
6271
|
}
|
|
5654
|
-
async function findBindingByAlias(directory, sourceRef, sessionRef, alias) {
|
|
5655
|
-
for (const name of await readdir(directory)) {
|
|
5656
|
-
if (!name.endsWith(".json")) continue;
|
|
5657
|
-
const binding = await readBindingCandidate(directory, name);
|
|
5658
|
-
if (binding === null || binding.identity.host_source_ref !== sourceRef || binding.identity.host_session_ref !== sessionRef) continue;
|
|
5659
|
-
if (binding.codex_turn_ids.includes(alias)) return binding;
|
|
5660
|
-
}
|
|
5661
|
-
return null;
|
|
5662
|
-
}
|
|
5663
|
-
async function readBindingCandidate(directory, name) {
|
|
5664
|
-
try {
|
|
5665
|
-
const value = JSON.parse(await readPrivateFile(join4(directory, name)));
|
|
5666
|
-
validateBinding(value);
|
|
5667
|
-
return value;
|
|
5668
|
-
} catch {
|
|
5669
|
-
return null;
|
|
5670
|
-
}
|
|
5671
|
-
}
|
|
5672
6272
|
function identityKey(identity) {
|
|
5673
6273
|
validateIdentity3(identity);
|
|
5674
6274
|
return cealRequestSha256(identity);
|
|
@@ -5681,18 +6281,18 @@ async function writeAtomicJson(path, value) {
|
|
|
5681
6281
|
if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
|
|
5682
6282
|
const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
5683
6283
|
try {
|
|
5684
|
-
const handle = await
|
|
6284
|
+
const handle = await open4(temp, "wx");
|
|
5685
6285
|
try {
|
|
5686
6286
|
await handle.writeFile(text, "utf8");
|
|
5687
6287
|
await handle.sync();
|
|
5688
6288
|
} finally {
|
|
5689
6289
|
await handle.close();
|
|
5690
6290
|
}
|
|
5691
|
-
await
|
|
5692
|
-
await syncDirectory(
|
|
6291
|
+
await rename2(temp, path);
|
|
6292
|
+
await syncDirectory(dirname4(path));
|
|
5693
6293
|
} catch (error) {
|
|
5694
6294
|
try {
|
|
5695
|
-
await
|
|
6295
|
+
await unlink3(temp);
|
|
5696
6296
|
} catch {
|
|
5697
6297
|
}
|
|
5698
6298
|
if (error instanceof CodexHostAdapterError) throw error;
|
|
@@ -5701,7 +6301,7 @@ async function writeAtomicJson(path, value) {
|
|
|
5701
6301
|
}
|
|
5702
6302
|
async function syncDirectory(path) {
|
|
5703
6303
|
try {
|
|
5704
|
-
const handle = await
|
|
6304
|
+
const handle = await open4(path, "r");
|
|
5705
6305
|
try {
|
|
5706
6306
|
await handle.sync();
|
|
5707
6307
|
} finally {
|
|
@@ -5712,7 +6312,7 @@ async function syncDirectory(path) {
|
|
|
5712
6312
|
}
|
|
5713
6313
|
async function readOptionalJson(path, validate) {
|
|
5714
6314
|
try {
|
|
5715
|
-
const value = JSON.parse(await
|
|
6315
|
+
const value = JSON.parse(await readOwnerStateFile(path));
|
|
5716
6316
|
validate(value);
|
|
5717
6317
|
return value;
|
|
5718
6318
|
} catch (error) {
|
|
@@ -5721,36 +6321,30 @@ async function readOptionalJson(path, validate) {
|
|
|
5721
6321
|
throw storageError("Codex owner state is malformed.");
|
|
5722
6322
|
}
|
|
5723
6323
|
}
|
|
5724
|
-
async function
|
|
6324
|
+
async function ensureOwnerDirectory(path) {
|
|
5725
6325
|
try {
|
|
5726
|
-
await
|
|
6326
|
+
await stat3(path);
|
|
5727
6327
|
} catch (error) {
|
|
5728
6328
|
if (!isNotFound(error)) throw storageError("Codex owner state directory is unavailable.");
|
|
5729
|
-
await mkdir3(path, { recursive: true
|
|
6329
|
+
await mkdir3(path, { recursive: true });
|
|
5730
6330
|
}
|
|
5731
|
-
const stats = await
|
|
5732
|
-
|
|
5733
|
-
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)")}).`);
|
|
5734
|
-
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")}).`);
|
|
5735
|
-
if (stats.uid !== uid) throw storageError(`Codex owner state directory is owned by another user (${refusalFact("uid", stats.uid, `expected ${uid}`)}).`);
|
|
6331
|
+
const stats = await stat3(path);
|
|
6332
|
+
if (!stats.isDirectory()) throw storageError("Codex owner state path is not a directory.");
|
|
5736
6333
|
}
|
|
5737
|
-
async function
|
|
6334
|
+
async function readOwnerStateFile(path) {
|
|
5738
6335
|
let handle;
|
|
5739
6336
|
try {
|
|
5740
|
-
handle = await
|
|
6337
|
+
handle = await open4(path);
|
|
5741
6338
|
const stats = await handle.stat();
|
|
5742
|
-
|
|
5743
|
-
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)")}).`);
|
|
5744
|
-
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")}).`);
|
|
5745
|
-
if (stats.uid !== uid) throw storageError(`Codex owner state file is owned by another user (${refusalFact("uid", stats.uid, `expected ${uid}`)}).`);
|
|
6339
|
+
if (!stats.isFile()) throw storageError("Codex owner state path is not a regular file.");
|
|
5746
6340
|
return await handle.readFile("utf8");
|
|
5747
6341
|
} finally {
|
|
5748
6342
|
await handle?.close();
|
|
5749
6343
|
}
|
|
5750
6344
|
}
|
|
5751
6345
|
function validateEnvelope(value) {
|
|
5752
|
-
if (!
|
|
5753
|
-
if (!sameKeys(value, ["schema_version", "identity", "host_input_ref", "
|
|
6346
|
+
if (!isRecord7(value)) throw storageError("Codex outbox entry is malformed.");
|
|
6347
|
+
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")}).`);
|
|
5754
6348
|
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}`)}).`);
|
|
5755
6349
|
validateIdentity3(value.identity);
|
|
5756
6350
|
validateEnvelopeReferences(value);
|
|
@@ -5758,32 +6352,30 @@ function validateEnvelope(value) {
|
|
|
5758
6352
|
validateEnvelopeTimestamps(value);
|
|
5759
6353
|
}
|
|
5760
6354
|
function validateEnvelopeReferences(value) {
|
|
5761
|
-
if (!
|
|
5762
|
-
if (!
|
|
5763
|
-
if (!
|
|
5764
|
-
if (!
|
|
5765
|
-
if (value.request_ref !== void 0 && !SAFE_REF6.test(String(value.request_ref))) throw storageError("Codex outbox Request reference is malformed.");
|
|
5766
|
-
if (value.codex_turn_id !== void 0 && !SAFE_REF6.test(String(value.codex_turn_id))) throw storageError("Codex outbox turn identity is malformed.");
|
|
6355
|
+
if (!SAFE_REF5.test(String(value.host_input_ref))) throw storageError("Codex outbox input reference is malformed.");
|
|
6356
|
+
if (!SAFE_REF5.test(String(value.prompt_ref))) throw storageError("Codex outbox references are malformed.");
|
|
6357
|
+
if (!SAFE_REF5.test(String(value.submission_ref))) throw storageError("Codex outbox references are malformed.");
|
|
6358
|
+
if (value.codex_turn_id !== void 0 && !SAFE_REF5.test(String(value.codex_turn_id))) throw storageError("Codex outbox turn identity is malformed.");
|
|
5767
6359
|
if (typeof value.origin_input !== "boolean") throw storageError("Codex outbox origin disposition is malformed.");
|
|
5768
6360
|
}
|
|
5769
6361
|
function validateRegistration(value) {
|
|
5770
|
-
if (!
|
|
6362
|
+
if (!isRecord7(value)) throw storageError("Codex transcript registration is malformed.");
|
|
5771
6363
|
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")}).`);
|
|
5772
6364
|
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}`)}).`);
|
|
5773
6365
|
validateRegistrationFields(value);
|
|
5774
6366
|
}
|
|
5775
6367
|
function validateRegistrationFields(value) {
|
|
5776
|
-
if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" &&
|
|
6368
|
+
if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" && SAFE_REF5.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 }])}).`);
|
|
5777
6369
|
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`)}).`);
|
|
5778
6370
|
if (typeof value.transcript_path !== "string") throw storageError(`Codex transcript registration path is malformed (${refusalFact("kind", typeof value.transcript_path, "string")}).`);
|
|
5779
6371
|
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}`)}).`);
|
|
5780
6372
|
if (!isTimestamp(value.registered_at) || !isTimestamp(value.expires_at)) throw storageError(`Codex transcript registration timestamps are malformed (${refusalFacts([{ name: "registered_at", value: value.registered_at }, { name: "expires_at", value: value.expires_at }])}).`);
|
|
5781
6373
|
}
|
|
5782
6374
|
function validRegistrationCwd(value) {
|
|
5783
|
-
return value === void 0 || typeof value === "string" &&
|
|
6375
|
+
return value === void 0 || typeof value === "string" && isAbsolute3(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
|
|
5784
6376
|
}
|
|
5785
6377
|
function validateCursor(value) {
|
|
5786
|
-
if (!
|
|
6378
|
+
if (!isRecord7(value)) throw storageError("Codex transcript cursor is malformed.");
|
|
5787
6379
|
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")}).`);
|
|
5788
6380
|
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}`)}).`);
|
|
5789
6381
|
if (!validCursorSource(value) || !validCursorPosition(value) || !validCursorStatus(value)) throw storageError("Codex transcript cursor fields are malformed.");
|
|
@@ -5791,7 +6383,7 @@ function validateCursor(value) {
|
|
|
5791
6383
|
if (value.reason !== void 0 && !isHostTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
|
|
5792
6384
|
}
|
|
5793
6385
|
function validCursorSource(value) {
|
|
5794
|
-
return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" &&
|
|
6386
|
+
return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" && SAFE_REF5.test(candidate)) && typeof value.transcript_path === "string" && value.transcript_path.length > 0 && typeof value.device === "string" && typeof value.inode === "string";
|
|
5795
6387
|
}
|
|
5796
6388
|
function validCursorPosition(value) {
|
|
5797
6389
|
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";
|
|
@@ -5800,23 +6392,23 @@ function validCursorStatus(value) {
|
|
|
5800
6392
|
return (value.status === "active" || value.status === "quarantined") && isTimestamp(value.updated_at);
|
|
5801
6393
|
}
|
|
5802
6394
|
function validActiveTask(value) {
|
|
5803
|
-
if (!
|
|
6395
|
+
if (!isRecord7(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;
|
|
5804
6396
|
return validActiveTaskIdentity(value) && validActiveTaskReferences(value) && validActiveTaskUsage(value);
|
|
5805
6397
|
}
|
|
5806
6398
|
function validActiveTaskIdentity(value) {
|
|
5807
|
-
return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" &&
|
|
6399
|
+
return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" && SAFE_REF5.test(candidate)) && isTimestamp(value.started_at);
|
|
5808
6400
|
}
|
|
5809
6401
|
function validActiveTaskReferences(value) {
|
|
5810
6402
|
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);
|
|
5811
6403
|
}
|
|
5812
6404
|
function optionalSafeRef2(value) {
|
|
5813
|
-
return value === void 0 || typeof value === "string" &&
|
|
6405
|
+
return value === void 0 || typeof value === "string" && SAFE_REF5.test(value);
|
|
5814
6406
|
}
|
|
5815
6407
|
function validActiveTaskUsage(value) {
|
|
5816
6408
|
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");
|
|
5817
6409
|
}
|
|
5818
6410
|
function validTokenUsage(value) {
|
|
5819
|
-
if (!
|
|
6411
|
+
if (!isRecord7(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
|
|
5820
6412
|
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;
|
|
5821
6413
|
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);
|
|
5822
6414
|
}
|
|
@@ -5826,7 +6418,7 @@ function nonNegativeInteger(value) {
|
|
|
5826
6418
|
function validateEnvelopeObservations(value) {
|
|
5827
6419
|
if (!Array.isArray(value.observations)) throw storageError("Codex outbox observations are malformed.");
|
|
5828
6420
|
try {
|
|
5829
|
-
for (const
|
|
6421
|
+
for (const observation of value.observations) validateCealHostObservationDraft(observation);
|
|
5830
6422
|
} catch {
|
|
5831
6423
|
throw storageError("Codex outbox observations are malformed.");
|
|
5832
6424
|
}
|
|
@@ -5835,34 +6427,23 @@ function validateEnvelopeTimestamps(value) {
|
|
|
5835
6427
|
if (!isTimestamp(value.created_at)) throw storageError("Codex outbox timestamps are malformed.");
|
|
5836
6428
|
if (!isTimestamp(value.expires_at)) throw storageError("Codex outbox timestamps are malformed.");
|
|
5837
6429
|
}
|
|
5838
|
-
function validateBinding(value) {
|
|
5839
|
-
if (!isRecord5(value)) throw storageError("Codex turn binding is malformed.");
|
|
5840
|
-
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")}).`);
|
|
5841
|
-
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}`)}).`);
|
|
5842
|
-
validateIdentity3(value.identity);
|
|
5843
|
-
if (!SAFE_REF6.test(String(value.prompt_ref)) || !SAFE_REF6.test(String(value.request_ref))) throw storageError("Codex turn binding references are malformed.");
|
|
5844
|
-
if (!isStringList(value.codex_turn_ids) || !isTimestamp(value.bound_at) || !isTimestamp(value.expires_at)) throw storageError("Codex turn binding fields are malformed.");
|
|
5845
|
-
}
|
|
5846
6430
|
function validateSession(value) {
|
|
5847
|
-
if (!
|
|
6431
|
+
if (!isRecord7(value)) throw storageError("Codex session snapshot is malformed.");
|
|
5848
6432
|
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")}).`);
|
|
5849
6433
|
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}`)}).`);
|
|
5850
|
-
if (!
|
|
6434
|
+
if (!SAFE_REF5.test(String(value.host_session_ref)) || !SAFE_REF5.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.");
|
|
5851
6435
|
}
|
|
5852
6436
|
function validateTurnSelection(value) {
|
|
5853
6437
|
if (!isHostInvocationTurnSelection(value)) throw storageError("Codex Host turn selection is malformed.");
|
|
5854
6438
|
}
|
|
5855
6439
|
function validateIdentity3(value) {
|
|
5856
|
-
if (!
|
|
6440
|
+
if (!isRecord7(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every((candidate) => typeof candidate === "string" && SAFE_REF5.test(candidate))) throw storageError("Codex Host identity is malformed.");
|
|
5857
6441
|
}
|
|
5858
6442
|
function isJsonValue(value) {
|
|
5859
6443
|
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
5860
6444
|
if (typeof value === "number") return Number.isFinite(value);
|
|
5861
6445
|
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
5862
|
-
return
|
|
5863
|
-
}
|
|
5864
|
-
function isStringList(value) {
|
|
5865
|
-
return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" && SAFE_REF6.test(candidate));
|
|
6446
|
+
return isRecord7(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
|
|
5866
6447
|
}
|
|
5867
6448
|
function isTimestamp(value) {
|
|
5868
6449
|
return typeof value === "string" && value.length <= 64 && !Number.isNaN(Date.parse(value));
|
|
@@ -5872,142 +6453,43 @@ function sameKeys(value, required, optional = []) {
|
|
|
5872
6453
|
const keys = Object.keys(value);
|
|
5873
6454
|
return keys.length >= required.length && keys.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
|
|
5874
6455
|
}
|
|
5875
|
-
function
|
|
6456
|
+
function isRecord7(value) {
|
|
5876
6457
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5877
6458
|
}
|
|
5878
6459
|
function isNotFound(error) {
|
|
5879
|
-
return
|
|
6460
|
+
return isRecord7(error) && error.code === "ENOENT";
|
|
5880
6461
|
}
|
|
5881
6462
|
function storageError(message) {
|
|
5882
6463
|
return new CodexHostAdapterError("storage", message);
|
|
5883
6464
|
}
|
|
5884
6465
|
|
|
5885
6466
|
// host/codex/src/codex-producer.ts
|
|
5886
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
5887
|
-
var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
5888
6467
|
var CodexProducer = class {
|
|
5889
|
-
producer_id = "codex";
|
|
5890
|
-
producer_label = "Codex";
|
|
5891
|
-
outbox_directory;
|
|
5892
6468
|
store;
|
|
5893
6469
|
core;
|
|
5894
6470
|
constructor(options) {
|
|
5895
6471
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
5896
|
-
this.outbox_directory = this.store.directory;
|
|
5897
6472
|
this.core = new HostProducerDrainCore(this, options);
|
|
5898
6473
|
}
|
|
5899
6474
|
drainOutbox() {
|
|
5900
6475
|
return this.core.drainOutbox();
|
|
5901
6476
|
}
|
|
5902
|
-
fail(code, message) {
|
|
5903
|
-
return new CodexHostAdapterError(code, message);
|
|
5904
|
-
}
|
|
5905
6477
|
listEnvelopes() {
|
|
5906
6478
|
return this.store.list();
|
|
5907
6479
|
}
|
|
5908
6480
|
removeEnvelope(envelope) {
|
|
5909
6481
|
return this.store.remove(envelope);
|
|
5910
6482
|
}
|
|
5911
|
-
submissionRefOf(envelope) {
|
|
5912
|
-
return envelope.submission_ref;
|
|
5913
|
-
}
|
|
5914
6483
|
expiresAtOf(envelope) {
|
|
5915
6484
|
return envelope.expires_at;
|
|
5916
6485
|
}
|
|
5917
|
-
createdAtOf(envelope) {
|
|
5918
|
-
return envelope.created_at;
|
|
5919
|
-
}
|
|
5920
|
-
async mapEnvelope(envelope) {
|
|
5921
|
-
const prompt = observation(envelope, "prompt");
|
|
5922
|
-
if (prompt !== void 0) return this.mapInput(envelope, prompt);
|
|
5923
|
-
return [mapTerminal(envelope)];
|
|
5924
|
-
}
|
|
5925
|
-
async mapInput(envelope, prompt) {
|
|
5926
|
-
const drafts = [];
|
|
5927
|
-
const session = observation(envelope, "session");
|
|
5928
|
-
if (session !== void 0) {
|
|
5929
|
-
const registrations = await this.store.listTranscriptRegistrations();
|
|
5930
|
-
const registration = registrations.find((item) => item.host_source_ref === envelope.identity.host_source_ref && item.host_session_ref === envelope.identity.host_session_ref);
|
|
5931
|
-
const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
|
|
5932
|
-
drafts.push({ frame_kind: "session_started", payload: compact({
|
|
5933
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
5934
|
-
working_directory_ref: `workspace:${createHash6("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
|
|
5935
|
-
model: recordText(session.value, "model"),
|
|
5936
|
-
host_version: nestedText(session.value, "host_version"),
|
|
5937
|
-
observations: [session]
|
|
5938
|
-
}) });
|
|
5939
|
-
}
|
|
5940
|
-
const turn = observation(envelope, "turn");
|
|
5941
|
-
if (turn !== void 0) drafts.push({ frame_kind: "turn_started", payload: compact({
|
|
5942
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
5943
|
-
local_turn_ref: localTurnRef(envelope),
|
|
5944
|
-
observations: [turn]
|
|
5945
|
-
}) });
|
|
5946
|
-
if (!isCealHostPromptObservationValue(prompt.value)) throw new CodexHostAdapterError("storage", "Codex prompt observation is malformed.");
|
|
5947
|
-
drafts.push({ frame_kind: "user_input", payload: compact({
|
|
5948
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
5949
|
-
local_turn_ref: localTurnRef(envelope),
|
|
5950
|
-
local_input_ref: envelope.host_input_ref,
|
|
5951
|
-
input_kind: envelope.input_kind,
|
|
5952
|
-
origin_input: envelope.origin_input,
|
|
5953
|
-
observations: [prompt],
|
|
5954
|
-
local_turn_aliases: envelope.codex_turn_id === void 0 ? void 0 : [envelope.codex_turn_id],
|
|
5955
|
-
context_expires_at: envelope.expires_at
|
|
5956
|
-
}) });
|
|
5957
|
-
return drafts;
|
|
5958
|
-
}
|
|
5959
6486
|
};
|
|
5960
|
-
function mapTerminal(envelope) {
|
|
5961
|
-
const timing = observation(envelope, "timing");
|
|
5962
|
-
const timingValue = asRecord(timing?.value);
|
|
5963
|
-
const aborted = timingValue?.terminal === "aborted";
|
|
5964
|
-
const usage = envelope.observations.filter((item) => ["token", "cost", "model"].includes(item.observation_kind));
|
|
5965
|
-
return { frame_kind: "turn_terminal", payload: compact({
|
|
5966
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
5967
|
-
local_turn_ref: localTurnRef(envelope),
|
|
5968
|
-
terminal_state: aborted ? "aborted" : "completed",
|
|
5969
|
-
abort_reason: aborted && typeof timingValue?.reason === "string" ? safeReasonRef(timingValue.reason) : void 0,
|
|
5970
|
-
duration_ms: nonNegativeNumber(timingValue?.duration_ms),
|
|
5971
|
-
time_to_first_token_ms: nonNegativeNumber(timingValue?.time_to_first_token_ms),
|
|
5972
|
-
usage: usage.length === 0 ? void 0 : Object.fromEntries(usage.map((item) => [item.observation_kind, item.value])),
|
|
5973
|
-
observations: envelope.observations
|
|
5974
|
-
}) };
|
|
5975
|
-
}
|
|
5976
|
-
function observation(envelope, kind) {
|
|
5977
|
-
return envelope.observations.find((item) => item.observation_kind === kind);
|
|
5978
|
-
}
|
|
5979
|
-
function localTurnRef(envelope) {
|
|
5980
|
-
return envelope.identity.host_turn_ref;
|
|
5981
|
-
}
|
|
5982
|
-
function compact(value) {
|
|
5983
|
-
return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
|
|
5984
|
-
}
|
|
5985
|
-
function asRecord(value) {
|
|
5986
|
-
return isRecord6(value) ? value : void 0;
|
|
5987
|
-
}
|
|
5988
|
-
function recordText(value, key) {
|
|
5989
|
-
const record3 = asRecord(value);
|
|
5990
|
-
return typeof record3?.[key] === "string" ? record3[key] : void 0;
|
|
5991
|
-
}
|
|
5992
|
-
function nestedText(value, key) {
|
|
5993
|
-
const nested = asRecord(asRecord(value)?.[key]);
|
|
5994
|
-
return typeof nested?.value === "string" ? nested.value : void 0;
|
|
5995
|
-
}
|
|
5996
|
-
function nonNegativeNumber(value) {
|
|
5997
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
5998
|
-
}
|
|
5999
|
-
function safeReasonRef(value) {
|
|
6000
|
-
return SAFE_REF7.test(value) ? value : `reason:${createHash6("sha256").update(value, "utf8").digest("hex")}`;
|
|
6001
|
-
}
|
|
6002
|
-
function isRecord6(value) {
|
|
6003
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6004
|
-
}
|
|
6005
6487
|
|
|
6006
6488
|
// host/codex/src/codex-transcript-baseline.ts
|
|
6007
6489
|
import { createHash as createHash7 } from "node:crypto";
|
|
6008
6490
|
|
|
6009
6491
|
// host/codex/src/codex-transcript-decoder.ts
|
|
6010
|
-
var
|
|
6492
|
+
var SAFE_REF6 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
|
|
6011
6493
|
var MAX_PROMPT_BYTES = 64 * 1024;
|
|
6012
6494
|
function decodeCodexTranscriptLine(line) {
|
|
6013
6495
|
let value;
|
|
@@ -6016,40 +6498,40 @@ function decodeCodexTranscriptLine(line) {
|
|
|
6016
6498
|
} catch {
|
|
6017
6499
|
reject("Codex transcript record is not valid JSON.");
|
|
6018
6500
|
}
|
|
6019
|
-
if (!
|
|
6501
|
+
if (!isRecord8(value)) reject("Codex transcript record envelope is malformed.");
|
|
6020
6502
|
const ordinal = safeOrdinal(value.ordinal);
|
|
6021
|
-
const
|
|
6022
|
-
if (!
|
|
6023
|
-
if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal,
|
|
6024
|
-
if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal,
|
|
6025
|
-
if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal,
|
|
6026
|
-
if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal,
|
|
6027
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
6028
|
-
}
|
|
6029
|
-
function decodeTurnContext(payload, ordinal,
|
|
6503
|
+
const timestamp3 = safeTimestamp(value.timestamp);
|
|
6504
|
+
if (!isRecord8(value.payload) || typeof value.type !== "string") reject("Codex transcript record payload is malformed.");
|
|
6505
|
+
if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal, timestamp3);
|
|
6506
|
+
if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal, timestamp3);
|
|
6507
|
+
if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal, timestamp3);
|
|
6508
|
+
if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal, timestamp3);
|
|
6509
|
+
return { kind: "ignored", ordinal, timestamp: timestamp3 };
|
|
6510
|
+
}
|
|
6511
|
+
function decodeTurnContext(payload, ordinal, timestamp3) {
|
|
6030
6512
|
return {
|
|
6031
6513
|
kind: "turn_context",
|
|
6032
6514
|
ordinal,
|
|
6033
|
-
timestamp:
|
|
6034
|
-
turn_id:
|
|
6515
|
+
timestamp: timestamp3,
|
|
6516
|
+
turn_id: safeRef4(payload.turn_id, "Codex turn context identity is invalid."),
|
|
6035
6517
|
model: boundedNonEmptyText(payload.model, 256, "Codex turn context model is invalid.")
|
|
6036
6518
|
};
|
|
6037
6519
|
}
|
|
6038
|
-
function decodeSessionMeta(payload, ordinal,
|
|
6039
|
-
const sessionId =
|
|
6520
|
+
function decodeSessionMeta(payload, ordinal, timestamp3) {
|
|
6521
|
+
const sessionId = safeRef4(payload.id, "Codex transcript session identity is invalid.");
|
|
6040
6522
|
const cliVersion = boundedNonEmptyText(payload.cli_version, 64, "Codex transcript version is invalid.");
|
|
6041
|
-
return { kind: "session_meta", ordinal, timestamp:
|
|
6523
|
+
return { kind: "session_meta", ordinal, timestamp: timestamp3, session_id: sessionId, cli_version: cliVersion };
|
|
6042
6524
|
}
|
|
6043
|
-
function decodeEventMessage(payload, ordinal,
|
|
6525
|
+
function decodeEventMessage(payload, ordinal, timestamp3) {
|
|
6044
6526
|
if (payload.type === "task_started") {
|
|
6045
|
-
return { kind: "task_started", ordinal, timestamp:
|
|
6527
|
+
return { kind: "task_started", ordinal, timestamp: timestamp3, turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."), started_at: safeNumber(payload.started_at) };
|
|
6046
6528
|
}
|
|
6047
6529
|
if (payload.type === "task_complete") {
|
|
6048
6530
|
return {
|
|
6049
6531
|
kind: "task_complete",
|
|
6050
6532
|
ordinal,
|
|
6051
|
-
timestamp:
|
|
6052
|
-
turn_id:
|
|
6533
|
+
timestamp: timestamp3,
|
|
6534
|
+
turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
|
|
6053
6535
|
started_at: safeNumber(payload.started_at),
|
|
6054
6536
|
completed_at: safeNumber(payload.completed_at),
|
|
6055
6537
|
duration_ms: safeNumber(payload.duration_ms),
|
|
@@ -6060,20 +6542,20 @@ function decodeEventMessage(payload, ordinal, timestamp4) {
|
|
|
6060
6542
|
return {
|
|
6061
6543
|
kind: "turn_aborted",
|
|
6062
6544
|
ordinal,
|
|
6063
|
-
timestamp:
|
|
6064
|
-
turn_id:
|
|
6545
|
+
timestamp: timestamp3,
|
|
6546
|
+
turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
|
|
6065
6547
|
started_at: safeNumber(payload.started_at),
|
|
6066
6548
|
completed_at: safeNumber(payload.completed_at),
|
|
6067
6549
|
duration_ms: safeNumber(payload.duration_ms),
|
|
6068
6550
|
reason: boundedText(payload.reason, 256, "Codex task abort reason is invalid.")
|
|
6069
6551
|
};
|
|
6070
6552
|
}
|
|
6071
|
-
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal,
|
|
6072
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
6553
|
+
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp3);
|
|
6554
|
+
return { kind: "ignored", ordinal, timestamp: timestamp3 };
|
|
6073
6555
|
}
|
|
6074
|
-
function decodeTokenCount(payload, ordinal,
|
|
6075
|
-
if (payload.info === null) return { kind: "token_count", ordinal, timestamp:
|
|
6076
|
-
if (!
|
|
6556
|
+
function decodeTokenCount(payload, ordinal, timestamp3) {
|
|
6557
|
+
if (payload.info === null) return { kind: "token_count", ordinal, timestamp: timestamp3 };
|
|
6558
|
+
if (!isRecord8(payload.info)) return { kind: "token_count", ordinal, timestamp: timestamp3, usage_gap_reason: "token_count_shape_not_attributable" };
|
|
6077
6559
|
const lastUsage = optionalTokenUsage(payload.info.last_token_usage);
|
|
6078
6560
|
const totalUsage = optionalTokenUsage(payload.info.total_token_usage);
|
|
6079
6561
|
const gap = lastUsage.status === "invalid" || totalUsage.status === "invalid";
|
|
@@ -6081,7 +6563,7 @@ function decodeTokenCount(payload, ordinal, timestamp4) {
|
|
|
6081
6563
|
return {
|
|
6082
6564
|
kind: "token_count",
|
|
6083
6565
|
ordinal,
|
|
6084
|
-
timestamp:
|
|
6566
|
+
timestamp: timestamp3,
|
|
6085
6567
|
...lastUsage.status === "valid" ? { last_usage: lastUsage.value } : {},
|
|
6086
6568
|
...totalUsage.status === "valid" ? { total_usage: totalUsage.value } : {},
|
|
6087
6569
|
...usageGapReason === void 0 ? {} : { usage_gap_reason: usageGapReason }
|
|
@@ -6097,7 +6579,7 @@ function optionalTokenUsage(value) {
|
|
|
6097
6579
|
}
|
|
6098
6580
|
}
|
|
6099
6581
|
function decodeTokenUsage(value) {
|
|
6100
|
-
if (!
|
|
6582
|
+
if (!isRecord8(value)) reject("Codex token usage is malformed.");
|
|
6101
6583
|
const usage = {
|
|
6102
6584
|
input_tokens: safeTokenCount(value.input_tokens),
|
|
6103
6585
|
cached_input_tokens: safeTokenCount(value.cached_input_tokens),
|
|
@@ -6109,35 +6591,33 @@ function decodeTokenUsage(value) {
|
|
|
6109
6591
|
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) reject("Codex token usage buckets are inconsistent.");
|
|
6110
6592
|
return usage;
|
|
6111
6593
|
}
|
|
6112
|
-
function decodeResponseItem(payload, ordinal,
|
|
6113
|
-
if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp:
|
|
6594
|
+
function decodeResponseItem(payload, ordinal, timestamp3) {
|
|
6595
|
+
if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp3 };
|
|
6114
6596
|
const metadata = userMessageMetadata(payload.internal_chat_message_metadata_passthrough);
|
|
6115
|
-
const
|
|
6116
|
-
if (
|
|
6597
|
+
const contentItemKind = userTextContentKind(metadata.content_item_kinds);
|
|
6598
|
+
if (contentItemKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp3 };
|
|
6117
6599
|
const prompt = userTextContent(payload.content);
|
|
6118
6600
|
return {
|
|
6119
6601
|
kind: "user_text",
|
|
6120
6602
|
ordinal,
|
|
6121
|
-
timestamp:
|
|
6603
|
+
timestamp: timestamp3,
|
|
6122
6604
|
prompt,
|
|
6123
|
-
|
|
6124
|
-
message_id:
|
|
6125
|
-
turn_id:
|
|
6605
|
+
content_item_kind: contentItemKind,
|
|
6606
|
+
message_id: safeRef4(payload.id, "Codex user message identity is invalid."),
|
|
6607
|
+
turn_id: safeRef4(metadata.turn_id, "Codex user message task identity is invalid.")
|
|
6126
6608
|
};
|
|
6127
6609
|
}
|
|
6128
|
-
function
|
|
6129
|
-
|
|
6130
|
-
if (kinds[0] === "user.text") return "human";
|
|
6131
|
-
return kinds[0] === "goal.internal_context" ? "host_automatic" : void 0;
|
|
6610
|
+
function userTextContentKind(kinds) {
|
|
6611
|
+
return kinds.length === 1 && (kinds[0] === "user.text" || kinds[0] === "goal.internal_context") ? kinds[0] : void 0;
|
|
6132
6612
|
}
|
|
6133
6613
|
function userMessageMetadata(value) {
|
|
6134
|
-
if (!
|
|
6614
|
+
if (!isRecord8(value) || !Array.isArray(value.content_item_kinds)) reject("Codex user message provenance is malformed.");
|
|
6135
6615
|
return { ...value, content_item_kinds: value.content_item_kinds };
|
|
6136
6616
|
}
|
|
6137
6617
|
function userTextContent(value) {
|
|
6138
6618
|
if (!Array.isArray(value)) reject(`Codex user text content is malformed (${refusalFact("kind", typeof value, "array")}).`);
|
|
6139
6619
|
if (value.length !== 1) reject(`Codex user text content is malformed (${refusalFact("items", value.length, "exactly one")}).`);
|
|
6140
|
-
if (!
|
|
6620
|
+
if (!isRecord8(value[0]) || value[0].type !== "input_text") reject(`Codex user text content is malformed (${refusalFact("item_type", isRecord8(value[0]) ? value[0].type : typeof value[0], "input_text")}).`);
|
|
6141
6621
|
return boundedText(value[0].text, MAX_PROMPT_BYTES, "Codex user text is invalid.");
|
|
6142
6622
|
}
|
|
6143
6623
|
function safeOrdinal(value) {
|
|
@@ -6158,8 +6638,8 @@ function safeTimestamp(value) {
|
|
|
6158
6638
|
if (!Number.isFinite(Date.parse(value))) reject(`Codex transcript timestamp is invalid (${refusalFact("parsed_ms", Date.parse(value), "a finite epoch milliseconds value")}).`);
|
|
6159
6639
|
return value;
|
|
6160
6640
|
}
|
|
6161
|
-
function
|
|
6162
|
-
if (typeof value !== "string" || !
|
|
6641
|
+
function safeRef4(value, message) {
|
|
6642
|
+
if (typeof value !== "string" || !SAFE_REF6.test(value)) reject(message);
|
|
6163
6643
|
return value;
|
|
6164
6644
|
}
|
|
6165
6645
|
function boundedText(value, maximum, message) {
|
|
@@ -6173,7 +6653,7 @@ function boundedNonEmptyText(value, maximum, message) {
|
|
|
6173
6653
|
if (result.length === 0) reject(message);
|
|
6174
6654
|
return result;
|
|
6175
6655
|
}
|
|
6176
|
-
function
|
|
6656
|
+
function isRecord8(value) {
|
|
6177
6657
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6178
6658
|
}
|
|
6179
6659
|
function reject(message) {
|
|
@@ -6197,45 +6677,49 @@ function codexTranscriptInitialCursor(registration, device, inode, now) {
|
|
|
6197
6677
|
anchor_sha256: digest(Buffer.alloc(0)),
|
|
6198
6678
|
header_validated: false,
|
|
6199
6679
|
status: "active",
|
|
6200
|
-
updated_at:
|
|
6680
|
+
updated_at: timestamp2(now)
|
|
6201
6681
|
};
|
|
6202
6682
|
}
|
|
6203
|
-
async function ownedCodexTranscriptFile(handle) {
|
|
6683
|
+
async function ownedCodexTranscriptFile(handle, platform = process.platform) {
|
|
6204
6684
|
const stats = await handle.stat();
|
|
6205
|
-
|
|
6685
|
+
if (!stats.isFile()) throw new CodexHostAdapterError("storage", "Codex transcript source is not a regular file.");
|
|
6686
|
+
if (platform !== "win32" && !isSelfOwned(stats.uid, platform, handle)) throw new CodexHostAdapterError("storage", "Codex transcript source is owned by another user.");
|
|
6206
6687
|
return { size: stats.size, device: String(stats.dev), inode: String(stats.ino) };
|
|
6207
6688
|
}
|
|
6208
|
-
function
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
}
|
|
6213
|
-
function assertCodexTranscriptHeader(registration, record3) {
|
|
6214
|
-
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([
|
|
6215
|
-
{ name: "kind", value: record3.kind, expected: "session_meta" },
|
|
6216
|
-
{ name: "session_id", value: record3.kind === "session_meta" ? record3.session_id : "unavailable", expected: registration.codex_session_id }
|
|
6689
|
+
function assertCodexTranscriptHeader(registration, record2) {
|
|
6690
|
+
if (record2.kind !== "session_meta" || record2.session_id !== registration.codex_session_id) throw new CodexHostAdapterError("payload_rejected", `Codex transcript header does not match its SessionStart registration (${refusalFacts([
|
|
6691
|
+
{ name: "kind", value: record2.kind, expected: "session_meta" },
|
|
6692
|
+
{ name: "session_id", value: record2.kind === "session_meta" ? record2.session_id : "unavailable", expected: registration.codex_session_id }
|
|
6217
6693
|
])}).`);
|
|
6218
6694
|
}
|
|
6219
6695
|
function digest(value) {
|
|
6220
6696
|
return createHash7("sha256").update(value).digest("hex");
|
|
6221
6697
|
}
|
|
6222
|
-
function
|
|
6698
|
+
function timestamp2(now) {
|
|
6223
6699
|
const value = now();
|
|
6224
6700
|
if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
|
|
6225
6701
|
return value.toISOString();
|
|
6226
6702
|
}
|
|
6227
6703
|
|
|
6704
|
+
// host/codex/src/codex-runtime-paths.ts
|
|
6705
|
+
import { join as join5 } from "node:path";
|
|
6706
|
+
var CODEX_HOME_ENVIRONMENT = "CODEX_HOME";
|
|
6707
|
+
var CODEX_HOME_DEFAULT_DIRECTORY = ".codex";
|
|
6708
|
+
function resolveCodexHome(environment, home) {
|
|
6709
|
+
return environment[CODEX_HOME_ENVIRONMENT] || join5(home, CODEX_HOME_DEFAULT_DIRECTORY);
|
|
6710
|
+
}
|
|
6711
|
+
|
|
6228
6712
|
// host/codex/src/codex-adapter.ts
|
|
6229
|
-
var
|
|
6713
|
+
var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
6230
6714
|
function codexSubmissionReference(hostInputRef, event, ordinal) {
|
|
6231
|
-
if (!
|
|
6715
|
+
if (!SAFE_REF7.test(hostInputRef) || !Number.isSafeInteger(ordinal) || ordinal < 0) throw new CodexHostAdapterError("invalid_input", "Codex submission identity is invalid.");
|
|
6232
6716
|
return `codex-submission:${cealRequestSha256({ host_input_ref: hostInputRef, event, ordinal })}`;
|
|
6233
6717
|
}
|
|
6234
6718
|
|
|
6235
6719
|
// host/codex/src/codex-transcript-watcher.ts
|
|
6236
6720
|
var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
6237
6721
|
store;
|
|
6238
|
-
|
|
6722
|
+
diagnostic;
|
|
6239
6723
|
constructor(options) {
|
|
6240
6724
|
super(options, {
|
|
6241
6725
|
default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
|
|
@@ -6243,7 +6727,7 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6243
6727
|
clock_failure: () => new CodexHostAdapterError("storage", "Codex watcher clock is invalid.")
|
|
6244
6728
|
});
|
|
6245
6729
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
6246
|
-
this.
|
|
6730
|
+
this.diagnostic = options.diagnostic ?? (() => void 0);
|
|
6247
6731
|
}
|
|
6248
6732
|
transcriptPathOf(registration) {
|
|
6249
6733
|
return registration.transcript_path;
|
|
@@ -6259,7 +6743,8 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6259
6743
|
if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
|
|
6260
6744
|
return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
|
|
6261
6745
|
}
|
|
6262
|
-
async scanFailed(handle, registration,
|
|
6746
|
+
async scanFailed(handle, registration, error) {
|
|
6747
|
+
this.diagnostic("watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
6263
6748
|
await this.quarantineFailure(registration, handle);
|
|
6264
6749
|
return { records: 0, enqueued: 0, quarantined: true };
|
|
6265
6750
|
}
|
|
@@ -6269,7 +6754,9 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6269
6754
|
async preflight(registration, handle, size, device, inode) {
|
|
6270
6755
|
const existing = await this.store.readTranscriptCursor(registration);
|
|
6271
6756
|
let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
|
|
6272
|
-
if (cursor.status === "quarantined")
|
|
6757
|
+
if (cursor.status === "quarantined") {
|
|
6758
|
+
return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
6759
|
+
}
|
|
6273
6760
|
const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
|
|
6274
6761
|
if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
|
|
6275
6762
|
await this.quarantine(registration, cursor, device, inode, reason);
|
|
@@ -6281,22 +6768,22 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6281
6768
|
let records = 0;
|
|
6282
6769
|
let enqueued = 0;
|
|
6283
6770
|
for (const line of complete.toString("utf8").split("\n").filter((value) => value.length > 0)) {
|
|
6284
|
-
const
|
|
6285
|
-
assertRecordSequence(registration, cursor, records, lastOrdinal,
|
|
6286
|
-
await this.observeSessionMetadata(registration,
|
|
6287
|
-
const outcome = await this.processRecord(registration, activeTask,
|
|
6771
|
+
const record2 = decodeCodexTranscriptLine(line);
|
|
6772
|
+
assertRecordSequence(registration, cursor, records, lastOrdinal, record2);
|
|
6773
|
+
await this.observeSessionMetadata(registration, record2);
|
|
6774
|
+
const outcome = await this.processRecord(registration, activeTask, record2);
|
|
6288
6775
|
activeTask = outcome.activeTask;
|
|
6289
6776
|
enqueued += outcome.enqueued;
|
|
6290
|
-
lastOrdinal =
|
|
6777
|
+
lastOrdinal = record2.ordinal;
|
|
6291
6778
|
records += 1;
|
|
6292
6779
|
}
|
|
6293
6780
|
await this.commitCursor(registration, handle, cursor, complete.length, device, inode, lastOrdinal, activeTask);
|
|
6294
6781
|
return { records, enqueued, quarantined: false };
|
|
6295
6782
|
}
|
|
6296
|
-
async observeSessionMetadata(registration,
|
|
6297
|
-
if (
|
|
6783
|
+
async observeSessionMetadata(registration, record2) {
|
|
6784
|
+
if (record2.kind !== "session_meta") return;
|
|
6298
6785
|
const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
|
|
6299
|
-
if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version:
|
|
6786
|
+
if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record2.cli_version } });
|
|
6300
6787
|
}
|
|
6301
6788
|
async commitCursor(registration, handle, cursor, consumedBytes, device, inode, lastOrdinal, activeTask) {
|
|
6302
6789
|
const offset = cursor.offset + consumedBytes;
|
|
@@ -6308,75 +6795,132 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6308
6795
|
const stats = await handle.stat().catch(() => null);
|
|
6309
6796
|
if (stats === null) return;
|
|
6310
6797
|
const existing = await this.store.readTranscriptCursor(registration).catch(() => null);
|
|
6311
|
-
|
|
6798
|
+
const quarantined = {
|
|
6799
|
+
...existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now),
|
|
6800
|
+
transcript_path: registration.transcript_path,
|
|
6801
|
+
device: String(stats.dev),
|
|
6802
|
+
inode: String(stats.ino),
|
|
6803
|
+
status: "quarantined",
|
|
6804
|
+
reason: "transcript_malformed",
|
|
6805
|
+
updated_at: this.stamp()
|
|
6806
|
+
};
|
|
6807
|
+
await this.store.writeTranscriptCursor(quarantined);
|
|
6312
6808
|
}
|
|
6313
|
-
async processRecord(registration, activeTask,
|
|
6314
|
-
if (
|
|
6315
|
-
|
|
6316
|
-
}
|
|
6317
|
-
if (
|
|
6318
|
-
if (
|
|
6319
|
-
if (record3.kind === "user_text") return this.processUserInput(registration, activeTask, record3);
|
|
6320
|
-
if (record3.kind === "task_complete" || record3.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record3);
|
|
6809
|
+
async processRecord(registration, activeTask, record2) {
|
|
6810
|
+
if (record2.kind === "task_started") return this.processTaskStarted(registration, record2);
|
|
6811
|
+
if (record2.kind === "turn_context") return { ...activeTask === void 0 ? {} : { activeTask: record2.turn_id === activeTask.codex_turn_id ? { ...activeTask, model: record2.model } : activeTask }, enqueued: 0 };
|
|
6812
|
+
if (record2.kind === "token_count") return { ...activeTask === void 0 ? {} : { activeTask: accumulateTokenCount(activeTask, record2) }, enqueued: 0 };
|
|
6813
|
+
if (record2.kind === "user_text") return this.processUserInput(registration, activeTask, record2);
|
|
6814
|
+
if (record2.kind === "task_complete" || record2.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record2);
|
|
6321
6815
|
return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
6322
6816
|
}
|
|
6323
|
-
async
|
|
6324
|
-
|
|
6325
|
-
|
|
6817
|
+
async processTaskStarted(registration, record2) {
|
|
6818
|
+
return { activeTask: { codex_turn_id: record2.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record2.turn_id), started_at: record2.timestamp }, enqueued: 0 };
|
|
6819
|
+
}
|
|
6820
|
+
async processUserInput(registration, activeTask, record2) {
|
|
6821
|
+
if (activeTask === void 0 || activeTask.codex_turn_id !== record2.turn_id) {
|
|
6822
|
+
const adopted = await this.adoptHookCorrelatedTurn(registration, record2);
|
|
6823
|
+
if (adopted === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
6824
|
+
activeTask = adopted;
|
|
6825
|
+
}
|
|
6826
|
+
const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record2.turn_id, record2.message_id, record2.ordinal, record2.prompt);
|
|
6326
6827
|
const origin = activeTask.origin_host_input_ref === void 0;
|
|
6327
|
-
const promptValue = promptObservationValue(
|
|
6828
|
+
const promptValue = promptObservationValue(record2.prompt);
|
|
6328
6829
|
const currentPromptRef = hostProducerPromptRef(promptValue.body_sha256);
|
|
6329
6830
|
const promptRef = activeTask.prompt_ref ?? currentPromptRef;
|
|
6831
|
+
const steeredPromptRef = await this.steeredPromptRef(registration, activeTask, origin, currentPromptRef);
|
|
6330
6832
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
6331
6833
|
const observations = [{
|
|
6332
|
-
observation_ref: observationRef("transcript_user_text", hostInputRef,
|
|
6834
|
+
observation_ref: observationRef(registration.host_source_ref, "transcript_user_text", hostInputRef, record2.prompt),
|
|
6333
6835
|
observation_kind: "prompt",
|
|
6334
|
-
observed_at:
|
|
6836
|
+
observed_at: record2.timestamp,
|
|
6335
6837
|
value: promptValue
|
|
6336
6838
|
}];
|
|
6337
6839
|
if (origin) {
|
|
6338
|
-
observations.push({ observation_ref: observationRef("transcript_task_started", activeTask.host_turn_ref,
|
|
6840
|
+
observations.push({ observation_ref: observationRef(registration.host_source_ref, "transcript_task_started", activeTask.host_turn_ref, record2.turn_id), observation_kind: "turn", observed_at: record2.timestamp, value: turnObservationValue(registration.codex_session_id, activeTask.host_turn_ref, "started") });
|
|
6339
6841
|
const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
|
|
6340
|
-
if (session !== null) observations.push({ observation_ref: observationRef("transcript_session", registration.host_session_ref, session.value), observation_kind: "session", observed_at:
|
|
6842
|
+
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: record2.timestamp, value: session.value });
|
|
6341
6843
|
}
|
|
6342
6844
|
const envelope = {
|
|
6343
6845
|
schema_version: "ceal.codex_observation_outbox.v1",
|
|
6344
6846
|
identity,
|
|
6345
6847
|
host_input_ref: hostInputRef,
|
|
6346
|
-
input_kind: record3.input_kind,
|
|
6347
6848
|
prompt_ref: promptRef,
|
|
6348
6849
|
observations,
|
|
6349
|
-
codex_turn_id:
|
|
6850
|
+
codex_turn_id: record2.turn_id,
|
|
6350
6851
|
origin_input: origin,
|
|
6351
|
-
submission_ref: codexSubmissionReference(hostInputRef, "input",
|
|
6352
|
-
created_at:
|
|
6852
|
+
submission_ref: codexSubmissionReference(hostInputRef, "input", record2.ordinal),
|
|
6853
|
+
created_at: record2.timestamp,
|
|
6353
6854
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
6354
6855
|
};
|
|
6355
|
-
await this.
|
|
6856
|
+
const persisted = await this.persistUserInputTurn(activeTask, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope);
|
|
6857
|
+
if (steeredPromptRef !== null) await this.activateTranscriptTurnContext(identity, steeredPromptRef, envelope.expires_at);
|
|
6858
|
+
return persisted;
|
|
6859
|
+
}
|
|
6860
|
+
/**
|
|
6861
|
+
* The steered prompt to re-activate under the running task, or null when
|
|
6862
|
+
* there is nothing to repair. A steered message joins the running turn
|
|
6863
|
+
* with new prompt bytes but no task_started. When the steer hook already
|
|
6864
|
+
* replaced the turn selection (a turn id the hook did not recognize as
|
|
6865
|
+
* the running one, or a hook that ran before any context was live), the
|
|
6866
|
+
* running task's context no longer matches and the next invocation would
|
|
6867
|
+
* wait for a boundary that never comes. When the hook kept the live
|
|
6868
|
+
* selection, the context still matches and this stays null.
|
|
6869
|
+
*/
|
|
6870
|
+
async steeredPromptRef(registration, activeTask, origin, currentPromptRef) {
|
|
6871
|
+
if (origin || activeTask.prompt_ref === void 0 || activeTask.prompt_ref === currentPromptRef) return null;
|
|
6872
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref).catch(() => null);
|
|
6873
|
+
return selection?.prompt_ref === currentPromptRef ? currentPromptRef : null;
|
|
6874
|
+
}
|
|
6875
|
+
/**
|
|
6876
|
+
* Adopt the live hooked turn when its transcript message arrives before
|
|
6877
|
+
* (or without) its task_started, as an interrupted turn's follow-up can.
|
|
6878
|
+
* The UserPromptSubmit hook names the exact hooked prompt, so only a
|
|
6879
|
+
* message carrying those bytes adopts; inherited history and other turns
|
|
6880
|
+
* stay ignored. A stale active task for another turn releases first, the
|
|
6881
|
+
* same as a task_started supersession.
|
|
6882
|
+
*/
|
|
6883
|
+
async adoptHookCorrelatedTurn(registration, record2) {
|
|
6884
|
+
const currentPromptRef = hostProducerPromptRef(promptObservationValue(record2.prompt).body_sha256);
|
|
6885
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref);
|
|
6886
|
+
if (selection?.prompt_ref !== currentPromptRef) return void 0;
|
|
6887
|
+
return {
|
|
6888
|
+
codex_turn_id: record2.turn_id,
|
|
6889
|
+
host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record2.turn_id),
|
|
6890
|
+
started_at: record2.timestamp
|
|
6891
|
+
};
|
|
6892
|
+
}
|
|
6893
|
+
async persistUserInputTurn(activeTask, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope) {
|
|
6356
6894
|
await this.store.put(envelope);
|
|
6357
|
-
if (origin) await activateTranscriptTurnContext(
|
|
6895
|
+
if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
|
|
6358
6896
|
return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
|
|
6359
6897
|
}
|
|
6360
|
-
async
|
|
6361
|
-
|
|
6362
|
-
|
|
6898
|
+
async activateTranscriptTurnContext(identity, promptRef, expiresAt) {
|
|
6899
|
+
try {
|
|
6900
|
+
const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
6901
|
+
const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
|
|
6902
|
+
await this.store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
|
|
6903
|
+
this.diagnostic("activation_recorded");
|
|
6904
|
+
} catch (error) {
|
|
6905
|
+
this.diagnostic("activation_failed");
|
|
6906
|
+
throw error;
|
|
6907
|
+
}
|
|
6363
6908
|
}
|
|
6364
|
-
async processTerminal(registration, activeTask,
|
|
6365
|
-
if (activeTask === void 0 || activeTask.codex_turn_id !==
|
|
6909
|
+
async processTerminal(registration, activeTask, record2) {
|
|
6910
|
+
if (activeTask === void 0 || activeTask.codex_turn_id !== record2.turn_id || activeTask.origin_host_input_ref === void 0 || activeTask.prompt_ref === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
6366
6911
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
6367
|
-
const timing =
|
|
6368
|
-
const observations = terminalObservations(registration, activeTask,
|
|
6912
|
+
const timing = record2.kind === "task_complete" ? { status: "available", unit: "milliseconds", started_at: record2.started_at, completed_at: record2.completed_at, duration_ms: record2.duration_ms, time_to_first_token_ms: record2.time_to_first_token_ms } : { status: "available", unit: "milliseconds", started_at: record2.started_at, completed_at: record2.completed_at, duration_ms: record2.duration_ms, terminal: "aborted", reason: record2.reason };
|
|
6913
|
+
const observations = terminalObservations(registration, activeTask, record2, timing);
|
|
6369
6914
|
await this.store.put({
|
|
6370
6915
|
schema_version: "ceal.codex_observation_outbox.v1",
|
|
6371
6916
|
identity,
|
|
6372
6917
|
host_input_ref: activeTask.origin_host_input_ref,
|
|
6373
|
-
input_kind: "host_automatic",
|
|
6374
6918
|
prompt_ref: activeTask.prompt_ref,
|
|
6375
6919
|
observations,
|
|
6376
|
-
codex_turn_id:
|
|
6920
|
+
codex_turn_id: record2.turn_id,
|
|
6377
6921
|
origin_input: false,
|
|
6378
|
-
submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal",
|
|
6379
|
-
created_at:
|
|
6922
|
+
submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record2.ordinal),
|
|
6923
|
+
created_at: record2.timestamp,
|
|
6380
6924
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
6381
6925
|
});
|
|
6382
6926
|
await this.store.clearInvocationContext(identity, this.now());
|
|
@@ -6387,15 +6931,18 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6387
6931
|
await this.store.writeTranscriptCursor(quarantined);
|
|
6388
6932
|
}
|
|
6389
6933
|
};
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6934
|
+
function codexTranscriptScanFailureKind(error) {
|
|
6935
|
+
return transcriptScanFailureKind(error, (candidate) => {
|
|
6936
|
+
if (!(candidate instanceof CodexHostAdapterError)) return void 0;
|
|
6937
|
+
if (candidate.code === "payload_rejected") return "payload_rejected";
|
|
6938
|
+
if (candidate.code === "storage") return "read_error";
|
|
6939
|
+
return void 0;
|
|
6940
|
+
});
|
|
6941
|
+
}
|
|
6942
|
+
function accumulateTokenCount(activeTask, record2) {
|
|
6943
|
+
if (record2.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record2.usage_gap_reason };
|
|
6944
|
+
const last = record2.last_usage;
|
|
6945
|
+
const total = record2.total_usage;
|
|
6399
6946
|
if (last === void 0 || ignoreTokenCount(activeTask.last_total_usage, total, last)) return activeTask;
|
|
6400
6947
|
const tokenUsage = addUsage(activeTask.token_usage, last);
|
|
6401
6948
|
return {
|
|
@@ -6409,21 +6956,16 @@ function ignoreTokenCount(previous, total, last) {
|
|
|
6409
6956
|
if (total === void 0 || previous === void 0) return false;
|
|
6410
6957
|
return sameUsage(total, previous) || regressedUsage(total, previous) && looksLikeStaleRegression(total, previous, last);
|
|
6411
6958
|
}
|
|
6412
|
-
function terminalObservations(registration, activeTask,
|
|
6959
|
+
function terminalObservations(registration, activeTask, record2, timing) {
|
|
6413
6960
|
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);
|
|
6414
|
-
const costValue = terminalCostValue();
|
|
6415
6961
|
const observations = [
|
|
6416
|
-
{ observation_ref: observationRef("transcript_terminal", activeTask.host_turn_ref,
|
|
6417
|
-
{ observation_ref: observationRef("transcript_timing", activeTask.host_turn_ref, timing), observation_kind: "timing", observed_at:
|
|
6418
|
-
{ observation_ref: observationRef("transcript_token", activeTask.host_turn_ref, tokenValue), observation_kind: "token", observed_at:
|
|
6419
|
-
{ observation_ref: observationRef("transcript_cost", activeTask.host_turn_ref, costValue), observation_kind: "cost", observed_at: record3.timestamp, value: costValue }
|
|
6962
|
+
{ observation_ref: observationRef(registration.host_source_ref, "transcript_terminal", activeTask.host_turn_ref, record2), observation_kind: "turn", observed_at: record2.timestamp, value: turnObservationValue(registration.codex_session_id, activeTask.host_turn_ref, "stopped") },
|
|
6963
|
+
{ observation_ref: observationRef(registration.host_source_ref, "transcript_timing", activeTask.host_turn_ref, timing), observation_kind: "timing", observed_at: record2.timestamp, value: timing },
|
|
6964
|
+
{ observation_ref: observationRef(registration.host_source_ref, "transcript_token", activeTask.host_turn_ref, tokenValue), observation_kind: "token", observed_at: record2.timestamp, value: tokenValue }
|
|
6420
6965
|
];
|
|
6421
|
-
if (activeTask.model !== void 0) observations.push({ observation_ref: observationRef("transcript_model", activeTask.host_turn_ref, activeTask.model), observation_kind: "model", observed_at:
|
|
6966
|
+
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: record2.timestamp, value: { status: "available", value: activeTask.model, source: "turn_context.model" } });
|
|
6422
6967
|
return observations;
|
|
6423
6968
|
}
|
|
6424
|
-
function terminalCostValue() {
|
|
6425
|
-
return notRecorded("provider_cost_not_observed");
|
|
6426
|
-
}
|
|
6427
6969
|
function notRecorded(reason) {
|
|
6428
6970
|
return hostObservationNotRecorded(reason);
|
|
6429
6971
|
}
|
|
@@ -6470,16 +7012,21 @@ function looksLikeStaleRegression(current, previous, last) {
|
|
|
6470
7012
|
if (previous.total_tokens <= 0 || current.total_tokens <= 0 || last.total_tokens <= 0) return false;
|
|
6471
7013
|
return current.total_tokens * 100 >= previous.total_tokens * 98 || current.total_tokens + last.total_tokens * 2 >= previous.total_tokens;
|
|
6472
7014
|
}
|
|
6473
|
-
function assertRecordSequence(registration, cursor, records, lastOrdinal,
|
|
6474
|
-
if ((cursor.header_validated || records > 0) &&
|
|
6475
|
-
if (!cursor.header_validated && records === 0) assertCodexTranscriptHeader(registration,
|
|
7015
|
+
function assertRecordSequence(registration, cursor, records, lastOrdinal, record2) {
|
|
7016
|
+
if ((cursor.header_validated || records > 0) && record2.ordinal <= lastOrdinal) throw new CodexHostAdapterError("payload_rejected", `Codex transcript ordinal is not monotonic (${refusalFact("ordinal", record2.ordinal, `greater than ${lastOrdinal}`)}).`);
|
|
7017
|
+
if (!cursor.header_validated && records === 0) assertCodexTranscriptHeader(registration, record2);
|
|
6476
7018
|
}
|
|
6477
7019
|
|
|
6478
7020
|
// host/codex/src/codex-producer-runtime.ts
|
|
6479
7021
|
async function drainCodexProducerOnce(options) {
|
|
6480
|
-
const
|
|
6481
|
-
|
|
6482
|
-
|
|
7022
|
+
const scan = await new CodexTranscriptWatcher({
|
|
7023
|
+
outbox_dir: options.outbox_dir,
|
|
7024
|
+
diagnostic: (event, failure_kind) => writeHostServiceDiagnostic("codex", event, failure_kind)
|
|
7025
|
+
}).scanOnce().catch((error) => {
|
|
7026
|
+
writeHostServiceDiagnostic("codex", "watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
7027
|
+
throw error;
|
|
7028
|
+
});
|
|
7029
|
+
const producer = new CodexProducer({ outbox_dir: options.outbox_dir, ...options.now === void 0 ? {} : { now: options.now } });
|
|
6483
7030
|
return { scanned: scan.enqueued, ...await producer.drainOutbox() };
|
|
6484
7031
|
}
|
|
6485
7032
|
|
|
@@ -6492,33 +7039,29 @@ var CODEX_PRODUCER_DESCRIPTOR = {
|
|
|
6492
7039
|
extra_paths: [CODEX_SESSIONS_ROOT],
|
|
6493
7040
|
config: (shared, extras) => ({ ...shared, codex_sessions_root: hostProducerExtraPath(extras, CODEX_SESSIONS_ROOT) }),
|
|
6494
7041
|
runtime_options: (config) => Object.freeze({
|
|
6495
|
-
outbox_dir: config.outbox_dir
|
|
6496
|
-
producer_installation_ref: config.producer_installation_ref,
|
|
6497
|
-
producer_epoch: config.producer_epoch,
|
|
6498
|
-
relay_endpoint: config.relay_endpoint,
|
|
6499
|
-
timeout_ms: config.ipc_timeout_ms
|
|
7042
|
+
outbox_dir: config.outbox_dir
|
|
6500
7043
|
}),
|
|
6501
7044
|
drain: drainCodexProducerOnce
|
|
6502
7045
|
};
|
|
6503
7046
|
|
|
6504
7047
|
// host/codex/src/codex-visible-output-budget.ts
|
|
6505
|
-
import { readFile as
|
|
7048
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
6506
7049
|
import { homedir } from "node:os";
|
|
6507
|
-
import { join as
|
|
7050
|
+
import { join as join6 } from "node:path";
|
|
6508
7051
|
var import_toml = __toESM(require_toml(), 1);
|
|
6509
7052
|
var CODEX_DEFAULT_TOOL_OUTPUT_TOKEN_LIMIT = 5e3;
|
|
6510
7053
|
var CODEX_UTF8_BYTES_PER_OUTPUT_TOKEN = 4;
|
|
6511
7054
|
async function resolveCodexVisibleOutputMaxBytes(environment = process.env) {
|
|
6512
|
-
const codexHome = environment
|
|
7055
|
+
const codexHome = resolveCodexHome(environment, homedir());
|
|
6513
7056
|
let source;
|
|
6514
7057
|
try {
|
|
6515
|
-
source = await
|
|
7058
|
+
source = await readFile2(join6(codexHome, "config.toml"), "utf8");
|
|
6516
7059
|
} catch (error) {
|
|
6517
7060
|
return isNotFound2(error) ? codexBytes(CODEX_DEFAULT_TOOL_OUTPUT_TOKEN_LIMIT) : void 0;
|
|
6518
7061
|
}
|
|
6519
7062
|
try {
|
|
6520
7063
|
const parsed = import_toml.default.parse(source);
|
|
6521
|
-
if (!
|
|
7064
|
+
if (!isRecord9(parsed)) return void 0;
|
|
6522
7065
|
const configured = parsed.tool_output_token_limit;
|
|
6523
7066
|
if (configured === void 0) return codexBytes(CODEX_DEFAULT_TOOL_OUTPUT_TOKEN_LIMIT);
|
|
6524
7067
|
return typeof configured === "number" && Number.isSafeInteger(configured) && configured > 0 ? codexBytes(configured) : void 0;
|
|
@@ -6529,7 +7072,7 @@ async function resolveCodexVisibleOutputMaxBytes(environment = process.env) {
|
|
|
6529
7072
|
function codexBytes(tokens) {
|
|
6530
7073
|
return Math.min(tokens * CODEX_UTF8_BYTES_PER_OUTPUT_TOKEN, CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES);
|
|
6531
7074
|
}
|
|
6532
|
-
function
|
|
7075
|
+
function isRecord9(value) {
|
|
6533
7076
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6534
7077
|
}
|
|
6535
7078
|
function isNotFound2(error) {
|
|
@@ -6546,11 +7089,28 @@ var CODEX_INVOCATION_AUTHENTICATION_DESCRIPTOR = {
|
|
|
6546
7089
|
config_flag: HOST_PRODUCER_CONFIG_FLAG,
|
|
6547
7090
|
parse_config_flag: parseHostProducerConfigFlag,
|
|
6548
7091
|
read_producer_config: (configPath) => readHostProducerConfig(CODEX_PRODUCER_DESCRIPTOR, configPath),
|
|
6549
|
-
resolve_visible_output_max_bytes: () => resolveCodexVisibleOutputMaxBytes()
|
|
7092
|
+
resolve_visible_output_max_bytes: () => resolveCodexVisibleOutputMaxBytes(),
|
|
7093
|
+
read_human_message_window: readCodexInvocationHumanMessageWindow
|
|
6550
7094
|
};
|
|
6551
7095
|
function runCodexInvocationAuthenticationCommand(args, options) {
|
|
6552
7096
|
return runHostInvocationAuthenticationCommand(CODEX_INVOCATION_AUTHENTICATION_DESCRIPTOR, args, options);
|
|
6553
7097
|
}
|
|
7098
|
+
async function readCodexInvocationHumanMessageWindow(input) {
|
|
7099
|
+
return readRegisteredHostInvocationHumanMessageWindow(() => input.store.listTranscriptRegistrations(), input.identity, (line, registration) => {
|
|
7100
|
+
let record2;
|
|
7101
|
+
try {
|
|
7102
|
+
record2 = decodeCodexTranscriptLine(line);
|
|
7103
|
+
} catch {
|
|
7104
|
+
return "malformed";
|
|
7105
|
+
}
|
|
7106
|
+
if (record2.kind !== "user_text" || record2.content_item_kind !== "user.text") return null;
|
|
7107
|
+
return {
|
|
7108
|
+
host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record2.turn_id),
|
|
7109
|
+
host_input_ref: hostInputReference(registration.host_source_ref, registration.host_session_ref, record2.turn_id, record2.message_id, record2.ordinal, record2.prompt),
|
|
7110
|
+
text: record2.prompt
|
|
7111
|
+
};
|
|
7112
|
+
});
|
|
7113
|
+
}
|
|
6554
7114
|
|
|
6555
7115
|
// host/codex/bin/ceal-codex-invocation-authentication.ts
|
|
6556
7116
|
var CODEX_INVOCATION_AUTHENTICATION_CLIENT_COMMAND = {
|