@corca-org/ceal-host-linux-arm64 1.0.9 → 1.0.11
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/README.md +2 -2
- package/bin/ceal-host +0 -0
- package/libexec/ceal-host/claude-hook.mjs +564 -205
- package/libexec/ceal-host/claude-invocation-authentication.mjs +588 -201
- package/libexec/ceal-host/claude-producer.mjs +620 -199
- package/libexec/ceal-host/codex-hook.mjs +1071 -576
- package/libexec/ceal-host/codex-invocation-authentication.mjs +770 -376
- package/libexec/ceal-host/codex-producer.mjs +805 -377
- package/libexec/ceal-host/control.mjs +1587 -777
- package/libexec/ceal-host/relay.mjs +1162 -564
- package/package.json +1 -1
- package/share/ceal-host/bundle-manifest.json +12 -12
- package/share/ceal-host/bundle-manifest.sigstore.json +1 -1
|
@@ -61,6 +61,46 @@ var client_wire_contract_default = {
|
|
|
61
61
|
schema_version: "ceal.command_request.v1",
|
|
62
62
|
absolute_transport_max_bytes: 65536
|
|
63
63
|
},
|
|
64
|
+
command_resolution: {
|
|
65
|
+
schema_version: "ceal.gateway_command_resolution.v1",
|
|
66
|
+
authority: {
|
|
67
|
+
anonymous: {
|
|
68
|
+
state: "anonymous",
|
|
69
|
+
fields: [
|
|
70
|
+
"state"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
authenticated: {
|
|
74
|
+
state: "authenticated",
|
|
75
|
+
fields: [
|
|
76
|
+
"state",
|
|
77
|
+
"principal_kind",
|
|
78
|
+
"organization_ref",
|
|
79
|
+
"instance_ref",
|
|
80
|
+
"access_group_ref",
|
|
81
|
+
"principal_ref",
|
|
82
|
+
"authority_revision_ref"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
semantic_options: {
|
|
87
|
+
inspection: {
|
|
88
|
+
values: [
|
|
89
|
+
"default",
|
|
90
|
+
"full"
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
cache_mode: {
|
|
94
|
+
values: [
|
|
95
|
+
"default",
|
|
96
|
+
"bypass"
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
compact: {
|
|
100
|
+
type: "boolean"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
64
104
|
command_response: {
|
|
65
105
|
schema_version: "ceal.command_response.v1",
|
|
66
106
|
descriptor_field: "verified_artifact",
|
|
@@ -74,12 +114,15 @@ var client_wire_contract_default = {
|
|
|
74
114
|
],
|
|
75
115
|
bare_document_fields: [
|
|
76
116
|
"invocation",
|
|
77
|
-
"required_action_sequence",
|
|
78
117
|
"entrypoints",
|
|
79
118
|
"common_concepts",
|
|
80
119
|
"operations",
|
|
81
120
|
"catalog_revision"
|
|
82
121
|
],
|
|
122
|
+
bare_document_optional_fields: [
|
|
123
|
+
"required_action_sequence",
|
|
124
|
+
"withheld"
|
|
125
|
+
],
|
|
83
126
|
prompt_field: "prompt",
|
|
84
127
|
prompt_schema_version: "ceal.prompt.v1",
|
|
85
128
|
focused_prompt_document_fields: [
|
|
@@ -92,7 +135,7 @@ var client_wire_contract_default = {
|
|
|
92
135
|
"prompt"
|
|
93
136
|
],
|
|
94
137
|
discovery_command: "discovery",
|
|
95
|
-
discovery_manifest_schema_version: "ceal.discovery_manifest.
|
|
138
|
+
discovery_manifest_schema_version: "ceal.discovery_manifest.v2",
|
|
96
139
|
discovery_manifest_document_fields: [
|
|
97
140
|
"discovery_manifest"
|
|
98
141
|
],
|
|
@@ -100,11 +143,29 @@ var client_wire_contract_default = {
|
|
|
100
143
|
bare_document_max_bytes: 65536,
|
|
101
144
|
complete_max_bytes: 66019,
|
|
102
145
|
absolute_transport_max_bytes: 4194304
|
|
146
|
+
},
|
|
147
|
+
resource_stream: {
|
|
148
|
+
path: "/api/ceal/v2/resources",
|
|
149
|
+
request_schema_version: "ceal.resource_stream_request.v1",
|
|
150
|
+
ref_pattern: "^resource:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
151
|
+
media_type_pattern: "^[a-z0-9][a-z0-9!#$&^_.+-]{0,63}/[a-z0-9][a-z0-9!#$&^_.+-]{0,63}$",
|
|
152
|
+
filename_max_bytes: 512,
|
|
153
|
+
max_bytes: 9007199254740991,
|
|
154
|
+
deadline_ms: 6e5,
|
|
155
|
+
fetch_argv: [
|
|
156
|
+
"resource",
|
|
157
|
+
"fetch"
|
|
158
|
+
],
|
|
159
|
+
ref_flag: "--ref"
|
|
103
160
|
}
|
|
104
161
|
};
|
|
105
162
|
|
|
106
163
|
// packages/ceal-client-protocol/src/client-wire-contract.ts
|
|
107
164
|
var CEAL_CLIENT_WIRE_CONTRACT = Object.freeze(client_wire_contract_default);
|
|
165
|
+
var commandResolution = CEAL_CLIENT_WIRE_CONTRACT.command_resolution;
|
|
166
|
+
var commandResolutionAuthority = commandResolution.authority;
|
|
167
|
+
var commandResolutionSemanticOptions = commandResolution.semantic_options;
|
|
168
|
+
var CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION = commandResolution.schema_version;
|
|
108
169
|
var artifact = CEAL_CLIENT_WIRE_CONTRACT.artifact_descriptor;
|
|
109
170
|
var request = CEAL_CLIENT_WIRE_CONTRACT.command_request;
|
|
110
171
|
var response = CEAL_CLIENT_WIRE_CONTRACT.command_response;
|
|
@@ -125,6 +186,7 @@ var CEAL_COMMAND_REQUEST_MAX_BYTES = request.absolute_transport_max_bytes;
|
|
|
125
186
|
var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_MAX_BYTES = response.bare_document_max_bytes;
|
|
126
187
|
var CEAL_COMMAND_RESPONSE_PROMPT_FIELD = response.prompt_field;
|
|
127
188
|
var CEAL_COMMAND_RESPONSE_PROMPT_SCHEMA_VERSION = response.prompt_schema_version;
|
|
189
|
+
var CEAL_COMMAND_RESPONSE_BARE_DOCUMENT_OPTIONAL_FIELDS = Object.freeze([...response.bare_document_optional_fields]);
|
|
128
190
|
var CEAL_COMMAND_RESPONSE_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.bare_document_fields, response.prompt_field]);
|
|
129
191
|
var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_document_fields]);
|
|
130
192
|
var CEAL_COMMAND_RESPONSE_FOCUSED_PROMPT_CONTINUATION_DOCUMENT_FIELDS = Object.freeze([...response.focused_prompt_continuation_document_fields]);
|
|
@@ -138,9 +200,50 @@ function vocabulary(words) {
|
|
|
138
200
|
return Object.freeze(words);
|
|
139
201
|
}
|
|
140
202
|
|
|
203
|
+
// packages/ceal-client-protocol/src/gateway-response-types.ts
|
|
204
|
+
var CEAL_OPERATION_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
|
|
205
|
+
var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
|
|
206
|
+
var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
|
|
207
|
+
var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
|
|
208
|
+
var CEAL_GATEWAY_READ_REUSE_DISPOSITIONS = vocabulary(["response_cache_hit", "corpus_hit", "corpus_hit_after_revalidation", "corpus_partial_provider_supplement", "provider_direct", "provider_continuation", "reuse_unavailable"]);
|
|
209
|
+
var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
|
|
210
|
+
var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
|
|
211
|
+
var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
|
|
212
|
+
var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
|
|
213
|
+
var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "agent_lease_admission", "provider_authenticated_event"]);
|
|
214
|
+
var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
|
|
215
|
+
var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
|
|
216
|
+
var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
|
|
217
|
+
var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
|
|
218
|
+
var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
|
|
219
|
+
var CEAL_WRITE_RECEIPT_PROVIDER_STATES = vocabulary(["outcome_unknown", "verified", "not_dispatched"]);
|
|
220
|
+
var CEAL_WRITE_RECEIPT_PROVIDER_READBACKS = vocabulary(["outcome_unknown", "verified", "not_applied"]);
|
|
221
|
+
var CEAL_PROTOCOL_VERSION = "1.4.0";
|
|
222
|
+
var CEAL_GATEWAY_ANNOUNCEMENT_SCOPE_STATEMENTS = Object.freeze({
|
|
223
|
+
github_app_installation_repositories: "Repositories in the installed GitHub App installation.",
|
|
224
|
+
slack_public_app_member_channels_only: "Public channels where the installed Slack app is a member, plus direct and multi-person direct messages the requester is a member of, as Slack reports that membership; private channels the administrator has not approved are not declared by this connector.",
|
|
225
|
+
notion_connected_logical_area: "Connected Notion logical area under provider-enforced sharing; descendant inventory is not declared.",
|
|
226
|
+
google_workspace_calendar_read_only: "Approved Calendar availability and event reads only; Calendar mutation is not declared.",
|
|
227
|
+
google_workspace_admitted_drive_target_metadata: "Metadata search for files in the shared drives and directly shared files the Connector reaches and the Organization admits as Targets; file-content read and mutation are not declared.",
|
|
228
|
+
google_workspace_admitted_drive_target_sheet_ranges: "Bounded values reads from Google Sheets in the shared drives and directly shared files the Connector reaches and the Organization admits as Targets; file mutation is not declared.",
|
|
229
|
+
google_workspace_admitted_drive_target_sheet_tabs: "Bounded tab metadata and grid extents from Google Sheets in the shared drives and directly shared files the Connector reaches and the Organization admits as Targets; cell contents and file mutation are not declared.",
|
|
230
|
+
google_workspace_admitted_drive_target_editable_sheet_ranges: "Bounded values updates and clears in editable Google Sheets in the shared drives and directly shared files the Connector reaches and the Organization admits as Targets; Docs, Slides, and other Drive file mutation are not declared."
|
|
231
|
+
});
|
|
232
|
+
var CEAL_OPERATION_ACCESS_SCHEMA_VERSION = "ceal.operation_access.v1";
|
|
233
|
+
var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
|
|
234
|
+
var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
|
|
235
|
+
var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
|
|
236
|
+
var CEAL_GATEWAY_ORGANIZATION_ROUTE_FACT_CALL_KEYS = Object.freeze([
|
|
237
|
+
"connector_ref",
|
|
238
|
+
"connector_revision",
|
|
239
|
+
"policy_decision_revision"
|
|
240
|
+
]);
|
|
241
|
+
|
|
141
242
|
// packages/ceal-client-protocol/src/operation-contract-types.ts
|
|
142
243
|
var CEAL_JSON_SCHEMA_2020_12 = "https://json-schema.org/draft/2020-12/schema";
|
|
143
244
|
var CEAL_OPERATION_ERROR_SCHEMA_VERSION = "ceal.operation_error.v1";
|
|
245
|
+
var CEAL_OPERATION_WITHHELD_CAUSES = vocabulary(["provider_permission", "access_level", "personal_authorization_required"]);
|
|
246
|
+
var CEAL_RETRY_AFTER_MAX_MS = 60 * 60 * 1e3;
|
|
144
247
|
var CEAL_JSON_SCHEMA_TYPES = vocabulary(["object", "array", "string", "integer", "number", "boolean", "null"]);
|
|
145
248
|
var OBSERVATION_POSTURES = vocabulary(["direct_readback", "approved_no_readback", "operator_mediated", "excluded"]);
|
|
146
249
|
var EFFECT_DOMAINS = vocabulary(["none", "gateway_state", "provider", "external_world"]);
|
|
@@ -176,27 +279,6 @@ var CEAL_OPERATION_APPROVAL_DISPOSITIONS = vocabulary([
|
|
|
176
279
|
"interactive_unavailable"
|
|
177
280
|
]);
|
|
178
281
|
|
|
179
|
-
// packages/ceal-client-protocol/src/gateway-response-types.ts
|
|
180
|
-
var CEAL_CAPABILITY_READINESS_VALUES = vocabulary(["ready", "degraded", "unavailable", "unknown"]);
|
|
181
|
-
var CEAL_TARGET_REQUIREMENTS = vocabulary(["required", "optional", "none"]);
|
|
182
|
-
var CEAL_AUDIT_OUTCOMES = vocabulary(["succeeded", "denied", "failed"]);
|
|
183
|
-
var CEAL_GATEWAY_PROVIDER_STEP_OUTCOMES = vocabulary(["completed", "rejected", "throttled", "failed"]);
|
|
184
|
-
var CEAL_GATEWAY_READ_REUSE_DISPOSITIONS = vocabulary(["response_cache_hit", "corpus_hit", "corpus_hit_after_revalidation", "corpus_partial_provider_supplement", "provider_direct", "provider_continuation", "reuse_unavailable"]);
|
|
185
|
-
var CEAL_GATEWAY_READ_REUSE_UNAVAILABLE_REASONS = vocabulary(["not_reported", "storage_unavailable", "coverage_incomplete", "freshness_refresh_required", "not_reusable", "bypassed"]);
|
|
186
|
-
var CEAL_POLICY_DECISIONS = vocabulary(["allowed", "denied", "not_evaluated"]);
|
|
187
|
-
var CEAL_CONNECTOR_ROUTE_PHASES = vocabulary(["scope_observation", "target_selection", "route_resolution"]);
|
|
188
|
-
var CEAL_CONNECTOR_ROUTE_CAUSES = vocabulary(["provider_throttled", "provider_unavailable", "binding_invalid", "scope_limit_exceeded"]);
|
|
189
|
-
var CEAL_WRITE_SOURCE_KINDS = vocabulary(["authenticated_registered_client", "agent_lease_admission", "provider_authenticated_event"]);
|
|
190
|
-
var CEAL_REFRESH_DELIVERIES = vocabulary(["initial", "recovery", "replay", "terminal_failure", "recovery_unavailable"]);
|
|
191
|
-
var CEAL_CLIENT_OPERATIONS = vocabulary(["handshake", "discover", "call", "readback"]);
|
|
192
|
-
var CEAL_WRITE_IDEMPOTENCY_POSTURES = vocabulary(["required", "optional", "not_required"]);
|
|
193
|
-
var CEAL_WRITE_PROVIDER_READBACK_POSTURES = vocabulary(["required", "best_effort", "not_available"]);
|
|
194
|
-
var CEAL_WRITE_ATTRIBUTIONS = vocabulary(["subject", "requester_event", "connector_integration"]);
|
|
195
|
-
var CEAL_PROTOCOL_VERSION = "1.4.0";
|
|
196
|
-
var CEAL_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "phase", "schema_version"]);
|
|
197
|
-
var CEAL_CLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["cause", ...CEAL_CONNECTOR_ROUTE_FAILURE_KEYS]);
|
|
198
|
-
var CEAL_UNCLASSIFIED_CONNECTOR_ROUTE_FAILURE_KEYS = vocabulary(["connector_kind", "error_class", "phase", "schema_version"]);
|
|
199
|
-
|
|
200
282
|
// packages/ceal-client-protocol/src/gateway-validation-primitives.ts
|
|
201
283
|
var CealProtocolValidationError = class extends Error {
|
|
202
284
|
name = "CealProtocolValidationError";
|
|
@@ -213,6 +295,7 @@ var SAFE_CONNECTOR_KIND = new RegExp(`^[a-z][a-z0-9-]{0,${MAX_SAFE_TOKEN_LENGTH
|
|
|
213
295
|
var FORBIDDEN_SECRET_KEY = /^(?:[a-z0-9_]*(?:token|secret|password|credential(?:s)?|private_?key)|api_?key|authorization|bearer|raw_?provider_?payload|provider_?payload)$/iu;
|
|
214
296
|
var FORBIDDEN_AUTHORITY_KEY = /^(?:actor_?ref|owner_?ref|registration_?ref|runner_?ref|auth_?decision|policy_?decision|host_?decision)$/iu;
|
|
215
297
|
var TEXT_ENCODER = new TextEncoder();
|
|
298
|
+
var CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS = 128;
|
|
216
299
|
var CEAL_WIRE_SAFE_TEXT_MAX_BYTES = 8 * 1024;
|
|
217
300
|
function assertSafeJsonValue(value, options, depth = 0, count = { value: 0 }) {
|
|
218
301
|
count.value += 1;
|
|
@@ -232,12 +315,12 @@ function assertSafeJsonString(value, options) {
|
|
|
232
315
|
if (byteLength(value) > CEAL_WIRE_SAFE_TEXT_MAX_BYTES || firstDisallowedControl(value, "multi_line") !== null) invalidByContext(options);
|
|
233
316
|
}
|
|
234
317
|
function assertSafeJsonArray(value, options, depth, count) {
|
|
235
|
-
if (value.length >
|
|
318
|
+
if (value.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
236
319
|
for (const item of value) assertSafeJsonValue(item, options, depth + 1, count);
|
|
237
320
|
}
|
|
238
321
|
function assertSafeJsonRecord(record6, options, depth, count) {
|
|
239
322
|
const entries = Object.entries(record6);
|
|
240
|
-
if (entries.length >
|
|
323
|
+
if (entries.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
241
324
|
const sourceUrlColumn = options.allowHttpsUrl ? compactSourceUrlColumn(record6.fields) : null;
|
|
242
325
|
for (const [key, child] of entries) {
|
|
243
326
|
if (key === "credential_material_included" && child !== false) invalidByContext(options);
|
|
@@ -263,9 +346,9 @@ function compactSourceUrlColumn(fields) {
|
|
|
263
346
|
return index < 0 ? null : index;
|
|
264
347
|
}
|
|
265
348
|
function assertSafeJsonCompactRows(value, sourceUrlColumn, options, depth, count) {
|
|
266
|
-
if (!Array.isArray(value) || value.length >
|
|
349
|
+
if (!Array.isArray(value) || value.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
267
350
|
for (const row of value) {
|
|
268
|
-
if (!Array.isArray(row) || row.length >
|
|
351
|
+
if (!Array.isArray(row) || row.length > CEAL_WIRE_SAFE_ARRAY_MAX_ITEMS) invalidByContext(options);
|
|
269
352
|
for (const [index, cell] of row.entries()) {
|
|
270
353
|
if (index === sourceUrlColumn && (cell === null || isSafeExternalHttpsUrl(cell))) continue;
|
|
271
354
|
assertSafeJsonValue(cell, options, depth + 2, count);
|
|
@@ -298,6 +381,7 @@ function isSafeResultContent(value, key) {
|
|
|
298
381
|
const maximum = key === "text" ? 8192 : 1024;
|
|
299
382
|
return typeof value === "string" && byteLength(value) <= maximum && firstDisallowedControl(value, "multi_line") === null;
|
|
300
383
|
}
|
|
384
|
+
var CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES = 512;
|
|
301
385
|
function normalizeCealSingleLineText(value) {
|
|
302
386
|
return value.split("").map((character) => hasControlCharacter(character) ? " " : character).join("").trim();
|
|
303
387
|
}
|
|
@@ -406,6 +490,28 @@ function cut(text) {
|
|
|
406
490
|
return text.length <= REFUSAL_VALUE_MAX_CHARS ? text : `${text.slice(0, REFUSAL_VALUE_MAX_CHARS)}...(+${text.length - REFUSAL_VALUE_MAX_CHARS} chars)`;
|
|
407
491
|
}
|
|
408
492
|
|
|
493
|
+
// packages/ceal-client-protocol/src/operation-access-class.ts
|
|
494
|
+
var CEAL_OPERATION_ACCESS_CLASSES = vocabulary(["read", "write"]);
|
|
495
|
+
|
|
496
|
+
// packages/ceal-client-protocol/src/domain-vocabulary.ts
|
|
497
|
+
var CEAL_INTEGRATION_KINDS = vocabulary(["github", "google-workspace", "notion", "slack"]);
|
|
498
|
+
var CEAL_CONNECTOR_STATUSES = vocabulary(["active", "disabled", "disconnected"]);
|
|
499
|
+
var CEAL_CONNECTOR_CUSTODIES = vocabulary(["organization", "personal"]);
|
|
500
|
+
var CEAL_PROVIDER_BOUNDARY_KINDS = vocabulary([
|
|
501
|
+
"github_account",
|
|
502
|
+
"github_organization",
|
|
503
|
+
"google_workspace_domain",
|
|
504
|
+
"notion_workspace",
|
|
505
|
+
"slack_workspace"
|
|
506
|
+
]);
|
|
507
|
+
var CEAL_PROVIDER_BOUNDARY_EVIDENCE = vocabulary(["provider_readback", "operator_stated"]);
|
|
508
|
+
var CEAL_ACCESS_LEVELS = vocabulary(["none", "read", "read_write"]);
|
|
509
|
+
var CEAL_PERSONAL_ACCESS_CEILINGS = vocabulary(["disabled", "read", "read_write"]);
|
|
510
|
+
var CEAL_TARGET_VISIBILITIES = vocabulary(["public", "private"]);
|
|
511
|
+
var CEAL_PRINCIPAL_KINDS = vocabulary(["human", "service", "runtime"]);
|
|
512
|
+
var CEAL_ORGANIZATION_ROLES = vocabulary(["member", "admin"]);
|
|
513
|
+
var LABEL_EXPECTATION = `public-safe display text of at most ${CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES} bytes`;
|
|
514
|
+
|
|
409
515
|
// packages/ceal-client-protocol/src/safe-json-budget.ts
|
|
410
516
|
var SAFE_JSON_MIN_BYTES_PER_NODE = 4;
|
|
411
517
|
function safeJsonNodeBudgetForBytes(maxBytes) {
|
|
@@ -435,7 +541,9 @@ var CEAL_OPERATION_JSON_SCHEMA_BUDGET = Object.freeze({
|
|
|
435
541
|
});
|
|
436
542
|
|
|
437
543
|
// packages/ceal-client-protocol/src/operation-validation-primitives.ts
|
|
438
|
-
var ISO_TIMESTAMP =
|
|
544
|
+
var ISO_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?([Zz]|([+-])(\d{2}):(\d{2}))$/u;
|
|
545
|
+
var CEAL_CALENDAR_DATE_PATTERN_SOURCE = "^\\d{4}-\\d{2}-\\d{2}$";
|
|
546
|
+
var CEAL_CALENDAR_DATE = new RegExp(CEAL_CALENDAR_DATE_PATTERN_SOURCE, "u");
|
|
439
547
|
function isOperationRecord(value) {
|
|
440
548
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
441
549
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -447,8 +555,56 @@ function requireOperationRecord(value, message) {
|
|
|
447
555
|
function requireClosedValue(value, members, message) {
|
|
448
556
|
if (typeof value !== "string" || !members.includes(value)) throw new TypeError(message);
|
|
449
557
|
}
|
|
558
|
+
function isCealIsoTimestamp(value) {
|
|
559
|
+
return typeof value === "string" && parseCealIsoTimestamp(value) !== null;
|
|
560
|
+
}
|
|
561
|
+
function parseCealIsoTimestamp(value) {
|
|
562
|
+
const fields = parseCealIsoTimestampFields(value);
|
|
563
|
+
if (fields === null || !validCalendarDate(fields) || !validClockTime(fields) || !validUtcOffset(fields)) return null;
|
|
564
|
+
const epochMilliseconds = cealFieldsEpochMilliseconds(fields);
|
|
565
|
+
if (!Number.isFinite(epochMilliseconds)) return null;
|
|
566
|
+
return {
|
|
567
|
+
epochNanoseconds: BigInt(epochMilliseconds) * 1000000n + BigInt(fields.fraction.padEnd(9, "0") || "0")
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function parseCealIsoTimestampFields(value) {
|
|
571
|
+
const match = ISO_TIMESTAMP.exec(value);
|
|
572
|
+
if (match === null) return null;
|
|
573
|
+
return {
|
|
574
|
+
year: Number(match[1]),
|
|
575
|
+
month: Number(match[2]),
|
|
576
|
+
day: Number(match[3]),
|
|
577
|
+
hour: Number(match[4]),
|
|
578
|
+
minute: Number(match[5]),
|
|
579
|
+
second: Number(match[6]),
|
|
580
|
+
fraction: match[7] ?? "",
|
|
581
|
+
offsetSign: match[9] === void 0 ? 0 : match[9] === "+" ? 1 : -1,
|
|
582
|
+
offsetHour: Number(match[10] ?? 0),
|
|
583
|
+
offsetMinute: Number(match[11] ?? 0)
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
function validCalendarDate({ year, month, day }) {
|
|
587
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth(year, month);
|
|
588
|
+
}
|
|
589
|
+
function validClockTime({ hour, minute, second }) {
|
|
590
|
+
return hour <= 23 && minute <= 59 && second <= 59;
|
|
591
|
+
}
|
|
592
|
+
function validUtcOffset({ offsetHour, offsetMinute }) {
|
|
593
|
+
return offsetHour <= 23 && offsetMinute <= 59;
|
|
594
|
+
}
|
|
595
|
+
function cealFieldsEpochMilliseconds(fields) {
|
|
596
|
+
const shiftedYear = fields.year < 100 ? fields.year + 400 : fields.year;
|
|
597
|
+
const cycleMilliseconds = fields.year < 100 ? (400 * 365 + 97) * 24 * 60 * 60 * 1e3 : 0;
|
|
598
|
+
const localMilliseconds = Date.UTC(shiftedYear, fields.month - 1, fields.day, fields.hour, fields.minute, fields.second, 0) - cycleMilliseconds;
|
|
599
|
+
const offsetMilliseconds = fields.offsetSign * (fields.offsetHour * 60 + fields.offsetMinute) * 6e4;
|
|
600
|
+
return localMilliseconds - offsetMilliseconds;
|
|
601
|
+
}
|
|
602
|
+
function daysInMonth(year, month) {
|
|
603
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
604
|
+
return [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month - 1] ?? 0;
|
|
605
|
+
}
|
|
450
606
|
function requireIsoTimestamp(value, message) {
|
|
451
|
-
if (
|
|
607
|
+
if (!isCealIsoTimestamp(value)) throw new TypeError(message);
|
|
452
608
|
}
|
|
453
609
|
|
|
454
610
|
// packages/ceal-client-protocol/src/operation-json-schema.ts
|
|
@@ -470,7 +626,7 @@ var CLIENT_AUDIENCE_SET = new Set(CLIENT_AUDIENCES);
|
|
|
470
626
|
// packages/ceal-client-protocol/src/schema-literals.ts
|
|
471
627
|
var CEAL_SCHEMA_LITERALS = [
|
|
472
628
|
"ceal.activation_transport.v1",
|
|
473
|
-
|
|
629
|
+
CEAL_OPERATION_ACCESS_SCHEMA_VERSION,
|
|
474
630
|
CEAL_OPERATION_CATALOG_SCHEMA_VERSION,
|
|
475
631
|
"ceal.client_refresh_request.v2",
|
|
476
632
|
"ceal.client_refresh_result.v2",
|
|
@@ -493,7 +649,6 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
493
649
|
"ceal.enrollment_exchange.v1",
|
|
494
650
|
"ceal.enrollment_result.v1",
|
|
495
651
|
"ceal.first_activation_causal_join.v1",
|
|
496
|
-
"ceal.gateway_aggregate_authorization_snapshot.v2",
|
|
497
652
|
"ceal.gateway_announcement_policy.v1",
|
|
498
653
|
"ceal.gateway_audit_call_detail.v1",
|
|
499
654
|
"ceal.gateway_audit_event.v1",
|
|
@@ -501,7 +656,7 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
501
656
|
"ceal.gateway_authorization_snapshot.v1",
|
|
502
657
|
"ceal.gateway_cache_origin.v1",
|
|
503
658
|
"ceal.gateway_call_result.v1",
|
|
504
|
-
|
|
659
|
+
CEAL_GATEWAY_COMMAND_RESOLUTION_SCHEMA_VERSION,
|
|
505
660
|
"ceal.gateway_connector_route_failure.v1",
|
|
506
661
|
"ceal.gateway_discovery.v3",
|
|
507
662
|
"ceal.gateway_handshake.v1",
|
|
@@ -536,7 +691,7 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
536
691
|
"ceal.operation_receipt.v1",
|
|
537
692
|
"ceal.operation_result.v1",
|
|
538
693
|
"ceal.operation_result.compact.v1",
|
|
539
|
-
"ceal.local_result_materialization.
|
|
694
|
+
"ceal.local_result_materialization.v2",
|
|
540
695
|
"ceal.local_result_root.v1",
|
|
541
696
|
"ceal.owner_bootstrap_exchange_request.v1",
|
|
542
697
|
"ceal.owner_bootstrap_exchange_result.v1",
|
|
@@ -546,7 +701,8 @@ var CEAL_SCHEMA_LITERALS = [
|
|
|
546
701
|
"ceal.request_cancellation.v1",
|
|
547
702
|
"ceal.request_graph_revision.v1",
|
|
548
703
|
"ceal.request_occurrence_correlation.v1",
|
|
549
|
-
"ceal.request_projection.v1"
|
|
704
|
+
"ceal.request_projection.v1",
|
|
705
|
+
"ceal.write_safety_finding.v1"
|
|
550
706
|
];
|
|
551
707
|
var CEAL_SCHEMA_LITERAL_SET = new Set(CEAL_SCHEMA_LITERALS);
|
|
552
708
|
|
|
@@ -561,21 +717,14 @@ var CEAL_BOUNDED_SERIES_CATALOG = Object.freeze({
|
|
|
561
717
|
thread_run_events: (root) => join(root, "run-events.jsonl"),
|
|
562
718
|
bounded_store_sidecar: (root) => `${root}.jsonl`,
|
|
563
719
|
gateway_audit_archive: (root) => `${root}.jsonl`,
|
|
564
|
-
gateway_capability_audit: (root) => join(root, "capability-audit.jsonl"),
|
|
565
|
-
gateway_retrieval_waste_partition: (root) => `${root}.jsonl`,
|
|
566
|
-
gateway_access_policy_publication: (root) => join(root, "access-policy-publications.jsonl"),
|
|
567
720
|
gateway_access_group_connector_control_audit: (root) => `${root}.audit.jsonl`,
|
|
568
721
|
gateway_access_group_connector_scope_audit: (root) => join(root, "profile-connector-scope-audit.jsonl"),
|
|
569
722
|
gateway_access_group_connector_activity_scope_audit: (root) => join(root, "profile-connector-activity-scope-audit.jsonl"),
|
|
723
|
+
gateway_access_group_connector_calendar_scope_audit: (root) => join(root, "profile-connector-calendar-scope-audit.jsonl"),
|
|
570
724
|
gateway_access_group_connector_readiness_audit: (root) => join(root, "profile-connector-readiness.audit.jsonl"),
|
|
571
725
|
gateway_organization_audit_ledger: (root) => join(root, "organization-ledger.jsonl"),
|
|
572
726
|
gateway_personal_client_audit: (root) => join(root, "audit.jsonl"),
|
|
573
|
-
gateway_personal_client_access_audit: (root) => join(root, "access-audit.jsonl")
|
|
574
|
-
gateway_admin_enrollment: (root) => join(root, "enrollments.jsonl"),
|
|
575
|
-
gateway_admin_login: (root) => join(root, "logins.jsonl"),
|
|
576
|
-
gateway_admin_family: (root) => join(root, "families.jsonl"),
|
|
577
|
-
gateway_admin_refresh_token: (root) => join(root, "refresh-tokens.jsonl"),
|
|
578
|
-
gateway_admin_access_token: (root) => join(root, "access-tokens.jsonl")
|
|
727
|
+
gateway_personal_client_access_audit: (root) => join(root, "access-audit.jsonl")
|
|
579
728
|
});
|
|
580
729
|
|
|
581
730
|
// packages/ceal-client-protocol/src/gateway-installation-activation.ts
|
|
@@ -583,8 +732,21 @@ var CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_DATA_CHARACTERS = 32;
|
|
|
583
732
|
var CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_CHECK_CHARACTERS = 8;
|
|
584
733
|
var CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_COMPACT_CHARACTERS = CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_DATA_CHARACTERS + CEAL_GATEWAY_INSTALLATION_ACTIVATION_CODE_CHECK_CHARACTERS;
|
|
585
734
|
|
|
735
|
+
// packages/ceal-client-protocol/src/gateway-entrypoint-commands.ts
|
|
736
|
+
var GATEWAY_ENTRYPOINT_COMMANDS = Object.freeze([
|
|
737
|
+
Object.freeze({ id: "controlPlane", path: Object.freeze(["control-plane"]) }),
|
|
738
|
+
Object.freeze({ id: "originShow", path: Object.freeze(["origin", "show"]) }),
|
|
739
|
+
Object.freeze({ id: "credentialsImport", path: Object.freeze(["credentials", "import"]) }),
|
|
740
|
+
Object.freeze({ id: "initialize", path: Object.freeze(["init"]) })
|
|
741
|
+
]);
|
|
742
|
+
|
|
586
743
|
// packages/ceal-client-protocol/src/host-registration.ts
|
|
587
744
|
var CEAL_HOST_REGISTRATION_RESULT_KEYS = Object.freeze(["credential_generation", "host_access_token", "host_installation_ref", "host_source_ref", "ok", "registration_attempt_ref", "schema_version", "status"]);
|
|
745
|
+
var CEAL_LOWERCASE_UUID_PATTERN = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
746
|
+
var CEAL_HOST_REGISTRATION_ATTEMPT_REF_PATTERN = new RegExp(`^host-registration-attempt:${CEAL_LOWERCASE_UUID_PATTERN}$`, "u");
|
|
747
|
+
var CEAL_HOST_INSTALLATION_REF_PATTERN = new RegExp(`^host-installation:${CEAL_LOWERCASE_UUID_PATTERN}$`, "u");
|
|
748
|
+
var CEAL_HOST_SOURCE_REF_PATTERN = new RegExp(`^host-source:${CEAL_LOWERCASE_UUID_PATTERN}$`, "u");
|
|
749
|
+
var CEAL_HOST_ACCESS_TOKEN_PATTERN = /^ceal_host_[A-Za-z0-9_-]{43}$/u;
|
|
588
750
|
|
|
589
751
|
// packages/ceal-client-protocol/src/agent-session-handoff.ts
|
|
590
752
|
var CEAL_AGENT_ADMISSION_PHASES = vocabulary([
|
|
@@ -616,12 +778,19 @@ var CEAL_AGENT_SERVICE_SESSION_RECORD_KEYS = vocabulary([
|
|
|
616
778
|
var CEAL_AGENT_SESSION_COMMIT_KEYS = vocabulary(["attempt_ref", "expected_session_revision", "session_revision"]);
|
|
617
779
|
|
|
618
780
|
// packages/ceal-client-protocol/src/operation-next-actions.ts
|
|
781
|
+
var CEAL_REPEAT_REQUEST_NEXT_ACTION = "repeat_request";
|
|
782
|
+
var CEAL_INSPECT_RECEIPT_NEXT_ACTION = "inspect_receipt";
|
|
619
783
|
var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
620
784
|
"select_did_you_mean",
|
|
621
785
|
"choose_new_output_path",
|
|
622
786
|
"choose_new_idempotency_key",
|
|
623
787
|
"restart_search",
|
|
624
788
|
"repeat_original_arguments",
|
|
789
|
+
// The occurrence this call addressed is settled and cannot move again -- a
|
|
790
|
+
// denied effect approval, an expired continuation -- so the caller runs the
|
|
791
|
+
// same call again as a new request. The refusal names the re-request; this
|
|
792
|
+
// word is the re-request.
|
|
793
|
+
CEAL_REPEAT_REQUEST_NEXT_ACTION,
|
|
625
794
|
"choose_new_request_identity",
|
|
626
795
|
// A refusal that names the argument (invalid_argument with `property`) tells the caller exactly what to change.
|
|
627
796
|
"correct_argument",
|
|
@@ -640,10 +809,35 @@ var CALLER_RECOVERABLE_NEXT_ACTION_VALUES = [
|
|
|
640
809
|
"use_original_input",
|
|
641
810
|
// The same mismatch on the Request binding rather than the input: the
|
|
642
811
|
// occurrence was admitted under one binding and the replay presents another.
|
|
643
|
-
"use_original_request_binding"
|
|
812
|
+
"use_original_request_binding",
|
|
813
|
+
// The client's own filesystem write was refused -- EACCES, EPERM, or a
|
|
814
|
+
// read-only mount -- which is how an agent sandbox fences writes outside its
|
|
815
|
+
// workspace. The caller reruns the same command with unsandboxed or escalated
|
|
816
|
+
// execution; re-adopting the session cannot help, because the sandbox refuses
|
|
817
|
+
// that write too.
|
|
818
|
+
"rerun_outside_agent_sandbox",
|
|
819
|
+
// The refusal names the one Operation to read next in `describe_command`:
|
|
820
|
+
// an absent Operation id with a sole catalog-ranked candidate, or a known
|
|
821
|
+
// Operation invoked without its input. Describing only reads the verified
|
|
822
|
+
// contract and dispatches nothing, and the caller then retries with the
|
|
823
|
+
// named Operation or input, so it is guidance the scorer counts.
|
|
824
|
+
"describe_operation",
|
|
825
|
+
// The refusal names the caller's own personal connect Operation in
|
|
826
|
+
// `required_action_sequence`: the Operation asked for, or the credential
|
|
827
|
+
// the call ran on, needs a personal connection the caller does not hold
|
|
828
|
+
// (never connected, revoked, or expired). The caller runs that exact
|
|
829
|
+
// Operation, completes the consent it hands back, and retries the same call.
|
|
830
|
+
"connect_personal_account",
|
|
831
|
+
// The provider or Gateway named a bounded wait; the caller waits that long
|
|
832
|
+
// and submits the same read again.
|
|
833
|
+
"retry_after"
|
|
644
834
|
];
|
|
645
835
|
var CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS = Object.freeze(CALLER_RECOVERABLE_NEXT_ACTION_VALUES);
|
|
646
836
|
var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
837
|
+
// Only an administrator can take the named step -- approving a place for an
|
|
838
|
+
// Operation, granting a withheld capability -- so a non-administrator asks
|
|
839
|
+
// one. The refusal names the administrator step; this word is the ask.
|
|
840
|
+
"ask_admin",
|
|
647
841
|
"await_pending_approval",
|
|
648
842
|
"check_gateway",
|
|
649
843
|
"inspect_context_store",
|
|
@@ -658,9 +852,13 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
|
658
852
|
"inspect_local_command",
|
|
659
853
|
"inspect_local_output",
|
|
660
854
|
"inspect_local_route",
|
|
661
|
-
|
|
855
|
+
CEAL_INSPECT_RECEIPT_NEXT_ACTION,
|
|
662
856
|
"inspect_request",
|
|
663
857
|
"none",
|
|
858
|
+
// The provider refused because of its own configuration -- an API not
|
|
859
|
+
// enabled, an app scope missing -- which an operator changes in that
|
|
860
|
+
// provider's console. No caller edit and no retry fixes it.
|
|
861
|
+
"operator_action",
|
|
664
862
|
"recover",
|
|
665
863
|
"reconcile",
|
|
666
864
|
// Reconciliation of one named occurrence, not of the deployment: a legacy
|
|
@@ -668,6 +866,10 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
|
668
866
|
// no edit to the call changes that. `reconcile` is the deployment-wide word.
|
|
669
867
|
"reconcile_occurrence",
|
|
670
868
|
"reduce_request_size",
|
|
869
|
+
// The Host's invocation context for this turn is stale or expired at the
|
|
870
|
+
// Gateway. The Host's next turn rebinds it and the same call then succeeds;
|
|
871
|
+
// nothing in the call changes, so it is not a remedy the scorer may count.
|
|
872
|
+
"refresh_host_invocation_context",
|
|
671
873
|
"repair_or_adopt_session",
|
|
672
874
|
"retry",
|
|
673
875
|
// The owner -- a correlation hook, an approval hook -- is not serving right
|
|
@@ -681,10 +883,23 @@ var NON_REMEDY_NEXT_ACTION_VALUES = [
|
|
|
681
883
|
var CEAL_NON_REMEDY_NEXT_ACTIONS = Object.freeze(NON_REMEDY_NEXT_ACTION_VALUES);
|
|
682
884
|
var DECLARED = /* @__PURE__ */ new Set([...CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS, ...CEAL_NON_REMEDY_NEXT_ACTIONS]);
|
|
683
885
|
var RECOVERABLE = new Set(CEAL_CALLER_RECOVERABLE_NEXT_ACTIONS);
|
|
886
|
+
var CEAL_OPERATION_WITHHELD_NEXT_ACTIONS = Object.freeze({
|
|
887
|
+
provider_permission: "ask_admin",
|
|
888
|
+
access_level: "ask_admin",
|
|
889
|
+
personal_authorization_required: "connect_personal_account"
|
|
890
|
+
});
|
|
684
891
|
|
|
685
892
|
// packages/ceal-client-protocol/src/operation-contract-artifact.ts
|
|
686
893
|
var CATALOG_REVISION = new RegExp(CEAL_VERIFIED_ARTIFACT_CATALOG_REVISION_PATTERN, "u");
|
|
687
894
|
var SHA256 = new RegExp(CEAL_VERIFIED_ARTIFACT_DIGEST_PATTERN, "u");
|
|
895
|
+
var CEAL_OPERATION_INVOKE_COMMAND = "invoke";
|
|
896
|
+
var CEAL_OPERATION_INPUT_FLAG = "--input";
|
|
897
|
+
var CEAL_OPERATION_ID_PLACEHOLDER = "<operation-id>";
|
|
898
|
+
var CEAL_OPERATION_INPUT_PLACEHOLDER = "<json-object>";
|
|
899
|
+
var CEAL_MODEL_INVOCATION_ARGUMENTS = [CEAL_OPERATION_INVOKE_COMMAND, CEAL_OPERATION_ID_PLACEHOLDER, CEAL_OPERATION_INPUT_FLAG, shellSingleQuoted(CEAL_OPERATION_INPUT_PLACEHOLDER)].join(" ");
|
|
900
|
+
function shellSingleQuoted(value) {
|
|
901
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
902
|
+
}
|
|
688
903
|
var CEAL_OPERATION_CONTRACT_REQUIRED_FIELDS = Object.freeze(["input_schema", "result_schema"]);
|
|
689
904
|
var CEAL_OPERATION_CONTRACT_OPTIONAL_FIELDS = Object.freeze(["summary", "input_resolution"]);
|
|
690
905
|
var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
|
|
@@ -713,9 +928,178 @@ var CEAL_VERIFIED_ARTIFACT_DESCRIPTOR_JSON_SCHEMA = Object.freeze({
|
|
|
713
928
|
}
|
|
714
929
|
});
|
|
715
930
|
|
|
931
|
+
// packages/ceal-client-protocol/src/bare-document.ts
|
|
932
|
+
var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_fields;
|
|
933
|
+
var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
|
|
934
|
+
|
|
935
|
+
// packages/ceal-client-protocol/src/leased-consumer-vocabulary.ts
|
|
936
|
+
var CEAL_TERMINAL_DISPOSITIONS = vocabulary(["completed", "failed", "cancelled"]);
|
|
937
|
+
var CEAL_LEASE_DISPOSITIONS = vocabulary([...CEAL_TERMINAL_DISPOSITIONS, "deferred"]);
|
|
938
|
+
var CEAL_AUTOMATIC_FEEDBACK_INTENTS = vocabulary(["initial"]);
|
|
939
|
+
var CEAL_LEASED_MESSAGE_DELIVERY_OPERATION_IDS = vocabulary(["message.send", "message.update", "message.delete"]);
|
|
940
|
+
var CEAL_REPOSITORY_VISIBILITIES = vocabulary(["public", "private", "internal"]);
|
|
941
|
+
var CEAL_RESULT_DELIVERY_OFFERS = vocabulary(["pending", "offered", "transport_lost"]);
|
|
942
|
+
var CEAL_RESULT_DELIVERY_STATES = vocabulary(["unavailable", ...CEAL_RESULT_DELIVERY_OFFERS]);
|
|
943
|
+
var CEAL_PROVIDER_OUTCOMES = vocabulary(["not_attempted", "outcome_unknown", "verified"]);
|
|
944
|
+
var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
945
|
+
"provider_page_budget",
|
|
946
|
+
"call_deadline",
|
|
947
|
+
"related_item_budget",
|
|
948
|
+
"provider_cursor_unavailable",
|
|
949
|
+
"malformed_provider_page",
|
|
950
|
+
"visibility_incomplete",
|
|
951
|
+
"unsupported_event_kind"
|
|
952
|
+
]);
|
|
953
|
+
var CEAL_ACTIVITY_CONNECTOR_KINDS = vocabulary(["slack", "github", "google-calendar", "google-drive", "notion"]);
|
|
954
|
+
var CEAL_ACTIVITY_COVERAGE_STATUSES = vocabulary(["complete_for_target", "continuation_required", "blocked"]);
|
|
955
|
+
var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
956
|
+
"slack.message",
|
|
957
|
+
"slack.reply",
|
|
958
|
+
"slack.direct_message",
|
|
959
|
+
"github.commit.authored",
|
|
960
|
+
"github.issue.opened",
|
|
961
|
+
"github.pull_request.opened",
|
|
962
|
+
"github.review.submitted",
|
|
963
|
+
"github.issue_comment.created",
|
|
964
|
+
"github.review_comment.created",
|
|
965
|
+
"calendar.organized",
|
|
966
|
+
"calendar.accepted_invite",
|
|
967
|
+
"calendar.recorded",
|
|
968
|
+
"drive.file.created",
|
|
969
|
+
"drive.file.modified",
|
|
970
|
+
"drive.file.viewed",
|
|
971
|
+
"notion.page.created",
|
|
972
|
+
"notion.page.edited"
|
|
973
|
+
]);
|
|
974
|
+
var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
975
|
+
"message_created",
|
|
976
|
+
"commit_authored",
|
|
977
|
+
"resource_created",
|
|
978
|
+
"review_submitted",
|
|
979
|
+
"comment_created",
|
|
980
|
+
"event_start",
|
|
981
|
+
"all_day_start",
|
|
982
|
+
"file_created",
|
|
983
|
+
"file_modified",
|
|
984
|
+
"file_modified_by_me",
|
|
985
|
+
"file_viewed_by_me",
|
|
986
|
+
"page_created",
|
|
987
|
+
"page_last_edited",
|
|
988
|
+
"resource_modified"
|
|
989
|
+
]);
|
|
990
|
+
var CEAL_ACTIVITY_V3_KINDS = vocabulary(["authored", "edited", "messaged", "responded"]);
|
|
991
|
+
var CEAL_RESOURCE_TIME_BASES = vocabulary(["created", "modified", "viewed"]);
|
|
992
|
+
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
993
|
+
var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
|
|
994
|
+
var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "kinds", "container_ref", "addressed_to_me", "unread"]);
|
|
995
|
+
var CEAL_ARTIFACT_STAGE_TERMINALS = vocabulary(["chunk_accepted", "artifact_ready", "idempotency_replayed"]);
|
|
996
|
+
var CEAL_READ_ITEM_KINDS = vocabulary(["conversation", "identity", "usergroup", "message", "file", "document"]);
|
|
997
|
+
var CEAL_LEASED_MESSAGE_SEARCH_COMPLETENESS = vocabulary(["complete", "continuation_available", "truncated"]);
|
|
998
|
+
var CEAL_OPAQUE_HANDLE_KINDS = vocabulary(["target", "message", "thread", "artifact", "document", "object"]);
|
|
999
|
+
var CEAL_WRITE_OBJECT_KINDS = vocabulary(["document", "comment", "cell_range"]);
|
|
1000
|
+
var CEAL_CONVERSATION_KINDS = vocabulary(["channel", "dm", "group"]);
|
|
1001
|
+
var CEAL_FILE_TYPE_FAMILIES = vocabulary(["all", "image", "pdf"]);
|
|
1002
|
+
var CEAL_GITHUB_ISSUE_STATES = vocabulary(["open", "closed", "all"]);
|
|
1003
|
+
var CEAL_NOTION_UPDATABLE_BLOCK_TYPES = vocabulary([
|
|
1004
|
+
"paragraph",
|
|
1005
|
+
"heading_1",
|
|
1006
|
+
"heading_2",
|
|
1007
|
+
"heading_3",
|
|
1008
|
+
"bulleted_list_item",
|
|
1009
|
+
"numbered_list_item",
|
|
1010
|
+
"to_do",
|
|
1011
|
+
"code"
|
|
1012
|
+
]);
|
|
1013
|
+
var CEAL_NOTION_NEUTRAL_TYPES = vocabulary([
|
|
1014
|
+
"text",
|
|
1015
|
+
"number",
|
|
1016
|
+
"boolean",
|
|
1017
|
+
"date",
|
|
1018
|
+
"select",
|
|
1019
|
+
"multi_select",
|
|
1020
|
+
"people",
|
|
1021
|
+
"url",
|
|
1022
|
+
"email",
|
|
1023
|
+
"phone",
|
|
1024
|
+
"unsupported"
|
|
1025
|
+
]);
|
|
1026
|
+
var CEAL_NOTION_IDENTITY_GAP_REASONS = vocabulary([
|
|
1027
|
+
"identity_unavailable",
|
|
1028
|
+
"identity_stale",
|
|
1029
|
+
"identity_ambiguous",
|
|
1030
|
+
"identity_revoked",
|
|
1031
|
+
"identity_unlinked"
|
|
1032
|
+
]);
|
|
1033
|
+
var CEAL_NOTION_STRING_VALUE_TYPES = vocabulary(["text", "select", "url", "email", "phone"]);
|
|
1034
|
+
var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "unsupported", "download_failed", "permission_denied"]);
|
|
1035
|
+
var CEAL_PLAN_ITEM_STATUSES = vocabulary(["pending", "active", "completed"]);
|
|
1036
|
+
var CEAL_FINAL_PRESENTATION_INTENTS = vocabulary(["final", "stop", "transient_notice"]);
|
|
1037
|
+
var CEAL_PRESENTATION_INTENTS = vocabulary(["progress", ...CEAL_FINAL_PRESENTATION_INTENTS]);
|
|
1038
|
+
|
|
1039
|
+
// packages/ceal-client-protocol/src/resource-row.ts
|
|
1040
|
+
var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
1041
|
+
schema_version: { required: true, timestamp: false },
|
|
1042
|
+
ref: { required: true, timestamp: false },
|
|
1043
|
+
next: { required: true, timestamp: false },
|
|
1044
|
+
kind: { required: true, timestamp: false },
|
|
1045
|
+
title: { required: true, timestamp: false },
|
|
1046
|
+
container_label: { required: true, timestamp: false },
|
|
1047
|
+
container_ref: { required: false, timestamp: false },
|
|
1048
|
+
source: { required: true, timestamp: false },
|
|
1049
|
+
owner: { required: false, timestamp: false },
|
|
1050
|
+
counterparts: { required: false, timestamp: false },
|
|
1051
|
+
created_at: { required: false, timestamp: true },
|
|
1052
|
+
created_at_local: { required: false, timestamp: true },
|
|
1053
|
+
modified_at: { required: false, timestamp: true },
|
|
1054
|
+
modified_at_local: { required: false, timestamp: true },
|
|
1055
|
+
viewed_by_me_at: { required: false, timestamp: true },
|
|
1056
|
+
visibility: { required: false, timestamp: false },
|
|
1057
|
+
start: { required: false, timestamp: false },
|
|
1058
|
+
end: { required: false, timestamp: false },
|
|
1059
|
+
status: { required: false, timestamp: false },
|
|
1060
|
+
snippet: { required: false, timestamp: false },
|
|
1061
|
+
state: { required: true, timestamp: false },
|
|
1062
|
+
timestamp_basis: { required: true, timestamp: false }
|
|
1063
|
+
});
|
|
1064
|
+
var RESOURCE_ROW_REQUIRED_KEYS = Object.entries(RESOURCE_ROW_KEY_RULES).flatMap(([key, rule]) => rule.required ? [key] : []);
|
|
1065
|
+
var RESOURCE_ROW_KEYS = new Set(Object.keys(RESOURCE_ROW_KEY_RULES));
|
|
1066
|
+
var RESOURCE_ROW_TIMESTAMP_KEYS = Object.entries(RESOURCE_ROW_KEY_RULES).flatMap(([key, rule]) => rule.timestamp ? [key] : []);
|
|
1067
|
+
|
|
1068
|
+
// packages/ceal-client-protocol/src/resource-read-result.ts
|
|
1069
|
+
var CEAL_RESOURCE_GET_COVERAGE_NEXT_ACTIONS = vocabulary(["reduce_request_size", "read_resource_again"]);
|
|
1070
|
+
|
|
1071
|
+
// packages/ceal-client-protocol/src/excluded-target-coverage-contract.ts
|
|
1072
|
+
var CEAL_EXCLUDED_TARGET_REASONS = vocabulary(["audience_narrower_than_place", "visibility_unknown"]);
|
|
1073
|
+
|
|
1074
|
+
// packages/ceal-client-protocol/src/excluded-target-coverage.ts
|
|
1075
|
+
var CEAL_EXCLUDED_TARGET_COVERAGE_MAX_ENTRIES = 64;
|
|
1076
|
+
function cealExcludedTargetCoverageJsonSchema() {
|
|
1077
|
+
const target = {
|
|
1078
|
+
target_ref: { type: "string", minLength: 8, maxLength: 134, pattern: "^target:[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$" },
|
|
1079
|
+
connector_kind: { type: "string", minLength: 1, maxLength: 64, pattern: "^[a-z][a-z0-9-]{0,63}$" },
|
|
1080
|
+
label: { type: "string", minLength: 1, maxLength: CEAL_PUBLIC_DISPLAY_NAME_MAX_BYTES },
|
|
1081
|
+
reason: { type: "string", enum: [...CEAL_EXCLUDED_TARGET_REASONS] }
|
|
1082
|
+
};
|
|
1083
|
+
return {
|
|
1084
|
+
type: "array",
|
|
1085
|
+
minItems: 1,
|
|
1086
|
+
maxItems: CEAL_EXCLUDED_TARGET_COVERAGE_MAX_ENTRIES,
|
|
1087
|
+
items: { type: "object", additionalProperties: false, maxProperties: 4, properties: target, required: ["target_ref", "reason"] }
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// packages/ceal-client-protocol/src/write-safety-finding.ts
|
|
1092
|
+
var CEAL_WRITE_SAFETY_FINDING_STATUSES = vocabulary(["warning", "unchecked"]);
|
|
1093
|
+
var CEAL_WRITE_SAFETY_AUDIENCES = vocabulary(["workspace", "person", "members", "unknown"]);
|
|
1094
|
+
var CEAL_WRITE_SAFETY_REFERENCE_REASONS = vocabulary(["not_published_to_audience", "audience_access_unknown"]);
|
|
1095
|
+
var CEAL_WRITE_SAFETY_UNCHECKED_REASONS = vocabulary(["authority_unavailable"]);
|
|
1096
|
+
|
|
716
1097
|
// packages/ceal-client-protocol/src/operation-occurrence.ts
|
|
717
1098
|
var MAX_RECEIPT_REVISION = Number.MAX_SAFE_INTEGER - 1;
|
|
718
1099
|
|
|
1100
|
+
// packages/ceal-client-protocol/src/leased-artifact-contract.ts
|
|
1101
|
+
var CEAL_LEASED_CONSUMER_ARTIFACT_CHUNK_MAX_BYTES = 12 * 1024;
|
|
1102
|
+
|
|
719
1103
|
// packages/ceal-client-protocol/src/leased-resource-stream.ts
|
|
720
1104
|
var CEAL_LEASED_CONSUMER_ATTACHMENT_STREAM_MAGIC = new Uint8Array([67, 69, 65, 76, 82, 83, 50, 0]);
|
|
721
1105
|
var CEAL_LEASED_CONSUMER_ATTACHMENT_STREAM_RECORD_PREFIX_BYTES = 8;
|
|
@@ -727,6 +1111,7 @@ var CEAL_ATTACHMENT_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_
|
|
|
727
1111
|
// packages/ceal-client-protocol/src/request-validation-primitives.ts
|
|
728
1112
|
import { createHash } from "node:crypto";
|
|
729
1113
|
var DIGEST = /^[a-f0-9]{64}$/u;
|
|
1114
|
+
var CEAL_SHA256_DIGEST_PATTERN = DIGEST.source;
|
|
730
1115
|
function isCealDigest(value) {
|
|
731
1116
|
return typeof value === "string" && DIGEST.test(value);
|
|
732
1117
|
}
|
|
@@ -753,7 +1138,7 @@ var CANCELLATION_STATES = vocabulary(["requested", "cancelled"]);
|
|
|
753
1138
|
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"]);
|
|
754
1139
|
var CEAL_REQUEST_JSON_SCHEMA = {
|
|
755
1140
|
type: "object",
|
|
756
|
-
maxProperties:
|
|
1141
|
+
maxProperties: 10,
|
|
757
1142
|
additionalProperties: false,
|
|
758
1143
|
properties: {
|
|
759
1144
|
schema_version: { type: "string", const: CEAL_REQUEST_SCHEMA_VERSION, maxLength: 32 },
|
|
@@ -776,7 +1161,8 @@ var CEAL_REQUEST_JSON_SCHEMA = {
|
|
|
776
1161
|
host_session_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
777
1162
|
host_turn_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
778
1163
|
host_input_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
779
|
-
host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source }
|
|
1164
|
+
host_source_ref: { type: "string", minLength: 1, maxLength: 128, pattern: SAFE_REF.source },
|
|
1165
|
+
requester_timezone: { type: "string", minLength: 1, maxLength: 128, description: "IANA timezone observed for the requester on this Request." }
|
|
780
1166
|
},
|
|
781
1167
|
required: CEAL_REQUEST_KEYS
|
|
782
1168
|
};
|
|
@@ -826,21 +1212,26 @@ var REQUEST_NODE_STANDING_OCCURRENCE_SCHEMA = {
|
|
|
826
1212
|
},
|
|
827
1213
|
required: ["schema_version", "node_ref", "operation_id", "correlation_ref", "occurrence_ref", "graph_revision", "graph_revision_digest", "admitted_input_sha256", "reason"]
|
|
828
1214
|
};
|
|
1215
|
+
var REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES = {
|
|
1216
|
+
schema_version: { type: "string", const: CEAL_OPERATION_ERROR_SCHEMA_VERSION, maxLength: 64 },
|
|
1217
|
+
code: REQUEST_ACTION_SAFE_REF_SCHEMA,
|
|
1218
|
+
message: { type: "string", minLength: 1, maxLength: 2048 },
|
|
1219
|
+
property: { type: "string", minLength: 1, maxLength: 256 },
|
|
1220
|
+
next_action: REQUEST_ACTION_SAFE_REF_SCHEMA,
|
|
1221
|
+
retryable: { type: "boolean" },
|
|
1222
|
+
source: { type: "string", enum: ["provider", "gateway"] },
|
|
1223
|
+
retry_after_ms: { type: "integer", minimum: 0, maximum: CEAL_RETRY_AFTER_MAX_MS },
|
|
1224
|
+
excluded_targets: cealExcludedTargetCoverageJsonSchema(),
|
|
1225
|
+
repaired_input: {},
|
|
1226
|
+
unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
|
|
1227
|
+
provider_handoff: { type: "string", const: "not_offered", maxLength: 32 },
|
|
1228
|
+
cause: { type: "string", enum: [...CEAL_OPERATION_WITHHELD_CAUSES] }
|
|
1229
|
+
};
|
|
829
1230
|
var REQUEST_EXECUTION_PREPARE_REFUSAL_SCHEMA = {
|
|
830
1231
|
type: "object",
|
|
831
1232
|
additionalProperties: false,
|
|
832
|
-
maxProperties:
|
|
833
|
-
properties:
|
|
834
|
-
schema_version: { type: "string", const: CEAL_OPERATION_ERROR_SCHEMA_VERSION, maxLength: 64 },
|
|
835
|
-
code: REQUEST_ACTION_SAFE_REF_SCHEMA,
|
|
836
|
-
message: { type: "string", minLength: 1, maxLength: 2048 },
|
|
837
|
-
property: { type: "string", minLength: 1, maxLength: 256 },
|
|
838
|
-
next_action: REQUEST_ACTION_SAFE_REF_SCHEMA,
|
|
839
|
-
retryable: { type: "boolean" },
|
|
840
|
-
repaired_input: {},
|
|
841
|
-
unrepaired_pointers: { type: "array", maxItems: 128, items: { type: "string", minLength: 1, maxLength: 256 } },
|
|
842
|
-
provider_handoff: { type: "string", const: "not_offered", maxLength: 32 }
|
|
843
|
-
},
|
|
1233
|
+
maxProperties: Object.keys(REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES).length,
|
|
1234
|
+
properties: REQUEST_EXECUTION_PREPARE_REFUSAL_PROPERTIES,
|
|
844
1235
|
required: ["schema_version", "code", "message", "next_action", "retryable"]
|
|
845
1236
|
};
|
|
846
1237
|
var CEAL_REQUEST_EXECUTION_ORDERS = vocabulary(["graph_dependency", "caller"]);
|
|
@@ -1023,6 +1414,7 @@ var CEAL_REQUEST_GRAPH_NODE_PREPARE_READINESS_JSON_SCHEMA = {
|
|
|
1023
1414
|
// packages/ceal-client-protocol/src/host-invocation-authentication.ts
|
|
1024
1415
|
var CEAL_HOST_INVOCATION_AUTHENTICATION_RESULT_SCHEMA = "ceal.host_invocation_authentication_result.v1";
|
|
1025
1416
|
var CEAL_HOST_INVOCATION_CONTEXT_PATH = "/api/ceal/v2/host-invocation-context";
|
|
1417
|
+
var CEAL_HOST_INGRESS_PATH = "/api/ceal/v2/host-ingress";
|
|
1026
1418
|
var CEAL_HOST_CLIENT_NONCE_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
|
|
1027
1419
|
var CEAL_HOST_CONTEXT_TOKEN_PATTERN = /^ceal_context_[A-Za-z0-9_-]{43}$/u;
|
|
1028
1420
|
function decodeCealHostInvocationAuthenticationResult(value, now = /* @__PURE__ */ new Date()) {
|
|
@@ -1041,16 +1433,14 @@ function exactKeys(value, expected) {
|
|
|
1041
1433
|
}
|
|
1042
1434
|
|
|
1043
1435
|
// packages/ceal-client-protocol/src/client-result-materialization.ts
|
|
1044
|
-
var
|
|
1045
|
-
var
|
|
1436
|
+
var CEAL_MODEL_VISIBLE_COMMAND_OUTPUT_MAX_BYTES = 32 * 1024;
|
|
1437
|
+
var REQUIRED_FIELDS2 = Object.freeze(["schema_version", "operation_id", "read_argv"]);
|
|
1438
|
+
var FIELDS = /* @__PURE__ */ new Set([...REQUIRED_FIELDS2, "local_result_root"]);
|
|
1046
1439
|
|
|
1047
1440
|
// packages/ceal-client-protocol/src/session-start-affordance.ts
|
|
1048
1441
|
var CEAL_SESSION_ENTRY_INVOCATION = "ceal";
|
|
1049
1442
|
var CEAL_LINK_RESOURCE_PRECEDENCE = `When the user asks to use Ceal or provides a link to information that may be available through installed organizational capabilities, run bare \`${CEAL_SESSION_ENTRY_INVOCATION}\`. The Host supplies the current user prompt without exposing it to the client command, and the Gateway returns the applicable entry in that one response. Treat a supplied link as a candidate Ceal resource rather than a public web-search target. Reach for a generic web fetch or web search only when the Gateway offered nothing.`;
|
|
1050
1443
|
|
|
1051
|
-
// packages/ceal-client-protocol/src/discovery-manifest.ts
|
|
1052
|
-
var CEAL_DISCOVERY_MANIFEST_CONNECTOR_KINDS = vocabulary(["github", "google-workspace", "notion", "slack"]);
|
|
1053
|
-
|
|
1054
1444
|
// packages/ceal-client-protocol/src/conversation-contracts.ts
|
|
1055
1445
|
var CEAL_APPROVAL_POLICY_KINDS = vocabulary(["requester_only", "named_users", "role_based"]);
|
|
1056
1446
|
var CEAL_APPROVAL_TARGET_KINDS = vocabulary([
|
|
@@ -1093,38 +1483,18 @@ var CEAL_GATEWAY_ANNOUNCEMENT_POLICY_NON_CLAIMS = vocabulary([
|
|
|
1093
1483
|
|
|
1094
1484
|
// packages/ceal-client-protocol/src/gateway-discovery-response-validation.ts
|
|
1095
1485
|
var ANNOUNCEMENT_POLICY_CAPABILITY_BINDINGS = Object.freeze({
|
|
1096
|
-
"
|
|
1097
|
-
"
|
|
1098
|
-
"github.issue.get": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1099
|
-
"github.pull_request.get": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1486
|
+
"repository.get": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1487
|
+
"resource.search": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1100
1488
|
"github.workflow_run.get": [{ effect: "read", scopeStatementKind: "github_app_installation_repositories", providerAuthorityKind: "github_app" }],
|
|
1101
1489
|
"message.search": [{ effect: "read", scopeStatementKind: "slack_public_app_member_channels_only", providerAuthorityKind: "slack_app" }],
|
|
1102
|
-
"message.get": [{ effect: "read", scopeStatementKind: "slack_public_app_member_channels_only", providerAuthorityKind: "slack_app" }],
|
|
1103
|
-
"resource.resolve": [
|
|
1104
|
-
{ effect: "read", scopeStatementKind: "slack_public_app_member_channels_only", providerAuthorityKind: "slack_app" },
|
|
1105
|
-
{ effect: "read", scopeStatementKind: "notion_connected_logical_area", providerAuthorityKind: "notion_integration" }
|
|
1106
|
-
],
|
|
1107
|
-
"conversation.thread.get": [{ effect: "read", scopeStatementKind: "slack_public_app_member_channels_only", providerAuthorityKind: "slack_app" }],
|
|
1108
1490
|
"notion.search": [{ effect: "read", scopeStatementKind: "notion_connected_logical_area", providerAuthorityKind: "notion_integration" }],
|
|
1109
|
-
"
|
|
1110
|
-
"
|
|
1111
|
-
"
|
|
1112
|
-
"
|
|
1113
|
-
"
|
|
1114
|
-
"
|
|
1115
|
-
"
|
|
1116
|
-
"sheets.values.update": [{ effect: "write", scopeStatementKind: "google_workspace_ceal_drive_or_direct_share_editable_sheet_ranges", providerAuthorityKind: "google_service_account" }],
|
|
1117
|
-
"sheets.values.clear": [{ effect: "write", scopeStatementKind: "google_workspace_ceal_drive_or_direct_share_editable_sheet_clear_ranges", providerAuthorityKind: "google_service_account" }]
|
|
1118
|
-
});
|
|
1119
|
-
var ANNOUNCEMENT_SCOPE_STATEMENTS = Object.freeze({
|
|
1120
|
-
github_app_installation_repositories: "Repositories in the installed GitHub App installation.",
|
|
1121
|
-
slack_public_app_member_channels_only: "Public channels where the installed Slack app is a member; private channels, direct messages, multi-person direct messages, and requester membership are not declared by this connector.",
|
|
1122
|
-
notion_connected_logical_area: "Connected Notion logical area under provider-enforced sharing; descendant inventory is not declared.",
|
|
1123
|
-
google_workspace_calendar_read_only: "Approved Calendar availability and event reads only; Calendar mutation is not declared.",
|
|
1124
|
-
google_workspace_ceal_drive_or_direct_share_metadata: "Metadata search for files in the organization shared drive named Ceal Drive and files directly shared with the provider application; file-content read and mutation are not declared.",
|
|
1125
|
-
google_workspace_ceal_drive_or_direct_share_sheet_ranges: "Bounded values reads from governed Google Sheets in the organization shared drive named Ceal Drive and directly shared files; file mutation is not declared.",
|
|
1126
|
-
google_workspace_ceal_drive_or_direct_share_editable_sheet_ranges: "Bounded values updates in governed editable Google Sheets in the organization shared drive named Ceal Drive and directly shared files; Docs, Slides, and other Drive file mutation are not declared.",
|
|
1127
|
-
google_workspace_ceal_drive_or_direct_share_editable_sheet_clear_ranges: "Bounded values clears in governed editable Google Sheets in the organization shared drive named Ceal Drive and directly shared files; Docs, Slides, and other Drive file mutation are not declared."
|
|
1491
|
+
"resource.get": [{ effect: "read", scopeStatementKind: "notion_connected_logical_area", providerAuthorityKind: "notion_integration" }],
|
|
1492
|
+
"availability.get": [{ effect: "read", scopeStatementKind: "google_workspace_calendar_read_only", providerAuthorityKind: "google_service_account" }],
|
|
1493
|
+
"event.search": [{ effect: "read", scopeStatementKind: "google_workspace_calendar_read_only", providerAuthorityKind: "google_service_account" }],
|
|
1494
|
+
"file.search": [{ effect: "read", scopeStatementKind: "google_workspace_admitted_drive_target_metadata", providerAuthorityKind: "google_service_account" }],
|
|
1495
|
+
"cell_range.get": [{ effect: "read", scopeStatementKind: "google_workspace_admitted_drive_target_sheet_ranges", providerAuthorityKind: "google_service_account" }],
|
|
1496
|
+
"sheet.list": [{ effect: "read", scopeStatementKind: "google_workspace_admitted_drive_target_sheet_tabs", providerAuthorityKind: "google_service_account" }],
|
|
1497
|
+
"cell_range.update": [{ effect: "write", scopeStatementKind: "google_workspace_admitted_drive_target_editable_sheet_ranges", providerAuthorityKind: "google_service_account" }]
|
|
1128
1498
|
});
|
|
1129
1499
|
|
|
1130
1500
|
// packages/ceal-client-protocol/src/gateway-write-identity.ts
|
|
@@ -1181,112 +1551,29 @@ var ADMIN_LINEAGE_NODE_KINDS = vocabulary(["source_read", "synthesis_preview", "
|
|
|
1181
1551
|
// packages/ceal-client-protocol/src/actor-kind.ts
|
|
1182
1552
|
var CEAL_ACTOR_KINDS = vocabulary(["human", "bot", "app", "unknown"]);
|
|
1183
1553
|
|
|
1184
|
-
// packages/ceal-client-protocol/src/
|
|
1185
|
-
var
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
var CEAL_PROVIDER_OUTCOMES = vocabulary(["not_attempted", "outcome_unknown", "verified"]);
|
|
1193
|
-
var CEAL_ACTIVITY_GAP_CODES = vocabulary([
|
|
1194
|
-
"provider_page_budget",
|
|
1195
|
-
"related_item_budget",
|
|
1196
|
-
"provider_cursor_unavailable",
|
|
1197
|
-
"malformed_provider_page",
|
|
1198
|
-
"visibility_incomplete",
|
|
1199
|
-
"unsupported_event_kind"
|
|
1200
|
-
]);
|
|
1201
|
-
var CEAL_ACTIVITY_COVERAGE_STATUSES = vocabulary(["complete_for_target", "continuation_required", "blocked"]);
|
|
1202
|
-
var CEAL_ACTIVITY_EVENT_KINDS = vocabulary([
|
|
1203
|
-
"slack.message",
|
|
1204
|
-
"slack.reply",
|
|
1205
|
-
"github.commit.authored",
|
|
1206
|
-
"github.issue.opened",
|
|
1207
|
-
"github.pull_request.opened",
|
|
1208
|
-
"github.review.submitted",
|
|
1209
|
-
"github.issue_comment.created",
|
|
1210
|
-
"github.review_comment.created",
|
|
1211
|
-
"calendar.organized",
|
|
1212
|
-
"calendar.accepted_invite",
|
|
1213
|
-
"calendar.recorded"
|
|
1214
|
-
]);
|
|
1215
|
-
var CEAL_ACTIVITY_TIMESTAMP_BASES = vocabulary([
|
|
1216
|
-
"message_created",
|
|
1217
|
-
"commit_authored",
|
|
1218
|
-
"resource_created",
|
|
1219
|
-
"review_submitted",
|
|
1220
|
-
"comment_created",
|
|
1221
|
-
"event_start",
|
|
1222
|
-
"all_day_start"
|
|
1554
|
+
// packages/ceal-client-protocol/src/personal-client-vocabulary.ts
|
|
1555
|
+
var CEAL_PERSONAL_CLIENT_BINDING_KEYS = vocabulary([
|
|
1556
|
+
"access_group_ref",
|
|
1557
|
+
"membership_ref",
|
|
1558
|
+
"registration_ref",
|
|
1559
|
+
"client_ref",
|
|
1560
|
+
"subject_ref",
|
|
1561
|
+
"instance_ref"
|
|
1223
1562
|
]);
|
|
1224
|
-
var
|
|
1225
|
-
var
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
"
|
|
1234
|
-
"heading_1",
|
|
1235
|
-
"heading_2",
|
|
1236
|
-
"heading_3",
|
|
1237
|
-
"bulleted_list_item",
|
|
1238
|
-
"numbered_list_item",
|
|
1239
|
-
"to_do",
|
|
1240
|
-
"code"
|
|
1241
|
-
]);
|
|
1242
|
-
var CEAL_NOTION_NEUTRAL_TYPES = vocabulary([
|
|
1243
|
-
"text",
|
|
1244
|
-
"number",
|
|
1245
|
-
"boolean",
|
|
1246
|
-
"date",
|
|
1247
|
-
"select",
|
|
1248
|
-
"multi_select",
|
|
1249
|
-
"people",
|
|
1250
|
-
"url",
|
|
1251
|
-
"email",
|
|
1252
|
-
"phone",
|
|
1253
|
-
"unsupported"
|
|
1254
|
-
]);
|
|
1255
|
-
var CEAL_NOTION_IDENTITY_GAP_REASONS = vocabulary([
|
|
1256
|
-
"identity_unavailable",
|
|
1257
|
-
"identity_stale",
|
|
1258
|
-
"identity_ambiguous",
|
|
1259
|
-
"identity_revoked",
|
|
1260
|
-
"identity_unlinked"
|
|
1261
|
-
]);
|
|
1262
|
-
var CEAL_NOTION_STRING_VALUE_TYPES = vocabulary(["text", "select", "url", "email", "phone"]);
|
|
1263
|
-
var CEAL_UNREAD_REASONS = vocabulary(["blocked", "unavailable", "too_large", "unsupported", "download_failed", "permission_denied"]);
|
|
1264
|
-
var CEAL_PLAN_ITEM_STATUSES = vocabulary(["pending", "active", "completed"]);
|
|
1265
|
-
var CEAL_FINAL_PRESENTATION_INTENTS = vocabulary(["final", "stop", "transient_notice"]);
|
|
1266
|
-
var CEAL_PRESENTATION_INTENTS = vocabulary(["progress", ...CEAL_FINAL_PRESENTATION_INTENTS]);
|
|
1267
|
-
|
|
1268
|
-
// packages/ceal-client-protocol/src/personal-client-vocabulary.ts
|
|
1269
|
-
var CEAL_PERSONAL_CLIENT_BINDING_KEYS = vocabulary([
|
|
1270
|
-
"access_group_ref",
|
|
1271
|
-
"membership_ref",
|
|
1272
|
-
"registration_ref",
|
|
1273
|
-
"client_ref",
|
|
1274
|
-
"subject_ref",
|
|
1275
|
-
"instance_ref"
|
|
1276
|
-
]);
|
|
1277
|
-
var CEAL_ENROLLMENT_FAILURE_CODES = vocabulary(["enrollment_invalid", "enrollment_expired", "enrollment_used"]);
|
|
1278
|
-
var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
1279
|
-
"refresh_invalid",
|
|
1280
|
-
"refresh_expired",
|
|
1281
|
-
"refresh_inactive",
|
|
1282
|
-
"refresh_replayed",
|
|
1283
|
-
"refresh_revoked",
|
|
1284
|
-
"authority_replaced",
|
|
1285
|
-
"refresh_recovery_unavailable"
|
|
1563
|
+
var CEAL_ENROLLMENT_FAILURE_CODES = vocabulary(["enrollment_invalid", "enrollment_expired", "enrollment_used"]);
|
|
1564
|
+
var CEAL_REFRESH_FAILURE_CODES = vocabulary([
|
|
1565
|
+
"refresh_invalid",
|
|
1566
|
+
"refresh_expired",
|
|
1567
|
+
"refresh_inactive",
|
|
1568
|
+
"refresh_replayed",
|
|
1569
|
+
"refresh_revoked",
|
|
1570
|
+
"authority_replaced",
|
|
1571
|
+
"refresh_recovery_unavailable",
|
|
1572
|
+
"refresh_recovery_expired"
|
|
1286
1573
|
]);
|
|
1287
1574
|
|
|
1288
1575
|
// packages/ceal-client-protocol/src/device-enrollment.ts
|
|
1289
|
-
var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired"]);
|
|
1576
|
+
var CEAL_DEVICE_ENROLLMENT_POLL_FAILURE_CODES = Object.freeze(["unsupported_feature", "recovery_required", "expired", "denied"]);
|
|
1290
1577
|
var POLL_RESULT_KEYS = Object.freeze(["retry_after_ms", "schema_version", "status"]);
|
|
1291
1578
|
|
|
1292
1579
|
// packages/ceal-client-protocol/src/personal-client-session.ts
|
|
@@ -1298,7 +1585,6 @@ var MAX_ARGUMENT_BYTES = 16 * 1024;
|
|
|
1298
1585
|
var HANDSHAKE_IDENTITY_KEYS = Object.freeze(["membership_ref", "registration_ref", "client_ref", "subject_ref", "instance_ref"]);
|
|
1299
1586
|
var AUDIT_IDENTITY_KEYS = Object.freeze(["event_ref", "membership_ref", "registration_ref", "client_ref", "subject_ref", "instance_ref"]);
|
|
1300
1587
|
var FAILURE_RESPONSE_KEYS = Object.freeze(["error", "ok", "proof_ref_or_unavailable", "protocol_version", "request_id"]);
|
|
1301
|
-
var MAX_RECOVERY_RETRY_AFTER_MS = 60 * 60 * 1e3;
|
|
1302
1588
|
|
|
1303
1589
|
// host/invocation-authentication-client-command.ts
|
|
1304
1590
|
var MAX_DETAIL_CHARACTERS = 240;
|
|
@@ -1309,8 +1595,8 @@ function hostTerminalFailure(error, component) {
|
|
|
1309
1595
|
if (code === "ENOENT") return { code: "host_dependency_unavailable", message: `A command or file the ${component} Host needs is unavailable: ${detail}`, next_action: "Install the missing dependency or restore the missing file, then rerun this command." };
|
|
1310
1596
|
if (code === "EACCES" || code === "EPERM") return { code: "host_permission_denied", message: `The ${component} Host was refused access to a local path: ${detail}`, next_action: "Repair the ownership or mode of the named path, then rerun this command." };
|
|
1311
1597
|
if (code === "ENOSPC") return { code: "host_storage_unavailable", message: `The ${component} Host could not write local state: ${detail}`, next_action: "Free local disk space, then rerun this command." };
|
|
1312
|
-
if (code !== null && HOST_OWNED_CODE.test(code)) return { code, message: `The ${component} Host command failed: ${detail}`, next_action: `Resolve the named failure above; run \`ceal-host doctor
|
|
1313
|
-
return { code: "host_failure_unclassified", message: `The ${component} Host command failed for a cause it cannot classify: ${detail}`, next_action: `Run \`ceal-host doctor
|
|
1598
|
+
if (code !== null && HOST_OWNED_CODE.test(code)) return { code, message: `The ${component} Host command failed: ${detail}`, next_action: `Resolve the named failure above; run \`ceal-host doctor\` only if it reports installed ${component} Host drift.` };
|
|
1599
|
+
return { code: "host_failure_unclassified", message: `The ${component} Host command failed for a cause it cannot classify: ${detail}`, next_action: `Run \`ceal-host doctor\`; if it reports no drift then the ${component} Host configuration is not the cause, and the message above is the only evidence of what is.` };
|
|
1314
1600
|
}
|
|
1315
1601
|
var DOCTOR_FLAGS = Object.freeze({
|
|
1316
1602
|
"--expected-sha256": "expected_sha256",
|
|
@@ -1326,10 +1612,56 @@ function errorCode(error) {
|
|
|
1326
1612
|
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
1327
1613
|
}
|
|
1328
1614
|
|
|
1329
|
-
// host/
|
|
1330
|
-
import {
|
|
1331
|
-
import {
|
|
1332
|
-
|
|
1615
|
+
// host/service-log.ts
|
|
1616
|
+
import { copyFile, open, stat, truncate } from "node:fs/promises";
|
|
1617
|
+
import { isAbsolute } from "node:path";
|
|
1618
|
+
var HOST_SERVICE_LOG_ENVIRONMENT = "CEAL_HOST_SERVICE_LOG";
|
|
1619
|
+
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
1620
|
+
var HOST_SERVICE_LOG_CHECK_INTERVAL_MS = 6e4;
|
|
1621
|
+
function hostServiceLogPreviousPath(path) {
|
|
1622
|
+
return `${path}.1`;
|
|
1623
|
+
}
|
|
1624
|
+
async function rotateHostServiceLog(path, maxBytes = HOST_SERVICE_LOG_MAX_BYTES) {
|
|
1625
|
+
let size;
|
|
1626
|
+
try {
|
|
1627
|
+
size = (await stat(path)).size;
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
if (isNotFound(error)) return false;
|
|
1630
|
+
throw error;
|
|
1631
|
+
}
|
|
1632
|
+
if (size <= maxBytes) return false;
|
|
1633
|
+
await copyFile(path, hostServiceLogPreviousPath(path));
|
|
1634
|
+
await truncate(path, 0);
|
|
1635
|
+
return true;
|
|
1636
|
+
}
|
|
1637
|
+
function boundHostServiceLog(environment = process.env) {
|
|
1638
|
+
const path = environment[HOST_SERVICE_LOG_ENVIRONMENT];
|
|
1639
|
+
if (path === void 0 || !isAbsolute(path)) return () => void 0;
|
|
1640
|
+
let reported = false;
|
|
1641
|
+
const check = () => {
|
|
1642
|
+
rotateHostServiceLog(path).catch((error) => {
|
|
1643
|
+
if (reported) return;
|
|
1644
|
+
reported = true;
|
|
1645
|
+
process.stderr.write(`[host-service-log] rotation_failed code=${errorCode2(error)}
|
|
1646
|
+
`);
|
|
1647
|
+
});
|
|
1648
|
+
};
|
|
1649
|
+
check();
|
|
1650
|
+
const timer = setInterval(check, HOST_SERVICE_LOG_CHECK_INTERVAL_MS);
|
|
1651
|
+
timer.unref();
|
|
1652
|
+
return () => clearInterval(timer);
|
|
1653
|
+
}
|
|
1654
|
+
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
1655
|
+
function isNotFound(error) {
|
|
1656
|
+
return errorCode2(error) === "ENOENT";
|
|
1657
|
+
}
|
|
1658
|
+
function errorCode2(error) {
|
|
1659
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "unknown";
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
// host/runtime-source-paths.ts
|
|
1663
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1664
|
+
import { posix as posix2, win32 as win322 } from "node:path";
|
|
1333
1665
|
|
|
1334
1666
|
// host/absolute-path.ts
|
|
1335
1667
|
import { posix, win32 } from "node:path";
|
|
@@ -1338,6 +1670,37 @@ function isAbsoluteNormalizedNonRootPath(value, flavor = process.platform, allow
|
|
|
1338
1670
|
return typeof value === "string" && value.length <= 4096 && !value.includes("\0") && pathApi.isAbsolute(value) && pathApi.normalize(value) === value && (allowRoot || pathApi.parse(value).root !== value);
|
|
1339
1671
|
}
|
|
1340
1672
|
|
|
1673
|
+
// host/runtime-source-paths.ts
|
|
1674
|
+
var HOST_SOURCE_ID = /^host-source:([0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12})$/u;
|
|
1675
|
+
function hostSourceUuid(sourceRef) {
|
|
1676
|
+
return HOST_SOURCE_ID.exec(sourceRef)?.[1] ?? null;
|
|
1677
|
+
}
|
|
1678
|
+
function hostRuntimeSourcePaths(stateRoot, sourceRef, platform = process.platform) {
|
|
1679
|
+
const sourceId = hostSourceUuid(sourceRef);
|
|
1680
|
+
if (!isAbsoluteNormalizedNonRootPath(stateRoot, platform) || sourceId === null) throw new TypeError("Host runtime source identity is invalid.");
|
|
1681
|
+
const path = platform === "win32" ? win322 : posix2;
|
|
1682
|
+
const sourcesRoot = path.join(stateRoot, "sources");
|
|
1683
|
+
const sourceRoot = path.join(sourcesRoot, sourceId);
|
|
1684
|
+
const runtimeRoot = path.join(stateRoot, "run");
|
|
1685
|
+
const endpoint = platform === "win32" ? `\\\\.\\pipe\\ceal-host-${createHash2("sha256").update(`${stateRoot}\0${sourceRef}`).digest("hex").slice(0, 24)}` : path.join(runtimeRoot, `${sourceId}.sock`);
|
|
1686
|
+
assertHostRuntimeIpcLength(endpoint, platform);
|
|
1687
|
+
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 };
|
|
1688
|
+
}
|
|
1689
|
+
function assertHostRuntimeIpcLength(endpoint, platform) {
|
|
1690
|
+
if (platform === "win32") return;
|
|
1691
|
+
const maximum = platform === "linux" ? 107 : 103;
|
|
1692
|
+
if (Buffer.byteLength(endpoint, "utf8") > maximum) throw Object.assign(new TypeError(`ipc_endpoint_too_long: Host IPC path exceeds ${maximum} bytes; use a shorter Host state root.`), { code: "ipc_endpoint_too_long" });
|
|
1693
|
+
}
|
|
1694
|
+
function assertHostRelaySourcePaths(stateRoot, config, platform = process.platform) {
|
|
1695
|
+
const paths = hostRuntimeSourcePaths(stateRoot, config.host_source_ref, platform);
|
|
1696
|
+
if (config.state_root !== paths.relayRoot || config.ipc_endpoint !== paths.endpoint) throw new TypeError("Host Relay state and IPC must belong to its configured source.");
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
// host/relay/src/host-relay-runtime.ts
|
|
1700
|
+
import { O_NOFOLLOW as O_NOFOLLOW3, O_RDONLY as O_RDONLY4 } from "node:constants";
|
|
1701
|
+
import { lstat as lstat4, open as open5 } from "node:fs/promises";
|
|
1702
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
1703
|
+
|
|
1341
1704
|
// host/abortable-wait.ts
|
|
1342
1705
|
function waitForAbort(signal, milliseconds) {
|
|
1343
1706
|
if (signal.aborted) return Promise.resolve();
|
|
@@ -1422,7 +1785,7 @@ function skipWhitespace(text, start) {
|
|
|
1422
1785
|
}
|
|
1423
1786
|
|
|
1424
1787
|
// host/relay/src/producer-frame.ts
|
|
1425
|
-
import { createHash as
|
|
1788
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
1426
1789
|
var HOST_PRODUCER_FRAME_SCHEMA = "ceal.host_producer_frame.v1";
|
|
1427
1790
|
var HOST_PRODUCER_MAX_JSON_BYTES = 262144;
|
|
1428
1791
|
var HOST_PRODUCER_FRAME_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
@@ -1468,7 +1831,7 @@ function decodeHostProducerFrame(input) {
|
|
|
1468
1831
|
return value;
|
|
1469
1832
|
}
|
|
1470
1833
|
function hostProducerFrameHash(frame) {
|
|
1471
|
-
return
|
|
1834
|
+
return createHash3("sha256").update(JSON.stringify(frame), "utf8").digest("hex");
|
|
1472
1835
|
}
|
|
1473
1836
|
function hostProducerIdentityRefs(hostSourceRef, frame, localSessionRef, localTurnRef, localInputRef) {
|
|
1474
1837
|
if (!opaqueHostSourceRef(hostSourceRef)) throw new TypeError("Gateway host source reference is invalid.");
|
|
@@ -1589,20 +1952,20 @@ function exactOptional(value, required, optional) {
|
|
|
1589
1952
|
|
|
1590
1953
|
// host/relay/src/relay-admission-store.ts
|
|
1591
1954
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
1592
|
-
import { join as
|
|
1955
|
+
import { join as join4 } from "node:path";
|
|
1593
1956
|
|
|
1594
1957
|
// host/relay/src/durable-atomic-replacement.ts
|
|
1595
1958
|
import { O_RDONLY } from "node:constants";
|
|
1596
|
-
import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
|
|
1959
|
+
import { lstat, mkdir, open as open2, rename, unlink } from "node:fs/promises";
|
|
1597
1960
|
import { dirname } from "node:path";
|
|
1598
1961
|
var DEFAULT_PORT = {
|
|
1599
1962
|
platform: process.platform,
|
|
1600
1963
|
mkdir: (path, options) => mkdir(path, options),
|
|
1601
1964
|
lstat: (path) => lstat(path),
|
|
1602
|
-
open_file: (path) =>
|
|
1965
|
+
open_file: (path) => open2(path, "wx", 384),
|
|
1603
1966
|
rename,
|
|
1604
1967
|
unlink,
|
|
1605
|
-
open_directory: (path) =>
|
|
1968
|
+
open_directory: (path) => open2(path, O_RDONLY),
|
|
1606
1969
|
unique_ref: () => `${process.pid}.${crypto.randomUUID()}`
|
|
1607
1970
|
};
|
|
1608
1971
|
async function durableAtomicReplace(target, data, port = DEFAULT_PORT) {
|
|
@@ -1662,7 +2025,7 @@ async function removeTemporary(path, port, failures) {
|
|
|
1662
2025
|
try {
|
|
1663
2026
|
await port.unlink(path);
|
|
1664
2027
|
} catch (error) {
|
|
1665
|
-
if (
|
|
2028
|
+
if (errorCode3(error) !== "ENOENT") failures.push(error);
|
|
1666
2029
|
}
|
|
1667
2030
|
}
|
|
1668
2031
|
function throwFailures(failures) {
|
|
@@ -1670,16 +2033,255 @@ function throwFailures(failures) {
|
|
|
1670
2033
|
if (failures.length > 1) throw new AggregateError(failures, "Host Relay durable replacement and cleanup failed.");
|
|
1671
2034
|
}
|
|
1672
2035
|
function directorySyncUnsupported(error) {
|
|
1673
|
-
return ["EINVAL", "ENOSYS", "ENOTSUP"].includes(
|
|
2036
|
+
return ["EINVAL", "ENOSYS", "ENOTSUP"].includes(errorCode3(error) ?? "");
|
|
1674
2037
|
}
|
|
1675
|
-
function
|
|
2038
|
+
function errorCode3(error) {
|
|
1676
2039
|
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
1677
2040
|
}
|
|
1678
2041
|
|
|
2042
|
+
// host/relay/src/relay-request-binding-store.ts
|
|
2043
|
+
import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
|
|
2044
|
+
import { open as open3 } from "node:fs/promises";
|
|
2045
|
+
import { join as join2 } from "node:path";
|
|
2046
|
+
var HOST_RELAY_REQUEST_BINDING_SCHEMA = "ceal.host_relay_request_binding.v1";
|
|
2047
|
+
var STATE_SCHEMA = "ceal.host_relay_request_binding_state.v1";
|
|
2048
|
+
var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2049
|
+
var MAX_BINDINGS = 8192;
|
|
2050
|
+
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
2051
|
+
var HostRelayBindingError = class extends TypeError {
|
|
2052
|
+
name = "HostRelayBindingError";
|
|
2053
|
+
code;
|
|
2054
|
+
constructor(code, message) {
|
|
2055
|
+
super(message);
|
|
2056
|
+
this.code = code;
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
2059
|
+
var RelayRequestBindingStore = class {
|
|
2060
|
+
tail = Promise.resolve();
|
|
2061
|
+
bindingWaiters = /* @__PURE__ */ new Set();
|
|
2062
|
+
root;
|
|
2063
|
+
constructor(root) {
|
|
2064
|
+
if (typeof root !== "string" || root.length === 0) throw new TypeError("Host Relay binding root is invalid.");
|
|
2065
|
+
this.root = root;
|
|
2066
|
+
}
|
|
2067
|
+
bind(input, now = /* @__PURE__ */ new Date()) {
|
|
2068
|
+
const run = this.tail.then(() => this.bindLocked(input, now));
|
|
2069
|
+
this.tail = run.then(() => void 0, () => void 0);
|
|
2070
|
+
return run;
|
|
2071
|
+
}
|
|
2072
|
+
query(input) {
|
|
2073
|
+
const run = this.tail.then(() => this.queryLocked(input));
|
|
2074
|
+
this.tail = run.then(() => void 0, () => void 0);
|
|
2075
|
+
return run;
|
|
2076
|
+
}
|
|
2077
|
+
waitForBinding(input, signal) {
|
|
2078
|
+
validateQuery(input);
|
|
2079
|
+
if (signal.aborted) return Promise.reject(new Error("Host Relay binding wait cancelled."));
|
|
2080
|
+
return new Promise((resolve, reject) => {
|
|
2081
|
+
let settled = false;
|
|
2082
|
+
const finish = (result2, error) => {
|
|
2083
|
+
if (settled) return;
|
|
2084
|
+
settled = true;
|
|
2085
|
+
this.bindingWaiters.delete(waiter);
|
|
2086
|
+
signal.removeEventListener("abort", waiter.abort);
|
|
2087
|
+
if (error !== void 0) reject(error);
|
|
2088
|
+
else if (result2?.status === "ready") resolve(result2);
|
|
2089
|
+
};
|
|
2090
|
+
const abort = () => finish(void 0, new Error("Host Relay binding wait cancelled."));
|
|
2091
|
+
const waiter = { query: input, resolve: (result2) => finish(result2), abort };
|
|
2092
|
+
this.bindingWaiters.add(waiter);
|
|
2093
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
2094
|
+
void this.query(input).then((result2) => {
|
|
2095
|
+
if (result2.status === "ready") finish(result2);
|
|
2096
|
+
}, (error) => finish(void 0, error));
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
async bindLocked(input, now) {
|
|
2100
|
+
const candidate = bindingFromGateway(input, now);
|
|
2101
|
+
const state = await this.read();
|
|
2102
|
+
const existing = state.bindings.find((item) => sameCanonicalIdentity(item, candidate));
|
|
2103
|
+
if (existing !== void 0) {
|
|
2104
|
+
if (!sameBinding(existing, candidate)) throw new HostRelayBindingError("canonical_turn_rebound", `Host Relay canonical turn is already bound to another Request: ${refusalFacts([{ name: "candidate_request_ref", value: candidate.request_ref, expected: existing.request_ref }, { name: "candidate_prompt_ref", value: candidate.prompt_ref, expected: existing.prompt_ref }, { name: "candidate_context_expires_at", value: candidate.context_expires_at, expected: String(existing.context_expires_at) }, { name: "candidate_local_turn_aliases", value: candidate.local_turn_aliases, expected: JSON.stringify(existing.local_turn_aliases) }])}.`);
|
|
2105
|
+
return existing;
|
|
2106
|
+
}
|
|
2107
|
+
if (state.bindings.length >= MAX_BINDINGS) throw new HostRelayBindingError("binding_capacity_exhausted", `Host Relay request binding capacity is exhausted: ${refusalFact("binding_count", state.bindings.length, `below ${MAX_BINDINGS}`)}.`);
|
|
2108
|
+
const next = { schema_version: STATE_SCHEMA, bindings: [...state.bindings, candidate] };
|
|
2109
|
+
await this.write(next);
|
|
2110
|
+
this.resolveBindingWaiters(candidate);
|
|
2111
|
+
return structuredClone(candidate);
|
|
2112
|
+
}
|
|
2113
|
+
resolveBindingWaiters(binding) {
|
|
2114
|
+
for (const waiter of this.bindingWaiters) {
|
|
2115
|
+
if (relayRequestBindingMatchesQuery(waiter.query, binding)) waiter.resolve({ status: "ready", binding: structuredClone(binding) });
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
async queryLocked(input) {
|
|
2119
|
+
validateQuery(input);
|
|
2120
|
+
const state = await this.read();
|
|
2121
|
+
const matches = state.bindings.filter((binding2) => relayRequestBindingMatchesQuery(input, binding2));
|
|
2122
|
+
if (matches.length === 0) return { status: "no_binding" };
|
|
2123
|
+
const binding = matches[0];
|
|
2124
|
+
if (binding === void 0) throw new TypeError("Host Relay binding query result is invalid.");
|
|
2125
|
+
return { status: "ready", binding: structuredClone(binding) };
|
|
2126
|
+
}
|
|
2127
|
+
async read() {
|
|
2128
|
+
let handle;
|
|
2129
|
+
try {
|
|
2130
|
+
handle = await open3(join2(this.root, "request-bindings.json"), O_RDONLY2 | O_NOFOLLOW);
|
|
2131
|
+
const stats = await handle.stat();
|
|
2132
|
+
if (!stats.isFile() || !ownerPrivate2(stats)) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFacts([{ name: "state_is_file", value: stats.isFile(), expected: "true" }, { name: "state_uid", value: stats.uid, expected: String(process.geteuid?.() ?? stats.uid) }, { name: "state_mode", value: (stats.mode & 511).toString(8), expected: "no group or other permission bits" }])}.`);
|
|
2133
|
+
if (stats.size > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFact("state_bytes", stats.size, `at most ${MAX_STATE_BYTES}`)}.`);
|
|
2134
|
+
return decodeState(JSON.parse(await handle.readFile("utf8")));
|
|
2135
|
+
} catch (error) {
|
|
2136
|
+
if (errorCode4(error) === "ENOENT") return { schema_version: STATE_SCHEMA, bindings: [] };
|
|
2137
|
+
throw error;
|
|
2138
|
+
} finally {
|
|
2139
|
+
await handle?.close();
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
async write(state) {
|
|
2143
|
+
const encoded = JSON.stringify(state);
|
|
2144
|
+
if (Buffer.byteLength(encoded, "utf8") > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is too large: ${refusalFact("state_bytes", Buffer.byteLength(encoded, "utf8"), `at most ${MAX_STATE_BYTES}`)}.`);
|
|
2145
|
+
await durableAtomicReplace(join2(this.root, "request-bindings.json"), encoded);
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
function relayRequestWorkContextFromBinding(binding) {
|
|
2149
|
+
return createRelayRequestWorkContext({ request_ref: binding.request_ref, host_binding: {
|
|
2150
|
+
host_source_ref: binding.host_source_ref,
|
|
2151
|
+
host_session_ref: binding.host_session_ref,
|
|
2152
|
+
host_turn_ref: binding.host_turn_ref,
|
|
2153
|
+
host_input_ref: binding.host_input_ref,
|
|
2154
|
+
prompt_ref: binding.prompt_ref
|
|
2155
|
+
} });
|
|
2156
|
+
}
|
|
2157
|
+
function createRelayRequestWorkContext(input) {
|
|
2158
|
+
if (!record2(input) || !exactKeys3(input, ["request_ref", "host_binding"]) || !safeRef2(input.request_ref) || !record2(input.host_binding)) {
|
|
2159
|
+
throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
|
|
2160
|
+
}
|
|
2161
|
+
const hostBinding = input.host_binding;
|
|
2162
|
+
if (!exactKeys3(hostBinding, ["host_source_ref", "host_session_ref", "host_turn_ref", "host_input_ref", "prompt_ref"]) || ![hostBinding.host_source_ref, hostBinding.host_session_ref, hostBinding.host_turn_ref, hostBinding.host_input_ref, hostBinding.prompt_ref].every(safeRef2)) {
|
|
2163
|
+
throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
|
|
2164
|
+
}
|
|
2165
|
+
const hostSourceRef = requiredRef(hostBinding.host_source_ref);
|
|
2166
|
+
const hostSessionRef = requiredRef(hostBinding.host_session_ref);
|
|
2167
|
+
const hostTurnRef = requiredRef(hostBinding.host_turn_ref);
|
|
2168
|
+
const hostInputRef = requiredRef(hostBinding.host_input_ref);
|
|
2169
|
+
const promptRef = requiredRef(hostBinding.prompt_ref);
|
|
2170
|
+
return Object.freeze({ request_ref: input.request_ref, host_binding: Object.freeze({
|
|
2171
|
+
host_source_ref: hostSourceRef,
|
|
2172
|
+
host_session_ref: hostSessionRef,
|
|
2173
|
+
host_turn_ref: hostTurnRef,
|
|
2174
|
+
host_input_ref: hostInputRef,
|
|
2175
|
+
prompt_ref: promptRef
|
|
2176
|
+
}) });
|
|
2177
|
+
}
|
|
2178
|
+
function bindingFromGateway(input, now) {
|
|
2179
|
+
const payload = input.frame.payload;
|
|
2180
|
+
const candidate = {
|
|
2181
|
+
schema_version: HOST_RELAY_REQUEST_BINDING_SCHEMA,
|
|
2182
|
+
producer_id: input.frame.producer_id,
|
|
2183
|
+
producer_installation_ref: input.frame.producer_installation_ref,
|
|
2184
|
+
local_session_ref: requiredRef(payload.local_session_ref),
|
|
2185
|
+
local_turn_ref: requiredRef(payload.local_turn_ref),
|
|
2186
|
+
local_turn_aliases: optionalRefs2(payload.local_turn_aliases),
|
|
2187
|
+
prompt_ref: requiredRef(input.prompt_ref),
|
|
2188
|
+
request_ref: requiredRef(input.request_ref),
|
|
2189
|
+
bound_at: timestamp2(now.toISOString()),
|
|
2190
|
+
...payload.context_expires_at === void 0 ? {} : { context_expires_at: timestamp2(payload.context_expires_at) },
|
|
2191
|
+
host_source_ref: requiredRef(input.host_binding.host_source_ref),
|
|
2192
|
+
host_session_ref: requiredRef(input.host_binding.host_session_ref),
|
|
2193
|
+
host_turn_ref: requiredRef(input.host_binding.host_turn_ref),
|
|
2194
|
+
host_input_ref: requiredRef(input.host_binding.host_input_ref)
|
|
2195
|
+
};
|
|
2196
|
+
if (input.host_binding.prompt_ref !== input.prompt_ref) throw new HostRelayBindingError("context_identity_mismatch", "Host Relay binding prompt reference does not match the acknowledged submission.");
|
|
2197
|
+
validateBinding(candidate);
|
|
2198
|
+
return candidate;
|
|
2199
|
+
}
|
|
2200
|
+
function decodeState(value) {
|
|
2201
|
+
if (!record2(value) || !exactKeys3(value, ["schema_version", "bindings"])) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("state_keys", record2(value) ? Object.keys(value) : [], "exactly schema_version and bindings")}.`);
|
|
2202
|
+
if (value.schema_version !== STATE_SCHEMA) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("schema_version", value.schema_version, STATE_SCHEMA)}.`);
|
|
2203
|
+
if (!Array.isArray(value.bindings)) throw new TypeError(`Host Relay binding state is invalid: ${refusalFact("bindings", value.bindings, "an array")}.`);
|
|
2204
|
+
const bindings = value.bindings.map((item) => {
|
|
2205
|
+
validateBinding(item);
|
|
2206
|
+
return item;
|
|
2207
|
+
});
|
|
2208
|
+
const keys = bindings.map(bindingKey);
|
|
2209
|
+
if (new Set(keys).size !== keys.length) throw new TypeError("Host Relay binding state contains duplicate canonical turns.");
|
|
2210
|
+
return { schema_version: STATE_SCHEMA, bindings };
|
|
2211
|
+
}
|
|
2212
|
+
function validateBinding(value) {
|
|
2213
|
+
if (!record2(value) || !exactOptionalKeys(value, ["schema_version", "producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref", "local_turn_aliases", "prompt_ref", "request_ref", "bound_at", "host_source_ref", "host_session_ref", "host_turn_ref", "host_input_ref"], ["context_expires_at"]) || !validBindingIdentity(value) || !validBindingRequest(value)) throw new TypeError("Host Relay request binding is invalid.");
|
|
2214
|
+
}
|
|
2215
|
+
function validBindingIdentity(value) {
|
|
2216
|
+
return value.schema_version === HOST_RELAY_REQUEST_BINDING_SCHEMA && producer(value.producer_id) && [value.producer_installation_ref, value.local_session_ref, value.local_turn_ref].every(safeRef2) && refList(value.local_turn_aliases);
|
|
2217
|
+
}
|
|
2218
|
+
function validBindingRequest(value) {
|
|
2219
|
+
const tuple = [value.host_source_ref, value.host_session_ref, value.host_turn_ref, value.host_input_ref];
|
|
2220
|
+
return safeRef2(value.prompt_ref) && safeRef2(value.request_ref) && validTimestamp(value.bound_at) && (value.context_expires_at === void 0 || validTimestamp(value.context_expires_at)) && tuple.every(safeRef2);
|
|
2221
|
+
}
|
|
2222
|
+
function validateQuery(value) {
|
|
2223
|
+
if (!record2(value) || !exactKeys3(value, ["producer_id", "producer_installation_ref", "local_session_ref", "local_turn_ref"]) || !producer(value.producer_id) || !safeRef2(value.producer_installation_ref) || !safeRef2(value.local_session_ref) || !safeRef2(value.local_turn_ref)) throw new TypeError("Host Relay binding query is invalid.");
|
|
2224
|
+
}
|
|
2225
|
+
function relayRequestBindingMatchesQuery(query, binding) {
|
|
2226
|
+
return query.producer_id === binding.producer_id && query.local_session_ref === binding.local_session_ref && (query.local_turn_ref === binding.local_turn_ref || binding.local_turn_aliases.includes(query.local_turn_ref)) && query.producer_installation_ref === binding.producer_installation_ref;
|
|
2227
|
+
}
|
|
2228
|
+
function sameCanonicalIdentity(left, right) {
|
|
2229
|
+
return bindingKey(left) === bindingKey(right);
|
|
2230
|
+
}
|
|
2231
|
+
function sameBinding(left, right) {
|
|
2232
|
+
return left.request_ref === right.request_ref && left.prompt_ref === right.prompt_ref && left.context_expires_at === right.context_expires_at && left.host_source_ref === right.host_source_ref && left.host_session_ref === right.host_session_ref && left.host_turn_ref === right.host_turn_ref && left.host_input_ref === right.host_input_ref && JSON.stringify(left.local_turn_aliases) === JSON.stringify(right.local_turn_aliases);
|
|
2233
|
+
}
|
|
2234
|
+
function bindingKey(value) {
|
|
2235
|
+
return `${value.producer_id}\0${value.producer_installation_ref}\0${value.local_session_ref}\0${value.local_turn_ref}`;
|
|
2236
|
+
}
|
|
2237
|
+
function requiredRef(value) {
|
|
2238
|
+
if (!safeRef2(value)) throw new TypeError("Host Relay binding reference is invalid.");
|
|
2239
|
+
return value;
|
|
2240
|
+
}
|
|
2241
|
+
function optionalRefs2(value) {
|
|
2242
|
+
if (value === void 0) return [];
|
|
2243
|
+
if (!refList(value)) throw new TypeError("Host Relay binding aliases are invalid.");
|
|
2244
|
+
return [...value];
|
|
2245
|
+
}
|
|
2246
|
+
function timestamp2(value) {
|
|
2247
|
+
if (!validTimestamp(value)) throw new TypeError("Host Relay binding timestamp is invalid.");
|
|
2248
|
+
return value;
|
|
2249
|
+
}
|
|
2250
|
+
function producer(value) {
|
|
2251
|
+
return value === "codex" || value === "claude";
|
|
2252
|
+
}
|
|
2253
|
+
function safeRef2(value) {
|
|
2254
|
+
return typeof value === "string" && SAFE_REF3.test(value);
|
|
2255
|
+
}
|
|
2256
|
+
function refList(value) {
|
|
2257
|
+
return Array.isArray(value) && value.length <= 16 && value.every(safeRef2) && new Set(value).size === value.length;
|
|
2258
|
+
}
|
|
2259
|
+
function validTimestamp(value) {
|
|
2260
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
2261
|
+
}
|
|
2262
|
+
function record2(value) {
|
|
2263
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2264
|
+
}
|
|
2265
|
+
function exactKeys3(value, keys) {
|
|
2266
|
+
return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
2267
|
+
}
|
|
2268
|
+
function exactOptionalKeys(value, required, optional) {
|
|
2269
|
+
const keys = Object.keys(value);
|
|
2270
|
+
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2271
|
+
}
|
|
2272
|
+
function errorCode4(error) {
|
|
2273
|
+
return record2(error) && typeof error.code === "string" ? error.code : null;
|
|
2274
|
+
}
|
|
2275
|
+
function ownerPrivate2(stats) {
|
|
2276
|
+
if (process.platform === "win32") return true;
|
|
2277
|
+
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
2278
|
+
return stats.uid === uid && (stats.mode & 63) === 0;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
1679
2281
|
// host/relay/src/relay-quarantine-store.ts
|
|
1680
|
-
import { createHash as
|
|
2282
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
1681
2283
|
import { readFile } from "node:fs/promises";
|
|
1682
|
-
import { join as
|
|
2284
|
+
import { join as join3 } from "node:path";
|
|
1683
2285
|
var RELAY_QUARANTINE_SCHEMA = "ceal.host_relay_quarantine_state.v1";
|
|
1684
2286
|
var RELAY_QUARANTINE_MAX_RECORDS = 512;
|
|
1685
2287
|
var RELAY_QUARANTINE_MAX_BYTES = 16 * 1024 * 1024;
|
|
@@ -1720,27 +2322,27 @@ var RelayQuarantineStore = class {
|
|
|
1720
2322
|
const raw = Buffer.from(rawFrame).subarray(0, MAX_CAPTURE_BYTES);
|
|
1721
2323
|
const state = await this.readState();
|
|
1722
2324
|
const record6 = {
|
|
1723
|
-
quarantine_ref: `quarantine:${
|
|
2325
|
+
quarantine_ref: `quarantine:${createHash4("sha256").update(`${producerId}\0${reason}\0${capturedAt}\0`).update(raw).digest("hex")}`,
|
|
1724
2326
|
producer_id: producerId,
|
|
1725
2327
|
reason,
|
|
1726
2328
|
captured_at: capturedAt,
|
|
1727
|
-
frame_sha256:
|
|
2329
|
+
frame_sha256: createHash4("sha256").update(raw).digest("hex"),
|
|
1728
2330
|
charged_bytes: raw.byteLength,
|
|
1729
2331
|
raw_frame_base64: raw.toString("base64")
|
|
1730
2332
|
};
|
|
1731
2333
|
const records = state.producers[producerId];
|
|
1732
2334
|
records.push(record6);
|
|
1733
2335
|
while (records.length > this.maxRecords || totalBytes(records) > this.maxBytes) records.shift();
|
|
1734
|
-
await durableAtomicReplace(
|
|
2336
|
+
await durableAtomicReplace(join3(this.root, "quarantine.json"), JSON.stringify(state));
|
|
1735
2337
|
});
|
|
1736
2338
|
this.tail = run.then(() => void 0, () => void 0);
|
|
1737
2339
|
await run;
|
|
1738
2340
|
}
|
|
1739
2341
|
async readState() {
|
|
1740
2342
|
try {
|
|
1741
|
-
return
|
|
2343
|
+
return decodeState2(JSON.parse(await readFile(join3(this.root, "quarantine.json"), "utf8")));
|
|
1742
2344
|
} catch (error) {
|
|
1743
|
-
if (
|
|
2345
|
+
if (errorCode5(error) === "ENOENT") return emptyState();
|
|
1744
2346
|
throw error;
|
|
1745
2347
|
}
|
|
1746
2348
|
}
|
|
@@ -1751,9 +2353,9 @@ function emptyState() {
|
|
|
1751
2353
|
function totalBytes(records) {
|
|
1752
2354
|
return records.reduce((total, record6) => total + record6.charged_bytes, 0);
|
|
1753
2355
|
}
|
|
1754
|
-
function
|
|
1755
|
-
if (!
|
|
1756
|
-
if (!
|
|
2356
|
+
function decodeState2(value) {
|
|
2357
|
+
if (!record3(value) || value.schema_version !== RELAY_QUARANTINE_SCHEMA) throw new TypeError(`Host Relay quarantine state is invalid: ${refusalFact("schema_version", record3(value) ? value.schema_version : void 0, RELAY_QUARANTINE_SCHEMA)}.`);
|
|
2358
|
+
if (!record3(value.producers)) throw new TypeError(`Host Relay quarantine state is invalid: ${refusalFact("producer_ids", record3(value.producers) ? Object.keys(value.producers) : [], `the ${PRODUCERS2.length} quarantine producer buckets`)}.`);
|
|
1757
2359
|
const state = emptyState();
|
|
1758
2360
|
for (const producerId of PRODUCERS2) {
|
|
1759
2361
|
const records = value.producers[producerId];
|
|
@@ -1763,7 +2365,7 @@ function decodeState(value) {
|
|
|
1763
2365
|
return state;
|
|
1764
2366
|
}
|
|
1765
2367
|
function validRecord(value) {
|
|
1766
|
-
if (!
|
|
2368
|
+
if (!record3(value) || typeof value.quarantine_ref !== "string" || !quarantineProducer(value.producer_id) || typeof value.reason !== "string" || !Number.isFinite(Date.parse(String(value.captured_at))) || typeof value.frame_sha256 !== "string" || typeof value.charged_bytes !== "number" || !Number.isSafeInteger(value.charged_bytes) || value.charged_bytes < 0 || typeof value.raw_frame_base64 !== "string") return false;
|
|
1767
2369
|
return true;
|
|
1768
2370
|
}
|
|
1769
2371
|
function quarantineProducer(value) {
|
|
@@ -1772,16 +2374,16 @@ function quarantineProducer(value) {
|
|
|
1772
2374
|
function inferProducer(rawFrame) {
|
|
1773
2375
|
try {
|
|
1774
2376
|
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.from(rawFrame).subarray(4)));
|
|
1775
|
-
return
|
|
2377
|
+
return record3(value) && (value.producer_id === "codex" || value.producer_id === "claude") ? value.producer_id : "unknown";
|
|
1776
2378
|
} catch {
|
|
1777
2379
|
return "unknown";
|
|
1778
2380
|
}
|
|
1779
2381
|
}
|
|
1780
|
-
function
|
|
2382
|
+
function record3(value) {
|
|
1781
2383
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1782
2384
|
}
|
|
1783
|
-
function
|
|
1784
|
-
return
|
|
2385
|
+
function errorCode5(error) {
|
|
2386
|
+
return record3(error) && typeof error.code === "string" ? error.code : null;
|
|
1785
2387
|
}
|
|
1786
2388
|
|
|
1787
2389
|
// host/relay/src/relay-admission-store.ts
|
|
@@ -1791,11 +2393,12 @@ var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
|
1791
2393
|
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
1792
2394
|
var RelayAdmissionStore = class {
|
|
1793
2395
|
tail = Promise.resolve();
|
|
2396
|
+
drainWaiters = /* @__PURE__ */ new Set();
|
|
1794
2397
|
root;
|
|
1795
2398
|
quarantine;
|
|
1796
2399
|
limits;
|
|
1797
2400
|
faultHooks;
|
|
1798
|
-
constructor(root, limits = DEFAULT_LIMITS, faultHooks = {}, quarantine = new RelayQuarantineStore(
|
|
2401
|
+
constructor(root, limits = DEFAULT_LIMITS, faultHooks = {}, quarantine = new RelayQuarantineStore(join4(root, "quarantine"))) {
|
|
1799
2402
|
this.root = root;
|
|
1800
2403
|
this.quarantine = quarantine;
|
|
1801
2404
|
this.limits = limits;
|
|
@@ -1810,10 +2413,36 @@ var RelayAdmissionStore = class {
|
|
|
1810
2413
|
await this.tail;
|
|
1811
2414
|
return this.read();
|
|
1812
2415
|
}
|
|
2416
|
+
async hasPendingRequestBinding(query) {
|
|
2417
|
+
const state = await this.read();
|
|
2418
|
+
return state.entries.some((entry2) => pendingFrameMatchesBindingQuery(entry2, query));
|
|
2419
|
+
}
|
|
1813
2420
|
async drainRound(deliver, options) {
|
|
1814
2421
|
const run = this.tail.then(() => this.drainLocked(deliver, options));
|
|
1815
2422
|
this.tail = run.then(() => void 0, () => void 0);
|
|
1816
|
-
|
|
2423
|
+
const result2 = await run;
|
|
2424
|
+
if (result2.delivered.length > 0 || result2.refused.length > 0 || result2.retained.length > 0) this.resolveDrainWaiters();
|
|
2425
|
+
return result2;
|
|
2426
|
+
}
|
|
2427
|
+
waitForDrainChange(signal) {
|
|
2428
|
+
if (signal.aborted) return Promise.reject(new Error("Host Relay admission wait cancelled."));
|
|
2429
|
+
return new Promise((resolve, reject) => {
|
|
2430
|
+
let settled = false;
|
|
2431
|
+
const finish = (error) => {
|
|
2432
|
+
if (settled) return;
|
|
2433
|
+
settled = true;
|
|
2434
|
+
this.drainWaiters.delete(waiter);
|
|
2435
|
+
signal.removeEventListener("abort", waiter.abort);
|
|
2436
|
+
if (error !== void 0) reject(error);
|
|
2437
|
+
else resolve();
|
|
2438
|
+
};
|
|
2439
|
+
const waiter = { resolve: () => finish(), abort: () => finish(new Error("Host Relay admission wait cancelled.")) };
|
|
2440
|
+
this.drainWaiters.add(waiter);
|
|
2441
|
+
signal.addEventListener("abort", waiter.abort, { once: true });
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
resolveDrainWaiters() {
|
|
2445
|
+
for (const waiter of this.drainWaiters) waiter.resolve();
|
|
1817
2446
|
}
|
|
1818
2447
|
async admitLocked(frame) {
|
|
1819
2448
|
const state = await this.read();
|
|
@@ -1870,308 +2499,109 @@ var RelayAdmissionStore = class {
|
|
|
1870
2499
|
if (!installation2) throw new TypeError("Host Relay delivery installation is unavailable.");
|
|
1871
2500
|
if (outcome.status === "refused" || exhausted) {
|
|
1872
2501
|
await this.quarantine.retainFrame(head.frame, "gateway_refused");
|
|
1873
|
-
installation2.last_refusal = { sequence: head.sequence, submission_ref: input.submission_ref, ...refusalCause(outcome, head), refused_at_ms: options.now_ms };
|
|
1874
|
-
refused.push(input.submission_ref);
|
|
1875
|
-
} else {
|
|
1876
|
-
await options.after_gateway_accept_before_delivery_fsync?.({ fault_point: "after_gateway_accept_before_delivery_fsync", delivery: structuredClone(input) });
|
|
1877
|
-
delivered.push(input.submission_ref);
|
|
1878
|
-
}
|
|
1879
|
-
state.entries.splice(state.entries.indexOf(head), 1);
|
|
1880
|
-
installation2.delivered_through_sequence = Math.max(installation2.delivered_through_sequence, head.sequence);
|
|
1881
|
-
await this.write(state);
|
|
1882
|
-
}
|
|
1883
|
-
return { delivered, retained, refused };
|
|
1884
|
-
}
|
|
1885
|
-
async read() {
|
|
1886
|
-
try {
|
|
1887
|
-
return decodeState2(JSON.parse(await readFile2(join3(this.root, "admission.json"), "utf8")));
|
|
1888
|
-
} catch (error) {
|
|
1889
|
-
if (errorCode4(error) === "ENOENT") return { schema_version: "ceal.host_relay_admission_state.v1", installations: {}, entries: [] };
|
|
1890
|
-
throw error;
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
async write(state) {
|
|
1894
|
-
await durableAtomicReplace(join3(this.root, "admission.json"), JSON.stringify(state));
|
|
1895
|
-
}
|
|
1896
|
-
};
|
|
1897
|
-
function installationKey(frame) {
|
|
1898
|
-
return `${frame.producer_id}\0${frame.producer_installation_ref}`;
|
|
1899
|
-
}
|
|
1900
|
-
function replayCode(existing, hash, sequence, installation2) {
|
|
1901
|
-
if (existing !== void 0) return existing.hash === hash ? "already_admitted" : "frame_identity_conflict";
|
|
1902
|
-
return sequence <= installation2.delivered_through_sequence ? "already_admitted" : null;
|
|
1903
|
-
}
|
|
1904
|
-
function bytes(entries) {
|
|
1905
|
-
return entries.reduce((total, entry2) => total + entry2.charged_bytes, 0);
|
|
1906
|
-
}
|
|
1907
|
-
function retryExhausted(outcome, entry2) {
|
|
1908
|
-
return outcome.status === "retryable" && entry2.retry_attempts >= MAX_RETRYABLE_ATTEMPTS;
|
|
1909
|
-
}
|
|
1910
|
-
function refusalCause(outcome, entry2) {
|
|
1911
|
-
if (outcome.status === "refused") return { code: outcome.code, message: outcome.message };
|
|
1912
|
-
const reason = outcome.status === "retryable" ? outcome.reason ?? "no reason reported" : "no reason reported";
|
|
1913
|
-
return { code: "retry_exhausted", message: `Gateway delivery stayed retryable through ${entry2.retry_attempts} attempts: ${reason}` };
|
|
1914
|
-
}
|
|
1915
|
-
function scheduleRetry(entry2, nowMs, reason) {
|
|
1916
|
-
if (reason === void 0) delete entry2.retry_reason;
|
|
1917
|
-
else entry2.retry_reason = reason;
|
|
1918
|
-
entry2.retry_attempts += 1;
|
|
1919
|
-
entry2.retry_at_ms = nowMs + 1e3 * (RETRY_SECONDS[Math.min(entry2.retry_attempts - 1, RETRY_SECONDS.length - 1)] ?? 60);
|
|
1920
|
-
}
|
|
1921
|
-
function result(code, state) {
|
|
1922
|
-
return { code, admitted_through_sequence: state.admitted_through_sequence, delivered_through_sequence: state.delivered_through_sequence };
|
|
1923
|
-
}
|
|
1924
|
-
function errorCode4(error) {
|
|
1925
|
-
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
1926
|
-
}
|
|
1927
|
-
function decodeState2(value) {
|
|
1928
|
-
if (!stateRecord(value)) throw new TypeError("Host Relay admission state is invalid.");
|
|
1929
|
-
const installations = {};
|
|
1930
|
-
for (const [key, item] of Object.entries(value.installations)) {
|
|
1931
|
-
if (!installation(item)) throw new TypeError("Host Relay installation state is invalid.");
|
|
1932
|
-
installations[key] = item;
|
|
1933
|
-
}
|
|
1934
|
-
const entries = [];
|
|
1935
|
-
for (const item of value.entries) {
|
|
1936
|
-
if (!entry(item)) throw new TypeError("Host Relay entry state is invalid.");
|
|
1937
|
-
entries.push(item);
|
|
1938
|
-
}
|
|
1939
|
-
return { schema_version: "ceal.host_relay_admission_state.v1", installations, entries };
|
|
1940
|
-
}
|
|
1941
|
-
function stateRecord(value) {
|
|
1942
|
-
return object(value) && value.schema_version === "ceal.host_relay_admission_state.v1" && object(value.installations) && Array.isArray(value.entries);
|
|
1943
|
-
}
|
|
1944
|
-
function installation(value) {
|
|
1945
|
-
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));
|
|
1946
|
-
}
|
|
1947
|
-
function refusalRecord(value) {
|
|
1948
|
-
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);
|
|
1949
|
-
}
|
|
1950
|
-
function entry(value) {
|
|
1951
|
-
return object(value) && producer(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);
|
|
1952
|
-
}
|
|
1953
|
-
function producer(value) {
|
|
1954
|
-
return value === "codex" || value === "claude";
|
|
1955
|
-
}
|
|
1956
|
-
function object(value) {
|
|
1957
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
// host/relay/src/relay-gateway-bridge.ts
|
|
1961
|
-
import { readFile as readFile3 } from "node:fs/promises";
|
|
1962
|
-
import { join as join5 } from "node:path";
|
|
1963
|
-
|
|
1964
|
-
// host/relay/src/relay-request-binding-store.ts
|
|
1965
|
-
import { O_NOFOLLOW, O_RDONLY as O_RDONLY2 } from "node:constants";
|
|
1966
|
-
import { open as open2 } from "node:fs/promises";
|
|
1967
|
-
import { join as join4 } from "node:path";
|
|
1968
|
-
var HOST_RELAY_REQUEST_BINDING_SCHEMA = "ceal.host_relay_request_binding.v1";
|
|
1969
|
-
var STATE_SCHEMA = "ceal.host_relay_request_binding_state.v1";
|
|
1970
|
-
var SAFE_REF3 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
1971
|
-
var MAX_BINDINGS = 8192;
|
|
1972
|
-
var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
1973
|
-
var HostRelayBindingError = class extends TypeError {
|
|
1974
|
-
name = "HostRelayBindingError";
|
|
1975
|
-
code;
|
|
1976
|
-
constructor(code, message) {
|
|
1977
|
-
super(message);
|
|
1978
|
-
this.code = code;
|
|
1979
|
-
}
|
|
1980
|
-
};
|
|
1981
|
-
var RelayRequestBindingStore = class {
|
|
1982
|
-
tail = Promise.resolve();
|
|
1983
|
-
root;
|
|
1984
|
-
constructor(root) {
|
|
1985
|
-
if (typeof root !== "string" || root.length === 0) throw new TypeError("Host Relay binding root is invalid.");
|
|
1986
|
-
this.root = root;
|
|
1987
|
-
}
|
|
1988
|
-
bind(input, now = /* @__PURE__ */ new Date()) {
|
|
1989
|
-
const run = this.tail.then(() => this.bindLocked(input, now));
|
|
1990
|
-
this.tail = run.then(() => void 0, () => void 0);
|
|
1991
|
-
return run;
|
|
1992
|
-
}
|
|
1993
|
-
query(input) {
|
|
1994
|
-
const run = this.tail.then(() => this.queryLocked(input));
|
|
1995
|
-
this.tail = run.then(() => void 0, () => void 0);
|
|
1996
|
-
return run;
|
|
1997
|
-
}
|
|
1998
|
-
async bindLocked(input, now) {
|
|
1999
|
-
const candidate = bindingFromGateway(input, now);
|
|
2000
|
-
const state = await this.read();
|
|
2001
|
-
const existing = state.bindings.find((item) => sameCanonicalIdentity(item, candidate));
|
|
2002
|
-
if (existing !== void 0) {
|
|
2003
|
-
if (!sameBinding(existing, candidate)) throw new HostRelayBindingError("canonical_turn_rebound", `Host Relay canonical turn is already bound to another Request: ${refusalFacts([{ name: "candidate_request_ref", value: candidate.request_ref, expected: existing.request_ref }, { name: "candidate_prompt_ref", value: candidate.prompt_ref, expected: existing.prompt_ref }, { name: "candidate_context_expires_at", value: candidate.context_expires_at, expected: String(existing.context_expires_at) }, { name: "candidate_local_turn_aliases", value: candidate.local_turn_aliases, expected: JSON.stringify(existing.local_turn_aliases) }])}.`);
|
|
2004
|
-
return existing;
|
|
2005
|
-
}
|
|
2006
|
-
if (state.bindings.length >= MAX_BINDINGS) throw new HostRelayBindingError("binding_capacity_exhausted", `Host Relay request binding capacity is exhausted: ${refusalFact("binding_count", state.bindings.length, `below ${MAX_BINDINGS}`)}.`);
|
|
2007
|
-
const next = { schema_version: STATE_SCHEMA, bindings: [...state.bindings, candidate] };
|
|
2008
|
-
await this.write(next);
|
|
2009
|
-
return structuredClone(candidate);
|
|
2010
|
-
}
|
|
2011
|
-
async queryLocked(input) {
|
|
2012
|
-
validateQuery(input);
|
|
2013
|
-
const state = await this.read();
|
|
2014
|
-
const matches = state.bindings.filter((binding2) => queryMatches(input, binding2));
|
|
2015
|
-
if (matches.length === 0) return { status: "no_binding" };
|
|
2016
|
-
const binding = matches[0];
|
|
2017
|
-
if (binding === void 0) throw new TypeError("Host Relay binding query result is invalid.");
|
|
2018
|
-
return { status: "ready", binding: structuredClone(binding) };
|
|
2502
|
+
installation2.last_refusal = { sequence: head.sequence, submission_ref: input.submission_ref, ...refusalCause(outcome, head), refused_at_ms: options.now_ms };
|
|
2503
|
+
refused.push(input.submission_ref);
|
|
2504
|
+
} else {
|
|
2505
|
+
await options.after_gateway_accept_before_delivery_fsync?.({ fault_point: "after_gateway_accept_before_delivery_fsync", delivery: structuredClone(input) });
|
|
2506
|
+
delivered.push(input.submission_ref);
|
|
2507
|
+
}
|
|
2508
|
+
state.entries.splice(state.entries.indexOf(head), 1);
|
|
2509
|
+
installation2.delivered_through_sequence = Math.max(installation2.delivered_through_sequence, head.sequence);
|
|
2510
|
+
await this.write(state);
|
|
2511
|
+
}
|
|
2512
|
+
return { delivered, retained, refused };
|
|
2019
2513
|
}
|
|
2020
2514
|
async read() {
|
|
2021
|
-
let handle;
|
|
2022
2515
|
try {
|
|
2023
|
-
|
|
2024
|
-
const stats = await handle.stat();
|
|
2025
|
-
if (!stats.isFile() || !ownerPrivate2(stats)) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFacts([{ name: "state_is_file", value: stats.isFile(), expected: "true" }, { name: "state_uid", value: stats.uid, expected: String(process.geteuid?.() ?? stats.uid) }, { name: "state_mode", value: (stats.mode & 511).toString(8), expected: "no group or other permission bits" }])}.`);
|
|
2026
|
-
if (stats.size > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is unsafe: ${refusalFact("state_bytes", stats.size, `at most ${MAX_STATE_BYTES}`)}.`);
|
|
2027
|
-
return decodeState3(JSON.parse(await handle.readFile("utf8")));
|
|
2516
|
+
return decodeState3(JSON.parse(await readFile2(join4(this.root, "admission.json"), "utf8")));
|
|
2028
2517
|
} catch (error) {
|
|
2029
|
-
if (
|
|
2518
|
+
if (errorCode6(error) === "ENOENT") return { schema_version: "ceal.host_relay_admission_state.v1", installations: {}, entries: [] };
|
|
2030
2519
|
throw error;
|
|
2031
|
-
} finally {
|
|
2032
|
-
await handle?.close();
|
|
2033
2520
|
}
|
|
2034
2521
|
}
|
|
2035
2522
|
async write(state) {
|
|
2036
|
-
|
|
2037
|
-
if (Buffer.byteLength(encoded, "utf8") > MAX_STATE_BYTES) throw new TypeError(`Host Relay binding state is too large: ${refusalFact("state_bytes", Buffer.byteLength(encoded, "utf8"), `at most ${MAX_STATE_BYTES}`)}.`);
|
|
2038
|
-
await durableAtomicReplace(join4(this.root, "request-bindings.json"), encoded);
|
|
2523
|
+
await durableAtomicReplace(join4(this.root, "admission.json"), JSON.stringify(state));
|
|
2039
2524
|
}
|
|
2040
2525
|
};
|
|
2041
|
-
function
|
|
2042
|
-
return
|
|
2043
|
-
host_source_ref: binding.host_source_ref,
|
|
2044
|
-
host_session_ref: binding.host_session_ref,
|
|
2045
|
-
host_turn_ref: binding.host_turn_ref,
|
|
2046
|
-
host_input_ref: binding.host_input_ref,
|
|
2047
|
-
prompt_ref: binding.prompt_ref
|
|
2048
|
-
} });
|
|
2049
|
-
}
|
|
2050
|
-
function createRelayRequestWorkContext(input) {
|
|
2051
|
-
if (!record3(input) || !exactKeys3(input, ["request_ref", "host_binding"]) || !safeRef2(input.request_ref) || !record3(input.host_binding)) {
|
|
2052
|
-
throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
|
|
2053
|
-
}
|
|
2054
|
-
const hostBinding = input.host_binding;
|
|
2055
|
-
if (!exactKeys3(hostBinding, ["host_source_ref", "host_session_ref", "host_turn_ref", "host_input_ref", "prompt_ref"]) || ![hostBinding.host_source_ref, hostBinding.host_session_ref, hostBinding.host_turn_ref, hostBinding.host_input_ref, hostBinding.prompt_ref].every(safeRef2)) {
|
|
2056
|
-
throw new HostRelayBindingError("context_identity_missing", "Host Relay work context does not contain the complete acknowledged Host tuple.");
|
|
2057
|
-
}
|
|
2058
|
-
const hostSourceRef = requiredRef(hostBinding.host_source_ref);
|
|
2059
|
-
const hostSessionRef = requiredRef(hostBinding.host_session_ref);
|
|
2060
|
-
const hostTurnRef = requiredRef(hostBinding.host_turn_ref);
|
|
2061
|
-
const hostInputRef = requiredRef(hostBinding.host_input_ref);
|
|
2062
|
-
const promptRef = requiredRef(hostBinding.prompt_ref);
|
|
2063
|
-
return Object.freeze({ request_ref: input.request_ref, host_binding: Object.freeze({
|
|
2064
|
-
host_source_ref: hostSourceRef,
|
|
2065
|
-
host_session_ref: hostSessionRef,
|
|
2066
|
-
host_turn_ref: hostTurnRef,
|
|
2067
|
-
host_input_ref: hostInputRef,
|
|
2068
|
-
prompt_ref: promptRef
|
|
2069
|
-
}) });
|
|
2070
|
-
}
|
|
2071
|
-
function bindingFromGateway(input, now) {
|
|
2072
|
-
const payload = input.frame.payload;
|
|
2073
|
-
const candidate = {
|
|
2074
|
-
schema_version: HOST_RELAY_REQUEST_BINDING_SCHEMA,
|
|
2075
|
-
producer_id: input.frame.producer_id,
|
|
2076
|
-
producer_installation_ref: input.frame.producer_installation_ref,
|
|
2077
|
-
local_session_ref: requiredRef(payload.local_session_ref),
|
|
2078
|
-
local_turn_ref: requiredRef(payload.local_turn_ref),
|
|
2079
|
-
local_turn_aliases: optionalRefs2(payload.local_turn_aliases),
|
|
2080
|
-
prompt_ref: requiredRef(input.prompt_ref),
|
|
2081
|
-
request_ref: requiredRef(input.request_ref),
|
|
2082
|
-
bound_at: timestamp2(now.toISOString()),
|
|
2083
|
-
...payload.context_expires_at === void 0 ? {} : { context_expires_at: timestamp2(payload.context_expires_at) },
|
|
2084
|
-
host_source_ref: requiredRef(input.host_binding.host_source_ref),
|
|
2085
|
-
host_session_ref: requiredRef(input.host_binding.host_session_ref),
|
|
2086
|
-
host_turn_ref: requiredRef(input.host_binding.host_turn_ref),
|
|
2087
|
-
host_input_ref: requiredRef(input.host_binding.host_input_ref)
|
|
2088
|
-
};
|
|
2089
|
-
if (input.host_binding.prompt_ref !== input.prompt_ref) throw new HostRelayBindingError("context_identity_mismatch", "Host Relay binding prompt reference does not match the acknowledged submission.");
|
|
2090
|
-
validateBinding(candidate);
|
|
2091
|
-
return candidate;
|
|
2526
|
+
function installationKey(frame) {
|
|
2527
|
+
return `${frame.producer_id}\0${frame.producer_installation_ref}`;
|
|
2092
2528
|
}
|
|
2093
|
-
function
|
|
2094
|
-
if (
|
|
2095
|
-
|
|
2096
|
-
if (
|
|
2097
|
-
const
|
|
2098
|
-
|
|
2099
|
-
|
|
2529
|
+
function pendingFrameMatchesBindingQuery(entry2, query) {
|
|
2530
|
+
if (entry2.frame.frame_kind !== "user_input") return false;
|
|
2531
|
+
const payload = entry2.frame.payload;
|
|
2532
|
+
if (typeof payload.local_session_ref !== "string" || typeof payload.local_turn_ref !== "string") return false;
|
|
2533
|
+
const aliases = Array.isArray(payload.local_turn_aliases) ? payload.local_turn_aliases.filter((value) => typeof value === "string") : [];
|
|
2534
|
+
return relayRequestBindingMatchesQuery(query, {
|
|
2535
|
+
producer_id: entry2.frame.producer_id,
|
|
2536
|
+
producer_installation_ref: entry2.frame.producer_installation_ref,
|
|
2537
|
+
local_session_ref: payload.local_session_ref,
|
|
2538
|
+
local_turn_ref: payload.local_turn_ref,
|
|
2539
|
+
local_turn_aliases: aliases
|
|
2100
2540
|
});
|
|
2101
|
-
const keys = bindings.map(bindingKey);
|
|
2102
|
-
if (new Set(keys).size !== keys.length) throw new TypeError("Host Relay binding state contains duplicate canonical turns.");
|
|
2103
|
-
return { schema_version: STATE_SCHEMA, bindings };
|
|
2104
2541
|
}
|
|
2105
|
-
function
|
|
2106
|
-
if (
|
|
2542
|
+
function replayCode(existing, hash, sequence, installation2) {
|
|
2543
|
+
if (existing !== void 0) return existing.hash === hash ? "already_admitted" : "frame_identity_conflict";
|
|
2544
|
+
return sequence <= installation2.delivered_through_sequence ? "already_admitted" : null;
|
|
2107
2545
|
}
|
|
2108
|
-
function
|
|
2109
|
-
return
|
|
2546
|
+
function bytes(entries) {
|
|
2547
|
+
return entries.reduce((total, entry2) => total + entry2.charged_bytes, 0);
|
|
2110
2548
|
}
|
|
2111
|
-
function
|
|
2112
|
-
|
|
2113
|
-
return safeRef2(value.prompt_ref) && safeRef2(value.request_ref) && validTimestamp(value.bound_at) && (value.context_expires_at === void 0 || validTimestamp(value.context_expires_at)) && tuple.every(safeRef2);
|
|
2549
|
+
function retryExhausted(outcome, entry2) {
|
|
2550
|
+
return outcome.status === "retryable" && entry2.retry_attempts >= MAX_RETRYABLE_ATTEMPTS;
|
|
2114
2551
|
}
|
|
2115
|
-
function
|
|
2116
|
-
if (
|
|
2552
|
+
function refusalCause(outcome, entry2) {
|
|
2553
|
+
if (outcome.status === "refused") return { code: outcome.code, message: outcome.message };
|
|
2554
|
+
const reason = outcome.status === "retryable" ? outcome.reason ?? "no reason reported" : "no reason reported";
|
|
2555
|
+
return { code: "retry_exhausted", message: `Gateway delivery stayed retryable through ${entry2.retry_attempts} attempts: ${reason}` };
|
|
2117
2556
|
}
|
|
2118
|
-
function
|
|
2119
|
-
|
|
2557
|
+
function scheduleRetry(entry2, nowMs, reason) {
|
|
2558
|
+
if (reason === void 0) delete entry2.retry_reason;
|
|
2559
|
+
else entry2.retry_reason = reason;
|
|
2560
|
+
entry2.retry_attempts += 1;
|
|
2561
|
+
entry2.retry_at_ms = nowMs + 1e3 * (RETRY_SECONDS[Math.min(entry2.retry_attempts - 1, RETRY_SECONDS.length - 1)] ?? 60);
|
|
2120
2562
|
}
|
|
2121
|
-
function
|
|
2122
|
-
return
|
|
2563
|
+
function result(code, state) {
|
|
2564
|
+
return { code, admitted_through_sequence: state.admitted_through_sequence, delivered_through_sequence: state.delivered_through_sequence };
|
|
2123
2565
|
}
|
|
2124
|
-
function
|
|
2125
|
-
return
|
|
2566
|
+
function errorCode6(error) {
|
|
2567
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : null;
|
|
2126
2568
|
}
|
|
2127
|
-
function
|
|
2128
|
-
|
|
2569
|
+
function decodeState3(value) {
|
|
2570
|
+
if (!stateRecord(value)) throw new TypeError("Host Relay admission state is invalid.");
|
|
2571
|
+
const installations = {};
|
|
2572
|
+
for (const [key, item] of Object.entries(value.installations)) {
|
|
2573
|
+
if (!installation(item)) throw new TypeError("Host Relay installation state is invalid.");
|
|
2574
|
+
installations[key] = item;
|
|
2575
|
+
}
|
|
2576
|
+
const entries = [];
|
|
2577
|
+
for (const item of value.entries) {
|
|
2578
|
+
if (!entry(item)) throw new TypeError("Host Relay entry state is invalid.");
|
|
2579
|
+
entries.push(item);
|
|
2580
|
+
}
|
|
2581
|
+
return { schema_version: "ceal.host_relay_admission_state.v1", installations, entries };
|
|
2129
2582
|
}
|
|
2130
|
-
function
|
|
2131
|
-
|
|
2132
|
-
return value;
|
|
2583
|
+
function stateRecord(value) {
|
|
2584
|
+
return object(value) && value.schema_version === "ceal.host_relay_admission_state.v1" && object(value.installations) && Array.isArray(value.entries);
|
|
2133
2585
|
}
|
|
2134
|
-
function
|
|
2135
|
-
|
|
2136
|
-
if (!refList(value)) throw new TypeError("Host Relay binding aliases are invalid.");
|
|
2137
|
-
return [...value];
|
|
2586
|
+
function installation(value) {
|
|
2587
|
+
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));
|
|
2138
2588
|
}
|
|
2139
|
-
function
|
|
2140
|
-
|
|
2141
|
-
|
|
2589
|
+
function refusalRecord(value) {
|
|
2590
|
+
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);
|
|
2591
|
+
}
|
|
2592
|
+
function entry(value) {
|
|
2593
|
+
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);
|
|
2142
2594
|
}
|
|
2143
2595
|
function producer2(value) {
|
|
2144
2596
|
return value === "codex" || value === "claude";
|
|
2145
2597
|
}
|
|
2146
|
-
function
|
|
2147
|
-
return typeof value === "
|
|
2148
|
-
}
|
|
2149
|
-
function refList(value) {
|
|
2150
|
-
return Array.isArray(value) && value.length <= 16 && value.every(safeRef2) && new Set(value).size === value.length;
|
|
2151
|
-
}
|
|
2152
|
-
function validTimestamp(value) {
|
|
2153
|
-
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
2154
|
-
}
|
|
2155
|
-
function record3(value) {
|
|
2156
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2157
|
-
}
|
|
2158
|
-
function exactKeys3(value, keys) {
|
|
2159
|
-
return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
2160
|
-
}
|
|
2161
|
-
function exactOptionalKeys(value, required, optional) {
|
|
2162
|
-
const keys = Object.keys(value);
|
|
2163
|
-
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => required.includes(key) || optional.includes(key));
|
|
2164
|
-
}
|
|
2165
|
-
function errorCode5(error) {
|
|
2166
|
-
return record3(error) && typeof error.code === "string" ? error.code : null;
|
|
2167
|
-
}
|
|
2168
|
-
function ownerPrivate2(stats) {
|
|
2169
|
-
if (process.platform === "win32") return true;
|
|
2170
|
-
const uid = typeof process.geteuid === "function" ? process.geteuid() : stats.uid;
|
|
2171
|
-
return stats.uid === uid && (stats.mode & 63) === 0;
|
|
2598
|
+
function object(value) {
|
|
2599
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2172
2600
|
}
|
|
2173
2601
|
|
|
2174
2602
|
// host/relay/src/relay-gateway-bridge.ts
|
|
2603
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
2604
|
+
import { join as join5 } from "node:path";
|
|
2175
2605
|
var STATE_SCHEMA2 = "ceal.host_relay_gateway_state.v1";
|
|
2176
2606
|
var SAFE_REF4 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
2177
2607
|
var HOST_GATEWAY_ACKNOWLEDGEMENT_DISPOSITIONS = ["created", "adopted", "replayed", "approval_approved", "approval_denied", "approval_replayed"];
|
|
@@ -2291,7 +2721,7 @@ var RelayGatewayBridge = class {
|
|
|
2291
2721
|
if (state.host_source_ref !== this.hostSourceRef) throw new TypeError(`Host Relay Gateway source binding changed: ${refusalFacts([{ name: "stored_host_source_ref", value: state.host_source_ref }, { name: "configured_host_source_ref", value: this.hostSourceRef }])}.`);
|
|
2292
2722
|
return state;
|
|
2293
2723
|
} catch (error) {
|
|
2294
|
-
if (
|
|
2724
|
+
if (errorCode7(error) === "ENOENT") return { schema_version: STATE_SCHEMA2, host_source_ref: this.hostSourceRef, sessions: {}, turns: {} };
|
|
2295
2725
|
throw error;
|
|
2296
2726
|
}
|
|
2297
2727
|
}
|
|
@@ -2302,7 +2732,7 @@ var RelayGatewayBridge = class {
|
|
|
2302
2732
|
var RETRYABLE_HTTP_STATUSES = /* @__PURE__ */ new Set([401, 403, 408, 429]);
|
|
2303
2733
|
function submissionRefusal(error) {
|
|
2304
2734
|
const status = isRecord(error) && typeof error.status === "number" ? error.status : void 0;
|
|
2305
|
-
const code =
|
|
2735
|
+
const code = errorCode7(error) ?? "gateway_unavailable";
|
|
2306
2736
|
const definitive = code === "payload_rejected" || status !== void 0 && status >= 400 && status < 500 && !RETRYABLE_HTTP_STATUSES.has(status);
|
|
2307
2737
|
return { code, message: errorMessage(error), definitive };
|
|
2308
2738
|
}
|
|
@@ -2383,7 +2813,7 @@ function isRecord(value) {
|
|
|
2383
2813
|
function validOptionalTimeout(value) {
|
|
2384
2814
|
return value === void 0 || Number.isSafeInteger(value) && Number(value) >= 1 && Number(value) <= 6e4;
|
|
2385
2815
|
}
|
|
2386
|
-
function
|
|
2816
|
+
function errorCode7(error) {
|
|
2387
2817
|
return isRecord(error) && typeof error.code === "string" ? error.code : null;
|
|
2388
2818
|
}
|
|
2389
2819
|
function decodeState4(value) {
|
|
@@ -2427,7 +2857,7 @@ function isInputRefusal(value) {
|
|
|
2427
2857
|
|
|
2428
2858
|
// host/relay/src/relay-gateway-http.ts
|
|
2429
2859
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_RDONLY as O_RDONLY3 } from "node:constants";
|
|
2430
|
-
import { lstat as lstat2, open as
|
|
2860
|
+
import { lstat as lstat2, open as open4 } from "node:fs/promises";
|
|
2431
2861
|
import { dirname as dirname2 } from "node:path";
|
|
2432
2862
|
|
|
2433
2863
|
// host/gateway-http.ts
|
|
@@ -2477,6 +2907,7 @@ async function submitHostIngress(options) {
|
|
|
2477
2907
|
}
|
|
2478
2908
|
|
|
2479
2909
|
// host/relay/src/relay-gateway-http.ts
|
|
2910
|
+
var HOST_REGISTRATION_STATE_SCHEMA = "ceal.host_registration_state.v1";
|
|
2480
2911
|
var HOST_REGISTRATION_STATE_KEYS = Object.freeze([
|
|
2481
2912
|
"credential_generation",
|
|
2482
2913
|
"gateway_request_origin",
|
|
@@ -2487,19 +2918,30 @@ var HOST_REGISTRATION_STATE_KEYS = Object.freeze([
|
|
|
2487
2918
|
"schema_version",
|
|
2488
2919
|
"status"
|
|
2489
2920
|
]);
|
|
2490
|
-
var HOST_INGRESS_PATH = "/api/ceal/v2/host-ingress";
|
|
2491
2921
|
var HOST_REGISTRATION_STATE_MAX_BYTES = 65536;
|
|
2492
2922
|
var HostRelayGatewayError = class extends Error {
|
|
2493
2923
|
name = "HostRelayGatewayError";
|
|
2494
2924
|
code;
|
|
2495
2925
|
/** HTTP status of a Gateway refusal, when one answered. */
|
|
2496
2926
|
status;
|
|
2497
|
-
|
|
2927
|
+
/** The Gateway's own refusal word, when it named one; a bounded token, never its message. */
|
|
2928
|
+
error_code;
|
|
2929
|
+
constructor(code, message, status, errorCode9) {
|
|
2498
2930
|
super(message);
|
|
2499
2931
|
this.code = code;
|
|
2500
2932
|
if (status !== void 0) this.status = status;
|
|
2933
|
+
if (errorCode9 !== void 0) this.error_code = errorCode9;
|
|
2501
2934
|
}
|
|
2502
2935
|
};
|
|
2936
|
+
var GATEWAY_REFUSAL_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
2937
|
+
async function gatewayRefusalCode(response2) {
|
|
2938
|
+
try {
|
|
2939
|
+
const body = await response2.json();
|
|
2940
|
+
return isCealJsonRecord(body) && typeof body.error_code === "string" && GATEWAY_REFUSAL_CODE_PATTERN.test(body.error_code) ? body.error_code : void 0;
|
|
2941
|
+
} catch {
|
|
2942
|
+
return void 0;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2503
2945
|
function refusalMessage(status, body) {
|
|
2504
2946
|
const record6 = isCealJsonRecord(body) ? body : {};
|
|
2505
2947
|
const code = typeof record6.error_code === "string" ? record6.error_code : "unknown";
|
|
@@ -2545,7 +2987,7 @@ function createHostRelayGatewayHttpPort(options) {
|
|
|
2545
2987
|
} catch {
|
|
2546
2988
|
throw new HostRelayGatewayError("gateway_unavailable", "Gateway Host invocation context is unavailable.");
|
|
2547
2989
|
}
|
|
2548
|
-
if (!response2.ok) throw new HostRelayGatewayError(response2.status === 401 ? "credential" : "gateway_refused", `Gateway Host invocation context was refused (${response2.status}).`, response2.status);
|
|
2990
|
+
if (!response2.ok) throw new HostRelayGatewayError(response2.status === 401 ? "credential" : "gateway_refused", `Gateway Host invocation context was refused (${response2.status}).`, response2.status, await gatewayRefusalCode(response2));
|
|
2549
2991
|
let value;
|
|
2550
2992
|
try {
|
|
2551
2993
|
value = await response2.json();
|
|
@@ -2561,7 +3003,7 @@ function createHostRelayGatewayHttpPort(options) {
|
|
|
2561
3003
|
});
|
|
2562
3004
|
}
|
|
2563
3005
|
function hostRelayGatewayEndpoint(value) {
|
|
2564
|
-
return hostRelayGatewayRoute(value,
|
|
3006
|
+
return hostRelayGatewayRoute(value, CEAL_HOST_INGRESS_PATH);
|
|
2565
3007
|
}
|
|
2566
3008
|
function hostRelayGatewayRoute(value, path) {
|
|
2567
3009
|
let parsed;
|
|
@@ -2601,7 +3043,7 @@ async function readHostRegistrationAuthority(path, expectedHostSourceRef) {
|
|
|
2601
3043
|
let handle;
|
|
2602
3044
|
try {
|
|
2603
3045
|
await assertOwnerPrivateDirectory(dirname2(path));
|
|
2604
|
-
handle = await
|
|
3046
|
+
handle = await open4(path, O_RDONLY3 | O_NOFOLLOW2);
|
|
2605
3047
|
const stats = await handle.stat();
|
|
2606
3048
|
if (!stats.isFile() || !ownerPrivate3(stats)) throw new HostRelayGatewayError("credential", "Host Relay credential file is not owner-private.");
|
|
2607
3049
|
const raw = await handle.readFile("utf8");
|
|
@@ -2631,13 +3073,13 @@ function decodeHostRegistrationAuthority(value, expectedHostSourceRef) {
|
|
|
2631
3073
|
return { gateway_request_origin: value.gateway_request_origin, host_access_token: value.host_access_token };
|
|
2632
3074
|
}
|
|
2633
3075
|
function validRegistrationIdentity(value, expectedHostSourceRef) {
|
|
2634
|
-
return value.schema_version ===
|
|
3076
|
+
return value.schema_version === HOST_REGISTRATION_STATE_SCHEMA && value.status === "active" && value.host_source_ref === expectedHostSourceRef && typeof value.registration_attempt_ref === "string" && CEAL_HOST_REGISTRATION_ATTEMPT_REF_PATTERN.test(value.registration_attempt_ref) && typeof value.host_installation_ref === "string" && CEAL_HOST_INSTALLATION_REF_PATTERN.test(value.host_installation_ref) && typeof value.host_source_ref === "string" && hostSourceUuid(value.host_source_ref) !== null;
|
|
2635
3077
|
}
|
|
2636
3078
|
function validRegistrationCredential(value) {
|
|
2637
3079
|
return Number.isSafeInteger(value.credential_generation) && Number(value.credential_generation) > 0 && validHostAccessToken(value.host_access_token);
|
|
2638
3080
|
}
|
|
2639
3081
|
function validHostAccessToken(value) {
|
|
2640
|
-
return typeof value === "string" &&
|
|
3082
|
+
return typeof value === "string" && CEAL_HOST_ACCESS_TOKEN_PATTERN.test(value);
|
|
2641
3083
|
}
|
|
2642
3084
|
function validGatewayOrigin(value) {
|
|
2643
3085
|
try {
|
|
@@ -2670,7 +3112,7 @@ import { chmod, lstat as lstat3, unlink as unlink2 } from "node:fs/promises";
|
|
|
2670
3112
|
import { createConnection, createServer } from "node:net";
|
|
2671
3113
|
|
|
2672
3114
|
// host/relay/src/current-prompt-custody.ts
|
|
2673
|
-
import { createHash as
|
|
3115
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
2674
3116
|
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
2675
3117
|
var MAX_CURRENT_PROMPTS = 1024;
|
|
2676
3118
|
var HostCurrentPromptCustody = class {
|
|
@@ -2684,7 +3126,7 @@ var HostCurrentPromptCustody = class {
|
|
|
2684
3126
|
const bytes2 = Buffer.byteLength(prompt, "utf8");
|
|
2685
3127
|
const expiry = Date.parse(expiresAt);
|
|
2686
3128
|
if (bytes2 === 0 || bytes2 > HOST_CURRENT_PROMPT_MAX_BYTES || !Number.isFinite(expiry) || expiry <= this.#now()) throw new TypeError("current_prompt_unavailable");
|
|
2687
|
-
if (hostProducerPromptRef(
|
|
3129
|
+
if (hostProducerPromptRef(createHash5("sha256").update(prompt, "utf8").digest("hex")) !== identity.prompt_ref) throw new TypeError("current_prompt_unavailable");
|
|
2688
3130
|
this.#purge();
|
|
2689
3131
|
const key = promptKey(identity);
|
|
2690
3132
|
if (this.#consumed.has(key)) return;
|
|
@@ -2737,11 +3179,46 @@ var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_co
|
|
|
2737
3179
|
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
2738
3180
|
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
2739
3181
|
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
3182
|
+
var HOST_RELAY_INVOCATION_MINT_REFUSAL_REASONS = vocabulary([
|
|
3183
|
+
"context_stale",
|
|
3184
|
+
"context_expired",
|
|
3185
|
+
"host_source_refused",
|
|
3186
|
+
"host_credential_refused",
|
|
3187
|
+
"gateway_unavailable",
|
|
3188
|
+
"gateway_refused",
|
|
3189
|
+
"gateway_response_invalid",
|
|
3190
|
+
"deadline",
|
|
3191
|
+
"cancelled",
|
|
3192
|
+
"mint_failed",
|
|
3193
|
+
"relay_failed"
|
|
3194
|
+
]);
|
|
3195
|
+
var HostRelayInvocationMintRefusal = class extends Error {
|
|
3196
|
+
reason;
|
|
3197
|
+
constructor(reason) {
|
|
3198
|
+
super(reason);
|
|
3199
|
+
this.name = "HostRelayInvocationMintRefusal";
|
|
3200
|
+
this.reason = reason;
|
|
3201
|
+
}
|
|
3202
|
+
};
|
|
3203
|
+
var HOST_RELAY_INVOCATION_MINT_OUTCOMES = vocabulary(["completed", "caller_cancelled", "deadline", "failed", "no_binding"]);
|
|
3204
|
+
var HOST_RELAY_EXCHANGE_FAILURES = vocabulary([
|
|
3205
|
+
"permission_denied",
|
|
3206
|
+
"socket_missing",
|
|
3207
|
+
"connection_refused",
|
|
3208
|
+
"closed_early",
|
|
3209
|
+
"response_invalid",
|
|
3210
|
+
"timed_out",
|
|
3211
|
+
"cancelled",
|
|
3212
|
+
"unavailable"
|
|
3213
|
+
]);
|
|
2740
3214
|
var HostRelayIpcError = class extends Error {
|
|
2741
3215
|
code;
|
|
2742
|
-
|
|
3216
|
+
/** Set only when the client-side socket exchange itself failed. */
|
|
3217
|
+
failure;
|
|
3218
|
+
constructor(code, message = code, failure) {
|
|
2743
3219
|
super(message);
|
|
2744
3220
|
this.code = code;
|
|
3221
|
+
this.failure = failure;
|
|
2745
3222
|
}
|
|
2746
3223
|
};
|
|
2747
3224
|
async function startHostRelayIpcServer(options) {
|
|
@@ -2750,7 +3227,7 @@ async function startHostRelayIpcServer(options) {
|
|
|
2750
3227
|
const sockets = /* @__PURE__ */ new Set();
|
|
2751
3228
|
const quarantine = options.quarantine ?? options.admission.quarantine;
|
|
2752
3229
|
const currentPrompts = options.currentPrompts ?? new HostCurrentPromptCustody();
|
|
2753
|
-
const server = createServer((socket) => serveSocket(socket, options
|
|
3230
|
+
const server = createServer((socket) => serveSocket(socket, options, currentPrompts, quarantine, active, sockets));
|
|
2754
3231
|
await listen(server, options.endpoint);
|
|
2755
3232
|
if (process.platform !== "win32") await chmod(options.endpoint, 384);
|
|
2756
3233
|
return { endpoint: options.endpoint, close: () => closeServer(server, sockets, options.endpoint) };
|
|
@@ -2762,12 +3239,12 @@ function encodeHostRelayIpcResponse(response2) {
|
|
|
2762
3239
|
body.copy(result2, HOST_RELAY_IPC_LENGTH_BYTES);
|
|
2763
3240
|
return result2;
|
|
2764
3241
|
}
|
|
2765
|
-
function serveSocket(socket,
|
|
3242
|
+
function serveSocket(socket, options, currentPrompts, quarantine, active, sockets) {
|
|
2766
3243
|
const state = { buffer: Buffer.alloc(0), closed: false, tail: Promise.resolve(), abort: new AbortController() };
|
|
2767
3244
|
sockets.add(socket);
|
|
2768
3245
|
socket.on("data", (chunk) => {
|
|
2769
3246
|
state.buffer = Buffer.concat([state.buffer, Buffer.from(chunk)]);
|
|
2770
|
-
state.tail = state.tail.then(() => drainFrames(socket, state,
|
|
3247
|
+
state.tail = state.tail.then(() => drainFrames(socket, state, options, currentPrompts, quarantine, active)).catch(() => {
|
|
2771
3248
|
writeResponse(socket, errorResponse("relay_unavailable"));
|
|
2772
3249
|
socket.end();
|
|
2773
3250
|
});
|
|
@@ -2785,11 +3262,11 @@ function serveSocket(socket, admission, bindings, currentPrompts, mintInvocation
|
|
|
2785
3262
|
else socket.end();
|
|
2786
3263
|
});
|
|
2787
3264
|
}
|
|
2788
|
-
async function drainFrames(socket, state,
|
|
2789
|
-
while (!state.closed && state.buffer.length >= HOST_RELAY_IPC_LENGTH_BYTES && await processBufferedRequest(socket, state,
|
|
3265
|
+
async function drainFrames(socket, state, options, currentPrompts, quarantine, active) {
|
|
3266
|
+
while (!state.closed && state.buffer.length >= HOST_RELAY_IPC_LENGTH_BYTES && await processBufferedRequest(socket, state, options, currentPrompts, quarantine, active)) {
|
|
2790
3267
|
}
|
|
2791
3268
|
}
|
|
2792
|
-
async function processBufferedRequest(socket, state,
|
|
3269
|
+
async function processBufferedRequest(socket, state, options, currentPrompts, quarantine, active) {
|
|
2793
3270
|
const length = state.buffer.readUInt32BE(0);
|
|
2794
3271
|
const promptWire = currentPromptWire(state.buffer);
|
|
2795
3272
|
const maximum = promptWire ? HOST_CURRENT_PROMPT_MAX_BYTES + 4096 : HOST_PRODUCER_MAX_JSON_BYTES;
|
|
@@ -2818,8 +3295,13 @@ async function processBufferedRequest(socket, state, admission, bindings, curren
|
|
|
2818
3295
|
}
|
|
2819
3296
|
return true;
|
|
2820
3297
|
}
|
|
2821
|
-
|
|
2822
|
-
|
|
3298
|
+
const answered = await processHostQuery(socket, request2, options, currentPrompts, state.abort.signal);
|
|
3299
|
+
if (answered === "closed") {
|
|
3300
|
+
socket.end();
|
|
3301
|
+
return false;
|
|
3302
|
+
}
|
|
3303
|
+
if (answered === "answered") return true;
|
|
3304
|
+
await processProducerRequest(socket, state, request2, encoded, options.admission, quarantine, active);
|
|
2823
3305
|
return true;
|
|
2824
3306
|
}
|
|
2825
3307
|
async function refuseInvalidBufferedRequest(socket, rawBuffer, length, promptWire, quarantine) {
|
|
@@ -2851,16 +3333,29 @@ async function processProducerRequest(socket, state, request2, encoded, admissio
|
|
|
2851
3333
|
}
|
|
2852
3334
|
writeResponse(socket, admissionResponse(await admission.admit(request2)));
|
|
2853
3335
|
}
|
|
2854
|
-
async function processHostQuery(socket, request2,
|
|
3336
|
+
async function processHostQuery(socket, request2, options, currentPrompts, signal) {
|
|
2855
3337
|
if (request2.schema_version === HOST_RELAY_BINDING_QUERY_SCHEMA) {
|
|
2856
|
-
|
|
2857
|
-
|
|
3338
|
+
let result2;
|
|
3339
|
+
try {
|
|
3340
|
+
result2 = await options.bindings.query(bindingQueryFromRequest(request2));
|
|
3341
|
+
} catch {
|
|
3342
|
+
return "closed";
|
|
3343
|
+
}
|
|
3344
|
+
writeBindingResponse(socket, result2);
|
|
3345
|
+
return "answered";
|
|
2858
3346
|
}
|
|
2859
3347
|
if (request2.schema_version === HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA) {
|
|
2860
|
-
|
|
2861
|
-
|
|
3348
|
+
let response2;
|
|
3349
|
+
const resolve = (invocationSignal) => resolveInvocationContext(request2, options.admission, options.bindings, currentPrompts, options.mintInvocationContext, invocationSignal);
|
|
3350
|
+
try {
|
|
3351
|
+
response2 = options.invocationMintTimeoutMs === void 0 && options.scheduleInvocationMintDeadline === void 0 && options.observeInvocationMint === void 0 ? await resolve(signal) : await observeInvocationMint(resolve, signal, options.invocationMintTimeoutMs, options.scheduleInvocationMintDeadline, options.observeInvocationMint);
|
|
3352
|
+
} catch {
|
|
3353
|
+
response2 = { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: "relay_failed" };
|
|
3354
|
+
}
|
|
3355
|
+
writeInvocationContextResponse(socket, response2);
|
|
3356
|
+
return "answered";
|
|
2862
3357
|
}
|
|
2863
|
-
return
|
|
3358
|
+
return "not_a_query";
|
|
2864
3359
|
}
|
|
2865
3360
|
function claimProducer(socket, state, frame, active) {
|
|
2866
3361
|
const key = producerKey(frame);
|
|
@@ -2894,14 +3389,93 @@ function writeInvocationContextResponse(socket, response2) {
|
|
|
2894
3389
|
function writeCurrentPromptResponse(socket, status) {
|
|
2895
3390
|
if (!socket.destroyed) socket.write(encodeLengthPrefixed({ schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status }));
|
|
2896
3391
|
}
|
|
2897
|
-
async function resolveInvocationContext(request2, bindings, currentPrompts, mint, signal) {
|
|
3392
|
+
async function resolveInvocationContext(request2, admission, bindings, currentPrompts, mint, signal) {
|
|
2898
3393
|
if (mint === void 0) throw new HostRelayIpcError("relay_unavailable");
|
|
2899
|
-
const
|
|
2900
|
-
|
|
3394
|
+
const query = bindingQueryFromRequest(request2);
|
|
3395
|
+
const binding = await waitForRequestBinding(query, admission, bindings, signal);
|
|
3396
|
+
if (binding.status !== "ready") return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "no_binding" };
|
|
2901
3397
|
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 });
|
|
2902
|
-
|
|
3398
|
+
let result2;
|
|
3399
|
+
try {
|
|
3400
|
+
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);
|
|
3401
|
+
} catch (error) {
|
|
3402
|
+
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: error instanceof HostRelayInvocationMintRefusal ? error.reason : "mint_failed" };
|
|
3403
|
+
}
|
|
2903
3404
|
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "ready", result: result2 };
|
|
2904
3405
|
}
|
|
3406
|
+
async function waitForRequestBinding(query, admission, bindings, signal) {
|
|
3407
|
+
let binding = await bindings.query(query);
|
|
3408
|
+
if (binding.status === "ready") return binding;
|
|
3409
|
+
while (await admission.hasPendingRequestBinding(query)) {
|
|
3410
|
+
const cycle = new AbortController();
|
|
3411
|
+
const cancelCycle = () => cycle.abort(signal.reason);
|
|
3412
|
+
if (signal.aborted) cancelCycle();
|
|
3413
|
+
else signal.addEventListener("abort", cancelCycle, { once: true });
|
|
3414
|
+
try {
|
|
3415
|
+
const changed = Promise.race([
|
|
3416
|
+
bindings.waitForBinding(query, cycle.signal).then((result3) => ({ kind: "bound", result: result3 })),
|
|
3417
|
+
admission.waitForDrainChange(cycle.signal).then(() => ({ kind: "drained" }))
|
|
3418
|
+
]).then((result3) => result3, (error) => ({ kind: "cancelled", error }));
|
|
3419
|
+
binding = await bindings.query(query);
|
|
3420
|
+
if (binding.status === "ready") return binding;
|
|
3421
|
+
if (!await admission.hasPendingRequestBinding(query)) return await bindings.query(query);
|
|
3422
|
+
const result2 = await changed;
|
|
3423
|
+
if (result2.kind === "cancelled") throw result2.error;
|
|
3424
|
+
} finally {
|
|
3425
|
+
signal.removeEventListener("abort", cancelCycle);
|
|
3426
|
+
cycle.abort("wait_cycle_complete");
|
|
3427
|
+
}
|
|
3428
|
+
binding = await bindings.query(query);
|
|
3429
|
+
if (binding.status === "ready") return binding;
|
|
3430
|
+
}
|
|
3431
|
+
return await bindings.query(query);
|
|
3432
|
+
}
|
|
3433
|
+
async function observeInvocationMint(resolve, callerSignal, timeoutMs, scheduleDeadline, observe) {
|
|
3434
|
+
const started = process.hrtime.bigint();
|
|
3435
|
+
const controller2 = new AbortController();
|
|
3436
|
+
let cancellation = null;
|
|
3437
|
+
let outcome = "failed";
|
|
3438
|
+
let reason;
|
|
3439
|
+
const cancel = (cause) => {
|
|
3440
|
+
if (cancellation === null) {
|
|
3441
|
+
cancellation = cause;
|
|
3442
|
+
controller2.abort(cause);
|
|
3443
|
+
}
|
|
3444
|
+
};
|
|
3445
|
+
const callerCancelled = () => cancel("caller_cancelled");
|
|
3446
|
+
const cancelled = new Promise((_resolve, reject) => controller2.signal.addEventListener("abort", () => reject(new Error("invocation mint cancelled")), { once: true }));
|
|
3447
|
+
const clearDeadline = timeoutMs === void 0 ? () => void 0 : (scheduleDeadline ?? scheduleInvocationMintDeadline)(() => cancel("deadline"), timeoutMs);
|
|
3448
|
+
if (callerSignal.aborted) callerCancelled();
|
|
3449
|
+
else callerSignal.addEventListener("abort", callerCancelled, { once: true });
|
|
3450
|
+
try {
|
|
3451
|
+
const response2 = await Promise.race([resolve(controller2.signal), cancelled]);
|
|
3452
|
+
({ outcome, reason } = invocationMintObservationForResponse(response2));
|
|
3453
|
+
return response2;
|
|
3454
|
+
} catch {
|
|
3455
|
+
({ outcome, reason } = invocationMintObservationForCancellation(cancellation));
|
|
3456
|
+
return { schema_version: HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA, status: "refused", reason: reason ?? "relay_failed" };
|
|
3457
|
+
} finally {
|
|
3458
|
+
clearDeadline();
|
|
3459
|
+
callerSignal.removeEventListener("abort", callerCancelled);
|
|
3460
|
+
observe?.({ outcome, elapsed_ms: Number((process.hrtime.bigint() - started) / 1000000n), ...reason === void 0 ? {} : { reason } });
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
function invocationMintObservationForResponse(response2) {
|
|
3464
|
+
if (response2.status === "no_binding") return { outcome: "no_binding" };
|
|
3465
|
+
if (response2.status === "ready") return { outcome: "completed" };
|
|
3466
|
+
if (response2.reason === "deadline") return { outcome: "deadline", reason: "deadline" };
|
|
3467
|
+
if (response2.reason === "cancelled") return { outcome: "caller_cancelled", reason: "cancelled" };
|
|
3468
|
+
return { outcome: "failed", reason: response2.reason };
|
|
3469
|
+
}
|
|
3470
|
+
function invocationMintObservationForCancellation(cancellation) {
|
|
3471
|
+
if (cancellation === "deadline") return { outcome: "deadline", reason: "deadline" };
|
|
3472
|
+
if (cancellation === "caller_cancelled") return { outcome: "caller_cancelled", reason: "cancelled" };
|
|
3473
|
+
return { outcome: "failed", reason: "relay_failed" };
|
|
3474
|
+
}
|
|
3475
|
+
function scheduleInvocationMintDeadline(expire, milliseconds) {
|
|
3476
|
+
const timeout = setTimeout(expire, milliseconds);
|
|
3477
|
+
return () => clearTimeout(timeout);
|
|
3478
|
+
}
|
|
2905
3479
|
function admissionResponse(result2) {
|
|
2906
3480
|
return { schema_version: HOST_RELAY_IPC_RESPONSE_SCHEMA, ...result2 };
|
|
2907
3481
|
}
|
|
@@ -2987,7 +3561,7 @@ async function assertEndpointVacant(endpoint) {
|
|
|
2987
3561
|
await lstat3(endpoint);
|
|
2988
3562
|
throw new HostRelayIpcError("relay_unavailable", "Host Relay IPC endpoint is already present.");
|
|
2989
3563
|
} catch (error) {
|
|
2990
|
-
if (
|
|
3564
|
+
if (errorCode8(error) !== "ENOENT") throw error;
|
|
2991
3565
|
}
|
|
2992
3566
|
}
|
|
2993
3567
|
function listen(server, endpoint) {
|
|
@@ -3004,7 +3578,7 @@ async function closeServer(server, sockets, endpoint) {
|
|
|
3004
3578
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
3005
3579
|
if (process.platform !== "win32") await unlink2(endpoint).catch(() => void 0);
|
|
3006
3580
|
}
|
|
3007
|
-
function
|
|
3581
|
+
function errorCode8(error) {
|
|
3008
3582
|
return record4(error) && typeof error.code === "string" ? error.code : null;
|
|
3009
3583
|
}
|
|
3010
3584
|
|
|
@@ -3026,7 +3600,7 @@ async function readHostRelayConfig(path) {
|
|
|
3026
3600
|
let handle;
|
|
3027
3601
|
try {
|
|
3028
3602
|
await assertOwnerPrivateDirectory2(dirname3(path));
|
|
3029
|
-
handle = await
|
|
3603
|
+
handle = await open5(path, O_RDONLY4 | O_NOFOLLOW3);
|
|
3030
3604
|
const stats = await handle.stat();
|
|
3031
3605
|
if (!stats.isFile() || !ownerPrivate4(stats)) throw new HostRelayConfigError("config_not_private", "Host Relay config file is not owner-private.");
|
|
3032
3606
|
if (stats.size < HOST_RELAY_CONFIG_MIN_BYTES || stats.size > HOST_RELAY_CONFIG_MAX_BYTES) throw new HostRelayConfigError("invalid_config", `invalid_config: Host Relay config size is invalid: ${refusalFact("config_bytes", stats.size, `between ${HOST_RELAY_CONFIG_MIN_BYTES} and ${HOST_RELAY_CONFIG_MAX_BYTES}`)}.`);
|
|
@@ -3038,7 +3612,9 @@ async function readHostRelayConfig(path) {
|
|
|
3038
3612
|
} catch {
|
|
3039
3613
|
throw new HostRelayConfigError("invalid_json", "Host Relay config is not JSON.");
|
|
3040
3614
|
}
|
|
3041
|
-
|
|
3615
|
+
const config = decodeHostRelayConfig(value);
|
|
3616
|
+
validateHostRelaySourcePaths(config);
|
|
3617
|
+
return config;
|
|
3042
3618
|
} catch (error) {
|
|
3043
3619
|
if (error instanceof HostRelayConfigError) throw error;
|
|
3044
3620
|
throw new HostRelayConfigError("config_unavailable", "Host Relay config is unavailable.");
|
|
@@ -3046,6 +3622,13 @@ async function readHostRelayConfig(path) {
|
|
|
3046
3622
|
await handle?.close();
|
|
3047
3623
|
}
|
|
3048
3624
|
}
|
|
3625
|
+
function validateHostRelaySourcePaths(config) {
|
|
3626
|
+
try {
|
|
3627
|
+
assertHostRelaySourcePaths(dirname3(config.registration_state_file), config);
|
|
3628
|
+
} catch (error) {
|
|
3629
|
+
throw new HostRelayConfigError("invalid_config", error instanceof Error ? error.message : "Host Relay source tuple is invalid.");
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3049
3632
|
function decodeHostRelayConfig(value) {
|
|
3050
3633
|
const required = ["schema_version", "registration_state_file", "host_source_ref", "state_root", "ipc_endpoint"];
|
|
3051
3634
|
const optional = ["drain_interval_ms", "gateway_timeout_ms"];
|
|
@@ -3068,6 +3651,8 @@ function validConfigIdentity(value) {
|
|
|
3068
3651
|
function validConfigRuntime(value) {
|
|
3069
3652
|
return isAbsoluteNormalizedNonRootPath(value.state_root, process.platform, true) && isAbsoluteNormalizedNonRootPath(value.ipc_endpoint, process.platform, true) && boundedInteger(value.drain_interval_ms, 50, 3e4, true) && boundedInteger(value.gateway_timeout_ms, 100, 6e4, true);
|
|
3070
3653
|
}
|
|
3654
|
+
var HOST_RELAY_INVOCATION_MINT_LOG_TAG = "[host-relay-invocation-mint]";
|
|
3655
|
+
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");
|
|
3071
3656
|
async function serveHostRelay(config, signal, dependencies = {}) {
|
|
3072
3657
|
const admission = new RelayAdmissionStore(join6(config.state_root, "admission"));
|
|
3073
3658
|
const bindings = new RelayRequestBindingStore(join6(config.state_root, "bindings"));
|
|
@@ -3077,7 +3662,7 @@ async function serveHostRelay(config, signal, dependencies = {}) {
|
|
|
3077
3662
|
} });
|
|
3078
3663
|
let server;
|
|
3079
3664
|
try {
|
|
3080
|
-
server = await (
|
|
3665
|
+
server = await startHostRelayRuntimeIpc(config, admission, bindings, gateway, dependencies);
|
|
3081
3666
|
while (!signal.aborted) {
|
|
3082
3667
|
const round = await admission.drainRound((input) => bridge.deliver(input), { now_ms: (dependencies.now ?? Date.now)(), host_source_ref: config.host_source_ref });
|
|
3083
3668
|
if (round.delivered.length === 0 && round.refused.length === 0) await (dependencies.wait ?? ((milliseconds, waitSignal) => waitForAbort(waitSignal, milliseconds)))(config.drain_interval_ms, signal);
|
|
@@ -3086,41 +3671,52 @@ async function serveHostRelay(config, signal, dependencies = {}) {
|
|
|
3086
3671
|
await server?.close();
|
|
3087
3672
|
}
|
|
3088
3673
|
}
|
|
3089
|
-
async function
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
else callerSignal.addEventListener("abort", callerCancelled, { once: true });
|
|
3102
|
-
const cancelled = new Promise((_resolve, reject) => controller2.signal.addEventListener("abort", () => reject(new Error("invocation mint cancelled")), { once: true }));
|
|
3674
|
+
async function startHostRelayRuntimeIpc(config, admission, bindings, gateway, dependencies) {
|
|
3675
|
+
return (dependencies.startIpc ?? startHostRelayIpcServer)({
|
|
3676
|
+
endpoint: config.ipc_endpoint,
|
|
3677
|
+
admission,
|
|
3678
|
+
bindings,
|
|
3679
|
+
mintInvocationContext: (input, invocationSignal) => mintInvocationContext(gateway, input, invocationSignal),
|
|
3680
|
+
invocationMintTimeoutMs: config.gateway_timeout_ms,
|
|
3681
|
+
scheduleInvocationMintDeadline: dependencies.scheduleInvocationMintDeadline ?? scheduleInvocationMintDeadline2,
|
|
3682
|
+
observeInvocationMint: dependencies.observeInvocationMint ?? reportInvocationMint
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3685
|
+
async function mintInvocationContext(gateway, input, signal) {
|
|
3103
3686
|
try {
|
|
3104
|
-
|
|
3105
|
-
outcome = "completed";
|
|
3106
|
-
return result2;
|
|
3687
|
+
return await gateway.mintInvocationContext(input, signal);
|
|
3107
3688
|
} catch (error) {
|
|
3108
|
-
|
|
3109
|
-
throw error;
|
|
3110
|
-
}
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3689
|
+
const cancellation = signal.reason === "deadline" ? "deadline" : signal.reason === "caller_cancelled" ? "caller_cancelled" : null;
|
|
3690
|
+
throw new HostRelayInvocationMintRefusal(invocationMintRefusalReason(error, cancellation));
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
function invocationMintRefusalReason(error, cancellation) {
|
|
3694
|
+
if (cancellation === "deadline") return "deadline";
|
|
3695
|
+
if (cancellation === "caller_cancelled") return "cancelled";
|
|
3696
|
+
if (!(error instanceof HostRelayGatewayError)) return "mint_failed";
|
|
3697
|
+
if (error.code === "credential") return "host_credential_refused";
|
|
3698
|
+
if (error.code === "gateway_unavailable") return "gateway_unavailable";
|
|
3699
|
+
if (error.code === "invalid_response") return "gateway_response_invalid";
|
|
3700
|
+
if (error.code !== "gateway_refused") return "mint_failed";
|
|
3701
|
+
return (error.error_code === void 0 ? void 0 : GATEWAY_MINT_REFUSAL_REASONS.get(error.error_code)) ?? "gateway_refused";
|
|
3702
|
+
}
|
|
3703
|
+
var GATEWAY_MINT_REFUSAL_REASONS = /* @__PURE__ */ new Map([
|
|
3704
|
+
["request_context_refused", "context_stale"],
|
|
3705
|
+
["context_expired", "context_expired"],
|
|
3706
|
+
["host_source_refused", "host_source_refused"],
|
|
3707
|
+
["host_invocation_context_unavailable", "gateway_unavailable"]
|
|
3708
|
+
]);
|
|
3709
|
+
function scheduleInvocationMintDeadline2(expire, milliseconds) {
|
|
3117
3710
|
const timeout = setTimeout(expire, milliseconds);
|
|
3118
3711
|
return () => clearTimeout(timeout);
|
|
3119
3712
|
}
|
|
3120
3713
|
function reportInvocationMint(observation) {
|
|
3121
|
-
process.stderr.write(
|
|
3714
|
+
process.stderr.write(`${formatHostRelayInvocationMintLine(observation)}
|
|
3122
3715
|
`);
|
|
3123
3716
|
}
|
|
3717
|
+
function formatHostRelayInvocationMintLine(observation) {
|
|
3718
|
+
return `${HOST_RELAY_INVOCATION_MINT_LOG_TAG} outcome=${observation.outcome}${observation.reason === void 0 ? "" : ` reason=${observation.reason}`} elapsed_ms=${observation.elapsed_ms}`;
|
|
3719
|
+
}
|
|
3124
3720
|
async function runHostRelayCli(args, signal, dependencies = {}) {
|
|
3125
3721
|
try {
|
|
3126
3722
|
const configPath = parseServeArgs(args);
|
|
@@ -3174,6 +3770,7 @@ var controller = new AbortController();
|
|
|
3174
3770
|
var stop = () => controller.abort();
|
|
3175
3771
|
process.once("SIGINT", stop);
|
|
3176
3772
|
process.once("SIGTERM", stop);
|
|
3773
|
+
var stopLogBound = boundHostServiceLog();
|
|
3177
3774
|
void runHostRelayCli(process.argv.slice(2), controller.signal).then((result2) => {
|
|
3178
3775
|
process.stdout.write(`${JSON.stringify({ schema_version: "ceal.host_relay_command.v1", ...result2 })}
|
|
3179
3776
|
`);
|
|
@@ -3183,6 +3780,7 @@ void runHostRelayCli(process.argv.slice(2), controller.signal).then((result2) =>
|
|
|
3183
3780
|
`);
|
|
3184
3781
|
process.exitCode = 1;
|
|
3185
3782
|
}).finally(() => {
|
|
3783
|
+
stopLogBound();
|
|
3186
3784
|
process.removeListener("SIGINT", stop);
|
|
3187
3785
|
process.removeListener("SIGTERM", stop);
|
|
3188
3786
|
});
|