@corca-org/ceal-host-linux-arm64 1.0.18 → 1.0.20
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 +2041 -514
- package/libexec/ceal-host/claude-invocation-authentication.mjs +1899 -330
- package/libexec/ceal-host/claude-producer.mjs +1860 -406
- package/libexec/ceal-host/codex-hook.mjs +1618 -1013
- package/libexec/ceal-host/codex-invocation-authentication.mjs +3222 -505
- package/libexec/ceal-host/codex-producer.mjs +1126 -581
- package/libexec/ceal-host/control.mjs +2857 -1134
- package/libexec/ceal-host/relay.mjs +559 -116
- 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"]);
|
|
@@ -951,6 +977,7 @@ var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_f
|
|
|
951
977
|
var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
|
|
952
978
|
|
|
953
979
|
// packages/ceal-client-protocol/src/resource-row.ts
|
|
980
|
+
var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
|
|
954
981
|
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
955
982
|
var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
|
|
956
983
|
var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "since", "until", "authored_by_me", "authored_by", "include_replies", "kinds", "container_ref", "container_refs", "container_names", "addressed_to_me", "unread"]);
|
|
@@ -974,6 +1001,7 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
|
974
1001
|
"resource_modified"
|
|
975
1002
|
]);
|
|
976
1003
|
var CEAL_ACTIVITY_CONNECTOR_KINDS = vocabulary(["slack", "github", "google-calendar", "google-drive", "notion"]);
|
|
1004
|
+
var CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE = "unsupported_event_kind";
|
|
977
1005
|
var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
978
1006
|
"provider_page_budget",
|
|
979
1007
|
"call_deadline",
|
|
@@ -981,7 +1009,7 @@ var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
|
981
1009
|
"provider_cursor_unavailable",
|
|
982
1010
|
"malformed_provider_page",
|
|
983
1011
|
"visibility_incomplete",
|
|
984
|
-
|
|
1012
|
+
CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE,
|
|
985
1013
|
"direct_messages_excluded"
|
|
986
1014
|
]);
|
|
987
1015
|
var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
@@ -1033,6 +1061,7 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
1033
1061
|
next: { required: true, timestamp: false },
|
|
1034
1062
|
kind: { required: true, timestamp: false },
|
|
1035
1063
|
matched_kinds: { required: false, timestamp: false },
|
|
1064
|
+
title_match: { required: false, timestamp: false },
|
|
1036
1065
|
title: { required: true, timestamp: false },
|
|
1037
1066
|
container_label: { required: true, timestamp: false },
|
|
1038
1067
|
container_ref: { required: false, timestamp: false },
|
|
@@ -1046,6 +1075,8 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
1046
1075
|
modified_at: { required: false, timestamp: true },
|
|
1047
1076
|
modified_at_local: { required: false, timestamp: true },
|
|
1048
1077
|
viewed_by_me_at: { required: false, timestamp: true },
|
|
1078
|
+
own_ceal_write: { required: false, timestamp: false },
|
|
1079
|
+
owned_by_me: { required: false, timestamp: false },
|
|
1049
1080
|
visibility: { required: false, timestamp: false },
|
|
1050
1081
|
start: { required: false, timestamp: false },
|
|
1051
1082
|
end: { required: false, timestamp: false },
|
|
@@ -1079,6 +1110,15 @@ function cealExcludedTargetCoverageJsonSchema() {
|
|
|
1079
1110
|
};
|
|
1080
1111
|
}
|
|
1081
1112
|
|
|
1113
|
+
// packages/ceal-client-protocol/src/gateway-write-identity.ts
|
|
1114
|
+
var CEAL_GATEWAY_WRITE_IDENTITY_ROLES = Object.freeze(["replay_identity", "lookup_handle", "collision_evidence"]);
|
|
1115
|
+
var CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN = /^gateway-write-request:[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/u;
|
|
1116
|
+
var CEAL_GATEWAY_WRITE_IDENTITY_FIELDS = Object.freeze({
|
|
1117
|
+
replay_identity: "idempotency_claim_sha256",
|
|
1118
|
+
lookup_handle: "write_request_sha256",
|
|
1119
|
+
collision_evidence: "normalized_mutation_sha256"
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1082
1122
|
// packages/ceal-client-protocol/src/write-safety-finding.ts
|
|
1083
1123
|
var CEAL_WRITE_SAFETY_FINDING_STATUSES = vocabulary(["warning", "unchecked"]);
|
|
1084
1124
|
var CEAL_WRITE_SAFETY_AUDIENCES = vocabulary(["workspace", "person", "members", "unknown"]);
|
|
@@ -1106,7 +1146,11 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
|
1106
1146
|
"refresh_revoked",
|
|
1107
1147
|
"authority_replaced",
|
|
1108
1148
|
"refresh_recovery_unavailable",
|
|
1109
|
-
"refresh_recovery_expired"
|
|
1149
|
+
"refresh_recovery_expired",
|
|
1150
|
+
// The serving gate answers this while the Gateway is still starting and the
|
|
1151
|
+
// refresh was never attempted; the same attempt stays retryable. It is never
|
|
1152
|
+
// stored by the session owner, only answered at the serving edge.
|
|
1153
|
+
"gateway_starting"
|
|
1110
1154
|
]);
|
|
1111
1155
|
|
|
1112
1156
|
// packages/ceal-client-protocol/src/request-validation-primitives.ts
|
|
@@ -1139,7 +1183,7 @@ var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
|
|
|
1139
1183
|
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
1184
|
var CEAL_REQUEST_JSON_SCHEMA = {
|
|
1141
1185
|
type: "object",
|
|
1142
|
-
maxProperties:
|
|
1186
|
+
maxProperties: 11,
|
|
1143
1187
|
additionalProperties: false,
|
|
1144
1188
|
properties: {
|
|
1145
1189
|
schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
|
|
@@ -1163,7 +1207,8 @@ var CEAL_REQUEST_JSON_SCHEMA = {
|
|
|
1163
1207
|
host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1164
1208
|
host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1165
1209
|
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." }
|
|
1210
|
+
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
|
|
1211
|
+
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
1212
|
},
|
|
1168
1213
|
required: CEAL_REQUEST_KEYS
|
|
1169
1214
|
};
|
|
@@ -1222,6 +1267,7 @@ var REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES = {
|
|
|
1222
1267
|
retryable: { type: "boolean" },
|
|
1223
1268
|
source: { type: "string", enum: ["provider", "gateway"] },
|
|
1224
1269
|
retry_after_ms: { type: "integer", minimum: 0, maximum: CEAL_RETRY_AFTER_MAX_MS },
|
|
1270
|
+
write_request_ref: { type: "string", pattern: CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN.source, maxLength: 128 },
|
|
1225
1271
|
excluded_targets: cealExcludedTargetCoverageJsonSchema(),
|
|
1226
1272
|
repaired_input: {},
|
|
1227
1273
|
unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
|
|
@@ -1286,6 +1332,43 @@ function cealHostObservationRef(producer, digest2) {
|
|
|
1286
1332
|
if (!HOST_OBSERVATION_PRODUCER.test(producer) || !HOST_OBSERVATION_DIGEST.test(digest2)) invalid("Ceal Host observation reference parts are invalid.");
|
|
1287
1333
|
return `${CEAL_HOST_OBSERVATION_REF_PREFIX}${producer}-${digest2}`;
|
|
1288
1334
|
}
|
|
1335
|
+
var HOST_TOKEN_OBSERVATION_BREAKDOWN_KEYS = vocabulary(["cached_input", "cache_write_input", "reasoning_output"]);
|
|
1336
|
+
function createHostTokenObservationValue(counts, source) {
|
|
1337
|
+
const core = validTokenCore(counts);
|
|
1338
|
+
if (typeof source !== "string" || source.length === 0 || source.length > 256) invalid("Ceal Host token observation source is invalid.");
|
|
1339
|
+
const value = { status: "available", unit: "tokens", input: core.input, output: core.output, total: core.total, source };
|
|
1340
|
+
for (const breakdown of validTokenBreakdowns(counts)) value[breakdown.key] = breakdown.tokens;
|
|
1341
|
+
assertHostObservationValue(value, "token");
|
|
1342
|
+
return value;
|
|
1343
|
+
}
|
|
1344
|
+
function validTokenCore(counts) {
|
|
1345
|
+
if (!isOperationRecord(counts) || !safeCount(counts.input) || !safeCount(counts.output) || !safeCount(counts.total)) invalid("Ceal Host token observation counts are invalid.");
|
|
1346
|
+
if (counts.total !== counts.input + counts.output) invalid("Ceal Host token observation counts are invalid.");
|
|
1347
|
+
return { input: counts.input, output: counts.output, total: counts.total };
|
|
1348
|
+
}
|
|
1349
|
+
function validTokenBreakdowns(counts) {
|
|
1350
|
+
const breakdowns = [
|
|
1351
|
+
{ key: "cached_input", tokens: counts.cached_input },
|
|
1352
|
+
{ key: "cache_write_input", tokens: counts.cache_write_input },
|
|
1353
|
+
{ key: "reasoning_output", tokens: counts.reasoning_output }
|
|
1354
|
+
];
|
|
1355
|
+
const stated = breakdowns.filter((breakdown) => breakdown.tokens !== void 0).map((breakdown) => ({ key: breakdown.key, tokens: Number(breakdown.tokens) }));
|
|
1356
|
+
for (const breakdown of stated) {
|
|
1357
|
+
if (!safeCount(breakdown.tokens)) invalid("Ceal Host token observation counts are invalid.");
|
|
1358
|
+
}
|
|
1359
|
+
const cached = counts.cached_input ?? 0;
|
|
1360
|
+
const created = counts.cache_write_input ?? 0;
|
|
1361
|
+
const reasoning = counts.reasoning_output ?? 0;
|
|
1362
|
+
if (cached + created > counts.input || reasoning > counts.output) invalid("Ceal Host token observation counts are invalid.");
|
|
1363
|
+
return stated;
|
|
1364
|
+
}
|
|
1365
|
+
function hostObservationNotRecorded(reason) {
|
|
1366
|
+
if (typeof reason !== "string" || reason.length === 0 || reason.length > 256) invalid("Ceal Host observation gap reason is invalid.");
|
|
1367
|
+
return { status: "not_recorded", reason };
|
|
1368
|
+
}
|
|
1369
|
+
function safeCount(value) {
|
|
1370
|
+
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
1371
|
+
}
|
|
1289
1372
|
function validateCealHostObservationDraft(value) {
|
|
1290
1373
|
const record3 = operationRecord(value, "Ceal Host observation draft is invalid.");
|
|
1291
1374
|
requirePrefixedRef(record3.observation_ref, CEAL_HOST_OBSERVATION_REF_PREFIX, "Ceal Host observation reference is invalid.");
|
|
@@ -1437,12 +1520,85 @@ var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
|
|
|
1437
1520
|
"host_input_mismatch",
|
|
1438
1521
|
"host_prompt_mismatch"
|
|
1439
1522
|
]);
|
|
1523
|
+
var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
|
|
1524
|
+
"invalid_arguments",
|
|
1525
|
+
"options_invalid",
|
|
1526
|
+
"session_unavailable",
|
|
1527
|
+
"context_unavailable",
|
|
1528
|
+
"context_replaced",
|
|
1529
|
+
"no_binding",
|
|
1530
|
+
"context_stale",
|
|
1531
|
+
"context_expired",
|
|
1532
|
+
"host_source_refused",
|
|
1533
|
+
"host_credential_refused",
|
|
1534
|
+
"gateway_unavailable",
|
|
1535
|
+
"gateway_rate_limited",
|
|
1536
|
+
"gateway_refused",
|
|
1537
|
+
"gateway_response_invalid",
|
|
1538
|
+
"producer_frame_invalid",
|
|
1539
|
+
"relay_permission_denied",
|
|
1540
|
+
"relay_unavailable",
|
|
1541
|
+
"transient_retryable",
|
|
1542
|
+
"mint_failed",
|
|
1543
|
+
"relay_failed",
|
|
1544
|
+
"host_refusal_invalid"
|
|
1545
|
+
]);
|
|
1546
|
+
var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
1547
|
+
invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
|
|
1548
|
+
options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
|
|
1549
|
+
session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
1550
|
+
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; send a new message to start a fresh Host turn, then retry this command on that turn (run `ceal-host install` only if the Host hook registration is missing or drifted)" }),
|
|
1551
|
+
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
1552
|
+
no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
|
|
1553
|
+
context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
1554
|
+
context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "refresh_host_invocation_context" }),
|
|
1555
|
+
host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
|
|
1556
|
+
host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
|
|
1557
|
+
gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
|
|
1558
|
+
gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
|
|
1559
|
+
gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
|
|
1560
|
+
gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
|
|
1561
|
+
producer_frame_invalid: Object.freeze({ classification: "host_producer_frame_invalid", next_action: "ask_admin", remedy: "ask an administrator to inspect and repair the producer frame, then start a fresh Host turn" }),
|
|
1562
|
+
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" }),
|
|
1563
|
+
relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
|
|
1564
|
+
transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
|
|
1565
|
+
mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
|
|
1566
|
+
relay_failed: Object.freeze({ classification: "host_relay_failed", next_action: "answer_with_gap" }),
|
|
1567
|
+
host_refusal_invalid: Object.freeze({ classification: "host_refusal_invalid", next_action: "answer_with_gap" })
|
|
1568
|
+
});
|
|
1569
|
+
|
|
1570
|
+
// packages/ceal-client-protocol/src/host-service-diagnostic.ts
|
|
1571
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA = "ceal.host_service_diagnostic.v1";
|
|
1572
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
|
|
1573
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
1574
|
+
"hook_invoked",
|
|
1575
|
+
"registration_written",
|
|
1576
|
+
"hook_failed",
|
|
1577
|
+
"watcher_turn_observed",
|
|
1578
|
+
"current_prompt_registered",
|
|
1579
|
+
"current_prompt_registration_failed",
|
|
1580
|
+
"current_prompt_capacity_exhausted",
|
|
1581
|
+
"activation_recorded",
|
|
1582
|
+
"activation_failed",
|
|
1583
|
+
"watcher_scan_failed",
|
|
1584
|
+
"activation_wait_skipped",
|
|
1585
|
+
"activation_wait_engaged",
|
|
1586
|
+
"activation_wait_completed",
|
|
1587
|
+
"activation_wait_exhausted"
|
|
1588
|
+
]);
|
|
1589
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_FAILURE_KINDS = vocabulary([
|
|
1590
|
+
"malformed_line",
|
|
1591
|
+
"other",
|
|
1592
|
+
"payload_rejected",
|
|
1593
|
+
"read_error",
|
|
1594
|
+
"size_budget",
|
|
1595
|
+
"symlink"
|
|
1596
|
+
]);
|
|
1440
1597
|
|
|
1441
1598
|
// packages/ceal-client-protocol/src/client-result-materialization.ts
|
|
1442
1599
|
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"]);
|
|
1600
|
+
var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
|
|
1601
|
+
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
1602
|
|
|
1447
1603
|
// packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
|
|
1448
1604
|
var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -1472,14 +1628,6 @@ var CEAL_GATEWAY_ANNOUNCEMENT_POLICY_NON_CLAIMS = vocabulary([
|
|
|
1472
1628
|
"target_specific_scope_not_declared"
|
|
1473
1629
|
]);
|
|
1474
1630
|
|
|
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
1631
|
// packages/ceal-client-protocol/src/gateway-scoped-identity-projection-validation.ts
|
|
1484
1632
|
var PROJECTION_REVISION_KEYS = Object.freeze(["graph_revision", "subject_key_revision", "projection_revision"]);
|
|
1485
1633
|
|
|
@@ -1492,6 +1640,18 @@ var CEAL_SUPPORTED_GATEWAY_PROTOCOL_RANGE = Object.freeze({
|
|
|
1492
1640
|
// packages/ceal-client-protocol/src/admin-projection-vocabulary.ts
|
|
1493
1641
|
var ADMIN_PROJECTION_STATES = vocabulary(["ready", "empty", "partial", "unknown"]);
|
|
1494
1642
|
var ADMIN_VIEW_KINDS = vocabulary(["request_detail", "resource_access_timeline", "weekly_usage"]);
|
|
1643
|
+
var ADMIN_RECORD_PROJECTION_KINDS = vocabulary([
|
|
1644
|
+
"request_list",
|
|
1645
|
+
"usage_series",
|
|
1646
|
+
"connector_health",
|
|
1647
|
+
"person_connections",
|
|
1648
|
+
"client_sessions",
|
|
1649
|
+
"knowledge_base",
|
|
1650
|
+
"change_history",
|
|
1651
|
+
"approval_queue",
|
|
1652
|
+
"access_group_list",
|
|
1653
|
+
"capability_catalogue"
|
|
1654
|
+
]);
|
|
1495
1655
|
var ADMIN_GAP_KINDS = vocabulary(["unknown", "unavailable", "partial"]);
|
|
1496
1656
|
var ADMIN_UNAVAILABLE_REASONS = vocabulary([
|
|
1497
1657
|
"not_recorded",
|
|
@@ -1515,7 +1675,11 @@ var ADMIN_SOURCE_OWNERS = vocabulary([
|
|
|
1515
1675
|
"person",
|
|
1516
1676
|
"resource",
|
|
1517
1677
|
"prompt",
|
|
1518
|
-
"usage"
|
|
1678
|
+
"usage",
|
|
1679
|
+
"connector",
|
|
1680
|
+
"session",
|
|
1681
|
+
"knowledge",
|
|
1682
|
+
"policy"
|
|
1519
1683
|
]);
|
|
1520
1684
|
var ADMIN_COMPLETENESS_STATES = vocabulary(["complete", "partial", "unknown"]);
|
|
1521
1685
|
var ADMIN_OPERATION_OUTCOMES = vocabulary(["completed", "failed", "denied", "cancelled", "unknown"]);
|
|
@@ -1552,11 +1716,7 @@ var CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION = "ceal.codex_invocation_context.v1"
|
|
|
1552
1716
|
var CODEX_TRANSCRIPT_REGISTRATION_SCHEMA_VERSION = "ceal.codex_transcript_registration.v1";
|
|
1553
1717
|
var CODEX_TRANSCRIPT_CURSOR_SCHEMA_VERSION = "ceal.codex_transcript_cursor.v1";
|
|
1554
1718
|
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
|
-
}
|
|
1719
|
+
var CODEX_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit"];
|
|
1560
1720
|
var CodexHostAdapterError = class extends Error {
|
|
1561
1721
|
name = "CodexHostAdapterError";
|
|
1562
1722
|
code;
|
|
@@ -1571,12 +1731,12 @@ function isCodexHookEvent(value) {
|
|
|
1571
1731
|
|
|
1572
1732
|
// host/codex/src/codex-decoder.ts
|
|
1573
1733
|
var SAFE_REF2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
1574
|
-
var MAX_PAYLOAD_BYTES =
|
|
1734
|
+
var MAX_PAYLOAD_BYTES = 512 * 1024;
|
|
1575
1735
|
var MAX_TEXT = 256;
|
|
1576
1736
|
function decodeCodexHookPayload(event, raw) {
|
|
1577
|
-
if (event !== "SessionStart") reject(`Codex hook event is not installed by this adapter (${refusalFact("event", event, "SessionStart")})`);
|
|
1578
1737
|
const input = parsePayload(raw);
|
|
1579
|
-
return decodeSession(input, event);
|
|
1738
|
+
if (event === "SessionStart") return decodeSession(input, event);
|
|
1739
|
+
return decodeUserPromptSubmit(input, event);
|
|
1580
1740
|
}
|
|
1581
1741
|
function observationRef(event, identity, value) {
|
|
1582
1742
|
return cealHostObservationRef("codex", shortDigest({ event, identity, value }));
|
|
@@ -1628,6 +1788,20 @@ function decodeSession(input, event) {
|
|
|
1628
1788
|
transcript_path: requiredText(input.transcript_path, "transcript_path", 4096)
|
|
1629
1789
|
};
|
|
1630
1790
|
}
|
|
1791
|
+
function decodeUserPromptSubmit(input, event) {
|
|
1792
|
+
exactKeys(input, ["cwd", "hook_event_name", "model", "permission_mode", "prompt", "session_id", "transcript_path", "turn_id"], []);
|
|
1793
|
+
validateHookEventName(input, event);
|
|
1794
|
+
const cwd = requiredText(input.cwd, "cwd", 4096);
|
|
1795
|
+
if (!isAbsolute(cwd) || cwd.includes("\0")) reject("Codex cwd is invalid");
|
|
1796
|
+
return {
|
|
1797
|
+
event,
|
|
1798
|
+
cwd: resolve(cwd),
|
|
1799
|
+
session_id: requiredText(input.session_id, "session_id", MAX_TEXT),
|
|
1800
|
+
transcript_path: requiredText(input.transcript_path, "transcript_path", 4096),
|
|
1801
|
+
turn_id: requiredText(input.turn_id, "turn_id", MAX_TEXT),
|
|
1802
|
+
prompt: requiredText(input.prompt, "prompt", MAX_PAYLOAD_BYTES)
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1631
1805
|
function parsePayload(raw) {
|
|
1632
1806
|
try {
|
|
1633
1807
|
const text = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
@@ -1670,9 +1844,9 @@ function reject(message) {
|
|
|
1670
1844
|
}
|
|
1671
1845
|
|
|
1672
1846
|
// 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
|
|
1847
|
+
import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
|
|
1848
|
+
import { lstat as lstat2, mkdir as mkdir3, open as open3, readdir, rename as rename3, unlink as unlink2 } from "node:fs/promises";
|
|
1849
|
+
import { dirname, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
1676
1850
|
|
|
1677
1851
|
// host/invocation-context.ts
|
|
1678
1852
|
var HOST_INVOCATION_IDENTITY_KEYS = ["host_session_ref", "host_turn_ref", "host_source_ref"];
|
|
@@ -1686,17 +1860,32 @@ function hasHostInvocationIdentityKeys(value) {
|
|
|
1686
1860
|
function hasHostInvocationContextKeys(value, optional = []) {
|
|
1687
1861
|
return requiredKeys(value, HOST_INVOCATION_CONTEXT_REQUIRED_KEYS, optional);
|
|
1688
1862
|
}
|
|
1863
|
+
async function readLatestSessionProjection(file, hostSourceRef, hostSessionRef, readProjection) {
|
|
1864
|
+
const context = await readProjection(file);
|
|
1865
|
+
if (context === null || context.identity.host_source_ref !== hostSourceRef || context.identity.host_session_ref !== hostSessionRef) return null;
|
|
1866
|
+
return context;
|
|
1867
|
+
}
|
|
1868
|
+
function isLiveSessionProjection(context, now) {
|
|
1869
|
+
return context !== null && Date.parse(context.expires_at) > now.getTime();
|
|
1870
|
+
}
|
|
1689
1871
|
function invocationContextFromTurn(identity, promptRef, expiresAt, descriptor) {
|
|
1690
1872
|
const context = {
|
|
1691
1873
|
schema_version: descriptor.schema_version,
|
|
1692
1874
|
identity,
|
|
1693
1875
|
prompt_ref: promptRef,
|
|
1694
1876
|
expires_at: expiresAt,
|
|
1877
|
+
...descriptor.turn_generation_ref === void 0 ? {} : { turn_generation_ref: descriptor.turn_generation_ref },
|
|
1695
1878
|
...descriptor.working_directory === void 0 ? {} : { working_directory: descriptor.working_directory }
|
|
1696
1879
|
};
|
|
1697
1880
|
descriptor.validate(context);
|
|
1698
1881
|
return Object.freeze(context);
|
|
1699
1882
|
}
|
|
1883
|
+
function isHostInvocationTurnSelection(value) {
|
|
1884
|
+
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]));
|
|
1885
|
+
}
|
|
1886
|
+
function invocationTurnSelectionMatches(context, selection) {
|
|
1887
|
+
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;
|
|
1888
|
+
}
|
|
1700
1889
|
function exactKeys2(value, keys) {
|
|
1701
1890
|
const actual = Object.keys(value);
|
|
1702
1891
|
return actual.length === keys.length && keys.every((key) => Object.hasOwn(value, key)) && actual.every((key) => keys.includes(key));
|
|
@@ -1706,145 +1895,964 @@ function requiredKeys(value, required, optional) {
|
|
|
1706
1895
|
const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
|
|
1707
1896
|
return actual.length >= required.length && actual.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && actual.every((key) => allowed.has(key));
|
|
1708
1897
|
}
|
|
1898
|
+
function isRecord2(value) {
|
|
1899
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1900
|
+
}
|
|
1901
|
+
function safeRef(value) {
|
|
1902
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
1903
|
+
}
|
|
1709
1904
|
|
|
1710
|
-
// host/
|
|
1711
|
-
|
|
1712
|
-
|
|
1905
|
+
// host/relay/src/producer-frame.ts
|
|
1906
|
+
var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
|
|
1907
|
+
var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
|
|
1908
|
+
var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
1909
|
+
var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
1910
|
+
var PRODUCERS = /* @__PURE__ */ new Set(["codex", "claude"]);
|
|
1911
|
+
var HOST_PRODUCER_FRAME_KINDS = ["session_started", "turn_started", "user_input", "turn_terminal", "producer_status"];
|
|
1912
|
+
var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
|
|
1913
|
+
function isHostProducerFrameKind(value) {
|
|
1914
|
+
return typeof value === "string" && FRAME_KINDS.has(value);
|
|
1713
1915
|
}
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1916
|
+
var HOST_PRODUCER_FRAME_ERROR_CODES = [
|
|
1917
|
+
"invalid_frame_encoding",
|
|
1918
|
+
"invalid_frame_length",
|
|
1919
|
+
"frame_truncated",
|
|
1920
|
+
"unsupported_frame_schema",
|
|
1921
|
+
"invalid_frame_shape",
|
|
1922
|
+
"invalid_producer_identity",
|
|
1923
|
+
"frame_too_large"
|
|
1924
|
+
];
|
|
1925
|
+
var HostProducerFrameError = class extends Error {
|
|
1926
|
+
code;
|
|
1927
|
+
constructor(code, message = code) {
|
|
1928
|
+
super(message);
|
|
1929
|
+
this.code = code;
|
|
1717
1930
|
}
|
|
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);
|
|
1931
|
+
};
|
|
1932
|
+
function encodeHostProducerFrame(frame) {
|
|
1933
|
+
validateFrame(frame);
|
|
1934
|
+
const json = Buffer.from(JSON.stringify(frame), "utf8");
|
|
1935
|
+
if (json.byteLength > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("frame_bytes", json.byteLength, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
|
|
1936
|
+
const result = Buffer.allocUnsafe(json.byteLength + HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1937
|
+
result.writeUInt32BE(json.byteLength, 0);
|
|
1938
|
+
json.copy(result, HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1939
|
+
return result;
|
|
1733
1940
|
}
|
|
1734
|
-
function
|
|
1735
|
-
if (
|
|
1736
|
-
|
|
1941
|
+
function decodeHostProducerFrame(input) {
|
|
1942
|
+
if (input.byteLength < HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError("frame_truncated", `frame_truncated: ${refusalFact("frame_bytes", input.byteLength, `at least ${HOST_PRODUCER_FRAME_LENGTH_BYTES}`)}`);
|
|
1943
|
+
const length = input.readUInt32BE(0);
|
|
1944
|
+
if (length > HOST_PRODUCER_MAX_JSON_BYTES) throw new HostProducerFrameError("frame_too_large", `frame_too_large: ${refusalFact("declared_bytes", length, `at most ${HOST_PRODUCER_MAX_JSON_BYTES}`)}`);
|
|
1945
|
+
if (length === 0) throw new HostProducerFrameError("invalid_frame_length");
|
|
1946
|
+
if (input.byteLength !== length + HOST_PRODUCER_FRAME_LENGTH_BYTES) throw new HostProducerFrameError(input.byteLength < length + HOST_PRODUCER_FRAME_LENGTH_BYTES ? "frame_truncated" : "invalid_frame_length");
|
|
1947
|
+
const bytes = input.subarray(HOST_PRODUCER_FRAME_LENGTH_BYTES);
|
|
1948
|
+
const text = new TextDecoder("utf-8", { fatal: true });
|
|
1949
|
+
let value;
|
|
1950
|
+
try {
|
|
1951
|
+
value = JSON.parse(text.decode(bytes));
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
if (error instanceof TypeError) throw new HostProducerFrameError("invalid_frame_encoding");
|
|
1954
|
+
throw new HostProducerFrameError("frame_truncated");
|
|
1737
1955
|
}
|
|
1956
|
+
validateFrame(value);
|
|
1957
|
+
return value;
|
|
1738
1958
|
}
|
|
1739
|
-
function
|
|
1740
|
-
return
|
|
1959
|
+
function isHostSafeRef(value) {
|
|
1960
|
+
return safeRef2(value);
|
|
1741
1961
|
}
|
|
1742
|
-
function
|
|
1743
|
-
if (!
|
|
1962
|
+
function validateFrame(value) {
|
|
1963
|
+
if (!record(value)) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1964
|
+
if (value.schema_version !== HOST_PRODUCER_FRAME_SCHEMA) throw new HostProducerFrameError("unsupported_frame_schema", `unsupported_frame_schema: ${refusalFact("schema_version", value.schema_version, HOST_PRODUCER_FRAME_SCHEMA)}`);
|
|
1965
|
+
if (!exactKeys3(value, ["schema_version", "producer_id", "producer_installation_ref", "producer_epoch", "sequence", "frame_kind", "occurred_at", "payload"])) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1966
|
+
if (!PRODUCERS.has(String(value.producer_id)) || !safeRef2(value.producer_installation_ref)) throw new HostProducerFrameError("invalid_producer_identity");
|
|
1967
|
+
if (!positive(value.producer_epoch) || !positive(value.sequence) || !hostProducerFrameKind(value.frame_kind) || !timestamp(value.occurred_at) || !record(value.payload)) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1968
|
+
validatePayload(value.frame_kind, value.payload);
|
|
1744
1969
|
}
|
|
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}`)}).`);
|
|
1970
|
+
function validatePayload(kind, value) {
|
|
1971
|
+
const valid = kind === "session_started" ? validSession(value) : kind === "turn_started" ? validTurn(value) : kind === "user_input" ? validInput(value) : kind === "turn_terminal" ? validTerminal(value) : validStatus(value);
|
|
1972
|
+
if (!valid) throw new HostProducerFrameError("invalid_frame_shape");
|
|
1750
1973
|
}
|
|
1751
|
-
function
|
|
1752
|
-
return value
|
|
1974
|
+
function validSession(value) {
|
|
1975
|
+
return exactOptional(value, ["local_session_ref", "working_directory_ref"], ["model", "host_version", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.working_directory_ref) && optionalText(value.model, 256) && optionalText(value.host_version, 256) && validObservations(value.observations);
|
|
1753
1976
|
}
|
|
1754
|
-
function
|
|
1755
|
-
return value
|
|
1977
|
+
function validTurn(value) {
|
|
1978
|
+
return exactOptional(value, ["local_session_ref", "local_turn_ref"], ["model", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && optionalText(value.model, 256) && validObservations(value.observations);
|
|
1756
1979
|
}
|
|
1757
|
-
function
|
|
1758
|
-
return
|
|
1980
|
+
function validInput(value) {
|
|
1981
|
+
return exactOptional(value, ["local_session_ref", "local_turn_ref", "local_input_ref", "input_kind", "origin_input", "observations"], ["local_turn_aliases", "context_expires_at"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && safeRef2(value.local_input_ref) && validInputObservations(value.observations) && isCealHostInputKind(value.input_kind) && typeof value.origin_input === "boolean" && validInputOptionals(value);
|
|
1759
1982
|
}
|
|
1760
|
-
function
|
|
1761
|
-
return
|
|
1983
|
+
function validInputObservations(value) {
|
|
1984
|
+
return validObservations(value) && Array.isArray(value) && value.filter((item) => record(item) && item.observation_kind === "prompt").length === 1;
|
|
1762
1985
|
}
|
|
1763
|
-
function
|
|
1764
|
-
return value
|
|
1986
|
+
function validInputOptionals(value) {
|
|
1987
|
+
return optionalRefs(value.local_turn_aliases) && (value.context_expires_at === void 0 || timestamp(value.context_expires_at));
|
|
1765
1988
|
}
|
|
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);
|
|
1989
|
+
function validTerminal(value) {
|
|
1990
|
+
return exactOptional(value, ["local_session_ref", "local_turn_ref", "terminal_state"], ["abort_reason", "duration_ms", "time_to_first_token_ms", "usage", "observations"]) && safeRef2(value.local_session_ref) && safeRef2(value.local_turn_ref) && validTerminalDisposition(value.terminal_state, value.abort_reason) && optionalNonNegative(value.duration_ms) && optionalNonNegative(value.time_to_first_token_ms) && (value.usage === void 0 || jsonValue(value.usage)) && validObservations(value.observations);
|
|
1991
|
+
}
|
|
1992
|
+
function validTerminalDisposition(state, reason) {
|
|
1993
|
+
return (state === "completed" || state === "aborted") && (reason === void 0 || state === "aborted" && safeRef2(reason));
|
|
1994
|
+
}
|
|
1995
|
+
function validStatus(value) {
|
|
1996
|
+
return exactOptional(value, ["status", "reason_code"], ["detail"]) && ["ready", "degraded", "stopping"].includes(String(value.status)) && safeRef2(value.reason_code) && optionalText(value.detail, 4096);
|
|
1997
|
+
}
|
|
1998
|
+
function optionalText(value, maxLength) {
|
|
1999
|
+
return value === void 0 || typeof value === "string" && value.length > 0 && value.length <= maxLength;
|
|
2000
|
+
}
|
|
2001
|
+
function optionalNonNegative(value) {
|
|
2002
|
+
return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
2003
|
+
}
|
|
2004
|
+
function optionalRefs(value) {
|
|
2005
|
+
return value === void 0 || Array.isArray(value) && value.length <= 16 && value.every(safeRef2);
|
|
2006
|
+
}
|
|
2007
|
+
function validObservations(value) {
|
|
2008
|
+
if (value === void 0) return true;
|
|
2009
|
+
if (!Array.isArray(value) || value.length > 32) return false;
|
|
2010
|
+
return value.every((item) => {
|
|
1817
2011
|
try {
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
2012
|
+
validateCealHostObservationDraft(item);
|
|
2013
|
+
return true;
|
|
2014
|
+
} catch {
|
|
2015
|
+
return false;
|
|
1821
2016
|
}
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
2019
|
+
function jsonValue(value, depth = 0) {
|
|
2020
|
+
if (depth > 16) return false;
|
|
2021
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
2022
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
2023
|
+
if (Array.isArray(value)) return value.length <= 256 && value.every((item) => jsonValue(item, depth + 1));
|
|
2024
|
+
return record(value) && Object.keys(value).length <= 256 && Object.entries(value).every(([key, item]) => key.length > 0 && key.length <= 128 && jsonValue(item, depth + 1));
|
|
2025
|
+
}
|
|
2026
|
+
function record(value) {
|
|
2027
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2028
|
+
}
|
|
2029
|
+
function safeRef2(value) {
|
|
2030
|
+
return typeof value === "string" && SAFE_REF3.test(value);
|
|
2031
|
+
}
|
|
2032
|
+
function positive(value) {
|
|
2033
|
+
return Number.isSafeInteger(value) && Number(value) > 0;
|
|
2034
|
+
}
|
|
2035
|
+
function timestamp(value) {
|
|
2036
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
2037
|
+
}
|
|
2038
|
+
function hostProducerFrameKind(value) {
|
|
2039
|
+
return isHostProducerFrameKind(value);
|
|
2040
|
+
}
|
|
2041
|
+
function exactKeys3(value, keys) {
|
|
2042
|
+
return JSON.stringify(Object.keys(value).sort()) === JSON.stringify([...keys].sort());
|
|
2043
|
+
}
|
|
2044
|
+
function exactOptional(value, required, optional) {
|
|
2045
|
+
const keys = Object.keys(value);
|
|
2046
|
+
return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// host/relay/src/host-producer-core.ts
|
|
2050
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2051
|
+
import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
|
|
2052
|
+
import { mkdir as mkdir2, open as open2, readFile, rename as rename2, writeFile } from "node:fs/promises";
|
|
2053
|
+
import { join as join2 } from "node:path";
|
|
2054
|
+
|
|
2055
|
+
// host/relay/src/relay-ipc.ts
|
|
2056
|
+
import { createConnection, createServer } from "node:net";
|
|
2057
|
+
|
|
2058
|
+
// host/relay/src/durable-atomic-replacement.ts
|
|
2059
|
+
import { O_RDONLY } from "node:constants";
|
|
2060
|
+
import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
2061
|
+
var DEFAULT_PORT = {
|
|
2062
|
+
platform: process.platform,
|
|
2063
|
+
mkdir: (path, options) => mkdir(path, options),
|
|
2064
|
+
lstat: (path) => lstat(path),
|
|
2065
|
+
open_file: (path) => open(path, "wx", 384),
|
|
2066
|
+
rename,
|
|
2067
|
+
unlink,
|
|
2068
|
+
open_directory: (path) => open(path, O_RDONLY),
|
|
2069
|
+
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
2070
|
+
};
|
|
2071
|
+
|
|
2072
|
+
// host/relay/src/relay-request-binding-store.ts
|
|
2073
|
+
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
2074
|
+
|
|
2075
|
+
// host/relay/src/current-prompt-custody.ts
|
|
2076
|
+
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
2077
|
+
|
|
2078
|
+
// host/service-log.ts
|
|
2079
|
+
function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
|
|
2080
|
+
const line = `${JSON.stringify({ schema_version: CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA, producer_id, event, ...event === "watcher_scan_failed" && failure_kind !== void 0 ? { failure_kind } : {}, ...event === "watcher_scan_failed" && cause !== void 0 ? { cause } : {} })}
|
|
2081
|
+
`;
|
|
2082
|
+
try {
|
|
2083
|
+
(sink ?? defaultHostServiceDiagnosticSink)(line);
|
|
2084
|
+
} catch {
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
function defaultHostServiceDiagnosticSink(line) {
|
|
2088
|
+
process.stderr.write(line);
|
|
2089
|
+
}
|
|
2090
|
+
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
2091
|
+
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
2092
|
+
|
|
2093
|
+
// host/relay/src/relay-ipc.ts
|
|
2094
|
+
var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
2095
|
+
"schema_version",
|
|
2096
|
+
"producer_id",
|
|
2097
|
+
"producer_installation_ref",
|
|
2098
|
+
"local_session_ref",
|
|
2099
|
+
"local_turn_ref",
|
|
2100
|
+
"local_turn_aliases",
|
|
2101
|
+
"prompt_ref",
|
|
2102
|
+
"request_ref",
|
|
2103
|
+
"bound_at",
|
|
2104
|
+
"host_source_ref",
|
|
2105
|
+
"host_session_ref",
|
|
2106
|
+
"host_turn_ref",
|
|
2107
|
+
"host_input_ref"
|
|
2108
|
+
]);
|
|
2109
|
+
var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
|
|
2110
|
+
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
2111
|
+
var HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA = "ceal.host_relay_current_prompt_release.v1";
|
|
2112
|
+
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
2113
|
+
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
2114
|
+
var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
|
|
2115
|
+
var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
2116
|
+
"context_stale",
|
|
2117
|
+
"context_expired",
|
|
2118
|
+
"host_source_refused",
|
|
2119
|
+
"host_credential_refused",
|
|
2120
|
+
"gateway_unavailable",
|
|
2121
|
+
"gateway_refused",
|
|
2122
|
+
"gateway_response_invalid",
|
|
2123
|
+
"gateway_rate_limited",
|
|
2124
|
+
"producer_frame_invalid",
|
|
2125
|
+
"deadline",
|
|
2126
|
+
"cancelled",
|
|
2127
|
+
"mint_failed",
|
|
2128
|
+
"relay_failed",
|
|
2129
|
+
"request_authority_unavailable",
|
|
2130
|
+
"request_missing",
|
|
2131
|
+
"authority_mismatch",
|
|
2132
|
+
"host_source_mismatch",
|
|
2133
|
+
"host_session_mismatch",
|
|
2134
|
+
"host_turn_mismatch",
|
|
2135
|
+
"host_input_mismatch",
|
|
2136
|
+
"host_prompt_mismatch"
|
|
2137
|
+
]);
|
|
2138
|
+
var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
|
|
2139
|
+
"context_stale",
|
|
2140
|
+
"context_expired",
|
|
2141
|
+
"host_source_refused",
|
|
2142
|
+
"host_credential_refused",
|
|
2143
|
+
"gateway_unavailable",
|
|
2144
|
+
"gateway_rate_limited",
|
|
2145
|
+
"gateway_refused",
|
|
2146
|
+
"gateway_response_invalid",
|
|
2147
|
+
"producer_frame_invalid"
|
|
2148
|
+
]);
|
|
2149
|
+
var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
|
|
2150
|
+
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
2151
|
+
"permission_denied",
|
|
2152
|
+
"socket_missing",
|
|
2153
|
+
"connection_refused",
|
|
2154
|
+
"closed_early",
|
|
2155
|
+
"response_invalid",
|
|
2156
|
+
"timed_out",
|
|
2157
|
+
"cancelled",
|
|
2158
|
+
"unavailable"
|
|
2159
|
+
]);
|
|
2160
|
+
var HostRelayIpcError = class extends Error {
|
|
2161
|
+
code;
|
|
2162
|
+
/** Set only when the client-side socket exchange itself failed. */
|
|
2163
|
+
failure;
|
|
2164
|
+
constructor(code, message = code, failure) {
|
|
2165
|
+
super(message);
|
|
2166
|
+
this.code = code;
|
|
2167
|
+
this.failure = failure;
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
var HostRelayCurrentPromptCapacityRefusal = class extends Error {
|
|
2171
|
+
constructor() {
|
|
2172
|
+
super("current_prompt_capacity_exhausted");
|
|
2173
|
+
this.name = "HostRelayCurrentPromptCapacityRefusal";
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2176
|
+
function hostRelayExchangeFailure(error) {
|
|
2177
|
+
return error instanceof HostRelayIpcError ? error.failure : void 0;
|
|
2178
|
+
}
|
|
2179
|
+
function socketErrorFailure(error) {
|
|
2180
|
+
switch (errorCode(error)) {
|
|
2181
|
+
case "EACCES":
|
|
2182
|
+
case "EPERM":
|
|
2183
|
+
return "permission_denied";
|
|
2184
|
+
case "ENOENT":
|
|
2185
|
+
return "socket_missing";
|
|
2186
|
+
case "ECONNREFUSED":
|
|
2187
|
+
return "connection_refused";
|
|
2188
|
+
default:
|
|
2189
|
+
return "unavailable";
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
var HostRelayIpcAdmissionPort = class {
|
|
2193
|
+
endpoint;
|
|
2194
|
+
timeout_ms;
|
|
2195
|
+
constructor(endpoint, timeoutMs = 5e3) {
|
|
2196
|
+
this.endpoint = endpoint;
|
|
2197
|
+
this.timeout_ms = timeoutMs;
|
|
2198
|
+
}
|
|
2199
|
+
async admit(frame) {
|
|
2200
|
+
const response2 = await submitHostProducerFrame(this.endpoint, frame, this.timeout_ms);
|
|
2201
|
+
if (!relayAdmissionCode(response2.code)) throw new HostRelayIpcError(response2.code);
|
|
2202
|
+
return { code: response2.code, admitted_through_sequence: response2.admitted_through_sequence, delivered_through_sequence: response2.delivered_through_sequence };
|
|
2203
|
+
}
|
|
2204
|
+
};
|
|
2205
|
+
var HostRelayCurrentPromptPort = class {
|
|
2206
|
+
endpoint;
|
|
2207
|
+
timeout_ms;
|
|
2208
|
+
constructor(endpoint, timeoutMs = 5e3) {
|
|
2209
|
+
this.endpoint = endpoint;
|
|
2210
|
+
this.timeout_ms = timeoutMs;
|
|
2211
|
+
}
|
|
2212
|
+
async register(input) {
|
|
2213
|
+
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
|
|
2214
|
+
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
2215
|
+
if (response2.status === "capacity_exhausted") throw new HostRelayCurrentPromptCapacityRefusal();
|
|
2216
|
+
if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
|
|
2217
|
+
}
|
|
2218
|
+
async release(identity) {
|
|
2219
|
+
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA, ...identity };
|
|
2220
|
+
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
2221
|
+
if (response2.status !== "released") throw new HostRelayIpcError("relay_unavailable", response2.status, "response_invalid");
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
|
|
2225
|
+
return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
|
|
2226
|
+
}
|
|
2227
|
+
function exchange(endpoint, encoded, timeoutMs, decode, signal) {
|
|
2228
|
+
return new Promise((resolve3, reject3) => {
|
|
2229
|
+
const socket = createConnection(endpoint);
|
|
2230
|
+
let buffer = Buffer.alloc(0);
|
|
2231
|
+
let settled = false;
|
|
2232
|
+
const aborted = () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC was cancelled.", "cancelled"));
|
|
2233
|
+
const finish = (error, response2) => {
|
|
2234
|
+
if (settled) return;
|
|
2235
|
+
settled = true;
|
|
2236
|
+
signal?.removeEventListener("abort", aborted);
|
|
2237
|
+
socket.destroy();
|
|
2238
|
+
if (error !== null) reject3(error);
|
|
2239
|
+
else if (response2 !== void 0) resolve3(response2);
|
|
2240
|
+
};
|
|
2241
|
+
if (timeoutMs !== void 0) socket.setTimeout(timeoutMs, () => finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC timed out.", "timed_out")));
|
|
2242
|
+
if (signal?.aborted === true) {
|
|
2243
|
+
aborted();
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
2247
|
+
socket.once("error", (error) => {
|
|
2248
|
+
const failure = socketErrorFailure(error);
|
|
2249
|
+
finish(new HostRelayIpcError("relay_unavailable", `Host Relay IPC is unavailable: ${failure}.`, failure));
|
|
2250
|
+
});
|
|
2251
|
+
socket.once("connect", () => socket.write(encoded));
|
|
2252
|
+
socket.on("data", (chunk) => {
|
|
2253
|
+
try {
|
|
2254
|
+
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
|
2255
|
+
const decoded = decode(buffer);
|
|
2256
|
+
if (decoded !== null) finish(null, decoded);
|
|
2257
|
+
} catch (error) {
|
|
2258
|
+
finish(new HostRelayIpcError("relay_unavailable", error instanceof Error ? error.message : "Host Relay IPC response is invalid.", "response_invalid"));
|
|
2259
|
+
}
|
|
2260
|
+
});
|
|
2261
|
+
socket.once("end", () => {
|
|
2262
|
+
if (!settled) finish(new HostRelayIpcError("relay_unavailable", "Host Relay IPC closed without a response.", "closed_early"));
|
|
2263
|
+
});
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
function decodeHostRelayIpcResponse(input) {
|
|
2267
|
+
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response is truncated: ${refusalFact("response_bytes", input.byteLength, `at least ${HOST_RELAY_IPC_LENGTH_BYTES}`)}.`);
|
|
2268
|
+
const length = input.readUInt32BE(0);
|
|
2269
|
+
if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES || input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2270
|
+
let value;
|
|
2271
|
+
try {
|
|
2272
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
|
|
2273
|
+
} catch {
|
|
2274
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response is invalid.");
|
|
2275
|
+
}
|
|
2276
|
+
if (!validResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response shape is invalid.");
|
|
2277
|
+
return value;
|
|
2278
|
+
}
|
|
2279
|
+
function relayAdmissionCode(value) {
|
|
2280
|
+
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";
|
|
2281
|
+
}
|
|
2282
|
+
function validResponse(value) {
|
|
2283
|
+
return record2(value) && value.schema_version === HOST_RELAY_IPC_RESPONSE_SCHEMA && hostRelayIpcCode(value.code) && nonnegative(value.admitted_through_sequence) && nonnegative(value.delivered_through_sequence) && Object.keys(value).length === 4;
|
|
2284
|
+
}
|
|
2285
|
+
function hostRelayIpcCode(value) {
|
|
2286
|
+
return typeof value === "string" && ["admitted", "already_admitted", "producer_epoch_stale", "sequence_gap", "frame_identity_conflict", "producer_queue_full", "relay_queue_full", "invalid_frame_encoding", "invalid_frame_length", "frame_truncated", "unsupported_frame_schema", "invalid_frame_shape", "invalid_producer_identity", "frame_too_large", "producer_already_connected", "relay_unavailable"].includes(value);
|
|
2287
|
+
}
|
|
2288
|
+
function nonnegative(value) {
|
|
2289
|
+
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
2290
|
+
}
|
|
2291
|
+
function record2(value) {
|
|
2292
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2293
|
+
}
|
|
2294
|
+
function decodeResponseBuffer(buffer) {
|
|
2295
|
+
if (buffer.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2296
|
+
const length = buffer.readUInt32BE(0);
|
|
2297
|
+
if (length === 0 || length > HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC response length is invalid: ${refusalFact("declared_bytes", length, `between 1 and ${HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES}`)}.`);
|
|
2298
|
+
if (buffer.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2299
|
+
if (buffer.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC returned trailing bytes: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `the received message length minus ${HOST_RELAY_IPC_LENGTH_BYTES}` }, { name: "received_bytes", value: buffer.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2300
|
+
return decodeHostRelayIpcResponse(buffer);
|
|
2301
|
+
}
|
|
2302
|
+
function decodeCurrentPromptResponseBuffer(buffer) {
|
|
2303
|
+
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
|
|
2304
|
+
if (value === null) return null;
|
|
2305
|
+
if (!record2(value) || !exactKeys4(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "capacity_exhausted" && value.status !== "current_prompt_unavailable" && value.status !== "released") throw new HostRelayIpcError("relay_unavailable", "Host Relay current-prompt response is invalid.", "response_invalid");
|
|
2306
|
+
return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
|
|
2307
|
+
}
|
|
2308
|
+
function decodeLengthPrefixedJson(input, maximum) {
|
|
2309
|
+
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2310
|
+
const length = input.readUInt32BE(0);
|
|
2311
|
+
if (length === 0 || length > maximum) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC message length is invalid: ${refusalFact("declared_bytes", length, `between 1 and ${maximum}`)}.`);
|
|
2312
|
+
if (input.length < length + HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
2313
|
+
if (input.length !== length + HOST_RELAY_IPC_LENGTH_BYTES) throw new HostRelayIpcError("relay_unavailable", `Host Relay IPC message has trailing bytes: ${refusalFacts([{ name: "declared_bytes", value: length, expected: `the received message length minus ${HOST_RELAY_IPC_LENGTH_BYTES}` }, { name: "received_bytes", value: input.byteLength, expected: `exactly ${length + HOST_RELAY_IPC_LENGTH_BYTES}` }])}.`);
|
|
2314
|
+
try {
|
|
2315
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input.subarray(HOST_RELAY_IPC_LENGTH_BYTES)));
|
|
2316
|
+
} catch {
|
|
2317
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC message is invalid.");
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
function encodeLengthPrefixed(value) {
|
|
2321
|
+
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
2322
|
+
const output = Buffer.allocUnsafe(body.length + HOST_RELAY_IPC_LENGTH_BYTES);
|
|
2323
|
+
output.writeUInt32BE(body.length, 0);
|
|
2324
|
+
body.copy(output, HOST_RELAY_IPC_LENGTH_BYTES);
|
|
2325
|
+
return output;
|
|
2326
|
+
}
|
|
2327
|
+
function exactKeys4(value, required, optional = []) {
|
|
2328
|
+
const keys = Object.keys(value);
|
|
2329
|
+
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2330
|
+
}
|
|
2331
|
+
function errorCode(error) {
|
|
2332
|
+
return record2(error) && typeof error.code === "string" ? error.code : null;
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
// host/relay/src/host-producer-core.ts
|
|
2336
|
+
var HOST_TRANSCRIPT_CURSOR_REASONS = vocabulary(["transcript_replaced", "transcript_truncated", "transcript_rewritten", "transcript_malformed"]);
|
|
2337
|
+
function isHostTranscriptCursorReason(value) {
|
|
2338
|
+
return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
|
|
2339
|
+
}
|
|
2340
|
+
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
2341
|
+
var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
|
|
2342
|
+
function transcriptScanFailureKind(error, adapterFailureKind) {
|
|
2343
|
+
if (transcriptErrorCode(error) === "ELOOP") return "symlink";
|
|
2344
|
+
const adapter = adapterFailureKind(error);
|
|
2345
|
+
if (adapter !== void 0) return adapter;
|
|
2346
|
+
const code = transcriptErrorCode(error);
|
|
2347
|
+
if (code === "EFBIG") return "size_budget";
|
|
2348
|
+
if (code !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code)) return "read_error";
|
|
2349
|
+
return "other";
|
|
2350
|
+
}
|
|
2351
|
+
async function registerHostCurrentPromptDiagnosed(register, diagnostic) {
|
|
2352
|
+
try {
|
|
2353
|
+
await register();
|
|
2354
|
+
diagnostic("current_prompt_registered");
|
|
2355
|
+
return true;
|
|
2356
|
+
} catch (error) {
|
|
2357
|
+
if (!(error instanceof HostRelayCurrentPromptCapacityRefusal)) diagnostic("current_prompt_registration_failed");
|
|
2358
|
+
throw error;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
function transcriptErrorCode(error) {
|
|
2362
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
2363
|
+
}
|
|
2364
|
+
var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
|
|
2365
|
+
async function readTranscriptCompleteChunk(handle, offset, size) {
|
|
2366
|
+
const available = Math.max(0, size - offset);
|
|
2367
|
+
if (available === 0) return "empty";
|
|
2368
|
+
const chunks = [];
|
|
2369
|
+
let position = offset;
|
|
2370
|
+
let total = 0;
|
|
2371
|
+
let lastNewline = -1;
|
|
2372
|
+
while (position < size) {
|
|
2373
|
+
const buffer = Buffer.alloc(Math.min(HOST_TRANSCRIPT_READ_CHUNK_BYTES, size - position));
|
|
2374
|
+
const read = await handle.read(buffer, 0, buffer.length, position);
|
|
2375
|
+
if (read.bytesRead === 0) break;
|
|
2376
|
+
const chunk = buffer.subarray(0, read.bytesRead);
|
|
2377
|
+
chunks.push(chunk);
|
|
2378
|
+
const newline = chunk.lastIndexOf(10);
|
|
2379
|
+
if (newline >= 0) lastNewline = total + newline;
|
|
2380
|
+
total += chunk.length;
|
|
2381
|
+
position += chunk.length;
|
|
2382
|
+
if (lastNewline >= 0) break;
|
|
2383
|
+
}
|
|
2384
|
+
if (lastNewline < 0) return "partial";
|
|
2385
|
+
return Buffer.concat(chunks, total).subarray(0, lastNewline + 1);
|
|
2386
|
+
}
|
|
2387
|
+
async function readTranscriptAnchor(handle, offset, anchorBytes) {
|
|
2388
|
+
const start = Math.max(0, offset - anchorBytes);
|
|
2389
|
+
const anchor = Buffer.alloc(offset - start);
|
|
2390
|
+
if (anchor.length > 0) await handle.read(anchor, 0, anchor.length, start);
|
|
2391
|
+
return { start, sha256: transcriptSha256(anchor) };
|
|
2392
|
+
}
|
|
2393
|
+
async function transcriptAnchorMatches(handle, cursor) {
|
|
2394
|
+
const buffer = Buffer.alloc(cursor.offset - cursor.anchor_start);
|
|
2395
|
+
if (buffer.length > 0) await handle.read(buffer, 0, buffer.length, cursor.anchor_start);
|
|
2396
|
+
return transcriptSha256(buffer) === cursor.anchor_sha256;
|
|
2397
|
+
}
|
|
2398
|
+
function transcriptCursorIdentityReason(cursor, transcriptPath, device, inode) {
|
|
2399
|
+
if (cursor.device !== device || cursor.inode !== inode || cursor.transcript_path !== transcriptPath) return "transcript_replaced";
|
|
2400
|
+
return null;
|
|
2401
|
+
}
|
|
2402
|
+
async function transcriptCursorBoundsReason(handle, cursor, size) {
|
|
2403
|
+
if (size < cursor.offset) return "transcript_truncated";
|
|
2404
|
+
if (cursor.offset > 0 && !await transcriptAnchorMatches(handle, cursor)) return "transcript_rewritten";
|
|
2405
|
+
return null;
|
|
2406
|
+
}
|
|
2407
|
+
function transcriptSha256(value) {
|
|
2408
|
+
return createHash3("sha256").update(value).digest("hex");
|
|
2409
|
+
}
|
|
2410
|
+
function transcriptObjectSha256(value) {
|
|
2411
|
+
return createHash3("sha256").update(JSON.stringify(value), "utf8").digest("hex");
|
|
2412
|
+
}
|
|
2413
|
+
function isTranscriptRecord(value) {
|
|
2414
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2415
|
+
}
|
|
2416
|
+
function isMissingTranscriptError(error) {
|
|
2417
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2418
|
+
}
|
|
2419
|
+
function transcriptTimestamp(now, fail) {
|
|
2420
|
+
const value = now();
|
|
2421
|
+
if (!Number.isFinite(value.getTime())) throw fail();
|
|
2422
|
+
return value.toISOString();
|
|
2423
|
+
}
|
|
2424
|
+
function transcriptExpiration(now, retentionMs, fail) {
|
|
2425
|
+
return new Date(Date.parse(transcriptTimestamp(now, fail)) + retentionMs).toISOString();
|
|
2426
|
+
}
|
|
2427
|
+
function createTranscriptWatcherBase(options, owner) {
|
|
2428
|
+
if (typeof options.outbox_dir !== "string" || options.outbox_dir.length === 0) throw owner.invalid_outbox();
|
|
2429
|
+
return {
|
|
2430
|
+
outboxRetentionMs: options.retention_ms ?? owner.default_retention_ms,
|
|
2431
|
+
now: options.now ?? (() => /* @__PURE__ */ new Date()),
|
|
2432
|
+
log: options.log ?? ((line) => {
|
|
2433
|
+
process.stderr.write(`${line}
|
|
2434
|
+
`);
|
|
2435
|
+
}),
|
|
2436
|
+
producerInstallationRef: options.producer_installation_ref
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
async function scanTranscriptRegistrations(registrations, scanOne) {
|
|
2440
|
+
let records = 0;
|
|
2441
|
+
let enqueued = 0;
|
|
2442
|
+
let quarantined = 0;
|
|
2443
|
+
for (const registration of registrations) {
|
|
2444
|
+
const result = await scanOne(registration);
|
|
2445
|
+
records += result.records;
|
|
2446
|
+
enqueued += result.enqueued;
|
|
2447
|
+
if (result.quarantined) quarantined += 1;
|
|
2448
|
+
}
|
|
2449
|
+
return { registrations: registrations.length, records, enqueued, quarantined };
|
|
2450
|
+
}
|
|
2451
|
+
function formatRetiredTranscriptLog(producerLabel, transcriptPath) {
|
|
2452
|
+
return `${producerLabel} retired a transcript registration: transcript_path=${JSON.stringify(transcriptPath)}, reason=${TRANSCRIPT_MISSING_REASON}.`;
|
|
2453
|
+
}
|
|
2454
|
+
var HostTranscriptWatcher = class {
|
|
2455
|
+
outboxRetentionMs;
|
|
2456
|
+
now;
|
|
2457
|
+
log;
|
|
2458
|
+
producerInstallationRef;
|
|
2459
|
+
clockFailure;
|
|
2460
|
+
constructor(options, owner) {
|
|
2461
|
+
const base = createTranscriptWatcherBase(options, owner);
|
|
2462
|
+
this.outboxRetentionMs = base.outboxRetentionMs;
|
|
2463
|
+
this.now = base.now;
|
|
2464
|
+
this.log = base.log;
|
|
2465
|
+
this.producerInstallationRef = base.producerInstallationRef;
|
|
2466
|
+
this.clockFailure = owner.clock_failure;
|
|
2467
|
+
}
|
|
2468
|
+
async scanOnce() {
|
|
2469
|
+
return scanTranscriptRegistrations(await this.listRegistrations(), (registration) => this.scanRegistration(registration));
|
|
2470
|
+
}
|
|
2471
|
+
async scanRegistration(registration) {
|
|
2472
|
+
try {
|
|
2473
|
+
return await this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
|
|
2474
|
+
missing: () => this.retireMissing(registration),
|
|
2475
|
+
failed: (handle, error) => this.scanFailed(handle, registration, error)
|
|
2476
|
+
});
|
|
2477
|
+
} catch (error) {
|
|
2478
|
+
if (error instanceof HostRelayCurrentPromptCapacityRefusal) return { records: 0, enqueued: 0, quarantined: false };
|
|
2479
|
+
throw error;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
async openTranscript(registration, scan, handlers) {
|
|
2483
|
+
let handle;
|
|
2484
|
+
try {
|
|
2485
|
+
handle = await open2(this.transcriptPathOf(registration), O_RDONLY2 | O_NOFOLLOW);
|
|
2486
|
+
return await scan(handle);
|
|
2487
|
+
} catch (error) {
|
|
2488
|
+
if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
|
|
2489
|
+
if (hostRelayExchangeFailure(error) !== void 0 || error instanceof HostRelayCurrentPromptCapacityRefusal) throw error;
|
|
2490
|
+
return handlers.failed(handle, error);
|
|
2491
|
+
} finally {
|
|
2492
|
+
await handle?.close();
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
/**
|
|
2496
|
+
* A transcript the Host deleted never comes back, so retrying its
|
|
2497
|
+
* registration once per reconcile interval is a failure with no end.
|
|
2498
|
+
* Retire it at the scan that finds it missing and say so once. Only
|
|
2499
|
+
* ENOENT means gone; a permission or busy failure is about this moment,
|
|
2500
|
+
* not about the file, and keeps the registration for the next scan.
|
|
2501
|
+
*/
|
|
2502
|
+
async retireMissingTranscript(transcriptPath, producerLabel, retire) {
|
|
2503
|
+
await retire();
|
|
2504
|
+
this.log(formatRetiredTranscriptLog(producerLabel, transcriptPath));
|
|
2505
|
+
return { records: 0, enqueued: 0, quarantined: false };
|
|
2506
|
+
}
|
|
2507
|
+
stamp() {
|
|
2508
|
+
return transcriptTimestamp(this.now, this.clockFailure);
|
|
2509
|
+
}
|
|
2510
|
+
expire(retentionMs) {
|
|
2511
|
+
return transcriptExpiration(this.now, retentionMs, this.clockFailure);
|
|
2512
|
+
}
|
|
2513
|
+
sha(value) {
|
|
2514
|
+
return transcriptSha256(value);
|
|
2515
|
+
}
|
|
2516
|
+
objectSha(value) {
|
|
2517
|
+
return transcriptObjectSha256(value);
|
|
2518
|
+
}
|
|
2519
|
+
isRecord(value) {
|
|
2520
|
+
return isTranscriptRecord(value);
|
|
2521
|
+
}
|
|
2522
|
+
readChunk(handle, offset, size) {
|
|
2523
|
+
return readTranscriptCompleteChunk(handle, offset, size);
|
|
2524
|
+
}
|
|
2525
|
+
readAnchor(handle, offset, anchorBytes) {
|
|
2526
|
+
return readTranscriptAnchor(handle, offset, anchorBytes);
|
|
2527
|
+
}
|
|
2528
|
+
cursorIdentityReason(cursor, transcriptPath, device, inode) {
|
|
2529
|
+
return transcriptCursorIdentityReason(cursor, transcriptPath, device, inode);
|
|
2530
|
+
}
|
|
2531
|
+
cursorBoundsReason(handle, cursor, size) {
|
|
2532
|
+
return transcriptCursorBoundsReason(handle, cursor, size);
|
|
2533
|
+
}
|
|
2534
|
+
};
|
|
2535
|
+
var HOST_PRODUCER_STATE_IDENTITY = {
|
|
2536
|
+
codex: { file_name: "codex-producer-state.json", schema: "ceal.codex_producer_state.v1" },
|
|
2537
|
+
claude: { file_name: "claude-producer-state.json", schema: "ceal.claude_producer_state.v1" }
|
|
2538
|
+
};
|
|
2539
|
+
var HostProducerDrainCore = class {
|
|
2540
|
+
host;
|
|
2541
|
+
relay;
|
|
2542
|
+
statePath;
|
|
2543
|
+
stateSchema;
|
|
2544
|
+
installationRef;
|
|
2545
|
+
epoch;
|
|
2546
|
+
now;
|
|
2547
|
+
tail = Promise.resolve();
|
|
2548
|
+
constructor(host, runtime) {
|
|
2549
|
+
if (!isHostSafeRef(runtime.producer_installation_ref)) throw host.fail("configuration", `${host.producer_label} producer installation identity is invalid.`);
|
|
2550
|
+
const epoch = runtime.producer_epoch ?? 1;
|
|
2551
|
+
if (!Number.isSafeInteger(epoch) || epoch <= 0) throw host.fail("configuration", `${host.producer_label} producer epoch is invalid.`);
|
|
2552
|
+
this.host = host;
|
|
2553
|
+
this.relay = runtime.relay;
|
|
2554
|
+
this.statePath = join2(host.outbox_directory, HOST_PRODUCER_STATE_IDENTITY[host.producer_id].file_name);
|
|
2555
|
+
this.stateSchema = HOST_PRODUCER_STATE_IDENTITY[host.producer_id].schema;
|
|
2556
|
+
this.installationRef = runtime.producer_installation_ref;
|
|
2557
|
+
this.epoch = epoch;
|
|
2558
|
+
this.now = runtime.now ?? (() => /* @__PURE__ */ new Date());
|
|
2559
|
+
}
|
|
2560
|
+
drainOutbox() {
|
|
2561
|
+
const run = this.tail.then(() => this.drainLocked());
|
|
2562
|
+
this.tail = run.then(() => void 0, () => void 0);
|
|
2563
|
+
return run;
|
|
2564
|
+
}
|
|
2565
|
+
async drainLocked() {
|
|
2566
|
+
const read = await this.host.listEnvelopes();
|
|
2567
|
+
let admitted = 0;
|
|
2568
|
+
let expired = 0;
|
|
2569
|
+
let retained = 0;
|
|
2570
|
+
for (const envelope of read.entries) {
|
|
2571
|
+
const reserved = await this.hasReservation(this.host.submissionRefOf(envelope));
|
|
2572
|
+
if (!reserved && Date.parse(this.host.expiresAtOf(envelope)) <= this.now().getTime()) {
|
|
2573
|
+
await this.host.removeEnvelope(envelope);
|
|
2574
|
+
expired += 1;
|
|
2575
|
+
continue;
|
|
2576
|
+
}
|
|
2577
|
+
const frames = await this.reserve(envelope);
|
|
2578
|
+
let complete = true;
|
|
2579
|
+
for (const frame of frames) {
|
|
2580
|
+
const outcome = await this.relay.admit(frame);
|
|
2581
|
+
if (outcome.code !== "admitted" && outcome.code !== "already_admitted") {
|
|
2582
|
+
complete = false;
|
|
2583
|
+
break;
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
if (!complete) {
|
|
2587
|
+
retained += 1;
|
|
2588
|
+
break;
|
|
2589
|
+
}
|
|
2590
|
+
await this.host.removeEnvelope(envelope);
|
|
2591
|
+
await this.release(this.host.submissionRefOf(envelope));
|
|
2592
|
+
admitted += 1;
|
|
2593
|
+
}
|
|
2594
|
+
return { admitted, retained: retained + Math.max(0, read.entries.length - admitted - expired - retained), expired, malformed: read.malformed };
|
|
2595
|
+
}
|
|
2596
|
+
async hasReservation(submissionRef) {
|
|
2597
|
+
return (await this.readState()).pending[submissionRef] !== void 0;
|
|
2598
|
+
}
|
|
2599
|
+
async reserve(envelope) {
|
|
2600
|
+
const state = await this.readState();
|
|
2601
|
+
const submissionRef = this.host.submissionRefOf(envelope);
|
|
2602
|
+
const existing = state.pending[submissionRef];
|
|
2603
|
+
if (existing !== void 0) return existing;
|
|
2604
|
+
const drafts = await this.host.mapEnvelope(envelope);
|
|
2605
|
+
const frames = drafts.map((draft, offset) => ({
|
|
2606
|
+
schema_version: "ceal.host_producer_frame.v1",
|
|
2607
|
+
producer_id: this.host.producer_id,
|
|
2608
|
+
producer_installation_ref: this.installationRef,
|
|
2609
|
+
producer_epoch: this.epoch,
|
|
2610
|
+
sequence: state.next_sequence + offset,
|
|
2611
|
+
frame_kind: draft.frame_kind,
|
|
2612
|
+
occurred_at: this.host.createdAtOf(envelope),
|
|
2613
|
+
payload: draft.payload
|
|
2614
|
+
}));
|
|
2615
|
+
await this.writeState({ ...state, next_sequence: state.next_sequence + frames.length, pending: { ...state.pending, [submissionRef]: frames } });
|
|
2616
|
+
return frames;
|
|
2617
|
+
}
|
|
2618
|
+
async release(submissionRef) {
|
|
2619
|
+
const state = await this.readState();
|
|
2620
|
+
if (state.pending[submissionRef] === void 0) return;
|
|
2621
|
+
const pending = { ...state.pending };
|
|
2622
|
+
delete pending[submissionRef];
|
|
2623
|
+
await this.writeState({ ...state, pending });
|
|
2624
|
+
}
|
|
2625
|
+
async readState() {
|
|
2626
|
+
try {
|
|
2627
|
+
const value = JSON.parse(await readFile(this.statePath, "utf8"));
|
|
2628
|
+
return this.decodeState(value);
|
|
2629
|
+
} catch (error) {
|
|
2630
|
+
if (errorCode2(error) !== "ENOENT") throw error;
|
|
2631
|
+
return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: 1, pending: {} };
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
async writeState(state) {
|
|
2635
|
+
await mkdir2(this.host.outbox_directory, { recursive: true, mode: 448 });
|
|
2636
|
+
const temporary = `${this.statePath}.${process.pid}.new`;
|
|
2637
|
+
await writeFile(temporary, JSON.stringify(state), { mode: 384 });
|
|
2638
|
+
await rename2(temporary, this.statePath);
|
|
2639
|
+
}
|
|
2640
|
+
decodeState(value) {
|
|
2641
|
+
const label = this.host.producer_label;
|
|
2642
|
+
const fail = (code, message) => this.host.fail(code, message);
|
|
2643
|
+
if (!isDrainStateHeader(value, this.stateSchema, this.installationRef)) {
|
|
2644
|
+
throw fail("storage", `${label} producer state header is malformed or belongs to another installation (${refusalFacts([
|
|
2645
|
+
{ name: "schema_version", value: isTranscriptRecord(value) ? value.schema_version : void 0, expected: this.stateSchema },
|
|
2646
|
+
{ name: "producer_installation_ref", value: isTranscriptRecord(value) ? value.producer_installation_ref : void 0, expected: this.installationRef }
|
|
2647
|
+
])}).`);
|
|
2648
|
+
}
|
|
2649
|
+
if (Number(value.producer_epoch) > this.epoch) throw fail("storage", `${label} producer state epoch is ahead of this installation (${refusalFact("producer_epoch", value.producer_epoch, `at most ${this.epoch}`)}).`);
|
|
2650
|
+
const pending = {};
|
|
2651
|
+
const storedEpoch = Number(value.producer_epoch);
|
|
2652
|
+
for (const [submissionRef, frames] of Object.entries(value.pending)) {
|
|
2653
|
+
if (!isHostSafeRef(submissionRef) || !Array.isArray(frames) || frames.length === 0) throw fail("storage", `${label} producer pending reservation is malformed.`);
|
|
2654
|
+
pending[submissionRef] = frames.map((frame) => this.decodeStoredFrame(frame, storedEpoch));
|
|
2655
|
+
}
|
|
2656
|
+
return { schema_version: this.stateSchema, producer_installation_ref: this.installationRef, producer_epoch: this.epoch, next_sequence: Number(value.next_sequence), pending };
|
|
2657
|
+
}
|
|
2658
|
+
decodeStoredFrame(value, storedEpoch) {
|
|
2659
|
+
const label = this.host.producer_label;
|
|
2660
|
+
const json = Buffer.from(JSON.stringify(value), "utf8");
|
|
2661
|
+
const encoded = Buffer.allocUnsafe(json.length + 4);
|
|
2662
|
+
encoded.writeUInt32BE(json.length, 0);
|
|
2663
|
+
json.copy(encoded, 4);
|
|
2664
|
+
const frame = decodeHostProducerFrame(encoded);
|
|
2665
|
+
if (frame.producer_id !== this.host.producer_id || frame.producer_installation_ref !== this.installationRef || frame.producer_epoch > storedEpoch) throw this.host.fail("storage", `${label} producer pending frame identity is malformed (${refusalFacts([
|
|
2666
|
+
{ name: "producer_id", value: frame.producer_id, expected: this.host.producer_id },
|
|
2667
|
+
{ name: "producer_installation_ref", value: frame.producer_installation_ref, expected: this.installationRef },
|
|
2668
|
+
{ name: "producer_epoch", value: frame.producer_epoch, expected: `at most ${storedEpoch}` }
|
|
2669
|
+
])}).`);
|
|
2670
|
+
return frame;
|
|
2671
|
+
}
|
|
2672
|
+
};
|
|
2673
|
+
function isDrainStateHeader(value, schema, installationRef) {
|
|
2674
|
+
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);
|
|
2675
|
+
}
|
|
2676
|
+
function errorCode2(error) {
|
|
2677
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// host/relay/src/relay-quarantine-store.ts
|
|
2681
|
+
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
2682
|
+
var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
|
|
2683
|
+
var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
|
|
2684
|
+
|
|
2685
|
+
// host/relay/src/relay-admission-store.ts
|
|
2686
|
+
var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, producer_count: 4096, producer_bytes: 32 * 1024 * 1024 };
|
|
2687
|
+
var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
2688
|
+
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
2689
|
+
|
|
2690
|
+
// host/relay/src/relay-gateway-bridge.ts
|
|
2691
|
+
var HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS = ["created", "adopted", "replayed", "approval_approved", "approval_denied", "approval_replayed"];
|
|
2692
|
+
function isHostGatewayAcknowledgementDisposition(value) {
|
|
2693
|
+
return typeof value === "string" && HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS.some((disposition) => disposition === value);
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
// host/codex/src/codex-invocation-context.ts
|
|
2697
|
+
function codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef) {
|
|
2698
|
+
return invocationContextFromTurn(identity, promptRef, expiresAt, { schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION, validate: validateCodexHostInvocationContext, turn_generation_ref: turnGenerationRef });
|
|
2699
|
+
}
|
|
2700
|
+
function validateCodexHostInvocationContext(value) {
|
|
2701
|
+
if (!isContextShape(value)) {
|
|
2702
|
+
throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2703
|
+
}
|
|
2704
|
+
validateIdentity(value.identity);
|
|
2705
|
+
validateContextReferences(value);
|
|
2706
|
+
validateContextTimes(value);
|
|
2707
|
+
}
|
|
2708
|
+
function invocationContextFromBinding(binding, turnGenerationRef) {
|
|
2709
|
+
const context = {
|
|
2710
|
+
schema_version: CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION,
|
|
2711
|
+
identity: binding.identity,
|
|
2712
|
+
prompt_ref: binding.prompt_ref,
|
|
2713
|
+
request_ref: binding.request_ref,
|
|
2714
|
+
bound_at: binding.bound_at,
|
|
2715
|
+
expires_at: binding.expires_at,
|
|
2716
|
+
...turnGenerationRef === void 0 ? {} : { turn_generation_ref: turnGenerationRef }
|
|
2717
|
+
};
|
|
2718
|
+
validateCodexHostInvocationContext(context);
|
|
2719
|
+
return Object.freeze(context);
|
|
2720
|
+
}
|
|
2721
|
+
function validateIdentity(value) {
|
|
2722
|
+
if (!isRecord3(value) || !hasHostInvocationIdentityKeys(value) || ![value.host_session_ref, value.host_turn_ref, value.host_source_ref].every(safeRef3)) {
|
|
2723
|
+
throw new CodexHostAdapterError("storage", "Codex Host invocation identity is malformed.");
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
function isContextShape(value) {
|
|
2727
|
+
return isRecord3(value) && hasHostInvocationContextKeys(value, ["request_ref", "bound_at", "turn_generation_ref"]) && value.schema_version === CODEX_INVOCATION_CONTEXT_SCHEMA_VERSION;
|
|
2728
|
+
}
|
|
2729
|
+
function validateContextReferences(value) {
|
|
2730
|
+
if (!safeRef3(value.prompt_ref) || !optionalSafeRef(value.request_ref) || !optionalSafeRef(value.turn_generation_ref)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2731
|
+
}
|
|
2732
|
+
function validateContextTimes(value) {
|
|
2733
|
+
const expiresAt = value.expires_at;
|
|
2734
|
+
const boundAt = value.bound_at;
|
|
2735
|
+
if (!timestamp2(expiresAt) || !optionalTimestamp(boundAt)) throw new CodexHostAdapterError("storage", "Codex Host invocation context is malformed.");
|
|
2736
|
+
if (typeof boundAt === "string" && Date.parse(boundAt) > Date.parse(expiresAt)) throw new CodexHostAdapterError("storage", `Codex Host invocation context has invalid binding time (${refusalFact("bound_at", boundAt, `at or before ${expiresAt}`)}).`);
|
|
2737
|
+
}
|
|
2738
|
+
function optionalSafeRef(value) {
|
|
2739
|
+
return value === void 0 || safeRef3(value);
|
|
2740
|
+
}
|
|
2741
|
+
function optionalTimestamp(value) {
|
|
2742
|
+
return value === void 0 || timestamp2(value);
|
|
2743
|
+
}
|
|
2744
|
+
function safeRef3(value) {
|
|
2745
|
+
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
2746
|
+
}
|
|
2747
|
+
function timestamp2(value) {
|
|
2748
|
+
return typeof value === "string" && value.length <= 64 && Number.isFinite(Date.parse(value));
|
|
2749
|
+
}
|
|
2750
|
+
function isRecord3(value) {
|
|
2751
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
// host/codex/src/codex-outbox.ts
|
|
2755
|
+
var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2756
|
+
var CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS = 4096;
|
|
2757
|
+
var CodexOutboxStore = class {
|
|
2758
|
+
directory;
|
|
2759
|
+
entriesDirectory;
|
|
2760
|
+
bindingsDirectory;
|
|
2761
|
+
sessionsDirectory;
|
|
2762
|
+
activeContextsDirectory;
|
|
2763
|
+
turnSelectionsDirectory;
|
|
2764
|
+
registrationsDirectory;
|
|
2765
|
+
cursorsDirectory;
|
|
2766
|
+
constructor(directory) {
|
|
2767
|
+
if (typeof directory !== "string" || directory.length === 0) throw new CodexHostAdapterError("configuration", "Codex outbox directory is required.");
|
|
2768
|
+
this.directory = directory;
|
|
2769
|
+
this.entriesDirectory = join3(directory, "entries");
|
|
2770
|
+
this.bindingsDirectory = join3(directory, "bindings");
|
|
2771
|
+
this.sessionsDirectory = join3(directory, "sessions");
|
|
2772
|
+
this.activeContextsDirectory = join3(directory, "active-contexts");
|
|
2773
|
+
this.turnSelectionsDirectory = join3(directory, "turn-selections");
|
|
2774
|
+
this.registrationsDirectory = join3(directory, "transcript-registrations");
|
|
2775
|
+
this.cursorsDirectory = join3(directory, "transcript-cursors");
|
|
2776
|
+
}
|
|
2777
|
+
async prepare() {
|
|
2778
|
+
for (const path of [this.directory, this.entriesDirectory, this.bindingsDirectory, this.sessionsDirectory, this.activeContextsDirectory, this.turnSelectionsDirectory, this.registrationsDirectory, this.cursorsDirectory]) {
|
|
2779
|
+
await ensurePrivateOwnerDirectory(path);
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
async put(entry) {
|
|
2783
|
+
validateEnvelope(entry);
|
|
2784
|
+
await this.prepare();
|
|
2785
|
+
await writeAtomicJson(this.entryPath(entry.submission_ref), entry);
|
|
2786
|
+
}
|
|
2787
|
+
async list() {
|
|
2788
|
+
await this.prepare();
|
|
2789
|
+
const entries = [];
|
|
2790
|
+
let malformed = 0;
|
|
2791
|
+
for (const name of await readdir(this.entriesDirectory)) {
|
|
2792
|
+
if (!name.endsWith(".json")) continue;
|
|
2793
|
+
try {
|
|
2794
|
+
const value = JSON.parse(await readPrivateFile(join3(this.entriesDirectory, name)));
|
|
2795
|
+
validateEnvelope(value);
|
|
2796
|
+
entries.push(value);
|
|
2797
|
+
} catch {
|
|
2798
|
+
malformed += 1;
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
entries.sort((left, right) => left.created_at.localeCompare(right.created_at));
|
|
2802
|
+
return { entries, malformed };
|
|
2803
|
+
}
|
|
2804
|
+
async remove(entry) {
|
|
2805
|
+
validateEnvelope(entry);
|
|
2806
|
+
try {
|
|
2807
|
+
await unlink2(this.entryPath(entry.submission_ref));
|
|
2808
|
+
} catch (error) {
|
|
2809
|
+
if (!isNotFound(error)) throw storageError("Unable to remove the Codex outbox entry.");
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
async writeBinding(binding) {
|
|
2813
|
+
validateBinding(binding);
|
|
2814
|
+
await this.prepare();
|
|
2815
|
+
await writeAtomicJson(this.bindingPath(binding.identity), binding);
|
|
2816
|
+
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Select the exact Host turn that model-reachable commands may use. A new
|
|
2819
|
+
* prompt replaces this projection, so it cannot inherit the prior turn's
|
|
2820
|
+
* Request by session alone.
|
|
2821
|
+
*/
|
|
2822
|
+
async activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef) {
|
|
2823
|
+
const context = codexInvocationContextFromTurn(identity, promptRef, expiresAt, turnGenerationRef);
|
|
2824
|
+
await this.prepare();
|
|
2825
|
+
const path = this.activeContextPath(identity);
|
|
2826
|
+
try {
|
|
1838
2827
|
await writeAtomicJson(path, context);
|
|
1839
2828
|
} catch (error) {
|
|
1840
2829
|
try {
|
|
1841
|
-
await
|
|
2830
|
+
await unlink2(path);
|
|
1842
2831
|
} catch (unlinkError) {
|
|
1843
2832
|
if (!isNotFound(unlinkError)) throw storageError("Unable to clear a failed Codex Host context replacement.");
|
|
1844
2833
|
}
|
|
1845
2834
|
throw error;
|
|
1846
2835
|
}
|
|
1847
2836
|
}
|
|
2837
|
+
async writeInvocationTurnSelection(selection) {
|
|
2838
|
+
if (!isHostInvocationTurnSelection(selection)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection is invalid.");
|
|
2839
|
+
await this.prepare();
|
|
2840
|
+
await writeAtomicJson(this.turnSelectionPath(selection.host_source_ref, selection.host_session_ref), selection);
|
|
2841
|
+
}
|
|
2842
|
+
async readInvocationTurnSelection(hostSourceRef, hostSessionRef) {
|
|
2843
|
+
if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host turn selection identity is invalid.");
|
|
2844
|
+
await this.prepare();
|
|
2845
|
+
return readOptionalJson(this.turnSelectionPath(hostSourceRef, hostSessionRef), validateTurnSelection);
|
|
2846
|
+
}
|
|
2847
|
+
async attachInvocationTurnGeneration(identity, promptRef, turnGenerationRef) {
|
|
2848
|
+
const selection = await this.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
2849
|
+
if (selection === null || selection.prompt_ref !== promptRef || selection.turn_generation_ref !== turnGenerationRef) return false;
|
|
2850
|
+
const path = this.activeContextPath(identity);
|
|
2851
|
+
const active = await readOptionalJson(path, validateCodexHostInvocationContext);
|
|
2852
|
+
if (active === null || active.prompt_ref !== promptRef || !sameIdentity(active.identity, identity)) return false;
|
|
2853
|
+
await writeAtomicJson(path, { ...active, turn_generation_ref: turnGenerationRef });
|
|
2854
|
+
return true;
|
|
2855
|
+
}
|
|
1848
2856
|
/**
|
|
1849
2857
|
* Publish a Request reference only while the acknowledged binding is still
|
|
1850
2858
|
* the active turn. A delayed relay response therefore cannot overwrite a
|
|
@@ -1854,17 +2862,25 @@ var CodexOutboxStore = class {
|
|
|
1854
2862
|
validateBinding(binding);
|
|
1855
2863
|
const active = await this.readInvocationContext(binding.identity.host_source_ref, binding.identity.host_session_ref, now);
|
|
1856
2864
|
if (active === null || !sameIdentity(active.identity, binding.identity)) return false;
|
|
1857
|
-
await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding));
|
|
2865
|
+
await writeAtomicJson(this.activeContextPath(binding.identity), invocationContextFromBinding(binding, active.turn_generation_ref));
|
|
1858
2866
|
return true;
|
|
1859
2867
|
}
|
|
1860
|
-
/** Return only the
|
|
2868
|
+
/** Return only the live session projection; never fall back to another turn. */
|
|
1861
2869
|
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
2870
|
if (!(now instanceof Date) || !Number.isFinite(now.getTime())) throw new CodexHostAdapterError("invalid_input", "Codex Host context clock is invalid.");
|
|
2871
|
+
const context = await this.readRawInvocationContext(hostSourceRef, hostSessionRef);
|
|
2872
|
+
return isLiveSessionProjection(context, now) && invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
|
|
2873
|
+
}
|
|
2874
|
+
/**
|
|
2875
|
+
* The latest Codex session projection even when past expiry, so the
|
|
2876
|
+
* invocation authenticator can report an expired context distinctly from
|
|
2877
|
+
* a missing one. Every other caller keeps the fail-closed read above.
|
|
2878
|
+
*/
|
|
2879
|
+
async readRawInvocationContext(hostSourceRef, hostSessionRef) {
|
|
2880
|
+
if (!SAFE_REF4.test(hostSourceRef) || !SAFE_REF4.test(hostSessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex Host session identity is invalid.");
|
|
1864
2881
|
await this.prepare();
|
|
1865
|
-
const context = await
|
|
1866
|
-
|
|
1867
|
-
return Date.parse(context.expires_at) > now.getTime() ? context : null;
|
|
2882
|
+
const context = await readLatestSessionProjection(this.activeContextPath({ host_source_ref: hostSourceRef, host_session_ref: hostSessionRef }), hostSourceRef, hostSessionRef, (file) => readOptionalJson(file, validateCodexHostInvocationContext));
|
|
2883
|
+
return invocationTurnSelectionMatches(context, await this.readInvocationTurnSelection(hostSourceRef, hostSessionRef)) ? context : null;
|
|
1868
2884
|
}
|
|
1869
2885
|
/** Clear the projection only if the caller still owns its exact active turn. */
|
|
1870
2886
|
async clearInvocationContext(identity, now = /* @__PURE__ */ new Date()) {
|
|
@@ -1872,7 +2888,7 @@ var CodexOutboxStore = class {
|
|
|
1872
2888
|
const active = await this.readInvocationContext(identity.host_source_ref, identity.host_session_ref, now);
|
|
1873
2889
|
if (active === null || !sameIdentity(active.identity, identity)) return false;
|
|
1874
2890
|
try {
|
|
1875
|
-
await
|
|
2891
|
+
await unlink2(this.activeContextPath(identity));
|
|
1876
2892
|
} catch (error) {
|
|
1877
2893
|
if (!isNotFound(error)) throw storageError("Unable to clear the Codex Host invocation context.");
|
|
1878
2894
|
}
|
|
@@ -1890,7 +2906,7 @@ var CodexOutboxStore = class {
|
|
|
1890
2906
|
}
|
|
1891
2907
|
async readSession(sourceRef, sessionRef) {
|
|
1892
2908
|
const identity = { host_source_ref: sourceRef, host_session_ref: sessionRef };
|
|
1893
|
-
if (!
|
|
2909
|
+
if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) throw new CodexHostAdapterError("invalid_input", "Codex session identity is invalid.");
|
|
1894
2910
|
await this.prepare();
|
|
1895
2911
|
return readOptionalJson(this.sessionPath(identity.host_source_ref, identity.host_session_ref), validateSession);
|
|
1896
2912
|
}
|
|
@@ -1923,7 +2939,7 @@ var CodexOutboxStore = class {
|
|
|
1923
2939
|
for (const name of await readdir(this.registrationsDirectory)) {
|
|
1924
2940
|
if (!name.endsWith(".json")) continue;
|
|
1925
2941
|
try {
|
|
1926
|
-
const value = JSON.parse(await readPrivateFile(
|
|
2942
|
+
const value = JSON.parse(await readPrivateFile(join3(this.registrationsDirectory, name)));
|
|
1927
2943
|
validateRegistration(value);
|
|
1928
2944
|
registrations.push(value);
|
|
1929
2945
|
} catch {
|
|
@@ -1942,7 +2958,7 @@ var CodexOutboxStore = class {
|
|
|
1942
2958
|
await writeAtomicJson(this.cursorPath(cursor.host_source_ref, cursor.host_session_ref), cursor);
|
|
1943
2959
|
}
|
|
1944
2960
|
async findByAlias(sourceRef, sessionRef, alias) {
|
|
1945
|
-
if (!
|
|
2961
|
+
if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef) || !SAFE_REF4.test(alias)) return null;
|
|
1946
2962
|
const read = await this.list();
|
|
1947
2963
|
const envelope = findEnvelopeByAlias(read.entries, sourceRef, sessionRef, alias);
|
|
1948
2964
|
if (envelope !== null) return envelopeMatch(envelope);
|
|
@@ -1952,7 +2968,7 @@ var CodexOutboxStore = class {
|
|
|
1952
2968
|
return null;
|
|
1953
2969
|
}
|
|
1954
2970
|
async hasPriorTurn(sourceRef, sessionRef) {
|
|
1955
|
-
if (!
|
|
2971
|
+
if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) return false;
|
|
1956
2972
|
const read = await this.list();
|
|
1957
2973
|
if (read.entries.some((entry) => entry.identity.host_source_ref === sourceRef && entry.identity.host_session_ref === sessionRef)) return true;
|
|
1958
2974
|
await this.prepare();
|
|
@@ -1964,34 +2980,37 @@ var CodexOutboxStore = class {
|
|
|
1964
2980
|
return false;
|
|
1965
2981
|
}
|
|
1966
2982
|
entryPath(submissionRef) {
|
|
1967
|
-
if (!
|
|
1968
|
-
return
|
|
2983
|
+
if (!SAFE_REF4.test(submissionRef)) throw storageError("Codex submission identity is malformed.");
|
|
2984
|
+
return join3(this.entriesDirectory, `${cealRequestSha256(submissionRef)}.json`);
|
|
1969
2985
|
}
|
|
1970
2986
|
bindingPath(identity) {
|
|
1971
|
-
return
|
|
2987
|
+
return join3(this.bindingsDirectory, `${identityKey(identity)}.json`);
|
|
1972
2988
|
}
|
|
1973
2989
|
sessionPath(sourceRef, sessionRef) {
|
|
1974
|
-
return
|
|
2990
|
+
return join3(this.sessionsDirectory, `${identityKey({ host_source_ref: sourceRef, host_session_ref: sessionRef, host_turn_ref: "session" })}.json`);
|
|
1975
2991
|
}
|
|
1976
2992
|
activeContextPath(identity) {
|
|
1977
|
-
return
|
|
2993
|
+
return join3(this.activeContextsDirectory, `${invocationContextKey(identity)}.json`);
|
|
2994
|
+
}
|
|
2995
|
+
turnSelectionPath(sourceRef, sessionRef) {
|
|
2996
|
+
return join3(this.turnSelectionsDirectory, `${invocationContextKey({ host_source_ref: sourceRef, host_session_ref: sessionRef })}.json`);
|
|
1978
2997
|
}
|
|
1979
2998
|
registrationPath(sourceRef, sessionRef) {
|
|
1980
|
-
return
|
|
2999
|
+
return join3(this.registrationsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
|
|
1981
3000
|
}
|
|
1982
3001
|
cursorPath(sourceRef, sessionRef) {
|
|
1983
|
-
return
|
|
3002
|
+
return join3(this.cursorsDirectory, `${sessionStateKey(sourceRef, sessionRef)}.json`);
|
|
1984
3003
|
}
|
|
1985
3004
|
};
|
|
1986
3005
|
async function removeOwnerState(path, what) {
|
|
1987
3006
|
try {
|
|
1988
|
-
await
|
|
3007
|
+
await unlink2(path);
|
|
1989
3008
|
} catch (error) {
|
|
1990
3009
|
if (!isNotFound(error)) throw storageError(`Unable to retire the Codex transcript ${what}.`);
|
|
1991
3010
|
}
|
|
1992
3011
|
}
|
|
1993
3012
|
function sessionStateKey(sourceRef, sessionRef) {
|
|
1994
|
-
if (!
|
|
3013
|
+
if (!SAFE_REF4.test(sourceRef) || !SAFE_REF4.test(sessionRef)) throw storageError("Codex transcript session identity is malformed.");
|
|
1995
3014
|
return cealRequestSha256({ host_source_ref: sourceRef, host_session_ref: sessionRef });
|
|
1996
3015
|
}
|
|
1997
3016
|
function envelopeMatch(envelope) {
|
|
@@ -2018,7 +3037,7 @@ async function findBindingByAlias(directory, sourceRef, sessionRef, alias) {
|
|
|
2018
3037
|
}
|
|
2019
3038
|
async function readBindingCandidate(directory, name) {
|
|
2020
3039
|
try {
|
|
2021
|
-
const value = JSON.parse(await readPrivateFile(
|
|
3040
|
+
const value = JSON.parse(await readPrivateFile(join3(directory, name)));
|
|
2022
3041
|
validateBinding(value);
|
|
2023
3042
|
return value;
|
|
2024
3043
|
} catch {
|
|
@@ -2037,18 +3056,18 @@ async function writeAtomicJson(path, value) {
|
|
|
2037
3056
|
if (text === void 0) throw storageError("Codex owner state cannot be serialized.");
|
|
2038
3057
|
const temp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
2039
3058
|
try {
|
|
2040
|
-
const handle = await
|
|
3059
|
+
const handle = await open3(temp, "wx", 384);
|
|
2041
3060
|
try {
|
|
2042
3061
|
await handle.writeFile(text, "utf8");
|
|
2043
3062
|
await handle.sync();
|
|
2044
3063
|
} finally {
|
|
2045
3064
|
await handle.close();
|
|
2046
3065
|
}
|
|
2047
|
-
await
|
|
3066
|
+
await rename3(temp, path);
|
|
2048
3067
|
await syncDirectory(dirname(path));
|
|
2049
3068
|
} catch (error) {
|
|
2050
3069
|
try {
|
|
2051
|
-
await
|
|
3070
|
+
await unlink2(temp);
|
|
2052
3071
|
} catch {
|
|
2053
3072
|
}
|
|
2054
3073
|
if (error instanceof CodexHostAdapterError) throw error;
|
|
@@ -2057,7 +3076,7 @@ async function writeAtomicJson(path, value) {
|
|
|
2057
3076
|
}
|
|
2058
3077
|
async function syncDirectory(path) {
|
|
2059
3078
|
try {
|
|
2060
|
-
const handle = await
|
|
3079
|
+
const handle = await open3(path, "r");
|
|
2061
3080
|
try {
|
|
2062
3081
|
await handle.sync();
|
|
2063
3082
|
} finally {
|
|
@@ -2079,12 +3098,12 @@ async function readOptionalJson(path, validate) {
|
|
|
2079
3098
|
}
|
|
2080
3099
|
async function ensurePrivateOwnerDirectory(path) {
|
|
2081
3100
|
try {
|
|
2082
|
-
await
|
|
3101
|
+
await lstat2(path);
|
|
2083
3102
|
} catch (error) {
|
|
2084
3103
|
if (!isNotFound(error)) throw storageError("Codex owner state directory is unavailable.");
|
|
2085
|
-
await
|
|
3104
|
+
await mkdir3(path, { recursive: true, mode: 448 });
|
|
2086
3105
|
}
|
|
2087
|
-
const stats = await
|
|
3106
|
+
const stats = await lstat2(path);
|
|
2088
3107
|
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
2089
3108
|
if (!stats.isDirectory() || stats.isSymbolicLink()) throw storageError(`Codex owner state path is not a directory (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a directory mode (0o40xxx)")}).`);
|
|
2090
3109
|
if ((stats.mode & 63) !== 0) throw storageError(`Codex owner state directory is readable beyond its owner (${refusalFact("permission_bits", `0o${(stats.mode & 511).toString(8)}`, "no group or other bits")}).`);
|
|
@@ -2093,7 +3112,7 @@ async function ensurePrivateOwnerDirectory(path) {
|
|
|
2093
3112
|
async function readPrivateFile(path) {
|
|
2094
3113
|
let handle;
|
|
2095
3114
|
try {
|
|
2096
|
-
handle = await
|
|
3115
|
+
handle = await open3(path, O_RDONLY3 | O_NOFOLLOW2);
|
|
2097
3116
|
const stats = await handle.stat();
|
|
2098
3117
|
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
2099
3118
|
if (!stats.isFile()) throw storageError(`Codex owner state path is not a regular file (${refusalFact("mode", `0o${stats.mode.toString(8)}`, "a regular-file mode (0o100xxx)")}).`);
|
|
@@ -2105,7 +3124,7 @@ async function readPrivateFile(path) {
|
|
|
2105
3124
|
}
|
|
2106
3125
|
}
|
|
2107
3126
|
function validateEnvelope(value) {
|
|
2108
|
-
if (!
|
|
3127
|
+
if (!isRecord4(value)) throw storageError("Codex outbox entry is malformed.");
|
|
2109
3128
|
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
3129
|
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
3130
|
validateIdentity2(value.identity);
|
|
@@ -2114,22 +3133,22 @@ function validateEnvelope(value) {
|
|
|
2114
3133
|
validateEnvelopeTimestamps(value);
|
|
2115
3134
|
}
|
|
2116
3135
|
function validateEnvelopeReferences(value) {
|
|
2117
|
-
if (!
|
|
3136
|
+
if (!SAFE_REF4.test(String(value.host_input_ref))) throw storageError("Codex outbox input reference is malformed.");
|
|
2118
3137
|
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 && !
|
|
3138
|
+
if (!SAFE_REF4.test(String(value.prompt_ref))) throw storageError("Codex outbox references are malformed.");
|
|
3139
|
+
if (!SAFE_REF4.test(String(value.submission_ref))) throw storageError("Codex outbox references are malformed.");
|
|
3140
|
+
if (value.request_ref !== void 0 && !SAFE_REF4.test(String(value.request_ref))) throw storageError("Codex outbox Request reference is malformed.");
|
|
3141
|
+
if (value.codex_turn_id !== void 0 && !SAFE_REF4.test(String(value.codex_turn_id))) throw storageError("Codex outbox turn identity is malformed.");
|
|
2123
3142
|
if (typeof value.origin_input !== "boolean") throw storageError("Codex outbox origin disposition is malformed.");
|
|
2124
3143
|
}
|
|
2125
3144
|
function validateRegistration(value) {
|
|
2126
|
-
if (!
|
|
3145
|
+
if (!isRecord4(value)) throw storageError("Codex transcript registration is malformed.");
|
|
2127
3146
|
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
3147
|
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
3148
|
validateRegistrationFields(value);
|
|
2130
3149
|
}
|
|
2131
3150
|
function validateRegistrationFields(value) {
|
|
2132
|
-
if (![value.host_source_ref, value.host_session_ref, value.codex_session_id].every((candidate) => typeof candidate === "string" &&
|
|
3151
|
+
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
3152
|
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
3153
|
if (typeof value.transcript_path !== "string") throw storageError(`Codex transcript registration path is malformed (${refusalFact("kind", typeof value.transcript_path, "string")}).`);
|
|
2135
3154
|
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 +3158,15 @@ function validRegistrationCwd(value) {
|
|
|
2139
3158
|
return value === void 0 || typeof value === "string" && isAbsolute2(value) && value.length <= CODEX_TRANSCRIPT_PATH_MAX_CHARACTERS && !value.includes("\0");
|
|
2140
3159
|
}
|
|
2141
3160
|
function validateCursor(value) {
|
|
2142
|
-
if (!
|
|
3161
|
+
if (!isRecord4(value)) throw storageError("Codex transcript cursor is malformed.");
|
|
2143
3162
|
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
3163
|
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
3164
|
if (!validCursorSource(value) || !validCursorPosition(value) || !validCursorStatus(value)) throw storageError("Codex transcript cursor fields are malformed.");
|
|
2146
3165
|
if (value.active_task !== void 0 && !validActiveTask(value.active_task)) throw storageError("Codex transcript task cursor is malformed.");
|
|
2147
|
-
if (value.reason !== void 0 && !
|
|
3166
|
+
if (value.reason !== void 0 && !isHostTranscriptCursorReason(value.reason)) throw storageError("Codex transcript cursor reason is malformed.");
|
|
2148
3167
|
}
|
|
2149
3168
|
function validCursorSource(value) {
|
|
2150
|
-
return [value.host_source_ref, value.host_session_ref].every((candidate) => typeof candidate === "string" &&
|
|
3169
|
+
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
3170
|
}
|
|
2152
3171
|
function validCursorPosition(value) {
|
|
2153
3172
|
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 +3175,23 @@ function validCursorStatus(value) {
|
|
|
2156
3175
|
return (value.status === "active" || value.status === "quarantined") && isTimestamp(value.updated_at);
|
|
2157
3176
|
}
|
|
2158
3177
|
function validActiveTask(value) {
|
|
2159
|
-
if (!
|
|
3178
|
+
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
3179
|
return validActiveTaskIdentity(value) && validActiveTaskReferences(value) && validActiveTaskUsage(value);
|
|
2161
3180
|
}
|
|
2162
3181
|
function validActiveTaskIdentity(value) {
|
|
2163
|
-
return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" &&
|
|
3182
|
+
return [value.codex_turn_id, value.host_turn_ref].every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate)) && isTimestamp(value.started_at);
|
|
2164
3183
|
}
|
|
2165
3184
|
function validActiveTaskReferences(value) {
|
|
2166
3185
|
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
3186
|
}
|
|
2168
3187
|
function optionalSafeRef2(value) {
|
|
2169
|
-
return value === void 0 || typeof value === "string" &&
|
|
3188
|
+
return value === void 0 || typeof value === "string" && SAFE_REF4.test(value);
|
|
2170
3189
|
}
|
|
2171
3190
|
function validActiveTaskUsage(value) {
|
|
2172
3191
|
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
3192
|
}
|
|
2174
3193
|
function validTokenUsage(value) {
|
|
2175
|
-
if (!
|
|
3194
|
+
if (!isRecord4(value) || !sameKeys(value, ["input_tokens", "cached_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens", "total_tokens"])) return false;
|
|
2176
3195
|
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
3196
|
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
3197
|
}
|
|
@@ -2192,30 +3211,33 @@ function validateEnvelopeTimestamps(value) {
|
|
|
2192
3211
|
if (!isTimestamp(value.expires_at)) throw storageError("Codex outbox timestamps are malformed.");
|
|
2193
3212
|
}
|
|
2194
3213
|
function validateBinding(value) {
|
|
2195
|
-
if (!
|
|
3214
|
+
if (!isRecord4(value)) throw storageError("Codex turn binding is malformed.");
|
|
2196
3215
|
if (!sameKeys(value, ["schema_version", "identity", "prompt_ref", "request_ref", "codex_turn_ids", "bound_at", "expires_at"])) throw storageError(`Codex turn binding key set is malformed (${refusalFact("keys", Object.keys(value), "exactly the turn binding key set")}).`);
|
|
2197
3216
|
if (value.schema_version !== CODEX_TURN_BINDING_SCHEMA_VERSION) throw storageError(`Codex turn binding is malformed (${refusalFact("schema_version", value.schema_version, `expected ${CODEX_TURN_BINDING_SCHEMA_VERSION}`)}).`);
|
|
2198
3217
|
validateIdentity2(value.identity);
|
|
2199
|
-
if (!
|
|
3218
|
+
if (!SAFE_REF4.test(String(value.prompt_ref)) || !SAFE_REF4.test(String(value.request_ref))) throw storageError("Codex turn binding references are malformed.");
|
|
2200
3219
|
if (!isStringList(value.codex_turn_ids) || !isTimestamp(value.bound_at) || !isTimestamp(value.expires_at)) throw storageError("Codex turn binding fields are malformed.");
|
|
2201
3220
|
}
|
|
2202
3221
|
function validateSession(value) {
|
|
2203
|
-
if (!
|
|
3222
|
+
if (!isRecord4(value)) throw storageError("Codex session snapshot is malformed.");
|
|
2204
3223
|
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
3224
|
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 (!
|
|
3225
|
+
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.");
|
|
3226
|
+
}
|
|
3227
|
+
function validateTurnSelection(value) {
|
|
3228
|
+
if (!isHostInvocationTurnSelection(value)) throw storageError("Codex Host turn selection is malformed.");
|
|
2207
3229
|
}
|
|
2208
3230
|
function validateIdentity2(value) {
|
|
2209
|
-
if (!
|
|
3231
|
+
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
3232
|
}
|
|
2211
3233
|
function isJsonValue(value) {
|
|
2212
3234
|
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
2213
3235
|
if (typeof value === "number") return Number.isFinite(value);
|
|
2214
3236
|
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
2215
|
-
return
|
|
3237
|
+
return isRecord4(value) && Object.entries(value).every(([key, child]) => key.length > 0 && key.length <= 128 && isJsonValue(child));
|
|
2216
3238
|
}
|
|
2217
3239
|
function isStringList(value) {
|
|
2218
|
-
return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" &&
|
|
3240
|
+
return Array.isArray(value) && value.every((candidate) => typeof candidate === "string" && SAFE_REF4.test(candidate));
|
|
2219
3241
|
}
|
|
2220
3242
|
function isTimestamp(value) {
|
|
2221
3243
|
return typeof value === "string" && value.length <= 64 && !Number.isNaN(Date.parse(value));
|
|
@@ -2225,23 +3247,23 @@ function sameKeys(value, required, optional = []) {
|
|
|
2225
3247
|
const keys = Object.keys(value);
|
|
2226
3248
|
return keys.length >= required.length && keys.length <= allowed.size && required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
|
|
2227
3249
|
}
|
|
2228
|
-
function
|
|
3250
|
+
function isRecord4(value) {
|
|
2229
3251
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2230
3252
|
}
|
|
2231
3253
|
function isNotFound(error) {
|
|
2232
|
-
return
|
|
3254
|
+
return isRecord4(error) && error.code === "ENOENT";
|
|
2233
3255
|
}
|
|
2234
3256
|
function storageError(message) {
|
|
2235
3257
|
return new CodexHostAdapterError("storage", message);
|
|
2236
3258
|
}
|
|
2237
3259
|
|
|
2238
3260
|
// host/codex/src/codex-transcript-baseline.ts
|
|
2239
|
-
import { createHash as
|
|
2240
|
-
import { O_NOFOLLOW as
|
|
2241
|
-
import { open as
|
|
3261
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3262
|
+
import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
|
|
3263
|
+
import { open as open4 } from "node:fs/promises";
|
|
2242
3264
|
|
|
2243
3265
|
// host/codex/src/codex-transcript-decoder.ts
|
|
2244
|
-
var
|
|
3266
|
+
var SAFE_REF5 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/u;
|
|
2245
3267
|
var MAX_PROMPT_BYTES = 64 * 1024;
|
|
2246
3268
|
function decodeCodexTranscriptLine(line) {
|
|
2247
3269
|
let value;
|
|
@@ -2250,40 +3272,40 @@ function decodeCodexTranscriptLine(line) {
|
|
|
2250
3272
|
} catch {
|
|
2251
3273
|
reject2("Codex transcript record is not valid JSON.");
|
|
2252
3274
|
}
|
|
2253
|
-
if (!
|
|
3275
|
+
if (!isRecord5(value)) reject2("Codex transcript record envelope is malformed.");
|
|
2254
3276
|
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,
|
|
3277
|
+
const timestamp5 = safeTimestamp(value.timestamp);
|
|
3278
|
+
if (!isRecord5(value.payload) || typeof value.type !== "string") reject2("Codex transcript record payload is malformed.");
|
|
3279
|
+
if (value.type === "session_meta") return decodeSessionMeta(value.payload, ordinal, timestamp5);
|
|
3280
|
+
if (value.type === "turn_context") return decodeTurnContext(value.payload, ordinal, timestamp5);
|
|
3281
|
+
if (value.type === "event_msg") return decodeEventMessage(value.payload, ordinal, timestamp5);
|
|
3282
|
+
if (value.type === "response_item") return decodeResponseItem(value.payload, ordinal, timestamp5);
|
|
3283
|
+
return { kind: "ignored", ordinal, timestamp: timestamp5 };
|
|
3284
|
+
}
|
|
3285
|
+
function decodeTurnContext(payload, ordinal, timestamp5) {
|
|
2264
3286
|
return {
|
|
2265
3287
|
kind: "turn_context",
|
|
2266
3288
|
ordinal,
|
|
2267
|
-
timestamp:
|
|
2268
|
-
turn_id:
|
|
3289
|
+
timestamp: timestamp5,
|
|
3290
|
+
turn_id: safeRef4(payload.turn_id, "Codex turn context identity is invalid."),
|
|
2269
3291
|
model: boundedNonEmptyText(payload.model, 256, "Codex turn context model is invalid.")
|
|
2270
3292
|
};
|
|
2271
3293
|
}
|
|
2272
|
-
function decodeSessionMeta(payload, ordinal,
|
|
2273
|
-
const sessionId =
|
|
3294
|
+
function decodeSessionMeta(payload, ordinal, timestamp5) {
|
|
3295
|
+
const sessionId = safeRef4(payload.id, "Codex transcript session identity is invalid.");
|
|
2274
3296
|
const cliVersion = boundedNonEmptyText(payload.cli_version, 64, "Codex transcript version is invalid.");
|
|
2275
|
-
return { kind: "session_meta", ordinal, timestamp:
|
|
3297
|
+
return { kind: "session_meta", ordinal, timestamp: timestamp5, session_id: sessionId, cli_version: cliVersion };
|
|
2276
3298
|
}
|
|
2277
|
-
function decodeEventMessage(payload, ordinal,
|
|
3299
|
+
function decodeEventMessage(payload, ordinal, timestamp5) {
|
|
2278
3300
|
if (payload.type === "task_started") {
|
|
2279
|
-
return { kind: "task_started", ordinal, timestamp:
|
|
3301
|
+
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
3302
|
}
|
|
2281
3303
|
if (payload.type === "task_complete") {
|
|
2282
3304
|
return {
|
|
2283
3305
|
kind: "task_complete",
|
|
2284
3306
|
ordinal,
|
|
2285
|
-
timestamp:
|
|
2286
|
-
turn_id:
|
|
3307
|
+
timestamp: timestamp5,
|
|
3308
|
+
turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
|
|
2287
3309
|
started_at: safeNumber(payload.started_at),
|
|
2288
3310
|
completed_at: safeNumber(payload.completed_at),
|
|
2289
3311
|
duration_ms: safeNumber(payload.duration_ms),
|
|
@@ -2294,20 +3316,20 @@ function decodeEventMessage(payload, ordinal, timestamp6) {
|
|
|
2294
3316
|
return {
|
|
2295
3317
|
kind: "turn_aborted",
|
|
2296
3318
|
ordinal,
|
|
2297
|
-
timestamp:
|
|
2298
|
-
turn_id:
|
|
3319
|
+
timestamp: timestamp5,
|
|
3320
|
+
turn_id: safeRef4(payload.turn_id, "Codex task identity is invalid."),
|
|
2299
3321
|
started_at: safeNumber(payload.started_at),
|
|
2300
3322
|
completed_at: safeNumber(payload.completed_at),
|
|
2301
3323
|
duration_ms: safeNumber(payload.duration_ms),
|
|
2302
3324
|
reason: boundedText(payload.reason, 256, "Codex task abort reason is invalid.")
|
|
2303
3325
|
};
|
|
2304
3326
|
}
|
|
2305
|
-
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal,
|
|
2306
|
-
return { kind: "ignored", ordinal, timestamp:
|
|
3327
|
+
if (payload.type === "token_count") return decodeTokenCount(payload, ordinal, timestamp5);
|
|
3328
|
+
return { kind: "ignored", ordinal, timestamp: timestamp5 };
|
|
2307
3329
|
}
|
|
2308
|
-
function decodeTokenCount(payload, ordinal,
|
|
2309
|
-
if (payload.info === null) return { kind: "token_count", ordinal, timestamp:
|
|
2310
|
-
if (!
|
|
3330
|
+
function decodeTokenCount(payload, ordinal, timestamp5) {
|
|
3331
|
+
if (payload.info === null) return { kind: "token_count", ordinal, timestamp: timestamp5 };
|
|
3332
|
+
if (!isRecord5(payload.info)) return { kind: "token_count", ordinal, timestamp: timestamp5, usage_gap_reason: "token_count_shape_not_attributable" };
|
|
2311
3333
|
const lastUsage = optionalTokenUsage(payload.info.last_token_usage);
|
|
2312
3334
|
const totalUsage = optionalTokenUsage(payload.info.total_token_usage);
|
|
2313
3335
|
const gap = lastUsage.status === "invalid" || totalUsage.status === "invalid";
|
|
@@ -2315,7 +3337,7 @@ function decodeTokenCount(payload, ordinal, timestamp6) {
|
|
|
2315
3337
|
return {
|
|
2316
3338
|
kind: "token_count",
|
|
2317
3339
|
ordinal,
|
|
2318
|
-
timestamp:
|
|
3340
|
+
timestamp: timestamp5,
|
|
2319
3341
|
...lastUsage.status === "valid" ? { last_usage: lastUsage.value } : {},
|
|
2320
3342
|
...totalUsage.status === "valid" ? { total_usage: totalUsage.value } : {},
|
|
2321
3343
|
...usageGapReason === void 0 ? {} : { usage_gap_reason: usageGapReason }
|
|
@@ -2331,7 +3353,7 @@ function optionalTokenUsage(value) {
|
|
|
2331
3353
|
}
|
|
2332
3354
|
}
|
|
2333
3355
|
function decodeTokenUsage(value) {
|
|
2334
|
-
if (!
|
|
3356
|
+
if (!isRecord5(value)) reject2("Codex token usage is malformed.");
|
|
2335
3357
|
const usage = {
|
|
2336
3358
|
input_tokens: safeTokenCount(value.input_tokens),
|
|
2337
3359
|
cached_input_tokens: safeTokenCount(value.cached_input_tokens),
|
|
@@ -2343,20 +3365,20 @@ function decodeTokenUsage(value) {
|
|
|
2343
3365
|
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
3366
|
return usage;
|
|
2345
3367
|
}
|
|
2346
|
-
function decodeResponseItem(payload, ordinal,
|
|
2347
|
-
if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp:
|
|
3368
|
+
function decodeResponseItem(payload, ordinal, timestamp5) {
|
|
3369
|
+
if (payload.type !== "message" || payload.role !== "user") return { kind: "ignored", ordinal, timestamp: timestamp5 };
|
|
2348
3370
|
const metadata = userMessageMetadata(payload.internal_chat_message_metadata_passthrough);
|
|
2349
3371
|
const inputKind = inputKindFromContentItems(metadata.content_item_kinds);
|
|
2350
|
-
if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp:
|
|
3372
|
+
if (inputKind === void 0) return { kind: "ignored", ordinal, timestamp: timestamp5 };
|
|
2351
3373
|
const prompt = userTextContent(payload.content);
|
|
2352
3374
|
return {
|
|
2353
3375
|
kind: "user_text",
|
|
2354
3376
|
ordinal,
|
|
2355
|
-
timestamp:
|
|
3377
|
+
timestamp: timestamp5,
|
|
2356
3378
|
prompt,
|
|
2357
3379
|
input_kind: inputKind,
|
|
2358
|
-
message_id:
|
|
2359
|
-
turn_id:
|
|
3380
|
+
message_id: safeRef4(payload.id, "Codex user message identity is invalid."),
|
|
3381
|
+
turn_id: safeRef4(metadata.turn_id, "Codex user message task identity is invalid.")
|
|
2360
3382
|
};
|
|
2361
3383
|
}
|
|
2362
3384
|
function inputKindFromContentItems(kinds) {
|
|
@@ -2365,13 +3387,13 @@ function inputKindFromContentItems(kinds) {
|
|
|
2365
3387
|
return kinds[0] === "goal.internal_context" ? "host_automatic" : void 0;
|
|
2366
3388
|
}
|
|
2367
3389
|
function userMessageMetadata(value) {
|
|
2368
|
-
if (!
|
|
3390
|
+
if (!isRecord5(value) || !Array.isArray(value.content_item_kinds)) reject2("Codex user message provenance is malformed.");
|
|
2369
3391
|
return { ...value, content_item_kinds: value.content_item_kinds };
|
|
2370
3392
|
}
|
|
2371
3393
|
function userTextContent(value) {
|
|
2372
3394
|
if (!Array.isArray(value)) reject2(`Codex user text content is malformed (${refusalFact("kind", typeof value, "array")}).`);
|
|
2373
3395
|
if (value.length !== 1) reject2(`Codex user text content is malformed (${refusalFact("items", value.length, "exactly one")}).`);
|
|
2374
|
-
if (!
|
|
3396
|
+
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
3397
|
return boundedText(value[0].text, MAX_PROMPT_BYTES, "Codex user text is invalid.");
|
|
2376
3398
|
}
|
|
2377
3399
|
function safeOrdinal(value) {
|
|
@@ -2392,8 +3414,8 @@ function safeTimestamp(value) {
|
|
|
2392
3414
|
if (!Number.isFinite(Date.parse(value))) reject2(`Codex transcript timestamp is invalid (${refusalFact("parsed_ms", Date.parse(value), "a finite epoch milliseconds value")}).`);
|
|
2393
3415
|
return value;
|
|
2394
3416
|
}
|
|
2395
|
-
function
|
|
2396
|
-
if (typeof value !== "string" || !
|
|
3417
|
+
function safeRef4(value, message) {
|
|
3418
|
+
if (typeof value !== "string" || !SAFE_REF5.test(value)) reject2(message);
|
|
2397
3419
|
return value;
|
|
2398
3420
|
}
|
|
2399
3421
|
function boundedText(value, maximum, message) {
|
|
@@ -2407,7 +3429,7 @@ function boundedNonEmptyText(value, maximum, message) {
|
|
|
2407
3429
|
if (result.length === 0) reject2(message);
|
|
2408
3430
|
return result;
|
|
2409
3431
|
}
|
|
2410
|
-
function
|
|
3432
|
+
function isRecord5(value) {
|
|
2411
3433
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2412
3434
|
}
|
|
2413
3435
|
function reject2(message) {
|
|
@@ -2420,7 +3442,7 @@ var CODEX_TRANSCRIPT_ANCHOR_BYTES = 256;
|
|
|
2420
3442
|
async function captureCodexTranscriptBaseline(registration, now = () => /* @__PURE__ */ new Date()) {
|
|
2421
3443
|
let handle;
|
|
2422
3444
|
try {
|
|
2423
|
-
handle = await
|
|
3445
|
+
handle = await open4(registration.transcript_path, O_RDONLY4 | O_NOFOLLOW3);
|
|
2424
3446
|
const { size, device, inode } = await ownedCodexTranscriptFile(handle);
|
|
2425
3447
|
const empty = codexTranscriptInitialCursor(registration, device, inode, now);
|
|
2426
3448
|
const headerLine = await firstCompleteLine(handle, size);
|
|
@@ -2440,7 +3462,7 @@ async function captureCodexTranscriptBaseline(registration, now = () => /* @__PU
|
|
|
2440
3462
|
anchor_start: anchorStart,
|
|
2441
3463
|
anchor_sha256: digest(anchor),
|
|
2442
3464
|
header_validated: true,
|
|
2443
|
-
updated_at:
|
|
3465
|
+
updated_at: timestamp3(now)
|
|
2444
3466
|
};
|
|
2445
3467
|
} finally {
|
|
2446
3468
|
await handle?.close();
|
|
@@ -2460,7 +3482,7 @@ function codexTranscriptInitialCursor(registration, device, inode, now) {
|
|
|
2460
3482
|
anchor_sha256: digest(Buffer.alloc(0)),
|
|
2461
3483
|
header_validated: false,
|
|
2462
3484
|
status: "active",
|
|
2463
|
-
updated_at:
|
|
3485
|
+
updated_at: timestamp3(now)
|
|
2464
3486
|
};
|
|
2465
3487
|
}
|
|
2466
3488
|
async function ownedCodexTranscriptFile(handle) {
|
|
@@ -2541,188 +3563,33 @@ function decodeHistoryLine(bytes) {
|
|
|
2541
3563
|
throw error;
|
|
2542
3564
|
}
|
|
2543
3565
|
}
|
|
2544
|
-
async function* completeLinesBackward(handle, end) {
|
|
2545
|
-
if (end <= 0) return;
|
|
2546
|
-
let position = end - 1;
|
|
2547
|
-
let carry = [];
|
|
2548
|
-
while (position > 0) {
|
|
2549
|
-
const start = Math.max(0, position - BASELINE_READ_CHUNK_BYTES);
|
|
2550
|
-
const buffer = Buffer.alloc(position - start);
|
|
2551
|
-
const read = await handle.read(buffer, 0, buffer.length, start);
|
|
2552
|
-
if (read.bytesRead !== buffer.length) throw new CodexHostAdapterError("storage", "Codex transcript changed size during baseline capture.");
|
|
2553
|
-
let lineEnd = buffer.length;
|
|
2554
|
-
for (let newline = buffer.lastIndexOf(10, lineEnd - 1); newline >= 0; newline = lineEnd > 0 ? buffer.lastIndexOf(10, lineEnd - 1) : -1) {
|
|
2555
|
-
yield { start: start + newline + 1, bytes: Buffer.concat([buffer.subarray(newline + 1, lineEnd), ...carry]) };
|
|
2556
|
-
carry = [];
|
|
2557
|
-
lineEnd = newline;
|
|
2558
|
-
}
|
|
2559
|
-
carry = [buffer.subarray(0, lineEnd), ...carry];
|
|
2560
|
-
position = start;
|
|
2561
|
-
}
|
|
2562
|
-
yield { start: 0, bytes: Buffer.concat(carry) };
|
|
2563
|
-
}
|
|
2564
|
-
function digest(value) {
|
|
2565
|
-
return createHash3("sha256").update(value).digest("hex");
|
|
2566
|
-
}
|
|
2567
|
-
function timestamp2(now) {
|
|
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());
|
|
3566
|
+
async function* completeLinesBackward(handle, end) {
|
|
3567
|
+
if (end <= 0) return;
|
|
3568
|
+
let position = end - 1;
|
|
3569
|
+
let carry = [];
|
|
3570
|
+
while (position > 0) {
|
|
3571
|
+
const start = Math.max(0, position - BASELINE_READ_CHUNK_BYTES);
|
|
3572
|
+
const buffer = Buffer.alloc(position - start);
|
|
3573
|
+
const read = await handle.read(buffer, 0, buffer.length, start);
|
|
3574
|
+
if (read.bytesRead !== buffer.length) throw new CodexHostAdapterError("storage", "Codex transcript changed size during baseline capture.");
|
|
3575
|
+
let lineEnd = buffer.length;
|
|
3576
|
+
for (let newline = buffer.lastIndexOf(10, lineEnd - 1); newline >= 0; newline = lineEnd > 0 ? buffer.lastIndexOf(10, lineEnd - 1) : -1) {
|
|
3577
|
+
yield { start: start + newline + 1, bytes: Buffer.concat([buffer.subarray(newline + 1, lineEnd), ...carry]) };
|
|
3578
|
+
carry = [];
|
|
3579
|
+
lineEnd = newline;
|
|
3580
|
+
}
|
|
3581
|
+
carry = [buffer.subarray(0, lineEnd), ...carry];
|
|
3582
|
+
position = start;
|
|
3583
|
+
}
|
|
3584
|
+
yield { start: 0, bytes: Buffer.concat(carry) };
|
|
2713
3585
|
}
|
|
2714
|
-
function
|
|
2715
|
-
|
|
2716
|
-
return required.every((key) => keys.includes(key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
3586
|
+
function digest(value) {
|
|
3587
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
2717
3588
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
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);
|
|
3589
|
+
function timestamp3(now) {
|
|
3590
|
+
const value = now();
|
|
3591
|
+
if (!Number.isFinite(value.getTime())) throw new CodexHostAdapterError("storage", "Codex watcher clock is invalid.");
|
|
3592
|
+
return value.toISOString();
|
|
2726
3593
|
}
|
|
2727
3594
|
|
|
2728
3595
|
// host/codex/src/codex-adapter.ts
|
|
@@ -2748,7 +3615,7 @@ function createCodexHostObservationAdapter(options) {
|
|
|
2748
3615
|
});
|
|
2749
3616
|
}
|
|
2750
3617
|
async function registerSession(event, options, store, now, retentionMs) {
|
|
2751
|
-
const defaultRoot =
|
|
3618
|
+
const defaultRoot = join4(process.env.CODEX_HOME || join4(homedir(), ".codex"), "sessions");
|
|
2752
3619
|
const transcriptPath = sourceBoundTranscriptPath(event.transcript_path, options.codex_sessions_root ?? defaultRoot);
|
|
2753
3620
|
const hostSessionRef = hostSessionReference(options.host_source_ref, event.session_id);
|
|
2754
3621
|
const observedAt = timestamp4(now);
|
|
@@ -2763,20 +3630,59 @@ async function registerSession(event, options, store, now, retentionMs) {
|
|
|
2763
3630
|
registered_at: observedAt,
|
|
2764
3631
|
expires_at: expiresAt
|
|
2765
3632
|
};
|
|
3633
|
+
const currentTurn = await selectCurrentTurn(event, options, store, hostSessionRef);
|
|
2766
3634
|
const existing = (await store.listTranscriptRegistrations()).find((candidate) => candidate.host_source_ref === registration.host_source_ref && candidate.host_session_ref === registration.host_session_ref);
|
|
2767
3635
|
if (existing === void 0) {
|
|
2768
3636
|
await store.writeTranscriptCursor(await captureCodexTranscriptBaseline(registration, now));
|
|
2769
3637
|
}
|
|
2770
3638
|
await store.registerTranscript(registration);
|
|
2771
|
-
await store.
|
|
2772
|
-
|
|
2773
|
-
|
|
3639
|
+
if (existing !== void 0 && currentTurn !== void 0 && (await store.readTranscriptCursor(registration))?.status === "quarantined") {
|
|
3640
|
+
await store.writeTranscriptCursor(await captureCodexTranscriptBaseline(registration, now));
|
|
3641
|
+
}
|
|
3642
|
+
if (event.event === "SessionStart") {
|
|
3643
|
+
await store.saveSession({
|
|
3644
|
+
schema_version: "ceal.codex_session.v1",
|
|
3645
|
+
host_session_ref: hostSessionRef,
|
|
3646
|
+
host_source_ref: options.host_source_ref,
|
|
3647
|
+
observed_at: observedAt,
|
|
3648
|
+
value: sessionObservationValue(event),
|
|
3649
|
+
expires_at: expiresAt
|
|
3650
|
+
});
|
|
3651
|
+
} else if (currentTurn !== void 0) {
|
|
3652
|
+
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);
|
|
3653
|
+
}
|
|
3654
|
+
return { event: event.event, disposition: "ignored", reason: "transcript_registered", ...currentTurn === void 0 ? {} : { host_turn_ref: currentTurn.host_turn_ref } };
|
|
3655
|
+
}
|
|
3656
|
+
async function selectCurrentTurn(event, options, store, hostSessionRef) {
|
|
3657
|
+
if (event.event !== "UserPromptSubmit") return void 0;
|
|
3658
|
+
const host_turn_ref = hostTurnReference(options.host_source_ref, hostSessionRef, event.turn_id);
|
|
3659
|
+
const steered = await liveSteeredTurn(options, store, hostSessionRef, host_turn_ref);
|
|
3660
|
+
if (steered !== void 0) return steered;
|
|
3661
|
+
const currentTurn = {
|
|
3662
|
+
host_turn_ref,
|
|
3663
|
+
prompt_ref: hostProducerPromptRef(promptObservationValue(event.prompt).body_sha256),
|
|
3664
|
+
turn_generation_ref: options.random_uuid?.() ?? randomUUID()
|
|
3665
|
+
};
|
|
3666
|
+
await store.writeInvocationTurnSelection({
|
|
3667
|
+
schema_version: "ceal.host_invocation_turn_selection.v1",
|
|
2774
3668
|
host_source_ref: options.host_source_ref,
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
3669
|
+
host_session_ref: hostSessionRef,
|
|
3670
|
+
prompt_ref: currentTurn.prompt_ref,
|
|
3671
|
+
turn_generation_ref: currentTurn.turn_generation_ref
|
|
2778
3672
|
});
|
|
2779
|
-
return
|
|
3673
|
+
return currentTurn;
|
|
3674
|
+
}
|
|
3675
|
+
async function liveSteeredTurn(options, store, hostSessionRef, hostTurnRef) {
|
|
3676
|
+
let live;
|
|
3677
|
+
try {
|
|
3678
|
+
live = await store.readInvocationContext(options.host_source_ref, hostSessionRef, (options.now ?? (() => /* @__PURE__ */ new Date()))());
|
|
3679
|
+
} catch {
|
|
3680
|
+
return void 0;
|
|
3681
|
+
}
|
|
3682
|
+
if (live === null || live.identity.host_turn_ref !== hostTurnRef) return void 0;
|
|
3683
|
+
const selection = await store.readInvocationTurnSelection(options.host_source_ref, hostSessionRef).catch(() => null);
|
|
3684
|
+
if (selection === null || selection.prompt_ref !== live.prompt_ref || selection.turn_generation_ref !== live.turn_generation_ref) return void 0;
|
|
3685
|
+
return { host_turn_ref: hostTurnRef, prompt_ref: selection.prompt_ref, turn_generation_ref: selection.turn_generation_ref };
|
|
2780
3686
|
}
|
|
2781
3687
|
function sourceBoundTranscriptPath(path, sessionsRoot) {
|
|
2782
3688
|
if (!isAbsolute3(path) || !isAbsolute3(sessionsRoot)) throw new CodexHostAdapterError("configuration", "Codex transcript registration paths must be absolute.");
|
|
@@ -2884,7 +3790,7 @@ async function submitWithDeadline(gateway, submission, timeoutMs, signal) {
|
|
|
2884
3790
|
});
|
|
2885
3791
|
try {
|
|
2886
3792
|
const acknowledgement = await Promise.race([gateway.submitHostTurn(submission, controller.signal), timeout]);
|
|
2887
|
-
if (!
|
|
3793
|
+
if (!isRecord6(acknowledgement) || !SAFE_REF6.test(String(acknowledgement.request_ref)) || !isHostGatewayAcknowledgementDisposition(acknowledgement.disposition)) {
|
|
2888
3794
|
throw new CodexHostAdapterError("payload_rejected", "Gateway Host acknowledgement is invalid.");
|
|
2889
3795
|
}
|
|
2890
3796
|
return acknowledgement;
|
|
@@ -2914,12 +3820,12 @@ function timestamp4(now) {
|
|
|
2914
3820
|
return value.toISOString();
|
|
2915
3821
|
}
|
|
2916
3822
|
function validateOptions(options) {
|
|
2917
|
-
if (!
|
|
3823
|
+
if (!isRecord6(options)) throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
|
|
2918
3824
|
validateAdapterOwner(options);
|
|
2919
3825
|
validateAdapterBehavior(options);
|
|
2920
3826
|
}
|
|
2921
3827
|
function validateAdapterOwner(options) {
|
|
2922
|
-
if (!SAFE_REF6.test(options.host_source_ref) || options.outbox_dir.length === 0 || !
|
|
3828
|
+
if (!SAFE_REF6.test(options.host_source_ref) || options.outbox_dir.length === 0 || !isRecord6(options.gateway) || typeof options.gateway.submitHostTurn !== "function") {
|
|
2923
3829
|
throw new CodexHostAdapterError("configuration", "Codex adapter options are invalid.");
|
|
2924
3830
|
}
|
|
2925
3831
|
if (options.codex_sessions_root !== void 0 && !isAbsolute3(options.codex_sessions_root)) throw new CodexHostAdapterError("configuration", "Codex sessions root is invalid.");
|
|
@@ -2931,7 +3837,7 @@ function validateAdapterBehavior(options) {
|
|
|
2931
3837
|
function safeReason(error) {
|
|
2932
3838
|
return error instanceof CodexHostAdapterError ? error.message.slice(0, 128) : "codex_session_registration_rejected";
|
|
2933
3839
|
}
|
|
2934
|
-
function
|
|
3840
|
+
function isRecord6(value) {
|
|
2935
3841
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2936
3842
|
}
|
|
2937
3843
|
|
|
@@ -2939,7 +3845,7 @@ function isRecord5(value) {
|
|
|
2939
3845
|
import { posix as posix3, win32 as win323 } from "node:path";
|
|
2940
3846
|
|
|
2941
3847
|
// host/runtime-source-paths.ts
|
|
2942
|
-
import { createHash as
|
|
3848
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
2943
3849
|
import { posix as posix2, win32 as win322 } from "node:path";
|
|
2944
3850
|
|
|
2945
3851
|
// host/absolute-path.ts
|
|
@@ -2961,7 +3867,7 @@ function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platfor
|
|
|
2961
3867
|
const sourcesRoot = path.join(stateRoot, "sources");
|
|
2962
3868
|
const sourceRoot = path.join(sourcesRoot, sourceId);
|
|
2963
3869
|
const runtimeRoot = path.join(stateRoot, "run");
|
|
2964
|
-
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${
|
|
3870
|
+
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash5("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
|
|
2965
3871
|
assertHostRuntimeIpcLength(endpoint, platform);
|
|
2966
3872
|
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
3873
|
}
|
|
@@ -2977,7 +3883,7 @@ function assertHostProducerSourcePaths(stateRoot, config, producer, platform = p
|
|
|
2977
3883
|
|
|
2978
3884
|
// host/producer-service.ts
|
|
2979
3885
|
import { constants } from "node:fs";
|
|
2980
|
-
import { lstat as lstat3, open as
|
|
3886
|
+
import { lstat as lstat3, open as open5 } from "node:fs/promises";
|
|
2981
3887
|
var SAFE_REF7 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2982
3888
|
var HOST_PRODUCER_CONFIG_FLAG = "--ceal-host-config";
|
|
2983
3889
|
var HostProducerServiceError = class extends Error {
|
|
@@ -3043,7 +3949,7 @@ async function readProducerConfigText(configPath, platform, error) {
|
|
|
3043
3949
|
}
|
|
3044
3950
|
function decodeProducerConfigCore(text, options) {
|
|
3045
3951
|
const value = parseCanonicalJson(text, options.error);
|
|
3046
|
-
if (!
|
|
3952
|
+
if (!isRecord7(value) || !hasConfigKeys(value, options.keys) || value.schema_version !== options.schema) throw options.error("invalid_config");
|
|
3047
3953
|
const hostSourceRef = typeof value.host_source_ref === "string" ? value.host_source_ref : "";
|
|
3048
3954
|
const installationRef = typeof value.producer_installation_ref === "string" ? value.producer_installation_ref : "";
|
|
3049
3955
|
const producerEpoch = validateIdentity3(hostSourceRef, installationRef, value.producer_epoch, options.error);
|
|
@@ -3088,7 +3994,7 @@ function validateConfigFile(value, platform, error) {
|
|
|
3088
3994
|
}
|
|
3089
3995
|
async function openConfigFile(configPath, flags, error) {
|
|
3090
3996
|
try {
|
|
3091
|
-
return await
|
|
3997
|
+
return await open5(configPath, flags);
|
|
3092
3998
|
} catch {
|
|
3093
3999
|
throw error("config_unavailable");
|
|
3094
4000
|
}
|
|
@@ -3105,363 +4011,83 @@ async function readBoundedConfig(handle, expectedBytes, error) {
|
|
|
3105
4011
|
if (bytesRead === 0) break;
|
|
3106
4012
|
offset += bytesRead;
|
|
3107
4013
|
}
|
|
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
|
-
});
|
|
3308
|
-
}
|
|
3309
|
-
function decodeHostRelayIpcResponse(input) {
|
|
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;
|
|
4014
|
+
if (offset !== expectedBytes) throw error("invalid_config", refusalFact("bytes_read", offset, `the opened file's ${expectedBytes} bytes without growth or truncation`));
|
|
3314
4015
|
try {
|
|
3315
|
-
|
|
4016
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(buffer.subarray(0, offset));
|
|
3316
4017
|
} catch {
|
|
3317
|
-
throw
|
|
4018
|
+
throw error("invalid_config");
|
|
3318
4019
|
}
|
|
3319
|
-
if (!validResponse(value)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC response shape is invalid.");
|
|
3320
|
-
return value;
|
|
3321
|
-
}
|
|
3322
|
-
function relayAdmissionCode(value) {
|
|
3323
|
-
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";
|
|
3324
|
-
}
|
|
3325
|
-
function validResponse(value) {
|
|
3326
|
-
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;
|
|
3327
|
-
}
|
|
3328
|
-
function hostRelayIpcCode(value) {
|
|
3329
|
-
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);
|
|
3330
4020
|
}
|
|
3331
|
-
function
|
|
3332
|
-
|
|
4021
|
+
function parseCanonicalJson(text, error) {
|
|
4022
|
+
try {
|
|
4023
|
+
const value = JSON.parse(text);
|
|
4024
|
+
const canonical = JSON.stringify(value);
|
|
4025
|
+
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) }])}`);
|
|
4026
|
+
return value;
|
|
4027
|
+
} catch (parseError) {
|
|
4028
|
+
throw error("invalid_config", refusalFacts([{ name: "parse_error", value: parseError }, { name: "config_bytes", value: Buffer.byteLength(text, "utf8"), expected: "canonical JSON" }]));
|
|
4029
|
+
}
|
|
3333
4030
|
}
|
|
3334
|
-
function
|
|
3335
|
-
|
|
4031
|
+
function firstDifferenceIndex(left, right) {
|
|
4032
|
+
const shared = Math.min(left.length, right.length);
|
|
4033
|
+
for (let index = 0; index < shared; index += 1) if (left[index] !== right[index]) return index;
|
|
4034
|
+
return shared;
|
|
3336
4035
|
}
|
|
3337
|
-
function
|
|
3338
|
-
if (
|
|
3339
|
-
|
|
3340
|
-
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}`)}.`);
|
|
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);
|
|
4036
|
+
function validateIdentity3(sourceRef, installationRef, epoch, error) {
|
|
4037
|
+
if (!SAFE_REF7.test(sourceRef) || !SAFE_REF7.test(installationRef) || !positive2(epoch, 2147483647)) throw error("invalid_config");
|
|
4038
|
+
return epoch;
|
|
3344
4039
|
}
|
|
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 };
|
|
4040
|
+
function validateIntervals(reconcile, retry, timeout, error) {
|
|
4041
|
+
if (!boundedMilliseconds(reconcile) || !boundedMilliseconds(retry) || !boundedMilliseconds(timeout)) throw error("invalid_config");
|
|
4042
|
+
return [reconcile, retry, timeout];
|
|
3350
4043
|
}
|
|
3351
|
-
function
|
|
3352
|
-
|
|
3353
|
-
const length = input.readUInt32BE(0);
|
|
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
|
-
}
|
|
4044
|
+
function hasConfigKeys(value, required) {
|
|
4045
|
+
return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || Object.hasOwn(HOST_PRODUCER_TIMING_DEFAULTS, key));
|
|
3362
4046
|
}
|
|
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;
|
|
4047
|
+
function isRecord7(value) {
|
|
4048
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3369
4049
|
}
|
|
3370
|
-
function
|
|
3371
|
-
return
|
|
4050
|
+
function positive2(value, maximum) {
|
|
4051
|
+
return Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= maximum;
|
|
3372
4052
|
}
|
|
3373
|
-
function
|
|
3374
|
-
return
|
|
4053
|
+
function boundedMilliseconds(value) {
|
|
4054
|
+
return positive2(value, 6e4) && Number(value) >= 25;
|
|
3375
4055
|
}
|
|
3376
4056
|
|
|
3377
4057
|
// 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";
|
|
4058
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
3382
4059
|
var SAFE_REF8 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
3383
4060
|
var CodexProducer = class {
|
|
4061
|
+
producer_id = "codex";
|
|
4062
|
+
producer_label = "Codex";
|
|
4063
|
+
outbox_directory;
|
|
3384
4064
|
store;
|
|
3385
|
-
|
|
3386
|
-
installationRef;
|
|
3387
|
-
epoch;
|
|
3388
|
-
relay;
|
|
3389
|
-
now;
|
|
3390
|
-
tail = Promise.resolve();
|
|
4065
|
+
core;
|
|
3391
4066
|
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
4067
|
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());
|
|
4068
|
+
this.outbox_directory = this.store.directory;
|
|
4069
|
+
this.core = new HostProducerDrainCore(this, options);
|
|
3401
4070
|
}
|
|
3402
4071
|
drainOutbox() {
|
|
3403
|
-
|
|
3404
|
-
this.tail = run.then(() => void 0, () => void 0);
|
|
3405
|
-
return run;
|
|
4072
|
+
return this.core.drainOutbox();
|
|
3406
4073
|
}
|
|
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 };
|
|
4074
|
+
fail(code, message) {
|
|
4075
|
+
return new CodexHostAdapterError(code, message);
|
|
3437
4076
|
}
|
|
3438
|
-
|
|
3439
|
-
return
|
|
4077
|
+
listEnvelopes() {
|
|
4078
|
+
return this.store.list();
|
|
3440
4079
|
}
|
|
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;
|
|
4080
|
+
removeEnvelope(envelope) {
|
|
4081
|
+
return this.store.remove(envelope);
|
|
3458
4082
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
4083
|
+
submissionRefOf(envelope) {
|
|
4084
|
+
return envelope.submission_ref;
|
|
4085
|
+
}
|
|
4086
|
+
expiresAtOf(envelope) {
|
|
4087
|
+
return envelope.expires_at;
|
|
4088
|
+
}
|
|
4089
|
+
createdAtOf(envelope) {
|
|
4090
|
+
return envelope.created_at;
|
|
3465
4091
|
}
|
|
3466
4092
|
async mapEnvelope(envelope) {
|
|
3467
4093
|
const prompt = observation(envelope, "prompt");
|
|
@@ -3477,7 +4103,7 @@ var CodexProducer = class {
|
|
|
3477
4103
|
const directoryIdentity = registration?.cwd ?? envelope.identity.host_session_ref;
|
|
3478
4104
|
drafts.push({ frame_kind: "session_started", payload: compact({
|
|
3479
4105
|
local_session_ref: envelope.identity.host_session_ref,
|
|
3480
|
-
working_directory_ref: `workspace:${
|
|
4106
|
+
working_directory_ref: `workspace:${createHash6("sha256").update(directoryIdentity, "utf8").digest("hex")}`,
|
|
3481
4107
|
model: recordText(session.value, "model"),
|
|
3482
4108
|
host_version: nestedText(session.value, "host_version"),
|
|
3483
4109
|
observations: [session]
|
|
@@ -3502,21 +4128,6 @@ var CodexProducer = class {
|
|
|
3502
4128
|
}) });
|
|
3503
4129
|
return drafts;
|
|
3504
4130
|
}
|
|
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
4131
|
};
|
|
3521
4132
|
function mapTerminal(envelope) {
|
|
3522
4133
|
const timing = observation(envelope, "timing");
|
|
@@ -3544,7 +4155,7 @@ function compact(value) {
|
|
|
3544
4155
|
return Object.fromEntries(Object.entries(value).filter((entry) => entry[1] !== void 0));
|
|
3545
4156
|
}
|
|
3546
4157
|
function asRecord(value) {
|
|
3547
|
-
return
|
|
4158
|
+
return isRecord8(value) ? value : void 0;
|
|
3548
4159
|
}
|
|
3549
4160
|
function recordText(value, key) {
|
|
3550
4161
|
const record3 = asRecord(value);
|
|
@@ -3558,122 +4169,60 @@ function nonNegativeNumber(value) {
|
|
|
3558
4169
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
3559
4170
|
}
|
|
3560
4171
|
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;
|
|
4172
|
+
return SAFE_REF8.test(value) ? value : `reason:${createHash6("sha256").update(value, "utf8").digest("hex")}`;
|
|
3596
4173
|
}
|
|
3597
|
-
function
|
|
4174
|
+
function isRecord8(value) {
|
|
3598
4175
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3599
4176
|
}
|
|
3600
4177
|
|
|
3601
4178
|
// 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 {
|
|
4179
|
+
var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
3608
4180
|
store;
|
|
3609
|
-
outboxRetentionMs;
|
|
3610
|
-
now;
|
|
3611
|
-
log;
|
|
3612
|
-
producerInstallationRef;
|
|
3613
4181
|
registerCurrentPrompt;
|
|
4182
|
+
releaseCurrentPrompt;
|
|
4183
|
+
diagnostic;
|
|
3614
4184
|
constructor(options) {
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
this.log = options.log ?? ((line) => {
|
|
3620
|
-
process.stderr.write(`${line}
|
|
3621
|
-
`);
|
|
4185
|
+
super(options, {
|
|
4186
|
+
default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
|
|
4187
|
+
invalid_outbox: () => new CodexHostAdapterError("configuration", "Codex transcript watcher outbox is invalid."),
|
|
4188
|
+
clock_failure: () => new CodexHostAdapterError("storage", "Codex watcher clock is invalid.")
|
|
3622
4189
|
});
|
|
3623
|
-
this.
|
|
4190
|
+
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
3624
4191
|
this.registerCurrentPrompt = options.register_current_prompt;
|
|
4192
|
+
this.releaseCurrentPrompt = options.release_current_prompt;
|
|
4193
|
+
this.diagnostic = options.diagnostic ?? (() => void 0);
|
|
3625
4194
|
}
|
|
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 };
|
|
4195
|
+
transcriptPathOf(registration) {
|
|
4196
|
+
return registration.transcript_path;
|
|
3638
4197
|
}
|
|
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
|
-
}
|
|
4198
|
+
async listRegistrations() {
|
|
4199
|
+
return this.store.listTranscriptRegistrations();
|
|
3656
4200
|
}
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
4201
|
+
async scanOpened(handle, registration) {
|
|
4202
|
+
const { size, device, inode } = await ownedCodexTranscriptFile(handle);
|
|
4203
|
+
const preflight = await this.preflight(registration, handle, size, device, inode);
|
|
4204
|
+
if (preflight.cursor === null) return preflight.result;
|
|
4205
|
+
const complete = await this.readChunk(handle, preflight.cursor.offset, size);
|
|
4206
|
+
if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
|
|
4207
|
+
return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
|
|
4208
|
+
}
|
|
4209
|
+
async scanFailed(handle, registration, error) {
|
|
4210
|
+
this.diagnostic("watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
4211
|
+
await this.quarantineFailure(registration, handle);
|
|
4212
|
+
return { records: 0, enqueued: 0, quarantined: true };
|
|
4213
|
+
}
|
|
4214
|
+
async retireMissing(registration) {
|
|
4215
|
+
await this.releaseStoredPromptBestEffort(registration.host_session_ref, await this.store.readTranscriptCursor(registration).catch(() => null));
|
|
4216
|
+
return this.retireMissingTranscript(registration.transcript_path, "Codex", () => this.store.retireTranscriptRegistration(registration));
|
|
3671
4217
|
}
|
|
3672
4218
|
async preflight(registration, handle, size, device, inode) {
|
|
3673
4219
|
const existing = await this.store.readTranscriptCursor(registration);
|
|
3674
4220
|
let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
|
|
3675
|
-
if (cursor.status === "quarantined")
|
|
3676
|
-
|
|
4221
|
+
if (cursor.status === "quarantined") {
|
|
4222
|
+
if (cursor.reason !== "transcript_malformed") await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
|
|
4223
|
+
return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
4224
|
+
}
|
|
4225
|
+
const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
|
|
3677
4226
|
if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
|
|
3678
4227
|
await this.quarantine(registration, cursor, device, inode, reason);
|
|
3679
4228
|
return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
@@ -3699,39 +4248,53 @@ var CodexTranscriptWatcher = class {
|
|
|
3699
4248
|
async observeSessionMetadata(registration, record3) {
|
|
3700
4249
|
if (record3.kind !== "session_meta") return;
|
|
3701
4250
|
const session = await this.store.readSession(registration.host_source_ref, registration.host_session_ref);
|
|
3702
|
-
if (session !== null &&
|
|
4251
|
+
if (session !== null && this.isRecord(session.value)) await this.store.saveSession({ ...session, value: { ...session.value, host_version: record3.cli_version } });
|
|
3703
4252
|
}
|
|
3704
4253
|
async commitCursor(registration, handle, cursor, consumedBytes, device, inode, lastOrdinal, activeTask) {
|
|
3705
4254
|
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) });
|
|
4255
|
+
const anchor = await this.readAnchor(handle, offset, CODEX_TRANSCRIPT_ANCHOR_BYTES);
|
|
4256
|
+
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
4257
|
}
|
|
3711
4258
|
async quarantineFailure(registration, handle) {
|
|
3712
4259
|
if (handle === void 0) return;
|
|
3713
4260
|
const stats = await handle.stat().catch(() => null);
|
|
3714
4261
|
if (stats === null) return;
|
|
3715
4262
|
const existing = await this.store.readTranscriptCursor(registration).catch(() => null);
|
|
3716
|
-
|
|
4263
|
+
const quarantined = {
|
|
4264
|
+
...existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now),
|
|
4265
|
+
transcript_path: registration.transcript_path,
|
|
4266
|
+
device: String(stats.dev),
|
|
4267
|
+
inode: String(stats.ino),
|
|
4268
|
+
status: "quarantined",
|
|
4269
|
+
reason: "transcript_malformed",
|
|
4270
|
+
updated_at: this.stamp()
|
|
4271
|
+
};
|
|
4272
|
+
await this.store.writeTranscriptCursor(quarantined);
|
|
3717
4273
|
}
|
|
3718
4274
|
async processRecord(registration, activeTask, record3) {
|
|
3719
|
-
if (record3.kind === "task_started")
|
|
3720
|
-
return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
|
|
3721
|
-
}
|
|
4275
|
+
if (record3.kind === "task_started") return this.processTaskStarted(registration, activeTask, record3);
|
|
3722
4276
|
if (record3.kind === "turn_context") return { ...activeTask === void 0 ? {} : { activeTask: record3.turn_id === activeTask.codex_turn_id ? { ...activeTask, model: record3.model } : activeTask }, enqueued: 0 };
|
|
3723
4277
|
if (record3.kind === "token_count") return { ...activeTask === void 0 ? {} : { activeTask: accumulateTokenCount(activeTask, record3) }, enqueued: 0 };
|
|
3724
4278
|
if (record3.kind === "user_text") return this.processUserInput(registration, activeTask, record3);
|
|
3725
4279
|
if (record3.kind === "task_complete" || record3.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record3);
|
|
3726
4280
|
return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
3727
4281
|
}
|
|
4282
|
+
async processTaskStarted(registration, activeTask, record3) {
|
|
4283
|
+
if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
4284
|
+
return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
|
|
4285
|
+
}
|
|
3728
4286
|
async processUserInput(registration, activeTask, record3) {
|
|
3729
|
-
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id)
|
|
4287
|
+
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id) {
|
|
4288
|
+
const adopted = await this.adoptHookCorrelatedTurn(registration, activeTask, record3);
|
|
4289
|
+
if (adopted === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
4290
|
+
activeTask = adopted;
|
|
4291
|
+
}
|
|
3730
4292
|
const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id, record3.message_id, record3.ordinal, record3.prompt);
|
|
3731
4293
|
const origin = activeTask.origin_host_input_ref === void 0;
|
|
3732
4294
|
const promptValue = promptObservationValue(record3.prompt);
|
|
3733
4295
|
const currentPromptRef = hostProducerPromptRef(promptValue.body_sha256);
|
|
3734
4296
|
const promptRef = activeTask.prompt_ref ?? currentPromptRef;
|
|
4297
|
+
const steeredPromptRef = await this.steeredPromptRef(registration, activeTask, origin, currentPromptRef);
|
|
3735
4298
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
3736
4299
|
const observations = [{
|
|
3737
4300
|
observation_ref: observationRef("transcript_user_text", hostInputRef, record3.prompt),
|
|
@@ -3755,16 +4318,90 @@ var CodexTranscriptWatcher = class {
|
|
|
3755
4318
|
origin_input: origin,
|
|
3756
4319
|
submission_ref: codexSubmissionReference(hostInputRef, "input", record3.ordinal),
|
|
3757
4320
|
created_at: record3.timestamp,
|
|
3758
|
-
expires_at:
|
|
4321
|
+
expires_at: this.expire(this.outboxRetentionMs)
|
|
4322
|
+
};
|
|
4323
|
+
const persisted = await this.persistUserInputTurn(registration, activeTask, record3, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope);
|
|
4324
|
+
if (steeredPromptRef !== null) await this.activateTranscriptTurnContext(identity, steeredPromptRef, envelope.expires_at);
|
|
4325
|
+
return persisted;
|
|
4326
|
+
}
|
|
4327
|
+
/**
|
|
4328
|
+
* The steered prompt to re-activate under the running task, or null when
|
|
4329
|
+
* there is nothing to repair. A steered message joins the running turn
|
|
4330
|
+
* with new prompt bytes but no task_started. When the steer hook already
|
|
4331
|
+
* replaced the turn selection (a turn id the hook did not recognize as
|
|
4332
|
+
* the running one, or a hook that ran before any context was live), the
|
|
4333
|
+
* running task's context no longer matches and the next invocation would
|
|
4334
|
+
* wait for a boundary that never comes. When the hook kept the live
|
|
4335
|
+
* selection, the context still matches and this stays null.
|
|
4336
|
+
*/
|
|
4337
|
+
async steeredPromptRef(registration, activeTask, origin, currentPromptRef) {
|
|
4338
|
+
if (origin || activeTask.prompt_ref === void 0 || activeTask.prompt_ref === currentPromptRef) return null;
|
|
4339
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref).catch(() => null);
|
|
4340
|
+
return selection?.prompt_ref === currentPromptRef ? currentPromptRef : null;
|
|
4341
|
+
}
|
|
4342
|
+
/**
|
|
4343
|
+
* Adopt the live hooked turn when its transcript message arrives before
|
|
4344
|
+
* (or without) its task_started, as an interrupted turn's follow-up can.
|
|
4345
|
+
* The UserPromptSubmit hook names the exact hooked prompt, so only a
|
|
4346
|
+
* message carrying those bytes adopts; inherited history and other turns
|
|
4347
|
+
* stay ignored. A stale active task for another turn releases first, the
|
|
4348
|
+
* same as a task_started supersession.
|
|
4349
|
+
*/
|
|
4350
|
+
async adoptHookCorrelatedTurn(registration, activeTask, record3) {
|
|
4351
|
+
const currentPromptRef = hostProducerPromptRef(promptObservationValue(record3.prompt).body_sha256);
|
|
4352
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref);
|
|
4353
|
+
if (selection?.prompt_ref !== currentPromptRef) return void 0;
|
|
4354
|
+
if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) {
|
|
4355
|
+
await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
4356
|
+
}
|
|
4357
|
+
return {
|
|
4358
|
+
codex_turn_id: record3.turn_id,
|
|
4359
|
+
host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id),
|
|
4360
|
+
started_at: record3.timestamp
|
|
3759
4361
|
};
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
4362
|
+
}
|
|
4363
|
+
async persistUserInputTurn(registration, activeTask, record3, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope) {
|
|
4364
|
+
const custodyRegistered = await this.registerPrompt(origin, registration, activeTask, currentPromptRef, record3.prompt, envelope.expires_at);
|
|
4365
|
+
if (custodyRegistered) this.diagnostic("watcher_turn_observed");
|
|
4366
|
+
try {
|
|
4367
|
+
await this.store.put(envelope);
|
|
4368
|
+
if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
|
|
4369
|
+
} catch (error) {
|
|
4370
|
+
if (custodyRegistered) await this.releasePrompt(registration.host_session_ref, activeTask, currentPromptRef).catch(() => void 0);
|
|
4371
|
+
throw error;
|
|
4372
|
+
}
|
|
3763
4373
|
return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
|
|
3764
4374
|
}
|
|
3765
4375
|
async registerPrompt(origin, registration, activeTask, promptRef, prompt, expiresAt) {
|
|
3766
|
-
if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return;
|
|
3767
|
-
|
|
4376
|
+
if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return false;
|
|
4377
|
+
const register = this.registerCurrentPrompt;
|
|
4378
|
+
const installationRef = this.producerInstallationRef;
|
|
4379
|
+
return registerHostCurrentPromptDiagnosed(
|
|
4380
|
+
() => register({ producer_id: "codex", producer_installation_ref: installationRef, local_session_ref: registration.host_session_ref, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef, current_prompt: prompt, context_expires_at: expiresAt }),
|
|
4381
|
+
(event) => this.diagnostic(event)
|
|
4382
|
+
);
|
|
4383
|
+
}
|
|
4384
|
+
async activateTranscriptTurnContext(identity, promptRef, expiresAt) {
|
|
4385
|
+
try {
|
|
4386
|
+
const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
4387
|
+
const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
|
|
4388
|
+
await this.store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
|
|
4389
|
+
this.diagnostic("activation_recorded");
|
|
4390
|
+
} catch (error) {
|
|
4391
|
+
this.diagnostic("activation_failed");
|
|
4392
|
+
throw error;
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
4395
|
+
async releasePrompt(sessionRef, activeTask, promptRef) {
|
|
4396
|
+
if (this.producerInstallationRef === void 0 || this.releaseCurrentPrompt === void 0) return;
|
|
4397
|
+
await this.releaseCurrentPrompt({ producer_id: "codex", producer_installation_ref: this.producerInstallationRef, local_session_ref: sessionRef, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef });
|
|
4398
|
+
}
|
|
4399
|
+
async releaseStoredPromptBestEffort(sessionRef, cursor) {
|
|
4400
|
+
await this.releaseActiveTaskBestEffort(sessionRef, cursor?.active_task);
|
|
4401
|
+
}
|
|
4402
|
+
async releaseActiveTaskBestEffort(sessionRef, activeTask) {
|
|
4403
|
+
if (activeTask?.prompt_ref === void 0) return;
|
|
4404
|
+
await this.releasePrompt(sessionRef, activeTask, activeTask.prompt_ref).catch(() => void 0);
|
|
3768
4405
|
}
|
|
3769
4406
|
async processTerminal(registration, activeTask, record3) {
|
|
3770
4407
|
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id || activeTask.origin_host_input_ref === void 0 || activeTask.prompt_ref === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
@@ -3782,16 +4419,26 @@ var CodexTranscriptWatcher = class {
|
|
|
3782
4419
|
origin_input: false,
|
|
3783
4420
|
submission_ref: codexSubmissionReference(activeTask.origin_host_input_ref, "terminal", record3.ordinal),
|
|
3784
4421
|
created_at: record3.timestamp,
|
|
3785
|
-
expires_at:
|
|
4422
|
+
expires_at: this.expire(this.outboxRetentionMs)
|
|
3786
4423
|
});
|
|
4424
|
+
await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
3787
4425
|
await this.store.clearInvocationContext(identity, this.now());
|
|
3788
4426
|
return { enqueued: 1 };
|
|
3789
4427
|
}
|
|
3790
4428
|
async quarantine(registration, cursor, device, inode, reason) {
|
|
3791
|
-
|
|
4429
|
+
await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
|
|
4430
|
+
const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at: this.stamp() };
|
|
3792
4431
|
await this.store.writeTranscriptCursor(quarantined);
|
|
3793
4432
|
}
|
|
3794
4433
|
};
|
|
4434
|
+
function codexTranscriptScanFailureKind(error) {
|
|
4435
|
+
return transcriptScanFailureKind(error, (candidate) => {
|
|
4436
|
+
if (!(candidate instanceof CodexHostAdapterError)) return void 0;
|
|
4437
|
+
if (candidate.code === "payload_rejected") return "payload_rejected";
|
|
4438
|
+
if (candidate.code === "storage") return "read_error";
|
|
4439
|
+
return void 0;
|
|
4440
|
+
});
|
|
4441
|
+
}
|
|
3795
4442
|
function accumulateTokenCount(activeTask, record3) {
|
|
3796
4443
|
if (record3.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record3.usage_gap_reason };
|
|
3797
4444
|
const last = record3.last_usage;
|
|
@@ -3825,19 +4472,18 @@ function terminalCostValue() {
|
|
|
3825
4472
|
return notRecorded("provider_cost_not_observed");
|
|
3826
4473
|
}
|
|
3827
4474
|
function notRecorded(reason) {
|
|
3828
|
-
return
|
|
4475
|
+
return hostObservationNotRecorded(reason);
|
|
3829
4476
|
}
|
|
3830
4477
|
function tokenObservationValue(usage) {
|
|
3831
4478
|
return {
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
source: "codex_transcript.event_msg.token_count.info.last_token_usage",
|
|
4479
|
+
...createHostTokenObservationValue({
|
|
4480
|
+
input: usage.input_tokens,
|
|
4481
|
+
output: usage.output_tokens,
|
|
4482
|
+
total: usage.total_tokens,
|
|
4483
|
+
cached_input: usage.cached_input_tokens,
|
|
4484
|
+
cache_write_input: usage.cache_write_input_tokens,
|
|
4485
|
+
reasoning_output: usage.reasoning_output_tokens
|
|
4486
|
+
}, "codex_transcript.event_msg.token_count.info.last_token_usage"),
|
|
3841
4487
|
attribution: "sequential_active_task",
|
|
3842
4488
|
total_usage_role: "dedup_and_reset_detection_only"
|
|
3843
4489
|
};
|
|
@@ -3871,65 +4517,24 @@ function looksLikeStaleRegression(current, previous, last) {
|
|
|
3871
4517
|
if (previous.total_tokens <= 0 || current.total_tokens <= 0 || last.total_tokens <= 0) return false;
|
|
3872
4518
|
return current.total_tokens * 100 >= previous.total_tokens * 98 || current.total_tokens + last.total_tokens * 2 >= previous.total_tokens;
|
|
3873
4519
|
}
|
|
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
4520
|
function assertRecordSequence(registration, cursor, records, lastOrdinal, record3) {
|
|
3906
4521
|
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
4522
|
if (!cursor.header_validated && records === 0) assertCodexTranscriptHeader(registration, record3);
|
|
3908
4523
|
}
|
|
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
4524
|
|
|
3929
4525
|
// host/codex/src/codex-producer-runtime.ts
|
|
3930
4526
|
async function drainCodexProducerOnce(options) {
|
|
3931
4527
|
const promptPort = new HostRelayCurrentPromptPort(options.relay_endpoint, options.timeout_ms);
|
|
3932
|
-
const scan = await new CodexTranscriptWatcher({
|
|
4528
|
+
const scan = await new CodexTranscriptWatcher({
|
|
4529
|
+
outbox_dir: options.outbox_dir,
|
|
4530
|
+
producer_installation_ref: options.producer_installation_ref,
|
|
4531
|
+
register_current_prompt: (input) => promptPort.register(input),
|
|
4532
|
+
release_current_prompt: (identity) => promptPort.release(identity),
|
|
4533
|
+
diagnostic: (event, failure_kind) => writeHostServiceDiagnostic("codex", event, failure_kind)
|
|
4534
|
+
}).scanOnce().catch((error) => {
|
|
4535
|
+
writeHostServiceDiagnostic("codex", "watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
4536
|
+
throw error;
|
|
4537
|
+
});
|
|
3933
4538
|
const producer = new CodexProducer({ outbox_dir: options.outbox_dir, producer_installation_ref: options.producer_installation_ref, relay: new HostRelayIpcAdmissionPort(options.relay_endpoint, options.timeout_ms), ...options.producer_epoch === void 0 ? {} : { producer_epoch: options.producer_epoch }, ...options.now === void 0 ? {} : { now: options.now } });
|
|
3934
4539
|
return { scanned: scan.enqueued, ...await producer.drainOutbox() };
|
|
3935
4540
|
}
|
|
@@ -3953,7 +4558,7 @@ var CODEX_PRODUCER_DESCRIPTOR = {
|
|
|
3953
4558
|
};
|
|
3954
4559
|
|
|
3955
4560
|
// host/codex/src/codex-hook-runner.ts
|
|
3956
|
-
var MAX_STDIN_BYTES =
|
|
4561
|
+
var MAX_STDIN_BYTES = 512 * 1024;
|
|
3957
4562
|
async function runCodexHookFromStdin(event, stdin, options) {
|
|
3958
4563
|
const adapter = createCodexHostObservationAdapter(options);
|
|
3959
4564
|
return adapter.handle(event, await readStdin(stdin));
|