@corca-org/ceal-host-win32-x64 1.0.20 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ceal-host.exe +0 -0
- package/libexec/ceal-host/claude-hook.mjs +1268 -1277
- package/libexec/ceal-host/claude-invocation-authentication.mjs +1944 -1323
- package/libexec/ceal-host/claude-producer.mjs +1346 -1376
- package/libexec/ceal-host/codex-hook.mjs +1103 -1471
- package/libexec/ceal-host/codex-invocation-authentication.mjs +1831 -1432
- package/libexec/ceal-host/codex-producer.mjs +1080 -1313
- package/libexec/ceal-host/control.mjs +2294 -2104
- package/libexec/ceal-host/relay.mjs +1700 -1856
- package/package.json +1 -1
- package/share/ceal-host/bundle-manifest.json +12 -12
- package/share/ceal-host/bundle-manifest.sigstore.json +1 -1
|
@@ -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,10 +3251,20 @@ 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
|
|
3012
3269
|
var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
|
|
3013
3270
|
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
@@ -3022,6 +3279,8 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
|
3022
3279
|
"resource_created",
|
|
3023
3280
|
"review_submitted",
|
|
3024
3281
|
"comment_created",
|
|
3282
|
+
"reaction_created",
|
|
3283
|
+
"ceal_write_completed",
|
|
3025
3284
|
"event_start",
|
|
3026
3285
|
"all_day_start",
|
|
3027
3286
|
"file_created",
|
|
@@ -3056,6 +3315,10 @@ var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
|
3056
3315
|
"github.review.submitted",
|
|
3057
3316
|
"github.issue_comment.created",
|
|
3058
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",
|
|
3059
3322
|
"calendar.organized",
|
|
3060
3323
|
"calendar.accepted_invite",
|
|
3061
3324
|
"calendar.recorded",
|
|
@@ -3077,6 +3340,10 @@ var CEAL_ACTIVITY_EVENT_TIMESTAMP_BASIS_MAP = Object.freeze({
|
|
|
3077
3340
|
"github.pull_request.opened": "resource_created",
|
|
3078
3341
|
"github.issue.edited": "resource_modified",
|
|
3079
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",
|
|
3080
3347
|
"calendar.organized": "resource_created",
|
|
3081
3348
|
"drive.file.created": "file_created",
|
|
3082
3349
|
"drive.file.modified": "file_modified_by_me",
|
|
@@ -3100,6 +3367,7 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
3100
3367
|
source_url: { required: false, timestamp: false },
|
|
3101
3368
|
source: { required: true, timestamp: false },
|
|
3102
3369
|
owner: { required: false, timestamp: false },
|
|
3370
|
+
actor_kind: { required: false, timestamp: false },
|
|
3103
3371
|
counterparts: { required: false, timestamp: false },
|
|
3104
3372
|
attachments: { required: false, timestamp: false },
|
|
3105
3373
|
created_at: { required: false, timestamp: true },
|
|
@@ -3107,9 +3375,18 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
3107
3375
|
modified_at: { required: false, timestamp: true },
|
|
3108
3376
|
modified_at_local: { required: false, timestamp: true },
|
|
3109
3377
|
viewed_by_me_at: { required: false, timestamp: true },
|
|
3378
|
+
modified_by_me_at: { required: false, timestamp: true },
|
|
3110
3379
|
own_ceal_write: { required: false, timestamp: false },
|
|
3111
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 },
|
|
3112
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 },
|
|
3113
3390
|
start: { required: false, timestamp: false },
|
|
3114
3391
|
end: { required: false, timestamp: false },
|
|
3115
3392
|
status: { required: false, timestamp: false },
|
|
@@ -3182,18 +3459,29 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
|
3182
3459
|
// The serving gate answers this while the Gateway is still starting and the
|
|
3183
3460
|
// refresh was never attempted; the same attempt stays retryable. It is never
|
|
3184
3461
|
// stored by the session owner, only answered at the serving edge.
|
|
3185
|
-
|
|
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"
|
|
3186
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");
|
|
3187
3475
|
|
|
3188
3476
|
// packages/ceal-client-protocol/src/request-validation-primitives.ts
|
|
3189
|
-
import { createHash } from "node:crypto";
|
|
3477
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3190
3478
|
var DIGEST = /^[a-f0-9]{64}$/u;
|
|
3191
3479
|
var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
|
|
3192
3480
|
function isCealDigest(value) {
|
|
3193
3481
|
return typeof value === "string" && DIGEST.test(value);
|
|
3194
3482
|
}
|
|
3195
3483
|
function cealRequestSha256(value) {
|
|
3196
|
-
return
|
|
3484
|
+
return createHash2("sha256").update(cealCanonicalJson(value), "utf8").digest("hex");
|
|
3197
3485
|
}
|
|
3198
3486
|
function operationRecord(value, message) {
|
|
3199
3487
|
requireOperationRecord(value, message);
|
|
@@ -3209,40 +3497,71 @@ function requirePrefixedRef(value, prefix, message) {
|
|
|
3209
3497
|
// packages/ceal-client-protocol/src/request-authority.ts
|
|
3210
3498
|
var CEAL_REQUEST_SCHEMA_VERSION = "ceal.request.v1";
|
|
3211
3499
|
var CEAL_REQUEST_ARTIFACT_OWNERS = vocabulary(["gateway", "host", "connector"]);
|
|
3212
|
-
var
|
|
3500
|
+
var CEAL_HOST_INVOCATION_IDENTITY_KEYS = vocabulary(["host_session_ref", "host_turn_ref", "host_source_ref"]);
|
|
3213
3501
|
var CEAL_REQUEST_ARTIFACT_RETENTION_CLASSES = vocabulary(["request", "source", "result"]);
|
|
3214
3502
|
var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
|
|
3215
|
-
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"]);
|
|
3216
3539
|
var CEAL_REQUEST_JSON_SCHEMA = {
|
|
3217
3540
|
type: "object",
|
|
3218
|
-
|
|
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,
|
|
3219
3543
|
additionalProperties: false,
|
|
3220
3544
|
properties: {
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
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
|
+
{
|
|
3225
3553
|
type: "object",
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
instance_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3230
|
-
access_group_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3231
|
-
principal_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3232
|
-
execution_subject_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
3233
|
-
authority_revision_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
3234
|
-
},
|
|
3235
|
-
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
|
|
3236
3557
|
},
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
},
|
|
3245
|
-
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
|
+
]
|
|
3246
3565
|
};
|
|
3247
3566
|
|
|
3248
3567
|
// packages/ceal-client-protocol/src/request-operands.ts
|
|
@@ -3353,10 +3672,6 @@ var CEAL_REQUEST_RUN_BATCH_JSON_SCHEMA = {
|
|
|
3353
3672
|
};
|
|
3354
3673
|
|
|
3355
3674
|
// packages/ceal-client-protocol/src/host-observation.ts
|
|
3356
|
-
var CEAL_HOST_INPUT_KINDS = vocabulary(["human", "host_automatic"]);
|
|
3357
|
-
function isCealHostInputKind(value) {
|
|
3358
|
-
return typeof value === "string" && CEAL_HOST_INPUT_KINDS.some((kind) => kind === value);
|
|
3359
|
-
}
|
|
3360
3675
|
var CEAL_HOST_OBSERVATION_REF_PREFIX = "host-observation:";
|
|
3361
3676
|
var HOST_OBSERVATION_PRODUCER = /^[a-z][a-z0-9]{0,31}$/u;
|
|
3362
3677
|
var HOST_OBSERVATION_DIGEST = /^[a-f0-9]{8,64}$/u;
|
|
@@ -3402,14 +3717,14 @@ function safeCount(value) {
|
|
|
3402
3717
|
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
3403
3718
|
}
|
|
3404
3719
|
function validateCealHostObservationDraft(value) {
|
|
3405
|
-
const
|
|
3406
|
-
requirePrefixedRef(
|
|
3407
|
-
requireClosedValue(
|
|
3408
|
-
requireIsoTimestamp(
|
|
3409
|
-
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);
|
|
3410
3725
|
}
|
|
3411
3726
|
var CEAL_REQUEST_MAX_OBSERVATION_VALUE_BYTES = 64 * 1024;
|
|
3412
|
-
var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "
|
|
3727
|
+
var CEAL_HOST_OBSERVATION_KINDS = vocabulary(["prompt", "session", "turn", "model", "token", "timing"]);
|
|
3413
3728
|
function isCealHostPromptObservationValue(value) {
|
|
3414
3729
|
return isOperationRecord(value) && Object.keys(value).length === 1 && isCealDigest(value.body_sha256);
|
|
3415
3730
|
}
|
|
@@ -3532,17 +3847,128 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
|
|
|
3532
3847
|
};
|
|
3533
3848
|
|
|
3534
3849
|
// packages/ceal-client-protocol/src/host-invocation-authentication.ts
|
|
3535
|
-
var
|
|
3536
|
-
var
|
|
3537
|
-
var
|
|
3538
|
-
var
|
|
3539
|
-
var
|
|
3540
|
-
var
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
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]])))}
|
|
3545
3970
|
`).byteLength;
|
|
3971
|
+
}
|
|
3546
3972
|
var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
|
|
3547
3973
|
"request_authority_unavailable",
|
|
3548
3974
|
"request_missing",
|
|
@@ -3566,6 +3992,7 @@ var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
|
|
|
3566
3992
|
"host_credential_refused",
|
|
3567
3993
|
"gateway_unavailable",
|
|
3568
3994
|
"gateway_rate_limited",
|
|
3995
|
+
"contract_mismatch",
|
|
3569
3996
|
"gateway_refused",
|
|
3570
3997
|
"gateway_response_invalid",
|
|
3571
3998
|
"producer_frame_invalid",
|
|
@@ -3580,19 +4007,23 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
3580
4007
|
invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
|
|
3581
4008
|
options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
|
|
3582
4009
|
session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
3583
|
-
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "
|
|
3584
|
-
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "
|
|
3585
|
-
no_binding
|
|
3586
|
-
|
|
3587
|
-
|
|
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" }),
|
|
3588
4018
|
host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
|
|
3589
4019
|
host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
|
|
3590
4020
|
gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
|
|
3591
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" }),
|
|
3592
4023
|
gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
|
|
3593
4024
|
gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
|
|
3594
|
-
producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin"
|
|
3595
|
-
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" }),
|
|
3596
4027
|
relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
|
|
3597
4028
|
transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
|
|
3598
4029
|
mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
|
|
@@ -3602,21 +4033,33 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
3602
4033
|
function isCealHostAuthenticationCause(value) {
|
|
3603
4034
|
return isVocabularyWord(CEAL_HOST_AUTHENTICATION_CAUSES, value);
|
|
3604
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
|
+
}
|
|
3605
4041
|
function decodeCealHostInvocationAuthenticationRequest(value) {
|
|
3606
|
-
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)) {
|
|
3607
4045
|
throw new TypeError("The Host invocation authentication request is invalid.");
|
|
3608
4046
|
}
|
|
3609
4047
|
return Object.freeze({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA, client_nonce: value.client_nonce });
|
|
3610
4048
|
}
|
|
3611
4049
|
function decodeCealHostInvocationAuthenticationResult(value, now = /* @__PURE__ */ new Date()) {
|
|
3612
|
-
|
|
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)) {
|
|
3613
4056
|
throw new TypeError("The Host invocation authentication result is invalid.");
|
|
3614
4057
|
}
|
|
3615
4058
|
return Object.freeze({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA, context_token: value.context_token, expires_at: value.expires_at });
|
|
3616
4059
|
}
|
|
3617
4060
|
function validFutureTimestamp(value, now) {
|
|
3618
|
-
const
|
|
3619
|
-
return Number.isFinite(
|
|
4061
|
+
const timestamp3 = Date.parse(value);
|
|
4062
|
+
return Number.isFinite(timestamp3) && new Date(timestamp3).toISOString() === value && timestamp3 > now.getTime();
|
|
3620
4063
|
}
|
|
3621
4064
|
function exactKeys(value, expected) {
|
|
3622
4065
|
const keys = Object.keys(value);
|
|
@@ -3630,10 +4073,6 @@ var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
|
3630
4073
|
"hook_invoked",
|
|
3631
4074
|
"registration_written",
|
|
3632
4075
|
"hook_failed",
|
|
3633
|
-
"watcher_turn_observed",
|
|
3634
|
-
"current_prompt_registered",
|
|
3635
|
-
"current_prompt_registration_failed",
|
|
3636
|
-
"current_prompt_capacity_exhausted",
|
|
3637
4076
|
"activation_recorded",
|
|
3638
4077
|
"activation_failed",
|
|
3639
4078
|
"watcher_scan_failed",
|
|
@@ -3656,6 +4095,9 @@ var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "un
|
|
|
3656
4095
|
var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
|
|
3657
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"]);
|
|
3658
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
|
+
|
|
3659
4101
|
// packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
|
|
3660
4102
|
var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
3661
4103
|
|
|
@@ -3714,6 +4156,7 @@ var ADMIN_UNAVAILABLE_REASONS = vocabulary([
|
|
|
3714
4156
|
"legacy",
|
|
3715
4157
|
"invalid_observation",
|
|
3716
4158
|
"redacted",
|
|
4159
|
+
"organization_setting_disabled",
|
|
3717
4160
|
"owner_unavailable",
|
|
3718
4161
|
"unit_unknown",
|
|
3719
4162
|
"retention_gap",
|
|
@@ -3743,15 +4186,13 @@ var ADMIN_APPROVAL_DISPOSITIONS = CEAL_OPERATION_APPROVAL_DISPOSITIONS;
|
|
|
3743
4186
|
var ADMIN_APPROVAL_POSTURES = vocabulary([...ADMIN_APPROVAL_DISPOSITIONS, "not_applicable"]);
|
|
3744
4187
|
var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "approval", "effect", "readback"]);
|
|
3745
4188
|
|
|
3746
|
-
// packages/ceal-client-protocol/src/actor-kind.ts
|
|
3747
|
-
var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
|
|
3748
|
-
|
|
3749
4189
|
// packages/ceal-client-protocol/src/device-enrollment.ts
|
|
3750
4190
|
var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
|
|
3751
4191
|
var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
|
|
3752
4192
|
|
|
3753
4193
|
// packages/ceal-client-protocol/src/personal-client-session.ts
|
|
3754
4194
|
var REFRESH_FAILURE_CODES = new Set(CEAL_REFRESH_FAILURE_CODES);
|
|
4195
|
+
var REVOKE_FAILURE_CODES = new Set(CEAL_CLIENT_REVOKE_FAILURE_CODES);
|
|
3755
4196
|
|
|
3756
4197
|
// packages/ceal-client-protocol/src/index.ts
|
|
3757
4198
|
var MAX_REQUEST_BYTES = 32 * 1024;
|
|
@@ -3843,16 +4284,59 @@ function hostClientProbeRefusal(probe, subject) {
|
|
|
3843
4284
|
}
|
|
3844
4285
|
|
|
3845
4286
|
// host/client-doctor.ts
|
|
3846
|
-
import { createHash as
|
|
4287
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
3847
4288
|
import { execFile } from "node:child_process";
|
|
3848
4289
|
import { access, open, readFile, realpath, stat } from "node:fs/promises";
|
|
3849
4290
|
import { delimiter, isAbsolute, join as join2 } from "node:path";
|
|
3850
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
|
|
3851
4335
|
var execFileAsync = promisify(execFile);
|
|
3852
4336
|
var GIT_OID = /^[a-f0-9]{40}$/u;
|
|
3853
4337
|
var SHA2562 = /^[a-f0-9]{64}$/u;
|
|
3854
4338
|
var SAFE_REF2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
|
|
3855
|
-
var CEAL_ENVIRONMENT_OVERRIDES =
|
|
4339
|
+
var CEAL_ENVIRONMENT_OVERRIDES = CEAL_CLIENT_CONTEXT_OVERRIDE_ENVIRONMENTS;
|
|
3856
4340
|
async function doctorClient(options = {}, component) {
|
|
3857
4341
|
const environment = canonicalClientEnvironment(options.environment ?? process.env);
|
|
3858
4342
|
const run = options.run ?? executeCealCommand;
|
|
@@ -3889,7 +4373,7 @@ async function inspectExecutable(options, environment, component) {
|
|
|
3889
4373
|
return { ok: true, value: executable };
|
|
3890
4374
|
}
|
|
3891
4375
|
async function inspectVersion(options, executable, environment, run, component) {
|
|
3892
|
-
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)));
|
|
3893
4377
|
if (probe.kind !== "value") return probeFailure(probe, "version", component, options);
|
|
3894
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.`);
|
|
3895
4379
|
return { ok: true, value: probe.value };
|
|
@@ -3906,8 +4390,8 @@ function probeCode(code) {
|
|
|
3906
4390
|
return "ceal_output_unreadable";
|
|
3907
4391
|
}
|
|
3908
4392
|
async function inspectCanonicalContext(options, executable, environment, run, component) {
|
|
3909
|
-
const probe = hostClientProbe(await safeRun(run, executable.path,
|
|
3910
|
-
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);
|
|
3911
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}.`);
|
|
3912
4396
|
return { ok: true, value: probe.value };
|
|
3913
4397
|
}
|
|
@@ -3928,7 +4412,7 @@ async function resolveCealExecutable(command, environment) {
|
|
|
3928
4412
|
const metadata = await stat(path);
|
|
3929
4413
|
if (!metadata.isFile()) continue;
|
|
3930
4414
|
if (await isLauncher(path)) return resolveLauncherBinary(path, environment);
|
|
3931
|
-
return { path, sha256:
|
|
4415
|
+
return { path, sha256: createHash3("sha256").update(await readFile(path)).digest("hex") };
|
|
3932
4416
|
} catch {
|
|
3933
4417
|
}
|
|
3934
4418
|
}
|
|
@@ -3947,18 +4431,19 @@ async function isLauncher(path) {
|
|
|
3947
4431
|
}
|
|
3948
4432
|
}
|
|
3949
4433
|
async function resolveLauncherBinary(launcherPath, environment) {
|
|
3950
|
-
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 } });
|
|
3951
4436
|
let parsed;
|
|
3952
4437
|
try {
|
|
3953
4438
|
parsed = JSON.parse(result.stdout);
|
|
3954
4439
|
} catch {
|
|
3955
4440
|
throw new Error("launcher doctor returned unreadable output");
|
|
3956
4441
|
}
|
|
3957
|
-
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");
|
|
3958
4443
|
const binaryPath = await realpath(parsed.binary_path);
|
|
3959
4444
|
const metadata = await stat(binaryPath);
|
|
3960
4445
|
if (!metadata.isFile()) throw new Error("launcher doctor reported a non-file binary path");
|
|
3961
|
-
return { path: binaryPath, sha256:
|
|
4446
|
+
return { path: binaryPath, sha256: createHash3("sha256").update(await readFile(binaryPath)).digest("hex") };
|
|
3962
4447
|
}
|
|
3963
4448
|
async function executeCealCommand(executable, args, environment) {
|
|
3964
4449
|
try {
|
|
@@ -3993,7 +4478,7 @@ function selectedContextRecord(item) {
|
|
|
3993
4478
|
return { name: item.name, instance_ref: item.instance_ref };
|
|
3994
4479
|
}
|
|
3995
4480
|
function versionIdentity(value) {
|
|
3996
|
-
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;
|
|
3997
4482
|
if (value.version.length === 0 || !GIT_OID.test(value.source_commit) || !GIT_OID.test(value.source_tree)) return null;
|
|
3998
4483
|
return { version: value.version, source_commit: value.source_commit, source_tree: value.source_tree };
|
|
3999
4484
|
}
|
|
@@ -4017,16 +4502,9 @@ async function doctorCodexClient(options = {}) {
|
|
|
4017
4502
|
import { performance } from "node:perf_hooks";
|
|
4018
4503
|
|
|
4019
4504
|
// host/relay/src/producer-frame.ts
|
|
4020
|
-
var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
|
|
4021
|
-
var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
|
|
4022
4505
|
var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
4023
|
-
var
|
|
4024
|
-
var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
|
|
4025
|
-
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"];
|
|
4026
4507
|
var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
|
|
4027
|
-
function isHostProducerFrameKind(value) {
|
|
4028
|
-
return typeof value === "string" && FRAME_KINDS.has(value);
|
|
4029
|
-
}
|
|
4030
4508
|
var HOST_PRODUCER_FRAME_ERROR_CODES = [
|
|
4031
4509
|
"invalid_frame_encoding",
|
|
4032
4510
|
"invalid_frame_length",
|
|
@@ -4036,473 +4514,205 @@ var HOST_PRODUCER_FRAME_ERROR_CODES = [
|
|
|
4036
4514
|
"invalid_producer_identity",
|
|
4037
4515
|
"frame_too_large"
|
|
4038
4516
|
];
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
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) {
|
|
4042
4531
|
super(message);
|
|
4043
|
-
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";
|
|
4044
4540
|
}
|
|
4045
4541
|
};
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
return
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
const
|
|
4062
|
-
const text = new TextDecoder("utf-8", { fatal: true });
|
|
4063
|
-
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);
|
|
4064
4558
|
try {
|
|
4065
|
-
|
|
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;
|
|
4066
4564
|
} catch (error) {
|
|
4067
|
-
|
|
4068
|
-
throw
|
|
4565
|
+
await handle.close();
|
|
4566
|
+
throw error;
|
|
4069
4567
|
}
|
|
4070
|
-
validateFrame(value);
|
|
4071
|
-
return value;
|
|
4072
4568
|
}
|
|
4073
|
-
function
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
if (
|
|
4082
|
-
|
|
4083
|
-
}
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
if (
|
|
4087
|
-
}
|
|
4088
|
-
function
|
|
4089
|
-
|
|
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
|
+
});
|
|
4090
4614
|
}
|
|
4091
|
-
function
|
|
4092
|
-
|
|
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.");
|
|
4093
4629
|
}
|
|
4094
|
-
function
|
|
4095
|
-
|
|
4630
|
+
function resolveInstalledHostExecutable() {
|
|
4631
|
+
const privateRuntimeRoot = dirname(fileURLToPath(import.meta.url));
|
|
4632
|
+
return win322.join(privateRuntimeRoot, "..", "..", "bin", "ceal-host.exe");
|
|
4096
4633
|
}
|
|
4097
|
-
function
|
|
4098
|
-
return
|
|
4634
|
+
function isRecord2(value) {
|
|
4635
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4099
4636
|
}
|
|
4100
|
-
function
|
|
4101
|
-
return
|
|
4637
|
+
function isOwnedBy(uid, expectedUid) {
|
|
4638
|
+
return Number.isSafeInteger(uid) && Number.isSafeInteger(expectedUid) && uid === expectedUid;
|
|
4102
4639
|
}
|
|
4103
|
-
function
|
|
4104
|
-
|
|
4640
|
+
function isSelfOwned(uid, platform = process.platform, _handle) {
|
|
4641
|
+
if (platform === "win32" || typeof process.geteuid !== "function") return false;
|
|
4642
|
+
return isOwnedBy(uid, process.geteuid());
|
|
4105
4643
|
}
|
|
4106
|
-
function
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
function validStatus(value) {
|
|
4110
|
-
return exactOptional(value, ["status", "reason_code"], ["detail"]) && ["ready", "degraded", "stopping"].includes(String(value.status)) && safeRef(value.reason_code) && optionalText(value.detail, 4096);
|
|
4111
|
-
}
|
|
4112
|
-
function optionalText(value, maxLength) {
|
|
4113
|
-
return value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maxLength;
|
|
4114
|
-
}
|
|
4115
|
-
function optionalNonNegative(value) {
|
|
4116
|
-
return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
4117
|
-
}
|
|
4118
|
-
function optionalRefs(value) {
|
|
4119
|
-
return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef);
|
|
4120
|
-
}
|
|
4121
|
-
function validObservations(value) {
|
|
4122
|
-
if (value === void 0) return true;
|
|
4123
|
-
if (!Array.isArray(value) || value.length > 32) return false;
|
|
4124
|
-
return value.every((item) => {
|
|
4125
|
-
try {
|
|
4126
|
-
validateCealHostObservationDraft(item);
|
|
4127
|
-
return true;
|
|
4128
|
-
} catch {
|
|
4129
|
-
return false;
|
|
4130
|
-
}
|
|
4131
|
-
});
|
|
4132
|
-
}
|
|
4133
|
-
function jsonValue(value, depth = 0) {
|
|
4134
|
-
if (depth > 16) return false;
|
|
4135
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
4136
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
4137
|
-
if (Array.isArray(value)) return value.length <= 256 && value.every((item) => jsonValue(item, depth + 1));
|
|
4138
|
-
return record(value) && Object.keys(value).length <= 256 && Object.entries(value).every(([key, item]) => key.length > 0 && key.length <= 128 && jsonValue(item, depth + 1));
|
|
4139
|
-
}
|
|
4140
|
-
function record(value) {
|
|
4141
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4142
|
-
}
|
|
4143
|
-
function safeRef(value) {
|
|
4144
|
-
return typeof value === "string" && SAFE_REF3.test(value);
|
|
4145
|
-
}
|
|
4146
|
-
function positive(value) {
|
|
4147
|
-
return Number.isSafeInteger(value) && Number(value) > 0;
|
|
4148
|
-
}
|
|
4149
|
-
function timestamp(value) {
|
|
4150
|
-
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
4151
|
-
}
|
|
4152
|
-
function hostProducerFrameKind(value) {
|
|
4153
|
-
return isHostProducerFrameKind(value);
|
|
4154
|
-
}
|
|
4155
|
-
function exactKeys2(value, keys) {
|
|
4156
|
-
return JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
|
|
4157
|
-
}
|
|
4158
|
-
function exactOptional(value, required, optional) {
|
|
4159
|
-
const keys = Object.keys(value);
|
|
4160
|
-
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;
|
|
4161
4647
|
}
|
|
4162
4648
|
|
|
4163
4649
|
// host/relay/src/host-producer-core.ts
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
import { join as join3 } from "node:path";
|
|
4168
|
-
|
|
4169
|
-
// host/relay/src/relay-ipc.ts
|
|
4170
|
-
import { createConnection, createServer } from "node:net";
|
|
4171
|
-
|
|
4172
|
-
// host/relay/src/durable-atomic-replacement.ts
|
|
4173
|
-
import { O_RDONLY } from "node:constants";
|
|
4174
|
-
import { lstat, mkdir, open as open2, rename, unlink } from "node:fs/promises";
|
|
4175
|
-
var DEFAULT_PORT = {
|
|
4176
|
-
platform: process.platform,
|
|
4177
|
-
mkdir: (path, options) => mkdir(path, options),
|
|
4178
|
-
lstat: (path) => lstat(path),
|
|
4179
|
-
open_file: (path) => open2(path, "wx", 384),
|
|
4180
|
-
rename,
|
|
4181
|
-
unlink,
|
|
4182
|
-
open_directory: (path) => open2(path, O_RDONLY),
|
|
4183
|
-
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
4184
|
-
};
|
|
4185
|
-
|
|
4186
|
-
// host/relay/src/relay-request-binding-store.ts
|
|
4187
|
-
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
4188
|
-
|
|
4189
|
-
// host/relay/src/current-prompt-custody.ts
|
|
4190
|
-
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
4191
|
-
|
|
4192
|
-
// host/service-log.ts
|
|
4193
|
-
function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
|
|
4194
|
-
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 } : {} })}
|
|
4195
|
-
`;
|
|
4196
|
-
try {
|
|
4197
|
-
(sink ?? defaultHostServiceDiagnosticSink)(line);
|
|
4198
|
-
} catch {
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
function defaultHostServiceDiagnosticSink(line) {
|
|
4202
|
-
process.stderr.write(line);
|
|
4203
|
-
}
|
|
4204
|
-
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
4205
|
-
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
4206
|
-
|
|
4207
|
-
// host/relay/src/relay-ipc.ts
|
|
4208
|
-
var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
4209
|
-
"schema_version",
|
|
4210
|
-
"producer_id",
|
|
4211
|
-
"producer_installation_ref",
|
|
4212
|
-
"local_session_ref",
|
|
4213
|
-
"local_turn_ref",
|
|
4214
|
-
"local_turn_aliases",
|
|
4215
|
-
"prompt_ref",
|
|
4216
|
-
"request_ref",
|
|
4217
|
-
"bound_at",
|
|
4218
|
-
"host_source_ref",
|
|
4219
|
-
"host_session_ref",
|
|
4220
|
-
"host_turn_ref",
|
|
4221
|
-
"host_input_ref"
|
|
4222
|
-
]);
|
|
4223
|
-
var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
|
|
4224
|
-
var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v1";
|
|
4225
|
-
var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v1";
|
|
4226
|
-
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
4227
|
-
var HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA = "ceal.host_relay_current_prompt_release.v1";
|
|
4228
|
-
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
4229
|
-
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
4230
|
-
var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
|
|
4231
|
-
var HOST_RELAY_QUERY_RESPONSE_MAX_BYTES = 8192;
|
|
4232
|
-
var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
4233
|
-
"context_stale",
|
|
4234
|
-
"context_expired",
|
|
4235
|
-
"host_source_refused",
|
|
4236
|
-
"host_credential_refused",
|
|
4237
|
-
"gateway_unavailable",
|
|
4238
|
-
"gateway_refused",
|
|
4239
|
-
"gateway_response_invalid",
|
|
4240
|
-
"gateway_rate_limited",
|
|
4241
|
-
"producer_frame_invalid",
|
|
4242
|
-
"deadline",
|
|
4243
|
-
"cancelled",
|
|
4244
|
-
"mint_failed",
|
|
4245
|
-
"relay_failed",
|
|
4246
|
-
"request_authority_unavailable",
|
|
4247
|
-
"request_missing",
|
|
4248
|
-
"authority_mismatch",
|
|
4249
|
-
"host_source_mismatch",
|
|
4250
|
-
"host_session_mismatch",
|
|
4251
|
-
"host_turn_mismatch",
|
|
4252
|
-
"host_input_mismatch",
|
|
4253
|
-
"host_prompt_mismatch"
|
|
4254
|
-
]);
|
|
4255
|
-
var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
|
|
4256
|
-
"context_stale",
|
|
4257
|
-
"context_expired",
|
|
4258
|
-
"host_source_refused",
|
|
4259
|
-
"host_credential_refused",
|
|
4260
|
-
"gateway_unavailable",
|
|
4261
|
-
"gateway_rate_limited",
|
|
4262
|
-
"gateway_refused",
|
|
4263
|
-
"gateway_response_invalid",
|
|
4264
|
-
"producer_frame_invalid"
|
|
4265
|
-
]);
|
|
4266
|
-
var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
|
|
4267
|
-
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
4268
|
-
"permission_denied",
|
|
4269
|
-
"socket_missing",
|
|
4270
|
-
"connection_refused",
|
|
4271
|
-
"closed_early",
|
|
4272
|
-
"response_invalid",
|
|
4273
|
-
"timed_out",
|
|
4274
|
-
"cancelled",
|
|
4275
|
-
"unavailable"
|
|
4276
|
-
]);
|
|
4277
|
-
var HostRelayIpcError = class extends Error {
|
|
4278
|
-
code;
|
|
4279
|
-
/** Set only when the client-side socket exchange itself failed. */
|
|
4280
|
-
failure;
|
|
4281
|
-
constructor(code, message = code, failure2) {
|
|
4282
|
-
super(message);
|
|
4283
|
-
this.code = code;
|
|
4284
|
-
this.failure = failure2;
|
|
4285
|
-
}
|
|
4286
|
-
};
|
|
4287
|
-
var HostRelayCurrentPromptCapacityRefusal = class extends Error {
|
|
4288
|
-
constructor() {
|
|
4289
|
-
super("current_prompt_capacity_exhausted");
|
|
4290
|
-
this.name = "HostRelayCurrentPromptCapacityRefusal";
|
|
4291
|
-
}
|
|
4292
|
-
};
|
|
4293
|
-
function hostRelayExchangeFailure(error) {
|
|
4294
|
-
return error instanceof HostRelayIpcError ? error.failure : void 0;
|
|
4295
|
-
}
|
|
4296
|
-
function socketErrorFailure(error) {
|
|
4297
|
-
switch (errorCode2(error)) {
|
|
4298
|
-
case "EACCES":
|
|
4299
|
-
case "EPERM":
|
|
4300
|
-
return "permission_denied";
|
|
4301
|
-
case "ENOENT":
|
|
4302
|
-
return "socket_missing";
|
|
4303
|
-
case "ECONNREFUSED":
|
|
4304
|
-
return "connection_refused";
|
|
4305
|
-
default:
|
|
4306
|
-
return "unavailable";
|
|
4307
|
-
}
|
|
4308
|
-
}
|
|
4309
|
-
var HostRelayInvocationContextPort = class {
|
|
4310
|
-
endpoint;
|
|
4311
|
-
constructor(endpoint) {
|
|
4312
|
-
this.endpoint = endpoint;
|
|
4313
|
-
}
|
|
4314
|
-
mintInvocationContext(input, signal) {
|
|
4315
|
-
return queryHostRelayInvocationContext(this.endpoint, input, signal);
|
|
4316
|
-
}
|
|
4317
|
-
};
|
|
4318
|
-
var HostRelayIpcAdmissionPort = class {
|
|
4319
|
-
endpoint;
|
|
4320
|
-
timeout_ms;
|
|
4321
|
-
constructor(endpoint, timeoutMs = 5e3) {
|
|
4322
|
-
this.endpoint = endpoint;
|
|
4323
|
-
this.timeout_ms = timeoutMs;
|
|
4324
|
-
}
|
|
4325
|
-
async admit(frame) {
|
|
4326
|
-
const response2 = await submitHostProducerFrame(this.endpoint, frame, this.timeout_ms);
|
|
4327
|
-
if (!relayAdmissionCode(response2.code)) throw new HostRelayIpcError(response2.code);
|
|
4328
|
-
return { code: response2.code, admitted_through_sequence: response2.admitted_through_sequence, delivered_through_sequence: response2.delivered_through_sequence };
|
|
4329
|
-
}
|
|
4330
|
-
};
|
|
4331
|
-
var HostRelayCurrentPromptPort = class {
|
|
4332
|
-
endpoint;
|
|
4333
|
-
timeout_ms;
|
|
4334
|
-
constructor(endpoint, timeoutMs = 5e3) {
|
|
4335
|
-
this.endpoint = endpoint;
|
|
4336
|
-
this.timeout_ms = timeoutMs;
|
|
4337
|
-
}
|
|
4338
|
-
async register(input) {
|
|
4339
|
-
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
|
|
4340
|
-
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
4341
|
-
if (response2.status === "capacity_exhausted") throw new HostRelayCurrentPromptCapacityRefusal();
|
|
4342
|
-
if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
|
|
4343
|
-
}
|
|
4344
|
-
async release(identity) {
|
|
4345
|
-
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA, ...identity };
|
|
4346
|
-
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
4347
|
-
if (response2.status !== "released") throw new HostRelayIpcError("relay_unavailable", response2.status, "response_invalid");
|
|
4348
|
-
}
|
|
4349
|
-
};
|
|
4350
|
-
function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
|
|
4351
|
-
return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
|
|
4352
|
-
}
|
|
4353
|
-
function queryHostRelayInvocationContext(endpoint, query, signal) {
|
|
4354
|
-
return exchange(endpoint, encodeInvocationContextQuery(query), void 0, decodeInvocationContextResponseBuffer, signal);
|
|
4355
|
-
}
|
|
4356
|
-
function exchange(endpoint, encoded, timeoutMs, decode, signal) {
|
|
4357
|
-
return new Promise((resolve, reject2) => {
|
|
4358
|
-
const socket = createConnection(endpoint);
|
|
4359
|
-
let buffer = Buffer.alloc(0);
|
|
4360
|
-
let settled = false;
|
|
4361
|
-
const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
|
|
4362
|
-
const finish = (error, response2) => {
|
|
4363
|
-
if (settled) return;
|
|
4364
|
-
settled = true;
|
|
4365
|
-
signal?.removeEventListener("abort", aborted);
|
|
4366
|
-
socket.destroy();
|
|
4367
|
-
if (error !== null) reject2(error);
|
|
4368
|
-
else if (response2 !== void 0) resolve(response2);
|
|
4369
|
-
};
|
|
4370
|
-
if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
|
|
4371
|
-
if (signal?.aborted === true) {
|
|
4372
|
-
aborted();
|
|
4373
|
-
return;
|
|
4374
|
-
}
|
|
4375
|
-
signal?.addEventListener("abort", aborted, { once: true });
|
|
4376
|
-
socket.once("error", (error) => {
|
|
4377
|
-
const failure2 = socketErrorFailure(error);
|
|
4378
|
-
finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure2}.`, failure2));
|
|
4379
|
-
});
|
|
4380
|
-
socket.once("connect", () => socket.write(encoded));
|
|
4381
|
-
socket.on("data", (chunk) => {
|
|
4382
|
-
try {
|
|
4383
|
-
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
|
4384
|
-
const decoded = decode(buffer);
|
|
4385
|
-
if (decoded !== null) finish(null, decoded);
|
|
4386
|
-
} catch (error) {
|
|
4387
|
-
finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
|
|
4388
|
-
}
|
|
4389
|
-
});
|
|
4390
|
-
socket.once("end", () => {
|
|
4391
|
-
if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
|
|
4392
|
-
});
|
|
4393
|
-
});
|
|
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);
|
|
4394
4653
|
}
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
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);
|
|
4400
4660
|
try {
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
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();
|
|
4404
4669
|
}
|
|
4405
|
-
if (!validResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response shape is invalid.");
|
|
4406
|
-
return value;
|
|
4407
|
-
}
|
|
4408
|
-
function relayAdmissionCode(value) {
|
|
4409
|
-
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";
|
|
4410
|
-
}
|
|
4411
|
-
function validResponse(value) {
|
|
4412
|
-
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;
|
|
4413
|
-
}
|
|
4414
|
-
function hostRelayIpcCode(value) {
|
|
4415
|
-
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);
|
|
4416
|
-
}
|
|
4417
|
-
function nonnegative(value) {
|
|
4418
|
-
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
4419
|
-
}
|
|
4420
|
-
function record2(value) {
|
|
4421
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4422
|
-
}
|
|
4423
|
-
function decodeResponseBuffer(buffer) {
|
|
4424
|
-
if (buffer.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
4425
|
-
const length = buffer.readUInt32BE(0);
|
|
4426
|
-
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}`)}.`);
|
|
4427
|
-
if (buffer.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
4428
|
-
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}` }])}.`);
|
|
4429
|
-
return decodeHostRelayIpcResponse(buffer);
|
|
4430
|
-
}
|
|
4431
|
-
function decodeInvocationContextResponseBuffer(buffer) {
|
|
4432
|
-
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_QUERY_RESPONSE_MAX_BYTES);
|
|
4433
|
-
if (value === null) return null;
|
|
4434
|
-
if (!validInvocationContextResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay invocation context response is invalid.");
|
|
4435
|
-
return value;
|
|
4436
|
-
}
|
|
4437
|
-
function decodeCurrentPromptResponseBuffer(buffer) {
|
|
4438
|
-
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
|
|
4439
|
-
if (value === null) return null;
|
|
4440
|
-
if (!record2(value) || !exactKeys3(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "capacity_exhausted" && value.status !== "current_prompt_unavailable" && value.status !== "released") throw new HostRelayIpcError("relay_unavailable", "Host Relay current-prompt response is invalid.", "response_invalid");
|
|
4441
|
-
return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
|
|
4442
|
-
}
|
|
4443
|
-
function encodeInvocationContextQuery(query) {
|
|
4444
|
-
const value = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA, ...query };
|
|
4445
|
-
if (!validInvocationContextQueryRequest(value)) throw new HostRelayIpcError("invalid_frame_shape");
|
|
4446
|
-
return encodeLengthPrefixed(value);
|
|
4447
4670
|
}
|
|
4448
|
-
function
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
|
|
4456
|
-
}
|
|
4457
|
-
function validInvocationContextResponse(value) {
|
|
4458
|
-
if (!record2(value) || value.schema_version !== HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA) return false;
|
|
4459
|
-
if (value.status === "no_binding") return Object.keys(value).length === 2;
|
|
4460
|
-
if (value.status === "refused") return Object.keys(value).length === 3 && isVocabularyWord(HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS, value.reason);
|
|
4461
|
-
if (value.status !== "ready" || Object.keys(value).length !== 3) return false;
|
|
4462
|
-
try {
|
|
4463
|
-
decodeCealHostInvocationAuthenticationResult(value.result);
|
|
4464
|
-
return true;
|
|
4465
|
-
} catch {
|
|
4466
|
-
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;
|
|
4467
4678
|
}
|
|
4679
|
+
return output.subarray(0, offset);
|
|
4468
4680
|
}
|
|
4469
|
-
function
|
|
4470
|
-
if (
|
|
4471
|
-
const
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
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) {
|
|
4475
4687
|
try {
|
|
4476
|
-
|
|
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));
|
|
4477
4691
|
} catch {
|
|
4478
|
-
throw new
|
|
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);
|
|
4479
4713
|
}
|
|
4714
|
+
return boundCealHostInvocationHumanMessageWindow(newestFirst, truncated);
|
|
4480
4715
|
}
|
|
4481
|
-
function encodeLengthPrefixed(value) {
|
|
4482
|
-
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
4483
|
-
const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
|
|
4484
|
-
output.writeUInt32BE(body.length, 0);
|
|
4485
|
-
body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
|
|
4486
|
-
return output;
|
|
4487
|
-
}
|
|
4488
|
-
function exactKeys3(value, required, optional = []) {
|
|
4489
|
-
const keys = Object.keys(value);
|
|
4490
|
-
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
4491
|
-
}
|
|
4492
|
-
function safeRef2(value) {
|
|
4493
|
-
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
4494
|
-
}
|
|
4495
|
-
function errorCode2(error) {
|
|
4496
|
-
return record2(error) && typeof error.code === "string" ? error.code : null;
|
|
4497
|
-
}
|
|
4498
|
-
|
|
4499
|
-
// host/relay/src/host-producer-core.ts
|
|
4500
|
-
var HOST_TRANSCRIPT_CURSOR_REASONS = vocabulary(["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"]);
|
|
4501
|
-
function isHostTranscriptCursorReason(value) {
|
|
4502
|
-
return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
|
|
4503
|
-
}
|
|
4504
|
-
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
4505
|
-
var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
|
|
4506
4716
|
function transcriptScanFailureKind(error, adapterFailureKind) {
|
|
4507
4717
|
if (transcriptErrorCode(error) === "ELOOP") return "symlink";
|
|
4508
4718
|
const adapter = adapterFailureKind(error);
|
|
@@ -4512,16 +4722,6 @@ function transcriptScanFailureKind(error, adapterFailureKind) {
|
|
|
4512
4722
|
if (code !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code)) return "read_error";
|
|
4513
4723
|
return "other";
|
|
4514
4724
|
}
|
|
4515
|
-
async function registerHostCurrentPromptDiagnosed(register, diagnostic) {
|
|
4516
|
-
try {
|
|
4517
|
-
await register();
|
|
4518
|
-
diagnostic("current_prompt_registered");
|
|
4519
|
-
return true;
|
|
4520
|
-
} catch (error) {
|
|
4521
|
-
if (!(error instanceof HostRelayCurrentPromptCapacityRefusal)) diagnostic("current_prompt_registration_failed");
|
|
4522
|
-
throw error;
|
|
4523
|
-
}
|
|
4524
|
-
}
|
|
4525
4725
|
function transcriptErrorCode(error) {
|
|
4526
4726
|
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
4527
4727
|
}
|
|
@@ -4569,10 +4769,10 @@ async function transcriptCursorBoundsReason(handle, cursor, size) {
|
|
|
4569
4769
|
return null;
|
|
4570
4770
|
}
|
|
4571
4771
|
function transcriptSha256(value) {
|
|
4572
|
-
return
|
|
4772
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
4573
4773
|
}
|
|
4574
4774
|
function transcriptObjectSha256(value) {
|
|
4575
|
-
return
|
|
4775
|
+
return createHash4("sha256").update(JSON.stringify(value), "utf8").digest("hex");
|
|
4576
4776
|
}
|
|
4577
4777
|
function isTranscriptRecord(value) {
|
|
4578
4778
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -4633,24 +4833,18 @@ var HostTranscriptWatcher = class {
|
|
|
4633
4833
|
return scanTranscriptRegistrations(await this.listRegistrations(), (registration) => this.scanRegistration(registration));
|
|
4634
4834
|
}
|
|
4635
4835
|
async scanRegistration(registration) {
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
});
|
|
4641
|
-
} catch (error) {
|
|
4642
|
-
if (error instanceof HostRelayCurrentPromptCapacityRefusal) return { records: 0, enqueued: 0, quarantined: false };
|
|
4643
|
-
throw error;
|
|
4644
|
-
}
|
|
4836
|
+
return this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
|
|
4837
|
+
missing: () => this.retireMissing(registration),
|
|
4838
|
+
failed: (handle, error) => this.scanFailed(handle, registration, error)
|
|
4839
|
+
});
|
|
4645
4840
|
}
|
|
4646
4841
|
async openTranscript(registration, scan, handlers) {
|
|
4647
4842
|
let handle;
|
|
4648
4843
|
try {
|
|
4649
|
-
handle = await
|
|
4844
|
+
handle = await openNoFollow(this.transcriptPathOf(registration));
|
|
4650
4845
|
return await scan(handle);
|
|
4651
4846
|
} catch (error) {
|
|
4652
4847
|
if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
|
|
4653
|
-
if (hostRelayExchangeFailure(error) !== void 0 || error instanceof HostRelayCurrentPromptCapacityRefusal) throw error;
|
|
4654
4848
|
return handlers.failed(handle, error);
|
|
4655
4849
|
} finally {
|
|
4656
4850
|
await handle?.close();
|
|
@@ -4696,29 +4890,12 @@ var HostTranscriptWatcher = class {
|
|
|
4696
4890
|
return transcriptCursorBoundsReason(handle, cursor, size);
|
|
4697
4891
|
}
|
|
4698
4892
|
};
|
|
4699
|
-
var HOST_PRODUCER_STATE_IDENTITY = {
|
|
4700
|
-
codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
|
|
4701
|
-
claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
|
|
4702
|
-
};
|
|
4703
4893
|
var HostProducerDrainCore = class {
|
|
4704
4894
|
host;
|
|
4705
|
-
relay;
|
|
4706
|
-
statePath;
|
|
4707
|
-
stateSchema;
|
|
4708
|
-
installationRef;
|
|
4709
|
-
epoch;
|
|
4710
4895
|
now;
|
|
4711
4896
|
tail = Promise.resolve();
|
|
4712
4897
|
constructor(host, runtime) {
|
|
4713
|
-
if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
|
|
4714
|
-
const epoch = runtime.producer_epoch ?? 1;
|
|
4715
|
-
if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
|
|
4716
4898
|
this.host = host;
|
|
4717
|
-
this.relay = runtime.relay;
|
|
4718
|
-
this.statePath = join3(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
|
|
4719
|
-
this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
|
|
4720
|
-
this.installationRef = runtime.producer_installation_ref;
|
|
4721
|
-
this.epoch = epoch;
|
|
4722
4899
|
this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
|
|
4723
4900
|
}
|
|
4724
4901
|
drainOutbox() {
|
|
@@ -4728,145 +4905,563 @@ var HostProducerDrainCore = class {
|
|
|
4728
4905
|
}
|
|
4729
4906
|
async drainLocked() {
|
|
4730
4907
|
const read = await this.host.listEnvelopes();
|
|
4731
|
-
let
|
|
4908
|
+
let removed = 0;
|
|
4732
4909
|
let expired = 0;
|
|
4733
|
-
let retained = 0;
|
|
4734
4910
|
for (const envelope of read.entries) {
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
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;
|
|
4740
5130
|
}
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
|
|
4746
|
-
complete = false;
|
|
4747
|
-
break;
|
|
4748
|
-
}
|
|
5131
|
+
if (frame !== null) {
|
|
5132
|
+
buffer = frame.rest;
|
|
5133
|
+
resolve({ value: frame.value, rest: Buffer.alloc(0) });
|
|
5134
|
+
return;
|
|
4749
5135
|
}
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
5136
|
+
pending = { resolve, reject: reject2 };
|
|
5137
|
+
}),
|
|
5138
|
+
cancel: (error) => {
|
|
5139
|
+
if (pending !== null) {
|
|
5140
|
+
const waiting = pending;
|
|
5141
|
+
pending = null;
|
|
5142
|
+
waiting.reject(error);
|
|
4753
5143
|
}
|
|
4754
|
-
await this.host.removeEnvelope(envelope);
|
|
4755
|
-
await this.release(this.host.submissionRefOf(envelope));
|
|
4756
|
-
admitted += 1;
|
|
4757
5144
|
}
|
|
4758
|
-
|
|
4759
|
-
}
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
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();
|
|
4781
5190
|
}
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
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;
|
|
5228
|
+
}
|
|
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;
|
|
4788
5254
|
}
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
const value = JSON.parse(await readFile2(this.statePath, "utf8"));
|
|
4792
|
-
return this.decodeState(value);
|
|
4793
|
-
} catch (error) {
|
|
4794
|
-
if (errorCode3(error) !== "ENOENT") throw error;
|
|
4795
|
-
return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
|
|
4796
|
-
}
|
|
5255
|
+
mintInvocationContext(input, signal) {
|
|
5256
|
+
return queryHostRelayInvocationContext(this.endpoint, input, this.timeout_ms, signal, this.pipe_key);
|
|
4797
5257
|
}
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
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;
|
|
4812
5280
|
}
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
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);
|
|
4819
5317
|
}
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
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;
|
|
4835
5348
|
}
|
|
4836
|
-
}
|
|
4837
|
-
function
|
|
4838
|
-
|
|
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);
|
|
4839
5397
|
}
|
|
4840
5398
|
function errorCode3(error) {
|
|
4841
|
-
return
|
|
5399
|
+
return record(error) && typeof error.code === "string" ? error.code : null;
|
|
4842
5400
|
}
|
|
4843
5401
|
|
|
4844
|
-
// host/
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
var
|
|
5402
|
+
// host/service-log.ts
|
|
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
|
+
`;
|
|
5409
|
+
try {
|
|
5410
|
+
(sink ?? defaultHostServiceDiagnosticSink)(line);
|
|
5411
|
+
} catch {
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
function defaultHostServiceDiagnosticSink(line) {
|
|
5415
|
+
writeHostServiceLogLine(line);
|
|
5416
|
+
process.stderr.write(line);
|
|
5417
|
+
}
|
|
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
|
+
}
|
|
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
|
+
}
|
|
4848
5443
|
|
|
4849
|
-
// host/
|
|
4850
|
-
var
|
|
4851
|
-
var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
4852
|
-
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
5444
|
+
// host/local-capability-descriptor.ts
|
|
5445
|
+
var HOST_LOCAL_CAPABILITY_AUTHENTICATION_SUFFIX = Object.freeze(["authenticate", "--client-nonce"]);
|
|
4853
5446
|
|
|
4854
5447
|
// host/invocation-authentication-command.ts
|
|
4855
|
-
var HOST_INVOCATION_AUTHENTICATION_COMMAND_PREFIX = ["authenticate", "--client-nonce"];
|
|
4856
|
-
var HOST_AUTHENTICATION_BUDGET_ENVIRONMENT = "CEAL_HOST_AUTHENTICATION_BUDGET_MS";
|
|
4857
5448
|
var HOST_CONTEXT_ACTIVATION_WAIT_MAX_MS = 2e4;
|
|
4858
5449
|
var CODEX_CONTEXT_ACTIVATION_WAIT_MAX_MS = HOST_CONTEXT_ACTIVATION_WAIT_MAX_MS;
|
|
4859
5450
|
var HOST_AUTHENTICATION_COMPLETION_RESERVE_MS = 5e3;
|
|
4860
5451
|
var INVOCATION_CONTEXT_POLL_INTERVAL_MS = 250;
|
|
4861
5452
|
var REFUSAL_REASON_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
4862
5453
|
function installedHostInvocationAuthenticationCommandOptions(config, contextBudgetMs, maxContextWaitMs) {
|
|
4863
|
-
|
|
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
|
+
};
|
|
4864
5459
|
return {
|
|
4865
5460
|
host_source_ref: config.host_source_ref,
|
|
4866
5461
|
outbox_dir: config.outbox_dir,
|
|
4867
5462
|
producer_installation_ref: config.producer_installation_ref,
|
|
4868
5463
|
context_activation_wait_ms: boundedContextActivationWait(contextBudgetMs, config.ipc_timeout_ms, maxContextWaitMs),
|
|
4869
|
-
mint_invocation_context: (input) =>
|
|
5464
|
+
mint_invocation_context: async (input) => new HostRelayInvocationContextPort(config.relay_endpoint, await loadPipeKey(), config.ipc_timeout_ms).mintInvocationContext(input)
|
|
4870
5465
|
};
|
|
4871
5466
|
}
|
|
4872
5467
|
async function runHostInvocationAuthenticationCommand(descriptor2, args, options) {
|
|
@@ -4887,26 +5482,31 @@ function authenticationRefusalReason(error) {
|
|
|
4887
5482
|
return isCealHostAuthenticationCause(message) ? message : "host_refusal_invalid";
|
|
4888
5483
|
}
|
|
4889
5484
|
async function authenticateHostInvocation(descriptor2, input, options) {
|
|
4890
|
-
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");
|
|
4891
5486
|
const request2 = decodeCealHostInvocationAuthenticationRequest({ schema_version: CEAL_HOST_INVOCATION_AUTHENTICATION_REQUEST_SCHEMA, client_nonce: input[2] });
|
|
4892
5487
|
const config = validateOptions(options);
|
|
4893
5488
|
const now = config.now ?? (() => /* @__PURE__ */ new Date());
|
|
4894
5489
|
const { store, current } = await currentInvocation(descriptor2, config, now);
|
|
4895
5490
|
if (config.mint_invocation_context === void 0) throw new TypeError("relay_failed");
|
|
4896
|
-
const query =
|
|
4897
|
-
producer_id: descriptor2.producer_id,
|
|
4898
|
-
producer_installation_ref: config.producer_installation_ref,
|
|
4899
|
-
local_session_ref: current.identity.host_session_ref,
|
|
4900
|
-
local_turn_ref: current.identity.host_turn_ref,
|
|
4901
|
-
client_nonce: request2.client_nonce,
|
|
4902
|
-
...await visibleOutputBudget(descriptor2)
|
|
4903
|
-
};
|
|
5491
|
+
const query = await invocationMintQuery(descriptor2, store, current, request2.client_nonce, config);
|
|
4904
5492
|
const response2 = await mintedResult(config.mint_invocation_context, query);
|
|
4905
5493
|
const completedAt = now();
|
|
4906
5494
|
const rechecked = await store.readInvocationContext(current.identity.host_source_ref, current.identity.host_session_ref, completedAt);
|
|
4907
5495
|
if (rechecked === null || !sameTurn(current, rechecked)) throw new TypeError("context_replaced");
|
|
4908
5496
|
return decodeCealHostInvocationAuthenticationResult(response2.result, completedAt);
|
|
4909
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
|
+
}
|
|
4910
5510
|
async function visibleOutputBudget(descriptor2) {
|
|
4911
5511
|
const bytes = await descriptor2.resolve_visible_output_max_bytes?.();
|
|
4912
5512
|
return bytes === void 0 ? {} : { visible_output_max_bytes: bytes };
|
|
@@ -4916,30 +5516,15 @@ async function mintedResult(mint, query) {
|
|
|
4916
5516
|
try {
|
|
4917
5517
|
response2 = await mint(query);
|
|
4918
5518
|
} catch (error) {
|
|
4919
|
-
throw new TypeError(
|
|
5519
|
+
throw new TypeError(cealHostAuthenticationCauseForExchangeFailure(hostRelayExchangeFailure(error) ?? "unavailable"));
|
|
4920
5520
|
}
|
|
4921
|
-
if (response2.status
|
|
4922
|
-
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");
|
|
4923
5522
|
return response2;
|
|
4924
5523
|
}
|
|
4925
|
-
function mintRefusalCause(reason) {
|
|
4926
|
-
if (isDirectMintRefusalCause(reason)) return reason;
|
|
4927
|
-
if (reason === "deadline" || reason === "cancelled") return "transient_retryable";
|
|
4928
|
-
if (reason === "mint_failed" || reason === "relay_failed") return reason;
|
|
4929
|
-
return isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, reason) ? "context_stale" : "host_refusal_invalid";
|
|
4930
|
-
}
|
|
4931
|
-
function isDirectMintRefusalCause(reason) {
|
|
4932
|
-
return isVocabularyWord(HOST_RELAY_DIRECT_WIRE_MINT_REASONS, reason);
|
|
4933
|
-
}
|
|
4934
|
-
function exchangeFailureCause(failure2) {
|
|
4935
|
-
if (failure2 === "permission_denied") return "relay_permission_denied";
|
|
4936
|
-
if (failure2 === "timed_out") return "transient_retryable";
|
|
4937
|
-
return "relay_unavailable";
|
|
4938
|
-
}
|
|
4939
5524
|
async function currentInvocation(descriptor2, config, now) {
|
|
4940
5525
|
const store = descriptor2.create_store(config.outbox_dir);
|
|
4941
5526
|
const identity = resolveIdentity(descriptor2, config);
|
|
4942
|
-
if (identity.status
|
|
5527
|
+
if (identity.status === "session_unavailable") throw new TypeError("no_binding");
|
|
4943
5528
|
const waitBudgetMs = config.context_activation_wait_ms ?? 0;
|
|
4944
5529
|
const monotonicNow = config.monotonic_now ?? (() => performance.now());
|
|
4945
5530
|
const deadline = monotonicNow() + waitBudgetMs;
|
|
@@ -5003,9 +5588,9 @@ async function resolveOptions(descriptor2, args, options) {
|
|
|
5003
5588
|
};
|
|
5004
5589
|
}
|
|
5005
5590
|
function authenticationBudgetMsFromEnvironment() {
|
|
5006
|
-
const value = process.env[
|
|
5591
|
+
const value = process.env[CEAL_HOST_AUTHENTICATION_BUDGET_ENVIRONMENT];
|
|
5007
5592
|
if (value === void 0) return 0;
|
|
5008
|
-
if (
|
|
5593
|
+
if (!CEAL_HOST_AUTHENTICATION_BUDGET_PATTERN.test(value)) throw new TypeError("invalid_arguments");
|
|
5009
5594
|
return Number(value);
|
|
5010
5595
|
}
|
|
5011
5596
|
function boundedContextActivationWait(contextBudgetMs, relayTimeoutMs, maxContextWaitMs) {
|
|
@@ -5042,16 +5627,16 @@ function writeResult(value, sink) {
|
|
|
5042
5627
|
}
|
|
5043
5628
|
|
|
5044
5629
|
// host/producer-service.ts
|
|
5045
|
-
import { posix as
|
|
5630
|
+
import { posix as posix4, win32 as win326 } from "node:path";
|
|
5046
5631
|
|
|
5047
5632
|
// host/runtime-source-paths.ts
|
|
5048
|
-
import { createHash as
|
|
5049
|
-
import { posix as
|
|
5633
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
5634
|
+
import { posix as posix3, win32 as win325 } from "node:path";
|
|
5050
5635
|
|
|
5051
5636
|
// host/absolute-path.ts
|
|
5052
|
-
import { posix, win32 } from "node:path";
|
|
5637
|
+
import { posix as posix2, win32 as win324 } from "node:path";
|
|
5053
5638
|
function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allowRoot = false) {
|
|
5054
|
-
const pathApi = flavor === "win32" || flavor === "windows" ?
|
|
5639
|
+
const pathApi = flavor === "win32" || flavor === "windows" ? win324 : posix2;
|
|
5055
5640
|
return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
|
|
5056
5641
|
}
|
|
5057
5642
|
|
|
@@ -5063,28 +5648,23 @@ function hostSourceUuid(sourceRef) {
|
|
|
5063
5648
|
function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
|
|
5064
5649
|
const sourceId = hostSourceUuid(sourceRef);
|
|
5065
5650
|
if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
|
|
5066
|
-
const path = platform === "win32" ?
|
|
5651
|
+
const path = platform === "win32" ? win325 : posix3;
|
|
5067
5652
|
const sourcesRoot = path.join(stateRoot, "sources");
|
|
5068
5653
|
const sourceRoot = path.join(sourcesRoot, sourceId);
|
|
5069
5654
|
const runtimeRoot = path.join(stateRoot, "run");
|
|
5070
|
-
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${
|
|
5071
|
-
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`);
|
|
5072
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 };
|
|
5073
5657
|
}
|
|
5074
|
-
function assertHostRuntimeIpcLength(endpoint, platform) {
|
|
5075
|
-
if (platform === "win32") return;
|
|
5076
|
-
const maximum = platform === "linux" ? 107 : 103;
|
|
5077
|
-
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" });
|
|
5078
|
-
}
|
|
5079
5658
|
function assertHostProducerSourcePaths(stateRoot, config, producer, platform = process.platform) {
|
|
5080
5659
|
const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
|
|
5081
|
-
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;
|
|
5082
5662
|
}
|
|
5083
5663
|
|
|
5084
5664
|
// host/producer-service.ts
|
|
5085
|
-
import { constants } from "node:fs";
|
|
5086
|
-
import { lstat as
|
|
5087
|
-
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;
|
|
5088
5668
|
var HOST_PRODUCER_CONFIG_FLAG = "--ceal-host-config";
|
|
5089
5669
|
var HostProducerServiceError = class extends Error {
|
|
5090
5670
|
code;
|
|
@@ -5095,18 +5675,19 @@ var HostProducerServiceError = class extends Error {
|
|
|
5095
5675
|
}
|
|
5096
5676
|
};
|
|
5097
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";
|
|
5098
5679
|
var HOST_PRODUCER_TIMING_DEFAULTS = Object.freeze({ reconcile_interval_ms: 250, retry_backoff_ms: 1e3, ipc_timeout_ms: 5e3 });
|
|
5099
5680
|
function hostProducerExtraPath(extras, key) {
|
|
5100
5681
|
const value = extras[key];
|
|
5101
|
-
if (value === void 0) throw hostProducerError("invalid_config");
|
|
5682
|
+
if (value === void 0) throw hostProducerError("invalid_config", refusalFact(key, "missing", "an absolute path"));
|
|
5102
5683
|
return value;
|
|
5103
5684
|
}
|
|
5104
5685
|
function parseHostProducerConfigFlag(args, platform = process.platform) {
|
|
5105
5686
|
return parseProducerHostConfigFlag(args, HOST_PRODUCER_CONFIG_FLAG, platform, hostProducerError);
|
|
5106
5687
|
}
|
|
5107
|
-
async function readHostProducerConfig(descriptor2, configPath, platform = process.platform) {
|
|
5108
|
-
const config = decodeHostProducerConfigText(descriptor2, await readProducerConfigText(configPath, platform, hostProducerError), platform);
|
|
5109
|
-
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;
|
|
5110
5691
|
const root = path.resolve(config.outbox_dir, "../../../../..");
|
|
5111
5692
|
try {
|
|
5112
5693
|
assertHostProducerSourcePaths(root, config, descriptor2.producer_id, platform);
|
|
@@ -5132,15 +5713,31 @@ function parseProducerHostConfigFlag(args, flag, platform, error) {
|
|
|
5132
5713
|
assertProducerAbsolutePath(value, platform, "config", error);
|
|
5133
5714
|
return value;
|
|
5134
5715
|
}
|
|
5135
|
-
async function readProducerConfigText(configPath, platform, error) {
|
|
5136
|
-
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
|
+
}
|
|
5137
5735
|
const pathStat = await configPathStat(configPath, error);
|
|
5138
5736
|
validateConfigFile(pathStat, platform, error);
|
|
5139
|
-
const
|
|
5140
|
-
const handle = await openConfigFile(configPath, constants.O_RDONLY | noFollow, error);
|
|
5737
|
+
const handle = await openConfigFile(configPath, platform, error, custody);
|
|
5141
5738
|
try {
|
|
5142
5739
|
const opened = await handle.stat();
|
|
5143
|
-
validateConfigFile(opened, platform, error);
|
|
5740
|
+
validateConfigFile(opened, platform, error, handle);
|
|
5144
5741
|
validateOpenedFile(opened, pathStat, platform, error);
|
|
5145
5742
|
return await readBoundedConfig(handle, opened.size, error);
|
|
5146
5743
|
} finally {
|
|
@@ -5148,8 +5745,9 @@ async function readProducerConfigText(configPath, platform, error) {
|
|
|
5148
5745
|
}
|
|
5149
5746
|
}
|
|
5150
5747
|
function decodeProducerConfigCore(text, options) {
|
|
5748
|
+
const ownership = producerConfigKeyOwnership(options.keys, options.platform);
|
|
5151
5749
|
const value = parseCanonicalJson(text, options.error);
|
|
5152
|
-
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 })));
|
|
5153
5751
|
const hostSourceRef = typeof value.host_source_ref === "string" ? value.host_source_ref : "";
|
|
5154
5752
|
const installationRef = typeof value.producer_installation_ref === "string" ? value.producer_installation_ref : "";
|
|
5155
5753
|
const producerEpoch = validateIdentity(hostSourceRef, installationRef, value.producer_epoch, options.error);
|
|
@@ -5167,14 +5765,21 @@ function decodeProducerConfigCore(text, options) {
|
|
|
5167
5765
|
producer_installation_ref: installationRef,
|
|
5168
5766
|
producer_epoch: producerEpoch,
|
|
5169
5767
|
outbox_dir: value.outbox_dir,
|
|
5170
|
-
relay_endpoint: value
|
|
5768
|
+
relay_endpoint: producerConfigEndpoint(value),
|
|
5171
5769
|
reconcile_interval_ms: intervals[0],
|
|
5172
5770
|
retry_backoff_ms: intervals[1],
|
|
5173
5771
|
ipc_timeout_ms: intervals[2]
|
|
5174
5772
|
};
|
|
5175
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
|
+
}
|
|
5176
5781
|
function assertProducerRelayEndpoint(value, platform, error) {
|
|
5177
|
-
if (platform === "win32"
|
|
5782
|
+
if (platform === "win32") return;
|
|
5178
5783
|
assertProducerAbsolutePath(value, platform, "runtime", error);
|
|
5179
5784
|
}
|
|
5180
5785
|
function assertProducerAbsolutePath(value, platform, kind, error) {
|
|
@@ -5182,26 +5787,27 @@ function assertProducerAbsolutePath(value, platform, kind, error) {
|
|
|
5182
5787
|
}
|
|
5183
5788
|
async function configPathStat(configPath, error) {
|
|
5184
5789
|
try {
|
|
5185
|
-
return await
|
|
5790
|
+
return await lstat3(configPath);
|
|
5186
5791
|
} catch {
|
|
5187
5792
|
throw error("config_unavailable");
|
|
5188
5793
|
}
|
|
5189
5794
|
}
|
|
5190
|
-
function validateConfigFile(value, platform, error) {
|
|
5795
|
+
function validateConfigFile(value, platform, error, handle) {
|
|
5191
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" }]));
|
|
5192
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}`));
|
|
5193
|
-
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" }]));
|
|
5194
5799
|
}
|
|
5195
|
-
async function openConfigFile(configPath,
|
|
5800
|
+
async function openConfigFile(configPath, platform, error, custody) {
|
|
5196
5801
|
try {
|
|
5197
|
-
return await
|
|
5198
|
-
} 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");
|
|
5199
5805
|
throw error("config_unavailable");
|
|
5200
5806
|
}
|
|
5201
5807
|
}
|
|
5202
5808
|
function validateOpenedFile(opened, pathStat, platform, error) {
|
|
5203
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" }]));
|
|
5204
|
-
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) }]));
|
|
5205
5811
|
}
|
|
5206
5812
|
async function readBoundedConfig(handle, expectedBytes, error) {
|
|
5207
5813
|
const buffer = Buffer.allocUnsafe(expectedBytes + 1);
|
|
@@ -5233,34 +5839,58 @@ function firstDifferenceIndex(left, right) {
|
|
|
5233
5839
|
for (let index = 0; index < shared; index += 1) if (left[index] !== right[index]) return index;
|
|
5234
5840
|
return shared;
|
|
5235
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
|
+
}
|
|
5236
5853
|
function validateIdentity(sourceRef, installationRef, epoch, error) {
|
|
5237
|
-
if (!
|
|
5854
|
+
if (!SAFE_REF3.test(sourceRef) || !SAFE_REF3.test(installationRef) || !positive(epoch, 2147483647)) throw error("invalid_config", refusalFacts(producerIdentityFacts(sourceRef, installationRef, epoch)));
|
|
5238
5855
|
return epoch;
|
|
5239
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
|
+
}
|
|
5240
5864
|
function validateIntervals(reconcile, retry, timeout, error) {
|
|
5241
|
-
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)));
|
|
5242
5866
|
return [reconcile, retry, timeout];
|
|
5243
5867
|
}
|
|
5244
|
-
function
|
|
5245
|
-
|
|
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;
|
|
5246
5874
|
}
|
|
5247
|
-
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) {
|
|
5248
5879
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5249
5880
|
}
|
|
5250
|
-
function
|
|
5881
|
+
function positive(value, maximum) {
|
|
5251
5882
|
return Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= maximum;
|
|
5252
5883
|
}
|
|
5253
5884
|
function boundedMilliseconds(value) {
|
|
5254
|
-
return
|
|
5885
|
+
return positive(value, 6e4) && Number(value) >= 25;
|
|
5255
5886
|
}
|
|
5256
5887
|
|
|
5257
5888
|
// host/codex/src/codex-decoder.ts
|
|
5258
|
-
import { createHash as
|
|
5889
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
5259
5890
|
|
|
5260
5891
|
// host/codex/src/codex-types.ts
|
|
5261
5892
|
var CODEX_THREAD_ID_ENVIRONMENT = "CODEX_THREAD_ID";
|
|
5262
5893
|
var CODEX_OUTBOX_SCHEMA_VERSION = "ceal.codex_observation_outbox.v1";
|
|
5263
|
-
var CODEX_TURN_BINDING_SCHEMA_VERSION = "ceal.codex_turn_binding.v1";
|
|
5264
5894
|
var CODEX_SESSION_SCHEMA_VERSION = "ceal.codex_session.v1";
|
|
5265
5895
|
var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1";
|
|
5266
5896
|
var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
|
|
@@ -5276,20 +5906,20 @@ var CodexHostAdapterError = class extends Error {
|
|
|
5276
5906
|
};
|
|
5277
5907
|
|
|
5278
5908
|
// host/codex/src/codex-decoder.ts
|
|
5279
|
-
var
|
|
5909
|
+
var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
5280
5910
|
var MAX_PAYLOAD_BYTES = 512 * 1024;
|
|
5281
|
-
function observationRef(event, identity, value) {
|
|
5282
|
-
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 }));
|
|
5283
5913
|
}
|
|
5284
5914
|
function hostSessionReference(sourceRef, sessionId) {
|
|
5285
5915
|
return `codex-session:${shortDigest({ source_ref: sourceRef, session_id: sessionId })}`;
|
|
5286
5916
|
}
|
|
5287
5917
|
function hostTurnReference(sourceRef, sessionRef, nativeTurnId) {
|
|
5288
|
-
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.");
|
|
5289
5919
|
return `codex-turn:${shortDigest({ source_ref: sourceRef, session_ref: sessionRef, native_turn_id: nativeTurnId })}`;
|
|
5290
5920
|
}
|
|
5291
5921
|
function hostInputReference(sourceRef, sessionRef, nativeTurnId, messageId, ordinal, prompt) {
|
|
5292
|
-
if (![sourceRef, sessionRef, nativeTurnId, messageId].every((value) =>
|
|
5922
|
+
if (![sourceRef, sessionRef, nativeTurnId, messageId].every((value) => SAFE_REF4.test(value)) || !Number.isSafeInteger(ordinal) || ordinal < 0) {
|
|
5293
5923
|
throw new CodexHostAdapterError("invalid_input", "Codex input identity is invalid.");
|
|
5294
5924
|
}
|
|
5295
5925
|
return `codex-input:${shortDigest({ source_ref: sourceRef, session_ref: sessionRef, native_turn_id: nativeTurnId, message_id: messageId, ordinal, prompt_sha256: sha256(prompt) })}`;
|
|
@@ -5301,25 +5931,24 @@ function turnObservationValue(sessionId, turnRef, status) {
|
|
|
5301
5931
|
return { status, host_session_id_sha256: sha256(sessionId), host_turn_ref: turnRef };
|
|
5302
5932
|
}
|
|
5303
5933
|
function sha256(value) {
|
|
5304
|
-
return
|
|
5934
|
+
return createHash6("sha256").update(value, "utf8").digest("hex");
|
|
5305
5935
|
}
|
|
5306
5936
|
function shortDigest(value) {
|
|
5307
5937
|
return cealRequestSha256(value);
|
|
5308
5938
|
}
|
|
5309
5939
|
|
|
5310
5940
|
// host/codex/src/codex-outbox.ts
|
|
5311
|
-
import {
|
|
5312
|
-
import {
|
|
5313
|
-
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";
|
|
5314
5943
|
|
|
5315
5944
|
// host/invocation-context.ts
|
|
5316
|
-
var
|
|
5945
|
+
var HOST_INVOCATION_TURN_SELECTION_SCHEMA = "ceal.host_invocation_turn_selection.v1";
|
|
5317
5946
|
var HOST_INVOCATION_CONTEXT_REQUIRED_KEYS = ["schema_version", "identity", "prompt_ref", "expires_at"];
|
|
5318
5947
|
function invocationContextKey(identity) {
|
|
5319
5948
|
return cealRequestSha256({ host_source_ref: identity.host_source_ref, host_session_ref: identity.host_session_ref });
|
|
5320
5949
|
}
|
|
5321
5950
|
function hasHostInvocationIdentityKeys(value) {
|
|
5322
|
-
return
|
|
5951
|
+
return exactKeys3(value, CEAL_HOST_INVOCATION_IDENTITY_KEYS);
|
|
5323
5952
|
}
|
|
5324
5953
|
function hasHostInvocationContextKeys(value, optional = []) {
|
|
5325
5954
|
return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
|
|
@@ -5345,12 +5974,9 @@ function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor2)
|
|
|
5345
5974
|
return Object.freeze(context);
|
|
5346
5975
|
}
|
|
5347
5976
|
function isHostInvocationTurnSelection(value) {
|
|
5348
|
-
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]));
|
|
5349
5978
|
}
|
|
5350
|
-
function
|
|
5351
|
-
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;
|
|
5352
|
-
}
|
|
5353
|
-
function exactKeys4(value, keys) {
|
|
5979
|
+
function exactKeys3(value, keys) {
|
|
5354
5980
|
const actual = Object.keys(value);
|
|
5355
5981
|
return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
|
|
5356
5982
|
}
|
|
@@ -5359,10 +5985,10 @@ function requiredKeys(value, required, optional) {
|
|
|
5359
5985
|
const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
|
|
5360
5986
|
return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
|
|
5361
5987
|
}
|
|
5362
|
-
function
|
|
5988
|
+
function isRecord5(value) {
|
|
5363
5989
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5364
5990
|
}
|
|
5365
|
-
function
|
|
5991
|
+
function safeRef2(value) {
|
|
5366
5992
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
5367
5993
|
}
|
|
5368
5994
|
|
|
@@ -5378,59 +6004,40 @@ function validateCodexHostInvocationContext(value) {
|
|
|
5378
6004
|
validateContextReferences(value);
|
|
5379
6005
|
validateContextTimes(value);
|
|
5380
6006
|
}
|
|
5381
|
-
function invocationContextFromBinding(binding, turnGenerationRef) {
|
|
5382
|
-
const context = {
|
|
5383
|
-
schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
|
|
5384
|
-
identity: binding.identity,
|
|
5385
|
-
prompt_ref: binding.prompt_ref,
|
|
5386
|
-
request_ref: binding.request_ref,
|
|
5387
|
-
bound_at: binding.bound_at,
|
|
5388
|
-
expires_at: binding.expires_at,
|
|
5389
|
-
...turnGenerationRef === void 0 ? {} : { turn_generation_ref: turnGenerationRef }
|
|
5390
|
-
};
|
|
5391
|
-
validateCodexHostInvocationContext(context);
|
|
5392
|
-
return Object.freeze(context);
|
|
5393
|
-
}
|
|
5394
6007
|
function validateIdentity2(value) {
|
|
5395
|
-
if (!
|
|
6008
|
+
if (!isRecord6(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef3)) {
|
|
5396
6009
|
throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
|
|
5397
6010
|
}
|
|
5398
6011
|
}
|
|
5399
6012
|
function isContextShape(value) {
|
|
5400
|
-
return
|
|
6013
|
+
return isRecord6(value) && hasHostInvocationContextKeys(value, ["turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
|
|
5401
6014
|
}
|
|
5402
6015
|
function validateContextReferences(value) {
|
|
5403
|
-
if (!
|
|
6016
|
+
if (!safeRef3(value.prompt_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
5404
6017
|
}
|
|
5405
6018
|
function validateContextTimes(value) {
|
|
5406
6019
|
const expiresAt = value.expires_at;
|
|
5407
|
-
|
|
5408
|
-
if (!timestamp2(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
5409
|
-
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.");
|
|
5410
6021
|
}
|
|
5411
6022
|
function optionalSafeRef(value) {
|
|
5412
|
-
return value === void 0 ||
|
|
5413
|
-
}
|
|
5414
|
-
function optionalTimestamp(value) {
|
|
5415
|
-
return value === void 0 || timestamp2(value);
|
|
6023
|
+
return value === void 0 || safeRef3(value);
|
|
5416
6024
|
}
|
|
5417
|
-
function
|
|
6025
|
+
function safeRef3(value) {
|
|
5418
6026
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
5419
6027
|
}
|
|
5420
|
-
function
|
|
6028
|
+
function timestamp(value) {
|
|
5421
6029
|
return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
5422
6030
|
}
|
|
5423
|
-
function
|
|
6031
|
+
function isRecord6(value) {
|
|
5424
6032
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5425
6033
|
}
|
|
5426
6034
|
|
|
5427
6035
|
// host/codex/src/codex-outbox.ts
|
|
5428
|
-
var
|
|
6036
|
+
var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
5429
6037
|
var CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS = 4096;
|
|
5430
6038
|
var CodexOutboxStore = class {
|
|
5431
6039
|
directory;
|
|
5432
6040
|
entriesDirectory;
|
|
5433
|
-
bindingsDirectory;
|
|
5434
6041
|
sessionsDirectory;
|
|
5435
6042
|
activeContextsDirectory;
|
|
5436
6043
|
turnSelectionsDirectory;
|
|
@@ -5440,7 +6047,6 @@ var CodexOutboxStore = class {
|
|
|
5440
6047
|
if (typeof directory !== "string" || directory.length === 0) throw new CodexHostAdapterError("configuration", "Codex outbox directory is required.");
|
|
5441
6048
|
this.directory = directory;
|
|
5442
6049
|
this.entriesDirectory = join4(directory, "entries");
|
|
5443
|
-
this.bindingsDirectory = join4(directory, "bindings");
|
|
5444
6050
|
this.sessionsDirectory = join4(directory, "sessions");
|
|
5445
6051
|
this.activeContextsDirectory = join4(directory, "active-contexts");
|
|
5446
6052
|
this.turnSelectionsDirectory = join4(directory, "turn-selections");
|
|
@@ -5448,8 +6054,8 @@ var CodexOutboxStore = class {
|
|
|
5448
6054
|
this.cursorsDirectory = join4(directory, "transcript-cursors");
|
|
5449
6055
|
}
|
|
5450
6056
|
async prepare() {
|
|
5451
|
-
for (const path of [this.directory, this.entriesDirectory, this.
|
|
5452
|
-
await
|
|
6057
|
+
for (const path of [this.directory, this.entriesDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
|
|
6058
|
+
await ensureOwnerDirectory(path);
|
|
5453
6059
|
}
|
|
5454
6060
|
}
|
|
5455
6061
|
async put(entry) {
|
|
@@ -5464,7 +6070,7 @@ var CodexOutboxStore = class {
|
|
|
5464
6070
|
for (const name of await readdir(this.entriesDirectory)) {
|
|
5465
6071
|
if (!name.endsWith(".json")) continue;
|
|
5466
6072
|
try {
|
|
5467
|
-
const value = JSON.parse(await
|
|
6073
|
+
const value = JSON.parse(await readOwnerStateFile(join4(this.entriesDirectory, name)));
|
|
5468
6074
|
validateEnvelope(value);
|
|
5469
6075
|
entries.push(value);
|
|
5470
6076
|
} catch {
|
|
@@ -5477,16 +6083,11 @@ var CodexOutboxStore = class {
|
|
|
5477
6083
|
async remove(entry) {
|
|
5478
6084
|
validateEnvelope(entry);
|
|
5479
6085
|
try {
|
|
5480
|
-
await
|
|
6086
|
+
await unlink3(this.entryPath(entry.submission_ref));
|
|
5481
6087
|
} catch (error) {
|
|
5482
6088
|
if (!isNotFound(error)) throw storageError("Unable to remove the Codex outbox entry.");
|
|
5483
6089
|
}
|
|
5484
6090
|
}
|
|
5485
|
-
async writeBinding(binding) {
|
|
5486
|
-
validateBinding(binding);
|
|
5487
|
-
await this.prepare();
|
|
5488
|
-
await writeAtomicJson(this.bindingPath(binding.identity), binding);
|
|
5489
|
-
}
|
|
5490
6091
|
/**
|
|
5491
6092
|
* Select the exact Host turn that model-reachable commands may use. A new
|
|
5492
6093
|
* prompt replaces this projection, so it cannot inherit the prior turn's
|
|
@@ -5500,7 +6101,7 @@ var CodexOutboxStore = class {
|
|
|
5500
6101
|
await writeAtomicJson(path, context);
|
|
5501
6102
|
} catch (error) {
|
|
5502
6103
|
try {
|
|
5503
|
-
await
|
|
6104
|
+
await unlink3(path);
|
|
5504
6105
|
} catch (unlinkError) {
|
|
5505
6106
|
if (!isNotFound(unlinkError)) throw storageError("Unable to clear a failed Codex Host context replacement.");
|
|
5506
6107
|
}
|
|
@@ -5513,7 +6114,7 @@ var CodexOutboxStore = class {
|
|
|
5513
6114
|
await writeAtomicJson(this.turnSelectionPath(selection.host_source_ref, selection.host_session_ref), selection);
|
|
5514
6115
|
}
|
|
5515
6116
|
async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
|
|
5516
|
-
if (!
|
|
6117
|
+
if (!SAFE_REF5.test(hostSourceRef) || !SAFE_REF5.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
|
|
5517
6118
|
await this.prepare();
|
|
5518
6119
|
return readOptionalJson(this.turnSelectionPath(hostSourceRef, hostSessionRef), validateTurnSelection);
|
|
5519
6120
|
}
|
|
@@ -5526,23 +6127,11 @@ var CodexOutboxStore = class {
|
|
|
5526
6127
|
await writeAtomicJson(path, { ...active, turn_generation_ref: turnGenerationRef });
|
|
5527
6128
|
return true;
|
|
5528
6129
|
}
|
|
5529
|
-
/**
|
|
5530
|
-
* Publish a Request reference only while the acknowledged binding is still
|
|
5531
|
-
* the active turn. A delayed relay response therefore cannot overwrite a
|
|
5532
|
-
* newer prompt's context.
|
|
5533
|
-
*/
|
|
5534
|
-
async bindInvocationContext(binding, now = /* @__PURE__ */ new Date()) {
|
|
5535
|
-
validateBinding(binding);
|
|
5536
|
-
const active = await this.readInvocationContext(binding.identity.host_source_ref, binding.identity.host_session_ref, now);
|
|
5537
|
-
if (active === null || !sameIdentity(active.identity, binding.identity)) return false;
|
|
5538
|
-
await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding, active.turn_generation_ref));
|
|
5539
|
-
return true;
|
|
5540
|
-
}
|
|
5541
6130
|
/** Return only the live session projection; never fall back to another turn. */
|
|
5542
6131
|
async readInvocationContext(hostSourceRef, hostSessionRef, now = /* @__PURE__ */ new Date()) {
|
|
5543
6132
|
if (!(now instanceof Date) || !Number.isFinite(now.getTime())) throw new CodexHostAdapterError("invalid_input", "Codex Host context clock is invalid.");
|
|
5544
6133
|
const context = await this.readRawInvocationContext(hostSourceRef, hostSessionRef);
|
|
5545
|
-
return isLiveSessionProjection(context, now)
|
|
6134
|
+
return isLiveSessionProjection(context, now) ? context : null;
|
|
5546
6135
|
}
|
|
5547
6136
|
/**
|
|
5548
6137
|
* The latest Codex session projection even when past expiry, so the
|
|
@@ -5550,10 +6139,10 @@ var CodexOutboxStore = class {
|
|
|
5550
6139
|
* a missing one. Every other caller keeps the fail-closed read above.
|
|
5551
6140
|
*/
|
|
5552
6141
|
async readRawInvocationContext(hostSourceRef, hostSessionRef) {
|
|
5553
|
-
if (!
|
|
6142
|
+
if (!SAFE_REF5.test(hostSourceRef) || !SAFE_REF5.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
|
|
5554
6143
|
await this.prepare();
|
|
5555
6144
|
const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, (file) => readOptionalJson(file, validateCodexHostInvocationContext));
|
|
5556
|
-
return
|
|
6145
|
+
return context;
|
|
5557
6146
|
}
|
|
5558
6147
|
/** Clear the projection only if the caller still owns its exact active turn. */
|
|
5559
6148
|
async clearInvocationContext(identity, now = /* @__PURE__ */ new Date()) {
|
|
@@ -5561,17 +6150,12 @@ var CodexOutboxStore = class {
|
|
|
5561
6150
|
const active = await this.readInvocationContext(identity.host_source_ref, identity.host_session_ref, now);
|
|
5562
6151
|
if (active === null || !sameIdentity(active.identity, identity)) return false;
|
|
5563
6152
|
try {
|
|
5564
|
-
await
|
|
6153
|
+
await unlink3(this.activeContextPath(identity));
|
|
5565
6154
|
} catch (error) {
|
|
5566
6155
|
if (!isNotFound(error)) throw storageError("Unable to clear the Codex Host invocation context.");
|
|
5567
6156
|
}
|
|
5568
6157
|
return true;
|
|
5569
6158
|
}
|
|
5570
|
-
async readBinding(identity) {
|
|
5571
|
-
validateIdentity3(identity);
|
|
5572
|
-
await this.prepare();
|
|
5573
|
-
return readOptionalJson(this.bindingPath(identity), validateBinding);
|
|
5574
|
-
}
|
|
5575
6159
|
async saveSession(snapshot) {
|
|
5576
6160
|
validateSession(snapshot);
|
|
5577
6161
|
await this.prepare();
|
|
@@ -5579,7 +6163,7 @@ var CodexOutboxStore = class {
|
|
|
5579
6163
|
}
|
|
5580
6164
|
async readSession(sourceRef, sessionRef) {
|
|
5581
6165
|
const identity = { host_source_ref: sourceRef, host_session_ref: sessionRef };
|
|
5582
|
-
if (!
|
|
6166
|
+
if (!SAFE_REF5.test(sourceRef) || !SAFE_REF5.test(sessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex session identity is invalid.");
|
|
5583
6167
|
await this.prepare();
|
|
5584
6168
|
return readOptionalJson(this.sessionPath(identity.host_source_ref, identity.host_session_ref), validateSession);
|
|
5585
6169
|
}
|
|
@@ -5612,7 +6196,7 @@ var CodexOutboxStore = class {
|
|
|
5612
6196
|
for (const name of await readdir(this.registrationsDirectory)) {
|
|
5613
6197
|
if (!name.endsWith(".json")) continue;
|
|
5614
6198
|
try {
|
|
5615
|
-
const value = JSON.parse(await
|
|
6199
|
+
const value = JSON.parse(await readOwnerStateFile(join4(this.registrationsDirectory, name)));
|
|
5616
6200
|
validateRegistration(value);
|
|
5617
6201
|
registrations.push(value);
|
|
5618
6202
|
} catch {
|
|
@@ -5631,34 +6215,23 @@ var CodexOutboxStore = class {
|
|
|
5631
6215
|
await writeAtomicJson(this.cursorPath(cursor.host_source_ref, cursor.host_session_ref), cursor);
|
|
5632
6216
|
}
|
|
5633
6217
|
async findByAlias(sourceRef, sessionRef, alias) {
|
|
5634
|
-
if (!
|
|
6218
|
+
if (!SAFE_REF5.test(sourceRef) || !SAFE_REF5.test(sessionRef) || !SAFE_REF5.test(alias)) return null;
|
|
5635
6219
|
const read = await this.list();
|
|
5636
6220
|
const envelope = findEnvelopeByAlias(read.entries, sourceRef, sessionRef, alias);
|
|
5637
6221
|
if (envelope !== null) return envelopeMatch(envelope);
|
|
5638
6222
|
await this.prepare();
|
|
5639
|
-
const binding = await findBindingByAlias(this.bindingsDirectory, sourceRef, sessionRef, alias);
|
|
5640
|
-
if (binding !== null) return bindingMatch(binding);
|
|
5641
6223
|
return null;
|
|
5642
6224
|
}
|
|
5643
6225
|
async hasPriorTurn(sourceRef, sessionRef) {
|
|
5644
|
-
if (!
|
|
6226
|
+
if (!SAFE_REF5.test(sourceRef) || !SAFE_REF5.test(sessionRef)) return false;
|
|
5645
6227
|
const read = await this.list();
|
|
5646
6228
|
if (read.entries.some((entry) => entry.identity.host_source_ref === sourceRef && entry.identity.host_session_ref === sessionRef)) return true;
|
|
5647
|
-
await this.prepare();
|
|
5648
|
-
for (const name of await readdir(this.bindingsDirectory)) {
|
|
5649
|
-
if (!name.endsWith(".json")) continue;
|
|
5650
|
-
const binding = await readBindingCandidate(this.bindingsDirectory, name);
|
|
5651
|
-
if (binding?.identity.host_source_ref === sourceRef && binding.identity.host_session_ref === sessionRef) return true;
|
|
5652
|
-
}
|
|
5653
6229
|
return false;
|
|
5654
6230
|
}
|
|
5655
6231
|
entryPath(submissionRef) {
|
|
5656
|
-
if (!
|
|
6232
|
+
if (!SAFE_REF5.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
|
|
5657
6233
|
return join4(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
|
|
5658
6234
|
}
|
|
5659
|
-
bindingPath(identity) {
|
|
5660
|
-
return join4(this.bindingsDirectory, `${identityKey(identity)}.json`);
|
|
5661
|
-
}
|
|
5662
6235
|
sessionPath(sourceRef, sessionRef) {
|
|
5663
6236
|
return join4(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
|
|
5664
6237
|
}
|
|
@@ -5677,20 +6250,17 @@ var CodexOutboxStore = class {
|
|
|
5677
6250
|
};
|
|
5678
6251
|
async function removeOwnerState(path, what) {
|
|
5679
6252
|
try {
|
|
5680
|
-
await
|
|
6253
|
+
await unlink3(path);
|
|
5681
6254
|
} catch (error) {
|
|
5682
6255
|
if (!isNotFound(error)) throw storageError(`Unable to retire the Codex transcript ${what}.`);
|
|
5683
6256
|
}
|
|
5684
6257
|
}
|
|
5685
6258
|
function sessionStateKey(sourceRef, sessionRef) {
|
|
5686
|
-
if (!
|
|
6259
|
+
if (!SAFE_REF5.test(sourceRef) || !SAFE_REF5.test(sessionRef)) throw storageError("Codex transcript session identity is malformed.");
|
|
5687
6260
|
return cealRequestSha256({ host_source_ref: sourceRef, host_session_ref: sessionRef });
|
|
5688
6261
|
}
|
|
5689
6262
|
function envelopeMatch(envelope) {
|
|
5690
|
-
return { identity: envelope.identity, prompt_ref: envelope.prompt_ref,
|
|
5691
|
-
}
|
|
5692
|
-
function bindingMatch(binding) {
|
|
5693
|
-
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 };
|
|
5694
6264
|
}
|
|
5695
6265
|
function findEnvelopeByAlias(entries, sourceRef, sessionRef, alias) {
|
|
5696
6266
|
for (const envelope of entries) {
|
|
@@ -5699,24 +6269,6 @@ function findEnvelopeByAlias(entries, sourceRef, sessionRef, alias) {
|
|
|
5699
6269
|
}
|
|
5700
6270
|
return null;
|
|
5701
6271
|
}
|
|
5702
|
-
async function findBindingByAlias(directory, sourceRef, sessionRef, alias) {
|
|
5703
|
-
for (const name of await readdir(directory)) {
|
|
5704
|
-
if (!name.endsWith(".json")) continue;
|
|
5705
|
-
const binding = await readBindingCandidate(directory, name);
|
|
5706
|
-
if (binding === null || binding.identity.host_source_ref !== sourceRef || binding.identity.host_session_ref !== sessionRef) continue;
|
|
5707
|
-
if (binding.codex_turn_ids.includes(alias)) return binding;
|
|
5708
|
-
}
|
|
5709
|
-
return null;
|
|
5710
|
-
}
|
|
5711
|
-
async function readBindingCandidate(directory, name) {
|
|
5712
|
-
try {
|
|
5713
|
-
const value = JSON.parse(await readPrivateFile(join4(directory, name)));
|
|
5714
|
-
validateBinding(value);
|
|
5715
|
-
return value;
|
|
5716
|
-
} catch {
|
|
5717
|
-
return null;
|
|
5718
|
-
}
|
|
5719
|
-
}
|
|
5720
6272
|
function identityKey(identity) {
|
|
5721
6273
|
validateIdentity3(identity);
|
|
5722
6274
|
return cealRequestSha256(identity);
|
|
@@ -5729,18 +6281,18 @@ async function writeAtomicJson(path, value) {
|
|
|
5729
6281
|
if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
|
|
5730
6282
|
const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
5731
6283
|
try {
|
|
5732
|
-
const handle = await
|
|
6284
|
+
const handle = await open4(temp, "wx");
|
|
5733
6285
|
try {
|
|
5734
6286
|
await handle.writeFile(text, "utf8");
|
|
5735
6287
|
await handle.sync();
|
|
5736
6288
|
} finally {
|
|
5737
6289
|
await handle.close();
|
|
5738
6290
|
}
|
|
5739
|
-
await
|
|
5740
|
-
await syncDirectory(
|
|
6291
|
+
await rename2(temp, path);
|
|
6292
|
+
await syncDirectory(dirname4(path));
|
|
5741
6293
|
} catch (error) {
|
|
5742
6294
|
try {
|
|
5743
|
-
await
|
|
6295
|
+
await unlink3(temp);
|
|
5744
6296
|
} catch {
|
|
5745
6297
|
}
|
|
5746
6298
|
if (error instanceof CodexHostAdapterError) throw error;
|
|
@@ -5749,7 +6301,7 @@ async function writeAtomicJson(path, value) {
|
|
|
5749
6301
|
}
|
|
5750
6302
|
async function syncDirectory(path) {
|
|
5751
6303
|
try {
|
|
5752
|
-
const handle = await
|
|
6304
|
+
const handle = await open4(path, "r");
|
|
5753
6305
|
try {
|
|
5754
6306
|
await handle.sync();
|
|
5755
6307
|
} finally {
|
|
@@ -5760,7 +6312,7 @@ async function syncDirectory(path) {
|
|
|
5760
6312
|
}
|
|
5761
6313
|
async function readOptionalJson(path, validate) {
|
|
5762
6314
|
try {
|
|
5763
|
-
const value = JSON.parse(await
|
|
6315
|
+
const value = JSON.parse(await readOwnerStateFile(path));
|
|
5764
6316
|
validate(value);
|
|
5765
6317
|
return value;
|
|
5766
6318
|
} catch (error) {
|
|
@@ -5769,36 +6321,30 @@ async function readOptionalJson(path, validate) {
|
|
|
5769
6321
|
throw storageError("Codex owner state is malformed.");
|
|
5770
6322
|
}
|
|
5771
6323
|
}
|
|
5772
|
-
async function
|
|
6324
|
+
async function ensureOwnerDirectory(path) {
|
|
5773
6325
|
try {
|
|
5774
|
-
await
|
|
6326
|
+
await stat3(path);
|
|
5775
6327
|
} catch (error) {
|
|
5776
6328
|
if (!isNotFound(error)) throw storageError("Codex owner state directory is unavailable.");
|
|
5777
|
-
await mkdir3(path, { recursive: true
|
|
6329
|
+
await mkdir3(path, { recursive: true });
|
|
5778
6330
|
}
|
|
5779
|
-
const stats = await
|
|
5780
|
-
|
|
5781
|
-
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)")}).`);
|
|
5782
|
-
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")}).`);
|
|
5783
|
-
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.");
|
|
5784
6333
|
}
|
|
5785
|
-
async function
|
|
6334
|
+
async function readOwnerStateFile(path) {
|
|
5786
6335
|
let handle;
|
|
5787
6336
|
try {
|
|
5788
|
-
handle = await
|
|
6337
|
+
handle = await open4(path);
|
|
5789
6338
|
const stats = await handle.stat();
|
|
5790
|
-
|
|
5791
|
-
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)")}).`);
|
|
5792
|
-
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")}).`);
|
|
5793
|
-
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.");
|
|
5794
6340
|
return await handle.readFile("utf8");
|
|
5795
6341
|
} finally {
|
|
5796
6342
|
await handle?.close();
|
|
5797
6343
|
}
|
|
5798
6344
|
}
|
|
5799
6345
|
function validateEnvelope(value) {
|
|
5800
|
-
if (!
|
|
5801
|
-
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")}).`);
|
|
5802
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}`)}).`);
|
|
5803
6349
|
validateIdentity3(value.identity);
|
|
5804
6350
|
validateEnvelopeReferences(value);
|
|
@@ -5806,32 +6352,30 @@ function validateEnvelope(value) {
|
|
|
5806
6352
|
validateEnvelopeTimestamps(value);
|
|
5807
6353
|
}
|
|
5808
6354
|
function validateEnvelopeReferences(value) {
|
|
5809
|
-
if (!
|
|
5810
|
-
if (!
|
|
5811
|
-
if (!
|
|
5812
|
-
if (!
|
|
5813
|
-
if (value.request_ref !== void 0 && !SAFE_REF6.test(String(value.request_ref))) throw storageError("Codex outbox Request reference is malformed.");
|
|
5814
|
-
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.");
|
|
5815
6359
|
if (typeof value.origin_input !== "boolean") throw storageError("Codex outbox origin disposition is malformed.");
|
|
5816
6360
|
}
|
|
5817
6361
|
function validateRegistration(value) {
|
|
5818
|
-
if (!
|
|
6362
|
+
if (!isRecord7(value)) throw storageError("Codex transcript registration is malformed.");
|
|
5819
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")}).`);
|
|
5820
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}`)}).`);
|
|
5821
6365
|
validateRegistrationFields(value);
|
|
5822
6366
|
}
|
|
5823
6367
|
function validateRegistrationFields(value) {
|
|
5824
|
-
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 }])}).`);
|
|
5825
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`)}).`);
|
|
5826
6370
|
if (typeof value.transcript_path !== "string") throw storageError(`Codex transcript registration path is malformed (${refusalFact("kind", typeof value.transcript_path, "string")}).`);
|
|
5827
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}`)}).`);
|
|
5828
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 }])}).`);
|
|
5829
6373
|
}
|
|
5830
6374
|
function validRegistrationCwd(value) {
|
|
5831
|
-
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");
|
|
5832
6376
|
}
|
|
5833
6377
|
function validateCursor(value) {
|
|
5834
|
-
if (!
|
|
6378
|
+
if (!isRecord7(value)) throw storageError("Codex transcript cursor is malformed.");
|
|
5835
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")}).`);
|
|
5836
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}`)}).`);
|
|
5837
6381
|
if (!validCursorSource(value) || !validCursorPosition(value) || !validCursorStatus(value)) throw storageError("Codex transcript cursor fields are malformed.");
|
|
@@ -5839,7 +6383,7 @@ function validateCursor(value) {
|
|
|
5839
6383
|
if (value.reason !== void 0 && !isHostTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
|
|
5840
6384
|
}
|
|
5841
6385
|
function validCursorSource(value) {
|
|
5842
|
-
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";
|
|
5843
6387
|
}
|
|
5844
6388
|
function validCursorPosition(value) {
|
|
5845
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";
|
|
@@ -5848,23 +6392,23 @@ function validCursorStatus(value) {
|
|
|
5848
6392
|
return (value.status === "active" || value.status === "quarantined") && isTimestamp(value.updated_at);
|
|
5849
6393
|
}
|
|
5850
6394
|
function validActiveTask(value) {
|
|
5851
|
-
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;
|
|
5852
6396
|
return validActiveTaskIdentity(value) && validActiveTaskReferences(value) && validActiveTaskUsage(value);
|
|
5853
6397
|
}
|
|
5854
6398
|
function validActiveTaskIdentity(value) {
|
|
5855
|
-
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);
|
|
5856
6400
|
}
|
|
5857
6401
|
function validActiveTaskReferences(value) {
|
|
5858
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);
|
|
5859
6403
|
}
|
|
5860
6404
|
function optionalSafeRef2(value) {
|
|
5861
|
-
return value === void 0 || typeof value === "string" &&
|
|
6405
|
+
return value === void 0 || typeof value === "string" && SAFE_REF5.test(value);
|
|
5862
6406
|
}
|
|
5863
6407
|
function validActiveTaskUsage(value) {
|
|
5864
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");
|
|
5865
6409
|
}
|
|
5866
6410
|
function validTokenUsage(value) {
|
|
5867
|
-
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;
|
|
5868
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;
|
|
5869
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);
|
|
5870
6414
|
}
|
|
@@ -5874,7 +6418,7 @@ function nonNegativeInteger(value) {
|
|
|
5874
6418
|
function validateEnvelopeObservations(value) {
|
|
5875
6419
|
if (!Array.isArray(value.observations)) throw storageError("Codex outbox observations are malformed.");
|
|
5876
6420
|
try {
|
|
5877
|
-
for (const
|
|
6421
|
+
for (const observation of value.observations) validateCealHostObservationDraft(observation);
|
|
5878
6422
|
} catch {
|
|
5879
6423
|
throw storageError("Codex outbox observations are malformed.");
|
|
5880
6424
|
}
|
|
@@ -5883,34 +6427,23 @@ function validateEnvelopeTimestamps(value) {
|
|
|
5883
6427
|
if (!isTimestamp(value.created_at)) throw storageError("Codex outbox timestamps are malformed.");
|
|
5884
6428
|
if (!isTimestamp(value.expires_at)) throw storageError("Codex outbox timestamps are malformed.");
|
|
5885
6429
|
}
|
|
5886
|
-
function validateBinding(value) {
|
|
5887
|
-
if (!isRecord5(value)) throw storageError("Codex turn binding is malformed.");
|
|
5888
|
-
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")}).`);
|
|
5889
|
-
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}`)}).`);
|
|
5890
|
-
validateIdentity3(value.identity);
|
|
5891
|
-
if (!SAFE_REF6.test(String(value.prompt_ref)) || !SAFE_REF6.test(String(value.request_ref))) throw storageError("Codex turn binding references are malformed.");
|
|
5892
|
-
if (!isStringList(value.codex_turn_ids) || !isTimestamp(value.bound_at) || !isTimestamp(value.expires_at)) throw storageError("Codex turn binding fields are malformed.");
|
|
5893
|
-
}
|
|
5894
6430
|
function validateSession(value) {
|
|
5895
|
-
if (!
|
|
6431
|
+
if (!isRecord7(value)) throw storageError("Codex session snapshot is malformed.");
|
|
5896
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")}).`);
|
|
5897
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}`)}).`);
|
|
5898
|
-
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.");
|
|
5899
6435
|
}
|
|
5900
6436
|
function validateTurnSelection(value) {
|
|
5901
6437
|
if (!isHostInvocationTurnSelection(value)) throw storageError("Codex Host turn selection is malformed.");
|
|
5902
6438
|
}
|
|
5903
6439
|
function validateIdentity3(value) {
|
|
5904
|
-
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.");
|
|
5905
6441
|
}
|
|
5906
6442
|
function isJsonValue(value) {
|
|
5907
6443
|
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
5908
6444
|
if (typeof value === "number") return Number.isFinite(value);
|
|
5909
6445
|
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
5910
|
-
return
|
|
5911
|
-
}
|
|
5912
|
-
function isStringList(value) {
|
|
5913
|
-
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));
|
|
5914
6447
|
}
|
|
5915
6448
|
function isTimestamp(value) {
|
|
5916
6449
|
return typeof value === "string" && value.length <= 64 && !Number.isNaN(Date.parse(value));
|
|
@@ -5920,142 +6453,43 @@ function sameKeys(value, required, optional = []) {
|
|
|
5920
6453
|
const keys = Object.keys(value);
|
|
5921
6454
|
return keys.length >= required.length && keys.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
|
|
5922
6455
|
}
|
|
5923
|
-
function
|
|
6456
|
+
function isRecord7(value) {
|
|
5924
6457
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5925
6458
|
}
|
|
5926
6459
|
function isNotFound(error) {
|
|
5927
|
-
return
|
|
6460
|
+
return isRecord7(error) && error.code === "ENOENT";
|
|
5928
6461
|
}
|
|
5929
6462
|
function storageError(message) {
|
|
5930
6463
|
return new CodexHostAdapterError("storage", message);
|
|
5931
6464
|
}
|
|
5932
6465
|
|
|
5933
6466
|
// host/codex/src/codex-producer.ts
|
|
5934
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
5935
|
-
var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
5936
6467
|
var CodexProducer = class {
|
|
5937
|
-
producer_id = "codex";
|
|
5938
|
-
producer_label = "Codex";
|
|
5939
|
-
outbox_directory;
|
|
5940
6468
|
store;
|
|
5941
6469
|
core;
|
|
5942
6470
|
constructor(options) {
|
|
5943
6471
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
5944
|
-
this.outbox_directory = this.store.directory;
|
|
5945
6472
|
this.core = new HostProducerDrainCore(this, options);
|
|
5946
6473
|
}
|
|
5947
6474
|
drainOutbox() {
|
|
5948
6475
|
return this.core.drainOutbox();
|
|
5949
6476
|
}
|
|
5950
|
-
fail(code, message) {
|
|
5951
|
-
return new CodexHostAdapterError(code, message);
|
|
5952
|
-
}
|
|
5953
6477
|
listEnvelopes() {
|
|
5954
6478
|
return this.store.list();
|
|
5955
6479
|
}
|
|
5956
6480
|
removeEnvelope(envelope) {
|
|
5957
6481
|
return this.store.remove(envelope);
|
|
5958
6482
|
}
|
|
5959
|
-
submissionRefOf(envelope) {
|
|
5960
|
-
return envelope.submission_ref;
|
|
5961
|
-
}
|
|
5962
6483
|
expiresAtOf(envelope) {
|
|
5963
6484
|
return envelope.expires_at;
|
|
5964
6485
|
}
|
|
5965
|
-
createdAtOf(envelope) {
|
|
5966
|
-
return envelope.created_at;
|
|
5967
|
-
}
|
|
5968
|
-
async mapEnvelope(envelope) {
|
|
5969
|
-
const prompt = observation(envelope, "prompt");
|
|
5970
|
-
if (prompt !== void 0) return this.mapInput(envelope, prompt);
|
|
5971
|
-
return [mapTerminal(envelope)];
|
|
5972
|
-
}
|
|
5973
|
-
async mapInput(envelope, prompt) {
|
|
5974
|
-
const drafts = [];
|
|
5975
|
-
const session = observation(envelope, "session");
|
|
5976
|
-
if (session !== void 0) {
|
|
5977
|
-
const registrations = await this.store.listTranscriptRegistrations();
|
|
5978
|
-
const registration = registrations.find((item) => item.host_source_ref === envelope.identity.host_source_ref && item.host_session_ref === envelope.identity.host_session_ref);
|
|
5979
|
-
const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
|
|
5980
|
-
drafts.push({ frame_kind: "session_started", payload: compact({
|
|
5981
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
5982
|
-
working_directory_ref: `workspace:${createHash6("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
|
|
5983
|
-
model: recordText(session.value, "model"),
|
|
5984
|
-
host_version: nestedText(session.value, "host_version"),
|
|
5985
|
-
observations: [session]
|
|
5986
|
-
}) });
|
|
5987
|
-
}
|
|
5988
|
-
const turn = observation(envelope, "turn");
|
|
5989
|
-
if (turn !== void 0) drafts.push({ frame_kind: "turn_started", payload: compact({
|
|
5990
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
5991
|
-
local_turn_ref: localTurnRef(envelope),
|
|
5992
|
-
observations: [turn]
|
|
5993
|
-
}) });
|
|
5994
|
-
if (!isCealHostPromptObservationValue(prompt.value)) throw new CodexHostAdapterError("storage", "Codex prompt observation is malformed.");
|
|
5995
|
-
drafts.push({ frame_kind: "user_input", payload: compact({
|
|
5996
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
5997
|
-
local_turn_ref: localTurnRef(envelope),
|
|
5998
|
-
local_input_ref: envelope.host_input_ref,
|
|
5999
|
-
input_kind: envelope.input_kind,
|
|
6000
|
-
origin_input: envelope.origin_input,
|
|
6001
|
-
observations: [prompt],
|
|
6002
|
-
local_turn_aliases: envelope.codex_turn_id === void 0 ? void 0 : [envelope.codex_turn_id],
|
|
6003
|
-
context_expires_at: envelope.expires_at
|
|
6004
|
-
}) });
|
|
6005
|
-
return drafts;
|
|
6006
|
-
}
|
|
6007
6486
|
};
|
|
6008
|
-
function mapTerminal(envelope) {
|
|
6009
|
-
const timing = observation(envelope, "timing");
|
|
6010
|
-
const timingValue = asRecord(timing?.value);
|
|
6011
|
-
const aborted = timingValue?.terminal === "aborted";
|
|
6012
|
-
const usage = envelope.observations.filter((item) => ["token", "cost", "model"].includes(item.observation_kind));
|
|
6013
|
-
return { frame_kind: "turn_terminal", payload: compact({
|
|
6014
|
-
local_session_ref: envelope.identity.host_session_ref,
|
|
6015
|
-
local_turn_ref: localTurnRef(envelope),
|
|
6016
|
-
terminal_state: aborted ? "aborted" : "completed",
|
|
6017
|
-
abort_reason: aborted && typeof timingValue?.reason === "string" ? safeReasonRef(timingValue.reason) : void 0,
|
|
6018
|
-
duration_ms: nonNegativeNumber(timingValue?.duration_ms),
|
|
6019
|
-
time_to_first_token_ms: nonNegativeNumber(timingValue?.time_to_first_token_ms),
|
|
6020
|
-
usage: usage.length === 0 ? void 0 : Object.fromEntries(usage.map((item) => [item.observation_kind, item.value])),
|
|
6021
|
-
observations: envelope.observations
|
|
6022
|
-
}) };
|
|
6023
|
-
}
|
|
6024
|
-
function observation(envelope, kind) {
|
|
6025
|
-
return envelope.observations.find((item) => item.observation_kind === kind);
|
|
6026
|
-
}
|
|
6027
|
-
function localTurnRef(envelope) {
|
|
6028
|
-
return envelope.identity.host_turn_ref;
|
|
6029
|
-
}
|
|
6030
|
-
function compact(value) {
|
|
6031
|
-
return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
|
|
6032
|
-
}
|
|
6033
|
-
function asRecord(value) {
|
|
6034
|
-
return isRecord6(value) ? value : void 0;
|
|
6035
|
-
}
|
|
6036
|
-
function recordText(value, key) {
|
|
6037
|
-
const record3 = asRecord(value);
|
|
6038
|
-
return typeof record3?.[key] === "string" ? record3[key] : void 0;
|
|
6039
|
-
}
|
|
6040
|
-
function nestedText(value, key) {
|
|
6041
|
-
const nested = asRecord(asRecord(value)?.[key]);
|
|
6042
|
-
return typeof nested?.value === "string" ? nested.value : void 0;
|
|
6043
|
-
}
|
|
6044
|
-
function nonNegativeNumber(value) {
|
|
6045
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
6046
|
-
}
|
|
6047
|
-
function safeReasonRef(value) {
|
|
6048
|
-
return SAFE_REF7.test(value) ? value : `reason:${createHash6("sha256").update(value, "utf8").digest("hex")}`;
|
|
6049
|
-
}
|
|
6050
|
-
function isRecord6(value) {
|
|
6051
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6052
|
-
}
|
|
6053
6487
|
|
|
6054
6488
|
// host/codex/src/codex-transcript-baseline.ts
|
|
6055
6489
|
import { createHash as createHash7 } from "node:crypto";
|
|
6056
6490
|
|
|
6057
6491
|
// host/codex/src/codex-transcript-decoder.ts
|
|
6058
|
-
var
|
|
6492
|
+
var SAFE_REF6 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
|
|
6059
6493
|
var MAX_PROMPT_BYTES = 64 * 1024;
|
|
6060
6494
|
function decodeCodexTranscriptLine(line) {
|
|
6061
6495
|
let value;
|
|
@@ -6064,40 +6498,40 @@ function decodeCodexTranscriptLine(line) {
|
|
|
6064
6498
|
} catch {
|
|
6065
6499
|
reject("Codex transcript record is not valid JSON.");
|
|
6066
6500
|
}
|
|
6067
|
-
if (!
|
|
6501
|
+
if (!isRecord8(value)) reject("Codex transcript record envelope is malformed.");
|
|
6068
6502
|
const ordinal = safeOrdinal(value.ordinal);
|
|
6069
|
-
const
|
|
6070
|
-
if (!
|
|
6071
|
-
if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal,
|
|
6072
|
-
if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal,
|
|
6073
|
-
if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal,
|
|
6074
|
-
if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal,
|
|
6075
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
6076
|
-
}
|
|
6077
|
-
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) {
|
|
6078
6512
|
return {
|
|
6079
6513
|
kind: "turn_context",
|
|
6080
6514
|
ordinal,
|
|
6081
|
-
timestamp:
|
|
6082
|
-
turn_id:
|
|
6515
|
+
timestamp: timestamp3,
|
|
6516
|
+
turn_id: safeRef4(payload.turn_id, "Codex turn context identity is invalid."),
|
|
6083
6517
|
model: boundedNonEmptyText(payload.model, 256, "Codex turn context model is invalid.")
|
|
6084
6518
|
};
|
|
6085
6519
|
}
|
|
6086
|
-
function decodeSessionMeta(payload, ordinal,
|
|
6087
|
-
const sessionId =
|
|
6520
|
+
function decodeSessionMeta(payload, ordinal, timestamp3) {
|
|
6521
|
+
const sessionId = safeRef4(payload.id, "Codex transcript session identity is invalid.");
|
|
6088
6522
|
const cliVersion = boundedNonEmptyText(payload.cli_version, 64, "Codex transcript version is invalid.");
|
|
6089
|
-
return { kind: "session_meta", ordinal, timestamp:
|
|
6523
|
+
return { kind: "session_meta", ordinal, timestamp: timestamp3, session_id: sessionId, cli_version: cliVersion };
|
|
6090
6524
|
}
|
|
6091
|
-
function decodeEventMessage(payload, ordinal,
|
|
6525
|
+
function decodeEventMessage(payload, ordinal, timestamp3) {
|
|
6092
6526
|
if (payload.type === "task_started") {
|
|
6093
|
-
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) };
|
|
6094
6528
|
}
|
|
6095
6529
|
if (payload.type === "task_complete") {
|
|
6096
6530
|
return {
|
|
6097
6531
|
kind: "task_complete",
|
|
6098
6532
|
ordinal,
|
|
6099
|
-
timestamp:
|
|
6100
|
-
turn_id:
|
|
6533
|
+
timestamp: timestamp3,
|
|
6534
|
+
turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
|
|
6101
6535
|
started_at: safeNumber(payload.started_at),
|
|
6102
6536
|
completed_at: safeNumber(payload.completed_at),
|
|
6103
6537
|
duration_ms: safeNumber(payload.duration_ms),
|
|
@@ -6108,20 +6542,20 @@ function decodeEventMessage(payload, ordinal, timestamp4) {
|
|
|
6108
6542
|
return {
|
|
6109
6543
|
kind: "turn_aborted",
|
|
6110
6544
|
ordinal,
|
|
6111
|
-
timestamp:
|
|
6112
|
-
turn_id:
|
|
6545
|
+
timestamp: timestamp3,
|
|
6546
|
+
turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
|
|
6113
6547
|
started_at: safeNumber(payload.started_at),
|
|
6114
6548
|
completed_at: safeNumber(payload.completed_at),
|
|
6115
6549
|
duration_ms: safeNumber(payload.duration_ms),
|
|
6116
6550
|
reason: boundedText(payload.reason, 256, "Codex task abort reason is invalid.")
|
|
6117
6551
|
};
|
|
6118
6552
|
}
|
|
6119
|
-
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal,
|
|
6120
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
6553
|
+
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp3);
|
|
6554
|
+
return { kind: "ignored", ordinal, timestamp: timestamp3 };
|
|
6121
6555
|
}
|
|
6122
|
-
function decodeTokenCount(payload, ordinal,
|
|
6123
|
-
if (payload.info === null) return { kind: "token_count", ordinal, timestamp:
|
|
6124
|
-
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" };
|
|
6125
6559
|
const lastUsage = optionalTokenUsage(payload.info.last_token_usage);
|
|
6126
6560
|
const totalUsage = optionalTokenUsage(payload.info.total_token_usage);
|
|
6127
6561
|
const gap = lastUsage.status === "invalid" || totalUsage.status === "invalid";
|
|
@@ -6129,7 +6563,7 @@ function decodeTokenCount(payload, ordinal, timestamp4) {
|
|
|
6129
6563
|
return {
|
|
6130
6564
|
kind: "token_count",
|
|
6131
6565
|
ordinal,
|
|
6132
|
-
timestamp:
|
|
6566
|
+
timestamp: timestamp3,
|
|
6133
6567
|
...lastUsage.status === "valid" ? { last_usage: lastUsage.value } : {},
|
|
6134
6568
|
...totalUsage.status === "valid" ? { total_usage: totalUsage.value } : {},
|
|
6135
6569
|
...usageGapReason === void 0 ? {} : { usage_gap_reason: usageGapReason }
|
|
@@ -6145,7 +6579,7 @@ function optionalTokenUsage(value) {
|
|
|
6145
6579
|
}
|
|
6146
6580
|
}
|
|
6147
6581
|
function decodeTokenUsage(value) {
|
|
6148
|
-
if (!
|
|
6582
|
+
if (!isRecord8(value)) reject("Codex token usage is malformed.");
|
|
6149
6583
|
const usage = {
|
|
6150
6584
|
input_tokens: safeTokenCount(value.input_tokens),
|
|
6151
6585
|
cached_input_tokens: safeTokenCount(value.cached_input_tokens),
|
|
@@ -6157,35 +6591,33 @@ function decodeTokenUsage(value) {
|
|
|
6157
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.");
|
|
6158
6592
|
return usage;
|
|
6159
6593
|
}
|
|
6160
|
-
function decodeResponseItem(payload, ordinal,
|
|
6161
|
-
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 };
|
|
6162
6596
|
const metadata = userMessageMetadata(payload.internal_chat_message_metadata_passthrough);
|
|
6163
|
-
const
|
|
6164
|
-
if (
|
|
6597
|
+
const contentItemKind = userTextContentKind(metadata.content_item_kinds);
|
|
6598
|
+
if (contentItemKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp3 };
|
|
6165
6599
|
const prompt = userTextContent(payload.content);
|
|
6166
6600
|
return {
|
|
6167
6601
|
kind: "user_text",
|
|
6168
6602
|
ordinal,
|
|
6169
|
-
timestamp:
|
|
6603
|
+
timestamp: timestamp3,
|
|
6170
6604
|
prompt,
|
|
6171
|
-
|
|
6172
|
-
message_id:
|
|
6173
|
-
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.")
|
|
6174
6608
|
};
|
|
6175
6609
|
}
|
|
6176
|
-
function
|
|
6177
|
-
|
|
6178
|
-
if (kinds[0] === "user.text") return "human";
|
|
6179
|
-
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;
|
|
6180
6612
|
}
|
|
6181
6613
|
function userMessageMetadata(value) {
|
|
6182
|
-
if (!
|
|
6614
|
+
if (!isRecord8(value) || !Array.isArray(value.content_item_kinds)) reject("Codex user message provenance is malformed.");
|
|
6183
6615
|
return { ...value, content_item_kinds: value.content_item_kinds };
|
|
6184
6616
|
}
|
|
6185
6617
|
function userTextContent(value) {
|
|
6186
6618
|
if (!Array.isArray(value)) reject(`Codex user text content is malformed (${refusalFact("kind", typeof value, "array")}).`);
|
|
6187
6619
|
if (value.length !== 1) reject(`Codex user text content is malformed (${refusalFact("items", value.length, "exactly one")}).`);
|
|
6188
|
-
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")}).`);
|
|
6189
6621
|
return boundedText(value[0].text, MAX_PROMPT_BYTES, "Codex user text is invalid.");
|
|
6190
6622
|
}
|
|
6191
6623
|
function safeOrdinal(value) {
|
|
@@ -6206,8 +6638,8 @@ function safeTimestamp(value) {
|
|
|
6206
6638
|
if (!Number.isFinite(Date.parse(value))) reject(`Codex transcript timestamp is invalid (${refusalFact("parsed_ms", Date.parse(value), "a finite epoch milliseconds value")}).`);
|
|
6207
6639
|
return value;
|
|
6208
6640
|
}
|
|
6209
|
-
function
|
|
6210
|
-
if (typeof value !== "string" || !
|
|
6641
|
+
function safeRef4(value, message) {
|
|
6642
|
+
if (typeof value !== "string" || !SAFE_REF6.test(value)) reject(message);
|
|
6211
6643
|
return value;
|
|
6212
6644
|
}
|
|
6213
6645
|
function boundedText(value, maximum, message) {
|
|
@@ -6221,7 +6653,7 @@ function boundedNonEmptyText(value, maximum, message) {
|
|
|
6221
6653
|
if (result.length === 0) reject(message);
|
|
6222
6654
|
return result;
|
|
6223
6655
|
}
|
|
6224
|
-
function
|
|
6656
|
+
function isRecord8(value) {
|
|
6225
6657
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6226
6658
|
}
|
|
6227
6659
|
function reject(message) {
|
|
@@ -6245,46 +6677,48 @@ function codexTranscriptInitialCursor(registration, device, inode, now) {
|
|
|
6245
6677
|
anchor_sha256: digest(Buffer.alloc(0)),
|
|
6246
6678
|
header_validated: false,
|
|
6247
6679
|
status: "active",
|
|
6248
|
-
updated_at:
|
|
6680
|
+
updated_at: timestamp2(now)
|
|
6249
6681
|
};
|
|
6250
6682
|
}
|
|
6251
|
-
async function ownedCodexTranscriptFile(handle) {
|
|
6683
|
+
async function ownedCodexTranscriptFile(handle, platform = process.platform) {
|
|
6252
6684
|
const stats = await handle.stat();
|
|
6253
|
-
|
|
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.");
|
|
6254
6687
|
return { size: stats.size, device: String(stats.dev), inode: String(stats.ino) };
|
|
6255
6688
|
}
|
|
6256
|
-
function
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
}
|
|
6261
|
-
function assertCodexTranscriptHeader(registration, record3) {
|
|
6262
|
-
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([
|
|
6263
|
-
{ name: "kind", value: record3.kind, expected: "session_meta" },
|
|
6264
|
-
{ 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 }
|
|
6265
6693
|
])}).`);
|
|
6266
6694
|
}
|
|
6267
6695
|
function digest(value) {
|
|
6268
6696
|
return createHash7("sha256").update(value).digest("hex");
|
|
6269
6697
|
}
|
|
6270
|
-
function
|
|
6698
|
+
function timestamp2(now) {
|
|
6271
6699
|
const value = now();
|
|
6272
6700
|
if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
|
|
6273
6701
|
return value.toISOString();
|
|
6274
6702
|
}
|
|
6275
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
|
+
|
|
6276
6712
|
// host/codex/src/codex-adapter.ts
|
|
6277
|
-
var
|
|
6713
|
+
var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
6278
6714
|
function codexSubmissionReference(hostInputRef, event, ordinal) {
|
|
6279
|
-
if (!
|
|
6715
|
+
if (!SAFE_REF7.test(hostInputRef) || !Number.isSafeInteger(ordinal) || ordinal < 0) throw new CodexHostAdapterError("invalid_input", "Codex submission identity is invalid.");
|
|
6280
6716
|
return `codex-submission:${cealRequestSha256({ host_input_ref: hostInputRef, event, ordinal })}`;
|
|
6281
6717
|
}
|
|
6282
6718
|
|
|
6283
6719
|
// host/codex/src/codex-transcript-watcher.ts
|
|
6284
6720
|
var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
6285
6721
|
store;
|
|
6286
|
-
registerCurrentPrompt;
|
|
6287
|
-
releaseCurrentPrompt;
|
|
6288
6722
|
diagnostic;
|
|
6289
6723
|
constructor(options) {
|
|
6290
6724
|
super(options, {
|
|
@@ -6293,8 +6727,6 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6293
6727
|
clock_failure: () => new CodexHostAdapterError("storage", "Codex watcher clock is invalid.")
|
|
6294
6728
|
});
|
|
6295
6729
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
6296
|
-
this.registerCurrentPrompt = options.register_current_prompt;
|
|
6297
|
-
this.releaseCurrentPrompt = options.release_current_prompt;
|
|
6298
6730
|
this.diagnostic = options.diagnostic ?? (() => void 0);
|
|
6299
6731
|
}
|
|
6300
6732
|
transcriptPathOf(registration) {
|
|
@@ -6317,14 +6749,12 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6317
6749
|
return { records: 0, enqueued: 0, quarantined: true };
|
|
6318
6750
|
}
|
|
6319
6751
|
async retireMissing(registration) {
|
|
6320
|
-
await this.releaseStoredPromptBestEffort(registration.host_session_ref, await this.store.readTranscriptCursor(registration).catch(() => null));
|
|
6321
6752
|
return this.retireMissingTranscript(registration.transcript_path, "Codex", () => this.store.retireTranscriptRegistration(registration));
|
|
6322
6753
|
}
|
|
6323
6754
|
async preflight(registration, handle, size, device, inode) {
|
|
6324
6755
|
const existing = await this.store.readTranscriptCursor(registration);
|
|
6325
6756
|
let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
|
|
6326
6757
|
if (cursor.status === "quarantined") {
|
|
6327
|
-
if (cursor.reason !== "transcript_malformed") await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
|
|
6328
6758
|
return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
6329
6759
|
}
|
|
6330
6760
|
const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
|
|
@@ -6338,22 +6768,22 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6338
6768
|
let records = 0;
|
|
6339
6769
|
let enqueued = 0;
|
|
6340
6770
|
for (const line of complete.toString("utf8").split("\n").filter((value) => value.length > 0)) {
|
|
6341
|
-
const
|
|
6342
|
-
assertRecordSequence(registration, cursor, records, lastOrdinal,
|
|
6343
|
-
await this.observeSessionMetadata(registration,
|
|
6344
|
-
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);
|
|
6345
6775
|
activeTask = outcome.activeTask;
|
|
6346
6776
|
enqueued += outcome.enqueued;
|
|
6347
|
-
lastOrdinal =
|
|
6777
|
+
lastOrdinal = record2.ordinal;
|
|
6348
6778
|
records += 1;
|
|
6349
6779
|
}
|
|
6350
6780
|
await this.commitCursor(registration, handle, cursor, complete.length, device, inode, lastOrdinal, activeTask);
|
|
6351
6781
|
return { records, enqueued, quarantined: false };
|
|
6352
6782
|
}
|
|
6353
|
-
async observeSessionMetadata(registration,
|
|
6354
|
-
if (
|
|
6783
|
+
async observeSessionMetadata(registration, record2) {
|
|
6784
|
+
if (record2.kind !== "session_meta") return;
|
|
6355
6785
|
const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
|
|
6356
|
-
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 } });
|
|
6357
6787
|
}
|
|
6358
6788
|
async commitCursor(registration, handle, cursor, consumedBytes, device, inode, lastOrdinal, activeTask) {
|
|
6359
6789
|
const offset = cursor.offset + consumedBytes;
|
|
@@ -6376,56 +6806,54 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6376
6806
|
};
|
|
6377
6807
|
await this.store.writeTranscriptCursor(quarantined);
|
|
6378
6808
|
}
|
|
6379
|
-
async processRecord(registration, activeTask,
|
|
6380
|
-
if (
|
|
6381
|
-
if (
|
|
6382
|
-
if (
|
|
6383
|
-
if (
|
|
6384
|
-
if (
|
|
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);
|
|
6385
6815
|
return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
6386
6816
|
}
|
|
6387
|
-
async processTaskStarted(registration,
|
|
6388
|
-
|
|
6389
|
-
return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
|
|
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 };
|
|
6390
6819
|
}
|
|
6391
|
-
async processUserInput(registration, activeTask,
|
|
6392
|
-
if (activeTask === void 0 || activeTask.codex_turn_id !==
|
|
6393
|
-
const adopted = await this.adoptHookCorrelatedTurn(registration,
|
|
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);
|
|
6394
6823
|
if (adopted === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
6395
6824
|
activeTask = adopted;
|
|
6396
6825
|
}
|
|
6397
|
-
const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref,
|
|
6826
|
+
const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record2.turn_id, record2.message_id, record2.ordinal, record2.prompt);
|
|
6398
6827
|
const origin = activeTask.origin_host_input_ref === void 0;
|
|
6399
|
-
const promptValue = promptObservationValue(
|
|
6828
|
+
const promptValue = promptObservationValue(record2.prompt);
|
|
6400
6829
|
const currentPromptRef = hostProducerPromptRef(promptValue.body_sha256);
|
|
6401
6830
|
const promptRef = activeTask.prompt_ref ?? currentPromptRef;
|
|
6402
6831
|
const steeredPromptRef = await this.steeredPromptRef(registration, activeTask, origin, currentPromptRef);
|
|
6403
6832
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
6404
6833
|
const observations = [{
|
|
6405
|
-
observation_ref: observationRef("transcript_user_text", hostInputRef,
|
|
6834
|
+
observation_ref: observationRef(registration.host_source_ref, "transcript_user_text", hostInputRef, record2.prompt),
|
|
6406
6835
|
observation_kind: "prompt",
|
|
6407
|
-
observed_at:
|
|
6836
|
+
observed_at: record2.timestamp,
|
|
6408
6837
|
value: promptValue
|
|
6409
6838
|
}];
|
|
6410
6839
|
if (origin) {
|
|
6411
|
-
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") });
|
|
6412
6841
|
const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
|
|
6413
|
-
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 });
|
|
6414
6843
|
}
|
|
6415
6844
|
const envelope = {
|
|
6416
6845
|
schema_version: "ceal.codex_observation_outbox.v1",
|
|
6417
6846
|
identity,
|
|
6418
6847
|
host_input_ref: hostInputRef,
|
|
6419
|
-
input_kind: record3.input_kind,
|
|
6420
6848
|
prompt_ref: promptRef,
|
|
6421
6849
|
observations,
|
|
6422
|
-
codex_turn_id:
|
|
6850
|
+
codex_turn_id: record2.turn_id,
|
|
6423
6851
|
origin_input: origin,
|
|
6424
|
-
submission_ref: codexSubmissionReference(hostInputRef, "input",
|
|
6425
|
-
created_at:
|
|
6852
|
+
submission_ref: codexSubmissionReference(hostInputRef, "input", record2.ordinal),
|
|
6853
|
+
created_at: record2.timestamp,
|
|
6426
6854
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
6427
6855
|
};
|
|
6428
|
-
const persisted = await this.persistUserInputTurn(
|
|
6856
|
+
const persisted = await this.persistUserInputTurn(activeTask, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope);
|
|
6429
6857
|
if (steeredPromptRef !== null) await this.activateTranscriptTurnContext(identity, steeredPromptRef, envelope.expires_at);
|
|
6430
6858
|
return persisted;
|
|
6431
6859
|
}
|
|
@@ -6452,40 +6880,21 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6452
6880
|
* stay ignored. A stale active task for another turn releases first, the
|
|
6453
6881
|
* same as a task_started supersession.
|
|
6454
6882
|
*/
|
|
6455
|
-
async adoptHookCorrelatedTurn(registration,
|
|
6456
|
-
const currentPromptRef = hostProducerPromptRef(promptObservationValue(
|
|
6883
|
+
async adoptHookCorrelatedTurn(registration, record2) {
|
|
6884
|
+
const currentPromptRef = hostProducerPromptRef(promptObservationValue(record2.prompt).body_sha256);
|
|
6457
6885
|
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref);
|
|
6458
6886
|
if (selection?.prompt_ref !== currentPromptRef) return void 0;
|
|
6459
|
-
if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) {
|
|
6460
|
-
await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
6461
|
-
}
|
|
6462
6887
|
return {
|
|
6463
|
-
codex_turn_id:
|
|
6464
|
-
host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref,
|
|
6465
|
-
started_at:
|
|
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
|
|
6466
6891
|
};
|
|
6467
6892
|
}
|
|
6468
|
-
async persistUserInputTurn(
|
|
6469
|
-
|
|
6470
|
-
if (
|
|
6471
|
-
try {
|
|
6472
|
-
await this.store.put(envelope);
|
|
6473
|
-
if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
|
|
6474
|
-
} catch (error) {
|
|
6475
|
-
if (custodyRegistered) await this.releasePrompt(registration.host_session_ref, activeTask, currentPromptRef).catch(() => void 0);
|
|
6476
|
-
throw error;
|
|
6477
|
-
}
|
|
6893
|
+
async persistUserInputTurn(activeTask, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope) {
|
|
6894
|
+
await this.store.put(envelope);
|
|
6895
|
+
if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
|
|
6478
6896
|
return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
|
|
6479
6897
|
}
|
|
6480
|
-
async registerPrompt(origin, registration, activeTask, promptRef, prompt, expiresAt) {
|
|
6481
|
-
if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return false;
|
|
6482
|
-
const register = this.registerCurrentPrompt;
|
|
6483
|
-
const installationRef = this.producerInstallationRef;
|
|
6484
|
-
return registerHostCurrentPromptDiagnosed(
|
|
6485
|
-
() => register({ producer_id: "codex", producer_installation_ref: installationRef, local_session_ref: registration.host_session_ref, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef, current_prompt: prompt, context_expires_at: expiresAt }),
|
|
6486
|
-
(event) => this.diagnostic(event)
|
|
6487
|
-
);
|
|
6488
|
-
}
|
|
6489
6898
|
async activateTranscriptTurnContext(identity, promptRef, expiresAt) {
|
|
6490
6899
|
try {
|
|
6491
6900
|
const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
@@ -6497,41 +6906,27 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6497
6906
|
throw error;
|
|
6498
6907
|
}
|
|
6499
6908
|
}
|
|
6500
|
-
async
|
|
6501
|
-
if (
|
|
6502
|
-
await this.releaseCurrentPrompt({ producer_id: "codex", producer_installation_ref: this.producerInstallationRef, local_session_ref: sessionRef, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef });
|
|
6503
|
-
}
|
|
6504
|
-
async releaseStoredPromptBestEffort(sessionRef, cursor) {
|
|
6505
|
-
await this.releaseActiveTaskBestEffort(sessionRef, cursor?.active_task);
|
|
6506
|
-
}
|
|
6507
|
-
async releaseActiveTaskBestEffort(sessionRef, activeTask) {
|
|
6508
|
-
if (activeTask?.prompt_ref === void 0) return;
|
|
6509
|
-
await this.releasePrompt(sessionRef, activeTask, activeTask.prompt_ref).catch(() => void 0);
|
|
6510
|
-
}
|
|
6511
|
-
async processTerminal(registration, activeTask, record3) {
|
|
6512
|
-
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id || activeTask.origin_host_input_ref === void 0 || activeTask.prompt_ref === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
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 };
|
|
6513
6911
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
6514
|
-
const timing =
|
|
6515
|
-
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);
|
|
6516
6914
|
await this.store.put({
|
|
6517
6915
|
schema_version: "ceal.codex_observation_outbox.v1",
|
|
6518
6916
|
identity,
|
|
6519
6917
|
host_input_ref: activeTask.origin_host_input_ref,
|
|
6520
|
-
input_kind: "host_automatic",
|
|
6521
6918
|
prompt_ref: activeTask.prompt_ref,
|
|
6522
6919
|
observations,
|
|
6523
|
-
codex_turn_id:
|
|
6920
|
+
codex_turn_id: record2.turn_id,
|
|
6524
6921
|
origin_input: false,
|
|
6525
|
-
submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal",
|
|
6526
|
-
created_at:
|
|
6922
|
+
submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record2.ordinal),
|
|
6923
|
+
created_at: record2.timestamp,
|
|
6527
6924
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
6528
6925
|
});
|
|
6529
|
-
await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
6530
6926
|
await this.store.clearInvocationContext(identity, this.now());
|
|
6531
6927
|
return { enqueued: 1 };
|
|
6532
6928
|
}
|
|
6533
6929
|
async quarantine(registration, cursor, device, inode, reason) {
|
|
6534
|
-
await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
|
|
6535
6930
|
const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at: this.stamp() };
|
|
6536
6931
|
await this.store.writeTranscriptCursor(quarantined);
|
|
6537
6932
|
}
|
|
@@ -6544,10 +6939,10 @@ function codexTranscriptScanFailureKind(error) {
|
|
|
6544
6939
|
return void 0;
|
|
6545
6940
|
});
|
|
6546
6941
|
}
|
|
6547
|
-
function accumulateTokenCount(activeTask,
|
|
6548
|
-
if (
|
|
6549
|
-
const last =
|
|
6550
|
-
const total =
|
|
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;
|
|
6551
6946
|
if (last === void 0 || ignoreTokenCount(activeTask.last_total_usage, total, last)) return activeTask;
|
|
6552
6947
|
const tokenUsage = addUsage(activeTask.token_usage, last);
|
|
6553
6948
|
return {
|
|
@@ -6561,21 +6956,16 @@ function ignoreTokenCount(previous, total, last) {
|
|
|
6561
6956
|
if (total === void 0 || previous === void 0) return false;
|
|
6562
6957
|
return sameUsage(total, previous) || regressedUsage(total, previous) && looksLikeStaleRegression(total, previous, last);
|
|
6563
6958
|
}
|
|
6564
|
-
function terminalObservations(registration, activeTask,
|
|
6959
|
+
function terminalObservations(registration, activeTask, record2, timing) {
|
|
6565
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);
|
|
6566
|
-
const costValue = terminalCostValue();
|
|
6567
6961
|
const observations = [
|
|
6568
|
-
{ observation_ref: observationRef("transcript_terminal", activeTask.host_turn_ref,
|
|
6569
|
-
{ observation_ref: observationRef("transcript_timing", activeTask.host_turn_ref, timing), observation_kind: "timing", observed_at:
|
|
6570
|
-
{ observation_ref: observationRef("transcript_token", activeTask.host_turn_ref, tokenValue), observation_kind: "token", observed_at:
|
|
6571
|
-
{ 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 }
|
|
6572
6965
|
];
|
|
6573
|
-
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" } });
|
|
6574
6967
|
return observations;
|
|
6575
6968
|
}
|
|
6576
|
-
function terminalCostValue() {
|
|
6577
|
-
return notRecorded("provider_cost_not_observed");
|
|
6578
|
-
}
|
|
6579
6969
|
function notRecorded(reason) {
|
|
6580
6970
|
return hostObservationNotRecorded(reason);
|
|
6581
6971
|
}
|
|
@@ -6622,25 +7012,21 @@ function looksLikeStaleRegression(current, previous, last) {
|
|
|
6622
7012
|
if (previous.total_tokens <= 0 || current.total_tokens <= 0 || last.total_tokens <= 0) return false;
|
|
6623
7013
|
return current.total_tokens * 100 >= previous.total_tokens * 98 || current.total_tokens + last.total_tokens * 2 >= previous.total_tokens;
|
|
6624
7014
|
}
|
|
6625
|
-
function assertRecordSequence(registration, cursor, records, lastOrdinal,
|
|
6626
|
-
if ((cursor.header_validated || records > 0) &&
|
|
6627
|
-
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);
|
|
6628
7018
|
}
|
|
6629
7019
|
|
|
6630
7020
|
// host/codex/src/codex-producer-runtime.ts
|
|
6631
7021
|
async function drainCodexProducerOnce(options) {
|
|
6632
|
-
const promptPort = new HostRelayCurrentPromptPort(options.relay_endpoint, options.timeout_ms);
|
|
6633
7022
|
const scan = await new CodexTranscriptWatcher({
|
|
6634
7023
|
outbox_dir: options.outbox_dir,
|
|
6635
|
-
producer_installation_ref: options.producer_installation_ref,
|
|
6636
|
-
register_current_prompt: (input) => promptPort.register(input),
|
|
6637
|
-
release_current_prompt: (identity) => promptPort.release(identity),
|
|
6638
7024
|
diagnostic: (event, failure_kind) => writeHostServiceDiagnostic("codex", event, failure_kind)
|
|
6639
7025
|
}).scanOnce().catch((error) => {
|
|
6640
7026
|
writeHostServiceDiagnostic("codex", "watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
6641
7027
|
throw error;
|
|
6642
7028
|
});
|
|
6643
|
-
const producer = new CodexProducer({ outbox_dir: options.outbox_dir,
|
|
7029
|
+
const producer = new CodexProducer({ outbox_dir: options.outbox_dir, ...options.now === void 0 ? {} : { now: options.now } });
|
|
6644
7030
|
return { scanned: scan.enqueued, ...await producer.drainOutbox() };
|
|
6645
7031
|
}
|
|
6646
7032
|
|
|
@@ -6653,33 +7039,29 @@ var CODEX_PRODUCER_DESCRIPTOR = {
|
|
|
6653
7039
|
extra_paths: [CODEX_SESSIONS_ROOT],
|
|
6654
7040
|
config: (shared, extras) => ({ ...shared, codex_sessions_root: hostProducerExtraPath(extras, CODEX_SESSIONS_ROOT) }),
|
|
6655
7041
|
runtime_options: (config) => Object.freeze({
|
|
6656
|
-
outbox_dir: config.outbox_dir
|
|
6657
|
-
producer_installation_ref: config.producer_installation_ref,
|
|
6658
|
-
producer_epoch: config.producer_epoch,
|
|
6659
|
-
relay_endpoint: config.relay_endpoint,
|
|
6660
|
-
timeout_ms: config.ipc_timeout_ms
|
|
7042
|
+
outbox_dir: config.outbox_dir
|
|
6661
7043
|
}),
|
|
6662
7044
|
drain: drainCodexProducerOnce
|
|
6663
7045
|
};
|
|
6664
7046
|
|
|
6665
7047
|
// host/codex/src/codex-visible-output-budget.ts
|
|
6666
|
-
import { readFile as
|
|
7048
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
6667
7049
|
import { homedir } from "node:os";
|
|
6668
|
-
import { join as
|
|
7050
|
+
import { join as join6 } from "node:path";
|
|
6669
7051
|
var import_toml = __toESM(require_toml(), 1);
|
|
6670
7052
|
var CODEX_DEFAULT_TOOL_OUTPUT_TOKEN_LIMIT = 5e3;
|
|
6671
7053
|
var CODEX_UTF8_BYTES_PER_OUTPUT_TOKEN = 4;
|
|
6672
7054
|
async function resolveCodexVisibleOutputMaxBytes(environment = process.env) {
|
|
6673
|
-
const codexHome = environment
|
|
7055
|
+
const codexHome = resolveCodexHome(environment, homedir());
|
|
6674
7056
|
let source;
|
|
6675
7057
|
try {
|
|
6676
|
-
source = await
|
|
7058
|
+
source = await readFile2(join6(codexHome, "config.toml"), "utf8");
|
|
6677
7059
|
} catch (error) {
|
|
6678
7060
|
return isNotFound2(error) ? codexBytes(CODEX_DEFAULT_TOOL_OUTPUT_TOKEN_LIMIT) : void 0;
|
|
6679
7061
|
}
|
|
6680
7062
|
try {
|
|
6681
7063
|
const parsed = import_toml.default.parse(source);
|
|
6682
|
-
if (!
|
|
7064
|
+
if (!isRecord9(parsed)) return void 0;
|
|
6683
7065
|
const configured = parsed.tool_output_token_limit;
|
|
6684
7066
|
if (configured === void 0) return codexBytes(CODEX_DEFAULT_TOOL_OUTPUT_TOKEN_LIMIT);
|
|
6685
7067
|
return typeof configured === "number" && Number.isSafeInteger(configured) && configured > 0 ? codexBytes(configured) : void 0;
|
|
@@ -6690,7 +7072,7 @@ async function resolveCodexVisibleOutputMaxBytes(environment = process.env) {
|
|
|
6690
7072
|
function codexBytes(tokens) {
|
|
6691
7073
|
return Math.min(tokens * CODEX_UTF8_BYTES_PER_OUTPUT_TOKEN, CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES);
|
|
6692
7074
|
}
|
|
6693
|
-
function
|
|
7075
|
+
function isRecord9(value) {
|
|
6694
7076
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6695
7077
|
}
|
|
6696
7078
|
function isNotFound2(error) {
|
|
@@ -6707,11 +7089,28 @@ var CODEX_INVOCATION_AUTHENTICATION_DESCRIPTOR = {
|
|
|
6707
7089
|
config_flag: HOST_PRODUCER_CONFIG_FLAG,
|
|
6708
7090
|
parse_config_flag: parseHostProducerConfigFlag,
|
|
6709
7091
|
read_producer_config: (configPath) => readHostProducerConfig(CODEX_PRODUCER_DESCRIPTOR, configPath),
|
|
6710
|
-
resolve_visible_output_max_bytes: () => resolveCodexVisibleOutputMaxBytes()
|
|
7092
|
+
resolve_visible_output_max_bytes: () => resolveCodexVisibleOutputMaxBytes(),
|
|
7093
|
+
read_human_message_window: readCodexInvocationHumanMessageWindow
|
|
6711
7094
|
};
|
|
6712
7095
|
function runCodexInvocationAuthenticationCommand(args, options) {
|
|
6713
7096
|
return runHostInvocationAuthenticationCommand(CODEX_INVOCATION_AUTHENTICATION_DESCRIPTOR, args, options);
|
|
6714
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
|
+
}
|
|
6715
7114
|
|
|
6716
7115
|
// host/codex/bin/ceal-codex-invocation-authentication.ts
|
|
6717
7116
|
var CODEX_INVOCATION_AUTHENTICATION_CLIENT_COMMAND = {
|