@corca-org/ceal-host-linux-arm64 1.0.18 → 1.0.19
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 +0 -0
- package/libexec/ceal-host/claude-hook.mjs +1924 -522
- package/libexec/ceal-host/claude-invocation-authentication.mjs +1806 -330
- package/libexec/ceal-host/claude-producer.mjs +1773 -412
- package/libexec/ceal-host/codex-hook.mjs +1427 -1013
- package/libexec/ceal-host/codex-invocation-authentication.mjs +3071 -515
- package/libexec/ceal-host/codex-producer.mjs +815 -439
- package/libexec/ceal-host/control.mjs +2266 -1095
- package/libexec/ceal-host/relay.mjs +468 -103
- package/package.json +1 -1
- package/share/ceal-host/bundle-manifest.json +12 -12
- package/share/ceal-host/bundle-manifest.sigstore.json +1 -1
|
@@ -3,8 +3,9 @@ import { createRequire as __cealCreateRequire } from "node:module";
|
|
|
3
3
|
const require = __cealCreateRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// host/codex/src/codex-adapter.ts
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
6
7
|
import { homedir } from "node:os";
|
|
7
|
-
import { isAbsolute as isAbsolute3, join as
|
|
8
|
+
import { isAbsolute as isAbsolute3, join as join4, relative, resolve as resolve2 } from "node:path";
|
|
8
9
|
|
|
9
10
|
// packages/ceal-client-protocol/src/canonical-json.ts
|
|
10
11
|
function cealCanonicalJson(value) {
|
|
@@ -113,6 +114,9 @@ var client_wire_contract_default = {
|
|
|
113
114
|
"document"
|
|
114
115
|
],
|
|
115
116
|
optional_fields: [
|
|
117
|
+
"operation_id",
|
|
118
|
+
"visible_output_max_bytes",
|
|
119
|
+
"local_delivery",
|
|
116
120
|
"verified_artifact"
|
|
117
121
|
],
|
|
118
122
|
bare_document_fields: [
|
|
@@ -226,6 +230,7 @@ var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verifie
|
|
|
226
230
|
var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
|
|
227
231
|
var CEAL_PROTOCOL_VERSION = "1.4.0";
|
|
228
232
|
var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
|
|
233
|
+
var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
|
|
229
234
|
var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
|
|
230
235
|
"audited_call_pending",
|
|
231
236
|
"continuation_read_pending",
|
|
@@ -249,6 +254,7 @@ var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
|
|
|
249
254
|
// packages/ceal-client-protocol/src/operation-next-actions.ts
|
|
250
255
|
var CEAL_REPEAT_REQUEST_NEXT_ACTION = "repeat_request";
|
|
251
256
|
var CEAL_INSPECT_RECEIPT_NEXT_ACTION = "inspect_receipt";
|
|
257
|
+
var CEAL_ANSWER_WITH_GAP_NEXT_ACTION = "answer_with_gap";
|
|
252
258
|
var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
253
259
|
"select_did_you_mean",
|
|
254
260
|
"choose_new_output_path",
|
|
@@ -302,10 +308,20 @@ var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
|
302
308
|
"retry_after",
|
|
303
309
|
// The ref names a container of rows (a Notion database or data source), which
|
|
304
310
|
// has no body of its own; the caller searches its rows instead of reading it.
|
|
305
|
-
"search_container_rows"
|
|
311
|
+
"search_container_rows",
|
|
312
|
+
// A settled unknown write names the exact receipt Operation that can inspect
|
|
313
|
+
// its outcome without issuing another provider mutation.
|
|
314
|
+
"write_receipt.get",
|
|
315
|
+
// Command resolution carries the exact required Operation sequence beside
|
|
316
|
+
// this action, so the caller can execute each declared input in order.
|
|
317
|
+
"follow_required_action_sequence",
|
|
318
|
+
// The command needs caller-owned local materialization (for example an
|
|
319
|
+
// @path file) before the Gateway can receive the argument.
|
|
320
|
+
"materialize_local_argument"
|
|
306
321
|
];
|
|
307
322
|
var CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS = Object.freeze(CALLER_RECOVERABLE_NEXT_ACTION_VALUES);
|
|
308
323
|
var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
324
|
+
CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
309
325
|
// Only an administrator can take the named step -- approving a place for an
|
|
310
326
|
// Operation, granting a withheld capability -- so a non-administrator asks
|
|
311
327
|
// one. The refusal names the administrator step; this word is the ask.
|
|
@@ -353,7 +369,58 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
|
353
369
|
"select_exact_approval"
|
|
354
370
|
];
|
|
355
371
|
var CEAL_NON_REMEDY_NEXT_ACTIONS = Object.freeze(NON_REMEDY_NEXT_ACTION_VALUES);
|
|
356
|
-
var
|
|
372
|
+
var CEAL_DECLARED_NEXT_ACTIONS = Object.freeze([...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS, ...CEAL_NON_REMEDY_NEXT_ACTIONS]);
|
|
373
|
+
var DECLARED = new Set(CEAL_DECLARED_NEXT_ACTIONS);
|
|
374
|
+
function defineCealNonModelNextActions(actions) {
|
|
375
|
+
return actions;
|
|
376
|
+
}
|
|
377
|
+
var NON_MODEL_NEXT_ACTION_VALUES = defineCealNonModelNextActions([
|
|
378
|
+
CEAL_INSPECT_RECEIPT_NEXT_ACTION,
|
|
379
|
+
"await_pending_approval",
|
|
380
|
+
"inspect_gateway",
|
|
381
|
+
"operator_action",
|
|
382
|
+
"none",
|
|
383
|
+
"check_gateway",
|
|
384
|
+
"inspect_context_store",
|
|
385
|
+
"inspect_local_cache",
|
|
386
|
+
"inspect_local_client",
|
|
387
|
+
"inspect_local_command",
|
|
388
|
+
"inspect_local_output",
|
|
389
|
+
"inspect_local_route",
|
|
390
|
+
"inspect_request",
|
|
391
|
+
"reconcile",
|
|
392
|
+
"reconcile_occurrence",
|
|
393
|
+
"retry_after_owner_recovery",
|
|
394
|
+
"select_exact_approval"
|
|
395
|
+
]);
|
|
396
|
+
var NON_MODEL_NEXT_ACTIONS = new Set(NON_MODEL_NEXT_ACTION_VALUES);
|
|
397
|
+
function isCealModelActionableNonRemedyNextAction(action) {
|
|
398
|
+
return !NON_MODEL_NEXT_ACTIONS.has(action);
|
|
399
|
+
}
|
|
400
|
+
var CEAL_MODEL_ACTIONABLE_NEXT_ACTIONS = Object.freeze([
|
|
401
|
+
...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS,
|
|
402
|
+
...CEAL_NON_REMEDY_NEXT_ACTIONS.filter(isCealModelActionableNonRemedyNextAction)
|
|
403
|
+
]);
|
|
404
|
+
var MODEL_ACTIONABLE = new Set(CEAL_MODEL_ACTIONABLE_NEXT_ACTIONS);
|
|
405
|
+
var NON_MODEL_ACTION_TO_MODEL_ACTION = Object.freeze({
|
|
406
|
+
[CEAL_INSPECT_RECEIPT_NEXT_ACTION]: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
407
|
+
await_pending_approval: "ask_admin",
|
|
408
|
+
inspect_gateway: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
409
|
+
operator_action: "ask_admin",
|
|
410
|
+
none: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
411
|
+
check_gateway: "ask_admin",
|
|
412
|
+
inspect_context_store: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
413
|
+
inspect_local_cache: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
414
|
+
inspect_local_client: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
415
|
+
inspect_local_command: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
416
|
+
inspect_local_output: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
417
|
+
inspect_local_route: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
418
|
+
inspect_request: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
419
|
+
reconcile: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
420
|
+
reconcile_occurrence: "ask_admin",
|
|
421
|
+
retry_after_owner_recovery: "ask_admin",
|
|
422
|
+
select_exact_approval: "ask_admin"
|
|
423
|
+
});
|
|
357
424
|
var RETRY_PROMISE_NEXT_ACTIONS = ["retry", "retry_after"];
|
|
358
425
|
var RETRY_PROMISE = new Set(RETRY_PROMISE_NEXT_ACTIONS);
|
|
359
426
|
var RECOVERABLE = new Set(CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS);
|
|
@@ -623,21 +690,15 @@ var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
|
|
|
623
690
|
// packages/ceal-client-protocol/src/domain-vocabulary.ts
|
|
624
691
|
var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
|
|
625
692
|
var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
|
|
626
|
-
var CEAL_PROVIDER_BOUNDARY_KINDS = vocabulary([
|
|
627
|
-
"github_account",
|
|
628
|
-
"github_organization",
|
|
629
|
-
"google_workspace_domain",
|
|
630
|
-
"notion_workspace",
|
|
631
|
-
"slack_workspace"
|
|
632
|
-
]);
|
|
633
|
-
var CEAL_PROVIDER_BOUNDARY_EVIDENCE = vocabulary(["provider_readback", "operator_stated"]);
|
|
634
693
|
var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
|
|
635
694
|
var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
|
|
636
695
|
var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
|
|
637
696
|
var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
|
|
638
697
|
var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
|
|
639
|
-
var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "
|
|
698
|
+
var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
|
|
699
|
+
var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
|
|
640
700
|
var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
|
|
701
|
+
var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
|
|
641
702
|
var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
|
|
642
703
|
|
|
643
704
|
// packages/ceal-client-protocol/src/safe-json-budget.ts
|
|
@@ -813,7 +874,7 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
813
874
|
"ceal.operation_receipt.v1",
|
|
814
875
|
"ceal.operation_result.v1",
|
|
815
876
|
"ceal.operation_result.compact.v1",
|
|
816
|
-
"ceal.
|
|
877
|
+
"ceal.local_result_file.v1",
|
|
817
878
|
"ceal.local_result_root.v1",
|
|
818
879
|
"ceal.owner_bootstrap_exchange_request.v1",
|
|
819
880
|
"ceal.owner_bootstrap_exchange_result.v1",
|
|
@@ -907,41 +968,6 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
|
|
|
907
968
|
}
|
|
908
969
|
});
|
|
909
970
|
|
|
910
|
-
// packages/ceal-client-protocol/src/provider-operation-vocabulary.ts
|
|
911
|
-
var CEAL_FILE_TYPE_FAMILIES = vocabulary(["all", "image", "pdf"]);
|
|
912
|
-
var CEAL_GITHUB_ISSUE_STATES = vocabulary(["open", "closed", "all"]);
|
|
913
|
-
var CEAL_NOTION_UPDATABLE_BLOCK_TYPES = vocabulary([
|
|
914
|
-
"paragraph",
|
|
915
|
-
"heading_1",
|
|
916
|
-
"heading_2",
|
|
917
|
-
"heading_3",
|
|
918
|
-
"bulleted_list_item",
|
|
919
|
-
"numbered_list_item",
|
|
920
|
-
"to_do",
|
|
921
|
-
"code"
|
|
922
|
-
]);
|
|
923
|
-
var CEAL_NOTION_NEUTRAL_TYPES = vocabulary([
|
|
924
|
-
"text",
|
|
925
|
-
"number",
|
|
926
|
-
"boolean",
|
|
927
|
-
"date",
|
|
928
|
-
"select",
|
|
929
|
-
"multi_select",
|
|
930
|
-
"people",
|
|
931
|
-
"url",
|
|
932
|
-
"email",
|
|
933
|
-
"phone",
|
|
934
|
-
"unsupported"
|
|
935
|
-
]);
|
|
936
|
-
var CEAL_NOTION_IDENTITY_GAP_REASONS = vocabulary([
|
|
937
|
-
"identity_unavailable",
|
|
938
|
-
"identity_stale",
|
|
939
|
-
"identity_ambiguous",
|
|
940
|
-
"identity_revoked",
|
|
941
|
-
"identity_unlinked"
|
|
942
|
-
]);
|
|
943
|
-
var CEAL_NOTION_STRING_VALUE_TYPES = vocabulary(["text", "select", "url", "email", "phone"]);
|
|
944
|
-
|
|
945
971
|
// packages/ceal-client-protocol/src/opaque-reference.ts
|
|
946
972
|
var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "block", "object"]);
|
|
947
973
|
var CEAL_OPAQUE_REF_KINDS = vocabulary([...CEAL_OPAQUE_HANDLE_KINDS, "result", "operation-receipt", "reply-receipt", "field", "notion-data-source"]);
|
|
@@ -974,6 +1000,7 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
|
974
1000
|
"resource_modified"
|
|
975
1001
|
]);
|
|
976
1002
|
var CEAL_ACTIVITY_CONNECTOR_KINDS = vocabulary(["slack", "github", "google-calendar", "google-drive", "notion"]);
|
|
1003
|
+
var CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE = "unsupported_event_kind";
|
|
977
1004
|
var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
978
1005
|
"provider_page_budget",
|
|
979
1006
|
"call_deadline",
|
|
@@ -981,7 +1008,7 @@ var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
|
981
1008
|
"provider_cursor_unavailable",
|
|
982
1009
|
"malformed_provider_page",
|
|
983
1010
|
"visibility_incomplete",
|
|
984
|
-
|
|
1011
|
+
CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE,
|
|
985
1012
|
"direct_messages_excluded"
|
|
986
1013
|
]);
|
|
987
1014
|
var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
@@ -1046,6 +1073,8 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
1046
1073
|
modified_at: { required: false, timestamp: true },
|
|
1047
1074
|
modified_at_local: { required: false, timestamp: true },
|
|
1048
1075
|
viewed_by_me_at: { required: false, timestamp: true },
|
|
1076
|
+
own_ceal_write: { required: false, timestamp: false },
|
|
1077
|
+
owned_by_me: { required: false, timestamp: false },
|
|
1049
1078
|
visibility: { required: false, timestamp: false },
|
|
1050
1079
|
start: { required: false, timestamp: false },
|
|
1051
1080
|
end: { required: false, timestamp: false },
|
|
@@ -1079,6 +1108,15 @@ function cealExcludedTargetCoverageJsonSchema() {
|
|
|
1079
1108
|
};
|
|
1080
1109
|
}
|
|
1081
1110
|
|
|
1111
|
+
// packages/ceal-client-protocol/src/gateway-write-identity.ts
|
|
1112
|
+
var CEAL_GATEWAY_WRITE_IDENTITY_ROLES = Object.freeze(["replay_identity", "lookup_handle", "collision_evidence"]);
|
|
1113
|
+
var CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN = /^gateway-write-request:[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/u;
|
|
1114
|
+
var CEAL_GATEWAY_WRITE_IDENTITY_FIELDS = Object.freeze({
|
|
1115
|
+
replay_identity: "idempotency_claim_sha256",
|
|
1116
|
+
lookup_handle: "write_request_sha256",
|
|
1117
|
+
collision_evidence: "normalized_mutation_sha256"
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1082
1120
|
// packages/ceal-client-protocol/src/write-safety-finding.ts
|
|
1083
1121
|
var CEAL_WRITE_SAFETY_FINDING_STATUSES = vocabulary(["warning", "unchecked"]);
|
|
1084
1122
|
var CEAL_WRITE_SAFETY_AUDIENCES = vocabulary(["workspace", "person", "members", "unknown"]);
|
|
@@ -1106,7 +1144,11 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
|
1106
1144
|
"refresh_revoked",
|
|
1107
1145
|
"authority_replaced",
|
|
1108
1146
|
"refresh_recovery_unavailable",
|
|
1109
|
-
"refresh_recovery_expired"
|
|
1147
|
+
"refresh_recovery_expired",
|
|
1148
|
+
// The serving gate answers this while the Gateway is still starting and the
|
|
1149
|
+
// refresh was never attempted; the same attempt stays retryable. It is never
|
|
1150
|
+
// stored by the session owner, only answered at the serving edge.
|
|
1151
|
+
"gateway_starting"
|
|
1110
1152
|
]);
|
|
1111
1153
|
|
|
1112
1154
|
// packages/ceal-client-protocol/src/request-validation-primitives.ts
|
|
@@ -1139,7 +1181,7 @@ var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
|
|
|
1139
1181
|
var CEAL_REQUEST_KEYS = Object.freeze(["schema_version", "ceal_request_ref", "created_at", "authority", "prompt_ref", "host_session_ref", "host_turn_ref", "host_source_ref"]);
|
|
1140
1182
|
var CEAL_REQUEST_JSON_SCHEMA = {
|
|
1141
1183
|
type: "object",
|
|
1142
|
-
maxProperties:
|
|
1184
|
+
maxProperties: 11,
|
|
1143
1185
|
additionalProperties: false,
|
|
1144
1186
|
properties: {
|
|
1145
1187
|
schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
|
|
@@ -1163,7 +1205,8 @@ var CEAL_REQUEST_JSON_SCHEMA = {
|
|
|
1163
1205
|
host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1164
1206
|
host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1165
1207
|
host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1166
|
-
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." }
|
|
1208
|
+
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
|
|
1209
|
+
device_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source, description: "Device that submitted this Request through its client session; absent for Host turns and older records." }
|
|
1167
1210
|
},
|
|
1168
1211
|
required: CEAL_REQUEST_KEYS
|
|
1169
1212
|
};
|
|
@@ -1222,6 +1265,7 @@ var REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES = {
|
|
|
1222
1265
|
retryable: { type: "boolean" },
|
|
1223
1266
|
source: { type: "string", enum: ["provider", "gateway"] },
|
|
1224
1267
|
retry_after_ms: { type: "integer", minimum: 0, maximum: CEAL_RETRY_AFTER_MAX_MS },
|
|
1268
|
+
write_request_ref: { type: "string", pattern: CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN.source, maxLength: 128 },
|
|
1225
1269
|
excluded_targets: cealExcludedTargetCoverageJsonSchema(),
|
|
1226
1270
|
repaired_input: {},
|
|
1227
1271
|
unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
|
|
@@ -1286,6 +1330,43 @@ function cealHostObservationRef(producer, digest2) {
|
|
|
1286
1330
|
if (!HOST_OBSERVATION_PRODUCER.test(producer) || !HOST_OBSERVATION_DIGEST.test(digest2)) invalid("Ceal Host observation reference parts are invalid.");
|
|
1287
1331
|
return `${CEAL_HOST_OBSERVATION_REF_PREFIX}${producer}-${digest2}`;
|
|
1288
1332
|
}
|
|
1333
|
+
var HOST_TOKEN_OBSERVATION_BREAKDOWN_KEYS = vocabulary(["cached_input", "cache_write_input", "reasoning_output"]);
|
|
1334
|
+
function createHostTokenObservationValue(counts, source) {
|
|
1335
|
+
const core = validTokenCore(counts);
|
|
1336
|
+
if (typeof source !== "string" || source.length === 0 || source.length > 256) invalid("Ceal Host token observation source is invalid.");
|
|
1337
|
+
const value = { status: "available", unit: "tokens", input: core.input, output: core.output, total: core.total, source };
|
|
1338
|
+
for (const breakdown of validTokenBreakdowns(counts)) value[breakdown.key] = breakdown.tokens;
|
|
1339
|
+
assertHostObservationValue(value, "token");
|
|
1340
|
+
return value;
|
|
1341
|
+
}
|
|
1342
|
+
function validTokenCore(counts) {
|
|
1343
|
+
if (!isOperationRecord(counts) || !safeCount(counts.input) || !safeCount(counts.output) || !safeCount(counts.total)) invalid("Ceal Host token observation counts are invalid.");
|
|
1344
|
+
if (counts.total !== counts.input + counts.output) invalid("Ceal Host token observation counts are invalid.");
|
|
1345
|
+
return { input: counts.input, output: counts.output, total: counts.total };
|
|
1346
|
+
}
|
|
1347
|
+
function validTokenBreakdowns(counts) {
|
|
1348
|
+
const breakdowns = [
|
|
1349
|
+
{ key: "cached_input", tokens: counts.cached_input },
|
|
1350
|
+
{ key: "cache_write_input", tokens: counts.cache_write_input },
|
|
1351
|
+
{ key: "reasoning_output", tokens: counts.reasoning_output }
|
|
1352
|
+
];
|
|
1353
|
+
const stated = breakdowns.filter((breakdown) => breakdown.tokens !== void 0).map((breakdown) => ({ key: breakdown.key, tokens: Number(breakdown.tokens) }));
|
|
1354
|
+
for (const breakdown of stated) {
|
|
1355
|
+
if (!safeCount(breakdown.tokens)) invalid("Ceal Host token observation counts are invalid.");
|
|
1356
|
+
}
|
|
1357
|
+
const cached = counts.cached_input ?? 0;
|
|
1358
|
+
const created = counts.cache_write_input ?? 0;
|
|
1359
|
+
const reasoning = counts.reasoning_output ?? 0;
|
|
1360
|
+
if (cached + created > counts.input || reasoning > counts.output) invalid("Ceal Host token observation counts are invalid.");
|
|
1361
|
+
return stated;
|
|
1362
|
+
}
|
|
1363
|
+
function hostObservationNotRecorded(reason) {
|
|
1364
|
+
if (typeof reason !== "string" || reason.length === 0 || reason.length > 256) invalid("Ceal Host observation gap reason is invalid.");
|
|
1365
|
+
return { status: "not_recorded", reason };
|
|
1366
|
+
}
|
|
1367
|
+
function safeCount(value) {
|
|
1368
|
+
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
1369
|
+
}
|
|
1289
1370
|
function validateCealHostObservationDraft(value) {
|
|
1290
1371
|
const record3 = operationRecord(value, "Ceal Host observation draft is invalid.");
|
|
1291
1372
|
requirePrefixedRef(record3.observation_ref, CEAL_HOST_OBSERVATION_REF_PREFIX, "Ceal Host observation reference is invalid.");
|
|
@@ -1437,12 +1518,83 @@ var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
|
|
|
1437
1518
|
"host_input_mismatch",
|
|
1438
1519
|
"host_prompt_mismatch"
|
|
1439
1520
|
]);
|
|
1521
|
+
var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
|
|
1522
|
+
"invalid_arguments",
|
|
1523
|
+
"options_invalid",
|
|
1524
|
+
"session_unavailable",
|
|
1525
|
+
"context_unavailable",
|
|
1526
|
+
"context_replaced",
|
|
1527
|
+
"no_binding",
|
|
1528
|
+
"context_stale",
|
|
1529
|
+
"context_expired",
|
|
1530
|
+
"host_source_refused",
|
|
1531
|
+
"host_credential_refused",
|
|
1532
|
+
"gateway_unavailable",
|
|
1533
|
+
"gateway_rate_limited",
|
|
1534
|
+
"gateway_refused",
|
|
1535
|
+
"gateway_response_invalid",
|
|
1536
|
+
"producer_frame_invalid",
|
|
1537
|
+
"relay_permission_denied",
|
|
1538
|
+
"relay_unavailable",
|
|
1539
|
+
"transient_retryable",
|
|
1540
|
+
"mint_failed",
|
|
1541
|
+
"relay_failed",
|
|
1542
|
+
"host_refusal_invalid"
|
|
1543
|
+
]);
|
|
1544
|
+
var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
1545
|
+
invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
|
|
1546
|
+
options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
|
|
1547
|
+
session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
1548
|
+
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn; run `ceal-host install` to repair the Host hook registration, then retry this command on a fresh Host turn" }),
|
|
1549
|
+
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
1550
|
+
no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
|
|
1551
|
+
context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
1552
|
+
context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "refresh_host_invocation_context" }),
|
|
1553
|
+
host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
|
|
1554
|
+
host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
|
|
1555
|
+
gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
|
|
1556
|
+
gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
|
|
1557
|
+
gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
|
|
1558
|
+
gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
|
|
1559
|
+
producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin", remedy: "ask an administrator to inspect and repair the producer frame, then start a fresh Host turn" }),
|
|
1560
|
+
relay_permission_denied: Object.freeze({ classification: "host_relay_permission_denied", next_action: "rerun_outside_agent_sandbox", remedy: "an agent sandbox denied the Host Relay socket; run `ceal` as the only command, with no pipe, redirect, or `&&`, so the Host allow rule runs it outside the sandbox" }),
|
|
1561
|
+
relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
|
|
1562
|
+
transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
|
|
1563
|
+
mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
|
|
1564
|
+
relay_failed: Object.freeze({ classification: "host_relay_failed", next_action: "answer_with_gap" }),
|
|
1565
|
+
host_refusal_invalid: Object.freeze({ classification: "host_refusal_invalid", next_action: "answer_with_gap" })
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
// packages/ceal-client-protocol/src/host-service-diagnostic.ts
|
|
1569
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
|
|
1570
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
1571
|
+
"hook_invoked",
|
|
1572
|
+
"registration_written",
|
|
1573
|
+
"hook_failed",
|
|
1574
|
+
"watcher_turn_observed",
|
|
1575
|
+
"current_prompt_registered",
|
|
1576
|
+
"current_prompt_registration_failed",
|
|
1577
|
+
"activation_recorded",
|
|
1578
|
+
"activation_failed",
|
|
1579
|
+
"watcher_scan_failed",
|
|
1580
|
+
"activation_wait_skipped",
|
|
1581
|
+
"activation_wait_engaged",
|
|
1582
|
+
"activation_wait_completed",
|
|
1583
|
+
"activation_wait_exhausted"
|
|
1584
|
+
]);
|
|
1585
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_FAILURE_KINDS = vocabulary([
|
|
1586
|
+
"malformed_line",
|
|
1587
|
+
"other",
|
|
1588
|
+
"payload_rejected",
|
|
1589
|
+
"read_error",
|
|
1590
|
+
"size_budget",
|
|
1591
|
+
"symlink"
|
|
1592
|
+
]);
|
|
1440
1593
|
|
|
1441
1594
|
// packages/ceal-client-protocol/src/client-result-materialization.ts
|
|
1442
1595
|
var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "unsupported", "download_failed", "permission_denied"]);
|
|
1443
|
-
var
|
|
1444
|
-
var
|
|
1445
|
-
var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "local_result_root", "continue_argv", "model_disclosure"]);
|
|
1596
|
+
var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
|
|
1597
|
+
var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "items_path", "row_count", "result_complete", "coverage", "local_result_root", "model_disclosure", "continue_argv", "how_to_read"]);
|
|
1446
1598
|
|
|
1447
1599
|
// packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
|
|
1448
1600
|
var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -1472,14 +1624,6 @@ var CEAL_GATEWAY_ANNOUNCEMENT_POLICY_NON_CLAIMS = vocabulary([
|
|
|
1472
1624
|
"target_specific_scope_not_declared"
|
|
1473
1625
|
]);
|
|
1474
1626
|
|
|
1475
|
-
// packages/ceal-client-protocol/src/gateway-write-identity.ts
|
|
1476
|
-
var CEAL_GATEWAY_WRITE_IDENTITY_ROLES = Object.freeze(["replay_identity", "lookup_handle", "collision_evidence"]);
|
|
1477
|
-
var CEAL_GATEWAY_WRITE_IDENTITY_FIELDS = Object.freeze({
|
|
1478
|
-
replay_identity: "idempotency_claim_sha256",
|
|
1479
|
-
lookup_handle: "write_request_sha256",
|
|
1480
|
-
collision_evidence: "normalized_mutation_sha256"
|
|
1481
|
-
});
|
|
1482
|
-
|
|
1483
1627
|
// packages/ceal-client-protocol/src/gateway-scoped-identity-projection-validation.ts
|
|
1484
1628
|
var PROJECTION_REVISION_KEYS = Object.freeze(["graph_revision", "subject_key_revision", "projection_revision"]);
|
|
1485
1629
|
|
|
@@ -1492,6 +1636,18 @@ var CEAL_SUPPORTED_GATEWAY_PROTOCOL_RANGE = Object.freeze({
|
|
|
1492
1636
|
// packages/ceal-client-protocol/src/admin-projection-vocabulary.ts
|
|
1493
1637
|
var ADMIN_PROJECTION_STATES = vocabulary(["ready", "empty", "partial", "unknown"]);
|
|
1494
1638
|
var ADMIN_VIEW_KINDS = vocabulary(["request_detail", "resource_access_timeline", "weekly_usage"]);
|
|
1639
|
+
var ADMIN_RECORD_PROJECTION_KINDS = vocabulary([
|
|
1640
|
+
"request_list",
|
|
1641
|
+
"usage_series",
|
|
1642
|
+
"connector_health",
|
|
1643
|
+
"person_connections",
|
|
1644
|
+
"client_sessions",
|
|
1645
|
+
"knowledge_base",
|
|
1646
|
+
"change_history",
|
|
1647
|
+
"approval_queue",
|
|
1648
|
+
"access_group_list",
|
|
1649
|
+
"capability_catalogue"
|
|
1650
|
+
]);
|
|
1495
1651
|
var ADMIN_GAP_KINDS = vocabulary(["unknown", "unavailable", "partial"]);
|
|
1496
1652
|
var ADMIN_UNAVAILABLE_REASONS = vocabulary([
|
|
1497
1653
|
"not_recorded",
|
|
@@ -1515,7 +1671,11 @@ var ADMIN_SOURCE_OWNERS = vocabulary([
|
|
|
1515
1671
|
"person",
|
|
1516
1672
|
"resource",
|
|
1517
1673
|
"prompt",
|
|
1518
|
-
"usage"
|
|
1674
|
+
"usage",
|
|
1675
|
+
"connector",
|
|
1676
|
+
"session",
|
|
1677
|
+
"knowledge",
|
|
1678
|
+
"policy"
|
|
1519
1679
|
]);
|
|
1520
1680
|
var ADMIN_COMPLETENESS_STATES = vocabulary(["complete", "partial", "unknown"]);
|
|
1521
1681
|
var ADMIN_OPERATION_OUTCOMES = vocabulary(["completed", "failed", "denied", "cancelled", "unknown"]);
|
|
@@ -1552,11 +1712,7 @@ var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1"
|
|
|
1552
1712
|
var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
|
|
1553
1713
|
var CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION = "ceal.codex_transcript_cursor.v1";
|
|
1554
1714
|
var CODEX_DEFAULT_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
1555
|
-
var CODEX_HOOK_EVENTS = ["SessionStart"];
|
|
1556
|
-
var CODEX_TRANSCRIPT_CURSOR_REASONS = ["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"];
|
|
1557
|
-
function isCodexTranscriptCursorReason(value) {
|
|
1558
|
-
return typeof value === "string" && CODEX_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
|
|
1559
|
-
}
|
|
1715
|
+
var CODEX_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit"];
|
|
1560
1716
|
var CodexHostAdapterError = class extends Error {
|
|
1561
1717
|
name = "CodexHostAdapterError";
|
|
1562
1718
|
code;
|
|
@@ -1571,12 +1727,12 @@ function isCodexHookEvent(value) {
|
|
|
1571
1727
|
|
|
1572
1728
|
// host/codex/src/codex-decoder.ts
|
|
1573
1729
|
var SAFE_REF2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
1574
|
-
var MAX_PAYLOAD_BYTES =
|
|
1730
|
+
var MAX_PAYLOAD_BYTES = 512 * 1024;
|
|
1575
1731
|
var MAX_TEXT = 256;
|
|
1576
1732
|
function decodeCodexHookPayload(event, raw) {
|
|
1577
|
-
if (event !== "SessionStart") reject(`Codex hook event is not installed by this adapter (${refusalFact("event", event, "SessionStart")})`);
|
|
1578
1733
|
const input = parsePayload(raw);
|
|
1579
|
-
return decodeSession(input, event);
|
|
1734
|
+
if (event === "SessionStart") return decodeSession(input, event);
|
|
1735
|
+
return decodeUserPromptSubmit(input, event);
|
|
1580
1736
|
}
|
|
1581
1737
|
function observationRef(event, identity, value) {
|
|
1582
1738
|
return cealHostObservationRef("codex", shortDigest({ event, identity, value }));
|
|
@@ -1628,6 +1784,20 @@ function decodeSession(input, event) {
|
|
|
1628
1784
|
transcript_path: requiredText(input.transcript_path, "transcript_path", 4096)
|
|
1629
1785
|
};
|
|
1630
1786
|
}
|
|
1787
|
+
function decodeUserPromptSubmit(input, event) {
|
|
1788
|
+
exactKeys(input, ["cwd", "hook_event_name", "model", "permission_mode", "prompt", "session_id", "transcript_path", "turn_id"], []);
|
|
1789
|
+
validateHookEventName(input, event);
|
|
1790
|
+
const cwd = requiredText(input.cwd, "cwd", 4096);
|
|
1791
|
+
if (!isAbsolute(cwd) || cwd.includes("\0")) reject("Codex cwd is invalid");
|
|
1792
|
+
return {
|
|
1793
|
+
event,
|
|
1794
|
+
cwd: resolve(cwd),
|
|
1795
|
+
session_id: requiredText(input.session_id, "session_id", MAX_TEXT),
|
|
1796
|
+
transcript_path: requiredText(input.transcript_path, "transcript_path", 4096),
|
|
1797
|
+
turn_id: requiredText(input.turn_id, "turn_id", MAX_TEXT),
|
|
1798
|
+
prompt: requiredText(input.prompt, "prompt", MAX_PAYLOAD_BYTES)
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1631
1801
|
function parsePayload(raw) {
|
|
1632
1802
|
try {
|
|
1633
1803
|
const text = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
@@ -1670,9 +1840,9 @@ function reject(message) {
|
|
|
1670
1840
|
}
|
|
1671
1841
|
|
|
1672
1842
|
// host/codex/src/codex-outbox.ts
|
|
1673
|
-
import { O_NOFOLLOW, O_RDONLY } from "node:constants";
|
|
1674
|
-
import { lstat, mkdir, open, readdir, rename, unlink } from "node:fs/promises";
|
|
1675
|
-
import { dirname, isAbsolute as isAbsolute2, join as
|
|
1843
|
+
import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
|
|
1844
|
+
import { lstat as lstat2, mkdir as mkdir3, open as open3, readdir, rename as rename3, unlink as unlink2 } from "node:fs/promises";
|
|
1845
|
+
import { dirname, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
1676
1846
|
|
|
1677
1847
|
// host/invocation-context.ts
|
|
1678
1848
|
var HOST_INVOCATION_IDENTITY_KEYS = ["host_session_ref", "host_turn_ref", "host_source_ref"];
|
|
@@ -1686,17 +1856,32 @@ function hasHostInvocationIdentityKeys(value) {
|
|
|
1686
1856
|
function hasHostInvocationContextKeys(value, optional = []) {
|
|
1687
1857
|
return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
|
|
1688
1858
|
}
|
|
1859
|
+
async function readLatestSessionProjection(file, hostSourceRef, hostSessionRef, readProjection) {
|
|
1860
|
+
const context = await readProjection(file);
|
|
1861
|
+
if (context === null || context.identity.host_source_ref !== hostSourceRef || context.identity.host_session_ref !== hostSessionRef) return null;
|
|
1862
|
+
return context;
|
|
1863
|
+
}
|
|
1864
|
+
function isLiveSessionProjection(context, now) {
|
|
1865
|
+
return context !== null && Date.parse(context.expires_at) > now.getTime();
|
|
1866
|
+
}
|
|
1689
1867
|
function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor) {
|
|
1690
1868
|
const context = {
|
|
1691
1869
|
schema_version: descriptor.schema_version,
|
|
1692
1870
|
identity,
|
|
1693
1871
|
prompt_ref: promptRef,
|
|
1694
1872
|
expires_at: expiresAt,
|
|
1873
|
+
...descriptor.turn_generation_ref === void 0 ? {} : { turn_generation_ref: descriptor.turn_generation_ref },
|
|
1695
1874
|
...descriptor.working_directory === void 0 ? {} : { working_directory: descriptor.working_directory }
|
|
1696
1875
|
};
|
|
1697
1876
|
descriptor.validate(context);
|
|
1698
1877
|
return Object.freeze(context);
|
|
1699
1878
|
}
|
|
1879
|
+
function isHostInvocationTurnSelection(value) {
|
|
1880
|
+
return isRecord2(value) && Object.keys(value).length === 5 && value.schema_version === "ceal.host_invocation_turn_selection.v1" && ["host_source_ref", "host_session_ref", "prompt_ref", "turn_generation_ref"].every((key) => safeRef(value[key]));
|
|
1881
|
+
}
|
|
1882
|
+
function invocationTurnSelectionMatches(context, selection) {
|
|
1883
|
+
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;
|
|
1884
|
+
}
|
|
1700
1885
|
function exactKeys2(value, keys) {
|
|
1701
1886
|
const actual = Object.keys(value);
|
|
1702
1887
|
return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
|
|
@@ -1706,145 +1891,908 @@ function requiredKeys(value, required, optional) {
|
|
|
1706
1891
|
const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
|
|
1707
1892
|
return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
|
|
1708
1893
|
}
|
|
1894
|
+
function isRecord2(value) {
|
|
1895
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1896
|
+
}
|
|
1897
|
+
function safeRef(value) {
|
|
1898
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
1899
|
+
}
|
|
1709
1900
|
|
|
1710
|
-
// host/
|
|
1711
|
-
|
|
1712
|
-
|
|
1901
|
+
// host/relay/src/producer-frame.ts
|
|
1902
|
+
var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
|
|
1903
|
+
var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
|
|
1904
|
+
var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
1905
|
+
var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
1906
|
+
var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
|
|
1907
|
+
var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "user_input", "turn_terminal", "producer_status"];
|
|
1908
|
+
var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
|
|
1909
|
+
function isHostProducerFrameKind(value) {
|
|
1910
|
+
return typeof value === "string" && FRAME_KINDS.has(value);
|
|
1713
1911
|
}
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1912
|
+
var HOST_PRODUCER_FRAME_ERROR_CODES = [
|
|
1913
|
+
"invalid_frame_encoding",
|
|
1914
|
+
"invalid_frame_length",
|
|
1915
|
+
"frame_truncated",
|
|
1916
|
+
"unsupported_frame_schema",
|
|
1917
|
+
"invalid_frame_shape",
|
|
1918
|
+
"invalid_producer_identity",
|
|
1919
|
+
"frame_too_large"
|
|
1920
|
+
];
|
|
1921
|
+
var HostProducerFrameError = class extends Error {
|
|
1922
|
+
code;
|
|
1923
|
+
constructor(code, message = code) {
|
|
1924
|
+
super(message);
|
|
1925
|
+
this.code = code;
|
|
1717
1926
|
}
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
request_ref: binding.request_ref,
|
|
1728
|
-
bound_at: binding.bound_at,
|
|
1729
|
-
expires_at: binding.expires_at
|
|
1730
|
-
};
|
|
1731
|
-
validateCodexHostInvocationContext(context);
|
|
1732
|
-
return Object.freeze(context);
|
|
1927
|
+
};
|
|
1928
|
+
function encodeHostProducerFrame(frame) {
|
|
1929
|
+
validateFrame(frame);
|
|
1930
|
+
const json = Buffer.from(JSON.stringify(frame), "utf8");
|
|
1931
|
+
if (json.byteLength > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("frame_bytes", json.byteLength, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
|
|
1932
|
+
const result = Buffer.allocUnsafe(json.byteLength + HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1933
|
+
result.writeUInt32BE(json.byteLength, 0);
|
|
1934
|
+
json.copy(result, HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1935
|
+
return result;
|
|
1733
1936
|
}
|
|
1734
|
-
function
|
|
1735
|
-
if (
|
|
1736
|
-
|
|
1937
|
+
function decodeHostProducerFrame(input) {
|
|
1938
|
+
if (input.byteLength < HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError("frame_truncated", `frame_truncated: ${refusalFact("frame_bytes", input.byteLength, `at least ${HOST_PRODUCER_FRAME_LENGTH_BYTES}`)}`);
|
|
1939
|
+
const length = input.readUInt32BE(0);
|
|
1940
|
+
if (length > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("declared_bytes", length, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
|
|
1941
|
+
if (length === 0) throw new HostProducerFrameError("invalid_frame_length");
|
|
1942
|
+
if (input.byteLength !== length + HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError(input.byteLength < length + HOST_PRODUCER_FRAME_LENGTH_BYTES ? "frame_truncated" : "invalid_frame_length");
|
|
1943
|
+
const bytes = input.subarray(HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1944
|
+
const text = new TextDecoder("utf-8", { fatal: true });
|
|
1945
|
+
let value;
|
|
1946
|
+
try {
|
|
1947
|
+
value = JSON.parse(text.decode(bytes));
|
|
1948
|
+
} catch (error) {
|
|
1949
|
+
if (error instanceof TypeError) throw new HostProducerFrameError("invalid_frame_encoding");
|
|
1950
|
+
throw new HostProducerFrameError("frame_truncated");
|
|
1737
1951
|
}
|
|
1952
|
+
validateFrame(value);
|
|
1953
|
+
return value;
|
|
1738
1954
|
}
|
|
1739
|
-
function
|
|
1740
|
-
return
|
|
1955
|
+
function isHostSafeRef(value) {
|
|
1956
|
+
return safeRef2(value);
|
|
1741
1957
|
}
|
|
1742
|
-
function
|
|
1743
|
-
if (!
|
|
1958
|
+
function validateFrame(value) {
|
|
1959
|
+
if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1960
|
+
if (value.schema_version !== HOST_PRODUCER_FRAME_SCHEMA) throw new HostProducerFrameError("unsupported_frame_schema", `unsupported_frame_schema: ${refusalFact("schema_version", value.schema_version, HOST_PRODUCER_FRAME_SCHEMA)}`);
|
|
1961
|
+
if (!exactKeys3(value, ["schema_version", "producer_id", "producer_installation_ref", "producer_epoch", "sequence", "frame_kind", "occurred_at", "payload"])) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1962
|
+
if (!PRODUCERS.has(String(value.producer_id)) || !safeRef2(value.producer_installation_ref)) throw new HostProducerFrameError("invalid_producer_identity");
|
|
1963
|
+
if (!positive(value.producer_epoch) || !positive(value.sequence) || !hostProducerFrameKind(value.frame_kind) || !timestamp(value.occurred_at) || !record(value.payload)) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1964
|
+
validatePayload(value.frame_kind, value.payload);
|
|
1744
1965
|
}
|
|
1745
|
-
function
|
|
1746
|
-
const
|
|
1747
|
-
|
|
1748
|
-
if (!timestamp(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
1749
|
-
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}`)}).`);
|
|
1966
|
+
function validatePayload(kind, value) {
|
|
1967
|
+
const valid = kind === "session_started" ? validSession(value) : kind === "turn_started" ? validTurn(value) : kind === "user_input" ? validInput(value) : kind === "turn_terminal" ? validTerminal(value) : validStatus(value);
|
|
1968
|
+
if (!valid) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1750
1969
|
}
|
|
1751
|
-
function
|
|
1752
|
-
return value
|
|
1970
|
+
function validSession(value) {
|
|
1971
|
+
return exactOptional(value, ["local_session_ref", "working_directory_ref"], ["model", "host_version", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.working_directory_ref) && optionalText(value.model, 256) && optionalText(value.host_version, 256) && validObservations(value.observations);
|
|
1753
1972
|
}
|
|
1754
|
-
function
|
|
1755
|
-
return value
|
|
1973
|
+
function validTurn(value) {
|
|
1974
|
+
return exactOptional(value, ["local_session_ref", "local_turn_ref"], ["model", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && optionalText(value.model, 256) && validObservations(value.observations);
|
|
1756
1975
|
}
|
|
1757
|
-
function
|
|
1758
|
-
return
|
|
1976
|
+
function validInput(value) {
|
|
1977
|
+
return exactOptional(value, ["local_session_ref", "local_turn_ref", "local_input_ref", "input_kind", "origin_input", "observations"], ["local_turn_aliases", "context_expires_at"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && safeRef2(value.local_input_ref) && validInputObservations(value.observations) && isCealHostInputKind(value.input_kind) && typeof value.origin_input === "boolean" && validInputOptionals(value);
|
|
1759
1978
|
}
|
|
1760
|
-
function
|
|
1761
|
-
return
|
|
1979
|
+
function validInputObservations(value) {
|
|
1980
|
+
return validObservations(value) && Array.isArray(value) && value.filter((item) => record(item) && item.observation_kind === "prompt").length === 1;
|
|
1762
1981
|
}
|
|
1763
|
-
function
|
|
1764
|
-
return value
|
|
1982
|
+
function validInputOptionals(value) {
|
|
1983
|
+
return optionalRefs(value.local_turn_aliases) && (value.context_expires_at === void 0 || timestamp(value.context_expires_at));
|
|
1765
1984
|
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
async prepare() {
|
|
1789
|
-
for (const path of [this.directory, this.entriesDirectory, this.bindingsDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
|
|
1790
|
-
await ensurePrivateOwnerDirectory(path);
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
async put(entry) {
|
|
1794
|
-
validateEnvelope(entry);
|
|
1795
|
-
await this.prepare();
|
|
1796
|
-
await writeAtomicJson(this.entryPath(entry.submission_ref), entry);
|
|
1797
|
-
}
|
|
1798
|
-
async list() {
|
|
1799
|
-
await this.prepare();
|
|
1800
|
-
const entries = [];
|
|
1801
|
-
let malformed = 0;
|
|
1802
|
-
for (const name of await readdir(this.entriesDirectory)) {
|
|
1803
|
-
if (!name.endsWith(".json")) continue;
|
|
1804
|
-
try {
|
|
1805
|
-
const value = JSON.parse(await readPrivateFile(join2(this.entriesDirectory, name)));
|
|
1806
|
-
validateEnvelope(value);
|
|
1807
|
-
entries.push(value);
|
|
1808
|
-
} catch {
|
|
1809
|
-
malformed += 1;
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
entries.sort((left, right) => left.created_at.localeCompare(right.created_at));
|
|
1813
|
-
return { entries, malformed };
|
|
1814
|
-
}
|
|
1815
|
-
async remove(entry) {
|
|
1816
|
-
validateEnvelope(entry);
|
|
1985
|
+
function validTerminal(value) {
|
|
1986
|
+
return exactOptional(value, ["local_session_ref", "local_turn_ref", "terminal_state"], ["abort_reason", "duration_ms", "time_to_first_token_ms", "usage", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && validTerminalDisposition(value.terminal_state, value.abort_reason) && optionalNonNegative(value.duration_ms) && optionalNonNegative(value.time_to_first_token_ms) && (value.usage === void 0 || jsonValue(value.usage)) && validObservations(value.observations);
|
|
1987
|
+
}
|
|
1988
|
+
function validTerminalDisposition(state, reason) {
|
|
1989
|
+
return (state === "completed" || state === "aborted") && (reason === void 0 || state === "aborted" && safeRef2(reason));
|
|
1990
|
+
}
|
|
1991
|
+
function validStatus(value) {
|
|
1992
|
+
return exactOptional(value, ["status", "reason_code"], ["detail"]) && ["ready", "degraded", "stopping"].includes(String(value.status)) && safeRef2(value.reason_code) && optionalText(value.detail, 4096);
|
|
1993
|
+
}
|
|
1994
|
+
function optionalText(value, maxLength) {
|
|
1995
|
+
return value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maxLength;
|
|
1996
|
+
}
|
|
1997
|
+
function optionalNonNegative(value) {
|
|
1998
|
+
return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1999
|
+
}
|
|
2000
|
+
function optionalRefs(value) {
|
|
2001
|
+
return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef2);
|
|
2002
|
+
}
|
|
2003
|
+
function validObservations(value) {
|
|
2004
|
+
if (value === void 0) return true;
|
|
2005
|
+
if (!Array.isArray(value) || value.length > 32) return false;
|
|
2006
|
+
return value.every((item) => {
|
|
1817
2007
|
try {
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
2008
|
+
validateCealHostObservationDraft(item);
|
|
2009
|
+
return true;
|
|
2010
|
+
} catch {
|
|
2011
|
+
return false;
|
|
1821
2012
|
}
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
function jsonValue(value, depth = 0) {
|
|
2016
|
+
if (depth > 16) return false;
|
|
2017
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
2018
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
2019
|
+
if (Array.isArray(value)) return value.length <= 256 && value.every((item) => jsonValue(item, depth + 1));
|
|
2020
|
+
return record(value) && Object.keys(value).length <= 256 && Object.entries(value).every(([key, item]) => key.length > 0 && key.length <= 128 && jsonValue(item, depth + 1));
|
|
2021
|
+
}
|
|
2022
|
+
function record(value) {
|
|
2023
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2024
|
+
}
|
|
2025
|
+
function safeRef2(value) {
|
|
2026
|
+
return typeof value === "string" && SAFE_REF3.test(value);
|
|
2027
|
+
}
|
|
2028
|
+
function positive(value) {
|
|
2029
|
+
return Number.isSafeInteger(value) && Number(value) > 0;
|
|
2030
|
+
}
|
|
2031
|
+
function timestamp(value) {
|
|
2032
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
2033
|
+
}
|
|
2034
|
+
function hostProducerFrameKind(value) {
|
|
2035
|
+
return isHostProducerFrameKind(value);
|
|
2036
|
+
}
|
|
2037
|
+
function exactKeys3(value, keys) {
|
|
2038
|
+
return JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
|
|
2039
|
+
}
|
|
2040
|
+
function exactOptional(value, required, optional) {
|
|
2041
|
+
const keys = Object.keys(value);
|
|
2042
|
+
return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
// host/relay/src/host-producer-core.ts
|
|
2046
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2047
|
+
import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
|
|
2048
|
+
import { mkdir as mkdir2, open as open2, readFile, rename as rename2, writeFile } from "node:fs/promises";
|
|
2049
|
+
import { join as join2 } from "node:path";
|
|
2050
|
+
|
|
2051
|
+
// host/relay/src/relay-ipc.ts
|
|
2052
|
+
import { createConnection, createServer } from "node:net";
|
|
2053
|
+
|
|
2054
|
+
// host/relay/src/durable-atomic-replacement.ts
|
|
2055
|
+
import { O_RDONLY } from "node:constants";
|
|
2056
|
+
import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
2057
|
+
var DEFAULT_PORT = {
|
|
2058
|
+
platform: process.platform,
|
|
2059
|
+
mkdir: (path, options) => mkdir(path, options),
|
|
2060
|
+
lstat: (path) => lstat(path),
|
|
2061
|
+
open_file: (path) => open(path, "wx", 384),
|
|
2062
|
+
rename,
|
|
2063
|
+
unlink,
|
|
2064
|
+
open_directory: (path) => open(path, O_RDONLY),
|
|
2065
|
+
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
2066
|
+
};
|
|
2067
|
+
|
|
2068
|
+
// host/relay/src/relay-request-binding-store.ts
|
|
2069
|
+
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
2070
|
+
|
|
2071
|
+
// host/relay/src/current-prompt-custody.ts
|
|
2072
|
+
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
2073
|
+
|
|
2074
|
+
// host/relay/src/relay-ipc.ts
|
|
2075
|
+
var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
2076
|
+
"schema_version",
|
|
2077
|
+
"producer_id",
|
|
2078
|
+
"producer_installation_ref",
|
|
2079
|
+
"local_session_ref",
|
|
2080
|
+
"local_turn_ref",
|
|
2081
|
+
"local_turn_aliases",
|
|
2082
|
+
"prompt_ref",
|
|
2083
|
+
"request_ref",
|
|
2084
|
+
"bound_at",
|
|
2085
|
+
"host_source_ref",
|
|
2086
|
+
"host_session_ref",
|
|
2087
|
+
"host_turn_ref",
|
|
2088
|
+
"host_input_ref"
|
|
2089
|
+
]);
|
|
2090
|
+
var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
|
|
2091
|
+
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
2092
|
+
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
2093
|
+
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
2094
|
+
var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
|
|
2095
|
+
var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
2096
|
+
"context_stale",
|
|
2097
|
+
"context_expired",
|
|
2098
|
+
"host_source_refused",
|
|
2099
|
+
"host_credential_refused",
|
|
2100
|
+
"gateway_unavailable",
|
|
2101
|
+
"gateway_refused",
|
|
2102
|
+
"gateway_response_invalid",
|
|
2103
|
+
"gateway_rate_limited",
|
|
2104
|
+
"producer_frame_invalid",
|
|
2105
|
+
"deadline",
|
|
2106
|
+
"cancelled",
|
|
2107
|
+
"mint_failed",
|
|
2108
|
+
"relay_failed",
|
|
2109
|
+
"request_authority_unavailable",
|
|
2110
|
+
"request_missing",
|
|
2111
|
+
"authority_mismatch",
|
|
2112
|
+
"host_source_mismatch",
|
|
2113
|
+
"host_session_mismatch",
|
|
2114
|
+
"host_turn_mismatch",
|
|
2115
|
+
"host_input_mismatch",
|
|
2116
|
+
"host_prompt_mismatch"
|
|
2117
|
+
]);
|
|
2118
|
+
var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
|
|
2119
|
+
"context_stale",
|
|
2120
|
+
"context_expired",
|
|
2121
|
+
"host_source_refused",
|
|
2122
|
+
"host_credential_refused",
|
|
2123
|
+
"gateway_unavailable",
|
|
2124
|
+
"gateway_rate_limited",
|
|
2125
|
+
"gateway_refused",
|
|
2126
|
+
"gateway_response_invalid",
|
|
2127
|
+
"producer_frame_invalid"
|
|
2128
|
+
]);
|
|
2129
|
+
var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
|
|
2130
|
+
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
2131
|
+
"permission_denied",
|
|
2132
|
+
"socket_missing",
|
|
2133
|
+
"connection_refused",
|
|
2134
|
+
"closed_early",
|
|
2135
|
+
"response_invalid",
|
|
2136
|
+
"timed_out",
|
|
2137
|
+
"cancelled",
|
|
2138
|
+
"unavailable"
|
|
2139
|
+
]);
|
|
2140
|
+
var HostRelayIpcError = class extends Error {
|
|
2141
|
+
code;
|
|
2142
|
+
/** Set only when the client-side socket exchange itself failed. */
|
|
2143
|
+
failure;
|
|
2144
|
+
constructor(code, message = code, failure) {
|
|
2145
|
+
super(message);
|
|
2146
|
+
this.code = code;
|
|
2147
|
+
this.failure = failure;
|
|
2148
|
+
}
|
|
2149
|
+
};
|
|
2150
|
+
function hostRelayExchangeFailure(error) {
|
|
2151
|
+
return error instanceof HostRelayIpcError ? error.failure : void 0;
|
|
2152
|
+
}
|
|
2153
|
+
function socketErrorFailure(error) {
|
|
2154
|
+
switch (errorCode(error)) {
|
|
2155
|
+
case "EACCES":
|
|
2156
|
+
case "EPERM":
|
|
2157
|
+
return "permission_denied";
|
|
2158
|
+
case "ENOENT":
|
|
2159
|
+
return "socket_missing";
|
|
2160
|
+
case "ECONNREFUSED":
|
|
2161
|
+
return "connection_refused";
|
|
2162
|
+
default:
|
|
2163
|
+
return "unavailable";
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
var HostRelayIpcAdmissionPort = class {
|
|
2167
|
+
endpoint;
|
|
2168
|
+
timeout_ms;
|
|
2169
|
+
constructor(endpoint, timeoutMs = 5e3) {
|
|
2170
|
+
this.endpoint = endpoint;
|
|
2171
|
+
this.timeout_ms = timeoutMs;
|
|
2172
|
+
}
|
|
2173
|
+
async admit(frame) {
|
|
2174
|
+
const response2 = await submitHostProducerFrame(this.endpoint, frame, this.timeout_ms);
|
|
2175
|
+
if (!relayAdmissionCode(response2.code)) throw new HostRelayIpcError(response2.code);
|
|
2176
|
+
return { code: response2.code, admitted_through_sequence: response2.admitted_through_sequence, delivered_through_sequence: response2.delivered_through_sequence };
|
|
2177
|
+
}
|
|
2178
|
+
};
|
|
2179
|
+
var HostRelayCurrentPromptPort = class {
|
|
2180
|
+
endpoint;
|
|
2181
|
+
timeout_ms;
|
|
2182
|
+
constructor(endpoint, timeoutMs = 5e3) {
|
|
2183
|
+
this.endpoint = endpoint;
|
|
2184
|
+
this.timeout_ms = timeoutMs;
|
|
2185
|
+
}
|
|
2186
|
+
async register(input) {
|
|
2187
|
+
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
|
|
2188
|
+
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
2189
|
+
if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
|
|
2193
|
+
return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
|
|
2194
|
+
}
|
|
2195
|
+
function exchange(endpoint, encoded, timeoutMs, decode, signal) {
|
|
2196
|
+
return new Promise((resolve3, reject3) => {
|
|
2197
|
+
const socket = createConnection(endpoint);
|
|
2198
|
+
let buffer = Buffer.alloc(0);
|
|
2199
|
+
let settled = false;
|
|
2200
|
+
const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
|
|
2201
|
+
const finish = (error, response2) => {
|
|
2202
|
+
if (settled) return;
|
|
2203
|
+
settled = true;
|
|
2204
|
+
signal?.removeEventListener("abort", aborted);
|
|
2205
|
+
socket.destroy();
|
|
2206
|
+
if (error !== null) reject3(error);
|
|
2207
|
+
else if (response2 !== void 0) resolve3(response2);
|
|
2208
|
+
};
|
|
2209
|
+
if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
|
|
2210
|
+
if (signal?.aborted === true) {
|
|
2211
|
+
aborted();
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
2215
|
+
socket.once("error", (error) => {
|
|
2216
|
+
const failure = socketErrorFailure(error);
|
|
2217
|
+
finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure}.`, failure));
|
|
2218
|
+
});
|
|
2219
|
+
socket.once("connect", () => socket.write(encoded));
|
|
2220
|
+
socket.on("data", (chunk) => {
|
|
2221
|
+
try {
|
|
2222
|
+
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
|
2223
|
+
const decoded = decode(buffer);
|
|
2224
|
+
if (decoded !== null) finish(null, decoded);
|
|
2225
|
+
} catch (error) {
|
|
2226
|
+
finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
socket.once("end", () => {
|
|
2230
|
+
if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
|
|
2231
|
+
});
|
|
2232
|
+
});
|
|
2233
|
+
}
|
|
2234
|
+
function decodeHostRelayIpcResponse(input) {
|
|
2235
|
+
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response is truncated: ${refusalFact("response_bytes", input.byteLength, `at least ${HOST_RELAY_IPC_LENGTH_BYTES}`)}.`);
|
|
2236
|
+
const length = input.readUInt32BE(0);
|
|
2237
|
+
if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES || input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2238
|
+
let value;
|
|
2239
|
+
try {
|
|
2240
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
|
|
2241
|
+
} catch {
|
|
2242
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response is invalid.");
|
|
2243
|
+
}
|
|
2244
|
+
if (!validResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response shape is invalid.");
|
|
2245
|
+
return value;
|
|
2246
|
+
}
|
|
2247
|
+
function relayAdmissionCode(value) {
|
|
2248
|
+
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";
|
|
2249
|
+
}
|
|
2250
|
+
function validResponse(value) {
|
|
2251
|
+
return record2(value) && value.schema_version === HOST_RELAY_IPC_RESPONSE_SCHEMA && hostRelayIpcCode(value.code) && nonnegative(value.admitted_through_sequence) && nonnegative(value.delivered_through_sequence) && Object.keys(value).length === 4;
|
|
2252
|
+
}
|
|
2253
|
+
function hostRelayIpcCode(value) {
|
|
2254
|
+
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);
|
|
2255
|
+
}
|
|
2256
|
+
function nonnegative(value) {
|
|
2257
|
+
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
2258
|
+
}
|
|
2259
|
+
function record2(value) {
|
|
2260
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2261
|
+
}
|
|
2262
|
+
function decodeResponseBuffer(buffer) {
|
|
2263
|
+
if (buffer.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2264
|
+
const length = buffer.readUInt32BE(0);
|
|
2265
|
+
if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFact("declared_bytes", length, `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}`)}.`);
|
|
2266
|
+
if (buffer.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2267
|
+
if (buffer.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC returned trailing bytes: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `the received message length minus ${HOST_RELAY_IPC_LENGTH_BYTES}` }, { name: "received_bytes", value: buffer.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2268
|
+
return decodeHostRelayIpcResponse(buffer);
|
|
2269
|
+
}
|
|
2270
|
+
function decodeCurrentPromptResponseBuffer(buffer) {
|
|
2271
|
+
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
|
|
2272
|
+
if (value === null) return null;
|
|
2273
|
+
if (!record2(value) || !exactKeys4(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "current_prompt_unavailable") throw new HostRelayIpcError("relay_unavailable");
|
|
2274
|
+
return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
|
|
2275
|
+
}
|
|
2276
|
+
function decodeLengthPrefixedJson(input, maximum) {
|
|
2277
|
+
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2278
|
+
const length = input.readUInt32BE(0);
|
|
2279
|
+
if (length === 0 || length > maximum) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC message length is invalid: ${refusalFact("declared_bytes", length, `between 1 and ${maximum}`)}.`);
|
|
2280
|
+
if (input.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2281
|
+
if (input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC message has trailing bytes: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `the received message length minus ${HOST_RELAY_IPC_LENGTH_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2282
|
+
try {
|
|
2283
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
|
|
2284
|
+
} catch {
|
|
2285
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC message is invalid.");
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
function encodeLengthPrefixed(value) {
|
|
2289
|
+
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
2290
|
+
const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
|
|
2291
|
+
output.writeUInt32BE(body.length, 0);
|
|
2292
|
+
body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
|
|
2293
|
+
return output;
|
|
2294
|
+
}
|
|
2295
|
+
function exactKeys4(value, required, optional = []) {
|
|
2296
|
+
const keys = Object.keys(value);
|
|
2297
|
+
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2298
|
+
}
|
|
2299
|
+
function errorCode(error) {
|
|
2300
|
+
return record2(error) && typeof error.code === "string" ? error.code : null;
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
// host/relay/src/host-producer-core.ts
|
|
2304
|
+
var HOST_TRANSCRIPT_CURSOR_REASONS = vocabulary(["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"]);
|
|
2305
|
+
function isHostTranscriptCursorReason(value) {
|
|
2306
|
+
return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
|
|
2307
|
+
}
|
|
2308
|
+
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
2309
|
+
var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
|
|
2310
|
+
async function readTranscriptCompleteChunk(handle, offset, size) {
|
|
2311
|
+
const available = Math.max(0, size - offset);
|
|
2312
|
+
if (available === 0) return "empty";
|
|
2313
|
+
const chunks = [];
|
|
2314
|
+
let position = offset;
|
|
2315
|
+
let total = 0;
|
|
2316
|
+
let lastNewline = -1;
|
|
2317
|
+
while (position < size) {
|
|
2318
|
+
const buffer = Buffer.alloc(Math.min(HOST_TRANSCRIPT_READ_CHUNK_BYTES, size - position));
|
|
2319
|
+
const read = await handle.read(buffer, 0, buffer.length, position);
|
|
2320
|
+
if (read.bytesRead === 0) break;
|
|
2321
|
+
const chunk = buffer.subarray(0, read.bytesRead);
|
|
2322
|
+
chunks.push(chunk);
|
|
2323
|
+
const newline = chunk.lastIndexOf(10);
|
|
2324
|
+
if (newline >= 0) lastNewline = total + newline;
|
|
2325
|
+
total += chunk.length;
|
|
2326
|
+
position += chunk.length;
|
|
2327
|
+
if (lastNewline >= 0) break;
|
|
2328
|
+
}
|
|
2329
|
+
if (lastNewline < 0) return "partial";
|
|
2330
|
+
return Buffer.concat(chunks, total).subarray(0, lastNewline + 1);
|
|
2331
|
+
}
|
|
2332
|
+
async function readTranscriptAnchor(handle, offset, anchorBytes) {
|
|
2333
|
+
const start = Math.max(0, offset - anchorBytes);
|
|
2334
|
+
const anchor = Buffer.alloc(offset - start);
|
|
2335
|
+
if (anchor.length > 0) await handle.read(anchor, 0, anchor.length, start);
|
|
2336
|
+
return { start, sha256: transcriptSha256(anchor) };
|
|
2337
|
+
}
|
|
2338
|
+
async function transcriptAnchorMatches(handle, cursor) {
|
|
2339
|
+
const buffer = Buffer.alloc(cursor.offset - cursor.anchor_start);
|
|
2340
|
+
if (buffer.length > 0) await handle.read(buffer, 0, buffer.length, cursor.anchor_start);
|
|
2341
|
+
return transcriptSha256(buffer) === cursor.anchor_sha256;
|
|
2342
|
+
}
|
|
2343
|
+
function transcriptCursorIdentityReason(cursor, transcriptPath, device, inode) {
|
|
2344
|
+
if (cursor.device !== device || cursor.inode !== inode || cursor.transcript_path !== transcriptPath) return "transcript_replaced";
|
|
2345
|
+
return null;
|
|
2346
|
+
}
|
|
2347
|
+
async function transcriptCursorBoundsReason(handle, cursor, size) {
|
|
2348
|
+
if (size < cursor.offset) return "transcript_truncated";
|
|
2349
|
+
if (cursor.offset > 0 && !await transcriptAnchorMatches(handle, cursor)) return "transcript_rewritten";
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
function transcriptSha256(value) {
|
|
2353
|
+
return createHash3("sha256").update(value).digest("hex");
|
|
2354
|
+
}
|
|
2355
|
+
function transcriptObjectSha256(value) {
|
|
2356
|
+
return createHash3("sha256").update(JSON.stringify(value), "utf8").digest("hex");
|
|
2357
|
+
}
|
|
2358
|
+
function isTranscriptRecord(value) {
|
|
2359
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2360
|
+
}
|
|
2361
|
+
function isMissingTranscriptError(error) {
|
|
2362
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2363
|
+
}
|
|
2364
|
+
function transcriptTimestamp(now, fail) {
|
|
2365
|
+
const value = now();
|
|
2366
|
+
if (!Number.isFinite(value.getTime())) throw fail();
|
|
2367
|
+
return value.toISOString();
|
|
2368
|
+
}
|
|
2369
|
+
function transcriptExpiration(now, retentionMs, fail) {
|
|
2370
|
+
return new Date(Date.parse(transcriptTimestamp(now, fail)) + retentionMs).toISOString();
|
|
2371
|
+
}
|
|
2372
|
+
function createTranscriptWatcherBase(options, owner) {
|
|
2373
|
+
if (typeof options.outbox_dir !== "string" || options.outbox_dir.length === 0) throw owner.invalid_outbox();
|
|
2374
|
+
return {
|
|
2375
|
+
outboxRetentionMs: options.retention_ms ?? owner.default_retention_ms,
|
|
2376
|
+
now: options.now ?? (() => /* @__PURE__ */ new Date()),
|
|
2377
|
+
log: options.log ?? ((line) => {
|
|
2378
|
+
process.stderr.write(`${line}
|
|
2379
|
+
`);
|
|
2380
|
+
}),
|
|
2381
|
+
producerInstallationRef: options.producer_installation_ref
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
async function scanTranscriptRegistrations(registrations, scanOne) {
|
|
2385
|
+
let records = 0;
|
|
2386
|
+
let enqueued = 0;
|
|
2387
|
+
let quarantined = 0;
|
|
2388
|
+
for (const registration of registrations) {
|
|
2389
|
+
const result = await scanOne(registration);
|
|
2390
|
+
records += result.records;
|
|
2391
|
+
enqueued += result.enqueued;
|
|
2392
|
+
if (result.quarantined) quarantined += 1;
|
|
2393
|
+
}
|
|
2394
|
+
return { registrations: registrations.length, records, enqueued, quarantined };
|
|
2395
|
+
}
|
|
2396
|
+
function formatRetiredTranscriptLog(producerLabel, transcriptPath) {
|
|
2397
|
+
return `${producerLabel} retired a transcript registration: transcript_path=${JSON.stringify(transcriptPath)}, reason=${TRANSCRIPT_MISSING_REASON}.`;
|
|
2398
|
+
}
|
|
2399
|
+
var HostTranscriptWatcher = class {
|
|
2400
|
+
outboxRetentionMs;
|
|
2401
|
+
now;
|
|
2402
|
+
log;
|
|
2403
|
+
producerInstallationRef;
|
|
2404
|
+
clockFailure;
|
|
2405
|
+
constructor(options, owner) {
|
|
2406
|
+
const base = createTranscriptWatcherBase(options, owner);
|
|
2407
|
+
this.outboxRetentionMs = base.outboxRetentionMs;
|
|
2408
|
+
this.now = base.now;
|
|
2409
|
+
this.log = base.log;
|
|
2410
|
+
this.producerInstallationRef = base.producerInstallationRef;
|
|
2411
|
+
this.clockFailure = owner.clock_failure;
|
|
2412
|
+
}
|
|
2413
|
+
async scanOnce() {
|
|
2414
|
+
return scanTranscriptRegistrations(await this.listRegistrations(), (registration) => this.scanRegistration(registration));
|
|
2415
|
+
}
|
|
2416
|
+
async scanRegistration(registration) {
|
|
2417
|
+
return this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
|
|
2418
|
+
missing: () => this.retireMissing(registration),
|
|
2419
|
+
failed: (handle, error) => this.scanFailed(handle, registration, error)
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
async openTranscript(registration, scan, handlers) {
|
|
2423
|
+
let handle;
|
|
2424
|
+
try {
|
|
2425
|
+
handle = await open2(this.transcriptPathOf(registration), O_RDONLY2 | O_NOFOLLOW);
|
|
2426
|
+
return await scan(handle);
|
|
2427
|
+
} catch (error) {
|
|
2428
|
+
if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
|
|
2429
|
+
if (hostRelayExchangeFailure(error) !== void 0) throw error;
|
|
2430
|
+
return handlers.failed(handle, error);
|
|
2431
|
+
} finally {
|
|
2432
|
+
await handle?.close();
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
/**
|
|
2436
|
+
* A transcript the Host deleted never comes back, so retrying its
|
|
2437
|
+
* registration once per reconcile interval is a failure with no end.
|
|
2438
|
+
* Retire it at the scan that finds it missing and say so once. Only
|
|
2439
|
+
* ENOENT means gone; a permission or busy failure is about this moment,
|
|
2440
|
+
* not about the file, and keeps the registration for the next scan.
|
|
2441
|
+
*/
|
|
2442
|
+
async retireMissingTranscript(transcriptPath, producerLabel, retire) {
|
|
2443
|
+
await retire();
|
|
2444
|
+
this.log(formatRetiredTranscriptLog(producerLabel, transcriptPath));
|
|
2445
|
+
return { records: 0, enqueued: 0, quarantined: false };
|
|
2446
|
+
}
|
|
2447
|
+
stamp() {
|
|
2448
|
+
return transcriptTimestamp(this.now, this.clockFailure);
|
|
2449
|
+
}
|
|
2450
|
+
expire(retentionMs) {
|
|
2451
|
+
return transcriptExpiration(this.now, retentionMs, this.clockFailure);
|
|
2452
|
+
}
|
|
2453
|
+
sha(value) {
|
|
2454
|
+
return transcriptSha256(value);
|
|
2455
|
+
}
|
|
2456
|
+
objectSha(value) {
|
|
2457
|
+
return transcriptObjectSha256(value);
|
|
2458
|
+
}
|
|
2459
|
+
isRecord(value) {
|
|
2460
|
+
return isTranscriptRecord(value);
|
|
2461
|
+
}
|
|
2462
|
+
readChunk(handle, offset, size) {
|
|
2463
|
+
return readTranscriptCompleteChunk(handle, offset, size);
|
|
2464
|
+
}
|
|
2465
|
+
readAnchor(handle, offset, anchorBytes) {
|
|
2466
|
+
return readTranscriptAnchor(handle, offset, anchorBytes);
|
|
2467
|
+
}
|
|
2468
|
+
cursorIdentityReason(cursor, transcriptPath, device, inode) {
|
|
2469
|
+
return transcriptCursorIdentityReason(cursor, transcriptPath, device, inode);
|
|
2470
|
+
}
|
|
2471
|
+
cursorBoundsReason(handle, cursor, size) {
|
|
2472
|
+
return transcriptCursorBoundsReason(handle, cursor, size);
|
|
2473
|
+
}
|
|
2474
|
+
};
|
|
2475
|
+
var HOST_PRODUCER_STATE_IDENTITY = {
|
|
2476
|
+
codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
|
|
2477
|
+
claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
|
|
2478
|
+
};
|
|
2479
|
+
var HostProducerDrainCore = class {
|
|
2480
|
+
host;
|
|
2481
|
+
relay;
|
|
2482
|
+
statePath;
|
|
2483
|
+
stateSchema;
|
|
2484
|
+
installationRef;
|
|
2485
|
+
epoch;
|
|
2486
|
+
now;
|
|
2487
|
+
tail = Promise.resolve();
|
|
2488
|
+
constructor(host, runtime) {
|
|
2489
|
+
if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
|
|
2490
|
+
const epoch = runtime.producer_epoch ?? 1;
|
|
2491
|
+
if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
|
|
2492
|
+
this.host = host;
|
|
2493
|
+
this.relay = runtime.relay;
|
|
2494
|
+
this.statePath = join2(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
|
|
2495
|
+
this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
|
|
2496
|
+
this.installationRef = runtime.producer_installation_ref;
|
|
2497
|
+
this.epoch = epoch;
|
|
2498
|
+
this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
|
|
2499
|
+
}
|
|
2500
|
+
drainOutbox() {
|
|
2501
|
+
const run = this.tail.then(() => this.drainLocked());
|
|
2502
|
+
this.tail = run.then(() => void 0, () => void 0);
|
|
2503
|
+
return run;
|
|
2504
|
+
}
|
|
2505
|
+
async drainLocked() {
|
|
2506
|
+
const read = await this.host.listEnvelopes();
|
|
2507
|
+
let admitted = 0;
|
|
2508
|
+
let expired = 0;
|
|
2509
|
+
let retained = 0;
|
|
2510
|
+
for (const envelope of read.entries) {
|
|
2511
|
+
const reserved = await this.hasReservation(this.host.submissionRefOf(envelope));
|
|
2512
|
+
if (!reserved && Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) {
|
|
2513
|
+
await this.host.removeEnvelope(envelope);
|
|
2514
|
+
expired += 1;
|
|
2515
|
+
continue;
|
|
2516
|
+
}
|
|
2517
|
+
const frames = await this.reserve(envelope);
|
|
2518
|
+
let complete = true;
|
|
2519
|
+
for (const frame of frames) {
|
|
2520
|
+
const outcome = await this.relay.admit(frame);
|
|
2521
|
+
if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
|
|
2522
|
+
complete = false;
|
|
2523
|
+
break;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
if (!complete) {
|
|
2527
|
+
retained += 1;
|
|
2528
|
+
break;
|
|
2529
|
+
}
|
|
2530
|
+
await this.host.removeEnvelope(envelope);
|
|
2531
|
+
await this.release(this.host.submissionRefOf(envelope));
|
|
2532
|
+
admitted += 1;
|
|
2533
|
+
}
|
|
2534
|
+
return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
|
|
2535
|
+
}
|
|
2536
|
+
async hasReservation(submissionRef) {
|
|
2537
|
+
return (await this.readState()).pending[submissionRef] !== void 0;
|
|
2538
|
+
}
|
|
2539
|
+
async reserve(envelope) {
|
|
2540
|
+
const state = await this.readState();
|
|
2541
|
+
const submissionRef = this.host.submissionRefOf(envelope);
|
|
2542
|
+
const existing = state.pending[submissionRef];
|
|
2543
|
+
if (existing !== void 0) return existing;
|
|
2544
|
+
const drafts = await this.host.mapEnvelope(envelope);
|
|
2545
|
+
const frames = drafts.map((draft, offset) => ({
|
|
2546
|
+
schema_version: "ceal.host_producer_frame.v1",
|
|
2547
|
+
producer_id: this.host.producer_id,
|
|
2548
|
+
producer_installation_ref: this.installationRef,
|
|
2549
|
+
producer_epoch: this.epoch,
|
|
2550
|
+
sequence: state.next_sequence + offset,
|
|
2551
|
+
frame_kind: draft.frame_kind,
|
|
2552
|
+
occurred_at: this.host.createdAtOf(envelope),
|
|
2553
|
+
payload: draft.payload
|
|
2554
|
+
}));
|
|
2555
|
+
await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [submissionRef]: frames } });
|
|
2556
|
+
return frames;
|
|
2557
|
+
}
|
|
2558
|
+
async release(submissionRef) {
|
|
2559
|
+
const state = await this.readState();
|
|
2560
|
+
if (state.pending[submissionRef] === void 0) return;
|
|
2561
|
+
const pending = { ...state.pending };
|
|
2562
|
+
delete pending[submissionRef];
|
|
2563
|
+
await this.writeState({ ...state, pending });
|
|
2564
|
+
}
|
|
2565
|
+
async readState() {
|
|
2566
|
+
try {
|
|
2567
|
+
const value = JSON.parse(await readFile(this.statePath, "utf8"));
|
|
2568
|
+
return this.decodeState(value);
|
|
2569
|
+
} catch (error) {
|
|
2570
|
+
if (errorCode2(error) !== "ENOENT") throw error;
|
|
2571
|
+
return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
async writeState(state) {
|
|
2575
|
+
await mkdir2(this.host.outbox_directory, { recursive: true, mode: 448 });
|
|
2576
|
+
const temporary = `${this.statePath}.${process.pid}.new`;
|
|
2577
|
+
await writeFile(temporary, JSON.stringify(state), { mode: 384 });
|
|
2578
|
+
await rename2(temporary, this.statePath);
|
|
2579
|
+
}
|
|
2580
|
+
decodeState(value) {
|
|
2581
|
+
const label = this.host.producer_label;
|
|
2582
|
+
const fail = (code, message) => this.host.fail(code, message);
|
|
2583
|
+
if (!isDrainStateHeader(value, this.stateSchema, this.installationRef)) {
|
|
2584
|
+
throw fail("storage", `${label} producer state header is malformed or belongs to another installation (${refusalFacts([
|
|
2585
|
+
{ name: "schema_version", value: isTranscriptRecord(value) ? value.schema_version : void 0, expected: this.stateSchema },
|
|
2586
|
+
{ name: "producer_installation_ref", value: isTranscriptRecord(value) ? value.producer_installation_ref : void 0, expected: this.installationRef }
|
|
2587
|
+
])}).`);
|
|
2588
|
+
}
|
|
2589
|
+
if (Number(value.producer_epoch) > this.epoch) throw fail("storage", `${label} producer state epoch is ahead of this installation (${refusalFact("producer_epoch", value.producer_epoch, `at most ${this.epoch}`)}).`);
|
|
2590
|
+
const pending = {};
|
|
2591
|
+
const storedEpoch = Number(value.producer_epoch);
|
|
2592
|
+
for (const [submissionRef, frames] of Object.entries(value.pending)) {
|
|
2593
|
+
if (!isHostSafeRef(submissionRef) || !Array.isArray(frames) || frames.length === 0) throw fail("storage", `${label} producer pending reservation is malformed.`);
|
|
2594
|
+
pending[submissionRef] = frames.map((frame) => this.decodeStoredFrame(frame, storedEpoch));
|
|
2595
|
+
}
|
|
2596
|
+
return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: Number(value.next_sequence), pending };
|
|
2597
|
+
}
|
|
2598
|
+
decodeStoredFrame(value, storedEpoch) {
|
|
2599
|
+
const label = this.host.producer_label;
|
|
2600
|
+
const json = Buffer.from(JSON.stringify(value), "utf8");
|
|
2601
|
+
const encoded = Buffer.allocUnsafe(json.length + 4);
|
|
2602
|
+
encoded.writeUInt32BE(json.length, 0);
|
|
2603
|
+
json.copy(encoded, 4);
|
|
2604
|
+
const frame = decodeHostProducerFrame(encoded);
|
|
2605
|
+
if (frame.producer_id !== this.host.producer_id || frame.producer_installation_ref !== this.installationRef || frame.producer_epoch > storedEpoch) throw this.host.fail("storage", `${label} producer pending frame identity is malformed (${refusalFacts([
|
|
2606
|
+
{ name: "producer_id", value: frame.producer_id, expected: this.host.producer_id },
|
|
2607
|
+
{ name: "producer_installation_ref", value: frame.producer_installation_ref, expected: this.installationRef },
|
|
2608
|
+
{ name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${storedEpoch}` }
|
|
2609
|
+
])}).`);
|
|
2610
|
+
return frame;
|
|
2611
|
+
}
|
|
2612
|
+
};
|
|
2613
|
+
function isDrainStateHeader(value, schema, installationRef) {
|
|
2614
|
+
return isTranscriptRecord(value) && value.schema_version === schema && value.producer_installation_ref === installationRef && Number.isSafeInteger(value.producer_epoch) && Number(value.producer_epoch) > 0 && Number.isSafeInteger(value.next_sequence) && Number(value.next_sequence) > 0 && isTranscriptRecord(value.pending);
|
|
2615
|
+
}
|
|
2616
|
+
function errorCode2(error) {
|
|
2617
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
// host/relay/src/relay-quarantine-store.ts
|
|
2621
|
+
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
2622
|
+
var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
|
|
2623
|
+
var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
|
|
2624
|
+
|
|
2625
|
+
// host/relay/src/relay-admission-store.ts
|
|
2626
|
+
var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
|
|
2627
|
+
var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
2628
|
+
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
2629
|
+
|
|
2630
|
+
// host/relay/src/relay-gateway-bridge.ts
|
|
2631
|
+
var HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS = ["created", "adopted", "replayed", "approval_approved", "approval_denied", "approval_replayed"];
|
|
2632
|
+
function isHostGatewayAcknowledgementDisposition(value) {
|
|
2633
|
+
return typeof value === "string" && HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS.some((disposition) => disposition === value);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
// host/codex/src/codex-invocation-context.ts
|
|
2637
|
+
function codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef) {
|
|
2638
|
+
return invocationContextFromTurn(identity, promptRef, expiresAt, { schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION, validate: validateCodexHostInvocationContext, turn_generation_ref: turnGenerationRef });
|
|
2639
|
+
}
|
|
2640
|
+
function validateCodexHostInvocationContext(value) {
|
|
2641
|
+
if (!isContextShape(value)) {
|
|
2642
|
+
throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2643
|
+
}
|
|
2644
|
+
validateIdentity(value.identity);
|
|
2645
|
+
validateContextReferences(value);
|
|
2646
|
+
validateContextTimes(value);
|
|
2647
|
+
}
|
|
2648
|
+
function invocationContextFromBinding(binding, turnGenerationRef) {
|
|
2649
|
+
const context = {
|
|
2650
|
+
schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
|
|
2651
|
+
identity: binding.identity,
|
|
2652
|
+
prompt_ref: binding.prompt_ref,
|
|
2653
|
+
request_ref: binding.request_ref,
|
|
2654
|
+
bound_at: binding.bound_at,
|
|
2655
|
+
expires_at: binding.expires_at,
|
|
2656
|
+
...turnGenerationRef === void 0 ? {} : { turn_generation_ref: turnGenerationRef }
|
|
2657
|
+
};
|
|
2658
|
+
validateCodexHostInvocationContext(context);
|
|
2659
|
+
return Object.freeze(context);
|
|
2660
|
+
}
|
|
2661
|
+
function validateIdentity(value) {
|
|
2662
|
+
if (!isRecord3(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef3)) {
|
|
2663
|
+
throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
function isContextShape(value) {
|
|
2667
|
+
return isRecord3(value) && hasHostInvocationContextKeys(value, ["request_ref", "bound_at", "turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
|
|
2668
|
+
}
|
|
2669
|
+
function validateContextReferences(value) {
|
|
2670
|
+
if (!safeRef3(value.prompt_ref) || !optionalSafeRef(value.request_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2671
|
+
}
|
|
2672
|
+
function validateContextTimes(value) {
|
|
2673
|
+
const expiresAt = value.expires_at;
|
|
2674
|
+
const boundAt = value.bound_at;
|
|
2675
|
+
if (!timestamp2(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2676
|
+
if (typeof boundAt === "string" && Date.parse(boundAt) > Date.parse(expiresAt)) throw new CodexHostAdapterError("storage", `Codex Host invocation context has invalid binding time (${refusalFact("bound_at", boundAt, `at or before ${expiresAt}`)}).`);
|
|
2677
|
+
}
|
|
2678
|
+
function optionalSafeRef(value) {
|
|
2679
|
+
return value === void 0 || safeRef3(value);
|
|
2680
|
+
}
|
|
2681
|
+
function optionalTimestamp(value) {
|
|
2682
|
+
return value === void 0 || timestamp2(value);
|
|
2683
|
+
}
|
|
2684
|
+
function safeRef3(value) {
|
|
2685
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
2686
|
+
}
|
|
2687
|
+
function timestamp2(value) {
|
|
2688
|
+
return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
2689
|
+
}
|
|
2690
|
+
function isRecord3(value) {
|
|
2691
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
// host/codex/src/codex-outbox.ts
|
|
2695
|
+
var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2696
|
+
var CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS = 4096;
|
|
2697
|
+
var CodexOutboxStore = class {
|
|
2698
|
+
directory;
|
|
2699
|
+
entriesDirectory;
|
|
2700
|
+
bindingsDirectory;
|
|
2701
|
+
sessionsDirectory;
|
|
2702
|
+
activeContextsDirectory;
|
|
2703
|
+
turnSelectionsDirectory;
|
|
2704
|
+
registrationsDirectory;
|
|
2705
|
+
cursorsDirectory;
|
|
2706
|
+
constructor(directory) {
|
|
2707
|
+
if (typeof directory !== "string" || directory.length === 0) throw new CodexHostAdapterError("configuration", "Codex outbox directory is required.");
|
|
2708
|
+
this.directory = directory;
|
|
2709
|
+
this.entriesDirectory = join3(directory, "entries");
|
|
2710
|
+
this.bindingsDirectory = join3(directory, "bindings");
|
|
2711
|
+
this.sessionsDirectory = join3(directory, "sessions");
|
|
2712
|
+
this.activeContextsDirectory = join3(directory, "active-contexts");
|
|
2713
|
+
this.turnSelectionsDirectory = join3(directory, "turn-selections");
|
|
2714
|
+
this.registrationsDirectory = join3(directory, "transcript-registrations");
|
|
2715
|
+
this.cursorsDirectory = join3(directory, "transcript-cursors");
|
|
2716
|
+
}
|
|
2717
|
+
async prepare() {
|
|
2718
|
+
for (const path of [this.directory, this.entriesDirectory, this.bindingsDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
|
|
2719
|
+
await ensurePrivateOwnerDirectory(path);
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
async put(entry) {
|
|
2723
|
+
validateEnvelope(entry);
|
|
2724
|
+
await this.prepare();
|
|
2725
|
+
await writeAtomicJson(this.entryPath(entry.submission_ref), entry);
|
|
2726
|
+
}
|
|
2727
|
+
async list() {
|
|
2728
|
+
await this.prepare();
|
|
2729
|
+
const entries = [];
|
|
2730
|
+
let malformed = 0;
|
|
2731
|
+
for (const name of await readdir(this.entriesDirectory)) {
|
|
2732
|
+
if (!name.endsWith(".json")) continue;
|
|
2733
|
+
try {
|
|
2734
|
+
const value = JSON.parse(await readPrivateFile(join3(this.entriesDirectory, name)));
|
|
2735
|
+
validateEnvelope(value);
|
|
2736
|
+
entries.push(value);
|
|
2737
|
+
} catch {
|
|
2738
|
+
malformed += 1;
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
entries.sort((left, right) => left.created_at.localeCompare(right.created_at));
|
|
2742
|
+
return { entries, malformed };
|
|
2743
|
+
}
|
|
2744
|
+
async remove(entry) {
|
|
2745
|
+
validateEnvelope(entry);
|
|
2746
|
+
try {
|
|
2747
|
+
await unlink2(this.entryPath(entry.submission_ref));
|
|
2748
|
+
} catch (error) {
|
|
2749
|
+
if (!isNotFound(error)) throw storageError("Unable to remove the Codex outbox entry.");
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
async writeBinding(binding) {
|
|
2753
|
+
validateBinding(binding);
|
|
2754
|
+
await this.prepare();
|
|
2755
|
+
await writeAtomicJson(this.bindingPath(binding.identity), binding);
|
|
2756
|
+
}
|
|
2757
|
+
/**
|
|
2758
|
+
* Select the exact Host turn that model-reachable commands may use. A new
|
|
2759
|
+
* prompt replaces this projection, so it cannot inherit the prior turn's
|
|
2760
|
+
* Request by session alone.
|
|
2761
|
+
*/
|
|
2762
|
+
async activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef) {
|
|
2763
|
+
const context = codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef);
|
|
2764
|
+
await this.prepare();
|
|
2765
|
+
const path = this.activeContextPath(identity);
|
|
2766
|
+
try {
|
|
1838
2767
|
await writeAtomicJson(path, context);
|
|
1839
2768
|
} catch (error) {
|
|
1840
2769
|
try {
|
|
1841
|
-
await
|
|
2770
|
+
await unlink2(path);
|
|
1842
2771
|
} catch (unlinkError) {
|
|
1843
2772
|
if (!isNotFound(unlinkError)) throw storageError("Unable to clear a failed Codex Host context replacement.");
|
|
1844
2773
|
}
|
|
1845
2774
|
throw error;
|
|
1846
2775
|
}
|
|
1847
2776
|
}
|
|
2777
|
+
async writeInvocationTurnSelection(selection) {
|
|
2778
|
+
if (!isHostInvocationTurnSelection(selection)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection is invalid.");
|
|
2779
|
+
await this.prepare();
|
|
2780
|
+
await writeAtomicJson(this.turnSelectionPath(selection.host_source_ref, selection.host_session_ref), selection);
|
|
2781
|
+
}
|
|
2782
|
+
async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
|
|
2783
|
+
if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
|
|
2784
|
+
await this.prepare();
|
|
2785
|
+
return readOptionalJson(this.turnSelectionPath(hostSourceRef, hostSessionRef), validateTurnSelection);
|
|
2786
|
+
}
|
|
2787
|
+
async attachInvocationTurnGeneration(identity, promptRef, turnGenerationRef) {
|
|
2788
|
+
const selection = await this.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
2789
|
+
if (selection === null || selection.prompt_ref !== promptRef || selection.turn_generation_ref !== turnGenerationRef) return false;
|
|
2790
|
+
const path = this.activeContextPath(identity);
|
|
2791
|
+
const active = await readOptionalJson(path, validateCodexHostInvocationContext);
|
|
2792
|
+
if (active === null || active.prompt_ref !== promptRef || !sameIdentity(active.identity, identity)) return false;
|
|
2793
|
+
await writeAtomicJson(path, { ...active, turn_generation_ref: turnGenerationRef });
|
|
2794
|
+
return true;
|
|
2795
|
+
}
|
|
1848
2796
|
/**
|
|
1849
2797
|
* Publish a Request reference only while the acknowledged binding is still
|
|
1850
2798
|
* the active turn. A delayed relay response therefore cannot overwrite a
|
|
@@ -1854,17 +2802,25 @@ var CodexOutboxStore = class {
|
|
|
1854
2802
|
validateBinding(binding);
|
|
1855
2803
|
const active = await this.readInvocationContext(binding.identity.host_source_ref, binding.identity.host_session_ref, now);
|
|
1856
2804
|
if (active === null || !sameIdentity(active.identity, binding.identity)) return false;
|
|
1857
|
-
await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding));
|
|
2805
|
+
await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding, active.turn_generation_ref));
|
|
1858
2806
|
return true;
|
|
1859
2807
|
}
|
|
1860
|
-
/** Return only the
|
|
2808
|
+
/** Return only the live session projection; never fall back to another turn. */
|
|
1861
2809
|
async readInvocationContext(hostSourceRef, hostSessionRef, now = /* @__PURE__ */ new Date()) {
|
|
1862
|
-
if (!SAFE_REF3.test(hostSourceRef) || !SAFE_REF3.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
|
|
1863
2810
|
if (!(now instanceof Date) || !Number.isFinite(now.getTime())) throw new CodexHostAdapterError("invalid_input", "Codex Host context clock is invalid.");
|
|
2811
|
+
const context = await this.readRawInvocationContext(hostSourceRef, hostSessionRef);
|
|
2812
|
+
return isLiveSessionProjection(context, now) && invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* The latest Codex session projection even when past expiry, so the
|
|
2816
|
+
* invocation authenticator can report an expired context distinctly from
|
|
2817
|
+
* a missing one. Every other caller keeps the fail-closed read above.
|
|
2818
|
+
*/
|
|
2819
|
+
async readRawInvocationContext(hostSourceRef, hostSessionRef) {
|
|
2820
|
+
if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
|
|
1864
2821
|
await this.prepare();
|
|
1865
|
-
const context = await
|
|
1866
|
-
|
|
1867
|
-
return Date.parse(context.expires_at) > now.getTime() ? context : null;
|
|
2822
|
+
const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, (file) => readOptionalJson(file, validateCodexHostInvocationContext));
|
|
2823
|
+
return invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
|
|
1868
2824
|
}
|
|
1869
2825
|
/** Clear the projection only if the caller still owns its exact active turn. */
|
|
1870
2826
|
async clearInvocationContext(identity, now = /* @__PURE__ */ new Date()) {
|
|
@@ -1872,7 +2828,7 @@ var CodexOutboxStore = class {
|
|
|
1872
2828
|
const active = await this.readInvocationContext(identity.host_source_ref, identity.host_session_ref, now);
|
|
1873
2829
|
if (active === null || !sameIdentity(active.identity, identity)) return false;
|
|
1874
2830
|
try {
|
|
1875
|
-
await
|
|
2831
|
+
await unlink2(this.activeContextPath(identity));
|
|
1876
2832
|
} catch (error) {
|
|
1877
2833
|
if (!isNotFound(error)) throw storageError("Unable to clear the Codex Host invocation context.");
|
|
1878
2834
|
}
|
|
@@ -1890,7 +2846,7 @@ var CodexOutboxStore = class {
|
|
|
1890
2846
|
}
|
|
1891
2847
|
async readSession(sourceRef, sessionRef) {
|
|
1892
2848
|
const identity = { host_source_ref: sourceRef, host_session_ref: sessionRef };
|
|
1893
|
-
if (!
|
|
2849
|
+
if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex session identity is invalid.");
|
|
1894
2850
|
await this.prepare();
|
|
1895
2851
|
return readOptionalJson(this.sessionPath(identity.host_source_ref, identity.host_session_ref), validateSession);
|
|
1896
2852
|
}
|
|
@@ -1923,7 +2879,7 @@ var CodexOutboxStore = class {
|
|
|
1923
2879
|
for (const name of await readdir(this.registrationsDirectory)) {
|
|
1924
2880
|
if (!name.endsWith(".json")) continue;
|
|
1925
2881
|
try {
|
|
1926
|
-
const value = JSON.parse(await readPrivateFile(
|
|
2882
|
+
const value = JSON.parse(await readPrivateFile(join3(this.registrationsDirectory, name)));
|
|
1927
2883
|
validateRegistration(value);
|
|
1928
2884
|
registrations.push(value);
|
|
1929
2885
|
} catch {
|
|
@@ -1942,7 +2898,7 @@ var CodexOutboxStore = class {
|
|
|
1942
2898
|
await writeAtomicJson(this.cursorPath(cursor.host_source_ref, cursor.host_session_ref), cursor);
|
|
1943
2899
|
}
|
|
1944
2900
|
async findByAlias(sourceRef, sessionRef, alias) {
|
|
1945
|
-
if (!
|
|
2901
|
+
if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef) || !SAFE_REF4.test(alias)) return null;
|
|
1946
2902
|
const read = await this.list();
|
|
1947
2903
|
const envelope = findEnvelopeByAlias(read.entries, sourceRef, sessionRef, alias);
|
|
1948
2904
|
if (envelope !== null) return envelopeMatch(envelope);
|
|
@@ -1952,7 +2908,7 @@ var CodexOutboxStore = class {
|
|
|
1952
2908
|
return null;
|
|
1953
2909
|
}
|
|
1954
2910
|
async hasPriorTurn(sourceRef, sessionRef) {
|
|
1955
|
-
if (!
|
|
2911
|
+
if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) return false;
|
|
1956
2912
|
const read = await this.list();
|
|
1957
2913
|
if (read.entries.some((entry) => entry.identity.host_source_ref === sourceRef && entry.identity.host_session_ref === sessionRef)) return true;
|
|
1958
2914
|
await this.prepare();
|
|
@@ -1964,34 +2920,37 @@ var CodexOutboxStore = class {
|
|
|
1964
2920
|
return false;
|
|
1965
2921
|
}
|
|
1966
2922
|
entryPath(submissionRef) {
|
|
1967
|
-
if (!
|
|
1968
|
-
return
|
|
2923
|
+
if (!SAFE_REF4.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
|
|
2924
|
+
return join3(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
|
|
1969
2925
|
}
|
|
1970
2926
|
bindingPath(identity) {
|
|
1971
|
-
return
|
|
2927
|
+
return join3(this.bindingsDirectory, `${identityKey(identity)}.json`);
|
|
1972
2928
|
}
|
|
1973
2929
|
sessionPath(sourceRef, sessionRef) {
|
|
1974
|
-
return
|
|
2930
|
+
return join3(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
|
|
1975
2931
|
}
|
|
1976
2932
|
activeContextPath(identity) {
|
|
1977
|
-
return
|
|
2933
|
+
return join3(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
|
|
2934
|
+
}
|
|
2935
|
+
turnSelectionPath(sourceRef, sessionRef) {
|
|
2936
|
+
return join3(this.turnSelectionsDirectory, `${invocationContextKey({ host_source_ref: sourceRef, host_session_ref: sessionRef })}.json`);
|
|
1978
2937
|
}
|
|
1979
2938
|
registrationPath(sourceRef, sessionRef) {
|
|
1980
|
-
return
|
|
2939
|
+
return join3(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
|
|
1981
2940
|
}
|
|
1982
2941
|
cursorPath(sourceRef, sessionRef) {
|
|
1983
|
-
return
|
|
2942
|
+
return join3(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
|
|
1984
2943
|
}
|
|
1985
2944
|
};
|
|
1986
2945
|
async function removeOwnerState(path, what) {
|
|
1987
2946
|
try {
|
|
1988
|
-
await
|
|
2947
|
+
await unlink2(path);
|
|
1989
2948
|
} catch (error) {
|
|
1990
2949
|
if (!isNotFound(error)) throw storageError(`Unable to retire the Codex transcript ${what}.`);
|
|
1991
2950
|
}
|
|
1992
2951
|
}
|
|
1993
2952
|
function sessionStateKey(sourceRef, sessionRef) {
|
|
1994
|
-
if (!
|
|
2953
|
+
if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) throw storageError("Codex transcript session identity is malformed.");
|
|
1995
2954
|
return cealRequestSha256({ host_source_ref: sourceRef, host_session_ref: sessionRef });
|
|
1996
2955
|
}
|
|
1997
2956
|
function envelopeMatch(envelope) {
|
|
@@ -2018,7 +2977,7 @@ async function findBindingByAlias(directory, sourceRef, sessionRef, alias) {
|
|
|
2018
2977
|
}
|
|
2019
2978
|
async function readBindingCandidate(directory, name) {
|
|
2020
2979
|
try {
|
|
2021
|
-
const value = JSON.parse(await readPrivateFile(
|
|
2980
|
+
const value = JSON.parse(await readPrivateFile(join3(directory, name)));
|
|
2022
2981
|
validateBinding(value);
|
|
2023
2982
|
return value;
|
|
2024
2983
|
} catch {
|
|
@@ -2037,18 +2996,18 @@ async function writeAtomicJson(path, value) {
|
|
|
2037
2996
|
if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
|
|
2038
2997
|
const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
2039
2998
|
try {
|
|
2040
|
-
const handle = await
|
|
2999
|
+
const handle = await open3(temp, "wx", 384);
|
|
2041
3000
|
try {
|
|
2042
3001
|
await handle.writeFile(text, "utf8");
|
|
2043
3002
|
await handle.sync();
|
|
2044
3003
|
} finally {
|
|
2045
3004
|
await handle.close();
|
|
2046
3005
|
}
|
|
2047
|
-
await
|
|
3006
|
+
await rename3(temp, path);
|
|
2048
3007
|
await syncDirectory(dirname(path));
|
|
2049
3008
|
} catch (error) {
|
|
2050
3009
|
try {
|
|
2051
|
-
await
|
|
3010
|
+
await unlink2(temp);
|
|
2052
3011
|
} catch {
|
|
2053
3012
|
}
|
|
2054
3013
|
if (error instanceof CodexHostAdapterError) throw error;
|
|
@@ -2057,7 +3016,7 @@ async function writeAtomicJson(path, value) {
|
|
|
2057
3016
|
}
|
|
2058
3017
|
async function syncDirectory(path) {
|
|
2059
3018
|
try {
|
|
2060
|
-
const handle = await
|
|
3019
|
+
const handle = await open3(path, "r");
|
|
2061
3020
|
try {
|
|
2062
3021
|
await handle.sync();
|
|
2063
3022
|
} finally {
|
|
@@ -2079,12 +3038,12 @@ async function readOptionalJson(path, validate) {
|
|
|
2079
3038
|
}
|
|
2080
3039
|
async function ensurePrivateOwnerDirectory(path) {
|
|
2081
3040
|
try {
|
|
2082
|
-
await
|
|
3041
|
+
await lstat2(path);
|
|
2083
3042
|
} catch (error) {
|
|
2084
3043
|
if (!isNotFound(error)) throw storageError("Codex owner state directory is unavailable.");
|
|
2085
|
-
await
|
|
3044
|
+
await mkdir3(path, { recursive: true, mode: 448 });
|
|
2086
3045
|
}
|
|
2087
|
-
const stats = await
|
|
3046
|
+
const stats = await lstat2(path);
|
|
2088
3047
|
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
2089
3048
|
if (!stats.isDirectory() || stats.isSymbolicLink()) throw storageError(`Codex owner state path is not a directory (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a directory mode (0o40xxx)")}).`);
|
|
2090
3049
|
if ((stats.mode & 63) !== 0) throw storageError(`Codex owner state directory is readable beyond its owner (${refusalFact("permission_bits", `0o${(stats.mode & 511).toString(8)}`, "no group or other bits")}).`);
|
|
@@ -2093,7 +3052,7 @@ async function ensurePrivateOwnerDirectory(path) {
|
|
|
2093
3052
|
async function readPrivateFile(path) {
|
|
2094
3053
|
let handle;
|
|
2095
3054
|
try {
|
|
2096
|
-
handle = await
|
|
3055
|
+
handle = await open3(path, O_RDONLY3 | O_NOFOLLOW2);
|
|
2097
3056
|
const stats = await handle.stat();
|
|
2098
3057
|
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
2099
3058
|
if (!stats.isFile()) throw storageError(`Codex owner state path is not a regular file (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a regular-file mode (0o100xxx)")}).`);
|
|
@@ -2105,7 +3064,7 @@ async function readPrivateFile(path) {
|
|
|
2105
3064
|
}
|
|
2106
3065
|
}
|
|
2107
3066
|
function validateEnvelope(value) {
|
|
2108
|
-
if (!
|
|
3067
|
+
if (!isRecord4(value)) throw storageError("Codex outbox entry is malformed.");
|
|
2109
3068
|
if (!sameKeys(value, ["schema_version", "identity", "host_input_ref", "input_kind", "prompt_ref", "observations", "origin_input", "submission_ref", "created_at", "expires_at"], ["request_ref", "codex_turn_id"])) throw storageError(`Codex outbox entry key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the outbox entry key set")}).`);
|
|
2110
3069
|
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}`)}).`);
|
|
2111
3070
|
validateIdentity2(value.identity);
|
|
@@ -2114,22 +3073,22 @@ function validateEnvelope(value) {
|
|
|
2114
3073
|
validateEnvelopeTimestamps(value);
|
|
2115
3074
|
}
|
|
2116
3075
|
function validateEnvelopeReferences(value) {
|
|
2117
|
-
if (!
|
|
3076
|
+
if (!SAFE_REF4.test(String(value.host_input_ref))) throw storageError("Codex outbox input reference is malformed.");
|
|
2118
3077
|
if (!isCealHostInputKind(value.input_kind)) throw storageError("Codex outbox input provenance is malformed.");
|
|
2119
|
-
if (!
|
|
2120
|
-
if (!
|
|
2121
|
-
if (value.request_ref !== void 0 && !
|
|
2122
|
-
if (value.codex_turn_id !== void 0 && !
|
|
3078
|
+
if (!SAFE_REF4.test(String(value.prompt_ref))) throw storageError("Codex outbox references are malformed.");
|
|
3079
|
+
if (!SAFE_REF4.test(String(value.submission_ref))) throw storageError("Codex outbox references are malformed.");
|
|
3080
|
+
if (value.request_ref !== void 0 && !SAFE_REF4.test(String(value.request_ref))) throw storageError("Codex outbox Request reference is malformed.");
|
|
3081
|
+
if (value.codex_turn_id !== void 0 && !SAFE_REF4.test(String(value.codex_turn_id))) throw storageError("Codex outbox turn identity is malformed.");
|
|
2123
3082
|
if (typeof value.origin_input !== "boolean") throw storageError("Codex outbox origin disposition is malformed.");
|
|
2124
3083
|
}
|
|
2125
3084
|
function validateRegistration(value) {
|
|
2126
|
-
if (!
|
|
3085
|
+
if (!isRecord4(value)) throw storageError("Codex transcript registration is malformed.");
|
|
2127
3086
|
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")}).`);
|
|
2128
3087
|
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}`)}).`);
|
|
2129
3088
|
validateRegistrationFields(value);
|
|
2130
3089
|
}
|
|
2131
3090
|
function validateRegistrationFields(value) {
|
|
2132
|
-
if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" &&
|
|
3091
|
+
if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" && SAFE_REF4.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 }])}).`);
|
|
2133
3092
|
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`)}).`);
|
|
2134
3093
|
if (typeof value.transcript_path !== "string") throw storageError(`Codex transcript registration path is malformed (${refusalFact("kind", typeof value.transcript_path, "string")}).`);
|
|
2135
3094
|
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}`)}).`);
|
|
@@ -2139,15 +3098,15 @@ function validRegistrationCwd(value) {
|
|
|
2139
3098
|
return value === void 0 || typeof value === "string" && isAbsolute2(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
|
|
2140
3099
|
}
|
|
2141
3100
|
function validateCursor(value) {
|
|
2142
|
-
if (!
|
|
3101
|
+
if (!isRecord4(value)) throw storageError("Codex transcript cursor is malformed.");
|
|
2143
3102
|
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")}).`);
|
|
2144
3103
|
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}`)}).`);
|
|
2145
3104
|
if (!validCursorSource(value) || !validCursorPosition(value) || !validCursorStatus(value)) throw storageError("Codex transcript cursor fields are malformed.");
|
|
2146
3105
|
if (value.active_task !== void 0 && !validActiveTask(value.active_task)) throw storageError("Codex transcript task cursor is malformed.");
|
|
2147
|
-
if (value.reason !== void 0 && !
|
|
3106
|
+
if (value.reason !== void 0 && !isHostTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
|
|
2148
3107
|
}
|
|
2149
3108
|
function validCursorSource(value) {
|
|
2150
|
-
return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" &&
|
|
3109
|
+
return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate)) && typeof value.transcript_path === "string" && value.transcript_path.length > 0 && typeof value.device === "string" && typeof value.inode === "string";
|
|
2151
3110
|
}
|
|
2152
3111
|
function validCursorPosition(value) {
|
|
2153
3112
|
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";
|
|
@@ -2156,23 +3115,23 @@ function validCursorStatus(value) {
|
|
|
2156
3115
|
return (value.status === "active" || value.status === "quarantined") && isTimestamp(value.updated_at);
|
|
2157
3116
|
}
|
|
2158
3117
|
function validActiveTask(value) {
|
|
2159
|
-
if (!
|
|
3118
|
+
if (!isRecord4(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;
|
|
2160
3119
|
return validActiveTaskIdentity(value) && validActiveTaskReferences(value) && validActiveTaskUsage(value);
|
|
2161
3120
|
}
|
|
2162
3121
|
function validActiveTaskIdentity(value) {
|
|
2163
|
-
return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" &&
|
|
3122
|
+
return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate)) && isTimestamp(value.started_at);
|
|
2164
3123
|
}
|
|
2165
3124
|
function validActiveTaskReferences(value) {
|
|
2166
3125
|
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);
|
|
2167
3126
|
}
|
|
2168
3127
|
function optionalSafeRef2(value) {
|
|
2169
|
-
return value === void 0 || typeof value === "string" &&
|
|
3128
|
+
return value === void 0 || typeof value === "string" && SAFE_REF4.test(value);
|
|
2170
3129
|
}
|
|
2171
3130
|
function validActiveTaskUsage(value) {
|
|
2172
3131
|
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");
|
|
2173
3132
|
}
|
|
2174
3133
|
function validTokenUsage(value) {
|
|
2175
|
-
if (!
|
|
3134
|
+
if (!isRecord4(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
|
|
2176
3135
|
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;
|
|
2177
3136
|
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);
|
|
2178
3137
|
}
|
|
@@ -2192,30 +3151,33 @@ function validateEnvelopeTimestamps(value) {
|
|
|
2192
3151
|
if (!isTimestamp(value.expires_at)) throw storageError("Codex outbox timestamps are malformed.");
|
|
2193
3152
|
}
|
|
2194
3153
|
function validateBinding(value) {
|
|
2195
|
-
if (!
|
|
3154
|
+
if (!isRecord4(value)) throw storageError("Codex turn binding is malformed.");
|
|
2196
3155
|
if (!sameKeys(value, ["schema_version", "identity", "prompt_ref", "request_ref", "codex_turn_ids", "bound_at", "expires_at"])) throw storageError(`Codex turn binding key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the turn binding key set")}).`);
|
|
2197
3156
|
if (value.schema_version !== CODEX_TURN_BINDING_SCHEMA_VERSION) throw storageError(`Codex turn binding is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_TURN_BINDING_SCHEMA_VERSION}`)}).`);
|
|
2198
3157
|
validateIdentity2(value.identity);
|
|
2199
|
-
if (!
|
|
3158
|
+
if (!SAFE_REF4.test(String(value.prompt_ref)) || !SAFE_REF4.test(String(value.request_ref))) throw storageError("Codex turn binding references are malformed.");
|
|
2200
3159
|
if (!isStringList(value.codex_turn_ids) || !isTimestamp(value.bound_at) || !isTimestamp(value.expires_at)) throw storageError("Codex turn binding fields are malformed.");
|
|
2201
3160
|
}
|
|
2202
3161
|
function validateSession(value) {
|
|
2203
|
-
if (!
|
|
3162
|
+
if (!isRecord4(value)) throw storageError("Codex session snapshot is malformed.");
|
|
2204
3163
|
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")}).`);
|
|
2205
3164
|
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}`)}).`);
|
|
2206
|
-
if (!
|
|
3165
|
+
if (!SAFE_REF4.test(String(value.host_session_ref)) || !SAFE_REF4.test(String(value.host_source_ref)) || !isTimestamp(value.observed_at) || !isTimestamp(value.expires_at) || !isJsonValue(value.value)) throw storageError("Codex session snapshot fields are malformed.");
|
|
3166
|
+
}
|
|
3167
|
+
function validateTurnSelection(value) {
|
|
3168
|
+
if (!isHostInvocationTurnSelection(value)) throw storageError("Codex Host turn selection is malformed.");
|
|
2207
3169
|
}
|
|
2208
3170
|
function validateIdentity2(value) {
|
|
2209
|
-
if (!
|
|
3171
|
+
if (!isRecord4(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate))) throw storageError("Codex Host identity is malformed.");
|
|
2210
3172
|
}
|
|
2211
3173
|
function isJsonValue(value) {
|
|
2212
3174
|
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
2213
3175
|
if (typeof value === "number") return Number.isFinite(value);
|
|
2214
3176
|
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
2215
|
-
return
|
|
3177
|
+
return isRecord4(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
|
|
2216
3178
|
}
|
|
2217
3179
|
function isStringList(value) {
|
|
2218
|
-
return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" &&
|
|
3180
|
+
return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate));
|
|
2219
3181
|
}
|
|
2220
3182
|
function isTimestamp(value) {
|
|
2221
3183
|
return typeof value === "string" && value.length <= 64 && !Number.isNaN(Date.parse(value));
|
|
@@ -2225,23 +3187,23 @@ function sameKeys(value, required, optional = []) {
|
|
|
2225
3187
|
const keys = Object.keys(value);
|
|
2226
3188
|
return keys.length >= required.length && keys.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
|
|
2227
3189
|
}
|
|
2228
|
-
function
|
|
3190
|
+
function isRecord4(value) {
|
|
2229
3191
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2230
3192
|
}
|
|
2231
3193
|
function isNotFound(error) {
|
|
2232
|
-
return
|
|
3194
|
+
return isRecord4(error) && error.code === "ENOENT";
|
|
2233
3195
|
}
|
|
2234
3196
|
function storageError(message) {
|
|
2235
3197
|
return new CodexHostAdapterError("storage", message);
|
|
2236
3198
|
}
|
|
2237
3199
|
|
|
2238
3200
|
// host/codex/src/codex-transcript-baseline.ts
|
|
2239
|
-
import { createHash as
|
|
2240
|
-
import { O_NOFOLLOW as
|
|
2241
|
-
import { open as
|
|
3201
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3202
|
+
import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
|
|
3203
|
+
import { open as open4 } from "node:fs/promises";
|
|
2242
3204
|
|
|
2243
3205
|
// host/codex/src/codex-transcript-decoder.ts
|
|
2244
|
-
var
|
|
3206
|
+
var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
|
|
2245
3207
|
var MAX_PROMPT_BYTES = 64 * 1024;
|
|
2246
3208
|
function decodeCodexTranscriptLine(line) {
|
|
2247
3209
|
let value;
|
|
@@ -2250,40 +3212,40 @@ function decodeCodexTranscriptLine(line) {
|
|
|
2250
3212
|
} catch {
|
|
2251
3213
|
reject2("Codex transcript record is not valid JSON.");
|
|
2252
3214
|
}
|
|
2253
|
-
if (!
|
|
3215
|
+
if (!isRecord5(value)) reject2("Codex transcript record envelope is malformed.");
|
|
2254
3216
|
const ordinal = safeOrdinal(value.ordinal);
|
|
2255
|
-
const
|
|
2256
|
-
if (!
|
|
2257
|
-
if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal,
|
|
2258
|
-
if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal,
|
|
2259
|
-
if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal,
|
|
2260
|
-
if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal,
|
|
2261
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
2262
|
-
}
|
|
2263
|
-
function decodeTurnContext(payload, ordinal,
|
|
3217
|
+
const timestamp5 = safeTimestamp(value.timestamp);
|
|
3218
|
+
if (!isRecord5(value.payload) || typeof value.type !== "string") reject2("Codex transcript record payload is malformed.");
|
|
3219
|
+
if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal, timestamp5);
|
|
3220
|
+
if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal, timestamp5);
|
|
3221
|
+
if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal, timestamp5);
|
|
3222
|
+
if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal, timestamp5);
|
|
3223
|
+
return { kind: "ignored", ordinal, timestamp: timestamp5 };
|
|
3224
|
+
}
|
|
3225
|
+
function decodeTurnContext(payload, ordinal, timestamp5) {
|
|
2264
3226
|
return {
|
|
2265
3227
|
kind: "turn_context",
|
|
2266
3228
|
ordinal,
|
|
2267
|
-
timestamp:
|
|
2268
|
-
turn_id:
|
|
3229
|
+
timestamp: timestamp5,
|
|
3230
|
+
turn_id: safeRef4(payload.turn_id, "Codex turn context identity is invalid."),
|
|
2269
3231
|
model: boundedNonEmptyText(payload.model, 256, "Codex turn context model is invalid.")
|
|
2270
3232
|
};
|
|
2271
3233
|
}
|
|
2272
|
-
function decodeSessionMeta(payload, ordinal,
|
|
2273
|
-
const sessionId =
|
|
3234
|
+
function decodeSessionMeta(payload, ordinal, timestamp5) {
|
|
3235
|
+
const sessionId = safeRef4(payload.id, "Codex transcript session identity is invalid.");
|
|
2274
3236
|
const cliVersion = boundedNonEmptyText(payload.cli_version, 64, "Codex transcript version is invalid.");
|
|
2275
|
-
return { kind: "session_meta", ordinal, timestamp:
|
|
3237
|
+
return { kind: "session_meta", ordinal, timestamp: timestamp5, session_id: sessionId, cli_version: cliVersion };
|
|
2276
3238
|
}
|
|
2277
|
-
function decodeEventMessage(payload, ordinal,
|
|
3239
|
+
function decodeEventMessage(payload, ordinal, timestamp5) {
|
|
2278
3240
|
if (payload.type === "task_started") {
|
|
2279
|
-
return { kind: "task_started", ordinal, timestamp:
|
|
3241
|
+
return { kind: "task_started", ordinal, timestamp: timestamp5, turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."), started_at: safeNumber(payload.started_at) };
|
|
2280
3242
|
}
|
|
2281
3243
|
if (payload.type === "task_complete") {
|
|
2282
3244
|
return {
|
|
2283
3245
|
kind: "task_complete",
|
|
2284
3246
|
ordinal,
|
|
2285
|
-
timestamp:
|
|
2286
|
-
turn_id:
|
|
3247
|
+
timestamp: timestamp5,
|
|
3248
|
+
turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
|
|
2287
3249
|
started_at: safeNumber(payload.started_at),
|
|
2288
3250
|
completed_at: safeNumber(payload.completed_at),
|
|
2289
3251
|
duration_ms: safeNumber(payload.duration_ms),
|
|
@@ -2294,20 +3256,20 @@ function decodeEventMessage(payload, ordinal, timestamp6) {
|
|
|
2294
3256
|
return {
|
|
2295
3257
|
kind: "turn_aborted",
|
|
2296
3258
|
ordinal,
|
|
2297
|
-
timestamp:
|
|
2298
|
-
turn_id:
|
|
3259
|
+
timestamp: timestamp5,
|
|
3260
|
+
turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
|
|
2299
3261
|
started_at: safeNumber(payload.started_at),
|
|
2300
3262
|
completed_at: safeNumber(payload.completed_at),
|
|
2301
3263
|
duration_ms: safeNumber(payload.duration_ms),
|
|
2302
3264
|
reason: boundedText(payload.reason, 256, "Codex task abort reason is invalid.")
|
|
2303
3265
|
};
|
|
2304
3266
|
}
|
|
2305
|
-
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal,
|
|
2306
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
3267
|
+
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp5);
|
|
3268
|
+
return { kind: "ignored", ordinal, timestamp: timestamp5 };
|
|
2307
3269
|
}
|
|
2308
|
-
function decodeTokenCount(payload, ordinal,
|
|
2309
|
-
if (payload.info === null) return { kind: "token_count", ordinal, timestamp:
|
|
2310
|
-
if (!
|
|
3270
|
+
function decodeTokenCount(payload, ordinal, timestamp5) {
|
|
3271
|
+
if (payload.info === null) return { kind: "token_count", ordinal, timestamp: timestamp5 };
|
|
3272
|
+
if (!isRecord5(payload.info)) return { kind: "token_count", ordinal, timestamp: timestamp5, usage_gap_reason: "token_count_shape_not_attributable" };
|
|
2311
3273
|
const lastUsage = optionalTokenUsage(payload.info.last_token_usage);
|
|
2312
3274
|
const totalUsage = optionalTokenUsage(payload.info.total_token_usage);
|
|
2313
3275
|
const gap = lastUsage.status === "invalid" || totalUsage.status === "invalid";
|
|
@@ -2315,7 +3277,7 @@ function decodeTokenCount(payload, ordinal, timestamp6) {
|
|
|
2315
3277
|
return {
|
|
2316
3278
|
kind: "token_count",
|
|
2317
3279
|
ordinal,
|
|
2318
|
-
timestamp:
|
|
3280
|
+
timestamp: timestamp5,
|
|
2319
3281
|
...lastUsage.status === "valid" ? { last_usage: lastUsage.value } : {},
|
|
2320
3282
|
...totalUsage.status === "valid" ? { total_usage: totalUsage.value } : {},
|
|
2321
3283
|
...usageGapReason === void 0 ? {} : { usage_gap_reason: usageGapReason }
|
|
@@ -2331,7 +3293,7 @@ function optionalTokenUsage(value) {
|
|
|
2331
3293
|
}
|
|
2332
3294
|
}
|
|
2333
3295
|
function decodeTokenUsage(value) {
|
|
2334
|
-
if (!
|
|
3296
|
+
if (!isRecord5(value)) reject2("Codex token usage is malformed.");
|
|
2335
3297
|
const usage = {
|
|
2336
3298
|
input_tokens: safeTokenCount(value.input_tokens),
|
|
2337
3299
|
cached_input_tokens: safeTokenCount(value.cached_input_tokens),
|
|
@@ -2343,20 +3305,20 @@ function decodeTokenUsage(value) {
|
|
|
2343
3305
|
if (usage.total_tokens !== usage.input_tokens + usage.output_tokens || usage.cached_input_tokens + usage.cache_write_input_tokens > usage.input_tokens || usage.reasoning_output_tokens > usage.output_tokens) reject2("Codex token usage buckets are inconsistent.");
|
|
2344
3306
|
return usage;
|
|
2345
3307
|
}
|
|
2346
|
-
function decodeResponseItem(payload, ordinal,
|
|
2347
|
-
if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp:
|
|
3308
|
+
function decodeResponseItem(payload, ordinal, timestamp5) {
|
|
3309
|
+
if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp5 };
|
|
2348
3310
|
const metadata = userMessageMetadata(payload.internal_chat_message_metadata_passthrough);
|
|
2349
3311
|
const inputKind = inputKindFromContentItems(metadata.content_item_kinds);
|
|
2350
|
-
if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp:
|
|
3312
|
+
if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp5 };
|
|
2351
3313
|
const prompt = userTextContent(payload.content);
|
|
2352
3314
|
return {
|
|
2353
3315
|
kind: "user_text",
|
|
2354
3316
|
ordinal,
|
|
2355
|
-
timestamp:
|
|
3317
|
+
timestamp: timestamp5,
|
|
2356
3318
|
prompt,
|
|
2357
3319
|
input_kind: inputKind,
|
|
2358
|
-
message_id:
|
|
2359
|
-
turn_id:
|
|
3320
|
+
message_id: safeRef4(payload.id, "Codex user message identity is invalid."),
|
|
3321
|
+
turn_id: safeRef4(metadata.turn_id, "Codex user message task identity is invalid.")
|
|
2360
3322
|
};
|
|
2361
3323
|
}
|
|
2362
3324
|
function inputKindFromContentItems(kinds) {
|
|
@@ -2365,13 +3327,13 @@ function inputKindFromContentItems(kinds) {
|
|
|
2365
3327
|
return kinds[0] === "goal.internal_context" ? "host_automatic" : void 0;
|
|
2366
3328
|
}
|
|
2367
3329
|
function userMessageMetadata(value) {
|
|
2368
|
-
if (!
|
|
3330
|
+
if (!isRecord5(value) || !Array.isArray(value.content_item_kinds)) reject2("Codex user message provenance is malformed.");
|
|
2369
3331
|
return { ...value, content_item_kinds: value.content_item_kinds };
|
|
2370
3332
|
}
|
|
2371
3333
|
function userTextContent(value) {
|
|
2372
3334
|
if (!Array.isArray(value)) reject2(`Codex user text content is malformed (${refusalFact("kind", typeof value, "array")}).`);
|
|
2373
3335
|
if (value.length !== 1) reject2(`Codex user text content is malformed (${refusalFact("items", value.length, "exactly one")}).`);
|
|
2374
|
-
if (!
|
|
3336
|
+
if (!isRecord5(value[0]) || value[0].type !== "input_text") reject2(`Codex user text content is malformed (${refusalFact("item_type", isRecord5(value[0]) ? value[0].type : typeof value[0], "input_text")}).`);
|
|
2375
3337
|
return boundedText(value[0].text, MAX_PROMPT_BYTES, "Codex user text is invalid.");
|
|
2376
3338
|
}
|
|
2377
3339
|
function safeOrdinal(value) {
|
|
@@ -2392,8 +3354,8 @@ function safeTimestamp(value) {
|
|
|
2392
3354
|
if (!Number.isFinite(Date.parse(value))) reject2(`Codex transcript timestamp is invalid (${refusalFact("parsed_ms", Date.parse(value), "a finite epoch milliseconds value")}).`);
|
|
2393
3355
|
return value;
|
|
2394
3356
|
}
|
|
2395
|
-
function
|
|
2396
|
-
if (typeof value !== "string" || !
|
|
3357
|
+
function safeRef4(value, message) {
|
|
3358
|
+
if (typeof value !== "string" || !SAFE_REF5.test(value)) reject2(message);
|
|
2397
3359
|
return value;
|
|
2398
3360
|
}
|
|
2399
3361
|
function boundedText(value, maximum, message) {
|
|
@@ -2407,7 +3369,7 @@ function boundedNonEmptyText(value, maximum, message) {
|
|
|
2407
3369
|
if (result.length === 0) reject2(message);
|
|
2408
3370
|
return result;
|
|
2409
3371
|
}
|
|
2410
|
-
function
|
|
3372
|
+
function isRecord5(value) {
|
|
2411
3373
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2412
3374
|
}
|
|
2413
3375
|
function reject2(message) {
|
|
@@ -2420,7 +3382,7 @@ var CODEX_TRANSCRIPT_ANCHOR_BYTES = 256;
|
|
|
2420
3382
|
async function captureCodexTranscriptBaseline(registration, now = () => /* @__PURE__ */ new Date()) {
|
|
2421
3383
|
let handle;
|
|
2422
3384
|
try {
|
|
2423
|
-
handle = await
|
|
3385
|
+
handle = await open4(registration.transcript_path, O_RDONLY4 | O_NOFOLLOW3);
|
|
2424
3386
|
const { size, device, inode } = await ownedCodexTranscriptFile(handle);
|
|
2425
3387
|
const empty = codexTranscriptInitialCursor(registration, device, inode, now);
|
|
2426
3388
|
const headerLine = await firstCompleteLine(handle, size);
|
|
@@ -2440,7 +3402,7 @@ async function captureCodexTranscriptBaseline(registration, now = () => /* @__PU
|
|
|
2440
3402
|
anchor_start: anchorStart,
|
|
2441
3403
|
anchor_sha256: digest(anchor),
|
|
2442
3404
|
header_validated: true,
|
|
2443
|
-
updated_at:
|
|
3405
|
+
updated_at: timestamp3(now)
|
|
2444
3406
|
};
|
|
2445
3407
|
} finally {
|
|
2446
3408
|
await handle?.close();
|
|
@@ -2460,7 +3422,7 @@ function codexTranscriptInitialCursor(registration, device, inode, now) {
|
|
|
2460
3422
|
anchor_sha256: digest(Buffer.alloc(0)),
|
|
2461
3423
|
header_validated: false,
|
|
2462
3424
|
status: "active",
|
|
2463
|
-
updated_at:
|
|
3425
|
+
updated_at: timestamp3(now)
|
|
2464
3426
|
};
|
|
2465
3427
|
}
|
|
2466
3428
|
async function ownedCodexTranscriptFile(handle) {
|
|
@@ -2561,168 +3523,13 @@ async function* completeLinesBackward(handle, end) {
|
|
|
2561
3523
|
}
|
|
2562
3524
|
yield { start: 0, bytes: Buffer.concat(carry) };
|
|
2563
3525
|
}
|
|
2564
|
-
function digest(value) {
|
|
2565
|
-
return
|
|
2566
|
-
}
|
|
2567
|
-
function
|
|
2568
|
-
const value = now();
|
|
2569
|
-
if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
|
|
2570
|
-
return value.toISOString();
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
|
-
// host/relay/src/durable-atomic-replacement.ts
|
|
2574
|
-
import { O_RDONLY as O_RDONLY3 } from "node:constants";
|
|
2575
|
-
import { lstat as lstat2, mkdir as mkdir2, open as open3, rename as rename2, unlink as unlink2 } from "node:fs/promises";
|
|
2576
|
-
var DEFAULT_PORT = {
|
|
2577
|
-
platform: process.platform,
|
|
2578
|
-
mkdir: (path, options) => mkdir2(path, options),
|
|
2579
|
-
lstat: (path) => lstat2(path),
|
|
2580
|
-
open_file: (path) => open3(path, "wx", 384),
|
|
2581
|
-
rename: rename2,
|
|
2582
|
-
unlink: unlink2,
|
|
2583
|
-
open_directory: (path) => open3(path, O_RDONLY3),
|
|
2584
|
-
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
2585
|
-
};
|
|
2586
|
-
|
|
2587
|
-
// host/relay/src/producer-frame.ts
|
|
2588
|
-
var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
|
|
2589
|
-
var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
|
|
2590
|
-
var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
2591
|
-
var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2592
|
-
var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
|
|
2593
|
-
var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "user_input", "turn_terminal", "producer_status"];
|
|
2594
|
-
var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
|
|
2595
|
-
function isHostProducerFrameKind(value) {
|
|
2596
|
-
return typeof value === "string" && FRAME_KINDS.has(value);
|
|
2597
|
-
}
|
|
2598
|
-
var HostProducerFrameError = class extends Error {
|
|
2599
|
-
code;
|
|
2600
|
-
constructor(code, message = code) {
|
|
2601
|
-
super(message);
|
|
2602
|
-
this.code = code;
|
|
2603
|
-
}
|
|
2604
|
-
};
|
|
2605
|
-
function encodeHostProducerFrame(frame) {
|
|
2606
|
-
validateFrame(frame);
|
|
2607
|
-
const json = Buffer.from(JSON.stringify(frame), "utf8");
|
|
2608
|
-
if (json.byteLength > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("frame_bytes", json.byteLength, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
|
|
2609
|
-
const result = Buffer.allocUnsafe(json.byteLength + HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
2610
|
-
result.writeUInt32BE(json.byteLength, 0);
|
|
2611
|
-
json.copy(result, HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
2612
|
-
return result;
|
|
2613
|
-
}
|
|
2614
|
-
function decodeHostProducerFrame(input) {
|
|
2615
|
-
if (input.byteLength < HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError("frame_truncated", `frame_truncated: ${refusalFact("frame_bytes", input.byteLength, `at least ${HOST_PRODUCER_FRAME_LENGTH_BYTES}`)}`);
|
|
2616
|
-
const length = input.readUInt32BE(0);
|
|
2617
|
-
if (length > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("declared_bytes", length, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
|
|
2618
|
-
if (length === 0) throw new HostProducerFrameError("invalid_frame_length");
|
|
2619
|
-
if (input.byteLength !== length + HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError(input.byteLength < length + HOST_PRODUCER_FRAME_LENGTH_BYTES ? "frame_truncated" : "invalid_frame_length");
|
|
2620
|
-
const bytes = input.subarray(HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
2621
|
-
const text = new TextDecoder("utf-8", { fatal: true });
|
|
2622
|
-
let value;
|
|
2623
|
-
try {
|
|
2624
|
-
value = JSON.parse(text.decode(bytes));
|
|
2625
|
-
} catch (error) {
|
|
2626
|
-
if (error instanceof TypeError) throw new HostProducerFrameError("invalid_frame_encoding");
|
|
2627
|
-
throw new HostProducerFrameError("frame_truncated");
|
|
2628
|
-
}
|
|
2629
|
-
validateFrame(value);
|
|
2630
|
-
return value;
|
|
2631
|
-
}
|
|
2632
|
-
function validateFrame(value) {
|
|
2633
|
-
if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
|
|
2634
|
-
if (value.schema_version !== HOST_PRODUCER_FRAME_SCHEMA) throw new HostProducerFrameError("unsupported_frame_schema", `unsupported_frame_schema: ${refusalFact("schema_version", value.schema_version, HOST_PRODUCER_FRAME_SCHEMA)}`);
|
|
2635
|
-
if (!exactKeys3(value, ["schema_version", "producer_id", "producer_installation_ref", "producer_epoch", "sequence", "frame_kind", "occurred_at", "payload"])) throw new HostProducerFrameError("invalid_frame_shape");
|
|
2636
|
-
if (!PRODUCERS.has(String(value.producer_id)) || !safeRef3(value.producer_installation_ref)) throw new HostProducerFrameError("invalid_producer_identity");
|
|
2637
|
-
if (!positive(value.producer_epoch) || !positive(value.sequence) || !hostProducerFrameKind(value.frame_kind) || !timestamp3(value.occurred_at) || !record(value.payload)) throw new HostProducerFrameError("invalid_frame_shape");
|
|
2638
|
-
validatePayload(value.frame_kind, value.payload);
|
|
2639
|
-
}
|
|
2640
|
-
function validatePayload(kind, value) {
|
|
2641
|
-
const valid = kind === "session_started" ? validSession(value) : kind === "turn_started" ? validTurn(value) : kind === "user_input" ? validInput(value) : kind === "turn_terminal" ? validTerminal(value) : validStatus(value);
|
|
2642
|
-
if (!valid) throw new HostProducerFrameError("invalid_frame_shape");
|
|
2643
|
-
}
|
|
2644
|
-
function validSession(value) {
|
|
2645
|
-
return exactOptional(value, ["local_session_ref", "working_directory_ref"], ["model", "host_version", "observations"]) && safeRef3(value.local_session_ref) && safeRef3(value.working_directory_ref) && optionalText(value.model, 256) && optionalText(value.host_version, 256) && validObservations(value.observations);
|
|
2646
|
-
}
|
|
2647
|
-
function validTurn(value) {
|
|
2648
|
-
return exactOptional(value, ["local_session_ref", "local_turn_ref"], ["model", "observations"]) && safeRef3(value.local_session_ref) && safeRef3(value.local_turn_ref) && optionalText(value.model, 256) && validObservations(value.observations);
|
|
2649
|
-
}
|
|
2650
|
-
function validInput(value) {
|
|
2651
|
-
return exactOptional(value, ["local_session_ref", "local_turn_ref", "local_input_ref", "input_kind", "origin_input", "observations"], ["local_turn_aliases", "context_expires_at"]) && safeRef3(value.local_session_ref) && safeRef3(value.local_turn_ref) && safeRef3(value.local_input_ref) && validInputObservations(value.observations) && isCealHostInputKind(value.input_kind) && typeof value.origin_input === "boolean" && validInputOptionals(value);
|
|
2652
|
-
}
|
|
2653
|
-
function validInputObservations(value) {
|
|
2654
|
-
return validObservations(value) && Array.isArray(value) && value.filter((item) => record(item) && item.observation_kind === "prompt").length === 1;
|
|
2655
|
-
}
|
|
2656
|
-
function validInputOptionals(value) {
|
|
2657
|
-
return optionalRefs(value.local_turn_aliases) && (value.context_expires_at === void 0 || timestamp3(value.context_expires_at));
|
|
2658
|
-
}
|
|
2659
|
-
function validTerminal(value) {
|
|
2660
|
-
return exactOptional(value, ["local_session_ref", "local_turn_ref", "terminal_state"], ["abort_reason", "duration_ms", "time_to_first_token_ms", "usage", "observations"]) && safeRef3(value.local_session_ref) && safeRef3(value.local_turn_ref) && validTerminalDisposition(value.terminal_state, value.abort_reason) && optionalNonNegative(value.duration_ms) && optionalNonNegative(value.time_to_first_token_ms) && (value.usage === void 0 || jsonValue(value.usage)) && validObservations(value.observations);
|
|
2661
|
-
}
|
|
2662
|
-
function validTerminalDisposition(state, reason) {
|
|
2663
|
-
return (state === "completed" || state === "aborted") && (reason === void 0 || state === "aborted" && safeRef3(reason));
|
|
2664
|
-
}
|
|
2665
|
-
function validStatus(value) {
|
|
2666
|
-
return exactOptional(value, ["status", "reason_code"], ["detail"]) && ["ready", "degraded", "stopping"].includes(String(value.status)) && safeRef3(value.reason_code) && optionalText(value.detail, 4096);
|
|
2667
|
-
}
|
|
2668
|
-
function optionalText(value, maxLength) {
|
|
2669
|
-
return value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maxLength;
|
|
2670
|
-
}
|
|
2671
|
-
function optionalNonNegative(value) {
|
|
2672
|
-
return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
2673
|
-
}
|
|
2674
|
-
function optionalRefs(value) {
|
|
2675
|
-
return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef3);
|
|
2676
|
-
}
|
|
2677
|
-
function validObservations(value) {
|
|
2678
|
-
if (value === void 0) return true;
|
|
2679
|
-
if (!Array.isArray(value) || value.length > 32) return false;
|
|
2680
|
-
return value.every((item) => {
|
|
2681
|
-
try {
|
|
2682
|
-
validateCealHostObservationDraft(item);
|
|
2683
|
-
return true;
|
|
2684
|
-
} catch {
|
|
2685
|
-
return false;
|
|
2686
|
-
}
|
|
2687
|
-
});
|
|
2688
|
-
}
|
|
2689
|
-
function jsonValue(value, depth = 0) {
|
|
2690
|
-
if (depth > 16) return false;
|
|
2691
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
2692
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
2693
|
-
if (Array.isArray(value)) return value.length <= 256 && value.every((item) => jsonValue(item, depth + 1));
|
|
2694
|
-
return record(value) && Object.keys(value).length <= 256 && Object.entries(value).every(([key, item]) => key.length > 0 && key.length <= 128 && jsonValue(item, depth + 1));
|
|
2695
|
-
}
|
|
2696
|
-
function record(value) {
|
|
2697
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2698
|
-
}
|
|
2699
|
-
function safeRef3(value) {
|
|
2700
|
-
return typeof value === "string" && SAFE_REF5.test(value);
|
|
2701
|
-
}
|
|
2702
|
-
function positive(value) {
|
|
2703
|
-
return Number.isSafeInteger(value) && Number(value) > 0;
|
|
2704
|
-
}
|
|
2705
|
-
function timestamp3(value) {
|
|
2706
|
-
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
2707
|
-
}
|
|
2708
|
-
function hostProducerFrameKind(value) {
|
|
2709
|
-
return isHostProducerFrameKind(value);
|
|
2710
|
-
}
|
|
2711
|
-
function exactKeys3(value, keys) {
|
|
2712
|
-
return JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
|
|
2713
|
-
}
|
|
2714
|
-
function exactOptional(value, required, optional) {
|
|
2715
|
-
const keys = Object.keys(value);
|
|
2716
|
-
return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2717
|
-
}
|
|
2718
|
-
|
|
2719
|
-
// host/relay/src/relay-request-binding-store.ts
|
|
2720
|
-
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
2721
|
-
|
|
2722
|
-
// host/relay/src/relay-gateway-bridge.ts
|
|
2723
|
-
var HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS = ["created", "adopted", "replayed", "approval_approved", "approval_denied", "approval_replayed"];
|
|
2724
|
-
function isHostGatewayAcknowledgementDisposition(value) {
|
|
2725
|
-
return typeof value === "string" && HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS.some((disposition) => disposition === value);
|
|
3526
|
+
function digest(value) {
|
|
3527
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
3528
|
+
}
|
|
3529
|
+
function timestamp3(now) {
|
|
3530
|
+
const value = now();
|
|
3531
|
+
if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
|
|
3532
|
+
return value.toISOString();
|
|
2726
3533
|
}
|
|
2727
3534
|
|
|
2728
3535
|
// host/codex/src/codex-adapter.ts
|
|
@@ -2748,7 +3555,7 @@ function createCodexHostObservationAdapter(options) {
|
|
|
2748
3555
|
});
|
|
2749
3556
|
}
|
|
2750
3557
|
async function registerSession(event, options, store, now, retentionMs) {
|
|
2751
|
-
const defaultRoot =
|
|
3558
|
+
const defaultRoot = join4(process.env.CODEX_HOME || join4(homedir(), ".codex"), "sessions");
|
|
2752
3559
|
const transcriptPath = sourceBoundTranscriptPath(event.transcript_path, options.codex_sessions_root ?? defaultRoot);
|
|
2753
3560
|
const hostSessionRef = hostSessionReference(options.host_source_ref, event.session_id);
|
|
2754
3561
|
const observedAt = timestamp4(now);
|
|
@@ -2763,20 +3570,41 @@ async function registerSession(event, options, store, now, retentionMs) {
|
|
|
2763
3570
|
registered_at: observedAt,
|
|
2764
3571
|
expires_at: expiresAt
|
|
2765
3572
|
};
|
|
3573
|
+
const currentTurn = await selectCurrentTurn(event, options, store, hostSessionRef);
|
|
2766
3574
|
const existing = (await store.listTranscriptRegistrations()).find((candidate) => candidate.host_source_ref === registration.host_source_ref && candidate.host_session_ref === registration.host_session_ref);
|
|
2767
3575
|
if (existing === void 0) {
|
|
2768
3576
|
await store.writeTranscriptCursor(await captureCodexTranscriptBaseline(registration, now));
|
|
2769
3577
|
}
|
|
2770
3578
|
await store.registerTranscript(registration);
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
3579
|
+
if (event.event === "SessionStart") {
|
|
3580
|
+
await store.saveSession({
|
|
3581
|
+
schema_version: "ceal.codex_session.v1",
|
|
3582
|
+
host_session_ref: hostSessionRef,
|
|
3583
|
+
host_source_ref: options.host_source_ref,
|
|
3584
|
+
observed_at: observedAt,
|
|
3585
|
+
value: sessionObservationValue(event),
|
|
3586
|
+
expires_at: expiresAt
|
|
3587
|
+
});
|
|
3588
|
+
} else if (currentTurn !== void 0) {
|
|
3589
|
+
await store.attachInvocationTurnGeneration({ host_source_ref: options.host_source_ref, host_session_ref: hostSessionRef, host_turn_ref: currentTurn.host_turn_ref }, currentTurn.prompt_ref, currentTurn.turn_generation_ref);
|
|
3590
|
+
}
|
|
3591
|
+
return { event: event.event, disposition: "ignored", reason: "transcript_registered", ...currentTurn === void 0 ? {} : { host_turn_ref: currentTurn.host_turn_ref } };
|
|
3592
|
+
}
|
|
3593
|
+
async function selectCurrentTurn(event, options, store, hostSessionRef) {
|
|
3594
|
+
if (event.event !== "UserPromptSubmit") return void 0;
|
|
3595
|
+
const currentTurn = {
|
|
3596
|
+
host_turn_ref: hostTurnReference(options.host_source_ref, hostSessionRef, event.turn_id),
|
|
3597
|
+
prompt_ref: hostProducerPromptRef(promptObservationValue(event.prompt).body_sha256),
|
|
3598
|
+
turn_generation_ref: options.random_uuid?.() ?? randomUUID()
|
|
3599
|
+
};
|
|
3600
|
+
await store.writeInvocationTurnSelection({
|
|
3601
|
+
schema_version: "ceal.host_invocation_turn_selection.v1",
|
|
2774
3602
|
host_source_ref: options.host_source_ref,
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
3603
|
+
host_session_ref: hostSessionRef,
|
|
3604
|
+
prompt_ref: currentTurn.prompt_ref,
|
|
3605
|
+
turn_generation_ref: currentTurn.turn_generation_ref
|
|
2778
3606
|
});
|
|
2779
|
-
return
|
|
3607
|
+
return currentTurn;
|
|
2780
3608
|
}
|
|
2781
3609
|
function sourceBoundTranscriptPath(path, sessionsRoot) {
|
|
2782
3610
|
if (!isAbsolute3(path) || !isAbsolute3(sessionsRoot)) throw new CodexHostAdapterError("configuration", "Codex transcript registration paths must be absolute.");
|
|
@@ -2884,7 +3712,7 @@ async function submitWithDeadline(gateway, submission, timeoutMs, signal) {
|
|
|
2884
3712
|
});
|
|
2885
3713
|
try {
|
|
2886
3714
|
const acknowledgement = await Promise.race([gateway.submitHostTurn(submission, controller.signal), timeout]);
|
|
2887
|
-
if (!
|
|
3715
|
+
if (!isRecord6(acknowledgement) || !SAFE_REF6.test(String(acknowledgement.request_ref)) || !isHostGatewayAcknowledgementDisposition(acknowledgement.disposition)) {
|
|
2888
3716
|
throw new CodexHostAdapterError("payload_rejected", "Gateway Host acknowledgement is invalid.");
|
|
2889
3717
|
}
|
|
2890
3718
|
return acknowledgement;
|
|
@@ -2914,12 +3742,12 @@ function timestamp4(now) {
|
|
|
2914
3742
|
return value.toISOString();
|
|
2915
3743
|
}
|
|
2916
3744
|
function validateOptions(options) {
|
|
2917
|
-
if (!
|
|
3745
|
+
if (!isRecord6(options)) throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
|
|
2918
3746
|
validateAdapterOwner(options);
|
|
2919
3747
|
validateAdapterBehavior(options);
|
|
2920
3748
|
}
|
|
2921
3749
|
function validateAdapterOwner(options) {
|
|
2922
|
-
if (!SAFE_REF6.test(options.host_source_ref) || options.outbox_dir.length === 0 || !
|
|
3750
|
+
if (!SAFE_REF6.test(options.host_source_ref) || options.outbox_dir.length === 0 || !isRecord6(options.gateway) || typeof options.gateway.submitHostTurn !== "function") {
|
|
2923
3751
|
throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
|
|
2924
3752
|
}
|
|
2925
3753
|
if (options.codex_sessions_root !== void 0 && !isAbsolute3(options.codex_sessions_root)) throw new CodexHostAdapterError("configuration", "Codex sessions root is invalid.");
|
|
@@ -2931,7 +3759,7 @@ function validateAdapterBehavior(options) {
|
|
|
2931
3759
|
function safeReason(error) {
|
|
2932
3760
|
return error instanceof CodexHostAdapterError ? error.message.slice(0, 128) : "codex_session_registration_rejected";
|
|
2933
3761
|
}
|
|
2934
|
-
function
|
|
3762
|
+
function isRecord6(value) {
|
|
2935
3763
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2936
3764
|
}
|
|
2937
3765
|
|
|
@@ -2939,7 +3767,7 @@ function isRecord5(value) {
|
|
|
2939
3767
|
import { posix as posix3, win32 as win323 } from "node:path";
|
|
2940
3768
|
|
|
2941
3769
|
// host/runtime-source-paths.ts
|
|
2942
|
-
import { createHash as
|
|
3770
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
2943
3771
|
import { posix as posix2, win32 as win322 } from "node:path";
|
|
2944
3772
|
|
|
2945
3773
|
// host/absolute-path.ts
|
|
@@ -2961,7 +3789,7 @@ function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platfor
|
|
|
2961
3789
|
const sourcesRoot = path.join(stateRoot, "sources");
|
|
2962
3790
|
const sourceRoot = path.join(sourcesRoot, sourceId);
|
|
2963
3791
|
const runtimeRoot = path.join(stateRoot, "run");
|
|
2964
|
-
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${
|
|
3792
|
+
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash5("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
|
|
2965
3793
|
assertHostRuntimeIpcLength(endpoint, platform);
|
|
2966
3794
|
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 };
|
|
2967
3795
|
}
|
|
@@ -2977,7 +3805,7 @@ function assertHostProducerSourcePaths(stateRoot, config, producer, platform = p
|
|
|
2977
3805
|
|
|
2978
3806
|
// host/producer-service.ts
|
|
2979
3807
|
import { constants } from "node:fs";
|
|
2980
|
-
import { lstat as lstat3, open as
|
|
3808
|
+
import { lstat as lstat3, open as open5 } from "node:fs/promises";
|
|
2981
3809
|
var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2982
3810
|
var HOST_PRODUCER_CONFIG_FLAG = "--ceal-host-config";
|
|
2983
3811
|
var HostProducerServiceError = class extends Error {
|
|
@@ -3043,7 +3871,7 @@ async function readProducerConfigText(configPath, platform, error) {
|
|
|
3043
3871
|
}
|
|
3044
3872
|
function decodeProducerConfigCore(text, options) {
|
|
3045
3873
|
const value = parseCanonicalJson(text, options.error);
|
|
3046
|
-
if (!
|
|
3874
|
+
if (!isRecord7(value) || !hasConfigKeys(value, options.keys) || value.schema_version !== options.schema) throw options.error("invalid_config");
|
|
3047
3875
|
const hostSourceRef = typeof value.host_source_ref === "string" ? value.host_source_ref : "";
|
|
3048
3876
|
const installationRef = typeof value.producer_installation_ref === "string" ? value.producer_installation_ref : "";
|
|
3049
3877
|
const producerEpoch = validateIdentity3(hostSourceRef, installationRef, value.producer_epoch, options.error);
|
|
@@ -3071,397 +3899,117 @@ function assertProducerRelayEndpoint(value, platform, error) {
|
|
|
3071
3899
|
if (platform === "win32" && typeof value === "string" && /^\\\\\.\\pipe\\[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(value)) return;
|
|
3072
3900
|
assertProducerAbsolutePath(value, platform, "runtime", error);
|
|
3073
3901
|
}
|
|
3074
|
-
function assertProducerAbsolutePath(value, platform, kind, error) {
|
|
3075
|
-
if (!isAbsoluteNormalizedNonRootPath(value, platform) || /[\r\n]/u.test(value)) throw error(kind === "config" ? "invalid_config_path" : "invalid_config", refusalFact("path", value, "absolute, normalized, non-root, and without line breaks"));
|
|
3076
|
-
}
|
|
3077
|
-
async function configPathStat(configPath, error) {
|
|
3078
|
-
try {
|
|
3079
|
-
return await lstat3(configPath);
|
|
3080
|
-
} catch {
|
|
3081
|
-
throw error("config_unavailable");
|
|
3082
|
-
}
|
|
3083
|
-
}
|
|
3084
|
-
function validateConfigFile(value, platform, error) {
|
|
3085
|
-
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" }]));
|
|
3086
|
-
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}`));
|
|
3087
|
-
if (platform !== "win32" && (value.uid !== process.getuid?.() || (value.mode & 63) !== 0)) throw error("config_not_private", refusalFacts([{ name: "platform", value: platform, expected: "win32 or owner-private" }, { name: "config_uid", value: value.uid, expected: "the current user" }, { name: "config_mode", value: value.mode, expected: "owner-private" }]));
|
|
3088
|
-
}
|
|
3089
|
-
async function openConfigFile(configPath, flags, error) {
|
|
3090
|
-
try {
|
|
3091
|
-
return await open4(configPath, flags);
|
|
3092
|
-
} catch {
|
|
3093
|
-
throw error("config_unavailable");
|
|
3094
|
-
}
|
|
3095
|
-
}
|
|
3096
|
-
function validateOpenedFile(opened, pathStat, platform, error) {
|
|
3097
|
-
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" }]));
|
|
3098
|
-
if (platform !== "win32" && (opened.dev !== pathStat.dev || opened.ino !== pathStat.ino)) throw error("config_unavailable", refusalFacts([{ name: "platform", value: platform, expected: "win32, or 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) }]));
|
|
3099
|
-
}
|
|
3100
|
-
async function readBoundedConfig(handle, expectedBytes, error) {
|
|
3101
|
-
const buffer = Buffer.allocUnsafe(expectedBytes + 1);
|
|
3102
|
-
let offset = 0;
|
|
3103
|
-
while (offset < buffer.byteLength) {
|
|
3104
|
-
const { bytesRead } = await handle.read(buffer, offset, buffer.byteLength - offset, offset);
|
|
3105
|
-
if (bytesRead === 0) break;
|
|
3106
|
-
offset += bytesRead;
|
|
3107
|
-
}
|
|
3108
|
-
if (offset !== expectedBytes) throw error("invalid_config", refusalFact("bytes_read", offset, `the opened file's ${expectedBytes} bytes without growth or truncation`));
|
|
3109
|
-
try {
|
|
3110
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, offset));
|
|
3111
|
-
} catch {
|
|
3112
|
-
throw error("invalid_config");
|
|
3113
|
-
}
|
|
3114
|
-
}
|
|
3115
|
-
function parseCanonicalJson(text, error) {
|
|
3116
|
-
try {
|
|
3117
|
-
const value = JSON.parse(text);
|
|
3118
|
-
const canonical = JSON.stringify(value);
|
|
3119
|
-
if (canonical !== text.trim()) throw new Error(`noncanonical: ${refusalFacts([{ name: "config_bytes", value: Buffer.byteLength(text.trim(), "utf8"), expected: `the ${Buffer.byteLength(canonical, "utf8")} bytes of its canonical form` }, { name: "first_difference_index", value: firstDifferenceIndex(text.trim(), canonical) }])}`);
|
|
3120
|
-
return value;
|
|
3121
|
-
} catch (parseError) {
|
|
3122
|
-
throw error("invalid_config", refusalFacts([{ name: "parse_error", value: parseError }, { name: "config_bytes", value: Buffer.byteLength(text, "utf8"), expected: "canonical JSON" }]));
|
|
3123
|
-
}
|
|
3124
|
-
}
|
|
3125
|
-
function firstDifferenceIndex(left, right) {
|
|
3126
|
-
const shared = Math.min(left.length, right.length);
|
|
3127
|
-
for (let index = 0; index < shared; index += 1) if (left[index] !== right[index]) return index;
|
|
3128
|
-
return shared;
|
|
3129
|
-
}
|
|
3130
|
-
function validateIdentity3(sourceRef, installationRef, epoch, error) {
|
|
3131
|
-
if (!SAFE_REF7.test(sourceRef) || !SAFE_REF7.test(installationRef) || !positive2(epoch, 2147483647)) throw error("invalid_config");
|
|
3132
|
-
return epoch;
|
|
3133
|
-
}
|
|
3134
|
-
function validateIntervals(reconcile, retry, timeout, error) {
|
|
3135
|
-
if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config");
|
|
3136
|
-
return [reconcile, retry, timeout];
|
|
3137
|
-
}
|
|
3138
|
-
function hasConfigKeys(value, required) {
|
|
3139
|
-
return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || Object.hasOwn(HOST_PRODUCER_TIMING_DEFAULTS, key));
|
|
3140
|
-
}
|
|
3141
|
-
function isRecord6(value) {
|
|
3142
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3143
|
-
}
|
|
3144
|
-
function positive2(value, maximum) {
|
|
3145
|
-
return Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= maximum;
|
|
3146
|
-
}
|
|
3147
|
-
function boundedMilliseconds(value) {
|
|
3148
|
-
return positive2(value, 6e4) && Number(value) >= 25;
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
// host/relay/src/relay-quarantine-store.ts
|
|
3152
|
-
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
3153
|
-
|
|
3154
|
-
// host/relay/src/relay-admission-store.ts
|
|
3155
|
-
var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
|
|
3156
|
-
var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
3157
|
-
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
3158
|
-
|
|
3159
|
-
// host/relay/src/relay-ipc.ts
|
|
3160
|
-
import { createConnection, createServer } from "node:net";
|
|
3161
|
-
|
|
3162
|
-
// host/relay/src/current-prompt-custody.ts
|
|
3163
|
-
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
3164
|
-
|
|
3165
|
-
// host/relay/src/relay-ipc.ts
|
|
3166
|
-
var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
3167
|
-
"schema_version",
|
|
3168
|
-
"producer_id",
|
|
3169
|
-
"producer_installation_ref",
|
|
3170
|
-
"local_session_ref",
|
|
3171
|
-
"local_turn_ref",
|
|
3172
|
-
"local_turn_aliases",
|
|
3173
|
-
"prompt_ref",
|
|
3174
|
-
"request_ref",
|
|
3175
|
-
"bound_at",
|
|
3176
|
-
"host_source_ref",
|
|
3177
|
-
"host_session_ref",
|
|
3178
|
-
"host_turn_ref",
|
|
3179
|
-
"host_input_ref"
|
|
3180
|
-
]);
|
|
3181
|
-
var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
|
|
3182
|
-
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
3183
|
-
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
3184
|
-
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
3185
|
-
var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
|
|
3186
|
-
var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
3187
|
-
"context_stale",
|
|
3188
|
-
"context_expired",
|
|
3189
|
-
"host_source_refused",
|
|
3190
|
-
"host_credential_refused",
|
|
3191
|
-
"gateway_unavailable",
|
|
3192
|
-
"gateway_refused",
|
|
3193
|
-
"gateway_response_invalid",
|
|
3194
|
-
"deadline",
|
|
3195
|
-
"cancelled",
|
|
3196
|
-
"mint_failed",
|
|
3197
|
-
"relay_failed",
|
|
3198
|
-
"request_authority_unavailable",
|
|
3199
|
-
"request_missing",
|
|
3200
|
-
"authority_mismatch",
|
|
3201
|
-
"host_source_mismatch",
|
|
3202
|
-
"host_session_mismatch",
|
|
3203
|
-
"host_turn_mismatch",
|
|
3204
|
-
"host_input_mismatch",
|
|
3205
|
-
"host_prompt_mismatch"
|
|
3206
|
-
]);
|
|
3207
|
-
var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
|
|
3208
|
-
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
3209
|
-
"permission_denied",
|
|
3210
|
-
"socket_missing",
|
|
3211
|
-
"connection_refused",
|
|
3212
|
-
"closed_early",
|
|
3213
|
-
"response_invalid",
|
|
3214
|
-
"timed_out",
|
|
3215
|
-
"cancelled",
|
|
3216
|
-
"unavailable"
|
|
3217
|
-
]);
|
|
3218
|
-
var HostRelayIpcError = class extends Error {
|
|
3219
|
-
code;
|
|
3220
|
-
/** Set only when the client-side socket exchange itself failed. */
|
|
3221
|
-
failure;
|
|
3222
|
-
constructor(code, message = code, failure) {
|
|
3223
|
-
super(message);
|
|
3224
|
-
this.code = code;
|
|
3225
|
-
this.failure = failure;
|
|
3226
|
-
}
|
|
3227
|
-
};
|
|
3228
|
-
function socketErrorFailure(error) {
|
|
3229
|
-
switch (errorCode(error)) {
|
|
3230
|
-
case "EACCES":
|
|
3231
|
-
case "EPERM":
|
|
3232
|
-
return "permission_denied";
|
|
3233
|
-
case "ENOENT":
|
|
3234
|
-
return "socket_missing";
|
|
3235
|
-
case "ECONNREFUSED":
|
|
3236
|
-
return "connection_refused";
|
|
3237
|
-
default:
|
|
3238
|
-
return "unavailable";
|
|
3239
|
-
}
|
|
3240
|
-
}
|
|
3241
|
-
var HostRelayIpcAdmissionPort = class {
|
|
3242
|
-
endpoint;
|
|
3243
|
-
timeout_ms;
|
|
3244
|
-
constructor(endpoint, timeoutMs = 5e3) {
|
|
3245
|
-
this.endpoint = endpoint;
|
|
3246
|
-
this.timeout_ms = timeoutMs;
|
|
3247
|
-
}
|
|
3248
|
-
async admit(frame) {
|
|
3249
|
-
const response2 = await submitHostProducerFrame(this.endpoint, frame, this.timeout_ms);
|
|
3250
|
-
if (!relayAdmissionCode(response2.code)) throw new HostRelayIpcError(response2.code);
|
|
3251
|
-
return { code: response2.code, admitted_through_sequence: response2.admitted_through_sequence, delivered_through_sequence: response2.delivered_through_sequence };
|
|
3252
|
-
}
|
|
3253
|
-
};
|
|
3254
|
-
var HostRelayCurrentPromptPort = class {
|
|
3255
|
-
endpoint;
|
|
3256
|
-
timeout_ms;
|
|
3257
|
-
constructor(endpoint, timeoutMs = 5e3) {
|
|
3258
|
-
this.endpoint = endpoint;
|
|
3259
|
-
this.timeout_ms = timeoutMs;
|
|
3260
|
-
}
|
|
3261
|
-
async register(input) {
|
|
3262
|
-
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
|
|
3263
|
-
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
3264
|
-
if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
|
|
3265
|
-
}
|
|
3266
|
-
};
|
|
3267
|
-
function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
|
|
3268
|
-
return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
|
|
3269
|
-
}
|
|
3270
|
-
function exchange(endpoint, encoded, timeoutMs, decode, signal) {
|
|
3271
|
-
return new Promise((resolve3, reject3) => {
|
|
3272
|
-
const socket = createConnection(endpoint);
|
|
3273
|
-
let buffer = Buffer.alloc(0);
|
|
3274
|
-
let settled = false;
|
|
3275
|
-
const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
|
|
3276
|
-
const finish = (error, response2) => {
|
|
3277
|
-
if (settled) return;
|
|
3278
|
-
settled = true;
|
|
3279
|
-
signal?.removeEventListener("abort", aborted);
|
|
3280
|
-
socket.destroy();
|
|
3281
|
-
if (error !== null) reject3(error);
|
|
3282
|
-
else if (response2 !== void 0) resolve3(response2);
|
|
3283
|
-
};
|
|
3284
|
-
if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
|
|
3285
|
-
if (signal?.aborted === true) {
|
|
3286
|
-
aborted();
|
|
3287
|
-
return;
|
|
3288
|
-
}
|
|
3289
|
-
signal?.addEventListener("abort", aborted, { once: true });
|
|
3290
|
-
socket.once("error", (error) => {
|
|
3291
|
-
const failure = socketErrorFailure(error);
|
|
3292
|
-
finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure}.`, failure));
|
|
3293
|
-
});
|
|
3294
|
-
socket.once("connect", () => socket.write(encoded));
|
|
3295
|
-
socket.on("data", (chunk) => {
|
|
3296
|
-
try {
|
|
3297
|
-
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
|
3298
|
-
const decoded = decode(buffer);
|
|
3299
|
-
if (decoded !== null) finish(null, decoded);
|
|
3300
|
-
} catch (error) {
|
|
3301
|
-
finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
|
|
3302
|
-
}
|
|
3303
|
-
});
|
|
3304
|
-
socket.once("end", () => {
|
|
3305
|
-
if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
|
|
3306
|
-
});
|
|
3307
|
-
});
|
|
3902
|
+
function assertProducerAbsolutePath(value, platform, kind, error) {
|
|
3903
|
+
if (!isAbsoluteNormalizedNonRootPath(value, platform) || /[\r\n]/u.test(value)) throw error(kind === "config" ? "invalid_config_path" : "invalid_config", refusalFact("path", value, "absolute, normalized, non-root, and without line breaks"));
|
|
3308
3904
|
}
|
|
3309
|
-
function
|
|
3310
|
-
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response is truncated: ${refusalFact("response_bytes", input.byteLength, `at least ${HOST_RELAY_IPC_LENGTH_BYTES}`)}.`);
|
|
3311
|
-
const length = input.readUInt32BE(0);
|
|
3312
|
-
if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES || input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
3313
|
-
let value;
|
|
3905
|
+
async function configPathStat(configPath, error) {
|
|
3314
3906
|
try {
|
|
3315
|
-
|
|
3907
|
+
return await lstat3(configPath);
|
|
3316
3908
|
} catch {
|
|
3317
|
-
throw
|
|
3909
|
+
throw error("config_unavailable");
|
|
3318
3910
|
}
|
|
3319
|
-
if (!validResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response shape is invalid.");
|
|
3320
|
-
return value;
|
|
3321
3911
|
}
|
|
3322
|
-
function
|
|
3323
|
-
|
|
3912
|
+
function validateConfigFile(value, platform, error) {
|
|
3913
|
+
if (!value.isFile() || value.isSymbolicLink()) throw error("invalid_config", refusalFacts([{ name: "config_is_file", value: value.isFile(), expected: "true" }, { name: "config_is_symlink", value: value.isSymbolicLink(), expected: "false" }]));
|
|
3914
|
+
if (value.size <= 0 || value.size > CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES) throw error("invalid_config", refusalFact("config_bytes", value.size, `between 1 and ${CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES}`));
|
|
3915
|
+
if (platform !== "win32" && (value.uid !== process.getuid?.() || (value.mode & 63) !== 0)) throw error("config_not_private", refusalFacts([{ name: "platform", value: platform, expected: "win32 or owner-private" }, { name: "config_uid", value: value.uid, expected: "the current user" }, { name: "config_mode", value: value.mode, expected: "owner-private" }]));
|
|
3324
3916
|
}
|
|
3325
|
-
function
|
|
3326
|
-
|
|
3917
|
+
async function openConfigFile(configPath, flags, error) {
|
|
3918
|
+
try {
|
|
3919
|
+
return await open5(configPath, flags);
|
|
3920
|
+
} catch {
|
|
3921
|
+
throw error("config_unavailable");
|
|
3922
|
+
}
|
|
3327
3923
|
}
|
|
3328
|
-
function
|
|
3329
|
-
|
|
3924
|
+
function validateOpenedFile(opened, pathStat, platform, error) {
|
|
3925
|
+
if (!opened.isFile()) throw error("config_unavailable", refusalFacts([{ name: "opened_is_file", value: opened.isFile(), expected: "true" }, { name: "opened_mode", value: opened.mode.toString(8), expected: "a regular file mode" }]));
|
|
3926
|
+
if (platform !== "win32" && (opened.dev !== pathStat.dev || opened.ino !== pathStat.ino)) throw error("config_unavailable", refusalFacts([{ name: "platform", value: platform, expected: "win32, or 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) }]));
|
|
3330
3927
|
}
|
|
3331
|
-
function
|
|
3332
|
-
|
|
3928
|
+
async function readBoundedConfig(handle, expectedBytes, error) {
|
|
3929
|
+
const buffer = Buffer.allocUnsafe(expectedBytes + 1);
|
|
3930
|
+
let offset = 0;
|
|
3931
|
+
while (offset < buffer.byteLength) {
|
|
3932
|
+
const { bytesRead } = await handle.read(buffer, offset, buffer.byteLength - offset, offset);
|
|
3933
|
+
if (bytesRead === 0) break;
|
|
3934
|
+
offset += bytesRead;
|
|
3935
|
+
}
|
|
3936
|
+
if (offset !== expectedBytes) throw error("invalid_config", refusalFact("bytes_read", offset, `the opened file's ${expectedBytes} bytes without growth or truncation`));
|
|
3937
|
+
try {
|
|
3938
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, offset));
|
|
3939
|
+
} catch {
|
|
3940
|
+
throw error("invalid_config");
|
|
3941
|
+
}
|
|
3333
3942
|
}
|
|
3334
|
-
function
|
|
3335
|
-
|
|
3943
|
+
function parseCanonicalJson(text, error) {
|
|
3944
|
+
try {
|
|
3945
|
+
const value = JSON.parse(text);
|
|
3946
|
+
const canonical = JSON.stringify(value);
|
|
3947
|
+
if (canonical !== text.trim()) throw new Error(`noncanonical: ${refusalFacts([{ name: "config_bytes", value: Buffer.byteLength(text.trim(), "utf8"), expected: `the ${Buffer.byteLength(canonical, "utf8")} bytes of its canonical form` }, { name: "first_difference_index", value: firstDifferenceIndex(text.trim(), canonical) }])}`);
|
|
3948
|
+
return value;
|
|
3949
|
+
} catch (parseError) {
|
|
3950
|
+
throw error("invalid_config", refusalFacts([{ name: "parse_error", value: parseError }, { name: "config_bytes", value: Buffer.byteLength(text, "utf8"), expected: "canonical JSON" }]));
|
|
3951
|
+
}
|
|
3336
3952
|
}
|
|
3337
|
-
function
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
if (buffer.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
3342
|
-
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}` }])}.`);
|
|
3343
|
-
return decodeHostRelayIpcResponse(buffer);
|
|
3953
|
+
function firstDifferenceIndex(left, right) {
|
|
3954
|
+
const shared = Math.min(left.length, right.length);
|
|
3955
|
+
for (let index = 0; index < shared; index += 1) if (left[index] !== right[index]) return index;
|
|
3956
|
+
return shared;
|
|
3344
3957
|
}
|
|
3345
|
-
function
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
if (!record2(value) || !exactKeys4(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "current_prompt_unavailable") throw new HostRelayIpcError("relay_unavailable");
|
|
3349
|
-
return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
|
|
3958
|
+
function validateIdentity3(sourceRef, installationRef, epoch, error) {
|
|
3959
|
+
if (!SAFE_REF7.test(sourceRef) || !SAFE_REF7.test(installationRef) || !positive2(epoch, 2147483647)) throw error("invalid_config");
|
|
3960
|
+
return epoch;
|
|
3350
3961
|
}
|
|
3351
|
-
function
|
|
3352
|
-
if (
|
|
3353
|
-
|
|
3354
|
-
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}`)}.`);
|
|
3355
|
-
if (input.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
3356
|
-
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}` }])}.`);
|
|
3357
|
-
try {
|
|
3358
|
-
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
|
|
3359
|
-
} catch {
|
|
3360
|
-
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC message is invalid.");
|
|
3361
|
-
}
|
|
3962
|
+
function validateIntervals(reconcile, retry, timeout, error) {
|
|
3963
|
+
if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config");
|
|
3964
|
+
return [reconcile, retry, timeout];
|
|
3362
3965
|
}
|
|
3363
|
-
function
|
|
3364
|
-
|
|
3365
|
-
const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
|
|
3366
|
-
output.writeUInt32BE(body.length, 0);
|
|
3367
|
-
body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
|
|
3368
|
-
return output;
|
|
3966
|
+
function hasConfigKeys(value, required) {
|
|
3967
|
+
return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || Object.hasOwn(HOST_PRODUCER_TIMING_DEFAULTS, key));
|
|
3369
3968
|
}
|
|
3370
|
-
function
|
|
3371
|
-
return
|
|
3969
|
+
function isRecord7(value) {
|
|
3970
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3372
3971
|
}
|
|
3373
|
-
function
|
|
3374
|
-
return
|
|
3972
|
+
function positive2(value, maximum) {
|
|
3973
|
+
return Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= maximum;
|
|
3974
|
+
}
|
|
3975
|
+
function boundedMilliseconds(value) {
|
|
3976
|
+
return positive2(value, 6e4) && Number(value) >= 25;
|
|
3375
3977
|
}
|
|
3376
3978
|
|
|
3377
3979
|
// host/codex/src/codex-producer.ts
|
|
3378
|
-
import { createHash as
|
|
3379
|
-
import { mkdir as mkdir3, readFile, rename as rename3, writeFile } from "node:fs/promises";
|
|
3380
|
-
import { join as join4 } from "node:path";
|
|
3381
|
-
var STATE_SCHEMA = "ceal.codex_producer_state.v1";
|
|
3980
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
3382
3981
|
var SAFE_REF8 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
3383
3982
|
var CodexProducer = class {
|
|
3983
|
+
producer_id = "codex";
|
|
3984
|
+
producer_label = "Codex";
|
|
3985
|
+
outbox_directory;
|
|
3384
3986
|
store;
|
|
3385
|
-
|
|
3386
|
-
installationRef;
|
|
3387
|
-
epoch;
|
|
3388
|
-
relay;
|
|
3389
|
-
now;
|
|
3390
|
-
tail = Promise.resolve();
|
|
3987
|
+
core;
|
|
3391
3988
|
constructor(options) {
|
|
3392
|
-
if (!SAFE_REF8.test(options.producer_installation_ref)) throw new CodexHostAdapterError("configuration", "Codex producer installation identity is invalid.");
|
|
3393
|
-
const epoch = options.producer_epoch ?? 1;
|
|
3394
|
-
if (!Number.isSafeInteger(epoch) || epoch <= 0) throw new CodexHostAdapterError("configuration", "Codex producer epoch is invalid.");
|
|
3395
3989
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
3396
|
-
this.
|
|
3397
|
-
this.
|
|
3398
|
-
this.epoch = epoch;
|
|
3399
|
-
this.relay = options.relay;
|
|
3400
|
-
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
3990
|
+
this.outbox_directory = this.store.directory;
|
|
3991
|
+
this.core = new HostProducerDrainCore(this, options);
|
|
3401
3992
|
}
|
|
3402
3993
|
drainOutbox() {
|
|
3403
|
-
|
|
3404
|
-
this.tail = run.then(() => void 0, () => void 0);
|
|
3405
|
-
return run;
|
|
3994
|
+
return this.core.drainOutbox();
|
|
3406
3995
|
}
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
let admitted = 0;
|
|
3410
|
-
let expired = 0;
|
|
3411
|
-
let retained = 0;
|
|
3412
|
-
for (const envelope of read.entries) {
|
|
3413
|
-
const reserved = await this.hasReservation(envelope.submission_ref);
|
|
3414
|
-
if (!reserved && Date.parse(envelope.expires_at) <= this.now().getTime()) {
|
|
3415
|
-
await this.store.remove(envelope);
|
|
3416
|
-
expired += 1;
|
|
3417
|
-
continue;
|
|
3418
|
-
}
|
|
3419
|
-
const frames = await this.reserve(envelope);
|
|
3420
|
-
let complete = true;
|
|
3421
|
-
for (const frame of frames) {
|
|
3422
|
-
const outcome = await this.relay.admit(frame);
|
|
3423
|
-
if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
|
|
3424
|
-
complete = false;
|
|
3425
|
-
break;
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
if (!complete) {
|
|
3429
|
-
retained += 1;
|
|
3430
|
-
break;
|
|
3431
|
-
}
|
|
3432
|
-
await this.store.remove(envelope);
|
|
3433
|
-
await this.release(envelope.submission_ref);
|
|
3434
|
-
admitted += 1;
|
|
3435
|
-
}
|
|
3436
|
-
return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
|
|
3996
|
+
fail(code, message) {
|
|
3997
|
+
return new CodexHostAdapterError(code, message);
|
|
3437
3998
|
}
|
|
3438
|
-
|
|
3439
|
-
return
|
|
3999
|
+
listEnvelopes() {
|
|
4000
|
+
return this.store.list();
|
|
3440
4001
|
}
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
const existing = state.pending[envelope.submission_ref];
|
|
3444
|
-
if (existing !== void 0) return existing;
|
|
3445
|
-
const drafts = await this.mapEnvelope(envelope);
|
|
3446
|
-
const frames = drafts.map((draft, offset) => ({
|
|
3447
|
-
schema_version: "ceal.host_producer_frame.v1",
|
|
3448
|
-
producer_id: "codex",
|
|
3449
|
-
producer_installation_ref: this.installationRef,
|
|
3450
|
-
producer_epoch: this.epoch,
|
|
3451
|
-
sequence: state.next_sequence + offset,
|
|
3452
|
-
frame_kind: draft.frame_kind,
|
|
3453
|
-
occurred_at: envelope.created_at,
|
|
3454
|
-
payload: draft.payload
|
|
3455
|
-
}));
|
|
3456
|
-
await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [envelope.submission_ref]: frames } });
|
|
3457
|
-
return frames;
|
|
4002
|
+
removeEnvelope(envelope) {
|
|
4003
|
+
return this.store.remove(envelope);
|
|
3458
4004
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
4005
|
+
submissionRefOf(envelope) {
|
|
4006
|
+
return envelope.submission_ref;
|
|
4007
|
+
}
|
|
4008
|
+
expiresAtOf(envelope) {
|
|
4009
|
+
return envelope.expires_at;
|
|
4010
|
+
}
|
|
4011
|
+
createdAtOf(envelope) {
|
|
4012
|
+
return envelope.created_at;
|
|
3465
4013
|
}
|
|
3466
4014
|
async mapEnvelope(envelope) {
|
|
3467
4015
|
const prompt = observation(envelope, "prompt");
|
|
@@ -3477,7 +4025,7 @@ var CodexProducer = class {
|
|
|
3477
4025
|
const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
|
|
3478
4026
|
drafts.push({ frame_kind: "session_started", payload: compact({
|
|
3479
4027
|
local_session_ref: envelope.identity.host_session_ref,
|
|
3480
|
-
working_directory_ref: `workspace:${
|
|
4028
|
+
working_directory_ref: `workspace:${createHash6("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
|
|
3481
4029
|
model: recordText(session.value, "model"),
|
|
3482
4030
|
host_version: nestedText(session.value, "host_version"),
|
|
3483
4031
|
observations: [session]
|
|
@@ -3502,21 +4050,6 @@ var CodexProducer = class {
|
|
|
3502
4050
|
}) });
|
|
3503
4051
|
return drafts;
|
|
3504
4052
|
}
|
|
3505
|
-
async readState() {
|
|
3506
|
-
try {
|
|
3507
|
-
const value = JSON.parse(await readFile(this.statePath, "utf8"));
|
|
3508
|
-
return decodeState(value, this.installationRef, this.epoch);
|
|
3509
|
-
} catch (error) {
|
|
3510
|
-
if (errorCode2(error) !== "ENOENT") throw error;
|
|
3511
|
-
return { schema_version: STATE_SCHEMA, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
|
|
3512
|
-
}
|
|
3513
|
-
}
|
|
3514
|
-
async writeState(state) {
|
|
3515
|
-
await mkdir3(this.store.directory, { recursive: true, mode: 448 });
|
|
3516
|
-
const temporary = `${this.statePath}.${process.pid}.new`;
|
|
3517
|
-
await writeFile(temporary, JSON.stringify(state), { mode: 384 });
|
|
3518
|
-
await rename3(temporary, this.statePath);
|
|
3519
|
-
}
|
|
3520
4053
|
};
|
|
3521
4054
|
function mapTerminal(envelope) {
|
|
3522
4055
|
const timing = observation(envelope, "timing");
|
|
@@ -3544,7 +4077,7 @@ function compact(value) {
|
|
|
3544
4077
|
return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
|
|
3545
4078
|
}
|
|
3546
4079
|
function asRecord(value) {
|
|
3547
|
-
return
|
|
4080
|
+
return isRecord8(value) ? value : void 0;
|
|
3548
4081
|
}
|
|
3549
4082
|
function recordText(value, key) {
|
|
3550
4083
|
const record3 = asRecord(value);
|
|
@@ -3558,122 +4091,51 @@ function nonNegativeNumber(value) {
|
|
|
3558
4091
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
3559
4092
|
}
|
|
3560
4093
|
function safeReasonRef(value) {
|
|
3561
|
-
return SAFE_REF8.test(value) ? value : `reason:${
|
|
3562
|
-
}
|
|
3563
|
-
function errorCode2(error) {
|
|
3564
|
-
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
3565
|
-
}
|
|
3566
|
-
function decodeState(value, installationRef, epoch) {
|
|
3567
|
-
if (!validStateHeader(value, installationRef)) {
|
|
3568
|
-
throw new CodexHostAdapterError("storage", `Codex producer state header is malformed or belongs to another installation (${refusalFacts([
|
|
3569
|
-
{ name: "schema_version", value: isRecord7(value) ? value.schema_version : void 0, expected: STATE_SCHEMA },
|
|
3570
|
-
{ name: "producer_installation_ref", value: isRecord7(value) ? value.producer_installation_ref : void 0, expected: installationRef }
|
|
3571
|
-
])}).`);
|
|
3572
|
-
}
|
|
3573
|
-
if (Number(value.producer_epoch) > epoch) throw new CodexHostAdapterError("storage", `Codex producer state epoch is ahead of this installation (${refusalFact("producer_epoch", value.producer_epoch, `at most ${epoch}`)}).`);
|
|
3574
|
-
const pending = {};
|
|
3575
|
-
for (const [submissionRef, frames] of Object.entries(value.pending)) {
|
|
3576
|
-
if (!SAFE_REF8.test(submissionRef) || !Array.isArray(frames) || frames.length === 0) throw new CodexHostAdapterError("storage", "Codex producer pending reservation is malformed.");
|
|
3577
|
-
pending[submissionRef] = frames.map((frame) => decodeStoredFrame(frame, installationRef, Number(value.producer_epoch)));
|
|
3578
|
-
}
|
|
3579
|
-
return { schema_version: STATE_SCHEMA, producer_installation_ref: installationRef, producer_epoch: epoch, next_sequence: Number(value.next_sequence), pending };
|
|
3580
|
-
}
|
|
3581
|
-
function validStateHeader(value, installationRef) {
|
|
3582
|
-
return isRecord7(value) && value.schema_version === STATE_SCHEMA && value.producer_installation_ref === installationRef && Number.isSafeInteger(value.producer_epoch) && Number(value.producer_epoch) > 0 && Number.isSafeInteger(value.next_sequence) && Number(value.next_sequence) > 0 && isRecord7(value.pending);
|
|
3583
|
-
}
|
|
3584
|
-
function decodeStoredFrame(value, installationRef, epoch) {
|
|
3585
|
-
const json = Buffer.from(JSON.stringify(value), "utf8");
|
|
3586
|
-
const encoded = Buffer.allocUnsafe(json.length + 4);
|
|
3587
|
-
encoded.writeUInt32BE(json.length, 0);
|
|
3588
|
-
json.copy(encoded, 4);
|
|
3589
|
-
const frame = decodeHostProducerFrame(encoded);
|
|
3590
|
-
if (frame.producer_id !== "codex" || frame.producer_installation_ref !== installationRef || frame.producer_epoch > epoch) throw new CodexHostAdapterError("storage", `Codex producer pending frame identity is malformed (${refusalFacts([
|
|
3591
|
-
{ name: "producer_id", value: frame.producer_id, expected: "codex" },
|
|
3592
|
-
{ name: "producer_installation_ref", value: frame.producer_installation_ref, expected: installationRef },
|
|
3593
|
-
{ name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${epoch}` }
|
|
3594
|
-
])}).`);
|
|
3595
|
-
return frame;
|
|
4094
|
+
return SAFE_REF8.test(value) ? value : `reason:${createHash6("sha256").update(value, "utf8").digest("hex")}`;
|
|
3596
4095
|
}
|
|
3597
|
-
function
|
|
4096
|
+
function isRecord8(value) {
|
|
3598
4097
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3599
4098
|
}
|
|
3600
4099
|
|
|
3601
4100
|
// host/codex/src/codex-transcript-watcher.ts
|
|
3602
|
-
|
|
3603
|
-
import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
|
|
3604
|
-
import { open as open5 } from "node:fs/promises";
|
|
3605
|
-
var READ_CHUNK_BYTES = 256 * 1024;
|
|
3606
|
-
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
3607
|
-
var CodexTranscriptWatcher = class {
|
|
4101
|
+
var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
3608
4102
|
store;
|
|
3609
|
-
outboxRetentionMs;
|
|
3610
|
-
now;
|
|
3611
|
-
log;
|
|
3612
|
-
producerInstallationRef;
|
|
3613
4103
|
registerCurrentPrompt;
|
|
3614
4104
|
constructor(options) {
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
this.log = options.log ?? ((line) => {
|
|
3620
|
-
process.stderr.write(`${line}
|
|
3621
|
-
`);
|
|
4105
|
+
super(options, {
|
|
4106
|
+
default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
|
|
4107
|
+
invalid_outbox: () => new CodexHostAdapterError("configuration", "Codex transcript watcher outbox is invalid."),
|
|
4108
|
+
clock_failure: () => new CodexHostAdapterError("storage", "Codex watcher clock is invalid.")
|
|
3622
4109
|
});
|
|
3623
|
-
this.
|
|
4110
|
+
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
3624
4111
|
this.registerCurrentPrompt = options.register_current_prompt;
|
|
3625
4112
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
let records = 0;
|
|
3629
|
-
let enqueued = 0;
|
|
3630
|
-
let quarantined = 0;
|
|
3631
|
-
for (const registration of registrations) {
|
|
3632
|
-
const result = await this.scanRegistration(registration);
|
|
3633
|
-
records += result.records;
|
|
3634
|
-
enqueued += result.enqueued;
|
|
3635
|
-
if (result.quarantined) quarantined += 1;
|
|
3636
|
-
}
|
|
3637
|
-
return { registrations: registrations.length, records, enqueued, quarantined };
|
|
4113
|
+
transcriptPathOf(registration) {
|
|
4114
|
+
return registration.transcript_path;
|
|
3638
4115
|
}
|
|
3639
|
-
async
|
|
3640
|
-
|
|
3641
|
-
try {
|
|
3642
|
-
handle = await open5(registration.transcript_path, O_RDONLY4 | O_NOFOLLOW3);
|
|
3643
|
-
const { size, device, inode } = await ownedCodexTranscriptFile(handle);
|
|
3644
|
-
const preflight = await this.preflight(registration, handle, size, device, inode);
|
|
3645
|
-
if (preflight.cursor === null) return preflight.result;
|
|
3646
|
-
const complete = await readCompleteChunk(handle, preflight.cursor, size);
|
|
3647
|
-
if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
|
|
3648
|
-
return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
|
|
3649
|
-
} catch (error) {
|
|
3650
|
-
if (handle === void 0 && isMissingTranscript(error)) return await this.retireRegistration(registration);
|
|
3651
|
-
await this.quarantineFailure(registration, handle);
|
|
3652
|
-
return { records: 0, enqueued: 0, quarantined: true };
|
|
3653
|
-
} finally {
|
|
3654
|
-
await handle?.close();
|
|
3655
|
-
}
|
|
4116
|
+
async listRegistrations() {
|
|
4117
|
+
return this.store.listTranscriptRegistrations();
|
|
3656
4118
|
}
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
return
|
|
4119
|
+
async scanOpened(handle, registration) {
|
|
4120
|
+
const { size, device, inode } = await ownedCodexTranscriptFile(handle);
|
|
4121
|
+
const preflight = await this.preflight(registration, handle, size, device, inode);
|
|
4122
|
+
if (preflight.cursor === null) return preflight.result;
|
|
4123
|
+
const complete = await this.readChunk(handle, preflight.cursor.offset, size);
|
|
4124
|
+
if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
|
|
4125
|
+
return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
|
|
4126
|
+
}
|
|
4127
|
+
async scanFailed(handle, registration, _error) {
|
|
4128
|
+
await this.quarantineFailure(registration, handle);
|
|
4129
|
+
return { records: 0, enqueued: 0, quarantined: true };
|
|
4130
|
+
}
|
|
4131
|
+
async retireMissing(registration) {
|
|
4132
|
+
return this.retireMissingTranscript(registration.transcript_path, "Codex", () => this.store.retireTranscriptRegistration(registration));
|
|
3671
4133
|
}
|
|
3672
4134
|
async preflight(registration, handle, size, device, inode) {
|
|
3673
4135
|
const existing = await this.store.readTranscriptCursor(registration);
|
|
3674
4136
|
let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
|
|
3675
4137
|
if (cursor.status === "quarantined") return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
3676
|
-
const reason =
|
|
4138
|
+
const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
|
|
3677
4139
|
if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
|
|
3678
4140
|
await this.quarantine(registration, cursor, device, inode, reason);
|
|
3679
4141
|
return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
@@ -3699,14 +4161,12 @@ var CodexTranscriptWatcher = class {
|
|
|
3699
4161
|
async observeSessionMetadata(registration, record3) {
|
|
3700
4162
|
if (record3.kind !== "session_meta") return;
|
|
3701
4163
|
const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
|
|
3702
|
-
if (session !== null &&
|
|
4164
|
+
if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record3.cli_version } });
|
|
3703
4165
|
}
|
|
3704
4166
|
async commitCursor(registration, handle, cursor, consumedBytes, device, inode, lastOrdinal, activeTask) {
|
|
3705
4167
|
const offset = cursor.offset + consumedBytes;
|
|
3706
|
-
const
|
|
3707
|
-
|
|
3708
|
-
if (anchor.length > 0) await handle.read(anchor, 0, anchor.length, anchorStart);
|
|
3709
|
-
await this.store.writeTranscriptCursor({ schema_version: "ceal.codex_transcript_cursor.v1", host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, transcript_path: registration.transcript_path, device, inode, offset, last_ordinal: lastOrdinal, anchor_start: anchorStart, anchor_sha256: sha2562(anchor), header_validated: true, ...activeTask === void 0 ? {} : { active_task: activeTask }, status: "active", updated_at: timestamp5(this.now) });
|
|
4168
|
+
const anchor = await this.readAnchor(handle, offset, CODEX_TRANSCRIPT_ANCHOR_BYTES);
|
|
4169
|
+
await this.store.writeTranscriptCursor({ schema_version: "ceal.codex_transcript_cursor.v1", host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, transcript_path: registration.transcript_path, device, inode, offset, last_ordinal: lastOrdinal, anchor_start: anchor.start, anchor_sha256: anchor.sha256, header_validated: true, ...activeTask === void 0 ? {} : { active_task: activeTask }, status: "active", updated_at: this.stamp() });
|
|
3710
4170
|
}
|
|
3711
4171
|
async quarantineFailure(registration, handle) {
|
|
3712
4172
|
if (handle === void 0) return;
|
|
@@ -3755,11 +4215,11 @@ var CodexTranscriptWatcher = class {
|
|
|
3755
4215
|
origin_input: origin,
|
|
3756
4216
|
submission_ref: codexSubmissionReference(hostInputRef, "input", record3.ordinal),
|
|
3757
4217
|
created_at: record3.timestamp,
|
|
3758
|
-
expires_at:
|
|
4218
|
+
expires_at: this.expire(this.outboxRetentionMs)
|
|
3759
4219
|
};
|
|
3760
4220
|
await this.registerPrompt(origin, registration, activeTask, currentPromptRef, record3.prompt, envelope.expires_at);
|
|
3761
4221
|
await this.store.put(envelope);
|
|
3762
|
-
if (origin) await this.store
|
|
4222
|
+
if (origin) await activateTranscriptTurnContext(this.store, identity, promptRef, envelope.expires_at);
|
|
3763
4223
|
return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
|
|
3764
4224
|
}
|
|
3765
4225
|
async registerPrompt(origin, registration, activeTask, promptRef, prompt, expiresAt) {
|
|
@@ -3782,16 +4242,21 @@ var CodexTranscriptWatcher = class {
|
|
|
3782
4242
|
origin_input: false,
|
|
3783
4243
|
submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record3.ordinal),
|
|
3784
4244
|
created_at: record3.timestamp,
|
|
3785
|
-
expires_at:
|
|
4245
|
+
expires_at: this.expire(this.outboxRetentionMs)
|
|
3786
4246
|
});
|
|
3787
4247
|
await this.store.clearInvocationContext(identity, this.now());
|
|
3788
4248
|
return { enqueued: 1 };
|
|
3789
4249
|
}
|
|
3790
4250
|
async quarantine(registration, cursor, device, inode, reason) {
|
|
3791
|
-
const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at:
|
|
4251
|
+
const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at: this.stamp() };
|
|
3792
4252
|
await this.store.writeTranscriptCursor(quarantined);
|
|
3793
4253
|
}
|
|
3794
4254
|
};
|
|
4255
|
+
async function activateTranscriptTurnContext(store, identity, promptRef, expiresAt) {
|
|
4256
|
+
const selection = await store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
4257
|
+
const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
|
|
4258
|
+
await store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
|
|
4259
|
+
}
|
|
3795
4260
|
function accumulateTokenCount(activeTask, record3) {
|
|
3796
4261
|
if (record3.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record3.usage_gap_reason };
|
|
3797
4262
|
const last = record3.last_usage;
|
|
@@ -3825,19 +4290,18 @@ function terminalCostValue() {
|
|
|
3825
4290
|
return notRecorded("provider_cost_not_observed");
|
|
3826
4291
|
}
|
|
3827
4292
|
function notRecorded(reason) {
|
|
3828
|
-
return
|
|
4293
|
+
return hostObservationNotRecorded(reason);
|
|
3829
4294
|
}
|
|
3830
4295
|
function tokenObservationValue(usage) {
|
|
3831
4296
|
return {
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
source: "codex_transcript.event_msg.token_count.info.last_token_usage",
|
|
4297
|
+
...createHostTokenObservationValue({
|
|
4298
|
+
input: usage.input_tokens,
|
|
4299
|
+
output: usage.output_tokens,
|
|
4300
|
+
total: usage.total_tokens,
|
|
4301
|
+
cached_input: usage.cached_input_tokens,
|
|
4302
|
+
cache_write_input: usage.cache_write_input_tokens,
|
|
4303
|
+
reasoning_output: usage.reasoning_output_tokens
|
|
4304
|
+
}, "codex_transcript.event_msg.token_count.info.last_token_usage"),
|
|
3841
4305
|
attribution: "sequential_active_task",
|
|
3842
4306
|
total_usage_role: "dedup_and_reset_detection_only"
|
|
3843
4307
|
};
|
|
@@ -3871,60 +4335,10 @@ function looksLikeStaleRegression(current, previous, last) {
|
|
|
3871
4335
|
if (previous.total_tokens <= 0 || current.total_tokens <= 0 || last.total_tokens <= 0) return false;
|
|
3872
4336
|
return current.total_tokens * 100 >= previous.total_tokens * 98 || current.total_tokens + last.total_tokens * 2 >= previous.total_tokens;
|
|
3873
4337
|
}
|
|
3874
|
-
function isMissingTranscript(error) {
|
|
3875
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
3876
|
-
}
|
|
3877
|
-
async function cursorFailureReason(registration, handle, cursor, size, device, inode) {
|
|
3878
|
-
if (cursor.device !== device || cursor.inode !== inode || cursor.transcript_path !== registration.transcript_path) return "transcript_replaced";
|
|
3879
|
-
if (size < cursor.offset) return "transcript_truncated";
|
|
3880
|
-
if (cursor.offset > 0 && !await anchorMatches(handle, cursor)) return "transcript_rewritten";
|
|
3881
|
-
return null;
|
|
3882
|
-
}
|
|
3883
|
-
async function readCompleteChunk(handle, cursor, size) {
|
|
3884
|
-
const available = Math.max(0, size - cursor.offset);
|
|
3885
|
-
if (available === 0) return "empty";
|
|
3886
|
-
const chunks = [];
|
|
3887
|
-
let position = cursor.offset;
|
|
3888
|
-
let total = 0;
|
|
3889
|
-
let lastNewline = -1;
|
|
3890
|
-
while (position < size) {
|
|
3891
|
-
const buffer = Buffer.alloc(Math.min(READ_CHUNK_BYTES, size - position));
|
|
3892
|
-
const read = await handle.read(buffer, 0, buffer.length, position);
|
|
3893
|
-
if (read.bytesRead === 0) break;
|
|
3894
|
-
const chunk = buffer.subarray(0, read.bytesRead);
|
|
3895
|
-
chunks.push(chunk);
|
|
3896
|
-
const newline = chunk.lastIndexOf(10);
|
|
3897
|
-
if (newline >= 0) lastNewline = total + newline;
|
|
3898
|
-
total += chunk.length;
|
|
3899
|
-
position += chunk.length;
|
|
3900
|
-
if (lastNewline >= 0) break;
|
|
3901
|
-
}
|
|
3902
|
-
if (lastNewline < 0) return "partial";
|
|
3903
|
-
return Buffer.concat(chunks, total).subarray(0, lastNewline + 1);
|
|
3904
|
-
}
|
|
3905
4338
|
function assertRecordSequence(registration, cursor, records, lastOrdinal, record3) {
|
|
3906
4339
|
if ((cursor.header_validated || records > 0) && record3.ordinal <= lastOrdinal) throw new CodexHostAdapterError("payload_rejected", `Codex transcript ordinal is not monotonic (${refusalFact("ordinal", record3.ordinal, `greater than ${lastOrdinal}`)}).`);
|
|
3907
4340
|
if (!cursor.header_validated && records === 0) assertCodexTranscriptHeader(registration, record3);
|
|
3908
4341
|
}
|
|
3909
|
-
async function anchorMatches(handle, cursor) {
|
|
3910
|
-
const buffer = Buffer.alloc(cursor.offset - cursor.anchor_start);
|
|
3911
|
-
if (buffer.length > 0) await handle.read(buffer, 0, buffer.length, cursor.anchor_start);
|
|
3912
|
-
return sha2562(buffer) === cursor.anchor_sha256;
|
|
3913
|
-
}
|
|
3914
|
-
function sha2562(value) {
|
|
3915
|
-
return createHash6("sha256").update(value).digest("hex");
|
|
3916
|
-
}
|
|
3917
|
-
function isRecord8(value) {
|
|
3918
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3919
|
-
}
|
|
3920
|
-
function timestamp5(now) {
|
|
3921
|
-
const value = now();
|
|
3922
|
-
if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
|
|
3923
|
-
return value.toISOString();
|
|
3924
|
-
}
|
|
3925
|
-
function expiration(now, retentionMs) {
|
|
3926
|
-
return new Date(Date.parse(timestamp5(now)) + retentionMs).toISOString();
|
|
3927
|
-
}
|
|
3928
4342
|
|
|
3929
4343
|
// host/codex/src/codex-producer-runtime.ts
|
|
3930
4344
|
async function drainCodexProducerOnce(options) {
|
|
@@ -3953,7 +4367,7 @@ var CODEX_PRODUCER_DESCRIPTOR = {
|
|
|
3953
4367
|
};
|
|
3954
4368
|
|
|
3955
4369
|
// host/codex/src/codex-hook-runner.ts
|
|
3956
|
-
var MAX_STDIN_BYTES =
|
|
4370
|
+
var MAX_STDIN_BYTES = 512 * 1024;
|
|
3957
4371
|
async function runCodexHookFromStdin(event, stdin, options) {
|
|
3958
4372
|
const adapter = createCodexHostObservationAdapter(options);
|
|
3959
4373
|
return adapter.handle(event, await readStdin(stdin));
|