@corca-org/ceal-host-linux-arm64 1.0.19 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ceal-host +0 -0
- package/libexec/ceal-host/claude-hook.mjs +200 -75
- package/libexec/ceal-host/claude-invocation-authentication.mjs +174 -81
- package/libexec/ceal-host/claude-producer.mjs +212 -119
- package/libexec/ceal-host/codex-hook.mjs +216 -25
- package/libexec/ceal-host/codex-invocation-authentication.mjs +207 -46
- package/libexec/ceal-host/codex-producer.mjs +259 -90
- package/libexec/ceal-host/control.mjs +764 -212
- package/libexec/ceal-host/relay.mjs +92 -14
- package/package.json +1 -1
- package/share/ceal-host/bundle-manifest.json +12 -12
- package/share/ceal-host/bundle-manifest.sigstore.json +1 -1
|
@@ -3009,6 +3009,7 @@ var REQUIRED_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_f
|
|
|
3009
3009
|
var OPTIONAL_FIELDS = CEAL_CLIENT_WIRE_CONTRACT.command_response.bare_document_optional_fields;
|
|
3010
3010
|
|
|
3011
3011
|
// packages/ceal-client-protocol/src/resource-row.ts
|
|
3012
|
+
var CEAL_RESOURCE_ROW_TITLE_MATCH_DISPOSITIONS = vocabulary(["exact", "not_exact", "unknown"]);
|
|
3012
3013
|
var CEAL_RESOURCE_SEARCH_ORDERS = vocabulary(["relevance", "modified", "viewed", "created"]);
|
|
3013
3014
|
var CEAL_RESOURCE_SEARCH_DIRECTIONS = vocabulary(["ascending", "descending"]);
|
|
3014
3015
|
var CEAL_RESOURCE_SEARCH_FILTERS = vocabulary(["query", "modified_since", "since", "until", "authored_by_me", "authored_by", "include_replies", "kinds", "container_ref", "container_refs", "container_names", "addressed_to_me", "unread"]);
|
|
@@ -3092,6 +3093,7 @@ var RESOURCE_ROW_KEY_RULES = Object.freeze({
|
|
|
3092
3093
|
next: { required: true, timestamp: false },
|
|
3093
3094
|
kind: { required: true, timestamp: false },
|
|
3094
3095
|
matched_kinds: { required: false, timestamp: false },
|
|
3096
|
+
title_match: { required: false, timestamp: false },
|
|
3095
3097
|
title: { required: true, timestamp: false },
|
|
3096
3098
|
container_label: { required: true, timestamp: false },
|
|
3097
3099
|
container_ref: { required: false, timestamp: false },
|
|
@@ -3578,7 +3580,7 @@ var CEAL_HOST_AUTHENTICATION_POLICY = Object.freeze({
|
|
|
3578
3580
|
invalid_arguments: Object.freeze({ classification: "host_arguments_invalid", next_action: "answer_with_gap" }),
|
|
3579
3581
|
options_invalid: Object.freeze({ classification: "host_configuration_invalid", next_action: "answer_with_gap" }),
|
|
3580
3582
|
session_unavailable: Object.freeze({ classification: "host_session_unavailable", next_action: "answer_with_gap" }),
|
|
3581
|
-
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn;
|
|
3583
|
+
context_unavailable: Object.freeze({ classification: "host_context_unavailable", next_action: "refresh_host_invocation_context", remedy: "the Host has no invocation context from the current transcript turn; send a new message to start a fresh Host turn, then retry this command on that turn (run `ceal-host install` only if the Host hook registration is missing or drifted)" }),
|
|
3582
3584
|
context_replaced: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
3583
3585
|
no_binding: Object.freeze({ classification: "host_binding_unavailable", next_action: "refresh_host_invocation_context" }),
|
|
3584
3586
|
context_stale: Object.freeze({ classification: "host_invocation_context_stale", next_action: "refresh_host_invocation_context" }),
|
|
@@ -3631,6 +3633,7 @@ var CEAL_HOST_SERVICE_DIAGNOSTIC_EVENTS = vocabulary([
|
|
|
3631
3633
|
"watcher_turn_observed",
|
|
3632
3634
|
"current_prompt_registered",
|
|
3633
3635
|
"current_prompt_registration_failed",
|
|
3636
|
+
"current_prompt_capacity_exhausted",
|
|
3634
3637
|
"activation_recorded",
|
|
3635
3638
|
"activation_failed",
|
|
3636
3639
|
"watcher_scan_failed",
|
|
@@ -4186,6 +4189,21 @@ var MAX_STATE_BYTES = 8 * 1024 * 1024;
|
|
|
4186
4189
|
// host/relay/src/current-prompt-custody.ts
|
|
4187
4190
|
var HOST_CURRENT_PROMPT_MAX_BYTES = 256 * 1024;
|
|
4188
4191
|
|
|
4192
|
+
// host/service-log.ts
|
|
4193
|
+
function writeHostServiceDiagnostic(producer_id, event, failure_kind, cause, sink) {
|
|
4194
|
+
const line = `${JSON.stringify({ schema_version: CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA, producer_id, event, ...event === "watcher_scan_failed" && failure_kind !== void 0 ? { failure_kind } : {}, ...event === "watcher_scan_failed" && cause !== void 0 ? { cause } : {} })}
|
|
4195
|
+
`;
|
|
4196
|
+
try {
|
|
4197
|
+
(sink ?? defaultHostServiceDiagnosticSink)(line);
|
|
4198
|
+
} catch {
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
function defaultHostServiceDiagnosticSink(line) {
|
|
4202
|
+
process.stderr.write(line);
|
|
4203
|
+
}
|
|
4204
|
+
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
4205
|
+
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
4206
|
+
|
|
4189
4207
|
// host/relay/src/relay-ipc.ts
|
|
4190
4208
|
var HOST_RELAY_REQUEST_BINDING_KEYS = Object.freeze([
|
|
4191
4209
|
"schema_version",
|
|
@@ -4206,6 +4224,7 @@ var HOST_RELAY_IPC_RESPONSE_SCHEMA = "ceal.host_relay_admission.v1";
|
|
|
4206
4224
|
var HOST_RELAY_INVOCATION_CONTEXT_QUERY_SCHEMA = "ceal.host_relay_invocation_context_query.v1";
|
|
4207
4225
|
var HOST_RELAY_INVOCATION_CONTEXT_RESULT_SCHEMA = "ceal.host_relay_invocation_context_result.v1";
|
|
4208
4226
|
var HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA = "ceal.host_relay_current_prompt_register.v1";
|
|
4227
|
+
var HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA = "ceal.host_relay_current_prompt_release.v1";
|
|
4209
4228
|
var HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA = "ceal.host_relay_current_prompt_result.v1";
|
|
4210
4229
|
var HOST_RELAY_IPC_LENGTH_BYTES = Uint32Array.BYTES_PER_ELEMENT;
|
|
4211
4230
|
var HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES = 4096;
|
|
@@ -4265,6 +4284,12 @@ var HostRelayIpcError = class extends Error {
|
|
|
4265
4284
|
this.failure = failure2;
|
|
4266
4285
|
}
|
|
4267
4286
|
};
|
|
4287
|
+
var HostRelayCurrentPromptCapacityRefusal = class extends Error {
|
|
4288
|
+
constructor() {
|
|
4289
|
+
super("current_prompt_capacity_exhausted");
|
|
4290
|
+
this.name = "HostRelayCurrentPromptCapacityRefusal";
|
|
4291
|
+
}
|
|
4292
|
+
};
|
|
4268
4293
|
function hostRelayExchangeFailure(error) {
|
|
4269
4294
|
return error instanceof HostRelayIpcError ? error.failure : void 0;
|
|
4270
4295
|
}
|
|
@@ -4313,8 +4338,14 @@ var HostRelayCurrentPromptPort = class {
|
|
|
4313
4338
|
async register(input) {
|
|
4314
4339
|
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_REGISTER_SCHEMA, ...input };
|
|
4315
4340
|
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
4341
|
+
if (response2.status === "capacity_exhausted") throw new HostRelayCurrentPromptCapacityRefusal();
|
|
4316
4342
|
if (response2.status !== "registered") throw new HostRelayIpcError("relay_unavailable", response2.status);
|
|
4317
4343
|
}
|
|
4344
|
+
async release(identity) {
|
|
4345
|
+
const request2 = { schema_version: HOST_RELAY_CURRENT_PROMPT_RELEASE_SCHEMA, ...identity };
|
|
4346
|
+
const response2 = await exchange(this.endpoint, encodeLengthPrefixed(request2), this.timeout_ms, decodeCurrentPromptResponseBuffer);
|
|
4347
|
+
if (response2.status !== "released") throw new HostRelayIpcError("relay_unavailable", response2.status, "response_invalid");
|
|
4348
|
+
}
|
|
4318
4349
|
};
|
|
4319
4350
|
function submitHostProducerFrame(endpoint, frame, timeoutMs = 5e3) {
|
|
4320
4351
|
return exchange(endpoint, encodeHostProducerFrame(frame), timeoutMs, decodeResponseBuffer);
|
|
@@ -4406,7 +4437,7 @@ function decodeInvocationContextResponseBuffer(buffer) {
|
|
|
4406
4437
|
function decodeCurrentPromptResponseBuffer(buffer) {
|
|
4407
4438
|
const value = decodeLengthPrefixedJson(buffer, HOST_RELAY_ADMISSION_RESPONSE_MAX_BYTES);
|
|
4408
4439
|
if (value === null) return null;
|
|
4409
|
-
if (!record2(value) || !exactKeys3(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "current_prompt_unavailable") throw new HostRelayIpcError("relay_unavailable");
|
|
4440
|
+
if (!record2(value) || !exactKeys3(value, ["schema_version", "status"]) || value.schema_version !== HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA || value.status !== "registered" && value.status !== "capacity_exhausted" && value.status !== "current_prompt_unavailable" && value.status !== "released") throw new HostRelayIpcError("relay_unavailable", "Host Relay current-prompt response is invalid.", "response_invalid");
|
|
4410
4441
|
return { schema_version: HOST_RELAY_CURRENT_PROMPT_RESULT_SCHEMA, status: value.status };
|
|
4411
4442
|
}
|
|
4412
4443
|
function encodeInvocationContextQuery(query) {
|
|
@@ -4471,6 +4502,29 @@ function isHostTranscriptCursorReason(value) {
|
|
|
4471
4502
|
return typeof value === "string" && HOST_TRANSCRIPT_CURSOR_REASONS.some((reason) => reason === value);
|
|
4472
4503
|
}
|
|
4473
4504
|
var TRANSCRIPT_MISSING_REASON = "transcript_missing";
|
|
4505
|
+
var TRANSCRIPT_SCAN_READ_ERROR_ERRNOS = ["EBADF", "EACCES", "EIO", "EMFILE", "ENFILE", "ENOENT", "ENOTDIR", "EPERM"];
|
|
4506
|
+
function transcriptScanFailureKind(error, adapterFailureKind) {
|
|
4507
|
+
if (transcriptErrorCode(error) === "ELOOP") return "symlink";
|
|
4508
|
+
const adapter = adapterFailureKind(error);
|
|
4509
|
+
if (adapter !== void 0) return adapter;
|
|
4510
|
+
const code = transcriptErrorCode(error);
|
|
4511
|
+
if (code === "EFBIG") return "size_budget";
|
|
4512
|
+
if (code !== null && TRANSCRIPT_SCAN_READ_ERROR_ERRNOS.includes(code)) return "read_error";
|
|
4513
|
+
return "other";
|
|
4514
|
+
}
|
|
4515
|
+
async function registerHostCurrentPromptDiagnosed(register, diagnostic) {
|
|
4516
|
+
try {
|
|
4517
|
+
await register();
|
|
4518
|
+
diagnostic("current_prompt_registered");
|
|
4519
|
+
return true;
|
|
4520
|
+
} catch (error) {
|
|
4521
|
+
if (!(error instanceof HostRelayCurrentPromptCapacityRefusal)) diagnostic("current_prompt_registration_failed");
|
|
4522
|
+
throw error;
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
function transcriptErrorCode(error) {
|
|
4526
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
|
|
4527
|
+
}
|
|
4474
4528
|
var HOST_TRANSCRIPT_READ_CHUNK_BYTES = 256 * 1024;
|
|
4475
4529
|
async function readTranscriptCompleteChunk(handle, offset, size) {
|
|
4476
4530
|
const available = Math.max(0, size - offset);
|
|
@@ -4579,10 +4633,15 @@ var HostTranscriptWatcher = class {
|
|
|
4579
4633
|
return scanTranscriptRegistrations(await this.listRegistrations(), (registration) => this.scanRegistration(registration));
|
|
4580
4634
|
}
|
|
4581
4635
|
async scanRegistration(registration) {
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4636
|
+
try {
|
|
4637
|
+
return await this.openTranscript(registration, (handle) => this.scanOpened(handle, registration), {
|
|
4638
|
+
missing: () => this.retireMissing(registration),
|
|
4639
|
+
failed: (handle, error) => this.scanFailed(handle, registration, error)
|
|
4640
|
+
});
|
|
4641
|
+
} catch (error) {
|
|
4642
|
+
if (error instanceof HostRelayCurrentPromptCapacityRefusal) return { records: 0, enqueued: 0, quarantined: false };
|
|
4643
|
+
throw error;
|
|
4644
|
+
}
|
|
4586
4645
|
}
|
|
4587
4646
|
async openTranscript(registration, scan, handlers) {
|
|
4588
4647
|
let handle;
|
|
@@ -4591,7 +4650,7 @@ var HostTranscriptWatcher = class {
|
|
|
4591
4650
|
return await scan(handle);
|
|
4592
4651
|
} catch (error) {
|
|
4593
4652
|
if (handle === void 0 && isMissingTranscriptError(error)) return handlers.missing();
|
|
4594
|
-
if (hostRelayExchangeFailure(error) !== void 0) throw error;
|
|
4653
|
+
if (hostRelayExchangeFailure(error) !== void 0 || error instanceof HostRelayCurrentPromptCapacityRefusal) throw error;
|
|
4595
4654
|
return handlers.failed(handle, error);
|
|
4596
4655
|
} finally {
|
|
4597
4656
|
await handle?.close();
|
|
@@ -4792,17 +4851,6 @@ var DEFAULT_LIMITS = { global_count: 8192, global_bytes: 64 * 1024 * 1024, produ
|
|
|
4792
4851
|
var RETRY_SECONDS = [1, 2, 4, 8, 16, 32, 60];
|
|
4793
4852
|
var MAX_RETRYABLE_ATTEMPTS = RETRY_SECONDS.length + 10;
|
|
4794
4853
|
|
|
4795
|
-
// host/service-log.ts
|
|
4796
|
-
function writeHostServiceDiagnostic(producer_id, event, failure_kind) {
|
|
4797
|
-
try {
|
|
4798
|
-
process.stderr.write(`${JSON.stringify({ schema_version: CEAL_HOST_SERVICE_DIAGNOSTIC_SCHEMA, producer_id, event, ...event === "watcher_scan_failed" && failure_kind !== void 0 ? { failure_kind } : {} })}
|
|
4799
|
-
`);
|
|
4800
|
-
} catch {
|
|
4801
|
-
}
|
|
4802
|
-
}
|
|
4803
|
-
var HOST_SERVICE_LOG_MAX_BYTES = 1024 * 1024;
|
|
4804
|
-
var HOST_SERVICE_LOG_TAIL_BYTES = 64 * 1024;
|
|
4805
|
-
|
|
4806
4854
|
// host/invocation-authentication-command.ts
|
|
4807
4855
|
var HOST_INVOCATION_AUTHENTICATION_COMMAND_PREFIX = ["authenticate", "--client-nonce"];
|
|
4808
4856
|
var HOST_AUTHENTICATION_BUDGET_ENVIRONMENT = "CEAL_HOST_AUTHENTICATION_BUDGET_MS";
|
|
@@ -4899,36 +4947,36 @@ async function currentInvocation(descriptor2, config, now) {
|
|
|
4899
4947
|
let waitEngaged = false;
|
|
4900
4948
|
while (true) {
|
|
4901
4949
|
if (!firstCheck && monotonicNow() >= deadline) {
|
|
4902
|
-
recordActivationWaitExhaustion(descriptor2.producer_id, waitEngaged, waitBudgetMs);
|
|
4950
|
+
recordActivationWaitExhaustion(descriptor2.producer_id, waitEngaged, waitBudgetMs, config.diagnostic_sink);
|
|
4903
4951
|
throw new TypeError("context_unavailable");
|
|
4904
4952
|
}
|
|
4905
4953
|
firstCheck = false;
|
|
4906
4954
|
const observedAt = now();
|
|
4907
4955
|
const current = await readLiveSessionContext(store, identity.identity.host_source_ref, identity.identity.host_session_ref, observedAt);
|
|
4908
4956
|
if (current !== null) {
|
|
4909
|
-
recordActivationWaitCompletion(descriptor2.producer_id, waitEngaged);
|
|
4957
|
+
recordActivationWaitCompletion(descriptor2.producer_id, waitEngaged, config.diagnostic_sink);
|
|
4910
4958
|
return { store, current };
|
|
4911
4959
|
}
|
|
4912
4960
|
const remainingMs = deadline - monotonicNow();
|
|
4913
4961
|
if (remainingMs <= 0) {
|
|
4914
|
-
recordActivationWaitExhaustion(descriptor2.producer_id, waitEngaged, waitBudgetMs);
|
|
4962
|
+
recordActivationWaitExhaustion(descriptor2.producer_id, waitEngaged, waitBudgetMs, config.diagnostic_sink);
|
|
4915
4963
|
throw new TypeError("context_unavailable");
|
|
4916
4964
|
}
|
|
4917
|
-
recordActivationWaitEngagement(descriptor2.producer_id, waitEngaged);
|
|
4965
|
+
recordActivationWaitEngagement(descriptor2.producer_id, waitEngaged, config.diagnostic_sink);
|
|
4918
4966
|
waitEngaged = true;
|
|
4919
4967
|
const intervalMs = Math.min(INVOCATION_CONTEXT_POLL_INTERVAL_MS, remainingMs);
|
|
4920
4968
|
await (config.wait ?? waitForContextActivation)(intervalMs);
|
|
4921
4969
|
}
|
|
4922
4970
|
}
|
|
4923
|
-
function recordActivationWaitExhaustion(producer, engaged, budgetMs) {
|
|
4924
|
-
if (engaged || budgetMs > 0) writeHostServiceDiagnostic(producer, "activation_wait_exhausted");
|
|
4925
|
-
else writeHostServiceDiagnostic(producer, "activation_wait_skipped");
|
|
4971
|
+
function recordActivationWaitExhaustion(producer, engaged, budgetMs, sink) {
|
|
4972
|
+
if (engaged || budgetMs > 0) writeHostServiceDiagnostic(producer, "activation_wait_exhausted", void 0, void 0, sink);
|
|
4973
|
+
else writeHostServiceDiagnostic(producer, "activation_wait_skipped", void 0, void 0, sink);
|
|
4926
4974
|
}
|
|
4927
|
-
function recordActivationWaitCompletion(producer, engaged) {
|
|
4928
|
-
if (engaged) writeHostServiceDiagnostic(producer, "activation_wait_completed");
|
|
4975
|
+
function recordActivationWaitCompletion(producer, engaged, sink) {
|
|
4976
|
+
if (engaged) writeHostServiceDiagnostic(producer, "activation_wait_completed", void 0, void 0, sink);
|
|
4929
4977
|
}
|
|
4930
|
-
function recordActivationWaitEngagement(producer, engaged) {
|
|
4931
|
-
if (!engaged) writeHostServiceDiagnostic(producer, "activation_wait_engaged");
|
|
4978
|
+
function recordActivationWaitEngagement(producer, engaged, sink) {
|
|
4979
|
+
if (!engaged) writeHostServiceDiagnostic(producer, "activation_wait_engaged", void 0, void 0, sink);
|
|
4932
4980
|
}
|
|
4933
4981
|
async function readLiveSessionContext(store, hostSourceRef, hostSessionRef, now) {
|
|
4934
4982
|
const current = await store.readInvocationContext(hostSourceRef, hostSessionRef, now);
|
|
@@ -6236,6 +6284,8 @@ function codexSubmissionReference(hostInputRef, event, ordinal) {
|
|
|
6236
6284
|
var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
6237
6285
|
store;
|
|
6238
6286
|
registerCurrentPrompt;
|
|
6287
|
+
releaseCurrentPrompt;
|
|
6288
|
+
diagnostic;
|
|
6239
6289
|
constructor(options) {
|
|
6240
6290
|
super(options, {
|
|
6241
6291
|
default_retention_ms: CODEX_DEFAULT_RETENTION_MS,
|
|
@@ -6244,6 +6294,8 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6244
6294
|
});
|
|
6245
6295
|
this.store = new CodexOutboxStore(options.outbox_dir);
|
|
6246
6296
|
this.registerCurrentPrompt = options.register_current_prompt;
|
|
6297
|
+
this.releaseCurrentPrompt = options.release_current_prompt;
|
|
6298
|
+
this.diagnostic = options.diagnostic ?? (() => void 0);
|
|
6247
6299
|
}
|
|
6248
6300
|
transcriptPathOf(registration) {
|
|
6249
6301
|
return registration.transcript_path;
|
|
@@ -6259,17 +6311,22 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6259
6311
|
if (complete === "empty" || complete === "partial") return { records: 0, enqueued: 0, quarantined: false };
|
|
6260
6312
|
return await this.consumeCompleteChunk(registration, handle, preflight.cursor, complete, device, inode);
|
|
6261
6313
|
}
|
|
6262
|
-
async scanFailed(handle, registration,
|
|
6314
|
+
async scanFailed(handle, registration, error) {
|
|
6315
|
+
this.diagnostic("watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
6263
6316
|
await this.quarantineFailure(registration, handle);
|
|
6264
6317
|
return { records: 0, enqueued: 0, quarantined: true };
|
|
6265
6318
|
}
|
|
6266
6319
|
async retireMissing(registration) {
|
|
6320
|
+
await this.releaseStoredPromptBestEffort(registration.host_session_ref, await this.store.readTranscriptCursor(registration).catch(() => null));
|
|
6267
6321
|
return this.retireMissingTranscript(registration.transcript_path, "Codex", () => this.store.retireTranscriptRegistration(registration));
|
|
6268
6322
|
}
|
|
6269
6323
|
async preflight(registration, handle, size, device, inode) {
|
|
6270
6324
|
const existing = await this.store.readTranscriptCursor(registration);
|
|
6271
6325
|
let cursor = existing ?? codexTranscriptInitialCursor(registration, device, inode, this.now);
|
|
6272
|
-
if (cursor.status === "quarantined")
|
|
6326
|
+
if (cursor.status === "quarantined") {
|
|
6327
|
+
if (cursor.reason !== "transcript_malformed") await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
|
|
6328
|
+
return { cursor: null, result: { records: 0, enqueued: 0, quarantined: true } };
|
|
6329
|
+
}
|
|
6273
6330
|
const reason = this.cursorIdentityReason(cursor, registration.transcript_path, device, inode) ?? await this.cursorBoundsReason(handle, cursor, size);
|
|
6274
6331
|
if (reason === null) return { cursor, result: { records: 0, enqueued: 0, quarantined: false } };
|
|
6275
6332
|
await this.quarantine(registration, cursor, device, inode, reason);
|
|
@@ -6308,25 +6365,41 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6308
6365
|
const stats = await handle.stat().catch(() => null);
|
|
6309
6366
|
if (stats === null) return;
|
|
6310
6367
|
const existing = await this.store.readTranscriptCursor(registration).catch(() => null);
|
|
6311
|
-
|
|
6368
|
+
const quarantined = {
|
|
6369
|
+
...existing ?? codexTranscriptInitialCursor(registration, String(stats.dev), String(stats.ino), this.now),
|
|
6370
|
+
transcript_path: registration.transcript_path,
|
|
6371
|
+
device: String(stats.dev),
|
|
6372
|
+
inode: String(stats.ino),
|
|
6373
|
+
status: "quarantined",
|
|
6374
|
+
reason: "transcript_malformed",
|
|
6375
|
+
updated_at: this.stamp()
|
|
6376
|
+
};
|
|
6377
|
+
await this.store.writeTranscriptCursor(quarantined);
|
|
6312
6378
|
}
|
|
6313
6379
|
async processRecord(registration, activeTask, record3) {
|
|
6314
|
-
if (record3.kind === "task_started")
|
|
6315
|
-
return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
|
|
6316
|
-
}
|
|
6380
|
+
if (record3.kind === "task_started") return this.processTaskStarted(registration, activeTask, record3);
|
|
6317
6381
|
if (record3.kind === "turn_context") return { ...activeTask === void 0 ? {} : { activeTask: record3.turn_id === activeTask.codex_turn_id ? { ...activeTask, model: record3.model } : activeTask }, enqueued: 0 };
|
|
6318
6382
|
if (record3.kind === "token_count") return { ...activeTask === void 0 ? {} : { activeTask: accumulateTokenCount(activeTask, record3) }, enqueued: 0 };
|
|
6319
6383
|
if (record3.kind === "user_text") return this.processUserInput(registration, activeTask, record3);
|
|
6320
6384
|
if (record3.kind === "task_complete" || record3.kind === "turn_aborted") return this.processTerminal(registration, activeTask, record3);
|
|
6321
6385
|
return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
6322
6386
|
}
|
|
6387
|
+
async processTaskStarted(registration, activeTask, record3) {
|
|
6388
|
+
if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
6389
|
+
return { activeTask: { codex_turn_id: record3.turn_id, host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id), started_at: record3.timestamp }, enqueued: 0 };
|
|
6390
|
+
}
|
|
6323
6391
|
async processUserInput(registration, activeTask, record3) {
|
|
6324
|
-
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id)
|
|
6392
|
+
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id) {
|
|
6393
|
+
const adopted = await this.adoptHookCorrelatedTurn(registration, activeTask, record3);
|
|
6394
|
+
if (adopted === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
6395
|
+
activeTask = adopted;
|
|
6396
|
+
}
|
|
6325
6397
|
const hostInputRef = hostInputReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id, record3.message_id, record3.ordinal, record3.prompt);
|
|
6326
6398
|
const origin = activeTask.origin_host_input_ref === void 0;
|
|
6327
6399
|
const promptValue = promptObservationValue(record3.prompt);
|
|
6328
6400
|
const currentPromptRef = hostProducerPromptRef(promptValue.body_sha256);
|
|
6329
6401
|
const promptRef = activeTask.prompt_ref ?? currentPromptRef;
|
|
6402
|
+
const steeredPromptRef = await this.steeredPromptRef(registration, activeTask, origin, currentPromptRef);
|
|
6330
6403
|
const identity = { host_source_ref: registration.host_source_ref, host_session_ref: registration.host_session_ref, host_turn_ref: activeTask.host_turn_ref };
|
|
6331
6404
|
const observations = [{
|
|
6332
6405
|
observation_ref: observationRef("transcript_user_text", hostInputRef, record3.prompt),
|
|
@@ -6352,14 +6425,88 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6352
6425
|
created_at: record3.timestamp,
|
|
6353
6426
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
6354
6427
|
};
|
|
6355
|
-
await this.
|
|
6356
|
-
await this.
|
|
6357
|
-
|
|
6428
|
+
const persisted = await this.persistUserInputTurn(registration, activeTask, record3, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope);
|
|
6429
|
+
if (steeredPromptRef !== null) await this.activateTranscriptTurnContext(identity, steeredPromptRef, envelope.expires_at);
|
|
6430
|
+
return persisted;
|
|
6431
|
+
}
|
|
6432
|
+
/**
|
|
6433
|
+
* The steered prompt to re-activate under the running task, or null when
|
|
6434
|
+
* there is nothing to repair. A steered message joins the running turn
|
|
6435
|
+
* with new prompt bytes but no task_started. When the steer hook already
|
|
6436
|
+
* replaced the turn selection (a turn id the hook did not recognize as
|
|
6437
|
+
* the running one, or a hook that ran before any context was live), the
|
|
6438
|
+
* running task's context no longer matches and the next invocation would
|
|
6439
|
+
* wait for a boundary that never comes. When the hook kept the live
|
|
6440
|
+
* selection, the context still matches and this stays null.
|
|
6441
|
+
*/
|
|
6442
|
+
async steeredPromptRef(registration, activeTask, origin, currentPromptRef) {
|
|
6443
|
+
if (origin || activeTask.prompt_ref === void 0 || activeTask.prompt_ref === currentPromptRef) return null;
|
|
6444
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref).catch(() => null);
|
|
6445
|
+
return selection?.prompt_ref === currentPromptRef ? currentPromptRef : null;
|
|
6446
|
+
}
|
|
6447
|
+
/**
|
|
6448
|
+
* Adopt the live hooked turn when its transcript message arrives before
|
|
6449
|
+
* (or without) its task_started, as an interrupted turn's follow-up can.
|
|
6450
|
+
* The UserPromptSubmit hook names the exact hooked prompt, so only a
|
|
6451
|
+
* message carrying those bytes adopts; inherited history and other turns
|
|
6452
|
+
* stay ignored. A stale active task for another turn releases first, the
|
|
6453
|
+
* same as a task_started supersession.
|
|
6454
|
+
*/
|
|
6455
|
+
async adoptHookCorrelatedTurn(registration, activeTask, record3) {
|
|
6456
|
+
const currentPromptRef = hostProducerPromptRef(promptObservationValue(record3.prompt).body_sha256);
|
|
6457
|
+
const selection = await this.store.readInvocationTurnSelection(registration.host_source_ref, registration.host_session_ref);
|
|
6458
|
+
if (selection?.prompt_ref !== currentPromptRef) return void 0;
|
|
6459
|
+
if (activeTask !== void 0 && activeTask.codex_turn_id !== record3.turn_id && activeTask.prompt_ref !== void 0) {
|
|
6460
|
+
await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
6461
|
+
}
|
|
6462
|
+
return {
|
|
6463
|
+
codex_turn_id: record3.turn_id,
|
|
6464
|
+
host_turn_ref: hostTurnReference(registration.host_source_ref, registration.host_session_ref, record3.turn_id),
|
|
6465
|
+
started_at: record3.timestamp
|
|
6466
|
+
};
|
|
6467
|
+
}
|
|
6468
|
+
async persistUserInputTurn(registration, activeTask, record3, hostInputRef, origin, currentPromptRef, promptRef, identity, envelope) {
|
|
6469
|
+
const custodyRegistered = await this.registerPrompt(origin, registration, activeTask, currentPromptRef, record3.prompt, envelope.expires_at);
|
|
6470
|
+
if (custodyRegistered) this.diagnostic("watcher_turn_observed");
|
|
6471
|
+
try {
|
|
6472
|
+
await this.store.put(envelope);
|
|
6473
|
+
if (origin) await this.activateTranscriptTurnContext(identity, promptRef, envelope.expires_at);
|
|
6474
|
+
} catch (error) {
|
|
6475
|
+
if (custodyRegistered) await this.releasePrompt(registration.host_session_ref, activeTask, currentPromptRef).catch(() => void 0);
|
|
6476
|
+
throw error;
|
|
6477
|
+
}
|
|
6358
6478
|
return { activeTask: { ...activeTask, origin_host_input_ref: activeTask.origin_host_input_ref ?? hostInputRef, prompt_ref: activeTask.prompt_ref ?? currentPromptRef }, enqueued: 1 };
|
|
6359
6479
|
}
|
|
6360
6480
|
async registerPrompt(origin, registration, activeTask, promptRef, prompt, expiresAt) {
|
|
6361
|
-
if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return;
|
|
6362
|
-
|
|
6481
|
+
if (!origin || this.producerInstallationRef === void 0 || this.registerCurrentPrompt === void 0) return false;
|
|
6482
|
+
const register = this.registerCurrentPrompt;
|
|
6483
|
+
const installationRef = this.producerInstallationRef;
|
|
6484
|
+
return registerHostCurrentPromptDiagnosed(
|
|
6485
|
+
() => register({ producer_id: "codex", producer_installation_ref: installationRef, local_session_ref: registration.host_session_ref, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef, current_prompt: prompt, context_expires_at: expiresAt }),
|
|
6486
|
+
(event) => this.diagnostic(event)
|
|
6487
|
+
);
|
|
6488
|
+
}
|
|
6489
|
+
async activateTranscriptTurnContext(identity, promptRef, expiresAt) {
|
|
6490
|
+
try {
|
|
6491
|
+
const selection = await this.store.readInvocationTurnSelection(identity.host_source_ref, identity.host_session_ref);
|
|
6492
|
+
const turnGenerationRef = selection?.prompt_ref === promptRef ? selection.turn_generation_ref : void 0;
|
|
6493
|
+
await this.store.activateInvocationContext(identity, promptRef, expiresAt, turnGenerationRef);
|
|
6494
|
+
this.diagnostic("activation_recorded");
|
|
6495
|
+
} catch (error) {
|
|
6496
|
+
this.diagnostic("activation_failed");
|
|
6497
|
+
throw error;
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
async releasePrompt(sessionRef, activeTask, promptRef) {
|
|
6501
|
+
if (this.producerInstallationRef === void 0 || this.releaseCurrentPrompt === void 0) return;
|
|
6502
|
+
await this.releaseCurrentPrompt({ producer_id: "codex", producer_installation_ref: this.producerInstallationRef, local_session_ref: sessionRef, local_turn_ref: activeTask.host_turn_ref, prompt_ref: promptRef });
|
|
6503
|
+
}
|
|
6504
|
+
async releaseStoredPromptBestEffort(sessionRef, cursor) {
|
|
6505
|
+
await this.releaseActiveTaskBestEffort(sessionRef, cursor?.active_task);
|
|
6506
|
+
}
|
|
6507
|
+
async releaseActiveTaskBestEffort(sessionRef, activeTask) {
|
|
6508
|
+
if (activeTask?.prompt_ref === void 0) return;
|
|
6509
|
+
await this.releasePrompt(sessionRef, activeTask, activeTask.prompt_ref).catch(() => void 0);
|
|
6363
6510
|
}
|
|
6364
6511
|
async processTerminal(registration, activeTask, record3) {
|
|
6365
6512
|
if (activeTask === void 0 || activeTask.codex_turn_id !== record3.turn_id || activeTask.origin_host_input_ref === void 0 || activeTask.prompt_ref === void 0) return { ...activeTask === void 0 ? {} : { activeTask }, enqueued: 0 };
|
|
@@ -6379,18 +6526,23 @@ var CodexTranscriptWatcher = class extends HostTranscriptWatcher {
|
|
|
6379
6526
|
created_at: record3.timestamp,
|
|
6380
6527
|
expires_at: this.expire(this.outboxRetentionMs)
|
|
6381
6528
|
});
|
|
6529
|
+
await this.releasePrompt(registration.host_session_ref, activeTask, activeTask.prompt_ref);
|
|
6382
6530
|
await this.store.clearInvocationContext(identity, this.now());
|
|
6383
6531
|
return { enqueued: 1 };
|
|
6384
6532
|
}
|
|
6385
6533
|
async quarantine(registration, cursor, device, inode, reason) {
|
|
6534
|
+
await this.releaseStoredPromptBestEffort(registration.host_session_ref, cursor);
|
|
6386
6535
|
const quarantined = { ...cursor, transcript_path: registration.transcript_path, device, inode, status: "quarantined", reason, updated_at: this.stamp() };
|
|
6387
6536
|
await this.store.writeTranscriptCursor(quarantined);
|
|
6388
6537
|
}
|
|
6389
6538
|
};
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6539
|
+
function codexTranscriptScanFailureKind(error) {
|
|
6540
|
+
return transcriptScanFailureKind(error, (candidate) => {
|
|
6541
|
+
if (!(candidate instanceof CodexHostAdapterError)) return void 0;
|
|
6542
|
+
if (candidate.code === "payload_rejected") return "payload_rejected";
|
|
6543
|
+
if (candidate.code === "storage") return "read_error";
|
|
6544
|
+
return void 0;
|
|
6545
|
+
});
|
|
6394
6546
|
}
|
|
6395
6547
|
function accumulateTokenCount(activeTask, record3) {
|
|
6396
6548
|
if (record3.usage_gap_reason !== void 0) return { ...activeTask, token_usage_gap_reason: record3.usage_gap_reason };
|
|
@@ -6478,7 +6630,16 @@ function assertRecordSequence(registration, cursor, records, lastOrdinal, record
|
|
|
6478
6630
|
// host/codex/src/codex-producer-runtime.ts
|
|
6479
6631
|
async function drainCodexProducerOnce(options) {
|
|
6480
6632
|
const promptPort = new HostRelayCurrentPromptPort(options.relay_endpoint, options.timeout_ms);
|
|
6481
|
-
const scan = await new CodexTranscriptWatcher({
|
|
6633
|
+
const scan = await new CodexTranscriptWatcher({
|
|
6634
|
+
outbox_dir: options.outbox_dir,
|
|
6635
|
+
producer_installation_ref: options.producer_installation_ref,
|
|
6636
|
+
register_current_prompt: (input) => promptPort.register(input),
|
|
6637
|
+
release_current_prompt: (identity) => promptPort.release(identity),
|
|
6638
|
+
diagnostic: (event, failure_kind) => writeHostServiceDiagnostic("codex", event, failure_kind)
|
|
6639
|
+
}).scanOnce().catch((error) => {
|
|
6640
|
+
writeHostServiceDiagnostic("codex", "watcher_scan_failed", codexTranscriptScanFailureKind(error));
|
|
6641
|
+
throw error;
|
|
6642
|
+
});
|
|
6482
6643
|
const producer = new CodexProducer({ outbox_dir: options.outbox_dir, producer_installation_ref: options.producer_installation_ref, relay: new HostRelayIpcAdmissionPort(options.relay_endpoint, options.timeout_ms), ...options.producer_epoch === void 0 ? {} : { producer_epoch: options.producer_epoch }, ...options.now === void 0 ? {} : { now: options.now } });
|
|
6483
6644
|
return { scanned: scan.enqueued, ...await producer.drainOutbox() };
|
|
6484
6645
|
}
|