@corca-org/ceal-host-linux-arm64 1.0.17 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ceal-host +0 -0
- package/libexec/ceal-host/claude-hook.mjs +1944 -526
- package/libexec/ceal-host/claude-invocation-authentication.mjs +1827 -331
- package/libexec/ceal-host/claude-producer.mjs +1794 -413
- package/libexec/ceal-host/codex-hook.mjs +1407 -1332
- package/libexec/ceal-host/codex-invocation-authentication.mjs +3075 -856
- package/libexec/ceal-host/codex-producer.mjs +957 -918
- package/libexec/ceal-host/control.mjs +2289 -1452
- package/libexec/ceal-host/relay.mjs +822 -137
- package/package.json +1 -1
- package/share/ceal-host/bundle-manifest.json +12 -12
- package/share/ceal-host/bundle-manifest.sigstore.json +1 -1
|
@@ -109,6 +109,9 @@ var client_wire_contract_default = {
|
|
|
109
109
|
"document"
|
|
110
110
|
],
|
|
111
111
|
optional_fields: [
|
|
112
|
+
"operation_id",
|
|
113
|
+
"visible_output_max_bytes",
|
|
114
|
+
"local_delivery",
|
|
112
115
|
"verified_artifact"
|
|
113
116
|
],
|
|
114
117
|
bare_document_fields: [
|
|
@@ -195,11 +198,15 @@ var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_CONTINUATION_DOCUMENT_FIELDS = Object.f
|
|
|
195
198
|
var CEAL_COMMAND_RESPONSE_DISCOVERY_COMMAND = response.discovery_command;
|
|
196
199
|
var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_SCHEMA_VERSION = response.discovery_manifest_schema_version;
|
|
197
200
|
var CEAL_COMMAND_RESPONSE_DISCOVERY_MANIFEST_DOCUMENT_FIELDS = Object.freeze([...response.discovery_manifest_document_fields]);
|
|
201
|
+
var CEAL_COMMAND_RESPONSE_MAX_BYTES = CEAL_ENCODED_JSON_DOCUMENT_MAX_BYTES;
|
|
198
202
|
|
|
199
203
|
// packages/ceal-client-protocol/src/vocabulary.ts
|
|
200
204
|
function vocabulary(words) {
|
|
201
205
|
return Object.freeze(words);
|
|
202
206
|
}
|
|
207
|
+
function isVocabularyWord(words, value) {
|
|
208
|
+
return words.some((word) => word === value);
|
|
209
|
+
}
|
|
203
210
|
|
|
204
211
|
// packages/ceal-client-protocol/src/gateway-response-types.ts
|
|
205
212
|
var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
|
|
@@ -222,6 +229,7 @@ var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verifie
|
|
|
222
229
|
var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
|
|
223
230
|
var CEAL_PROTOCOL_VERSION = "1.4.0";
|
|
224
231
|
var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
|
|
232
|
+
var CEAL_GATEWAY_CONTINUATION_CURSOR_PHASES = vocabulary(["scan", "delivery", "terminal"]);
|
|
225
233
|
var CEAL_GATEWAY_ACTIVE_CALL_PHASES = vocabulary([
|
|
226
234
|
"audited_call_pending",
|
|
227
235
|
"continuation_read_pending",
|
|
@@ -245,6 +253,7 @@ var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
|
|
|
245
253
|
// packages/ceal-client-protocol/src/operation-next-actions.ts
|
|
246
254
|
var CEAL_REPEAT_REQUEST_NEXT_ACTION = "repeat_request";
|
|
247
255
|
var CEAL_INSPECT_RECEIPT_NEXT_ACTION = "inspect_receipt";
|
|
256
|
+
var CEAL_ANSWER_WITH_GAP_NEXT_ACTION = "answer_with_gap";
|
|
248
257
|
var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
249
258
|
"select_did_you_mean",
|
|
250
259
|
"choose_new_output_path",
|
|
@@ -298,10 +307,20 @@ var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
|
298
307
|
"retry_after",
|
|
299
308
|
// The ref names a container of rows (a Notion database or data source), which
|
|
300
309
|
// has no body of its own; the caller searches its rows instead of reading it.
|
|
301
|
-
"search_container_rows"
|
|
310
|
+
"search_container_rows",
|
|
311
|
+
// A settled unknown write names the exact receipt Operation that can inspect
|
|
312
|
+
// its outcome without issuing another provider mutation.
|
|
313
|
+
"write_receipt.get",
|
|
314
|
+
// Command resolution carries the exact required Operation sequence beside
|
|
315
|
+
// this action, so the caller can execute each declared input in order.
|
|
316
|
+
"follow_required_action_sequence",
|
|
317
|
+
// The command needs caller-owned local materialization (for example an
|
|
318
|
+
// @path file) before the Gateway can receive the argument.
|
|
319
|
+
"materialize_local_argument"
|
|
302
320
|
];
|
|
303
321
|
var CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS = Object.freeze(CALLER_RECOVERABLE_NEXT_ACTION_VALUES);
|
|
304
322
|
var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
323
|
+
CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
305
324
|
// Only an administrator can take the named step -- approving a place for an
|
|
306
325
|
// Operation, granting a withheld capability -- so a non-administrator asks
|
|
307
326
|
// one. The refusal names the administrator step; this word is the ask.
|
|
@@ -349,7 +368,58 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
|
349
368
|
"select_exact_approval"
|
|
350
369
|
];
|
|
351
370
|
var CEAL_NON_REMEDY_NEXT_ACTIONS = Object.freeze(NON_REMEDY_NEXT_ACTION_VALUES);
|
|
352
|
-
var
|
|
371
|
+
var CEAL_DECLARED_NEXT_ACTIONS = Object.freeze([...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS, ...CEAL_NON_REMEDY_NEXT_ACTIONS]);
|
|
372
|
+
var DECLARED = new Set(CEAL_DECLARED_NEXT_ACTIONS);
|
|
373
|
+
function defineCealNonModelNextActions(actions) {
|
|
374
|
+
return actions;
|
|
375
|
+
}
|
|
376
|
+
var NON_MODEL_NEXT_ACTION_VALUES = defineCealNonModelNextActions([
|
|
377
|
+
CEAL_INSPECT_RECEIPT_NEXT_ACTION,
|
|
378
|
+
"await_pending_approval",
|
|
379
|
+
"inspect_gateway",
|
|
380
|
+
"operator_action",
|
|
381
|
+
"none",
|
|
382
|
+
"check_gateway",
|
|
383
|
+
"inspect_context_store",
|
|
384
|
+
"inspect_local_cache",
|
|
385
|
+
"inspect_local_client",
|
|
386
|
+
"inspect_local_command",
|
|
387
|
+
"inspect_local_output",
|
|
388
|
+
"inspect_local_route",
|
|
389
|
+
"inspect_request",
|
|
390
|
+
"reconcile",
|
|
391
|
+
"reconcile_occurrence",
|
|
392
|
+
"retry_after_owner_recovery",
|
|
393
|
+
"select_exact_approval"
|
|
394
|
+
]);
|
|
395
|
+
var NON_MODEL_NEXT_ACTIONS = new Set(NON_MODEL_NEXT_ACTION_VALUES);
|
|
396
|
+
function isCealModelActionableNonRemedyNextAction(action) {
|
|
397
|
+
return !NON_MODEL_NEXT_ACTIONS.has(action);
|
|
398
|
+
}
|
|
399
|
+
var CEAL_MODEL_ACTIONABLE_NEXT_ACTIONS = Object.freeze([
|
|
400
|
+
...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS,
|
|
401
|
+
...CEAL_NON_REMEDY_NEXT_ACTIONS.filter(isCealModelActionableNonRemedyNextAction)
|
|
402
|
+
]);
|
|
403
|
+
var MODEL_ACTIONABLE = new Set(CEAL_MODEL_ACTIONABLE_NEXT_ACTIONS);
|
|
404
|
+
var NON_MODEL_ACTION_TO_MODEL_ACTION = Object.freeze({
|
|
405
|
+
[CEAL_INSPECT_RECEIPT_NEXT_ACTION]: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
406
|
+
await_pending_approval: "ask_admin",
|
|
407
|
+
inspect_gateway: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
408
|
+
operator_action: "ask_admin",
|
|
409
|
+
none: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
410
|
+
check_gateway: "ask_admin",
|
|
411
|
+
inspect_context_store: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
412
|
+
inspect_local_cache: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
413
|
+
inspect_local_client: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
414
|
+
inspect_local_command: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
415
|
+
inspect_local_output: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
416
|
+
inspect_local_route: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
417
|
+
inspect_request: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
418
|
+
reconcile: CEAL_ANSWER_WITH_GAP_NEXT_ACTION,
|
|
419
|
+
reconcile_occurrence: "ask_admin",
|
|
420
|
+
retry_after_owner_recovery: "ask_admin",
|
|
421
|
+
select_exact_approval: "ask_admin"
|
|
422
|
+
});
|
|
353
423
|
var RETRY_PROMISE_NEXT_ACTIONS = ["retry", "retry_after"];
|
|
354
424
|
var RETRY_PROMISE = new Set(RETRY_PROMISE_NEXT_ACTIONS);
|
|
355
425
|
var RECOVERABLE = new Set(CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS);
|
|
@@ -619,21 +689,15 @@ var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
|
|
|
619
689
|
// packages/ceal-client-protocol/src/domain-vocabulary.ts
|
|
620
690
|
var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
|
|
621
691
|
var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
|
|
622
|
-
var CEAL_PROVIDER_BOUNDARY_KINDS = vocabulary([
|
|
623
|
-
"github_account",
|
|
624
|
-
"github_organization",
|
|
625
|
-
"google_workspace_domain",
|
|
626
|
-
"notion_workspace",
|
|
627
|
-
"slack_workspace"
|
|
628
|
-
]);
|
|
629
|
-
var CEAL_PROVIDER_BOUNDARY_EVIDENCE = vocabulary(["provider_readback", "operator_stated"]);
|
|
630
692
|
var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
|
|
631
693
|
var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
|
|
632
694
|
var CEAL_TARGET_REF_PATTERN = "^[^\\p{Cc}]+$";
|
|
633
695
|
var TARGET_REF = new RegExp(CEAL_TARGET_REF_PATTERN, "u");
|
|
634
696
|
var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
|
|
635
|
-
var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "
|
|
697
|
+
var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "runtime"]);
|
|
698
|
+
var CEAL_PERSISTED_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
|
|
636
699
|
var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
|
|
700
|
+
var CEAL_CALENDAR_PERIODS = vocabulary(["day", "week", "month"]);
|
|
637
701
|
var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
|
|
638
702
|
|
|
639
703
|
// packages/ceal-client-protocol/src/safe-json-budget.ts
|
|
@@ -809,7 +873,7 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
809
873
|
"ceal.operation_receipt.v1",
|
|
810
874
|
"ceal.operation_result.v1",
|
|
811
875
|
"ceal.operation_result.compact.v1",
|
|
812
|
-
"ceal.
|
|
876
|
+
"ceal.local_result_file.v1",
|
|
813
877
|
"ceal.local_result_root.v1",
|
|
814
878
|
"ceal.owner_bootstrap_exchange_request.v1",
|
|
815
879
|
"ceal.owner_bootstrap_exchange_result.v1",
|
|
@@ -839,6 +903,9 @@ var CEAL_BOUNDED_SERIES_CATALOG = Object.freeze({
|
|
|
839
903
|
gateway_personal_client_access_audit: (root) => join(root, "access-audit.jsonl")
|
|
840
904
|
});
|
|
841
905
|
|
|
906
|
+
// packages/ceal-client-protocol/src/canonical-http-boundary.ts
|
|
907
|
+
var CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES = CEAL_COMMAND_RESPONSE_MAX_BYTES;
|
|
908
|
+
|
|
842
909
|
// packages/ceal-client-protocol/src/gateway-installation-activation.ts
|
|
843
910
|
var CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_DATA_CHARACTERS = 32;
|
|
844
911
|
var CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_CHECK_CHARACTERS = 8;
|
|
@@ -904,41 +971,6 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
|
|
|
904
971
|
}
|
|
905
972
|
});
|
|
906
973
|
|
|
907
|
-
// packages/ceal-client-protocol/src/provider-operation-vocabulary.ts
|
|
908
|
-
var CEAL_FILE_TYPE_FAMILIES = vocabulary(["all", "image", "pdf"]);
|
|
909
|
-
var CEAL_GITHUB_ISSUE_STATES = vocabulary(["open", "closed", "all"]);
|
|
910
|
-
var CEAL_NOTION_UPDATABLE_BLOCK_TYPES = vocabulary([
|
|
911
|
-
"paragraph",
|
|
912
|
-
"heading_1",
|
|
913
|
-
"heading_2",
|
|
914
|
-
"heading_3",
|
|
915
|
-
"bulleted_list_item",
|
|
916
|
-
"numbered_list_item",
|
|
917
|
-
"to_do",
|
|
918
|
-
"code"
|
|
919
|
-
]);
|
|
920
|
-
var CEAL_NOTION_NEUTRAL_TYPES = vocabulary([
|
|
921
|
-
"text",
|
|
922
|
-
"number",
|
|
923
|
-
"boolean",
|
|
924
|
-
"date",
|
|
925
|
-
"select",
|
|
926
|
-
"multi_select",
|
|
927
|
-
"people",
|
|
928
|
-
"url",
|
|
929
|
-
"email",
|
|
930
|
-
"phone",
|
|
931
|
-
"unsupported"
|
|
932
|
-
]);
|
|
933
|
-
var CEAL_NOTION_IDENTITY_GAP_REASONS = vocabulary([
|
|
934
|
-
"identity_unavailable",
|
|
935
|
-
"identity_stale",
|
|
936
|
-
"identity_ambiguous",
|
|
937
|
-
"identity_revoked",
|
|
938
|
-
"identity_unlinked"
|
|
939
|
-
]);
|
|
940
|
-
var CEAL_NOTION_STRING_VALUE_TYPES = vocabulary(["text", "select", "url", "email", "phone"]);
|
|
941
|
-
|
|
942
974
|
// packages/ceal-client-protocol/src/opaque-reference.ts
|
|
943
975
|
var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "block", "object"]);
|
|
944
976
|
var CEAL_OPAQUE_REF_KINDS = vocabulary([...CEAL_OPAQUE_HANDLE_KINDS, "result", "operation-receipt", "reply-receipt", "field", "notion-data-source"]);
|
|
@@ -950,7 +982,7 @@ var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_o
|
|
|
950
982
|
// packages/ceal-client-protocol/src/resource-row.ts
|
|
951
983
|
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
952
984
|
var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
|
|
953
|
-
var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "since", "until", "authored_by_me", "include_replies", "kinds", "container_ref", "container_refs", "container_names", "addressed_to_me", "unread"]);
|
|
985
|
+
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"]);
|
|
954
986
|
var CEAL_RESOURCE_TIME_BASES = vocabulary(["created", "modified", "viewed"]);
|
|
955
987
|
var CEAL_REPOSITORY_VISIBILITIES = vocabulary(["public", "private", "internal"]);
|
|
956
988
|
var CEAL_ACTIVITY_V3_KINDS = vocabulary(["authored", "edited", "messaged", "responded"]);
|
|
@@ -971,6 +1003,7 @@ var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
|
971
1003
|
"resource_modified"
|
|
972
1004
|
]);
|
|
973
1005
|
var CEAL_ACTIVITY_CONNECTOR_KINDS = vocabulary(["slack", "github", "google-calendar", "google-drive", "notion"]);
|
|
1006
|
+
var CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE = "unsupported_event_kind";
|
|
974
1007
|
var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
975
1008
|
"provider_page_budget",
|
|
976
1009
|
"call_deadline",
|
|
@@ -978,7 +1011,7 @@ var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
|
978
1011
|
"provider_cursor_unavailable",
|
|
979
1012
|
"malformed_provider_page",
|
|
980
1013
|
"visibility_incomplete",
|
|
981
|
-
|
|
1014
|
+
CEAL_ACTIVITY_UNSUPPORTED_EVENT_KIND_CODE,
|
|
982
1015
|
"direct_messages_excluded"
|
|
983
1016
|
]);
|
|
984
1017
|
var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
@@ -1029,6 +1062,7 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
1029
1062
|
ref: { required: true, timestamp: false },
|
|
1030
1063
|
next: { required: true, timestamp: false },
|
|
1031
1064
|
kind: { required: true, timestamp: false },
|
|
1065
|
+
matched_kinds: { required: false, timestamp: false },
|
|
1032
1066
|
title: { required: true, timestamp: false },
|
|
1033
1067
|
container_label: { required: true, timestamp: false },
|
|
1034
1068
|
container_ref: { required: false, timestamp: false },
|
|
@@ -1042,6 +1076,8 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
1042
1076
|
modified_at: { required: false, timestamp: true },
|
|
1043
1077
|
modified_at_local: { required: false, timestamp: true },
|
|
1044
1078
|
viewed_by_me_at: { required: false, timestamp: true },
|
|
1079
|
+
own_ceal_write: { required: false, timestamp: false },
|
|
1080
|
+
owned_by_me: { required: false, timestamp: false },
|
|
1045
1081
|
visibility: { required: false, timestamp: false },
|
|
1046
1082
|
start: { required: false, timestamp: false },
|
|
1047
1083
|
end: { required: false, timestamp: false },
|
|
@@ -1075,6 +1111,15 @@ function cealExcludedTargetCoverageJsonSchema() {
|
|
|
1075
1111
|
};
|
|
1076
1112
|
}
|
|
1077
1113
|
|
|
1114
|
+
// packages/ceal-client-protocol/src/gateway-write-identity.ts
|
|
1115
|
+
var CEAL_GATEWAY_WRITE_IDENTITY_ROLES = Object.freeze(["replay_identity", "lookup_handle", "collision_evidence"]);
|
|
1116
|
+
var CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN = /^gateway-write-request:[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/u;
|
|
1117
|
+
var CEAL_GATEWAY_WRITE_IDENTITY_FIELDS = Object.freeze({
|
|
1118
|
+
replay_identity: "idempotency_claim_sha256",
|
|
1119
|
+
lookup_handle: "write_request_sha256",
|
|
1120
|
+
collision_evidence: "normalized_mutation_sha256"
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1078
1123
|
// packages/ceal-client-protocol/src/write-safety-finding.ts
|
|
1079
1124
|
var CEAL_WRITE_SAFETY_FINDING_STATUSES = vocabulary(["warning", "unchecked"]);
|
|
1080
1125
|
var CEAL_WRITE_SAFETY_AUDIENCES = vocabulary(["workspace", "person", "members", "unknown"]);
|
|
@@ -1102,7 +1147,11 @@ var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
|
1102
1147
|
"refresh_revoked",
|
|
1103
1148
|
"authority_replaced",
|
|
1104
1149
|
"refresh_recovery_unavailable",
|
|
1105
|
-
"refresh_recovery_expired"
|
|
1150
|
+
"refresh_recovery_expired",
|
|
1151
|
+
// The serving gate answers this while the Gateway is still starting and the
|
|
1152
|
+
// refresh was never attempted; the same attempt stays retryable. It is never
|
|
1153
|
+
// stored by the session owner, only answered at the serving edge.
|
|
1154
|
+
"gateway_starting"
|
|
1106
1155
|
]);
|
|
1107
1156
|
|
|
1108
1157
|
// packages/ceal-client-protocol/src/request-validation-primitives.ts
|
|
@@ -1135,7 +1184,7 @@ var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
|
|
|
1135
1184
|
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"]);
|
|
1136
1185
|
var CEAL_REQUEST_JSON_SCHEMA = {
|
|
1137
1186
|
type: "object",
|
|
1138
|
-
maxProperties:
|
|
1187
|
+
maxProperties: 11,
|
|
1139
1188
|
additionalProperties: false,
|
|
1140
1189
|
properties: {
|
|
1141
1190
|
schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
|
|
@@ -1159,7 +1208,8 @@ var CEAL_REQUEST_JSON_SCHEMA = {
|
|
|
1159
1208
|
host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1160
1209
|
host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1161
1210
|
host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1162
|
-
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." }
|
|
1211
|
+
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." },
|
|
1212
|
+
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." }
|
|
1163
1213
|
},
|
|
1164
1214
|
required: CEAL_REQUEST_KEYS
|
|
1165
1215
|
};
|
|
@@ -1218,6 +1268,7 @@ var REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES = {
|
|
|
1218
1268
|
retryable: { type: "boolean" },
|
|
1219
1269
|
source: { type: "string", enum: ["provider", "gateway"] },
|
|
1220
1270
|
retry_after_ms: { type: "integer", minimum: 0, maximum: CEAL_RETRY_AFTER_MAX_MS },
|
|
1271
|
+
write_request_ref: { type: "string", pattern: CEAL_GATEWAY_WRITE_REQUEST_REF_PATTERN.source, maxLength: 128 },
|
|
1221
1272
|
excluded_targets: cealExcludedTargetCoverageJsonSchema(),
|
|
1222
1273
|
repaired_input: {},
|
|
1223
1274
|
unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
|
|
@@ -1276,6 +1327,7 @@ function isCealHostInputKind(value) {
|
|
|
1276
1327
|
return typeof value === "string" && CEAL_HOST_INPUT_KINDS.some((kind) => kind === value);
|
|
1277
1328
|
}
|
|
1278
1329
|
var CEAL_HOST_OBSERVATION_REF_PREFIX = "host-observation:";
|
|
1330
|
+
var HOST_TOKEN_OBSERVATION_BREAKDOWN_KEYS = vocabulary(["cached_input", "cache_write_input", "reasoning_output"]);
|
|
1279
1331
|
function validateCealHostObservationDraft(value) {
|
|
1280
1332
|
const record6 = operationRecord(value, "Ceal Host observation draft is invalid.");
|
|
1281
1333
|
requirePrefixedRef(record6.observation_ref, CEAL_HOST_OBSERVATION_REF_PREFIX, "Ceal Host observation reference is invalid.");
|
|
@@ -1419,6 +1471,104 @@ var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_MAX_BYTES = new TextEncoder().enc
|
|
|
1419
1471
|
expires_at: (/* @__PURE__ */ new Date("+010000-01-01T00:00:00.000Z")).toISOString()
|
|
1420
1472
|
})}
|
|
1421
1473
|
`).byteLength;
|
|
1474
|
+
var CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS = vocabulary([
|
|
1475
|
+
"request_authority_unavailable",
|
|
1476
|
+
"request_missing",
|
|
1477
|
+
"authority_mismatch",
|
|
1478
|
+
"host_source_mismatch",
|
|
1479
|
+
"host_session_mismatch",
|
|
1480
|
+
"host_turn_mismatch",
|
|
1481
|
+
"host_input_mismatch",
|
|
1482
|
+
"host_prompt_mismatch"
|
|
1483
|
+
]);
|
|
1484
|
+
var CEAL_HOST_AUTHENTICATION_CAUSES = vocabulary([
|
|
1485
|
+
"invalid_arguments",
|
|
1486
|
+
"options_invalid",
|
|
1487
|
+
"session_unavailable",
|
|
1488
|
+
"context_unavailable",
|
|
1489
|
+
"context_replaced",
|
|
1490
|
+
"no_binding",
|
|
1491
|
+
"context_stale",
|
|
1492
|
+
"context_expired",
|
|
1493
|
+
"host_source_refused",
|
|
1494
|
+
"host_credential_refused",
|
|
1495
|
+
"gateway_unavailable",
|
|
1496
|
+
"gateway_rate_limited",
|
|
1497
|
+
"gateway_refused",
|
|
1498
|
+
"gateway_response_invalid",
|
|
1499
|
+
"producer_frame_invalid",
|
|
1500
|
+
"relay_permission_denied",
|
|
1501
|
+
"relay_unavailable",
|
|
1502
|
+
"transient_retryable",
|
|
1503
|
+
"mint_failed",
|
|
1504
|
+
"relay_failed",
|
|
1505
|
+
"host_refusal_invalid"
|
|
1506
|
+
]);
|
|
1507
|
+
var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
1508
|
+
invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
|
|
1509
|
+
options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
|
|
1510
|
+
session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
1511
|
+
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn; run `ceal-host install` to repair the Host hook registration, then retry this command on a fresh Host turn" }),
|
|
1512
|
+
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
1513
|
+
no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
|
|
1514
|
+
context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
1515
|
+
context_expired: Object.freeze({ classification: "host_invocation_context_expired", next_action: "refresh_host_invocation_context" }),
|
|
1516
|
+
host_source_refused: Object.freeze({ classification: "host_source_refused", next_action: "answer_with_gap" }),
|
|
1517
|
+
host_credential_refused: Object.freeze({ classification: "host_registration_credential_refused", next_action: "ask_admin" }),
|
|
1518
|
+
gateway_unavailable: Object.freeze({ classification: "host_mint_gateway_unavailable", next_action: "ask_admin" }),
|
|
1519
|
+
gateway_rate_limited: Object.freeze({ classification: "host_mint_gateway_rate_limited", next_action: "answer_with_gap" }),
|
|
1520
|
+
gateway_refused: Object.freeze({ classification: "host_mint_gateway_refused", next_action: "answer_with_gap" }),
|
|
1521
|
+
gateway_response_invalid: Object.freeze({ classification: "host_mint_gateway_response_invalid", next_action: "answer_with_gap" }),
|
|
1522
|
+
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" }),
|
|
1523
|
+
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" }),
|
|
1524
|
+
relay_unavailable: Object.freeze({ classification: "host_relay_unavailable", next_action: "answer_with_gap" }),
|
|
1525
|
+
transient_retryable: Object.freeze({ classification: "host_mint_transient_retryable", next_action: "retry" }),
|
|
1526
|
+
mint_failed: Object.freeze({ classification: "host_mint_failed", next_action: "answer_with_gap" }),
|
|
1527
|
+
relay_failed: Object.freeze({ classification: "host_relay_failed", next_action: "answer_with_gap" }),
|
|
1528
|
+
host_refusal_invalid: Object.freeze({ classification: "host_refusal_invalid", next_action: "answer_with_gap" })
|
|
1529
|
+
});
|
|
1530
|
+
function projectCealHostAuthenticationCause(diagnostic) {
|
|
1531
|
+
return projectRelayCodeCause(diagnostic) ?? projectGatewayCodeCause(diagnostic) ?? projectGatewayStatusCause(diagnostic) ?? "mint_failed";
|
|
1532
|
+
}
|
|
1533
|
+
function projectRelayCodeCause(diagnostic) {
|
|
1534
|
+
switch (diagnostic.relayCode) {
|
|
1535
|
+
case "credential":
|
|
1536
|
+
return "host_credential_refused";
|
|
1537
|
+
case "configuration":
|
|
1538
|
+
return "options_invalid";
|
|
1539
|
+
case "payload_rejected":
|
|
1540
|
+
return "producer_frame_invalid";
|
|
1541
|
+
case "invalid_response":
|
|
1542
|
+
return "gateway_response_invalid";
|
|
1543
|
+
case "gateway_refused":
|
|
1544
|
+
return projectGatewayRefusedCause(diagnostic);
|
|
1545
|
+
case "gateway_unavailable":
|
|
1546
|
+
return diagnostic.exhausted === true ? void 0 : "gateway_unavailable";
|
|
1547
|
+
case void 0:
|
|
1548
|
+
case "":
|
|
1549
|
+
return void 0;
|
|
1550
|
+
default:
|
|
1551
|
+
return "mint_failed";
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
function projectGatewayRefusedCause(diagnostic) {
|
|
1555
|
+
if (diagnostic.gatewayStatus === 401 || diagnostic.gatewayStatus === 403) return "host_credential_refused";
|
|
1556
|
+
const stale = projectGatewayCodeCause(diagnostic);
|
|
1557
|
+
if (stale !== void 0) return stale;
|
|
1558
|
+
if (diagnostic.exhausted === true && (diagnostic.gatewayStatus === 408 || diagnostic.gatewayStatus === 429)) return "gateway_rate_limited";
|
|
1559
|
+
return "gateway_refused";
|
|
1560
|
+
}
|
|
1561
|
+
function projectGatewayCodeCause(diagnostic) {
|
|
1562
|
+
if (diagnostic.gatewayCode === "request_context_refused" && diagnostic.gatewayReason !== void 0 && isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, diagnostic.gatewayReason)) return "context_stale";
|
|
1563
|
+
if (diagnostic.gatewayCode === "context_expired") return "context_expired";
|
|
1564
|
+
if (diagnostic.gatewayCode === "host_source_refused") return "host_source_refused";
|
|
1565
|
+
return void 0;
|
|
1566
|
+
}
|
|
1567
|
+
function projectGatewayStatusCause(diagnostic) {
|
|
1568
|
+
if (diagnostic.gatewayStatus === 401 || diagnostic.gatewayStatus === 403) return "host_credential_refused";
|
|
1569
|
+
if (diagnostic.exhausted === true && (diagnostic.gatewayStatus === 408 || diagnostic.gatewayStatus === 429)) return "gateway_rate_limited";
|
|
1570
|
+
return void 0;
|
|
1571
|
+
}
|
|
1422
1572
|
function decodeCealHostInvocationAuthenticationResult(value, now = /* @__PURE__ */ new Date()) {
|
|
1423
1573
|
if (!isCealJsonRecord(value) || !exactKeys(value, ["schema_version", "context_token", "expires_at"]) || value.schema_version !== CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA || typeof value.context_token !== "string" || !CEAL_HOST_CONTEXT_TOKEN_PATTERN.test(value.context_token) || typeof value.expires_at !== "string" || !validFutureTimestamp(value.expires_at, now)) {
|
|
1424
1574
|
throw new TypeError("The Host invocation authentication result is invalid.");
|
|
@@ -1434,11 +1584,36 @@ function exactKeys(value, expected) {
|
|
|
1434
1584
|
return keys.length === expected.length && expected.every((key) => Object.hasOwn(value, key));
|
|
1435
1585
|
}
|
|
1436
1586
|
|
|
1587
|
+
// packages/ceal-client-protocol/src/host-service-diagnostic.ts
|
|
1588
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_PRODUCERS = vocabulary(["claude", "codex"]);
|
|
1589
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
1590
|
+
"hook_invoked",
|
|
1591
|
+
"registration_written",
|
|
1592
|
+
"hook_failed",
|
|
1593
|
+
"watcher_turn_observed",
|
|
1594
|
+
"current_prompt_registered",
|
|
1595
|
+
"current_prompt_registration_failed",
|
|
1596
|
+
"activation_recorded",
|
|
1597
|
+
"activation_failed",
|
|
1598
|
+
"watcher_scan_failed",
|
|
1599
|
+
"activation_wait_skipped",
|
|
1600
|
+
"activation_wait_engaged",
|
|
1601
|
+
"activation_wait_completed",
|
|
1602
|
+
"activation_wait_exhausted"
|
|
1603
|
+
]);
|
|
1604
|
+
var CEAL_HOST_SERVICE_DIAGNOSTIC_FAILURE_KINDS = vocabulary([
|
|
1605
|
+
"malformed_line",
|
|
1606
|
+
"other",
|
|
1607
|
+
"payload_rejected",
|
|
1608
|
+
"read_error",
|
|
1609
|
+
"size_budget",
|
|
1610
|
+
"symlink"
|
|
1611
|
+
]);
|
|
1612
|
+
|
|
1437
1613
|
// packages/ceal-client-protocol/src/client-result-materialization.ts
|
|
1438
1614
|
var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "unsupported", "download_failed", "permission_denied"]);
|
|
1439
|
-
var
|
|
1440
|
-
var
|
|
1441
|
-
var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "read_argv", "local_result_root", "continue_argv", "model_disclosure", "item_read_argv"]);
|
|
1615
|
+
var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "path", "sha256"]);
|
|
1616
|
+
var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "items_path", "row_count", "result_complete", "coverage", "local_result_root", "model_disclosure", "continue_argv", "how_to_read"]);
|
|
1442
1617
|
|
|
1443
1618
|
// packages/ceal-client-protocol/src/gateway-cache-origin-validation.ts
|
|
1444
1619
|
var CEAL_MAX_CACHE_ORIGIN_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -1468,14 +1643,6 @@ var CEAL_GATEWAY_ANNOUNCEMENT_POLICY_NON_CLAIMS = vocabulary([
|
|
|
1468
1643
|
"target_specific_scope_not_declared"
|
|
1469
1644
|
]);
|
|
1470
1645
|
|
|
1471
|
-
// packages/ceal-client-protocol/src/gateway-write-identity.ts
|
|
1472
|
-
var CEAL_GATEWAY_WRITE_IDENTITY_ROLES = Object.freeze(["replay_identity", "lookup_handle", "collision_evidence"]);
|
|
1473
|
-
var CEAL_GATEWAY_WRITE_IDENTITY_FIELDS = Object.freeze({
|
|
1474
|
-
replay_identity: "idempotency_claim_sha256",
|
|
1475
|
-
lookup_handle: "write_request_sha256",
|
|
1476
|
-
collision_evidence: "normalized_mutation_sha256"
|
|
1477
|
-
});
|
|
1478
|
-
|
|
1479
1646
|
// packages/ceal-client-protocol/src/gateway-scoped-identity-projection-validation.ts
|
|
1480
1647
|
var PROJECTION_REVISION_KEYS = Object.freeze(["graph_revision", "subject_key_revision", "projection_revision"]);
|
|
1481
1648
|
|
|
@@ -1488,6 +1655,18 @@ var CEAL_SUPPORTED_GATEWAY_PROTOCOL_RANGE = Object.freeze({
|
|
|
1488
1655
|
// packages/ceal-client-protocol/src/admin-projection-vocabulary.ts
|
|
1489
1656
|
var ADMIN_PROJECTION_STATES = vocabulary(["ready", "empty", "partial", "unknown"]);
|
|
1490
1657
|
var ADMIN_VIEW_KINDS = vocabulary(["request_detail", "resource_access_timeline", "weekly_usage"]);
|
|
1658
|
+
var ADMIN_RECORD_PROJECTION_KINDS = vocabulary([
|
|
1659
|
+
"request_list",
|
|
1660
|
+
"usage_series",
|
|
1661
|
+
"connector_health",
|
|
1662
|
+
"person_connections",
|
|
1663
|
+
"client_sessions",
|
|
1664
|
+
"knowledge_base",
|
|
1665
|
+
"change_history",
|
|
1666
|
+
"approval_queue",
|
|
1667
|
+
"access_group_list",
|
|
1668
|
+
"capability_catalogue"
|
|
1669
|
+
]);
|
|
1491
1670
|
var ADMIN_GAP_KINDS = vocabulary(["unknown", "unavailable", "partial"]);
|
|
1492
1671
|
var ADMIN_UNAVAILABLE_REASONS = vocabulary([
|
|
1493
1672
|
"not_recorded",
|
|
@@ -1511,7 +1690,11 @@ var ADMIN_SOURCE_OWNERS = vocabulary([
|
|
|
1511
1690
|
"person",
|
|
1512
1691
|
"resource",
|
|
1513
1692
|
"prompt",
|
|
1514
|
-
"usage"
|
|
1693
|
+
"usage",
|
|
1694
|
+
"connector",
|
|
1695
|
+
"session",
|
|
1696
|
+
"knowledge",
|
|
1697
|
+
"policy"
|
|
1515
1698
|
]);
|
|
1516
1699
|
var ADMIN_COMPLETENESS_STATES = vocabulary(["complete", "partial", "unknown"]);
|
|
1517
1700
|
var ADMIN_OPERATION_OUTCOMES = vocabulary(["completed", "failed", "denied", "cancelled", "unknown"]);
|
|
@@ -1649,7 +1832,7 @@ function assertHostRelaySourcePaths(stateRoot, config, platform = process.platfo
|
|
|
1649
1832
|
// host/relay/src/host-relay-runtime.ts
|
|
1650
1833
|
import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
|
|
1651
1834
|
import { lstat as lstat4, open as open5 } from "node:fs/promises";
|
|
1652
|
-
import { dirname as
|
|
1835
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
1653
1836
|
|
|
1654
1837
|
// host/abortable-wait.ts
|
|
1655
1838
|
function waitForAbort(signal, milliseconds) {
|
|
@@ -1746,6 +1929,15 @@ var FRAME_KINDS = new Set(HOST_PRODUCER_FRAME_KINDS);
|
|
|
1746
1929
|
function isHostProducerFrameKind(value) {
|
|
1747
1930
|
return typeof value === "string" && FRAME_KINDS.has(value);
|
|
1748
1931
|
}
|
|
1932
|
+
var HOST_PRODUCER_FRAME_ERROR_CODES = [
|
|
1933
|
+
"invalid_frame_encoding",
|
|
1934
|
+
"invalid_frame_length",
|
|
1935
|
+
"frame_truncated",
|
|
1936
|
+
"unsupported_frame_schema",
|
|
1937
|
+
"invalid_frame_shape",
|
|
1938
|
+
"invalid_producer_identity",
|
|
1939
|
+
"frame_too_large"
|
|
1940
|
+
];
|
|
1749
1941
|
var HostProducerFrameError = class extends Error {
|
|
1750
1942
|
code;
|
|
1751
1943
|
constructor(code, message = code) {
|
|
@@ -1753,6 +1945,9 @@ var HostProducerFrameError = class extends Error {
|
|
|
1753
1945
|
this.code = code;
|
|
1754
1946
|
}
|
|
1755
1947
|
};
|
|
1948
|
+
function assertHostProducerFrame(value) {
|
|
1949
|
+
validateFrame(value);
|
|
1950
|
+
}
|
|
1756
1951
|
function encodeHostProducerFrame(frame) {
|
|
1757
1952
|
validateFrame(frame);
|
|
1758
1953
|
const json = Buffer.from(JSON.stringify(frame), "utf8");
|
|
@@ -1783,6 +1978,30 @@ function decodeHostProducerFrame(input) {
|
|
|
1783
1978
|
function hostProducerFrameHash(frame) {
|
|
1784
1979
|
return createHash3("sha256").update(JSON.stringify(frame), "utf8").digest("hex");
|
|
1785
1980
|
}
|
|
1981
|
+
function extractHostBindingSelector(frame) {
|
|
1982
|
+
if (frame.frame_kind !== "user_input") return null;
|
|
1983
|
+
if (!PRODUCERS.has(frame.producer_id) || !safeRef(frame.producer_installation_ref)) return null;
|
|
1984
|
+
const payload = frame.payload;
|
|
1985
|
+
if (!safeRef(payload.local_session_ref) || !safeRef(payload.local_turn_ref)) return null;
|
|
1986
|
+
const aliases = payload.local_turn_aliases;
|
|
1987
|
+
if (aliases === void 0) {
|
|
1988
|
+
return Object.freeze({
|
|
1989
|
+
producer_id: frame.producer_id,
|
|
1990
|
+
producer_installation_ref: frame.producer_installation_ref,
|
|
1991
|
+
local_session_ref: payload.local_session_ref,
|
|
1992
|
+
local_turn_ref: payload.local_turn_ref,
|
|
1993
|
+
local_turn_aliases: Object.freeze([])
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
if (!Array.isArray(aliases) || aliases.length > 16 || !aliases.every(safeRef) || new Set(aliases).size !== aliases.length) return null;
|
|
1997
|
+
return Object.freeze({
|
|
1998
|
+
producer_id: frame.producer_id,
|
|
1999
|
+
producer_installation_ref: frame.producer_installation_ref,
|
|
2000
|
+
local_session_ref: payload.local_session_ref,
|
|
2001
|
+
local_turn_ref: payload.local_turn_ref,
|
|
2002
|
+
local_turn_aliases: Object.freeze([...aliases])
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
1786
2005
|
function hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef) {
|
|
1787
2006
|
if (!opaqueHostSourceRef(hostSourceRef)) throw new TypeError("Gateway host source reference is invalid.");
|
|
1788
2007
|
const host_session_ref = localSessionRef === void 0 ? void 0 : `ceal-host-session:${cealRequestSha256({ host_source_ref: hostSourceRef, producer_id: frame.producer_id, producer_installation_ref: frame.producer_installation_ref, local_session_ref: localSessionRef })}`;
|
|
@@ -1806,6 +2025,31 @@ function hostProducerSubmissionRef(hostSourceRef, frame) {
|
|
|
1806
2025
|
function opaqueHostSourceRef(value) {
|
|
1807
2026
|
return typeof value === "string" && SAFE_REF2.test(value) && !value.startsWith("ceal-host-source:");
|
|
1808
2027
|
}
|
|
2028
|
+
function isHostSafeRef(value) {
|
|
2029
|
+
return safeRef(value);
|
|
2030
|
+
}
|
|
2031
|
+
var BOUNDED_REFUSAL_WORD = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
2032
|
+
function isBoundedRefusalWord(value) {
|
|
2033
|
+
return typeof value === "string" && BOUNDED_REFUSAL_WORD.test(value);
|
|
2034
|
+
}
|
|
2035
|
+
function hasExactKeys2(value, required, optional = []) {
|
|
2036
|
+
return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
|
|
2037
|
+
}
|
|
2038
|
+
function isUndefinedOr(value, guard) {
|
|
2039
|
+
return value === void 0 || guard(value);
|
|
2040
|
+
}
|
|
2041
|
+
var HOST_RELAY_GATEWAY_ERROR_CODES = [
|
|
2042
|
+
"configuration",
|
|
2043
|
+
"credential",
|
|
2044
|
+
"gateway_unavailable",
|
|
2045
|
+
"gateway_refused",
|
|
2046
|
+
"invalid_response",
|
|
2047
|
+
"payload_rejected"
|
|
2048
|
+
];
|
|
2049
|
+
function isRelayGatewayErrorCode(value) {
|
|
2050
|
+
return isVocabularyWord(HOST_RELAY_GATEWAY_ERROR_CODES, value);
|
|
2051
|
+
}
|
|
2052
|
+
var RELAY_DELIVERY_DIAGNOSTIC_KEYS = ["relayCode", "gatewayStatus", "gatewayCode", "gatewayReason", "exhausted"];
|
|
1809
2053
|
function requiredIdentity(value) {
|
|
1810
2054
|
if (value === void 0) throw new TypeError("Canonical Host identity is incomplete.");
|
|
1811
2055
|
return value;
|
|
@@ -2234,6 +2478,8 @@ var RELAY_QUARANTINE_MAX_RECORDS = 512;
|
|
|
2234
2478
|
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
2235
2479
|
var MAX_CAPTURE_BYTES = 262148;
|
|
2236
2480
|
var PRODUCERS2 = ["codex", "claude", "unknown"];
|
|
2481
|
+
var RELAY_QUARANTINE_FRAME_REASONS = ["frame_identity_conflict", "sequence_gap", "gateway_refused"];
|
|
2482
|
+
var RELAY_QUARANTINE_REASONS = [...RELAY_QUARANTINE_FRAME_REASONS, ...HOST_PRODUCER_FRAME_ERROR_CODES];
|
|
2237
2483
|
var RelayQuarantineStore = class {
|
|
2238
2484
|
root;
|
|
2239
2485
|
maxRecords;
|
|
@@ -2246,8 +2492,8 @@ var RelayQuarantineStore = class {
|
|
|
2246
2492
|
if (!Number.isSafeInteger(this.maxRecords) || this.maxRecords < 1 || this.maxRecords > RELAY_QUARANTINE_MAX_RECORDS) throw new TypeError(`Relay quarantine record cap is invalid: ${refusalFact("max_records", this.maxRecords, `between 1 and ${RELAY_QUARANTINE_MAX_RECORDS}`)}.`);
|
|
2247
2493
|
if (!Number.isSafeInteger(this.maxBytes) || this.maxBytes < 1 || this.maxBytes > RELAY_QUARANTINE_MAX_BYTES) throw new TypeError(`Relay quarantine byte cap is invalid: ${refusalFact("max_bytes", this.maxBytes, `between 1 and ${RELAY_QUARANTINE_MAX_BYTES}`)}.`);
|
|
2248
2494
|
}
|
|
2249
|
-
async retainFrame(frame, reason, capturedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2250
|
-
await this.retain(encodeHostProducerFrame(frame), frame.producer_id, reason, capturedAt);
|
|
2495
|
+
async retainFrame(frame, reason, attribution, capturedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2496
|
+
await this.retain(encodeHostProducerFrame(frame), frame.producer_id, reason, capturedAt, attribution);
|
|
2251
2497
|
}
|
|
2252
2498
|
async retainMalformed(rawFrame, reason, capturedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2253
2499
|
await this.retain(rawFrame, inferProducer(rawFrame), reason, capturedAt);
|
|
@@ -2259,11 +2505,26 @@ var RelayQuarantineStore = class {
|
|
|
2259
2505
|
const records = state.producers[producerId].slice(-Math.min(limit, this.maxRecords)).map((record6) => structuredClone(record6));
|
|
2260
2506
|
return { producer_id: producerId, records, total_bytes: records.reduce((total, record6) => total + record6.charged_bytes, 0) };
|
|
2261
2507
|
}
|
|
2508
|
+
/**
|
|
2509
|
+
* The newest quarantined record whose exact-turn attribution matches the
|
|
2510
|
+
* query, or null when evicted, unattributed, or for another turn. The
|
|
2511
|
+
* file-backed state survives drain, restart, and overwrite.
|
|
2512
|
+
*/
|
|
2513
|
+
async findNewestAttributedMatch(query) {
|
|
2514
|
+
await this.tail;
|
|
2515
|
+
const state = await this.readState();
|
|
2516
|
+
const bucket = query.producer_id === "codex" || query.producer_id === "claude" ? state.producers[query.producer_id] : [];
|
|
2517
|
+
for (let index = bucket.length - 1; index >= 0; index -= 1) {
|
|
2518
|
+
const record6 = bucket[index];
|
|
2519
|
+
if (record6 !== void 0 && attributedRecordMatchesQuery(record6, query)) return structuredClone(record6);
|
|
2520
|
+
}
|
|
2521
|
+
return null;
|
|
2522
|
+
}
|
|
2262
2523
|
async snapshot() {
|
|
2263
2524
|
await this.tail;
|
|
2264
2525
|
return this.readState();
|
|
2265
2526
|
}
|
|
2266
|
-
async retain(rawFrame, producerId, reason, capturedAt) {
|
|
2527
|
+
async retain(rawFrame, producerId, reason, capturedAt, attribution) {
|
|
2267
2528
|
const run = this.tail.then(async () => {
|
|
2268
2529
|
if (!PRODUCERS2.includes(producerId) || !Number.isFinite(Date.parse(capturedAt))) throw new TypeError("Relay quarantine record identity is invalid.");
|
|
2269
2530
|
const raw = Buffer.from(rawFrame).subarray(0, MAX_CAPTURE_BYTES);
|
|
@@ -2275,7 +2536,8 @@ var RelayQuarantineStore = class {
|
|
|
2275
2536
|
captured_at: capturedAt,
|
|
2276
2537
|
frame_sha256: createHash4("sha256").update(raw).digest("hex"),
|
|
2277
2538
|
charged_bytes: raw.byteLength,
|
|
2278
|
-
raw_frame_base64: raw.toString("base64")
|
|
2539
|
+
raw_frame_base64: raw.toString("base64"),
|
|
2540
|
+
...attribution === void 0 ? {} : { attribution: structuredClone(attribution) }
|
|
2279
2541
|
};
|
|
2280
2542
|
const records = state.producers[producerId];
|
|
2281
2543
|
records.push(record6);
|
|
@@ -2312,8 +2574,47 @@ function decodeState2(value) {
|
|
|
2312
2574
|
return state;
|
|
2313
2575
|
}
|
|
2314
2576
|
function validRecord(value) {
|
|
2315
|
-
if (!record3(value) ||
|
|
2316
|
-
return
|
|
2577
|
+
if (!record3(value) || !validRecordIdentity(value)) return false;
|
|
2578
|
+
return value.attribution === void 0 || validAttribution(value.attribution);
|
|
2579
|
+
}
|
|
2580
|
+
function validRecordIdentity(value) {
|
|
2581
|
+
return typeof value.quarantine_ref === "string" && quarantineProducer(value.producer_id) && validReason(value.reason) && Number.isFinite(Date.parse(String(value.captured_at))) && typeof value.frame_sha256 === "string" && typeof value.raw_frame_base64 === "string" && validChargedBytes(value.charged_bytes);
|
|
2582
|
+
}
|
|
2583
|
+
function validReason(value) {
|
|
2584
|
+
return typeof value === "string" && RELAY_QUARANTINE_REASONS.includes(value);
|
|
2585
|
+
}
|
|
2586
|
+
function validChargedBytes(value) {
|
|
2587
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
2588
|
+
}
|
|
2589
|
+
var ATTRIBUTION_REQUIRED_KEYS = ["producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "local_turn_aliases", "sequence", "frame_sha256"];
|
|
2590
|
+
var ATTRIBUTION_OPTIONAL_KEYS = ["diagnostic"];
|
|
2591
|
+
function validAttribution(value) {
|
|
2592
|
+
if (!record3(value) || !hasExactKeys2(value, ATTRIBUTION_REQUIRED_KEYS, ATTRIBUTION_OPTIONAL_KEYS)) return false;
|
|
2593
|
+
return validAttributionIdentity(value) && validAttributionAliases(value.local_turn_aliases) && validAttributionEnvelope(value) && (value.diagnostic === void 0 || validAttributionDiagnostic(value.diagnostic));
|
|
2594
|
+
}
|
|
2595
|
+
function validAttributionIdentity(value) {
|
|
2596
|
+
return (value.producer_id === "codex" || value.producer_id === "claude") && isHostSafeRef(value.producer_installation_ref) && isHostSafeRef(value.local_session_ref) && isHostSafeRef(value.local_turn_ref);
|
|
2597
|
+
}
|
|
2598
|
+
function validAttributionAliases(value) {
|
|
2599
|
+
return Array.isArray(value) && value.length <= 16 && value.every(isHostSafeRef);
|
|
2600
|
+
}
|
|
2601
|
+
function validAttributionEnvelope(value) {
|
|
2602
|
+
return typeof value.sequence === "number" && Number.isSafeInteger(value.sequence) && value.sequence > 0 && typeof value.frame_sha256 === "string";
|
|
2603
|
+
}
|
|
2604
|
+
function validAttributionDiagnostic(value) {
|
|
2605
|
+
if (!record3(value) || !hasExactKeys2(value, [], RELAY_DELIVERY_DIAGNOSTIC_KEYS)) return false;
|
|
2606
|
+
return isUndefinedOr(value.relayCode, isRelayGatewayErrorCode) && isUndefinedOr(value.gatewayStatus, isSafeInteger) && isUndefinedOr(value.gatewayCode, isBoundedRefusalWord) && isUndefinedOr(value.gatewayReason, isBoundedRefusalWord) && isUndefinedOr(value.exhausted, isBoolean);
|
|
2607
|
+
}
|
|
2608
|
+
function isSafeInteger(value) {
|
|
2609
|
+
return Number.isSafeInteger(value);
|
|
2610
|
+
}
|
|
2611
|
+
function isBoolean(value) {
|
|
2612
|
+
return typeof value === "boolean";
|
|
2613
|
+
}
|
|
2614
|
+
function attributedRecordMatchesQuery(recordValue, query) {
|
|
2615
|
+
const attribution = recordValue.attribution;
|
|
2616
|
+
if (attribution === void 0) return false;
|
|
2617
|
+
return attribution.producer_id === query.producer_id && attribution.producer_installation_ref === query.producer_installation_ref && attribution.local_session_ref === query.local_session_ref && (attribution.local_turn_ref === query.local_turn_ref || attribution.local_turn_aliases.includes(query.local_turn_ref));
|
|
2317
2618
|
}
|
|
2318
2619
|
function quarantineProducer(value) {
|
|
2319
2620
|
return value === "codex" || value === "claude" || value === "unknown";
|
|
@@ -2401,11 +2702,11 @@ var RelayAdmissionStore = class {
|
|
|
2401
2702
|
const hash = hostProducerFrameHash(frame);
|
|
2402
2703
|
const replay = replayCode(existing, hash, frame.sequence, installation2);
|
|
2403
2704
|
if (replay !== null) {
|
|
2404
|
-
if (replay === "frame_identity_conflict") await this.quarantine.retainFrame(frame, replay);
|
|
2705
|
+
if (replay === "frame_identity_conflict") await this.quarantine.retainFrame(frame, replay, attributionForFrame(frame, hash, frame.sequence));
|
|
2405
2706
|
return result(replay, installation2);
|
|
2406
2707
|
}
|
|
2407
2708
|
if (frame.sequence !== installation2.admitted_through_sequence + 1) {
|
|
2408
|
-
await this.quarantine.retainFrame(frame, "sequence_gap");
|
|
2709
|
+
await this.quarantine.retainFrame(frame, "sequence_gap", attributionForFrame(frame, hash, frame.sequence));
|
|
2409
2710
|
return result("sequence_gap", installation2);
|
|
2410
2711
|
}
|
|
2411
2712
|
const chargedBytes = 4 + Buffer.byteLength(JSON.stringify(frame), "utf8");
|
|
@@ -2433,31 +2734,54 @@ var RelayAdmissionStore = class {
|
|
|
2433
2734
|
for (const producerId of PRODUCER_ORDER) {
|
|
2434
2735
|
const head = state.entries.find((entry2) => entry2.producer_id === producerId);
|
|
2435
2736
|
if (!head || head.retry_at_ms > options.now_ms) continue;
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
const exhausted = retryExhausted(outcome, head);
|
|
2439
|
-
if (outcome.status === "retryable" && !exhausted) {
|
|
2440
|
-
scheduleRetry(head, options.now_ms, outcome.reason);
|
|
2441
|
-
retained.push(input.submission_ref);
|
|
2442
|
-
await this.write(state);
|
|
2443
|
-
continue;
|
|
2444
|
-
}
|
|
2445
|
-
const installation2 = state.installations[installationKey(head.frame)];
|
|
2446
|
-
if (!installation2) throw new TypeError("Host Relay delivery installation is unavailable.");
|
|
2447
|
-
if (outcome.status === "refused" || exhausted) {
|
|
2448
|
-
await this.quarantine.retainFrame(head.frame, "gateway_refused");
|
|
2449
|
-
installation2.last_refusal = { sequence: head.sequence, submission_ref: input.submission_ref, ...refusalCause(outcome, head), refused_at_ms: options.now_ms };
|
|
2450
|
-
refused.push(input.submission_ref);
|
|
2451
|
-
} else {
|
|
2452
|
-
await options.after_gateway_accept_before_delivery_fsync?.({ fault_point: "after_gateway_accept_before_delivery_fsync", delivery: structuredClone(input) });
|
|
2453
|
-
delivered.push(input.submission_ref);
|
|
2454
|
-
}
|
|
2455
|
-
state.entries.splice(state.entries.indexOf(head), 1);
|
|
2456
|
-
installation2.delivered_through_sequence = Math.max(installation2.delivered_through_sequence, head.sequence);
|
|
2457
|
-
await this.write(state);
|
|
2737
|
+
if (await this.refusePoisonHead(state, head, options, refused)) continue;
|
|
2738
|
+
await this.deliverHead(state, head, deliver, options, delivered, retained, refused);
|
|
2458
2739
|
}
|
|
2459
2740
|
return { delivered, retained, refused };
|
|
2460
2741
|
}
|
|
2742
|
+
async deliverHead(state, head, deliver, options, delivered, retained, refused) {
|
|
2743
|
+
const input = { frame: structuredClone(head.frame), host_source_ref: options.host_source_ref, submission_ref: hostProducerSubmissionRef(options.host_source_ref, head.frame) };
|
|
2744
|
+
const outcome = await deliver(input);
|
|
2745
|
+
const exhausted = retryExhausted(outcome, head);
|
|
2746
|
+
if (outcome.status === "retryable" && !exhausted) {
|
|
2747
|
+
scheduleRetry(head, options.now_ms, outcome.reason);
|
|
2748
|
+
retained.push(input.submission_ref);
|
|
2749
|
+
await this.write(state);
|
|
2750
|
+
return;
|
|
2751
|
+
}
|
|
2752
|
+
const installation2 = state.installations[installationKey(head.frame)];
|
|
2753
|
+
if (!installation2) throw new TypeError("Host Relay delivery installation is unavailable.");
|
|
2754
|
+
if (outcome.status === "refused" || exhausted) {
|
|
2755
|
+
const diagnostic = exhaustedDiagnostic(outcome, exhausted);
|
|
2756
|
+
await this.quarantine.retainFrame(head.frame, "gateway_refused", attributionForFrame(head.frame, head.hash, head.sequence, diagnostic));
|
|
2757
|
+
installation2.last_refusal = { sequence: head.sequence, submission_ref: input.submission_ref, ...refusalCause(outcome, head), ...diagnostic === void 0 ? {} : { diagnostic }, refused_at_ms: options.now_ms };
|
|
2758
|
+
refused.push(input.submission_ref);
|
|
2759
|
+
} else {
|
|
2760
|
+
await options.after_gateway_accept_before_delivery_fsync?.({ fault_point: "after_gateway_accept_before_delivery_fsync", delivery: structuredClone(input) });
|
|
2761
|
+
delivered.push(input.submission_ref);
|
|
2762
|
+
}
|
|
2763
|
+
state.entries.splice(state.entries.indexOf(head), 1);
|
|
2764
|
+
installation2.delivered_through_sequence = Math.max(installation2.delivered_through_sequence, head.sequence);
|
|
2765
|
+
await this.write(state);
|
|
2766
|
+
}
|
|
2767
|
+
async refusePoisonHead(state, head, options, refused) {
|
|
2768
|
+
const poison = poisonFrameError(head.frame);
|
|
2769
|
+
if (poison === null) return false;
|
|
2770
|
+
const installation2 = state.installations[installationKey(head)];
|
|
2771
|
+
if (!hasIntactAdmissionEnvelope(head, installation2)) throw new TypeError("Host Relay admission state is invalid.");
|
|
2772
|
+
const json = Buffer.from(JSON.stringify(head.frame), "utf8");
|
|
2773
|
+
const raw = Buffer.allocUnsafe(json.byteLength + 4);
|
|
2774
|
+
raw.writeUInt32BE(json.byteLength, 0);
|
|
2775
|
+
json.copy(raw, 4);
|
|
2776
|
+
await this.quarantine.retainMalformed(raw, poison.code);
|
|
2777
|
+
const submissionRef = hostProducerSubmissionRef(options.host_source_ref, { producer_id: head.producer_id, producer_installation_ref: head.producer_installation_ref, sequence: head.sequence });
|
|
2778
|
+
installation2.last_refusal = { sequence: head.sequence, submission_ref: submissionRef, code: poison.code, message: `Host Relay persisted frame is invalid: ${poison.code}.`, refused_at_ms: options.now_ms };
|
|
2779
|
+
refused.push(submissionRef);
|
|
2780
|
+
state.entries.splice(state.entries.indexOf(head), 1);
|
|
2781
|
+
installation2.delivered_through_sequence = Math.max(installation2.delivered_through_sequence, head.sequence);
|
|
2782
|
+
await this.write(state);
|
|
2783
|
+
return true;
|
|
2784
|
+
}
|
|
2461
2785
|
async read() {
|
|
2462
2786
|
try {
|
|
2463
2787
|
return decodeState3(JSON.parse(await readFile2(join4(this.root, "admission.json"), "utf8")));
|
|
@@ -2474,17 +2798,22 @@ function installationKey(frame) {
|
|
|
2474
2798
|
return `${frame.producer_id}\0${frame.producer_installation_ref}`;
|
|
2475
2799
|
}
|
|
2476
2800
|
function pendingFrameMatchesBindingQuery(entry2, query) {
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2801
|
+
const selector = extractHostBindingSelector(entry2.frame);
|
|
2802
|
+
if (selector === null) return false;
|
|
2803
|
+
return relayRequestBindingMatchesQuery(query, selector);
|
|
2804
|
+
}
|
|
2805
|
+
function attributionForFrame(frame, hash, sequence, diagnostic) {
|
|
2806
|
+
const selector = extractHostBindingSelector(frame);
|
|
2807
|
+
if (selector === null) return void 0;
|
|
2808
|
+
if (frame.producer_id !== selector.producer_id || frame.producer_installation_ref !== selector.producer_installation_ref || frame.sequence !== sequence) return void 0;
|
|
2809
|
+
if (hostProducerFrameHash(frame) !== hash) return void 0;
|
|
2810
|
+
return { ...selector, sequence, frame_sha256: hash, ...diagnostic === void 0 ? {} : { diagnostic } };
|
|
2811
|
+
}
|
|
2812
|
+
function exhaustedDiagnostic(outcome, exhausted) {
|
|
2813
|
+
if (outcome.status !== "retryable" && outcome.status !== "refused") return void 0;
|
|
2814
|
+
if (outcome.diagnostic !== void 0) return exhausted ? { ...outcome.diagnostic, exhausted: true } : outcome.diagnostic;
|
|
2815
|
+
if (exhausted) return { exhausted: true };
|
|
2816
|
+
return void 0;
|
|
2488
2817
|
}
|
|
2489
2818
|
function replayCode(existing, hash, sequence, installation2) {
|
|
2490
2819
|
if (existing !== void 0) return existing.hash === hash ? "already_admitted" : "frame_identity_conflict";
|
|
@@ -2523,6 +2852,8 @@ function decodeState3(value) {
|
|
|
2523
2852
|
const entries = [];
|
|
2524
2853
|
for (const item of value.entries) {
|
|
2525
2854
|
if (!entry(item)) throw new TypeError("Host Relay entry state is invalid.");
|
|
2855
|
+
poisonFrameError(item.frame);
|
|
2856
|
+
if (!hasIntactAdmissionEnvelope(item, installations[installationKey(item)])) throw new TypeError("Host Relay admission state is invalid.");
|
|
2526
2857
|
entries.push(item);
|
|
2527
2858
|
}
|
|
2528
2859
|
return { schema_version: "ceal.host_relay_admission_state.v1", installations, entries };
|
|
@@ -2534,7 +2865,17 @@ function installation(value) {
|
|
|
2534
2865
|
return typeof value === "object" && value !== null && !Array.isArray(value) && "epoch" in value && "admitted_through_sequence" in value && "delivered_through_sequence" in value && [value.epoch, value.admitted_through_sequence, value.delivered_through_sequence].every(Number.isSafeInteger) && (!("last_refusal" in value) || refusalRecord(value.last_refusal));
|
|
2535
2866
|
}
|
|
2536
2867
|
function refusalRecord(value) {
|
|
2537
|
-
return object(value) && Number.isSafeInteger(value.sequence) && typeof value.submission_ref === "string" && typeof value.code === "string" && typeof value.message === "string" && Number.isSafeInteger(value.refused_at_ms);
|
|
2868
|
+
return object(value) && Number.isSafeInteger(value.sequence) && typeof value.submission_ref === "string" && typeof value.code === "string" && typeof value.message === "string" && Number.isSafeInteger(value.refused_at_ms) && (value.diagnostic === void 0 || validDiagnostic(value.diagnostic));
|
|
2869
|
+
}
|
|
2870
|
+
function validDiagnostic(value) {
|
|
2871
|
+
if (!object(value) || !hasExactKeys2(value, [], RELAY_DELIVERY_DIAGNOSTIC_KEYS)) return false;
|
|
2872
|
+
return isUndefinedOr(value.relayCode, isRelayGatewayErrorCode) && isUndefinedOr(value.gatewayStatus, isSafeInteger2) && isUndefinedOr(value.gatewayCode, isBoundedRefusalWord) && isUndefinedOr(value.gatewayReason, isBoundedRefusalWord) && isUndefinedOr(value.exhausted, isBoolean2);
|
|
2873
|
+
}
|
|
2874
|
+
function isSafeInteger2(value) {
|
|
2875
|
+
return Number.isSafeInteger(value);
|
|
2876
|
+
}
|
|
2877
|
+
function isBoolean2(value) {
|
|
2878
|
+
return typeof value === "boolean";
|
|
2538
2879
|
}
|
|
2539
2880
|
function entry(value) {
|
|
2540
2881
|
return object(value) && producer2(value.producer_id) && typeof value.producer_installation_ref === "string" && Number.isSafeInteger(value.sequence) && typeof value.hash === "string" && Number.isSafeInteger(value.charged_bytes) && object(value.frame) && Number.isSafeInteger(value.retry_attempts) && Number.isSafeInteger(value.retry_at_ms);
|
|
@@ -2545,6 +2886,60 @@ function producer2(value) {
|
|
|
2545
2886
|
function object(value) {
|
|
2546
2887
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2547
2888
|
}
|
|
2889
|
+
function poisonFrameError(frame) {
|
|
2890
|
+
try {
|
|
2891
|
+
assertHostProducerFrame(frame);
|
|
2892
|
+
return null;
|
|
2893
|
+
} catch (error) {
|
|
2894
|
+
if (error instanceof HostProducerFrameError) return error;
|
|
2895
|
+
throw error;
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
function hasIntactAdmissionEnvelope(entryValue, installation2) {
|
|
2899
|
+
if (installation2 === void 0) return false;
|
|
2900
|
+
return poisonIdentityValid(entryValue) && poisonEnvelopeMatches(entryValue, installation2) && poisonIntegrityMatches(entryValue) && poisonSequenceInRange(entryValue, installation2);
|
|
2901
|
+
}
|
|
2902
|
+
function poisonIdentityValid(entryValue) {
|
|
2903
|
+
if (entryValue.producer_id !== "codex" && entryValue.producer_id !== "claude") return false;
|
|
2904
|
+
if (!opaqueHostSourceRef(entryValue.producer_installation_ref)) return false;
|
|
2905
|
+
return Number.isSafeInteger(entryValue.sequence) && entryValue.sequence > 0;
|
|
2906
|
+
}
|
|
2907
|
+
function poisonEnvelopeMatches(entryValue, installation2) {
|
|
2908
|
+
const frame = entryValue.frame;
|
|
2909
|
+
if (!object(frame)) return false;
|
|
2910
|
+
return poisonEnvelopeSchema(frame) && poisonEnvelopeProducer(frame, entryValue) && poisonEnvelopeInstallation(frame, entryValue) && poisonEnvelopeEpoch(frame, installation2) && poisonEnvelopeSequence(frame, entryValue) && poisonEnvelopeKind(frame) && poisonEnvelopeTimestamp(frame);
|
|
2911
|
+
}
|
|
2912
|
+
function poisonEnvelopeSchema(frame) {
|
|
2913
|
+
return frame.schema_version === "ceal.host_producer_frame.v1";
|
|
2914
|
+
}
|
|
2915
|
+
function poisonEnvelopeProducer(frame, entryValue) {
|
|
2916
|
+
return producer2(frame.producer_id) && frame.producer_id === entryValue.producer_id;
|
|
2917
|
+
}
|
|
2918
|
+
function poisonEnvelopeInstallation(frame, entryValue) {
|
|
2919
|
+
return opaqueHostSourceRef(frame.producer_installation_ref) && frame.producer_installation_ref === entryValue.producer_installation_ref;
|
|
2920
|
+
}
|
|
2921
|
+
function poisonEnvelopeEpoch(frame, installation2) {
|
|
2922
|
+
return Number.isSafeInteger(frame.producer_epoch) && typeof frame.producer_epoch === "number" && frame.producer_epoch > 0 && frame.producer_epoch <= installation2.epoch;
|
|
2923
|
+
}
|
|
2924
|
+
function poisonEnvelopeSequence(frame, entryValue) {
|
|
2925
|
+
return Number.isSafeInteger(frame.sequence) && frame.sequence === entryValue.sequence;
|
|
2926
|
+
}
|
|
2927
|
+
function poisonEnvelopeKind(frame) {
|
|
2928
|
+
return isHostProducerFrameKind(frame.frame_kind);
|
|
2929
|
+
}
|
|
2930
|
+
function poisonEnvelopeTimestamp(frame) {
|
|
2931
|
+
return typeof frame.occurred_at === "string" && Number.isFinite(Date.parse(frame.occurred_at));
|
|
2932
|
+
}
|
|
2933
|
+
function poisonIntegrityMatches(entryValue) {
|
|
2934
|
+
const frame = entryValue.frame;
|
|
2935
|
+
if (!object(frame)) return false;
|
|
2936
|
+
const json = JSON.stringify(frame);
|
|
2937
|
+
return entryValue.charged_bytes === 4 + Buffer.byteLength(json, "utf8") && entryValue.hash === hostProducerFrameHash(entryValue.frame);
|
|
2938
|
+
}
|
|
2939
|
+
function poisonSequenceInRange(entryValue, installation2) {
|
|
2940
|
+
if (entryValue.sequence <= installation2.delivered_through_sequence) return false;
|
|
2941
|
+
return entryValue.sequence <= installation2.admitted_through_sequence;
|
|
2942
|
+
}
|
|
2548
2943
|
|
|
2549
2944
|
// host/relay/src/relay-gateway-bridge.ts
|
|
2550
2945
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
@@ -2688,10 +3083,22 @@ function submissionRefusal(error) {
|
|
|
2688
3083
|
const status = isRecord(error) && typeof error.status === "number" ? error.status : void 0;
|
|
2689
3084
|
const code = errorCode7(error) ?? "gateway_unavailable";
|
|
2690
3085
|
const definitive = code === "payload_rejected" || status !== void 0 && status >= 400 && status < 500 && !RETRYABLE_HTTP_STATUSES.has(status);
|
|
2691
|
-
return { code, message: errorMessage(error), definitive };
|
|
3086
|
+
return { code, message: errorMessage(error), definitive, diagnostic: typedDiagnostic(error, status) };
|
|
3087
|
+
}
|
|
3088
|
+
function typedDiagnostic(error, status) {
|
|
3089
|
+
if (!isRecord(error)) return status === void 0 ? {} : { gatewayStatus: status };
|
|
3090
|
+
const gatewayCode = isBoundedRefusalWord(error.error_code) ? error.error_code : void 0;
|
|
3091
|
+
const gatewayReason = isBoundedRefusalWord(error.refusal_reason) ? error.refusal_reason : void 0;
|
|
3092
|
+
return {
|
|
3093
|
+
...typeof error.code === "string" ? { relayCode: error.code } : {},
|
|
3094
|
+
...status === void 0 ? {} : { gatewayStatus: status },
|
|
3095
|
+
...gatewayCode === void 0 ? {} : { gatewayCode },
|
|
3096
|
+
...gatewayReason === void 0 ? {} : { gatewayReason }
|
|
3097
|
+
};
|
|
2692
3098
|
}
|
|
2693
3099
|
function deliveryRefusal(refusal) {
|
|
2694
|
-
|
|
3100
|
+
const diagnostic = refusal.diagnostic === void 0 ? {} : { diagnostic: refusal.diagnostic };
|
|
3101
|
+
return refusal.definitive ? { status: "refused", code: refusal.code, message: refusal.message, ...diagnostic } : { status: "retryable", reason: `${refusal.code}: ${refusal.message}`, ...diagnostic };
|
|
2695
3102
|
}
|
|
2696
3103
|
function errorMessage(error) {
|
|
2697
3104
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -2889,22 +3296,35 @@ var HostRelayGatewayError = class extends Error {
|
|
|
2889
3296
|
status;
|
|
2890
3297
|
/** The Gateway's own refusal word, when it named one; a bounded token, never its message. */
|
|
2891
3298
|
error_code;
|
|
2892
|
-
|
|
3299
|
+
/** The Gateway mint refusal's bounded continuity reason, when it named one; a fixed token, never its message. */
|
|
3300
|
+
refusal_reason;
|
|
3301
|
+
constructor(code, message, status, errorCode9, refusalReason) {
|
|
2893
3302
|
super(message);
|
|
2894
3303
|
this.code = code;
|
|
2895
3304
|
if (status !== void 0) this.status = status;
|
|
2896
3305
|
if (errorCode9 !== void 0) this.error_code = errorCode9;
|
|
3306
|
+
if (refusalReason !== void 0) this.refusal_reason = refusalReason;
|
|
2897
3307
|
}
|
|
2898
3308
|
};
|
|
2899
3309
|
var GATEWAY_REFUSAL_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
2900
|
-
async function
|
|
3310
|
+
async function gatewayRefusal(response2) {
|
|
2901
3311
|
try {
|
|
2902
3312
|
const body = await response2.json();
|
|
2903
|
-
|
|
3313
|
+
if (!isCealJsonRecord(body)) return {};
|
|
3314
|
+
const code = typeof body.error_code === "string" && GATEWAY_REFUSAL_CODE_PATTERN.test(body.error_code) ? body.error_code : void 0;
|
|
3315
|
+
const reason = code === "request_context_refused" && typeof body.reason === "string" && GATEWAY_REFUSAL_CODE_PATTERN.test(body.reason) && isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, body.reason) ? body.reason : void 0;
|
|
3316
|
+
return {
|
|
3317
|
+
...code === void 0 ? {} : { code },
|
|
3318
|
+
...reason === void 0 ? {} : { reason }
|
|
3319
|
+
};
|
|
2904
3320
|
} catch {
|
|
2905
|
-
return
|
|
3321
|
+
return {};
|
|
2906
3322
|
}
|
|
2907
3323
|
}
|
|
3324
|
+
function submitRefusalCode(body) {
|
|
3325
|
+
if (!isCealJsonRecord(body) || typeof body.error_code !== "string" || !GATEWAY_REFUSAL_CODE_PATTERN.test(body.error_code)) return void 0;
|
|
3326
|
+
return body.error_code;
|
|
3327
|
+
}
|
|
2908
3328
|
function refusalMessage(status, body) {
|
|
2909
3329
|
const record6 = isCealJsonRecord(body) ? body : {};
|
|
2910
3330
|
const code = typeof record6.error_code === "string" ? record6.error_code : "unknown";
|
|
@@ -2932,7 +3352,7 @@ function createHostRelayGatewayHttpPort(options) {
|
|
|
2932
3352
|
invalidEventTime: () => new HostRelayGatewayError("payload_rejected", "Host Relay submission has no event time."),
|
|
2933
3353
|
unavailable: () => new HostRelayGatewayError("gateway_unavailable", "Gateway Host ingress is unavailable."),
|
|
2934
3354
|
invalidResponse: () => new HostRelayGatewayError("invalid_response", "Gateway Host ingress response is not JSON."),
|
|
2935
|
-
refused: (status, body) => new HostRelayGatewayError("gateway_refused", refusalMessage(status, body), status),
|
|
3355
|
+
refused: (status, body) => new HostRelayGatewayError("gateway_refused", refusalMessage(status, body), status, submitRefusalCode(body)),
|
|
2936
3356
|
acknowledgement
|
|
2937
3357
|
});
|
|
2938
3358
|
},
|
|
@@ -2950,7 +3370,10 @@ function createHostRelayGatewayHttpPort(options) {
|
|
|
2950
3370
|
} catch {
|
|
2951
3371
|
throw new HostRelayGatewayError("gateway_unavailable", "Gateway Host invocation context is unavailable.");
|
|
2952
3372
|
}
|
|
2953
|
-
if (!response2.ok)
|
|
3373
|
+
if (!response2.ok) {
|
|
3374
|
+
const refusal = await gatewayRefusal(response2);
|
|
3375
|
+
throw new HostRelayGatewayError(response2.status === 401 ? "credential" : "gateway_refused", `Gateway Host invocation context was refused (${response2.status}).`, response2.status, refusal.code, refusal.reason);
|
|
3376
|
+
}
|
|
2954
3377
|
let value;
|
|
2955
3378
|
try {
|
|
2956
3379
|
value = await response2.json();
|
|
@@ -3071,8 +3494,9 @@ function acknowledgement(value) {
|
|
|
3071
3494
|
}
|
|
3072
3495
|
|
|
3073
3496
|
// host/relay/src/relay-ipc.ts
|
|
3074
|
-
import { chmod, lstat as lstat3, unlink as unlink2 } from "node:fs/promises";
|
|
3497
|
+
import { chmod, lstat as lstat3, rename as rename2, unlink as unlink2 } from "node:fs/promises";
|
|
3075
3498
|
import { createConnection, createServer } from "node:net";
|
|
3499
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
3076
3500
|
|
|
3077
3501
|
// host/relay/src/current-prompt-custody.ts
|
|
3078
3502
|
import { createHash as createHash5 } from "node:crypto";
|
|
@@ -3150,10 +3574,31 @@ var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
|
3150
3574
|
"gateway_unavailable",
|
|
3151
3575
|
"gateway_refused",
|
|
3152
3576
|
"gateway_response_invalid",
|
|
3577
|
+
"gateway_rate_limited",
|
|
3578
|
+
"producer_frame_invalid",
|
|
3153
3579
|
"deadline",
|
|
3154
3580
|
"cancelled",
|
|
3155
3581
|
"mint_failed",
|
|
3156
|
-
"relay_failed"
|
|
3582
|
+
"relay_failed",
|
|
3583
|
+
"request_authority_unavailable",
|
|
3584
|
+
"request_missing",
|
|
3585
|
+
"authority_mismatch",
|
|
3586
|
+
"host_source_mismatch",
|
|
3587
|
+
"host_session_mismatch",
|
|
3588
|
+
"host_turn_mismatch",
|
|
3589
|
+
"host_input_mismatch",
|
|
3590
|
+
"host_prompt_mismatch"
|
|
3591
|
+
]);
|
|
3592
|
+
var HOST_RELAY_DIRECT_WIRE_MINT_REASONS = vocabulary([
|
|
3593
|
+
"context_stale",
|
|
3594
|
+
"context_expired",
|
|
3595
|
+
"host_source_refused",
|
|
3596
|
+
"host_credential_refused",
|
|
3597
|
+
"gateway_unavailable",
|
|
3598
|
+
"gateway_rate_limited",
|
|
3599
|
+
"gateway_refused",
|
|
3600
|
+
"gateway_response_invalid",
|
|
3601
|
+
"producer_frame_invalid"
|
|
3157
3602
|
]);
|
|
3158
3603
|
var HostRelayInvocationMintRefusal = class extends Error {
|
|
3159
3604
|
reason;
|
|
@@ -3185,15 +3630,42 @@ var HostRelayIpcError = class extends Error {
|
|
|
3185
3630
|
}
|
|
3186
3631
|
};
|
|
3187
3632
|
async function startHostRelayIpcServer(options) {
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3633
|
+
return withRelayEndpointLock(options.endpoint, async () => {
|
|
3634
|
+
if (process.platform === "win32") {
|
|
3635
|
+
await assertEndpointVacant(options.endpoint);
|
|
3636
|
+
} else {
|
|
3637
|
+
await assertTrustedEndpointParent(options.endpoint);
|
|
3638
|
+
await removeStaleRelayEndpoint(options.endpoint, options.testHooks);
|
|
3639
|
+
}
|
|
3640
|
+
const active = /* @__PURE__ */ new Map();
|
|
3641
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
3642
|
+
const quarantine = options.quarantine ?? options.admission.quarantine;
|
|
3643
|
+
const currentPrompts = options.currentPrompts ?? new HostCurrentPromptCustody();
|
|
3644
|
+
const server = createServer((socket) => serveSocket(socket, options, currentPrompts, quarantine, active, sockets));
|
|
3645
|
+
if (process.platform === "win32") {
|
|
3646
|
+
try {
|
|
3647
|
+
await listen(server, options.endpoint);
|
|
3648
|
+
} catch (error) {
|
|
3649
|
+
await abandonRelayServer(server, sockets);
|
|
3650
|
+
throw error;
|
|
3651
|
+
}
|
|
3652
|
+
return { endpoint: options.endpoint, close: () => closeServer(server, sockets, options.endpoint) };
|
|
3653
|
+
}
|
|
3654
|
+
const staging = stagingRelayEndpoint(options.endpoint);
|
|
3655
|
+
await listenStagingRelayEndpoint(server, sockets, staging);
|
|
3656
|
+
let owned;
|
|
3657
|
+
try {
|
|
3658
|
+
await options.testHooks?.beforeCaptureOwnedEndpoint?.(options.endpoint);
|
|
3659
|
+
owned = await readStagingRelayEndpointIdentity(staging);
|
|
3660
|
+
await chmod(staging, 384);
|
|
3661
|
+
const identity = await publishStagingRelayEndpoint(staging, owned, options.endpoint);
|
|
3662
|
+
return { endpoint: options.endpoint, close: () => closeRelayServer(server, sockets, options.endpoint, identity, options.testHooks) };
|
|
3663
|
+
} catch (error) {
|
|
3664
|
+
if (owned !== void 0) await releaseOwnedRelayEndpoint(server, sockets, options.endpoint, owned);
|
|
3665
|
+
else await abandonRelayServer(server, sockets);
|
|
3666
|
+
throw error;
|
|
3667
|
+
}
|
|
3668
|
+
});
|
|
3197
3669
|
}
|
|
3198
3670
|
function encodeHostRelayIpcResponse(response2) {
|
|
3199
3671
|
const body = Buffer.from(JSON.stringify(response2), "utf8");
|
|
@@ -3355,17 +3827,68 @@ function writeCurrentPromptResponse(socket, status) {
|
|
|
3355
3827
|
async function resolveInvocationContext(request2, admission, bindings, currentPrompts, mint, signal) {
|
|
3356
3828
|
if (mint === void 0) throw new HostRelayIpcError("relay_unavailable");
|
|
3357
3829
|
const query = bindingQueryFromRequest(request2);
|
|
3830
|
+
const expectedSequences = pendingAdmissionSequences(await admission.snapshot(), query);
|
|
3358
3831
|
const binding = await waitForRequestBinding(query, admission, bindings, signal);
|
|
3359
|
-
if (binding.status !== "ready") return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "no_binding" };
|
|
3832
|
+
if (binding.status !== "ready") return await unboundInvocationContextRefusal(admission, await admission.snapshot(), query, expectedSequences) ?? { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "no_binding" };
|
|
3360
3833
|
const currentPrompt = currentPrompts.take({ producer_id: request2.producer_id, producer_installation_ref: request2.producer_installation_ref, local_session_ref: request2.local_session_ref, local_turn_ref: request2.local_turn_ref, prompt_ref: binding.binding.prompt_ref });
|
|
3361
3834
|
let result2;
|
|
3362
3835
|
try {
|
|
3363
|
-
result2 = await mint({ client_nonce: request2.client_nonce, context_expires_at: binding.binding.context_expires_at ?? binding.binding.bound_at, request_work_context: relayRequestWorkContextFromBinding(binding.binding), ...currentPrompt === void 0 ? {} : { current_prompt: currentPrompt } }, signal);
|
|
3836
|
+
result2 = await mint({ client_nonce: request2.client_nonce, context_expires_at: binding.binding.context_expires_at ?? binding.binding.bound_at, request_work_context: relayRequestWorkContextFromBinding(binding.binding), ...currentPrompt === void 0 ? {} : { current_prompt: currentPrompt }, ...request2.visible_output_max_bytes === void 0 ? {} : { visible_output_max_bytes: request2.visible_output_max_bytes } }, signal);
|
|
3364
3837
|
} catch (error) {
|
|
3365
3838
|
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: error instanceof HostRelayInvocationMintRefusal ? error.reason : "mint_failed" };
|
|
3366
3839
|
}
|
|
3367
3840
|
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "ready", result: result2 };
|
|
3368
3841
|
}
|
|
3842
|
+
async function unboundInvocationContextRefusal(admission, snapshot, query, expectedSequences) {
|
|
3843
|
+
if (expectedSequences.length > 0) {
|
|
3844
|
+
const refusal = snapshot.installations[`${query.producer_id}\0${query.producer_installation_ref}`]?.last_refusal;
|
|
3845
|
+
if (refusal !== void 0 && expectedSequences.includes(refusal.sequence)) {
|
|
3846
|
+
const reason2 = mintReasonForDeliveryRefusal(refusal.code, refusal.diagnostic);
|
|
3847
|
+
if (reason2 !== void 0) return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: reason2 };
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
const selector = Object.freeze({
|
|
3851
|
+
producer_id: query.producer_id,
|
|
3852
|
+
producer_installation_ref: query.producer_installation_ref,
|
|
3853
|
+
local_session_ref: query.local_session_ref,
|
|
3854
|
+
local_turn_ref: query.local_turn_ref,
|
|
3855
|
+
local_turn_aliases: Object.freeze([])
|
|
3856
|
+
});
|
|
3857
|
+
const match = await admission.quarantine.findNewestAttributedMatch(selector);
|
|
3858
|
+
if (match === null) return null;
|
|
3859
|
+
const reason = mintReasonForQuarantinedMatch(match);
|
|
3860
|
+
if (reason === void 0) return null;
|
|
3861
|
+
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason };
|
|
3862
|
+
}
|
|
3863
|
+
function mintReasonForDeliveryRefusal(code, diagnostic) {
|
|
3864
|
+
if (code !== "gateway_refused") return void 0;
|
|
3865
|
+
if (diagnostic === void 0) return "gateway_refused";
|
|
3866
|
+
return mintReasonForWireCause(projectCealHostAuthenticationCause(diagnostic));
|
|
3867
|
+
}
|
|
3868
|
+
function mintReasonForQuarantinedMatch(match) {
|
|
3869
|
+
if (match === null) return void 0;
|
|
3870
|
+
if (match.reason !== "gateway_refused") return "producer_frame_invalid";
|
|
3871
|
+
const diagnostic = match.attribution?.diagnostic;
|
|
3872
|
+
if (diagnostic === void 0) return void 0;
|
|
3873
|
+
return mintReasonForWireCause(projectCealHostAuthenticationCause(diagnostic));
|
|
3874
|
+
}
|
|
3875
|
+
function mintReasonForWireCause(cause) {
|
|
3876
|
+
if (isDirectWireCause(cause)) return cause;
|
|
3877
|
+
if (cause === "relay_failed" || cause === "relay_unavailable" || cause === "options_invalid") return "relay_failed";
|
|
3878
|
+
return "mint_failed";
|
|
3879
|
+
}
|
|
3880
|
+
function isDirectWireCause(cause) {
|
|
3881
|
+
return isVocabularyWord(HOST_RELAY_DIRECT_WIRE_MINT_REASONS, cause);
|
|
3882
|
+
}
|
|
3883
|
+
function pendingAdmissionSequences(snapshot, query) {
|
|
3884
|
+
const sequences = [];
|
|
3885
|
+
for (const entry2 of snapshot.entries) {
|
|
3886
|
+
const selector = extractHostBindingSelector(entry2.frame);
|
|
3887
|
+
if (selector === null || !relayRequestBindingMatchesQuery(query, selector)) continue;
|
|
3888
|
+
sequences.push(entry2.sequence);
|
|
3889
|
+
}
|
|
3890
|
+
return sequences;
|
|
3891
|
+
}
|
|
3369
3892
|
async function waitForRequestBinding(query, admission, bindings, signal) {
|
|
3370
3893
|
let binding = await bindings.query(query);
|
|
3371
3894
|
if (binding.status === "ready") return binding;
|
|
@@ -3483,11 +4006,14 @@ function bindingQueryFromRequest(value) {
|
|
|
3483
4006
|
return { producer_id: value.producer_id, producer_installation_ref: value.producer_installation_ref, local_session_ref: value.local_session_ref, local_turn_ref: value.local_turn_ref };
|
|
3484
4007
|
}
|
|
3485
4008
|
function validInvocationContextQueryRequest(value) {
|
|
3486
|
-
return validQueryIdentity(value, HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) && exactKeys4(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "client_nonce"]) && typeof value.client_nonce === "string" && CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce);
|
|
4009
|
+
return validQueryIdentity(value, HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) && exactKeys4(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "client_nonce"], ["visible_output_max_bytes"]) && typeof value.client_nonce === "string" && CEAL_HOST_CLIENT_NONCE_PATTERN.test(value.client_nonce) && (value.visible_output_max_bytes === void 0 || validVisibleOutputBudget(value.visible_output_max_bytes));
|
|
3487
4010
|
}
|
|
3488
4011
|
function validQueryIdentity(value, schema) {
|
|
3489
4012
|
return record4(value) && value.schema_version === schema && (value.producer_id === "codex" || value.producer_id === "claude") && safeRef3(value.producer_installation_ref) && safeRef3(value.local_session_ref) && safeRef3(value.local_turn_ref);
|
|
3490
4013
|
}
|
|
4014
|
+
function validVisibleOutputBudget(value) {
|
|
4015
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 && value <= CEAL_CANONICAL_HTTP_MAX_RESPONSE_BYTES;
|
|
4016
|
+
}
|
|
3491
4017
|
function decodeLengthPrefixedJson(input, maximum) {
|
|
3492
4018
|
if (input.length < HOST_RELAY_IPC_LENGTH_BYTES) return null;
|
|
3493
4019
|
const length = input.readUInt32BE(0);
|
|
@@ -3510,8 +4036,9 @@ function encodeLengthPrefixed(value) {
|
|
|
3510
4036
|
function currentPromptWire(value) {
|
|
3511
4037
|
return value.subarray(HOST_RELAY_IPC_LENGTH_BYTES, Math.min(value.length, HOST_RELAY_IPC_LENGTH_BYTES + 128)).includes(Buffer.from(`"schema_version":"${HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA}"`, "utf8"));
|
|
3512
4038
|
}
|
|
3513
|
-
function exactKeys4(value,
|
|
3514
|
-
|
|
4039
|
+
function exactKeys4(value, required, optional = []) {
|
|
4040
|
+
const keys = Object.keys(value);
|
|
4041
|
+
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
3515
4042
|
}
|
|
3516
4043
|
function safeRef3(value) {
|
|
3517
4044
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value);
|
|
@@ -3541,6 +4068,160 @@ async function closeServer(server, sockets, endpoint) {
|
|
|
3541
4068
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
3542
4069
|
if (process.platform !== "win32") await unlink2(endpoint).catch(() => void 0);
|
|
3543
4070
|
}
|
|
4071
|
+
var relayEndpointLocks = /* @__PURE__ */ new Map();
|
|
4072
|
+
function withRelayEndpointLock(endpoint, run) {
|
|
4073
|
+
const prior = relayEndpointLocks.get(endpoint) ?? Promise.resolve();
|
|
4074
|
+
let release;
|
|
4075
|
+
const gate = new Promise((resolve) => {
|
|
4076
|
+
release = resolve;
|
|
4077
|
+
});
|
|
4078
|
+
const tail = prior.then(() => gate);
|
|
4079
|
+
relayEndpointLocks.set(endpoint, tail);
|
|
4080
|
+
return prior.then(run).finally(() => {
|
|
4081
|
+
release?.();
|
|
4082
|
+
if (relayEndpointLocks.get(endpoint) === tail) relayEndpointLocks.delete(endpoint);
|
|
4083
|
+
});
|
|
4084
|
+
}
|
|
4085
|
+
function currentUid(fallback) {
|
|
4086
|
+
return typeof process.geteuid === "function" ? process.geteuid() : fallback;
|
|
4087
|
+
}
|
|
4088
|
+
async function assertTrustedEndpointParent(endpoint) {
|
|
4089
|
+
const parent = dirname3(endpoint);
|
|
4090
|
+
let stats;
|
|
4091
|
+
try {
|
|
4092
|
+
stats = await lstat3(parent);
|
|
4093
|
+
} catch {
|
|
4094
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint parent is unavailable; leaving it untouched.");
|
|
4095
|
+
}
|
|
4096
|
+
if (!stats.isDirectory() || stats.uid !== currentUid(stats.uid) || (stats.mode & 63) !== 0) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint parent is not an owner-private directory; leaving it untouched.");
|
|
4097
|
+
}
|
|
4098
|
+
var HOST_RELAY_ENDPOINT_PROBE_TIMEOUT_MS = 1e3;
|
|
4099
|
+
function probeRelayEndpoint(endpoint) {
|
|
4100
|
+
return new Promise((resolve) => {
|
|
4101
|
+
let settled = false;
|
|
4102
|
+
const socket = createConnection(endpoint);
|
|
4103
|
+
const finish = (outcome) => {
|
|
4104
|
+
if (settled) return;
|
|
4105
|
+
settled = true;
|
|
4106
|
+
clearTimeout(timer);
|
|
4107
|
+
socket.destroy();
|
|
4108
|
+
resolve(outcome);
|
|
4109
|
+
};
|
|
4110
|
+
const timer = setTimeout(() => finish("ambiguous"), HOST_RELAY_ENDPOINT_PROBE_TIMEOUT_MS);
|
|
4111
|
+
timer.unref?.();
|
|
4112
|
+
socket.once("connect", () => finish("live"));
|
|
4113
|
+
socket.once("error", (error) => {
|
|
4114
|
+
const code = errorCode8(error);
|
|
4115
|
+
if (code === "ECONNREFUSED") finish("stale");
|
|
4116
|
+
else if (code === "ENOENT") finish("missing");
|
|
4117
|
+
else finish("ambiguous");
|
|
4118
|
+
});
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
4121
|
+
async function removeStaleRelayEndpoint(endpoint, testHooks) {
|
|
4122
|
+
const observed = await readEndpointStats(endpoint);
|
|
4123
|
+
if (observed === null) return;
|
|
4124
|
+
if (!observed.isSocket()) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is already present and is not a socket; leaving it untouched.");
|
|
4125
|
+
if (observed.uid !== currentUid(observed.uid)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is not owned by the current uid; leaving it untouched.");
|
|
4126
|
+
const captured = { uid: observed.uid, dev: observed.dev, ino: observed.ino };
|
|
4127
|
+
await requireStaleEndpoint(endpoint);
|
|
4128
|
+
await testHooks?.beforeRemoveStaleEndpoint?.(endpoint);
|
|
4129
|
+
const current = await readEndpointStats(endpoint);
|
|
4130
|
+
if (current === null) return;
|
|
4131
|
+
requireSameEndpointIdentity(current, captured, "Host Relay IPC endpoint changed before cleanup; leaving it untouched.");
|
|
4132
|
+
await unlinkRelayEndpoint(endpoint);
|
|
4133
|
+
}
|
|
4134
|
+
async function requireStaleEndpoint(endpoint) {
|
|
4135
|
+
switch (await probeRelayEndpoint(endpoint)) {
|
|
4136
|
+
case "missing":
|
|
4137
|
+
case "stale":
|
|
4138
|
+
return;
|
|
4139
|
+
case "live":
|
|
4140
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is already served by a live Relay; leaving it untouched.");
|
|
4141
|
+
case "ambiguous":
|
|
4142
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint liveness is ambiguous; leaving it untouched.");
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
async function readEndpointStats(endpoint) {
|
|
4146
|
+
try {
|
|
4147
|
+
return await lstat3(endpoint);
|
|
4148
|
+
} catch (error) {
|
|
4149
|
+
if (errorCode8(error) === "ENOENT") return null;
|
|
4150
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint identity is ambiguous; leaving it untouched.");
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
function requireSameEndpointIdentity(current, owned, message) {
|
|
4154
|
+
if (!current.isSocket() || current.uid !== owned.uid || current.dev !== owned.dev || current.ino !== owned.ino) throw new HostRelayIpcError("relay_unavailable", message);
|
|
4155
|
+
}
|
|
4156
|
+
async function unlinkRelayEndpoint(endpoint) {
|
|
4157
|
+
try {
|
|
4158
|
+
await unlink2(endpoint);
|
|
4159
|
+
} catch (error) {
|
|
4160
|
+
if (errorCode8(error) !== "ENOENT") throw error;
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
var relayStagingSequence = 0;
|
|
4164
|
+
function stagingRelayEndpoint(canonical) {
|
|
4165
|
+
const parent = dirname3(canonical);
|
|
4166
|
+
const pid = process.pid.toString(36);
|
|
4167
|
+
const sequence = (relayStagingSequence++ % (36 * 36)).toString(36);
|
|
4168
|
+
return join6(parent, `.r${pid}${sequence}`);
|
|
4169
|
+
}
|
|
4170
|
+
async function readStagingRelayEndpointIdentity(staging) {
|
|
4171
|
+
let created;
|
|
4172
|
+
try {
|
|
4173
|
+
created = await lstat3(staging);
|
|
4174
|
+
} catch {
|
|
4175
|
+
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is unavailable after listen; leaving it untouched.");
|
|
4176
|
+
}
|
|
4177
|
+
if (!created.isSocket()) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint was replaced before this server listened; leaving it untouched.");
|
|
4178
|
+
if (created.uid !== currentUid(created.uid)) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is not owned by the current uid after listen; leaving it untouched.");
|
|
4179
|
+
return { uid: created.uid, dev: created.dev, ino: created.ino };
|
|
4180
|
+
}
|
|
4181
|
+
async function listenStagingRelayEndpoint(server, sockets, staging) {
|
|
4182
|
+
try {
|
|
4183
|
+
await listen(server, staging);
|
|
4184
|
+
} catch (error) {
|
|
4185
|
+
await abandonRelayServer(server, sockets);
|
|
4186
|
+
throw error;
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
async function publishStagingRelayEndpoint(staging, owned, canonical) {
|
|
4190
|
+
const incumbent = await readEndpointStats(canonical);
|
|
4191
|
+
if (incumbent !== null) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint was replaced before this server listened; leaving it untouched.");
|
|
4192
|
+
await rename2(staging, canonical);
|
|
4193
|
+
const published = await readEndpointStats(canonical);
|
|
4194
|
+
if (published === null) throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is unavailable after listen; leaving it untouched.");
|
|
4195
|
+
requireSameEndpointIdentity(published, owned, "Host Relay IPC endpoint was replaced before this server listened; leaving it untouched.");
|
|
4196
|
+
return { uid: published.uid, dev: published.dev, ino: published.ino };
|
|
4197
|
+
}
|
|
4198
|
+
async function releaseOwnedRelayEndpoint(server, sockets, endpoint, owned) {
|
|
4199
|
+
await abandonRelayServer(server, sockets);
|
|
4200
|
+
try {
|
|
4201
|
+
const current = await readEndpointStats(endpoint);
|
|
4202
|
+
if (current === null) return;
|
|
4203
|
+
requireSameEndpointIdentity(current, owned, "Host Relay IPC endpoint changed while releasing a failed startup; leaving it untouched.");
|
|
4204
|
+
await unlinkRelayEndpoint(endpoint);
|
|
4205
|
+
} catch {
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
async function closeRelayServer(server, sockets, endpoint, owned, testHooks) {
|
|
4209
|
+
await withRelayEndpointLock(endpoint, async () => {
|
|
4210
|
+
for (const socket of sockets) socket.destroy();
|
|
4211
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
4212
|
+
await testHooks?.beforeRemoveOwnedEndpoint?.(endpoint);
|
|
4213
|
+
const current = await readEndpointStats(endpoint);
|
|
4214
|
+
if (current === null) return;
|
|
4215
|
+
requireSameEndpointIdentity(current, owned, "Host Relay IPC endpoint was replaced after this server listened; leaving it untouched.");
|
|
4216
|
+
await unlinkRelayEndpoint(endpoint);
|
|
4217
|
+
});
|
|
4218
|
+
}
|
|
4219
|
+
async function abandonRelayServer(server, sockets) {
|
|
4220
|
+
for (const socket of sockets) socket.destroy();
|
|
4221
|
+
await new Promise((resolve) => {
|
|
4222
|
+
server.close(() => resolve());
|
|
4223
|
+
});
|
|
4224
|
+
}
|
|
3544
4225
|
function errorCode8(error) {
|
|
3545
4226
|
return record4(error) && typeof error.code === "string" ? error.code : null;
|
|
3546
4227
|
}
|
|
@@ -3562,7 +4243,7 @@ async function readHostRelayConfig(path) {
|
|
|
3562
4243
|
if (!isAbsoluteNormalizedNonRootPath(path, process.platform, true)) throw new HostRelayConfigError("invalid_config_path", `invalid_config_path: Host Relay config path is invalid: ${refusalFact("config_path", path, "absolute, normalized, and non-root")}.`);
|
|
3563
4244
|
let handle;
|
|
3564
4245
|
try {
|
|
3565
|
-
await assertOwnerPrivateDirectory2(
|
|
4246
|
+
await assertOwnerPrivateDirectory2(dirname4(path));
|
|
3566
4247
|
handle = await open5(path, O_RDONLY4 | O_NOFOLLOW3);
|
|
3567
4248
|
const stats = await handle.stat();
|
|
3568
4249
|
if (!stats.isFile() || !ownerPrivate4(stats)) throw new HostRelayConfigError("config_not_private", "Host Relay config file is not owner-private.");
|
|
@@ -3587,7 +4268,7 @@ async function readHostRelayConfig(path) {
|
|
|
3587
4268
|
}
|
|
3588
4269
|
function validateHostRelaySourcePaths(config) {
|
|
3589
4270
|
try {
|
|
3590
|
-
assertHostRelaySourcePaths(
|
|
4271
|
+
assertHostRelaySourcePaths(dirname4(config.registration_state_file), config);
|
|
3591
4272
|
} catch (error) {
|
|
3592
4273
|
throw new HostRelayConfigError("invalid_config", error instanceof Error ? error.message : "Host Relay source tuple is invalid.");
|
|
3593
4274
|
}
|
|
@@ -3617,10 +4298,10 @@ function validConfigRuntime(value) {
|
|
|
3617
4298
|
var HOST_RELAY_INVOCATION_MINT_LOG_TAG = "[host-relay-invocation-mint]";
|
|
3618
4299
|
var HOST_RELAY_INVOCATION_MINT_LINE = new RegExp(`^${HOST_RELAY_INVOCATION_MINT_LOG_TAG.replace(/[[\]]/gu, "\\$&")} outcome=([a-z_]+)(?: reason=([a-z][a-z0-9_]{0,63}))? elapsed_ms=[0-9]+$`, "u");
|
|
3619
4300
|
async function serveHostRelay(config, signal, dependencies = {}) {
|
|
3620
|
-
const admission = new RelayAdmissionStore(
|
|
3621
|
-
const bindings = new RelayRequestBindingStore(
|
|
4301
|
+
const admission = new RelayAdmissionStore(join7(config.state_root, "admission"));
|
|
4302
|
+
const bindings = new RelayRequestBindingStore(join7(config.state_root, "bindings"));
|
|
3622
4303
|
const gateway = (dependencies.createGateway ?? createHostRelayGatewayHttpPort)({ registration_state_file: config.registration_state_file, host_source_ref: config.host_source_ref });
|
|
3623
|
-
const bridge = new RelayGatewayBridge({ state_root:
|
|
4304
|
+
const bridge = new RelayGatewayBridge({ state_root: join7(config.state_root, "gateway"), host_source_ref: config.host_source_ref, gateway, timeout_ms: config.gateway_timeout_ms, on_request_bound: async (binding) => {
|
|
3624
4305
|
await bindings.bind(binding);
|
|
3625
4306
|
} });
|
|
3626
4307
|
let server;
|
|
@@ -3661,7 +4342,11 @@ function invocationMintRefusalReason(error, cancellation) {
|
|
|
3661
4342
|
if (error.code === "gateway_unavailable") return "gateway_unavailable";
|
|
3662
4343
|
if (error.code === "invalid_response") return "gateway_response_invalid";
|
|
3663
4344
|
if (error.code !== "gateway_refused") return "mint_failed";
|
|
3664
|
-
return (error
|
|
4345
|
+
return gatewayMintRefusalReason(error);
|
|
4346
|
+
}
|
|
4347
|
+
function gatewayMintRefusalReason(error) {
|
|
4348
|
+
if (error.error_code === "request_context_refused" && isVocabularyWord(CEAL_HOST_MINT_REQUEST_CONTEXT_REFUSAL_REASONS, error.refusal_reason)) return error.refusal_reason;
|
|
4349
|
+
return GATEWAY_MINT_REFUSAL_REASONS.get(error.error_code ?? "") ?? "gateway_refused";
|
|
3665
4350
|
}
|
|
3666
4351
|
var GATEWAY_MINT_REFUSAL_REASONS = /* @__PURE__ */ new Map([
|
|
3667
4352
|
["request_context_refused", "context_stale"],
|