@byok-sdk/client 0.14.0 → 0.15.0
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 +15 -0
- package/dist/adapters/codex/codex-adapter.d.ts +2 -0
- package/dist/adapters/codex/process-runner.d.ts +16 -17
- package/dist/adapters/detect-outcome.d.ts +18 -0
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +189 -50
- package/dist/adapters/index.js.map +1 -1
- package/dist/bin/byok-agent-memory-mcp.js +29 -0
- package/dist/bin/byok-agent-memory-mcp.js.map +1 -1
- package/dist/bin/byok-agent-message-mcp.js +29 -0
- package/dist/bin/byok-agent-message-mcp.js.map +1 -1
- package/dist/bin/byok-agent-team-mcp.js +29 -0
- package/dist/bin/byok-agent-team-mcp.js.map +1 -1
- package/dist/bin/byok-agent.js +1832 -932
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/bin/byok-approval-mcp.js +29 -0
- package/dist/bin/byok-approval-mcp.js.map +1 -1
- package/dist/bin/byok-mcp-env.d.ts +2 -0
- package/dist/bin/byok-mcp-env.js +36 -0
- package/dist/bin/byok-mcp-env.js.map +1 -0
- package/dist/bin/commands/doctor.d.ts +3 -0
- package/dist/bin/mcp-env-launcher.d.ts +5 -0
- package/dist/bin/runtime-probe.d.ts +6 -7
- package/dist/daemon/admission-wait.d.ts +2 -0
- package/dist/daemon/agent-egress-spool.d.ts +1 -0
- package/dist/daemon/agent-message-outbox.d.ts +15 -3
- package/dist/daemon/artifact-read.d.ts +7 -0
- package/dist/daemon/connection-manager.d.ts +18 -77
- package/dist/daemon/create-daemon.d.ts +11 -1
- package/dist/daemon/runtime-start.d.ts +3 -0
- package/dist/daemon/store.d.ts +2 -0
- package/dist/daemon/task-runner.d.ts +23 -0
- package/dist/daemon/terminal-commit-queue.d.ts +21 -0
- package/dist/daemon/terminal-identity.d.ts +6 -0
- package/dist/diagnostics/device-doctor.d.ts +46 -0
- package/dist/diagnostics/diagnostics.d.ts +3 -80
- package/dist/diagnostics/types.d.ts +82 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1942 -423
- package/dist/index.js.map +1 -1
- package/dist/runtime-detection.d.ts +3 -0
- package/dist/runtime-failure.d.ts +6 -0
- package/dist/sdk-reserved-helper-host.d.ts +1 -1
- package/dist/types.d.ts +20 -10
- package/dist/util/durable-jsonl.d.ts +12 -0
- package/package.json +4 -4
package/dist/bin/byok-agent.js
CHANGED
|
@@ -11,7 +11,7 @@ import { execFile, spawn, spawnSync } from 'child_process';
|
|
|
11
11
|
import net, { createServer, createConnection } from 'net';
|
|
12
12
|
import { isTenantId, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_MAX_TTL_MS, nonceSigningBytes, CapabilityDeclarationSchema, hasCapability, DeviceAssertionClaimsSchema, deviceAssertionSigningInput, DEVICE_ASSERTION_SCHEMA_ID } from '@byok-sdk/core';
|
|
13
13
|
import * as fs5 from 'fs/promises';
|
|
14
|
-
import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, TASK_STATES, AgentEgressPolicySchema, AgentHomeProjectionPayloadSchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentRefSchema, AgentContentReceiptPayloadSchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, isTaskOfferType, partitionAgentEvents, TASK_TRANSITIONS, decodeEnvelope, createEnvelope, encodeEnvelope, PROTOCOL_VERSION, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, TERMINAL_PROJECTION_SELECTION_CAPABILITY, PROVIDER_PROFILE_BINDING_CAPABILITY, STRICT_AGENT_ONLY_CAPABILITY, AGENT_HOME_PROJECTION_CAPABILITY, AGENT_EGRESS_POLICY_CAPABILITY, AGENT_EGRESS_FRESH_SESSION_CAPABILITY, AGENT_MESSAGE_EGRESS_CAPABILITY, AgentHomeProjectionCompletionRequestSchema, byokAgentHomeProjectionCompletionPath, AgentHomeProjectionReadbackSchema, parseMessage, checkResultDocument, MAX_MESSAGES_PER_BATCH, isServerToDaemonType, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, AgentMessagePublishPayloadSchema, AgentMessageDispositionPayloadSchema,
|
|
14
|
+
import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, TASK_STATES, AgentEgressPolicySchema, AgentHomeProjectionPayloadSchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentRefSchema, AgentContentReceiptPayloadSchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, isTaskOfferType, partitionAgentEvents, TASK_TRANSITIONS, decodeEnvelope, createEnvelope, encodeEnvelope, PROTOCOL_VERSION, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, RuntimeIdSchema, HarnessIdSchema, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, TERMINAL_PROJECTION_SELECTION_CAPABILITY, PROVIDER_PROFILE_BINDING_CAPABILITY, STRICT_AGENT_ONLY_CAPABILITY, AGENT_HOME_PROJECTION_CAPABILITY, AGENT_EGRESS_POLICY_CAPABILITY, AGENT_EGRESS_FRESH_SESSION_CAPABILITY, AGENT_MESSAGE_EGRESS_CAPABILITY, AgentHomeProjectionCompletionRequestSchema, byokAgentHomeProjectionCompletionPath, AgentHomeProjectionReadbackSchema, parseMessage, checkResultDocument, MAX_MESSAGES_PER_BATCH, isServerToDaemonType, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, AgentMessagePublishPayloadSchema, AgentMessageDispositionPayloadSchema, AGENT_MEMORY_PROJECTION_CAPABILITY, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, MAILBOX_READ_AHEAD_MAX_SEQS, MAILBOX_READ_AHEAD_CAPABILITY, MESSAGE_TYPES, AGENT_MEMORY_PROJECTION_MAX_ORDERING_VALUE, AgentMemoryProjectionMutationSchema, AGENT_MEMORY_PROJECTION_MAX_REDACTED_BYTES } from '@byok-sdk/protocol';
|
|
15
15
|
import { promisify } from 'util';
|
|
16
16
|
import { createRequire } from 'module';
|
|
17
17
|
import { createInterface as createInterface$1 } from 'readline/promises';
|
|
@@ -1418,7 +1418,8 @@ var DIST_SCRIPT_BY_KIND = Object.freeze({
|
|
|
1418
1418
|
"agent-message-mcp": "byok-agent-message-mcp.js",
|
|
1419
1419
|
"agent-memory-mcp": "byok-agent-memory-mcp.js",
|
|
1420
1420
|
"approval-mcp": "byok-approval-mcp.js",
|
|
1421
|
-
"agent-team-mcp": "byok-agent-team-mcp.js"
|
|
1421
|
+
"agent-team-mcp": "byok-agent-team-mcp.js",
|
|
1422
|
+
"mcp-env": "byok-mcp-env.js"
|
|
1422
1423
|
});
|
|
1423
1424
|
function assertExecutable(executable) {
|
|
1424
1425
|
if (!path14__default.isAbsolute(executable) || /[\u0000\r\n]/u.test(executable)) {
|
|
@@ -1436,8 +1437,11 @@ function resolveSdkReservedHelperBin(kind, host) {
|
|
|
1436
1437
|
source: "self-executable"
|
|
1437
1438
|
});
|
|
1438
1439
|
}
|
|
1440
|
+
const entryDir = path14__default.dirname(fileURLToPath(import.meta.url));
|
|
1441
|
+
const entryKind = path14__default.basename(entryDir);
|
|
1442
|
+
const distDir = entryKind === "adapters" || entryKind === "bin" ? path14__default.dirname(entryDir) : entryDir;
|
|
1439
1443
|
const script = path14__default.join(
|
|
1440
|
-
|
|
1444
|
+
distDir,
|
|
1441
1445
|
"bin",
|
|
1442
1446
|
DIST_SCRIPT_BY_KIND[kind]
|
|
1443
1447
|
);
|
|
@@ -1915,6 +1919,20 @@ var DeviceStore = class _DeviceStore {
|
|
|
1915
1919
|
await opened.handle.close();
|
|
1916
1920
|
}
|
|
1917
1921
|
}
|
|
1922
|
+
/** Rebuild only the non-secret projection, while the caller owns the store lease. */
|
|
1923
|
+
async reconcileMetadata(authority) {
|
|
1924
|
+
const projection = {
|
|
1925
|
+
deviceId: authority.deviceId,
|
|
1926
|
+
tenantId: authority.tenantId,
|
|
1927
|
+
devicePublicKey: authority.devicePublicKey
|
|
1928
|
+
};
|
|
1929
|
+
const current = await this.load();
|
|
1930
|
+
if (current !== void 0 && current.deviceId === projection.deviceId && current.tenantId === projection.tenantId && current.devicePublicKey === projection.devicePublicKey) {
|
|
1931
|
+
return false;
|
|
1932
|
+
}
|
|
1933
|
+
await this.save(projection);
|
|
1934
|
+
return true;
|
|
1935
|
+
}
|
|
1918
1936
|
async save(record4) {
|
|
1919
1937
|
assertDeviceMetadata(record4);
|
|
1920
1938
|
const storeDir = path14__default.dirname(this.filePath);
|
|
@@ -2361,7 +2379,7 @@ function resolveLocalAgentReleaseIdentity(input) {
|
|
|
2361
2379
|
|
|
2362
2380
|
// src/bin/official-release.ts
|
|
2363
2381
|
var OFFICIAL_LOCAL_AGENT_RELEASE = resolveLocalAgentReleaseIdentity({
|
|
2364
|
-
version: "0.
|
|
2382
|
+
version: "0.15.0"
|
|
2365
2383
|
});
|
|
2366
2384
|
|
|
2367
2385
|
// src/bin/config.ts
|
|
@@ -2762,6 +2780,19 @@ function projectRuntimeBoundaryFailure(value, expectedPhase) {
|
|
|
2762
2780
|
contractViolation: true
|
|
2763
2781
|
};
|
|
2764
2782
|
}
|
|
2783
|
+
var RuntimeStartupDisposalFailure = class extends Error {
|
|
2784
|
+
retryDisposal;
|
|
2785
|
+
constructor(retryDisposal, options) {
|
|
2786
|
+
super("runtime startup failed and process ownership remains quarantined", options);
|
|
2787
|
+
this.name = "RuntimeStartupDisposalFailure";
|
|
2788
|
+
this.retryDisposal = retryDisposal;
|
|
2789
|
+
Object.defineProperty(this, /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeStartupDisposalFailure/v1"), { value: true });
|
|
2790
|
+
Object.freeze(this);
|
|
2791
|
+
}
|
|
2792
|
+
};
|
|
2793
|
+
function isRuntimeStartupDisposalFailure(value) {
|
|
2794
|
+
return typeof value === "object" && value !== null && value[/* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeStartupDisposalFailure/v1")] === true && typeof value.retryDisposal === "function";
|
|
2795
|
+
}
|
|
2765
2796
|
|
|
2766
2797
|
// src/adapters/taskkill-pid-set.ts
|
|
2767
2798
|
var INTEGER_PATTERN = /\d+/g;
|
|
@@ -5519,6 +5550,7 @@ async function acquireDaemonOwner(storeDir, role, clock = () => /* @__PURE__ */
|
|
|
5519
5550
|
}
|
|
5520
5551
|
|
|
5521
5552
|
// src/types.ts
|
|
5553
|
+
var RUNTIME_DETECTION_FAILURE_KINDS = ["not-found", "not-executable", "timeout", "probe-failed"];
|
|
5522
5554
|
function frozenStrings(values) {
|
|
5523
5555
|
return values === void 0 ? void 0 : Object.freeze([...values]);
|
|
5524
5556
|
}
|
|
@@ -5743,6 +5775,132 @@ var GitWorkspaceStore = class {
|
|
|
5743
5775
|
await atomicWriteFile(this.filePath, JSON.stringify(ledger, null, 2), { mode: 384 });
|
|
5744
5776
|
}
|
|
5745
5777
|
};
|
|
5778
|
+
function terminalIdentity(runtimeId, agentRef) {
|
|
5779
|
+
return {
|
|
5780
|
+
...runtimeId === void 0 || RuntimeIdSchema.safeParse(runtimeId).success ? {} : { harnessId: HarnessIdSchema.parse(runtimeId) },
|
|
5781
|
+
...agentRef === void 0 ? {} : { agentRef }
|
|
5782
|
+
};
|
|
5783
|
+
}
|
|
5784
|
+
|
|
5785
|
+
// src/daemon/terminal-commit-queue.ts
|
|
5786
|
+
var TerminalCommitQueue = class {
|
|
5787
|
+
constructor(committed) {
|
|
5788
|
+
this.committed = committed;
|
|
5789
|
+
}
|
|
5790
|
+
committed;
|
|
5791
|
+
pending = /* @__PURE__ */ new Map();
|
|
5792
|
+
tail = Promise.resolve();
|
|
5793
|
+
timer;
|
|
5794
|
+
stopped = false;
|
|
5795
|
+
get pendingCount() {
|
|
5796
|
+
return this.pending.size;
|
|
5797
|
+
}
|
|
5798
|
+
hasPending(taskId) {
|
|
5799
|
+
return this.pending.has(taskId);
|
|
5800
|
+
}
|
|
5801
|
+
enqueue(taskId, commit) {
|
|
5802
|
+
if (!this.pending.has(taskId)) this.pending.set(taskId, { commit, receipt: Promise.resolve(), running: false });
|
|
5803
|
+
void this.attempt(taskId).catch(() => void 0);
|
|
5804
|
+
}
|
|
5805
|
+
receipt(taskId) {
|
|
5806
|
+
return this.pending.get(taskId)?.receipt ?? Promise.resolve();
|
|
5807
|
+
}
|
|
5808
|
+
retry(taskId) {
|
|
5809
|
+
return this.attempt(taskId);
|
|
5810
|
+
}
|
|
5811
|
+
resume() {
|
|
5812
|
+
this.stopped = false;
|
|
5813
|
+
if (this.pending.size > 0) this.armRetry();
|
|
5814
|
+
}
|
|
5815
|
+
async stop() {
|
|
5816
|
+
this.stopped = true;
|
|
5817
|
+
if (this.timer) clearTimeout(this.timer);
|
|
5818
|
+
this.timer = void 0;
|
|
5819
|
+
await Promise.all([...this.pending.keys()].map((taskId) => this.attempt(taskId)));
|
|
5820
|
+
}
|
|
5821
|
+
attempt(taskId) {
|
|
5822
|
+
const entry = this.pending.get(taskId);
|
|
5823
|
+
if (!entry) return Promise.resolve();
|
|
5824
|
+
if (entry.running) return entry.receipt;
|
|
5825
|
+
entry.running = true;
|
|
5826
|
+
const receipt = this.tail.catch(() => void 0).then(entry.commit);
|
|
5827
|
+
entry.receipt = receipt.then(() => {
|
|
5828
|
+
if (this.pending.get(taskId) === entry) this.pending.delete(taskId);
|
|
5829
|
+
this.committed(taskId);
|
|
5830
|
+
}, (error) => {
|
|
5831
|
+
entry.running = false;
|
|
5832
|
+
this.armRetry();
|
|
5833
|
+
throw error;
|
|
5834
|
+
});
|
|
5835
|
+
this.tail = entry.receipt;
|
|
5836
|
+
void entry.receipt.catch(() => void 0);
|
|
5837
|
+
return entry.receipt;
|
|
5838
|
+
}
|
|
5839
|
+
armRetry() {
|
|
5840
|
+
if (this.timer || this.stopped) return;
|
|
5841
|
+
this.timer = setTimeout(() => {
|
|
5842
|
+
this.timer = void 0;
|
|
5843
|
+
for (const taskId of this.pending.keys()) void this.attempt(taskId).catch(() => void 0);
|
|
5844
|
+
}, 1e3);
|
|
5845
|
+
this.timer.unref?.();
|
|
5846
|
+
}
|
|
5847
|
+
};
|
|
5848
|
+
|
|
5849
|
+
// src/runtime-detection.ts
|
|
5850
|
+
function validateRuntimeDetectResult(value) {
|
|
5851
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
5852
|
+
throw new TypeError("invalid runtime detection result");
|
|
5853
|
+
}
|
|
5854
|
+
const result = value;
|
|
5855
|
+
if (!Object.hasOwn(result, "kind")) throw new TypeError("invalid runtime detection result");
|
|
5856
|
+
const { kind, version, authPresent } = result;
|
|
5857
|
+
if (kind === "available") {
|
|
5858
|
+
if (Object.keys(result).some((key) => !["kind", "version", "authPresent"].includes(key)) || version !== void 0 && typeof version !== "string" || authPresent !== void 0 && typeof authPresent !== "boolean") {
|
|
5859
|
+
throw new TypeError("invalid runtime detection result");
|
|
5860
|
+
}
|
|
5861
|
+
return Object.freeze({
|
|
5862
|
+
kind,
|
|
5863
|
+
...version === void 0 ? {} : { version },
|
|
5864
|
+
...authPresent === void 0 ? {} : { authPresent }
|
|
5865
|
+
});
|
|
5866
|
+
}
|
|
5867
|
+
if (RUNTIME_DETECTION_FAILURE_KINDS.some((candidate) => candidate === kind) && Object.keys(result).every((key) => key === "kind")) {
|
|
5868
|
+
return Object.freeze({ kind });
|
|
5869
|
+
}
|
|
5870
|
+
throw new TypeError("invalid runtime detection result");
|
|
5871
|
+
}
|
|
5872
|
+
function classifyDetectError(error) {
|
|
5873
|
+
const code = error !== null && typeof error === "object" ? error.code : void 0;
|
|
5874
|
+
if (code === "ENOENT") return { kind: "not-found" };
|
|
5875
|
+
if (code === "EACCES" || code === "EPERM" || code === "ENOEXEC") return { kind: "not-executable" };
|
|
5876
|
+
return { kind: "probe-failed" };
|
|
5877
|
+
}
|
|
5878
|
+
async function probeRuntimeVersion(command, timeoutMs) {
|
|
5879
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) throw new TypeError("invalid runtime probe timeout");
|
|
5880
|
+
return new Promise((resolve) => {
|
|
5881
|
+
let timer;
|
|
5882
|
+
let timedOut = false;
|
|
5883
|
+
try {
|
|
5884
|
+
const child = execFile(command, ["--version"], { encoding: "utf8" }, (error, stdout, stderr) => {
|
|
5885
|
+
if (timer) clearTimeout(timer);
|
|
5886
|
+
if (error?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") resolve({ kind: "probe-failed" });
|
|
5887
|
+
else if (timedOut) resolve({ kind: "timeout" });
|
|
5888
|
+
else if (error) resolve(classifyDetectError(error));
|
|
5889
|
+
else resolve({ kind: "available", stdout, stderr });
|
|
5890
|
+
});
|
|
5891
|
+
timer = setTimeout(() => {
|
|
5892
|
+
timedOut = true;
|
|
5893
|
+
child.stdout?.destroy();
|
|
5894
|
+
child.stderr?.destroy();
|
|
5895
|
+
child.kill("SIGKILL");
|
|
5896
|
+
}, timeoutMs);
|
|
5897
|
+
timer.unref?.();
|
|
5898
|
+
} catch (error) {
|
|
5899
|
+
if (timer) clearTimeout(timer);
|
|
5900
|
+
resolve(classifyDetectError(error));
|
|
5901
|
+
}
|
|
5902
|
+
});
|
|
5903
|
+
}
|
|
5746
5904
|
|
|
5747
5905
|
// src/adapters/pi/mcp-config.ts
|
|
5748
5906
|
var BYOK_PI_MCP_CONFIG_PATH = "BYOK_PI_MCP_CONFIG_PATH";
|
|
@@ -6076,12 +6234,13 @@ var PiAdapter = class {
|
|
|
6076
6234
|
async detect() {
|
|
6077
6235
|
try {
|
|
6078
6236
|
const bin = this.resolveBin();
|
|
6079
|
-
const
|
|
6080
|
-
|
|
6237
|
+
const probe = await probeRuntimeVersion(bin.command, DETECT_TIMEOUT_MS);
|
|
6238
|
+
if (probe.kind !== "available") return probe;
|
|
6239
|
+
const version = probe.stdout.trim() || probe.stderr.trim();
|
|
6081
6240
|
const authPresent = PROVIDER_CREDENTIAL_ENV_NAMES.some((name) => process.env[name] !== void 0);
|
|
6082
|
-
return {
|
|
6083
|
-
} catch {
|
|
6084
|
-
return
|
|
6241
|
+
return { kind: "available", version, authPresent };
|
|
6242
|
+
} catch (error) {
|
|
6243
|
+
return classifyDetectError(error);
|
|
6085
6244
|
}
|
|
6086
6245
|
}
|
|
6087
6246
|
async prepare(input) {
|
|
@@ -7211,14 +7370,15 @@ var ClaudeAdapter = class {
|
|
|
7211
7370
|
environmentRequirements: { credentialNames: [] }
|
|
7212
7371
|
});
|
|
7213
7372
|
async detect() {
|
|
7214
|
-
const bin = this.resolveBin();
|
|
7215
7373
|
try {
|
|
7216
|
-
const
|
|
7217
|
-
const
|
|
7374
|
+
const bin = this.resolveBin();
|
|
7375
|
+
const probe = await probeRuntimeVersion(bin.command, DETECT_TIMEOUT_MS2);
|
|
7376
|
+
if (probe.kind !== "available") return probe;
|
|
7377
|
+
const version = probe.stdout.trim();
|
|
7218
7378
|
const authPresent = await this.probeAuthPresent(bin.command);
|
|
7219
|
-
return {
|
|
7220
|
-
} catch {
|
|
7221
|
-
return
|
|
7379
|
+
return { kind: "available", version, authPresent };
|
|
7380
|
+
} catch (error) {
|
|
7381
|
+
return classifyDetectError(error);
|
|
7222
7382
|
}
|
|
7223
7383
|
}
|
|
7224
7384
|
async prepare(input) {
|
|
@@ -7837,10 +7997,18 @@ function unmappedFrameKey(evt) {
|
|
|
7837
7997
|
return evt.type;
|
|
7838
7998
|
}
|
|
7839
7999
|
var STDERR_RING_CAPACITY3 = 20;
|
|
8000
|
+
var CODEX_MAX_FRAME_BYTES = 1024 * 1024;
|
|
8001
|
+
var CODEX_MAX_DEFERRED_BYTES = 4 * 1024 * 1024;
|
|
8002
|
+
var CODEX_MAX_STDERR_BYTES = 64 * 1024;
|
|
7840
8003
|
var CodexProcessRunner = class {
|
|
7841
8004
|
child;
|
|
7842
8005
|
onEvent;
|
|
7843
|
-
|
|
8006
|
+
frameChunks = [];
|
|
8007
|
+
frameBytes = 0;
|
|
8008
|
+
deferredBytes = 0;
|
|
8009
|
+
stderrBytes = 0;
|
|
8010
|
+
transportFailure;
|
|
8011
|
+
onFailure;
|
|
7844
8012
|
stderrRing = [];
|
|
7845
8013
|
closed = false;
|
|
7846
8014
|
exitCode = null;
|
|
@@ -7863,11 +8031,12 @@ var CodexProcessRunner = class {
|
|
|
7863
8031
|
deferredEvents = [];
|
|
7864
8032
|
constructor(options) {
|
|
7865
8033
|
this.onEvent = options.onEvent;
|
|
8034
|
+
this.onFailure = options.onFailure;
|
|
7866
8035
|
const spawnFn = options.spawnFn ?? spawn;
|
|
7867
8036
|
this.child = spawnFn(options.command, options.args, withOwnedProcessTree({
|
|
7868
8037
|
cwd: options.cwd,
|
|
7869
8038
|
env: options.env,
|
|
7870
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
8039
|
+
stdio: [options.instruction === void 0 ? "ignore" : "pipe", "pipe", "pipe"]
|
|
7871
8040
|
}));
|
|
7872
8041
|
this.closedPromise = new Promise((resolve) => {
|
|
7873
8042
|
this.resolveClosed = resolve;
|
|
@@ -7875,7 +8044,6 @@ var CodexProcessRunner = class {
|
|
|
7875
8044
|
this.adopted = this.adoptOwnedTree(options);
|
|
7876
8045
|
this.adopted.catch(() => {
|
|
7877
8046
|
});
|
|
7878
|
-
this.child.stdout.setEncoding("utf8");
|
|
7879
8047
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
7880
8048
|
this.child.stderr.setEncoding("utf8");
|
|
7881
8049
|
this.child.stderr.on("data", (chunk) => this.onStderr(chunk));
|
|
@@ -7884,6 +8052,12 @@ var CodexProcessRunner = class {
|
|
|
7884
8052
|
this.exitSignal = signal;
|
|
7885
8053
|
this.finishClosing();
|
|
7886
8054
|
});
|
|
8055
|
+
if (options.instruction !== void 0) {
|
|
8056
|
+
this.child.stdin.on("error", () => {
|
|
8057
|
+
this.failTransport("codex prompt stdin failed before delivery");
|
|
8058
|
+
});
|
|
8059
|
+
this.child.stdin.end(options.instruction, "utf8");
|
|
8060
|
+
}
|
|
7887
8061
|
this.child.on("error", () => {
|
|
7888
8062
|
this.finishClosing();
|
|
7889
8063
|
});
|
|
@@ -7969,12 +8143,19 @@ var CodexProcessRunner = class {
|
|
|
7969
8143
|
throw failure;
|
|
7970
8144
|
}
|
|
7971
8145
|
this.adoption = "adopted";
|
|
8146
|
+
this.deferredBytes = 0;
|
|
7972
8147
|
for (const evt of this.deferredEvents.splice(0)) this.onEvent(evt);
|
|
7973
8148
|
}
|
|
7974
8149
|
/** Arrival-order delivery, held back until the tree is backstopped (see `deferredEvents`). */
|
|
7975
|
-
deliver(evt) {
|
|
8150
|
+
deliver(evt, bytes) {
|
|
8151
|
+
if (this.transportFailure) return;
|
|
7976
8152
|
if (this.adoption === "failed") return;
|
|
7977
8153
|
if (this.adoption === "pending") {
|
|
8154
|
+
if (this.deferredBytes + bytes > CODEX_MAX_DEFERRED_BYTES) {
|
|
8155
|
+
this.failTransport("codex pre-adoption event buffer exceeded its byte budget");
|
|
8156
|
+
return;
|
|
8157
|
+
}
|
|
8158
|
+
this.deferredBytes += bytes;
|
|
7978
8159
|
this.deferredEvents.push(evt);
|
|
7979
8160
|
return;
|
|
7980
8161
|
}
|
|
@@ -7991,6 +8172,7 @@ var CodexProcessRunner = class {
|
|
|
7991
8172
|
* plus an empty stderr tail would bury the only reason anyone can act on.
|
|
7992
8173
|
*/
|
|
7993
8174
|
buildExitError(context) {
|
|
8175
|
+
if (this.transportFailure) return this.transportFailure;
|
|
7994
8176
|
if (this.adoptionFailure !== void 0) return this.adoptionFailure;
|
|
7995
8177
|
const parts = [`${context} (exit code=${this.exitCode}, signal=${this.exitSignal})`];
|
|
7996
8178
|
if (this.stderrRing.length > 0) {
|
|
@@ -7998,18 +8180,45 @@ var CodexProcessRunner = class {
|
|
|
7998
8180
|
}
|
|
7999
8181
|
return new Error(parts.join("; "));
|
|
8000
8182
|
}
|
|
8183
|
+
failTransport(reason) {
|
|
8184
|
+
if (this.transportFailure) return;
|
|
8185
|
+
this.transportFailure = new RuntimeExecutionFailure({
|
|
8186
|
+
phase: "run",
|
|
8187
|
+
category: "infrastructure",
|
|
8188
|
+
retry: "non-retryable",
|
|
8189
|
+
reason
|
|
8190
|
+
});
|
|
8191
|
+
this.frameChunks.length = 0;
|
|
8192
|
+
this.frameBytes = 0;
|
|
8193
|
+
this.deferredEvents.length = 0;
|
|
8194
|
+
this.deferredBytes = 0;
|
|
8195
|
+
this.onFailure?.(this.transportFailure);
|
|
8196
|
+
this.kill();
|
|
8197
|
+
}
|
|
8001
8198
|
onData(chunk) {
|
|
8002
|
-
this.
|
|
8003
|
-
let
|
|
8004
|
-
while (
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
if (
|
|
8009
|
-
|
|
8199
|
+
if (this.transportFailure) return;
|
|
8200
|
+
let offset = 0;
|
|
8201
|
+
while (offset < chunk.length) {
|
|
8202
|
+
const newline = chunk.indexOf(10, offset);
|
|
8203
|
+
const end = newline === -1 ? chunk.length : newline;
|
|
8204
|
+
const bytes = end - offset;
|
|
8205
|
+
if (this.frameBytes + bytes > CODEX_MAX_FRAME_BYTES) {
|
|
8206
|
+
this.failTransport("codex raw JSONL frame exceeded its byte budget");
|
|
8207
|
+
return;
|
|
8208
|
+
}
|
|
8209
|
+
if (bytes > 0) this.frameChunks.push(Buffer.from(chunk.subarray(offset, end)));
|
|
8210
|
+
this.frameBytes += bytes;
|
|
8211
|
+
if (newline === -1) return;
|
|
8212
|
+
const frame = Buffer.concat(this.frameChunks, this.frameBytes);
|
|
8213
|
+
this.frameChunks.length = 0;
|
|
8214
|
+
this.frameBytes = 0;
|
|
8215
|
+
const line = frame.toString("utf8").replace(/\r$/, "");
|
|
8216
|
+
if (line.length > 0) this.parseLine(line, frame.length);
|
|
8217
|
+
if (this.transportFailure) return;
|
|
8218
|
+
offset = newline + 1;
|
|
8010
8219
|
}
|
|
8011
8220
|
}
|
|
8012
|
-
parseLine(line) {
|
|
8221
|
+
parseLine(line, bytes) {
|
|
8013
8222
|
let parsed;
|
|
8014
8223
|
try {
|
|
8015
8224
|
parsed = JSON.parse(line);
|
|
@@ -8019,14 +8228,18 @@ var CodexProcessRunner = class {
|
|
|
8019
8228
|
if (!parsed || typeof parsed !== "object" || typeof parsed.type !== "string") {
|
|
8020
8229
|
return;
|
|
8021
8230
|
}
|
|
8022
|
-
this.deliver(parsed);
|
|
8231
|
+
this.deliver(parsed, bytes);
|
|
8023
8232
|
}
|
|
8024
8233
|
onStderr(chunk) {
|
|
8025
|
-
|
|
8234
|
+
const bounded2 = Buffer.from(chunk).subarray(-CODEX_MAX_STDERR_BYTES).toString("utf8");
|
|
8235
|
+
for (const rawLine of bounded2.split("\n")) {
|
|
8026
8236
|
const line = rawLine.trim();
|
|
8027
8237
|
if (line.length === 0) continue;
|
|
8028
8238
|
this.stderrRing.push(line);
|
|
8029
|
-
|
|
8239
|
+
this.stderrBytes += Buffer.byteLength(line);
|
|
8240
|
+
while (this.stderrRing.length > STDERR_RING_CAPACITY3 || this.stderrBytes > CODEX_MAX_STDERR_BYTES) {
|
|
8241
|
+
this.stderrBytes -= Buffer.byteLength(this.stderrRing.shift());
|
|
8242
|
+
}
|
|
8030
8243
|
}
|
|
8031
8244
|
}
|
|
8032
8245
|
};
|
|
@@ -8059,14 +8272,15 @@ var CodexAdapter = class {
|
|
|
8059
8272
|
environmentRequirements: { credentialNames: [] }
|
|
8060
8273
|
});
|
|
8061
8274
|
async detect() {
|
|
8062
|
-
const bin = this.resolveBin();
|
|
8063
8275
|
try {
|
|
8064
|
-
const
|
|
8065
|
-
const
|
|
8276
|
+
const bin = this.resolveBin();
|
|
8277
|
+
const probe = await probeRuntimeVersion(bin.command, DETECT_TIMEOUT_MS3);
|
|
8278
|
+
if (probe.kind !== "available") return probe;
|
|
8279
|
+
const version = probe.stdout.trim() || probe.stderr.trim();
|
|
8066
8280
|
const authPresent = await this.probeAuthPresent(bin);
|
|
8067
|
-
return {
|
|
8068
|
-
} catch {
|
|
8069
|
-
return
|
|
8281
|
+
return { kind: "available", version, authPresent };
|
|
8282
|
+
} catch (error) {
|
|
8283
|
+
return classifyDetectError(error);
|
|
8070
8284
|
}
|
|
8071
8285
|
}
|
|
8072
8286
|
/**
|
|
@@ -8209,7 +8423,7 @@ ${withStreams.stderr ?? ""}`);
|
|
|
8209
8423
|
reason: "prepared codex operation received a manifest with different runtime selection"
|
|
8210
8424
|
});
|
|
8211
8425
|
}
|
|
8212
|
-
const mcpConfigArgs = codexMcpConfigArgs(startInput.mcpServers, preparedMcpGrants);
|
|
8426
|
+
const mcpConfigArgs = codexMcpConfigArgs(startInput.mcpServers, runtimeEnv, this.options.sdkHelperHost, preparedMcpGrants);
|
|
8213
8427
|
const { sessionRef, runner } = await runCodexTurn({
|
|
8214
8428
|
command,
|
|
8215
8429
|
resumeRef: startInput.manifest.sessionRef,
|
|
@@ -8225,6 +8439,7 @@ ${withStreams.stderr ?? ""}`);
|
|
|
8225
8439
|
expectedSessionRef: startInput.manifest.sessionRef,
|
|
8226
8440
|
preparedGit: startInput.manifest.workspace.workspaceId !== void 0,
|
|
8227
8441
|
failurePhase: "start",
|
|
8442
|
+
signal: startInput.signal,
|
|
8228
8443
|
terminal
|
|
8229
8444
|
});
|
|
8230
8445
|
return new CodexSession({
|
|
@@ -8232,6 +8447,7 @@ ${withStreams.stderr ?? ""}`);
|
|
|
8232
8447
|
command,
|
|
8233
8448
|
workspaceDir,
|
|
8234
8449
|
env: startInput.env,
|
|
8450
|
+
mcpEnvironment: Object.fromEntries(Object.entries(runtimeEnv).filter(([key]) => key.startsWith("BYOK_MCP_PAYLOAD_"))),
|
|
8235
8451
|
spawnFn: this.options.spawnFn,
|
|
8236
8452
|
queue,
|
|
8237
8453
|
recordUnmapped,
|
|
@@ -8284,16 +8500,18 @@ function codexMcpToolApprovalArgs(name, tools) {
|
|
|
8284
8500
|
}
|
|
8285
8501
|
return args;
|
|
8286
8502
|
}
|
|
8287
|
-
function codexMcpConfigArgs(servers, grants = []) {
|
|
8503
|
+
function codexMcpConfigArgs(servers, env, helperHost, grants = []) {
|
|
8288
8504
|
if (servers === void 0 || Object.keys(servers).length === 0) return [];
|
|
8289
8505
|
const grantedTools = new Map(grants.map((grant) => [grant.server, grant.tools]));
|
|
8290
8506
|
const args = ["--ignore-user-config"];
|
|
8291
8507
|
for (const [name, server] of Object.entries(servers).sort(([left], [right]) => left.localeCompare(right))) {
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
}
|
|
8508
|
+
const key = `BYOK_MCP_PAYLOAD_${randomBytes(16).toString("hex").toUpperCase()}`;
|
|
8509
|
+
env[key] = JSON.stringify(server);
|
|
8510
|
+
const helper = resolveSdkReservedHelperBin("mcp-env", helperHost);
|
|
8511
|
+
args.push("-c", `mcp_servers.${name}.command=${JSON.stringify(helper.command)}`);
|
|
8512
|
+
args.push("-c", `mcp_servers.${name}.args=${JSON.stringify([...helper.args])}`);
|
|
8513
|
+
args.push("-c", `mcp_servers.${name}.env.BYOK_MCP_ENV_KEY=${JSON.stringify(key)}`);
|
|
8514
|
+
args.push("-c", `mcp_servers.${name}.env_vars=${JSON.stringify([key])}`);
|
|
8297
8515
|
const granted = grantedTools.get(name);
|
|
8298
8516
|
if (granted !== void 0) args.push(...codexMcpToolApprovalArgs(name, granted));
|
|
8299
8517
|
}
|
|
@@ -8321,7 +8539,7 @@ function buildArgv(resumeRef, policyArgs, instruction, modelId, preparedGit = fa
|
|
|
8321
8539
|
...modelId ? ["--model", modelId] : [],
|
|
8322
8540
|
...preparedGit ? [] : ["--skip-git-repo-check"],
|
|
8323
8541
|
...policyArgs,
|
|
8324
|
-
|
|
8542
|
+
"-"
|
|
8325
8543
|
];
|
|
8326
8544
|
}
|
|
8327
8545
|
async function runCodexTurn(params) {
|
|
@@ -8345,9 +8563,18 @@ async function runCodexTurn(params) {
|
|
|
8345
8563
|
runner = new CodexProcessRunner({
|
|
8346
8564
|
command: params.command,
|
|
8347
8565
|
args: argv,
|
|
8566
|
+
instruction: params.instruction,
|
|
8348
8567
|
cwd: params.cwd,
|
|
8349
8568
|
env: params.env,
|
|
8350
8569
|
spawnFn: params.spawnFn,
|
|
8570
|
+
onFailure: (failure) => {
|
|
8571
|
+
if (!firstLineSettled) {
|
|
8572
|
+
firstLineSettled = true;
|
|
8573
|
+
rejectFirstLine(failure);
|
|
8574
|
+
}
|
|
8575
|
+
params.terminal.failure = failure;
|
|
8576
|
+
params.queue.end();
|
|
8577
|
+
},
|
|
8351
8578
|
onEvent: (evt) => {
|
|
8352
8579
|
if (!firstLineSettled) {
|
|
8353
8580
|
firstLineSettled = true;
|
|
@@ -8414,6 +8641,16 @@ async function runCodexTurn(params) {
|
|
|
8414
8641
|
params.queue.end();
|
|
8415
8642
|
});
|
|
8416
8643
|
let sessionRef;
|
|
8644
|
+
const abortStartup = () => rejectFirstLine(new RuntimeExecutionFailure({
|
|
8645
|
+
phase: params.failurePhase,
|
|
8646
|
+
category: "infrastructure",
|
|
8647
|
+
retry: "non-retryable",
|
|
8648
|
+
reason: "codex startup cancelled or exceeded its deadline"
|
|
8649
|
+
}));
|
|
8650
|
+
const startupTimer = setTimeout(abortStartup, 3e4);
|
|
8651
|
+
startupTimer.unref?.();
|
|
8652
|
+
params.signal?.addEventListener("abort", abortStartup, { once: true });
|
|
8653
|
+
if (params.signal?.aborted) abortStartup();
|
|
8417
8654
|
try {
|
|
8418
8655
|
sessionRef = await new Promise((resolve, reject) => {
|
|
8419
8656
|
let settled = false;
|
|
@@ -8445,20 +8682,31 @@ async function runCodexTurn(params) {
|
|
|
8445
8682
|
});
|
|
8446
8683
|
});
|
|
8447
8684
|
} catch (err) {
|
|
8448
|
-
runner
|
|
8685
|
+
await disposeFailedStartup(runner, err);
|
|
8449
8686
|
throw err;
|
|
8687
|
+
} finally {
|
|
8688
|
+
clearTimeout(startupTimer);
|
|
8689
|
+
params.signal?.removeEventListener("abort", abortStartup);
|
|
8450
8690
|
}
|
|
8451
8691
|
if (params.expectedSessionRef !== void 0 && sessionRef !== params.expectedSessionRef) {
|
|
8452
|
-
|
|
8453
|
-
throw new RuntimeExecutionFailure({
|
|
8692
|
+
const failure = new RuntimeExecutionFailure({
|
|
8454
8693
|
phase: params.failurePhase,
|
|
8455
8694
|
category: "authority",
|
|
8456
8695
|
retry: "non-retryable",
|
|
8457
8696
|
reason: `codex exec resume echoed a different thread id than requested (requested ${params.expectedSessionRef}, got ${sessionRef})`
|
|
8458
8697
|
});
|
|
8698
|
+
await disposeFailedStartup(runner, failure);
|
|
8699
|
+
throw failure;
|
|
8459
8700
|
}
|
|
8460
8701
|
return { sessionRef, runner };
|
|
8461
8702
|
}
|
|
8703
|
+
async function disposeFailedStartup(runner, cause) {
|
|
8704
|
+
try {
|
|
8705
|
+
await runner.dispose();
|
|
8706
|
+
} catch (disposalFailure) {
|
|
8707
|
+
throw new RuntimeStartupDisposalFailure(() => runner.dispose(), { cause: new AggregateError([cause, disposalFailure]) });
|
|
8708
|
+
}
|
|
8709
|
+
}
|
|
8462
8710
|
var CodexSession = class {
|
|
8463
8711
|
/**
|
|
8464
8712
|
* NOT `readonly` (cross-model review finding): `followUp()` below
|
|
@@ -8494,6 +8742,7 @@ var CodexSession = class {
|
|
|
8494
8742
|
* recomputed from anything a later turn supplies, so a follow-up can never
|
|
8495
8743
|
* widen this session's MCP authority.
|
|
8496
8744
|
*/
|
|
8745
|
+
mcpEnvironment;
|
|
8497
8746
|
mcpConfigArgs;
|
|
8498
8747
|
terminal;
|
|
8499
8748
|
currentRunner;
|
|
@@ -8513,6 +8762,7 @@ var CodexSession = class {
|
|
|
8513
8762
|
this.modelId = options.modelId;
|
|
8514
8763
|
this.terminal = options.terminal;
|
|
8515
8764
|
this.mcpConfigArgs = options.mcpConfigArgs;
|
|
8765
|
+
this.mcpEnvironment = Object.freeze({ ...options.mcpEnvironment });
|
|
8516
8766
|
this.currentRunner = options.initialRunner;
|
|
8517
8767
|
this.ownedRunners.add(options.initialRunner);
|
|
8518
8768
|
void this.forgetRunnerOnceClosed(options.initialRunner);
|
|
@@ -8625,7 +8875,7 @@ var CodexSession = class {
|
|
|
8625
8875
|
// frozen MCP config replayed verbatim — see `mcpConfigArgs`.
|
|
8626
8876
|
policyArgs: [...mapping.args, ...this.mcpConfigArgs],
|
|
8627
8877
|
cwd: this.workspaceDir,
|
|
8628
|
-
env: withoutProviderCredentials(this.env),
|
|
8878
|
+
env: { ...withoutProviderCredentials(this.env), ...this.mcpEnvironment },
|
|
8629
8879
|
spawnFn: this.spawnFn,
|
|
8630
8880
|
workspaceDir: this.workspaceDir,
|
|
8631
8881
|
queue: this.queue,
|
|
@@ -9141,15 +9391,7 @@ var AuthManager = class {
|
|
|
9141
9391
|
if (await this.opts.store.load() === void 0) return void 0;
|
|
9142
9392
|
throw new DeviceRecordRePairRequiredError();
|
|
9143
9393
|
}
|
|
9144
|
-
|
|
9145
|
-
deviceId: authority.deviceId,
|
|
9146
|
-
tenantId: authority.tenantId,
|
|
9147
|
-
devicePublicKey: authority.devicePublicKey
|
|
9148
|
-
};
|
|
9149
|
-
const current = await this.opts.store.load();
|
|
9150
|
-
if (current === void 0 || current.deviceId !== projection.deviceId || current.tenantId !== projection.tenantId || current.devicePublicKey !== projection.devicePublicKey) {
|
|
9151
|
-
await this.opts.store.save(projection);
|
|
9152
|
-
}
|
|
9394
|
+
await this.opts.store.reconcileMetadata(authority);
|
|
9153
9395
|
return Object.freeze({ ...authority });
|
|
9154
9396
|
}
|
|
9155
9397
|
};
|
|
@@ -9446,9 +9688,9 @@ async function probeDarwin(run) {
|
|
|
9446
9688
|
return DARWIN_UUID_RE.exec(result.stdout)?.[1];
|
|
9447
9689
|
}
|
|
9448
9690
|
async function probeLinux(readFile3) {
|
|
9449
|
-
for (const
|
|
9691
|
+
for (const path47 of LINUX_MACHINE_ID_PATHS) {
|
|
9450
9692
|
try {
|
|
9451
|
-
const contents = (await readFile3(
|
|
9693
|
+
const contents = (await readFile3(path47)).trim();
|
|
9452
9694
|
if (contents.length > 0) return contents;
|
|
9453
9695
|
} catch {
|
|
9454
9696
|
}
|
|
@@ -9480,7 +9722,7 @@ async function withTimeout2(probe, timeoutMs) {
|
|
|
9480
9722
|
async function resolveMachineId(options) {
|
|
9481
9723
|
const platform = options.platform ?? process.platform;
|
|
9482
9724
|
const run = options.run ?? runDeviceCommand;
|
|
9483
|
-
const readFile3 = options.readFile ?? ((
|
|
9725
|
+
const readFile3 = options.readFile ?? ((path47) => fs5.readFile(path47, "utf8"));
|
|
9484
9726
|
let probe;
|
|
9485
9727
|
if (platform === "darwin") probe = probeDarwin(run);
|
|
9486
9728
|
else if (platform === "linux") probe = probeLinux(readFile3);
|
|
@@ -10504,13 +10746,35 @@ var LongPollClient = class {
|
|
|
10504
10746
|
}
|
|
10505
10747
|
async loop(signal) {
|
|
10506
10748
|
let retryAttempt = 0;
|
|
10749
|
+
let readCursor;
|
|
10750
|
+
let readAheadSupported = false;
|
|
10751
|
+
let warnedWindowAt;
|
|
10507
10752
|
while (this.running && !signal.aborted) {
|
|
10508
10753
|
try {
|
|
10509
10754
|
await this.opts.auth.getValidAccessToken();
|
|
10510
10755
|
const base = toHttpBase(this.opts.serverUrl);
|
|
10511
10756
|
const url = new URL(BYOK_EVENTS_PATH, base);
|
|
10512
10757
|
const cursor = this.opts.getCursor();
|
|
10758
|
+
const ack = cursor ?? 0;
|
|
10759
|
+
if (readCursor !== void 0 && readCursor - ack >= MAILBOX_READ_AHEAD_MAX_SEQS) {
|
|
10760
|
+
if (warnedWindowAt !== ack) {
|
|
10761
|
+
warnedWindowAt = ack;
|
|
10762
|
+
console.warn("[byok/client] mailbox read-ahead window full; acknowledgement is stalled");
|
|
10763
|
+
}
|
|
10764
|
+
readCursor = ack;
|
|
10765
|
+
await sleep(this.opts.retryDelayMs ?? 2e3, signal);
|
|
10766
|
+
continue;
|
|
10767
|
+
}
|
|
10768
|
+
const afterSeq = Math.max(ack, readCursor ?? ack);
|
|
10513
10769
|
if (cursor !== void 0) url.searchParams.set("cursor", String(cursor));
|
|
10770
|
+
if (afterSeq > ack) {
|
|
10771
|
+
if (!readAheadSupported) {
|
|
10772
|
+
const error = new Error("server does not advertise mailbox-read-ahead; cannot read past unacknowledged work");
|
|
10773
|
+
this.warnRouteFailure(this.eventsEndpoint, void 0, error);
|
|
10774
|
+
throw error;
|
|
10775
|
+
}
|
|
10776
|
+
url.searchParams.set("afterSeq", String(afterSeq));
|
|
10777
|
+
}
|
|
10514
10778
|
let res;
|
|
10515
10779
|
try {
|
|
10516
10780
|
res = await authedFetch(url, { method: "GET", signal }, this.opts.auth);
|
|
@@ -10529,6 +10793,8 @@ var LongPollClient = class {
|
|
|
10529
10793
|
return;
|
|
10530
10794
|
}
|
|
10531
10795
|
this.warnRouteFailure(this.eventsEndpoint, res.status, void 0);
|
|
10796
|
+
readCursor = void 0;
|
|
10797
|
+
readAheadSupported = false;
|
|
10532
10798
|
this.opts.onServerCapabilitiesInvalidated?.();
|
|
10533
10799
|
this.opts.onPollFailure?.();
|
|
10534
10800
|
this.opts.onOperationalOutcome?.("failure");
|
|
@@ -10538,12 +10804,16 @@ var LongPollClient = class {
|
|
|
10538
10804
|
}
|
|
10539
10805
|
let parsed;
|
|
10540
10806
|
try {
|
|
10541
|
-
parsed = parseLooseEventsPollResponse(await res.json(),
|
|
10542
|
-
validateTrustedEventsPage(parsed.events,
|
|
10807
|
+
parsed = parseLooseEventsPollResponse(await res.json(), afterSeq);
|
|
10808
|
+
validateTrustedEventsPage(parsed.events, afterSeq, parsed.cursor);
|
|
10809
|
+
if (afterSeq > ack && !parsed.capabilities.includes(MAILBOX_READ_AHEAD_CAPABILITY)) throw new Error("mailbox read-ahead capability was withdrawn");
|
|
10543
10810
|
} catch (err) {
|
|
10544
10811
|
this.warnRouteFailure(this.eventsEndpoint, res.status, err);
|
|
10545
10812
|
throw err;
|
|
10546
10813
|
}
|
|
10814
|
+
readAheadSupported = parsed.capabilities.includes(MAILBOX_READ_AHEAD_CAPABILITY);
|
|
10815
|
+
const madeReadProgress = parsed.cursor > afterSeq;
|
|
10816
|
+
readCursor = parsed.cursor;
|
|
10547
10817
|
this.opts.onServerCapabilities?.(parsed.capabilities);
|
|
10548
10818
|
let hadValidationFailureThisBatch = false;
|
|
10549
10819
|
let acceptedAnyEntry = false;
|
|
@@ -10567,10 +10837,11 @@ var LongPollClient = class {
|
|
|
10567
10837
|
if (this.opts.onEnvelope(envelope) !== false) acceptedAnyEntry = true;
|
|
10568
10838
|
}
|
|
10569
10839
|
if (parsed.events.length === 0) {
|
|
10840
|
+
readCursor = this.opts.getCursor() ?? 0;
|
|
10570
10841
|
retryAttempt = 0;
|
|
10571
10842
|
this.opts.onOperationalOutcome?.("success");
|
|
10572
10843
|
await sleep(this.opts.idleDelayMs ?? 250, signal);
|
|
10573
|
-
} else if (this.opts.isStalled?.() || hadValidationFailureThisBatch || !acceptedAnyEntry && this.opts.getCursor() === cursor) {
|
|
10844
|
+
} else if (!madeReadProgress && (this.opts.isStalled?.() || hadValidationFailureThisBatch || !acceptedAnyEntry && this.opts.getCursor() === cursor)) {
|
|
10574
10845
|
this.opts.onOperationalOutcome?.("failure");
|
|
10575
10846
|
const baseMs = this.opts.retryDelayMs ?? 2e3;
|
|
10576
10847
|
await sleep(this.opts.retryDelayForAttempt?.(retryAttempt++, baseMs) ?? baseMs, signal);
|
|
@@ -10579,6 +10850,8 @@ var LongPollClient = class {
|
|
|
10579
10850
|
this.opts.onOperationalOutcome?.("success");
|
|
10580
10851
|
}
|
|
10581
10852
|
} catch (err) {
|
|
10853
|
+
readCursor = void 0;
|
|
10854
|
+
readAheadSupported = false;
|
|
10582
10855
|
this.opts.onServerCapabilitiesInvalidated?.();
|
|
10583
10856
|
if (this.noteRevoked(err)) return;
|
|
10584
10857
|
if (!this.running || signal.aborted) return;
|
|
@@ -10631,6 +10904,7 @@ function createConnectionHelloEnvelope(opts, getCursor) {
|
|
|
10631
10904
|
productId: opts.productId,
|
|
10632
10905
|
clientVersion: opts.clientVersion,
|
|
10633
10906
|
runtimes: opts.runtimes,
|
|
10907
|
+
harnesses: opts.harnesses,
|
|
10634
10908
|
configuredToolsets: configuredToolsets === void 0 ? void 0 : [...configuredToolsets],
|
|
10635
10909
|
cursor: getCursor()
|
|
10636
10910
|
});
|
|
@@ -10644,8 +10918,8 @@ var ConnectionManager = class {
|
|
|
10644
10918
|
serverUrl: opts.serverUrl,
|
|
10645
10919
|
auth: opts.auth,
|
|
10646
10920
|
// The long-poll query cursor is also the kernel's irreversible ack.
|
|
10647
|
-
// Only report the successfully
|
|
10648
|
-
//
|
|
10921
|
+
// Only report the successfully persisted cursor; using received work
|
|
10922
|
+
// on the wire would ack an
|
|
10649
10923
|
// in-flight envelope before its handler settles and make a later
|
|
10650
10924
|
// failure impossible to redeliver.
|
|
10651
10925
|
getCursor: () => this.cursor,
|
|
@@ -10694,25 +10968,11 @@ var ConnectionManager = class {
|
|
|
10694
10968
|
* guarantees in docs/protocol.md §9.
|
|
10695
10969
|
*/
|
|
10696
10970
|
stalledAtSeq;
|
|
10697
|
-
/**
|
|
10698
|
-
* Design A (Wave 2, F3-on-long-poll): the second, in-memory watermark
|
|
10699
|
-
* alongside the durable `cursor`. `cursor` only ever advances AFTER a
|
|
10700
|
-
* `task.*` handler's side effects resolve successfully, and is persisted
|
|
10701
|
-
* (see `advanceCursor`) — that semantics is unchanged. `deliveredSeq`
|
|
10702
|
-
* advances eagerly, the instant a `task.*` envelope is admitted past
|
|
10703
|
-
* dedup (see `deliver`/`noteDelivered`), independent of whether its
|
|
10704
|
-
* handler has even started, let alone succeeded. It exists so a repeated
|
|
10705
|
-
* read at the durable cursor does not re-dispatch an envelope already in
|
|
10706
|
-
* flight — `handleOffer` must not start a second adapter session while a
|
|
10707
|
-
* first attempt is still running. On WS this same field is written the
|
|
10708
|
-
* same way, but since a live WS connection only ever pushes a given `seq`
|
|
10709
|
-
* once, it never has an observable effect there beyond mirroring
|
|
10710
|
-
* `cursor` (see `dedupWatermark`'s doc comment for why redelivery
|
|
10711
|
-
* correctness doesn't depend on resetting it anywhere).
|
|
10712
|
-
*/
|
|
10713
|
-
deliveredSeq;
|
|
10714
|
-
/** Finding F3: serializes `onEnvelope` calls into a per-connection FIFO — one envelope's handler always fully settles before the next one starts. */
|
|
10971
|
+
/** Drain barrier for independently running handlers; not an admission lock. */
|
|
10715
10972
|
processingChain = Promise.resolve();
|
|
10973
|
+
cursorInitialization;
|
|
10974
|
+
completionTail = Promise.resolve();
|
|
10975
|
+
controlTails = /* @__PURE__ */ new Map();
|
|
10716
10976
|
/**
|
|
10717
10977
|
* Design B (finding N4): the ONE outbound queue holds `Envelope` OBJECTS,
|
|
10718
10978
|
* never re-encoded/rebuilt strings, so a
|
|
@@ -10738,38 +10998,13 @@ var ConnectionManager = class {
|
|
|
10738
10998
|
terminalError;
|
|
10739
10999
|
settledWaiters = [];
|
|
10740
11000
|
pendingCursorSave = Promise.resolve();
|
|
10741
|
-
/**
|
|
10742
|
-
* Finding P2 (Fix 2b): seqs currently admitted into `processingChain` but
|
|
10743
|
-
* not yet settled — added in `deliver()` the moment a `task.*` envelope is
|
|
10744
|
-
* accepted past the ordinary watermark check, removed in `process()`'s
|
|
10745
|
-
* `finally` once that specific attempt resolves (success OR failure).
|
|
10746
|
-
* While stalled, `dedupWatermark()` deliberately stays frozen below
|
|
10747
|
-
* already-delivered seqs (see its own doc comment) so the failed seq's own
|
|
10748
|
-
* redelivery can get through — but that same frozen watermark also means
|
|
10749
|
-
* every OTHER seq above it rides along on every re-poll too. Without this,
|
|
10750
|
-
* a seq already mid-flight (e.g. a `task.offer` whose prepared operation start()
|
|
10751
|
-
* hasn't resolved yet) would be re-enqueued into `processingChain` on
|
|
10752
|
-
* every such re-poll, piling up duplicate copies that — once the first
|
|
10753
|
-
* finally resolves and the chain unwinds through them — run its handler
|
|
10754
|
-
* again; for `task.offer` specifically, a second adapter session
|
|
10755
|
-
* orphaning the first (`TaskRunner`'s own `this.tasks.has` guard, finding
|
|
10756
|
-
* P2c, is the second, independent layer against exactly that).
|
|
10757
|
-
*/
|
|
11001
|
+
/** Admission dedup while an individual handler is running. */
|
|
10758
11002
|
inFlightSeqs = /* @__PURE__ */ new Set();
|
|
10759
|
-
/**
|
|
10760
|
-
* Finding P2 (Fix 2b): seqs whose handler has already resolved
|
|
10761
|
-
* successfully at least once this session, tracked only while a stall is
|
|
10762
|
-
* in effect — cleared the moment `stalledAtSeq` itself clears (see
|
|
10763
|
-
* `process()`), since once unstalled the ordinary watermark check via
|
|
10764
|
-
* `deliveredSeq` already covers everything delivered so far, making this
|
|
10765
|
-
* redundant. Needed because the stall-gap-prevention rule in `process()`
|
|
10766
|
-
* deliberately does NOT advance `cursor` past a seq above the
|
|
10767
|
-
* still-unresolved `stalledAtSeq`, even once that seq's own handler
|
|
10768
|
-
* succeeds — so `dedupWatermark()` alone can't distinguish "already
|
|
10769
|
-
* succeeded, don't re-run" from "never yet attempted" for anything in
|
|
10770
|
-
* that gap.
|
|
10771
|
-
*/
|
|
11003
|
+
/** Successful side effects retained until their durable acknowledgement. */
|
|
10772
11004
|
processedSeqs = /* @__PURE__ */ new Set();
|
|
11005
|
+
failedEnvelopes = /* @__PURE__ */ new Map();
|
|
11006
|
+
/** Received work lacking a successful handler receipt, including malformed frames. */
|
|
11007
|
+
unresolvedSeqs = /* @__PURE__ */ new Set();
|
|
10773
11008
|
/**
|
|
10774
11009
|
* Finding P3: the pending `drainOutbox` long-poll retry backoff, if any —
|
|
10775
11010
|
* cancellable so `enterRevoked()` can unblock it immediately instead of
|
|
@@ -11062,8 +11297,8 @@ var ConnectionManager = class {
|
|
|
11062
11297
|
* - F3 (at-most-once): the old code persisted the cursor advance BEFORE
|
|
11063
11298
|
* `onEnvelope` even ran (fire-and-forget) — a handler that then failed
|
|
11064
11299
|
* left a redelivery-proof envelope permanently marked processed. Inbound
|
|
11065
|
-
*
|
|
11066
|
-
*
|
|
11300
|
+
* handlers may run independently; their receipt commits are serialized.
|
|
11301
|
+
* The cursor only advances
|
|
11067
11302
|
* AFTER the handler resolves successfully; a rejection leaves the
|
|
11068
11303
|
* cursor where it was (see `stalledAtSeq`), so a future reconnect's
|
|
11069
11304
|
* redelivery re-attempts it — safe because every server->daemon type is
|
|
@@ -11079,62 +11314,72 @@ var ConnectionManager = class {
|
|
|
11079
11314
|
if (tracked && watermark !== void 0 && envelope.seq <= watermark) return false;
|
|
11080
11315
|
if (tracked) {
|
|
11081
11316
|
const seq3 = envelope.seq;
|
|
11082
|
-
if (this.inFlightSeqs.has(seq3)
|
|
11317
|
+
if (this.inFlightSeqs.has(seq3)) return false;
|
|
11318
|
+
if (!this.processedSeqs.has(seq3)) this.unresolvedSeqs.add(seq3);
|
|
11083
11319
|
this.inFlightSeqs.add(seq3);
|
|
11084
|
-
this.noteDelivered(seq3);
|
|
11085
11320
|
}
|
|
11086
|
-
|
|
11321
|
+
const controlKey = envelope.task_id !== void 0 && !envelope.type.startsWith("task.offer") ? envelope.task_id : void 0;
|
|
11322
|
+
const prior = controlKey === void 0 ? Promise.resolve() : this.controlTails.get(controlKey) ?? Promise.resolve();
|
|
11323
|
+
const operation = prior.then(() => this.process(envelope, tracked));
|
|
11324
|
+
if (controlKey !== void 0) {
|
|
11325
|
+
this.controlTails.set(controlKey, operation);
|
|
11326
|
+
void operation.finally(() => {
|
|
11327
|
+
if (this.controlTails.get(controlKey) === operation) this.controlTails.delete(controlKey);
|
|
11328
|
+
});
|
|
11329
|
+
}
|
|
11330
|
+
this.processingChain = Promise.all([this.processingChain, operation]).then(() => void 0);
|
|
11087
11331
|
return true;
|
|
11088
11332
|
}
|
|
11089
|
-
/**
|
|
11090
|
-
*
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
* reaches `advanceCursor` already passed through `noteDelivered` first,
|
|
11096
|
-
* see `deliver`) — so this is the literal `max(cursor, deliveredSeq)` the
|
|
11097
|
-
* design calls for, just expressed via that invariant rather than an
|
|
11098
|
-
* explicit `Math.max`.
|
|
11099
|
-
*
|
|
11100
|
-
* While `stalledAtSeq` is set, this collapses to the durable `cursor`
|
|
11101
|
-
* alone, deliberately ignoring however far `deliveredSeq` had already run
|
|
11102
|
-
* ahead before the failure was known: that's what lets the stalled
|
|
11103
|
-
* envelope's own redelivery (and everything after it, right up to a
|
|
11104
|
-
* fresh success) get past this same dedup check instead of being
|
|
11105
|
-
* self-deduped by the client's own earlier eager tracking of envelopes
|
|
11106
|
-
* whose outcome wasn't known yet. No separate "reset deliveredSeq on
|
|
11107
|
-
* reconnect" step is needed for this to be correct — collapsing to
|
|
11108
|
-
* `cursor` exactly while stalled already produces the right answer on
|
|
11109
|
-
* every long-poll retry path. NOT resetting it unconditionally on every
|
|
11110
|
-
* retry lets `deliveredSeq` keep doing its job of not re-dispatching
|
|
11111
|
-
* something already in flight while a handler is still running.
|
|
11112
|
-
*/
|
|
11113
|
-
dedupWatermark() {
|
|
11114
|
-
return this.stalledAtSeq !== void 0 ? this.cursor : this.deliveredSeq ?? this.cursor;
|
|
11333
|
+
/** A committed terminal can settle a failed offer receipt even if cloud
|
|
11334
|
+
* cancellation has since filtered that offer from mailbox replay. */
|
|
11335
|
+
retryTaskReceipts(taskId) {
|
|
11336
|
+
for (const envelope of this.failedEnvelopes.values()) {
|
|
11337
|
+
if (envelope.task_id === taskId) this.deliver(envelope);
|
|
11338
|
+
}
|
|
11115
11339
|
}
|
|
11116
|
-
/**
|
|
11117
|
-
|
|
11118
|
-
|
|
11340
|
+
/** Only a durable acknowledgement can deduplicate a whole prefix. */
|
|
11341
|
+
dedupWatermark() {
|
|
11342
|
+
return this.cursor;
|
|
11119
11343
|
}
|
|
11120
11344
|
async process(envelope, tracked) {
|
|
11121
11345
|
const seq3 = tracked ? envelope.seq : void 0;
|
|
11122
11346
|
try {
|
|
11347
|
+
if (tracked && seq3 <= (this.cursor ?? 0)) return;
|
|
11123
11348
|
if (tracked && this.cursor === void 0) {
|
|
11124
|
-
|
|
11125
|
-
|
|
11349
|
+
this.cursorInitialization ??= this.opts.cursorStore.save(this.opts.serverUrl, this.opts.deviceId, 0).then(() => {
|
|
11350
|
+
this.cursor = 0;
|
|
11351
|
+
}).catch((error) => {
|
|
11352
|
+
this.cursorInitialization = void 0;
|
|
11353
|
+
throw error;
|
|
11354
|
+
});
|
|
11355
|
+
await this.cursorInitialization;
|
|
11126
11356
|
}
|
|
11127
|
-
await this.opts.onEnvelope(envelope);
|
|
11357
|
+
if (!tracked || !this.processedSeqs.has(seq3)) await this.opts.onEnvelope(envelope);
|
|
11128
11358
|
if (!tracked) return;
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11359
|
+
this.failedEnvelopes.delete(seq3);
|
|
11360
|
+
this.processedSeqs.add(seq3);
|
|
11361
|
+
this.unresolvedSeqs.delete(seq3);
|
|
11362
|
+
const completion = this.completionTail.catch(() => void 0).then(async () => {
|
|
11363
|
+
let firstUnresolved = Infinity;
|
|
11364
|
+
for (const value of this.unresolvedSeqs) firstUnresolved = Math.min(firstUnresolved, value);
|
|
11365
|
+
let prefix = this.cursor ?? 0;
|
|
11366
|
+
for (const value of this.processedSeqs) if (value < firstUnresolved) prefix = Math.max(prefix, value);
|
|
11367
|
+
if (prefix > (this.cursor ?? 0)) await this.advanceCursor(prefix);
|
|
11368
|
+
for (const value of this.processedSeqs) {
|
|
11369
|
+
if (value <= (this.cursor ?? 0)) {
|
|
11370
|
+
this.processedSeqs.delete(value);
|
|
11371
|
+
this.failedEnvelopes.delete(value);
|
|
11372
|
+
}
|
|
11373
|
+
}
|
|
11374
|
+
this.stalledAtSeq = this.unresolvedSeqs.size > 0 ? firstUnresolved : void 0;
|
|
11375
|
+
});
|
|
11376
|
+
this.completionTail = completion;
|
|
11377
|
+
await completion;
|
|
11136
11378
|
} catch (err) {
|
|
11137
|
-
if (tracked
|
|
11379
|
+
if (tracked) {
|
|
11380
|
+
this.failedEnvelopes.set(seq3, envelope);
|
|
11381
|
+
this.stalledAtSeq = Math.min(envelope.seq, this.stalledAtSeq ?? Infinity);
|
|
11382
|
+
}
|
|
11138
11383
|
console.error(
|
|
11139
11384
|
`[byok/client] envelope handler failed for ${envelope.type}${typeof envelope.seq === "number" ? ` (seq=${envelope.seq})` : ""}; cursor left unadvanced so a reconnect redelivers it:`,
|
|
11140
11385
|
err
|
|
@@ -11143,11 +11388,10 @@ var ConnectionManager = class {
|
|
|
11143
11388
|
if (tracked) this.inFlightSeqs.delete(seq3);
|
|
11144
11389
|
}
|
|
11145
11390
|
}
|
|
11146
|
-
/**
|
|
11391
|
+
/** Recorded synchronously at receive, before a later successful receipt can commit. */
|
|
11147
11392
|
noteValidationFailure(seq3) {
|
|
11148
|
-
this.
|
|
11149
|
-
|
|
11150
|
-
});
|
|
11393
|
+
this.unresolvedSeqs.add(seq3);
|
|
11394
|
+
this.stalledAtSeq = Math.min(seq3, this.stalledAtSeq ?? seq3);
|
|
11151
11395
|
}
|
|
11152
11396
|
async advanceCursor(seq3) {
|
|
11153
11397
|
if (this.cursor !== void 0 && seq3 <= this.cursor) return;
|
|
@@ -11517,7 +11761,7 @@ var CursorStore = class {
|
|
|
11517
11761
|
async save(serverUrl, deviceId, cursor) {
|
|
11518
11762
|
const file = this.fileFor(serverUrl, deviceId);
|
|
11519
11763
|
await promises.mkdir(path14__default.dirname(file), { recursive: true, mode: 448 });
|
|
11520
|
-
await atomicWriteFile(file, JSON.stringify({ cursor }));
|
|
11764
|
+
await atomicWriteFile(file, JSON.stringify({ cursor }), { fsync: true });
|
|
11521
11765
|
}
|
|
11522
11766
|
/** Remove any persisted cursor for (serverUrl, deviceId) — a no-op if none exists. Called from `pair()` (finding F5) so a device that's about to be replaced never leaves a cursor a future, unrelated device could somehow inherit. */
|
|
11523
11767
|
async clear(serverUrl, deviceId) {
|
|
@@ -12010,9 +12254,9 @@ function isSqliteAvailable() {
|
|
|
12010
12254
|
}
|
|
12011
12255
|
}
|
|
12012
12256
|
var SECURE_FILE_MODE = 384;
|
|
12013
|
-
function openJournalDatabase(
|
|
12257
|
+
function openJournalDatabase(path47, busyTimeoutMs, faults) {
|
|
12014
12258
|
const { DatabaseSync } = loadSqliteModule();
|
|
12015
|
-
const db = new DatabaseSync(
|
|
12259
|
+
const db = new DatabaseSync(path47, { timeout: busyTimeoutMs });
|
|
12016
12260
|
try {
|
|
12017
12261
|
faults?.onStep?.("after-open");
|
|
12018
12262
|
db.exec("PRAGMA auto_vacuum = INCREMENTAL;");
|
|
@@ -12156,9 +12400,9 @@ var RECEIVED_STATE = "received";
|
|
|
12156
12400
|
function byteLength2(value) {
|
|
12157
12401
|
return Buffer.byteLength(value, "utf8");
|
|
12158
12402
|
}
|
|
12159
|
-
function fileBytes(
|
|
12403
|
+
function fileBytes(path47) {
|
|
12160
12404
|
try {
|
|
12161
|
-
return statSync(
|
|
12405
|
+
return statSync(path47).size;
|
|
12162
12406
|
} catch {
|
|
12163
12407
|
return 0;
|
|
12164
12408
|
}
|
|
@@ -13498,6 +13742,109 @@ async function spillOversizedEvent(event, deps) {
|
|
|
13498
13742
|
return bounded2.result;
|
|
13499
13743
|
}
|
|
13500
13744
|
|
|
13745
|
+
// src/daemon/admission-wait.ts
|
|
13746
|
+
async function awaitAdmission(operation, signal) {
|
|
13747
|
+
signal.throwIfAborted();
|
|
13748
|
+
let onAbort;
|
|
13749
|
+
const aborted = new Promise((_, reject) => {
|
|
13750
|
+
onAbort = () => reject(signal.reason);
|
|
13751
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
13752
|
+
});
|
|
13753
|
+
try {
|
|
13754
|
+
return await Promise.race([Promise.resolve().then(() => {
|
|
13755
|
+
signal.throwIfAborted();
|
|
13756
|
+
return operation();
|
|
13757
|
+
}), aborted]);
|
|
13758
|
+
} finally {
|
|
13759
|
+
signal.removeEventListener("abort", onAbort);
|
|
13760
|
+
}
|
|
13761
|
+
}
|
|
13762
|
+
|
|
13763
|
+
// src/daemon/runtime-start.ts
|
|
13764
|
+
async function startOwnedRuntime(start, input, signal, timeoutMs) {
|
|
13765
|
+
const controller = new AbortController();
|
|
13766
|
+
let session;
|
|
13767
|
+
let failure;
|
|
13768
|
+
let settled = false;
|
|
13769
|
+
const retryDisposal = async () => {
|
|
13770
|
+
if (session) {
|
|
13771
|
+
let timer2;
|
|
13772
|
+
try {
|
|
13773
|
+
await Promise.race([
|
|
13774
|
+
Promise.resolve().then(() => session.interrupt()).catch(() => void 0),
|
|
13775
|
+
new Promise((resolve) => {
|
|
13776
|
+
timer2 = setTimeout(resolve, 1e3);
|
|
13777
|
+
timer2.unref?.();
|
|
13778
|
+
})
|
|
13779
|
+
]);
|
|
13780
|
+
} finally {
|
|
13781
|
+
if (timer2) clearTimeout(timer2);
|
|
13782
|
+
}
|
|
13783
|
+
return session.close();
|
|
13784
|
+
}
|
|
13785
|
+
if (isRuntimeStartupDisposalFailure(failure)) return failure.retryDisposal();
|
|
13786
|
+
if (settled && isRuntimeExecutionFailure(failure)) return;
|
|
13787
|
+
throw new RuntimeDisposalFailure({ stage: "quiescence", reason: "runtime startup has not returned a cleanup receipt" });
|
|
13788
|
+
};
|
|
13789
|
+
let rejectAbort;
|
|
13790
|
+
const aborted = new Promise((_, reject) => {
|
|
13791
|
+
rejectAbort = reject;
|
|
13792
|
+
});
|
|
13793
|
+
const abort = () => {
|
|
13794
|
+
controller.abort();
|
|
13795
|
+
rejectAbort(new RuntimeStartupDisposalFailure(retryDisposal));
|
|
13796
|
+
};
|
|
13797
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
13798
|
+
const timer = setTimeout(abort, timeoutMs);
|
|
13799
|
+
timer.unref?.();
|
|
13800
|
+
try {
|
|
13801
|
+
if (signal.aborted) {
|
|
13802
|
+
throw new RuntimeDisposalFailure({ stage: "quiescence", reason: "runtime startup was cancelled before invocation" });
|
|
13803
|
+
}
|
|
13804
|
+
const startup = Promise.resolve().then(() => start({ ...input, signal: controller.signal })).then((value) => {
|
|
13805
|
+
session = value;
|
|
13806
|
+
settled = true;
|
|
13807
|
+
return value;
|
|
13808
|
+
}, (error) => {
|
|
13809
|
+
failure = error;
|
|
13810
|
+
settled = true;
|
|
13811
|
+
throw error;
|
|
13812
|
+
});
|
|
13813
|
+
return await Promise.race([startup, aborted]);
|
|
13814
|
+
} finally {
|
|
13815
|
+
clearTimeout(timer);
|
|
13816
|
+
signal.removeEventListener("abort", abort);
|
|
13817
|
+
}
|
|
13818
|
+
}
|
|
13819
|
+
|
|
13820
|
+
// src/daemon/artifact-read.ts
|
|
13821
|
+
var DEFAULT_ARTIFACT_LIMITS = Object.freeze({
|
|
13822
|
+
maxFileBytes: 16 * 1024 * 1024,
|
|
13823
|
+
maxTaskBytes: 64 * 1024 * 1024
|
|
13824
|
+
});
|
|
13825
|
+
async function readArtifactBytes(handle, maxBytes, signal, account) {
|
|
13826
|
+
signal.throwIfAborted();
|
|
13827
|
+
const stat = await handle.stat();
|
|
13828
|
+
if (stat.size > maxBytes) throw new Error("artifact local byte budget exceeded");
|
|
13829
|
+
const chunks = [];
|
|
13830
|
+
let total = 0;
|
|
13831
|
+
const stream = handle.createReadStream({ autoClose: false, start: 0, signal, highWaterMark: 64 * 1024 });
|
|
13832
|
+
try {
|
|
13833
|
+
for await (const data of stream) {
|
|
13834
|
+
signal.throwIfAborted();
|
|
13835
|
+
const chunk = data;
|
|
13836
|
+
account(chunk.length);
|
|
13837
|
+
total += chunk.length;
|
|
13838
|
+
if (total > maxBytes) throw new Error("artifact local byte budget exceeded");
|
|
13839
|
+
chunks.push(chunk);
|
|
13840
|
+
}
|
|
13841
|
+
signal.throwIfAborted();
|
|
13842
|
+
return Buffer.concat(chunks, total);
|
|
13843
|
+
} finally {
|
|
13844
|
+
stream.destroy();
|
|
13845
|
+
}
|
|
13846
|
+
}
|
|
13847
|
+
|
|
13501
13848
|
// src/daemon/environment.ts
|
|
13502
13849
|
var BASE_PLATFORM_ALLOWLIST = [
|
|
13503
13850
|
"PATH",
|
|
@@ -13697,6 +14044,78 @@ var ProgressBatcher = class {
|
|
|
13697
14044
|
}
|
|
13698
14045
|
}
|
|
13699
14046
|
};
|
|
14047
|
+
var DurableJsonlFile = class {
|
|
14048
|
+
constructor(filePath) {
|
|
14049
|
+
this.filePath = filePath;
|
|
14050
|
+
}
|
|
14051
|
+
filePath;
|
|
14052
|
+
failure;
|
|
14053
|
+
assertWritable() {
|
|
14054
|
+
if (this.failure !== void 0) {
|
|
14055
|
+
throw new Error("durable JSONL writer is quarantined after uncertain I/O; reopen and validate the log before retrying", { cause: this.failure });
|
|
14056
|
+
}
|
|
14057
|
+
}
|
|
14058
|
+
/** Replay is not a durability receipt: confirm the recovered bytes before use. */
|
|
14059
|
+
async confirmRecovered() {
|
|
14060
|
+
let handle;
|
|
14061
|
+
try {
|
|
14062
|
+
handle = await promises.open(this.filePath, "r+");
|
|
14063
|
+
} catch (error) {
|
|
14064
|
+
if (error.code === "ENOENT") return;
|
|
14065
|
+
throw error;
|
|
14066
|
+
}
|
|
14067
|
+
try {
|
|
14068
|
+
await handle.sync();
|
|
14069
|
+
} finally {
|
|
14070
|
+
await handle.close();
|
|
14071
|
+
}
|
|
14072
|
+
await this.syncDirectory();
|
|
14073
|
+
}
|
|
14074
|
+
async append(json) {
|
|
14075
|
+
this.assertWritable();
|
|
14076
|
+
try {
|
|
14077
|
+
const handle = await promises.open(this.filePath, constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY, 384);
|
|
14078
|
+
try {
|
|
14079
|
+
const bytes = Buffer.from(`${json}
|
|
14080
|
+
`, "utf8");
|
|
14081
|
+
let offset = 0;
|
|
14082
|
+
while (offset < bytes.length) {
|
|
14083
|
+
const { bytesWritten } = await handle.write(bytes, offset, bytes.length - offset);
|
|
14084
|
+
if (bytesWritten <= 0) throw new Error("durable JSONL append made no progress");
|
|
14085
|
+
offset += bytesWritten;
|
|
14086
|
+
}
|
|
14087
|
+
await handle.sync();
|
|
14088
|
+
} finally {
|
|
14089
|
+
await handle.close();
|
|
14090
|
+
}
|
|
14091
|
+
await this.syncDirectory();
|
|
14092
|
+
} catch (error) {
|
|
14093
|
+
this.failure = error;
|
|
14094
|
+
throw error;
|
|
14095
|
+
}
|
|
14096
|
+
}
|
|
14097
|
+
async replace(lines) {
|
|
14098
|
+
this.assertWritable();
|
|
14099
|
+
try {
|
|
14100
|
+
await atomicWriteFile(this.filePath, lines.map((line) => `${line}
|
|
14101
|
+
`).join(""), { mode: 384, fsync: true });
|
|
14102
|
+
} catch (error) {
|
|
14103
|
+
this.failure = error;
|
|
14104
|
+
throw error;
|
|
14105
|
+
}
|
|
14106
|
+
}
|
|
14107
|
+
async syncDirectory() {
|
|
14108
|
+
if (process.platform === "win32") return;
|
|
14109
|
+
const directory = await promises.open(path14__default.dirname(this.filePath), "r");
|
|
14110
|
+
try {
|
|
14111
|
+
await directory.sync();
|
|
14112
|
+
} finally {
|
|
14113
|
+
await directory.close();
|
|
14114
|
+
}
|
|
14115
|
+
}
|
|
14116
|
+
};
|
|
14117
|
+
|
|
14118
|
+
// src/daemon/agent-message-outbox.ts
|
|
13700
14119
|
var AGENT_MESSAGE_DIRECTORY = path14__default.join(".byok", "messages");
|
|
13701
14120
|
var AGENT_MESSAGE_OUTBOX_FILENAME = "outbox-v1.jsonl";
|
|
13702
14121
|
var AgentMessageOutboxError = class extends Error {
|
|
@@ -13723,11 +14142,14 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13723
14142
|
constructor(homeDir, outboxPath) {
|
|
13724
14143
|
this.homeDir = homeDir;
|
|
13725
14144
|
this.outboxPath = outboxPath;
|
|
14145
|
+
this.file = new DurableJsonlFile(outboxPath);
|
|
13726
14146
|
}
|
|
13727
14147
|
homeDir;
|
|
13728
14148
|
outboxPath;
|
|
13729
14149
|
pendingByTask = /* @__PURE__ */ new Map();
|
|
14150
|
+
revokedTasks = /* @__PURE__ */ new Set();
|
|
13730
14151
|
dispositionByTask = /* @__PURE__ */ new Map();
|
|
14152
|
+
file;
|
|
13731
14153
|
nextCursor = 1;
|
|
13732
14154
|
logEntries = 0;
|
|
13733
14155
|
writeTail = Promise.resolve();
|
|
@@ -13736,6 +14158,7 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13736
14158
|
await ensureSecureDir(directory);
|
|
13737
14159
|
const outbox = new _AgentMessageOutbox(homeDir, path14__default.join(directory, AGENT_MESSAGE_OUTBOX_FILENAME));
|
|
13738
14160
|
await outbox.load();
|
|
14161
|
+
await outbox.file.confirmRecovered();
|
|
13739
14162
|
return outbox;
|
|
13740
14163
|
}
|
|
13741
14164
|
/** Re-open every existing Agent-local message outbox without following Agent-home symlinks. */
|
|
@@ -13776,9 +14199,9 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13776
14199
|
records() {
|
|
13777
14200
|
return Object.freeze([...this.pendingByTask.values()].sort((a, b) => a.cursor - b.cursor));
|
|
13778
14201
|
}
|
|
13779
|
-
/**
|
|
14202
|
+
/** Records without disposition or local revoke; transport replay additionally requires session binding. */
|
|
13780
14203
|
retryableRecords() {
|
|
13781
|
-
return Object.freeze(this.records().filter((record4) => !this.dispositionByTask.has(record4.taskId)));
|
|
14204
|
+
return Object.freeze(this.records().filter((record4) => !this.dispositionByTask.has(record4.taskId) && !this.revokedTasks.has(record4.taskId)));
|
|
13782
14205
|
}
|
|
13783
14206
|
get(taskId) {
|
|
13784
14207
|
return this.pendingByTask.get(taskId);
|
|
@@ -13790,13 +14213,14 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13790
14213
|
if (byteCount < 1 || byteCount > input.requirement.maxBytes) throw new AgentMessageOutboxError("message body exceeds the offer byte contract");
|
|
13791
14214
|
const existing = this.pendingByTask.get(input.taskId);
|
|
13792
14215
|
if (existing !== void 0) {
|
|
13793
|
-
if (existing.tenantId !== input.tenantId || existing.contentType !== input.contentType || existing.body !== input.body || existing.contract !== input.requirement.contract || !sameAgentRef(existing.agentRef, input.agentRef)) {
|
|
14216
|
+
if (existing.tenantId !== input.tenantId || existing.contentType !== input.contentType || existing.body !== input.body || existing.contract !== input.requirement.contract || input.sessionRef !== void 0 && existing.sessionRef !== input.sessionRef || !sameAgentRef(existing.agentRef, input.agentRef)) {
|
|
13794
14217
|
throw new AgentMessageOutboxError("required-message task already has a different immutable draft");
|
|
13795
14218
|
}
|
|
13796
14219
|
return existing;
|
|
13797
14220
|
}
|
|
13798
|
-
|
|
13799
|
-
|
|
14221
|
+
const sending = this.records().filter((record5) => !this.isTerminalEvidence(record5.taskId));
|
|
14222
|
+
if (sending.length >= input.maxPendingEvents) throw new AgentMessageOutboxError("message outbox event quota is exhausted");
|
|
14223
|
+
const pendingBytes = sending.reduce((sum, record5) => sum + record5.byteCount, 0);
|
|
13800
14224
|
if (pendingBytes + byteCount > input.maxPendingBytes) throw new AgentMessageOutboxError("message outbox byte quota is exhausted");
|
|
13801
14225
|
const record4 = Object.freeze({
|
|
13802
14226
|
schema: 1,
|
|
@@ -13818,8 +14242,9 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13818
14242
|
return record4;
|
|
13819
14243
|
});
|
|
13820
14244
|
}
|
|
13821
|
-
async activate(taskId, sessionRef) {
|
|
14245
|
+
async activate(taskId, sessionRef, isCurrent = () => true) {
|
|
13822
14246
|
return this.exclusive(async () => {
|
|
14247
|
+
if (!isCurrent() || this.revokedTasks.has(taskId)) return void 0;
|
|
13823
14248
|
const record4 = this.pendingByTask.get(taskId);
|
|
13824
14249
|
if (record4 === void 0) return void 0;
|
|
13825
14250
|
if (record4.sessionRef !== void 0) {
|
|
@@ -13832,6 +14257,18 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13832
14257
|
return activated;
|
|
13833
14258
|
});
|
|
13834
14259
|
}
|
|
14260
|
+
/** Local lifecycle cancellation, never a synthesized consumer disposition. */
|
|
14261
|
+
async revoke(taskId) {
|
|
14262
|
+
return this.exclusive(async () => {
|
|
14263
|
+
const record4 = this.pendingByTask.get(taskId);
|
|
14264
|
+
if (record4 === void 0 || this.revokedTasks.has(taskId)) return;
|
|
14265
|
+
await this.appendEntry({ schema: 1, kind: "revoke", taskId, messageId: record4.messageId });
|
|
14266
|
+
this.revokedTasks.add(taskId);
|
|
14267
|
+
});
|
|
14268
|
+
}
|
|
14269
|
+
isTerminalEvidence(taskId) {
|
|
14270
|
+
return this.revokedTasks.has(taskId) || this.dispositionByTask.get(taskId)?.outcome === "refused";
|
|
14271
|
+
}
|
|
13835
14272
|
publishPayload(record4) {
|
|
13836
14273
|
if (record4.sessionRef === void 0) throw new AgentMessageOutboxError("message draft is not bound to a durable session");
|
|
13837
14274
|
return AgentMessagePublishPayloadSchema.parse({
|
|
@@ -13855,6 +14292,7 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13855
14292
|
await this.appendEntry({ schema: 1, kind: "disposition", taskId, disposition });
|
|
13856
14293
|
if (disposition.outcome === "accepted") {
|
|
13857
14294
|
this.pendingByTask.delete(taskId);
|
|
14295
|
+
this.revokedTasks.delete(taskId);
|
|
13858
14296
|
this.dispositionByTask.delete(taskId);
|
|
13859
14297
|
} else {
|
|
13860
14298
|
this.dispositionByTask.set(taskId, disposition);
|
|
@@ -13873,6 +14311,7 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13873
14311
|
if (error.code === "ENOENT") return;
|
|
13874
14312
|
throw error;
|
|
13875
14313
|
}
|
|
14314
|
+
if (raw.length > 0 && !raw.endsWith("\n")) throw new AgentMessageOutboxError("durable JSONL log has an incomplete trailing frame; preserve the file for explicit repair");
|
|
13876
14315
|
for (const line of raw.split("\n")) {
|
|
13877
14316
|
if (line.length === 0) continue;
|
|
13878
14317
|
const entry = JSON.parse(line);
|
|
@@ -13887,11 +14326,16 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13887
14326
|
if (record4 === void 0 || record4.messageId !== entry.messageId) throw new AgentMessageOutboxError("message activation has no exact draft");
|
|
13888
14327
|
if (record4.sessionRef !== void 0 && record4.sessionRef !== entry.sessionRef) throw new AgentMessageOutboxError("message activation session conflicts");
|
|
13889
14328
|
this.pendingByTask.set(entry.taskId, Object.freeze({ ...record4, sessionRef: entry.sessionRef }));
|
|
14329
|
+
} else if (entry.kind === "revoke") {
|
|
14330
|
+
const record4 = this.pendingByTask.get(entry.taskId);
|
|
14331
|
+
if (record4 === void 0 || record4.messageId !== entry.messageId) throw new AgentMessageOutboxError("message revocation has no exact draft");
|
|
14332
|
+
this.revokedTasks.add(entry.taskId);
|
|
13890
14333
|
} else if (entry.kind === "disposition") {
|
|
13891
14334
|
const record4 = this.pendingByTask.get(entry.taskId);
|
|
13892
14335
|
if (record4 === void 0 || !exactDisposition(record4, entry.disposition)) throw new AgentMessageOutboxError("message outbox contains a mismatched disposition");
|
|
13893
14336
|
if (entry.disposition.outcome === "accepted") {
|
|
13894
14337
|
this.pendingByTask.delete(entry.taskId);
|
|
14338
|
+
this.revokedTasks.delete(entry.taskId);
|
|
13895
14339
|
this.dispositionByTask.delete(entry.taskId);
|
|
13896
14340
|
} else {
|
|
13897
14341
|
this.dispositionByTask.set(entry.taskId, entry.disposition);
|
|
@@ -13902,23 +14346,47 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13902
14346
|
}
|
|
13903
14347
|
}
|
|
13904
14348
|
async appendEntry(entry) {
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
await handle.write(`${stableJson(entry)}
|
|
13908
|
-
`, void 0, "utf8");
|
|
13909
|
-
await handle.sync();
|
|
13910
|
-
this.logEntries += 1;
|
|
13911
|
-
} finally {
|
|
13912
|
-
await handle.close();
|
|
13913
|
-
}
|
|
14349
|
+
await this.file.append(stableJson(entry));
|
|
14350
|
+
this.logEntries += 1;
|
|
13914
14351
|
}
|
|
13915
|
-
|
|
13916
|
-
|
|
14352
|
+
/**
|
|
14353
|
+
* Explicit operator maintenance under the Agent-home single-writer lease.
|
|
14354
|
+
* Copy complete terminal evidence durably before removing it from the live log.
|
|
14355
|
+
* Archives are audit artifacts, never another replay input. held remains live.
|
|
14356
|
+
*/
|
|
14357
|
+
async archiveTerminalRecords(archiveDirectory) {
|
|
14358
|
+
return this.exclusive(async () => {
|
|
14359
|
+
if (!path14__default.isAbsolute(archiveDirectory)) throw new AgentMessageOutboxError("message archive directory must be absolute");
|
|
14360
|
+
const terminal = this.records().filter((record4) => this.isTerminalEvidence(record4.taskId));
|
|
14361
|
+
if (terminal.length === 0) return void 0;
|
|
14362
|
+
await ensureSecureDir(archiveDirectory);
|
|
14363
|
+
const archivePath = path14__default.join(archiveDirectory, `message-terminal-${randomUUID()}.jsonl`);
|
|
14364
|
+
await atomicWriteFile(archivePath, this.snapshotEntries(terminal).map((entry) => `${stableJson(entry)}
|
|
14365
|
+
`).join(""), { mode: 384, fsync: true });
|
|
14366
|
+
const retained = this.records().filter((record4) => !this.isTerminalEvidence(record4.taskId));
|
|
14367
|
+
const entries = this.snapshotEntries(retained);
|
|
14368
|
+
await this.file.replace(entries.map((entry) => stableJson(entry)));
|
|
14369
|
+
for (const record4 of terminal) {
|
|
14370
|
+
this.pendingByTask.delete(record4.taskId);
|
|
14371
|
+
this.dispositionByTask.delete(record4.taskId);
|
|
14372
|
+
this.revokedTasks.delete(record4.taskId);
|
|
14373
|
+
}
|
|
14374
|
+
this.logEntries = entries.length;
|
|
14375
|
+
return archivePath;
|
|
14376
|
+
});
|
|
14377
|
+
}
|
|
14378
|
+
snapshotEntries(records) {
|
|
14379
|
+
return records.flatMap((record4) => {
|
|
13917
14380
|
const append = { schema: 1, kind: "append", record: record4 };
|
|
13918
14381
|
const disposition = this.dispositionByTask.get(record4.taskId);
|
|
13919
|
-
|
|
14382
|
+
const entries = disposition === void 0 ? [append] : [append, { schema: 1, kind: "disposition", taskId: record4.taskId, disposition }];
|
|
14383
|
+
if (this.revokedTasks.has(record4.taskId)) entries.push({ schema: 1, kind: "revoke", taskId: record4.taskId, messageId: record4.messageId });
|
|
14384
|
+
return entries;
|
|
13920
14385
|
});
|
|
13921
|
-
|
|
14386
|
+
}
|
|
14387
|
+
async compact() {
|
|
14388
|
+
const entries = this.snapshotEntries(this.records());
|
|
14389
|
+
await this.file.replace(entries.map((entry) => stableJson(entry)));
|
|
13922
14390
|
this.logEntries = entries.length;
|
|
13923
14391
|
}
|
|
13924
14392
|
async exclusive(fn) {
|
|
@@ -13929,6 +14397,7 @@ var AgentMessageOutbox = class _AgentMessageOutbox {
|
|
|
13929
14397
|
});
|
|
13930
14398
|
await prior;
|
|
13931
14399
|
try {
|
|
14400
|
+
this.file.assertWritable();
|
|
13932
14401
|
return await fn();
|
|
13933
14402
|
} finally {
|
|
13934
14403
|
release();
|
|
@@ -15109,7 +15578,7 @@ var TaskRunner = class {
|
|
|
15109
15578
|
*/
|
|
15110
15579
|
stoppingOffers = false;
|
|
15111
15580
|
get activeTaskCount() {
|
|
15112
|
-
return this.tasks.size;
|
|
15581
|
+
return this.tasks.size + this.startupOwners.size;
|
|
15113
15582
|
}
|
|
15114
15583
|
/**
|
|
15115
15584
|
* Transport-boundary classification for the currently active task. Legacy
|
|
@@ -15155,6 +15624,12 @@ var TaskRunner = class {
|
|
|
15155
15624
|
const active = this.tasks.get(taskId);
|
|
15156
15625
|
const context = pending ?? (active?.messageRequirement && active.agentRef && active.messageOutbox ? { agentRef: active.agentRef, requirement: active.messageRequirement, outbox: active.messageOutbox } : void 0);
|
|
15157
15626
|
if (context === void 0) throw new Error("task has no active required Agent message contract");
|
|
15627
|
+
const isCurrent = () => {
|
|
15628
|
+
if (this.messageContextByToken.get(input.contextToken) !== taskId || this.messageContextByTask.get(taskId) !== input.contextToken || this.pendingCancelled.has(taskId)) return false;
|
|
15629
|
+
const current2 = this.tasks.get(taskId);
|
|
15630
|
+
return current2 === void 0 ? active === void 0 && this.pendingMessageTasks.get(taskId) === pending : (active === void 0 || current2 === active) && current2.messageOutbox === context.outbox && this.canPublishAgentMessage(current2);
|
|
15631
|
+
};
|
|
15632
|
+
if (!isCurrent()) throw new Error("invalid or expired Agent message task context");
|
|
15158
15633
|
const record4 = await context.outbox.appendDraft({
|
|
15159
15634
|
taskId,
|
|
15160
15635
|
tenantId: this.deps.tenantId,
|
|
@@ -15165,8 +15640,17 @@ var TaskRunner = class {
|
|
|
15165
15640
|
maxPendingEvents: 64,
|
|
15166
15641
|
maxPendingBytes: 4 * 1024 * 1024
|
|
15167
15642
|
});
|
|
15168
|
-
if (
|
|
15169
|
-
const
|
|
15643
|
+
if (!isCurrent()) throw new Error("invalid or expired Agent message task context");
|
|
15644
|
+
const current = this.tasks.get(taskId);
|
|
15645
|
+
if (current === void 0) return { messageId: record4.messageId, state: "staged" };
|
|
15646
|
+
const activated = await this.activateAgentMessage(
|
|
15647
|
+
context.outbox,
|
|
15648
|
+
taskId,
|
|
15649
|
+
current.session.sessionRef,
|
|
15650
|
+
isCurrent,
|
|
15651
|
+
() => current.messageSendAttempted === true
|
|
15652
|
+
);
|
|
15653
|
+
if (!isCurrent()) throw new Error("invalid or expired Agent message task context");
|
|
15170
15654
|
if (activated === void 0) throw new Error("Agent message draft disappeared before activation");
|
|
15171
15655
|
this.sendAgentMessageRecord(context.outbox, activated);
|
|
15172
15656
|
return { messageId: activated.messageId, state: "pending" };
|
|
@@ -15202,19 +15686,39 @@ var TaskRunner = class {
|
|
|
15202
15686
|
});
|
|
15203
15687
|
return opened;
|
|
15204
15688
|
}
|
|
15689
|
+
/** A recovery terminal must not close first-message admission before this durable draft has a disposition. */
|
|
15690
|
+
hasPendingRecoveredAgentMessage(taskId) {
|
|
15691
|
+
return this.recoveredMessageOutboxes.get(taskId)?.retryableRecords().some((record4) => record4.taskId === taskId && record4.sessionRef !== void 0) === true;
|
|
15692
|
+
}
|
|
15205
15693
|
/** Retry stable recovered records after a transport handshake/re-handshake. */
|
|
15206
15694
|
retryRecoveredAgentMessages() {
|
|
15207
15695
|
for (const [taskId, outbox] of this.recoveredMessageOutboxes) {
|
|
15208
15696
|
const record4 = outbox.get(taskId);
|
|
15209
|
-
if (record4?.sessionRef !== void 0) this.sendAgentMessageRecord(outbox, record4);
|
|
15697
|
+
if (record4?.sessionRef !== void 0 && this.hasPendingRecoveredAgentMessage(taskId)) this.sendAgentMessageRecord(outbox, record4);
|
|
15698
|
+
}
|
|
15699
|
+
}
|
|
15700
|
+
canPublishAgentMessage(active) {
|
|
15701
|
+
return this.tasks.get(active.taskId) === active && !active.beingTornDown;
|
|
15702
|
+
}
|
|
15703
|
+
/** All authors and startup use the same queue-time and post-I/O authority check. */
|
|
15704
|
+
async activateAgentMessage(outbox, taskId, sessionRef, isCurrent, wasSendAttempted = () => false) {
|
|
15705
|
+
if (!isCurrent()) return void 0;
|
|
15706
|
+
const record4 = await outbox.activate(taskId, sessionRef, isCurrent);
|
|
15707
|
+
if (!isCurrent()) {
|
|
15708
|
+
if (!wasSendAttempted()) await outbox.revoke(taskId);
|
|
15709
|
+
return void 0;
|
|
15210
15710
|
}
|
|
15711
|
+
return record4;
|
|
15211
15712
|
}
|
|
15212
15713
|
sendAgentMessageRecord(outbox, record4) {
|
|
15714
|
+
const active = this.tasks.get(record4.taskId);
|
|
15715
|
+
const authorized = active === void 0 ? this.recoveredMessageOutboxes.get(record4.taskId) === outbox : active.messageOutbox === outbox && this.canPublishAgentMessage(active);
|
|
15716
|
+
if (!authorized || record4.sessionRef === void 0 || !outbox.retryableRecords().some((pending) => pending.messageId === record4.messageId)) return;
|
|
15717
|
+
if (active !== void 0) active.messageSendAttempted = true;
|
|
15213
15718
|
this.deps.send(createEnvelope("agent.message.publish", outbox.publishPayload(record4), {
|
|
15214
15719
|
taskId: record4.taskId,
|
|
15215
15720
|
sessionRef: record4.sessionRef
|
|
15216
15721
|
}));
|
|
15217
|
-
const active = this.tasks.get(record4.taskId);
|
|
15218
15722
|
if (active?.messageOutbox === outbox) {
|
|
15219
15723
|
if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
|
|
15220
15724
|
active.messageRetryTimer = setTimeout(() => {
|
|
@@ -15254,6 +15758,7 @@ var TaskRunner = class {
|
|
|
15254
15758
|
if (outcome === "accepted") this.recoveredMessageOutboxes.delete(taskId);
|
|
15255
15759
|
return;
|
|
15256
15760
|
}
|
|
15761
|
+
if (this.tasks.get(taskId) !== active) return;
|
|
15257
15762
|
if (outcome === "held") {
|
|
15258
15763
|
if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
|
|
15259
15764
|
active.messageRetryTimer = void 0;
|
|
@@ -15263,6 +15768,8 @@ var TaskRunner = class {
|
|
|
15263
15768
|
if (active.messageRetryTimer !== void 0) clearTimeout(active.messageRetryTimer);
|
|
15264
15769
|
active.messageRetryTimer = void 0;
|
|
15265
15770
|
this.revokeAgentMessageContext(taskId);
|
|
15771
|
+
active.pendingMessageCompletion = void 0;
|
|
15772
|
+
await this.fail(taskId, "required Agent message was refused", false);
|
|
15266
15773
|
return;
|
|
15267
15774
|
}
|
|
15268
15775
|
if (outcome !== "accepted") return;
|
|
@@ -15281,6 +15788,9 @@ var TaskRunner = class {
|
|
|
15281
15788
|
/** M4 Phase 2: stop claiming any FUTURE `task.offer` — see `stoppingOffers`'s own doc comment. Idempotent. */
|
|
15282
15789
|
stopAcceptingOffers() {
|
|
15283
15790
|
this.stoppingOffers = true;
|
|
15791
|
+
if (this.startupRetryTimer) clearTimeout(this.startupRetryTimer);
|
|
15792
|
+
this.startupRetryTimer = void 0;
|
|
15793
|
+
for (const abort of this.inFlightBlobAborts.values()) abort.abort();
|
|
15284
15794
|
}
|
|
15285
15795
|
/**
|
|
15286
15796
|
* Shutdown of every currently ACTIVE task for the control socket's
|
|
@@ -15319,9 +15829,65 @@ var TaskRunner = class {
|
|
|
15319
15829
|
* ahead of this method — see `daemon-control-socket.test.ts`'s dedicated
|
|
15320
15830
|
* regression test for the exact scenario.
|
|
15321
15831
|
*/
|
|
15322
|
-
|
|
15323
|
-
|
|
15832
|
+
/** Startup resources have an owner even before a Session can become active. */
|
|
15833
|
+
startupRetryTimer;
|
|
15834
|
+
claimedHarnesses = /* @__PURE__ */ new Map();
|
|
15835
|
+
homeReservations = /* @__PURE__ */ new Map();
|
|
15836
|
+
startupOwners = /* @__PURE__ */ new Map();
|
|
15837
|
+
startupDisposals = /* @__PURE__ */ new Map();
|
|
15838
|
+
disposeStartupOwner(taskId) {
|
|
15839
|
+
const existing = this.startupDisposals.get(taskId);
|
|
15840
|
+
if (existing) return existing;
|
|
15841
|
+
const attempt = this.disposeStartupOwnerOnce(taskId).finally(() => this.startupDisposals.delete(taskId));
|
|
15842
|
+
this.startupDisposals.set(taskId, attempt);
|
|
15843
|
+
return attempt;
|
|
15844
|
+
}
|
|
15845
|
+
async disposeStartupOwnerOnce(taskId) {
|
|
15846
|
+
const owner = this.startupOwners.get(taskId);
|
|
15847
|
+
if (!owner) return true;
|
|
15848
|
+
try {
|
|
15849
|
+
await owner.settle?.();
|
|
15850
|
+
await owner.dispose();
|
|
15851
|
+
await this.deps.awaitTerminalCommit?.(taskId);
|
|
15852
|
+
await owner.release();
|
|
15853
|
+
} catch (caught) {
|
|
15854
|
+
const failure = isRuntimeDisposalFailure(caught) ? caught : new RuntimeDisposalFailure({
|
|
15855
|
+
stage: "quiescence",
|
|
15856
|
+
reason: "startup runtime disposal has not been confirmed"
|
|
15857
|
+
}, { cause: caught });
|
|
15858
|
+
this.deps.onRuntimeDisposalFailure?.({
|
|
15859
|
+
taskId,
|
|
15860
|
+
runtimeId: owner.runtimeId,
|
|
15861
|
+
stage: failure.stage,
|
|
15862
|
+
reason: failure.message
|
|
15863
|
+
});
|
|
15864
|
+
console.error(`[byok/client] startup ownership retained for ${taskId}: ${failure.message}`);
|
|
15865
|
+
if (!this.startupRetryTimer && !this.stoppingOffers) {
|
|
15866
|
+
this.startupRetryTimer = setTimeout(() => {
|
|
15867
|
+
this.startupRetryTimer = void 0;
|
|
15868
|
+
for (const id of this.startupOwners.keys()) {
|
|
15869
|
+
if (!this.inFlightOffers.has(id)) void this.disposeStartupOwner(id);
|
|
15870
|
+
}
|
|
15871
|
+
}, 1e3);
|
|
15872
|
+
this.startupRetryTimer.unref?.();
|
|
15873
|
+
}
|
|
15874
|
+
return false;
|
|
15875
|
+
}
|
|
15876
|
+
if (this.startupOwners.get(taskId) === owner) this.startupOwners.delete(taskId);
|
|
15877
|
+
this.pendingMessageTasks.delete(taskId);
|
|
15878
|
+
this.revokeAgentMessageContext(taskId);
|
|
15879
|
+
this.revokeAgentMemoryContext(taskId);
|
|
15880
|
+
return true;
|
|
15881
|
+
}
|
|
15882
|
+
async shutdownActiveTasks(reason) {
|
|
15883
|
+
const active = [...this.tasks.values()];
|
|
15324
15884
|
await Promise.all(active.map((task) => this.shutdownTask(task, reason)));
|
|
15885
|
+
for (const taskId of this.startupOwners.keys()) {
|
|
15886
|
+
if (!await this.disposeStartupOwner(taskId)) throw new RuntimeDisposalFailure({
|
|
15887
|
+
stage: "quiescence",
|
|
15888
|
+
reason: "startup runtime ownership remains quarantined"
|
|
15889
|
+
});
|
|
15890
|
+
}
|
|
15325
15891
|
}
|
|
15326
15892
|
/**
|
|
15327
15893
|
* M5 batch-3 (workstream 2): the ONE shared per-task teardown sequence —
|
|
@@ -15353,14 +15919,19 @@ var TaskRunner = class {
|
|
|
15353
15919
|
* a genuine protocol bug, not a benign race — mirrors `pump()`'s own
|
|
15354
15920
|
* identity-check guard for the same class of race.
|
|
15355
15921
|
*/
|
|
15922
|
+
async interruptBounded(session) {
|
|
15923
|
+
await raceSettleFirst(
|
|
15924
|
+
() => session.interrupt(),
|
|
15925
|
+
this.deps.shutdownInterruptTimeoutMs ?? DEFAULT_SHUTDOWN_INTERRUPT_TIMEOUT_MS
|
|
15926
|
+
);
|
|
15927
|
+
}
|
|
15356
15928
|
async teardownActiveTask(active, reason, retryable) {
|
|
15357
15929
|
if (active.finalizationStarted) return this.finish(active.taskId);
|
|
15358
15930
|
if (!this.reserveSemanticTerminal(active)) return active.semanticTerminalSettled ?? false;
|
|
15359
15931
|
active.beingTornDown = true;
|
|
15360
15932
|
active.blobAbort.abort();
|
|
15361
15933
|
await this.observeGit(active, "salvage");
|
|
15362
|
-
|
|
15363
|
-
await raceSettleFirst(() => active.session.interrupt(), timeoutMs);
|
|
15934
|
+
await this.interruptBounded(active.session);
|
|
15364
15935
|
if (this.tasks.get(active.taskId) !== active) return true;
|
|
15365
15936
|
await this.persistAgentTerminalEvidence(active, "failed", reason);
|
|
15366
15937
|
this.deps.send(
|
|
@@ -15462,7 +16033,7 @@ var TaskRunner = class {
|
|
|
15462
16033
|
}
|
|
15463
16034
|
}
|
|
15464
16035
|
async handleOffer(taskId, payload, strictAgentOffer) {
|
|
15465
|
-
if (this.tasks.has(taskId) || this.finishedTaskIds.has(taskId) || this.strictDeclinedTaskIds.has(taskId)) {
|
|
16036
|
+
if (this.tasks.has(taskId) || this.startupOwners.has(taskId) || this.inFlightOffers.has(taskId) || this.finishedTaskIds.has(taskId) || this.strictDeclinedTaskIds.has(taskId)) {
|
|
15466
16037
|
return;
|
|
15467
16038
|
}
|
|
15468
16039
|
let agentRef;
|
|
@@ -15505,9 +16076,29 @@ var TaskRunner = class {
|
|
|
15505
16076
|
}
|
|
15506
16077
|
this.inFlightOffers.add(taskId);
|
|
15507
16078
|
const blobAbort = new AbortController();
|
|
16079
|
+
const startupDeadline = Date.now() + (this.deps.startupTimeoutMs ?? 3e4);
|
|
16080
|
+
const startupTimer = setTimeout(
|
|
16081
|
+
() => blobAbort.abort(new Error("runtime startup deadline exceeded during admission/start")),
|
|
16082
|
+
this.deps.startupTimeoutMs ?? 3e4
|
|
16083
|
+
);
|
|
16084
|
+
startupTimer.unref?.();
|
|
16085
|
+
const admissionWithdrawn = () => {
|
|
16086
|
+
if (!blobAbort.signal.aborted) return false;
|
|
16087
|
+
const cancelled = this.pendingCancelled.has(taskId);
|
|
16088
|
+
decline(cancelled ? "cancelled before claim" : this.stoppingOffers ? "daemon is shutting down" : "runtime startup deadline exceeded", !cancelled);
|
|
16089
|
+
return true;
|
|
16090
|
+
};
|
|
15508
16091
|
this.inFlightBlobAborts.set(taskId, blobAbort);
|
|
15509
16092
|
let agentBinding;
|
|
15510
16093
|
let agentLeaseTransferred = false;
|
|
16094
|
+
let reservedHome;
|
|
16095
|
+
const releaseReservation = () => {
|
|
16096
|
+
if (reservedHome === void 0) return;
|
|
16097
|
+
const remaining = this.homeReservations.get(reservedHome) - 1;
|
|
16098
|
+
if (remaining === 0) this.homeReservations.delete(reservedHome);
|
|
16099
|
+
else this.homeReservations.set(reservedHome, remaining);
|
|
16100
|
+
reservedHome = void 0;
|
|
16101
|
+
};
|
|
15511
16102
|
try {
|
|
15512
16103
|
if (this.pendingCancelled.has(taskId)) {
|
|
15513
16104
|
const reason = this.pendingCancelled.get(taskId);
|
|
@@ -15532,11 +16123,14 @@ var TaskRunner = class {
|
|
|
15532
16123
|
);
|
|
15533
16124
|
return;
|
|
15534
16125
|
}
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
16126
|
+
if (admissionWithdrawn()) return;
|
|
16127
|
+
const active = this.deps.agentHome.executionLeaseManager.activeAttemptCount(canonicalHome) + (this.homeReservations.get(canonicalHome) ?? 0);
|
|
16128
|
+
if (active >= limit) {
|
|
16129
|
+
decline(`agent home busy: ${active} active attempt(s)`, true);
|
|
15538
16130
|
return;
|
|
15539
16131
|
}
|
|
16132
|
+
this.homeReservations.set(canonicalHome, (this.homeReservations.get(canonicalHome) ?? 0) + 1);
|
|
16133
|
+
reservedHome = canonicalHome;
|
|
15540
16134
|
}
|
|
15541
16135
|
const guarded = this.deps.admissionGuard?.({ taskId, payload });
|
|
15542
16136
|
if (guarded !== void 0 && !guarded.admit) {
|
|
@@ -15568,6 +16162,14 @@ var TaskRunner = class {
|
|
|
15568
16162
|
);
|
|
15569
16163
|
return;
|
|
15570
16164
|
}
|
|
16165
|
+
if (payload.harnessId !== void 0 && (payload.runtime !== void 0 || payload.dispatchSelection !== void 0)) {
|
|
16166
|
+
decline("harnessId cannot be combined with a built-in runtime selection", false);
|
|
16167
|
+
return;
|
|
16168
|
+
}
|
|
16169
|
+
if (payload.harnessId !== void 0 && !(this.deps.getServerCapabilities?.() ?? []).includes("custom-harness")) {
|
|
16170
|
+
decline("server does not support custom-harness identity", false);
|
|
16171
|
+
return;
|
|
16172
|
+
}
|
|
15571
16173
|
const requiredToolsets = "requiredToolsets" in payload ? payload.requiredToolsets : void 0;
|
|
15572
16174
|
const resolvedMcp = requiredToolsets ? this.resolveMcpServers(requiredToolsets) : void 0;
|
|
15573
16175
|
if (resolvedMcp && !resolvedMcp.ok) {
|
|
@@ -15580,17 +16182,29 @@ var TaskRunner = class {
|
|
|
15580
16182
|
return;
|
|
15581
16183
|
}
|
|
15582
16184
|
const offered = withoutRequiredToolsets(payload);
|
|
15583
|
-
const requestedRuntime = payload.dispatchSelection?.runtimeId ?? payload.runtime;
|
|
16185
|
+
const requestedRuntime = payload.harnessId ?? payload.dispatchSelection?.runtimeId ?? payload.runtime;
|
|
15584
16186
|
const requiresAgentMemoryMcp = agentRef !== void 0 && this.deps.agentMemoryMcpBin !== void 0 && isAgentMemorySecureFilesystemAvailable(this.deps.agentMemoryFilesystemHelperBin !== void 0);
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
16187
|
+
let pick;
|
|
16188
|
+
try {
|
|
16189
|
+
pick = await this.pickAdapter(
|
|
16190
|
+
requestedRuntime,
|
|
16191
|
+
payload.policy.mode,
|
|
16192
|
+
requiredToolsets !== void 0 || messageRequirement !== void 0 || requiresAgentMemoryMcp,
|
|
16193
|
+
blobAbort.signal
|
|
16194
|
+
);
|
|
16195
|
+
} catch (error) {
|
|
16196
|
+
if (!admissionWithdrawn()) decline(`runtime detection failed: ${errorMessage5(error)}`, true);
|
|
16197
|
+
return;
|
|
16198
|
+
}
|
|
16199
|
+
if (admissionWithdrawn()) return;
|
|
15590
16200
|
if (!pick.ok) {
|
|
15591
16201
|
decline(pick.reason, pick.retryable);
|
|
15592
16202
|
return;
|
|
15593
16203
|
}
|
|
16204
|
+
if (!isKnownRuntimeId(pick.descriptor.id) && !(this.deps.getServerCapabilities?.() ?? []).includes("custom-harness")) {
|
|
16205
|
+
decline("server does not support the selected custom harness identity", false);
|
|
16206
|
+
return;
|
|
16207
|
+
}
|
|
15594
16208
|
const env = buildRuntimeEnv({
|
|
15595
16209
|
ambient: process.env,
|
|
15596
16210
|
requirements: pick.descriptor.environmentRequirements,
|
|
@@ -15664,18 +16278,20 @@ var TaskRunner = class {
|
|
|
15664
16278
|
}
|
|
15665
16279
|
let prepared;
|
|
15666
16280
|
try {
|
|
15667
|
-
prepared = await pick.adapter.prepare({
|
|
16281
|
+
prepared = await awaitAdmission(() => pick.adapter.prepare({
|
|
16282
|
+
signal: blobAbort.signal,
|
|
15668
16283
|
offer: offered,
|
|
15669
16284
|
policy: decision.policy,
|
|
15670
16285
|
descriptor: pick.descriptor,
|
|
15671
16286
|
requiredToolsetIds: requiredToolsets ?? [],
|
|
15672
16287
|
...taskMcpServers === void 0 ? {} : { mcpServers: taskMcpServers },
|
|
15673
16288
|
...mcpToolsetTools === void 0 ? {} : { mcpToolsetTools }
|
|
15674
|
-
});
|
|
16289
|
+
}), blobAbort.signal);
|
|
15675
16290
|
} catch (error) {
|
|
15676
|
-
decline(`runtime preparation failed: ${errorMessage5(error)}`, true);
|
|
16291
|
+
if (!admissionWithdrawn()) decline(`runtime preparation failed: ${errorMessage5(error)}`, true);
|
|
15677
16292
|
return;
|
|
15678
16293
|
}
|
|
16294
|
+
if (admissionWithdrawn()) return;
|
|
15679
16295
|
if (prepared.kind === "reject") {
|
|
15680
16296
|
decline(prepared.reason, prepared.retryable);
|
|
15681
16297
|
return;
|
|
@@ -15683,6 +16299,7 @@ var TaskRunner = class {
|
|
|
15683
16299
|
if (agentRef !== void 0) {
|
|
15684
16300
|
try {
|
|
15685
16301
|
agentBinding = await this.deps.agentHome.acquireExecution(agentRef, { taskId, sessionRef });
|
|
16302
|
+
releaseReservation();
|
|
15686
16303
|
} catch (error) {
|
|
15687
16304
|
decline(
|
|
15688
16305
|
`Agent home admission failed: ${errorMessage5(error)}`,
|
|
@@ -15807,7 +16424,16 @@ var TaskRunner = class {
|
|
|
15807
16424
|
},
|
|
15808
16425
|
forwardedEnvironmentNames: Object.freeze(Object.keys(env).sort())
|
|
15809
16426
|
});
|
|
16427
|
+
if (admissionWithdrawn()) {
|
|
16428
|
+
gitLease?.release();
|
|
16429
|
+
return;
|
|
16430
|
+
}
|
|
15810
16431
|
await this.deps.beforeClaim?.(taskId, manifest.descriptor.id);
|
|
16432
|
+
if (admissionWithdrawn()) {
|
|
16433
|
+
gitLease?.release();
|
|
16434
|
+
return;
|
|
16435
|
+
}
|
|
16436
|
+
if (!isKnownRuntimeId(manifest.descriptor.id)) this.claimedHarnesses.set(taskId, manifest.descriptor.id);
|
|
15811
16437
|
this.deps.send(
|
|
15812
16438
|
createEnvelope(
|
|
15813
16439
|
"task.claim",
|
|
@@ -15825,6 +16451,7 @@ var TaskRunner = class {
|
|
|
15825
16451
|
// where an auto-selected task left the server never learning
|
|
15826
16452
|
// which runtime actually ran.
|
|
15827
16453
|
runtime: isKnownRuntimeId(manifest.descriptor.id) ? manifest.descriptor.id : void 0,
|
|
16454
|
+
...!isKnownRuntimeId(manifest.descriptor.id) ? { harnessId: manifest.descriptor.id } : {},
|
|
15828
16455
|
// S0/D-4 (`task.claim.capabilities`, docs/protocol.md §2.4): the
|
|
15829
16456
|
// selected adapter's own capability self-report, carried on the
|
|
15830
16457
|
// same message that establishes the task↔runtime binding. The
|
|
@@ -15942,8 +16569,57 @@ var TaskRunner = class {
|
|
|
15942
16569
|
};
|
|
15943
16570
|
let session;
|
|
15944
16571
|
try {
|
|
15945
|
-
session = await
|
|
16572
|
+
session = await startOwnedRuntime(
|
|
16573
|
+
(input) => prepared.operation.start(input),
|
|
16574
|
+
startInput,
|
|
16575
|
+
blobAbort.signal,
|
|
16576
|
+
Math.max(0, startupDeadline - Date.now())
|
|
16577
|
+
);
|
|
15946
16578
|
} catch (err) {
|
|
16579
|
+
const disposalFailure = isRuntimeStartupDisposalFailure(err);
|
|
16580
|
+
const cancelled = this.pendingCancelled.has(taskId);
|
|
16581
|
+
if (disposalFailure || cancelled) {
|
|
16582
|
+
if (cancelled) this.revokeAgentMessageContext(taskId);
|
|
16583
|
+
const ownedBinding2 = agentBinding;
|
|
16584
|
+
const cancellation = createEnvelope("task.cancelled", {
|
|
16585
|
+
reason: this.pendingCancelled.get(taskId),
|
|
16586
|
+
...terminalIdentity(this.claimedHarnesses.get(taskId)),
|
|
16587
|
+
...agentBinding === void 0 ? {} : { agentRef: agentBinding.resolution.agentRef }
|
|
16588
|
+
}, { taskId });
|
|
16589
|
+
let cancellationSettled = false;
|
|
16590
|
+
this.startupOwners.set(taskId, {
|
|
16591
|
+
runtimeId: pick.descriptor.id,
|
|
16592
|
+
dispose: disposalFailure ? err.retryDisposal : async () => {
|
|
16593
|
+
},
|
|
16594
|
+
...cancelled ? { settle: async () => {
|
|
16595
|
+
if (cancellationSettled) return;
|
|
16596
|
+
await this.pendingMessageTasks.get(taskId)?.outbox.revoke(taskId);
|
|
16597
|
+
this.deps.send(cancellation);
|
|
16598
|
+
cancellationSettled = true;
|
|
16599
|
+
this.pendingCancelled.delete(taskId);
|
|
16600
|
+
this.addFinishedTaskId(taskId);
|
|
16601
|
+
} } : {},
|
|
16602
|
+
release: async () => {
|
|
16603
|
+
await ownedBinding2?.lease.release();
|
|
16604
|
+
gitLease?.release();
|
|
16605
|
+
}
|
|
16606
|
+
});
|
|
16607
|
+
agentLeaseTransferred = true;
|
|
16608
|
+
if (disposalFailure) this.deps.onRuntimeDisposalFailure?.({
|
|
16609
|
+
taskId,
|
|
16610
|
+
runtimeId: pick.descriptor.id,
|
|
16611
|
+
stage: "quiescence",
|
|
16612
|
+
reason: err.message
|
|
16613
|
+
});
|
|
16614
|
+
if (!cancelled) {
|
|
16615
|
+
if (agentBinding === void 0) await this.fail(taskId, errorMessage5(err), false);
|
|
16616
|
+
else await this.failClaimedAgent(taskId, errorMessage5(err), false, {
|
|
16617
|
+
binding: agentBinding,
|
|
16618
|
+
runtimeId: pick.descriptor.id
|
|
16619
|
+
});
|
|
16620
|
+
}
|
|
16621
|
+
return;
|
|
16622
|
+
}
|
|
15947
16623
|
const failure = projectRuntimeBoundaryFailure(err, "start");
|
|
15948
16624
|
if (failure.contractViolation) {
|
|
15949
16625
|
console.error("[byok/client] runtime adapter start() returned an untyped failure", err);
|
|
@@ -15960,143 +16636,169 @@ var TaskRunner = class {
|
|
|
15960
16636
|
}
|
|
15961
16637
|
return;
|
|
15962
16638
|
}
|
|
15963
|
-
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
15967
|
-
|
|
15968
|
-
|
|
15969
|
-
|
|
15970
|
-
taskId,
|
|
15971
|
-
`Agent session execution lease could not bind the runtime session: ${errorMessage5(error)}`,
|
|
15972
|
-
false,
|
|
15973
|
-
{
|
|
15974
|
-
binding: agentBinding,
|
|
15975
|
-
runtimeId: pick.descriptor.id,
|
|
15976
|
-
sessionRef: session.sessionRef
|
|
15977
|
-
}
|
|
15978
|
-
);
|
|
15979
|
-
return;
|
|
16639
|
+
const ownedBinding = agentBinding;
|
|
16640
|
+
this.startupOwners.set(taskId, {
|
|
16641
|
+
runtimeId: pick.descriptor.id,
|
|
16642
|
+
dispose: () => session.close(),
|
|
16643
|
+
release: async () => {
|
|
16644
|
+
await ownedBinding?.lease.release();
|
|
16645
|
+
gitLease?.release();
|
|
15980
16646
|
}
|
|
15981
|
-
}
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
session,
|
|
15989
|
-
workspaceDir,
|
|
15990
|
-
...agentBinding === void 0 ? {} : {
|
|
15991
|
-
agentBinding,
|
|
15992
|
-
agentRef: agentBinding.resolution.agentRef,
|
|
15993
|
-
agentHandoff: {
|
|
15994
|
-
sessionRef: session.sessionRef,
|
|
15995
|
-
runtimeId: pick.descriptor.id,
|
|
15996
|
-
cwd: workspaceDir
|
|
15997
|
-
}
|
|
15998
|
-
},
|
|
15999
|
-
gitWorkspaceId,
|
|
16000
|
-
gitLease,
|
|
16001
|
-
gitBaseline,
|
|
16002
|
-
summaryParts: [],
|
|
16003
|
-
finalTextParts: [],
|
|
16004
|
-
batcher: new ProgressBatcher(
|
|
16005
|
-
(seq3, events) => {
|
|
16006
|
-
const projected = active.egressEnabled ? this.deps.agentEgress?.projectLatestValue({
|
|
16007
|
-
agentRef: active.agentRef,
|
|
16647
|
+
});
|
|
16648
|
+
try {
|
|
16649
|
+
if (agentBinding !== void 0) {
|
|
16650
|
+
try {
|
|
16651
|
+
await agentBinding.lease.bindSession(session.sessionRef);
|
|
16652
|
+
} catch (error) {
|
|
16653
|
+
await this.failClaimedAgent(
|
|
16008
16654
|
taskId,
|
|
16009
|
-
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16015
|
-
|
|
16016
|
-
|
|
16017
|
-
|
|
16018
|
-
|
|
16019
|
-
|
|
16020
|
-
|
|
16021
|
-
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
cwd: workspaceDir,
|
|
16034
|
-
leaseId: binding.lease.leaseId
|
|
16035
|
-
}));
|
|
16036
|
-
} catch (error) {
|
|
16037
|
-
await session.close().catch(() => {
|
|
16038
|
-
});
|
|
16039
|
-
await this.failClaimedAgent(
|
|
16040
|
-
taskId,
|
|
16041
|
-
`Agent session handoff could not be durably written before start: ${errorMessage5(error)}`,
|
|
16042
|
-
false,
|
|
16043
|
-
{
|
|
16044
|
-
binding: agentBinding,
|
|
16655
|
+
`Agent session execution lease could not bind the runtime session: ${errorMessage5(error)}`,
|
|
16656
|
+
false,
|
|
16657
|
+
{
|
|
16658
|
+
binding: agentBinding,
|
|
16659
|
+
runtimeId: pick.descriptor.id,
|
|
16660
|
+
sessionRef: session.sessionRef
|
|
16661
|
+
}
|
|
16662
|
+
);
|
|
16663
|
+
return;
|
|
16664
|
+
}
|
|
16665
|
+
}
|
|
16666
|
+
let active;
|
|
16667
|
+
active = {
|
|
16668
|
+
taskId,
|
|
16669
|
+
blobAbort,
|
|
16670
|
+
egressEnabled: "egressPolicy" in payload,
|
|
16671
|
+
adapter: pick.adapter,
|
|
16672
|
+
session,
|
|
16673
|
+
workspaceDir,
|
|
16674
|
+
...agentBinding === void 0 ? {} : {
|
|
16675
|
+
agentBinding,
|
|
16676
|
+
agentRef: agentBinding.resolution.agentRef,
|
|
16677
|
+
agentHandoff: {
|
|
16678
|
+
sessionRef: session.sessionRef,
|
|
16045
16679
|
runtimeId: pick.descriptor.id,
|
|
16046
|
-
|
|
16680
|
+
cwd: workspaceDir
|
|
16047
16681
|
}
|
|
16682
|
+
},
|
|
16683
|
+
gitWorkspaceId,
|
|
16684
|
+
gitLease,
|
|
16685
|
+
gitBaseline,
|
|
16686
|
+
summaryParts: [],
|
|
16687
|
+
finalTextParts: [],
|
|
16688
|
+
batcher: new ProgressBatcher(
|
|
16689
|
+
(seq3, events) => {
|
|
16690
|
+
const projected = active.egressEnabled ? this.deps.agentEgress?.projectLatestValue({
|
|
16691
|
+
agentRef: active.agentRef,
|
|
16692
|
+
taskId,
|
|
16693
|
+
events,
|
|
16694
|
+
serverCapabilities: this.deps.getServerCapabilities?.() ?? []
|
|
16695
|
+
}) ?? [] : events;
|
|
16696
|
+
if (projected.length > 0) this.deps.send(createEnvelope("task.progress", { seq: seq3, events: [...projected] }, { taskId, seq: seq3 }));
|
|
16697
|
+
},
|
|
16698
|
+
this.deps.batcherOptions
|
|
16699
|
+
),
|
|
16700
|
+
approvalQueue: [],
|
|
16701
|
+
outputBytesSoFar: 0,
|
|
16702
|
+
startedAtMs: Date.now(),
|
|
16703
|
+
...messageRequirement === void 0 ? {} : {
|
|
16704
|
+
messageRequirement,
|
|
16705
|
+
messageOutbox: this.pendingMessageTasks.get(taskId).outbox
|
|
16706
|
+
},
|
|
16707
|
+
...terminalProjection === void 0 ? {} : { terminalProjection }
|
|
16708
|
+
};
|
|
16709
|
+
if (agentBinding !== void 0) {
|
|
16710
|
+
const binding = agentBinding;
|
|
16711
|
+
try {
|
|
16712
|
+
await this.deps.agentHome.mutateExecution(binding, () => this.deps.agentSessionHandoffs.record({
|
|
16713
|
+
agentRef: binding.resolution.agentRef,
|
|
16714
|
+
taskId,
|
|
16715
|
+
sessionRef: session.sessionRef,
|
|
16716
|
+
runtimeId: pick.descriptor.id,
|
|
16717
|
+
cwd: workspaceDir,
|
|
16718
|
+
leaseId: binding.lease.leaseId
|
|
16719
|
+
}));
|
|
16720
|
+
} catch (error) {
|
|
16721
|
+
await this.failClaimedAgent(
|
|
16722
|
+
taskId,
|
|
16723
|
+
`Agent session handoff could not be durably written before start: ${errorMessage5(error)}`,
|
|
16724
|
+
false,
|
|
16725
|
+
{
|
|
16726
|
+
binding: agentBinding,
|
|
16727
|
+
runtimeId: pick.descriptor.id,
|
|
16728
|
+
sessionRef: session.sessionRef
|
|
16729
|
+
}
|
|
16730
|
+
);
|
|
16731
|
+
return;
|
|
16732
|
+
}
|
|
16733
|
+
}
|
|
16734
|
+
const activatedMessageRecord = active.messageOutbox === void 0 ? void 0 : await this.activateAgentMessage(
|
|
16735
|
+
active.messageOutbox,
|
|
16736
|
+
taskId,
|
|
16737
|
+
session.sessionRef,
|
|
16738
|
+
() => !this.pendingCancelled.has(taskId) && !blobAbort.signal.aborted
|
|
16739
|
+
);
|
|
16740
|
+
if (this.pendingCancelled.has(taskId)) {
|
|
16741
|
+
const reason = this.pendingCancelled.get(taskId);
|
|
16742
|
+
this.pendingCancelled.delete(taskId);
|
|
16743
|
+
agentLeaseTransferred = agentBinding !== void 0;
|
|
16744
|
+
this.startupOwners.delete(taskId);
|
|
16745
|
+
this.tasks.set(taskId, active);
|
|
16746
|
+
this.reserveSemanticTerminal(active);
|
|
16747
|
+
await this.interruptBounded(session);
|
|
16748
|
+
await this.updateGitPhaseBestEffort(gitWorkspaceId, "cancelled");
|
|
16749
|
+
await this.persistAgentTerminalEvidence(active, "cancelled", reason);
|
|
16750
|
+
this.deps.send(
|
|
16751
|
+
createEnvelope(
|
|
16752
|
+
"task.cancelled",
|
|
16753
|
+
{ reason, ...this.terminalInferenceUsagePayload(active), ...this.agentTerminalPayload(active) },
|
|
16754
|
+
{ taskId }
|
|
16755
|
+
)
|
|
16048
16756
|
);
|
|
16757
|
+
await this.finish(taskId);
|
|
16049
16758
|
return;
|
|
16050
16759
|
}
|
|
16051
|
-
|
|
16052
|
-
const activatedMessageRecord = active.messageOutbox === void 0 ? void 0 : await active.messageOutbox.activate(taskId, session.sessionRef);
|
|
16053
|
-
if (this.pendingCancelled.has(taskId)) {
|
|
16054
|
-
const reason = this.pendingCancelled.get(taskId);
|
|
16055
|
-
this.pendingCancelled.delete(taskId);
|
|
16760
|
+
this.deps.send(createEnvelope("task.started", {}, { taskId }));
|
|
16056
16761
|
agentLeaseTransferred = agentBinding !== void 0;
|
|
16762
|
+
this.inFlightBlobAborts.delete(taskId);
|
|
16763
|
+
this.startupOwners.delete(taskId);
|
|
16057
16764
|
this.tasks.set(taskId, active);
|
|
16058
|
-
this.
|
|
16059
|
-
|
|
16060
|
-
|
|
16061
|
-
} catch {
|
|
16765
|
+
this.pendingMessageTasks.delete(taskId);
|
|
16766
|
+
if (active.messageOutbox !== void 0 && activatedMessageRecord !== void 0) {
|
|
16767
|
+
this.sendAgentMessageRecord(active.messageOutbox, activatedMessageRecord);
|
|
16062
16768
|
}
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
16077
|
-
|
|
16078
|
-
|
|
16079
|
-
|
|
16080
|
-
|
|
16081
|
-
|
|
16082
|
-
|
|
16083
|
-
|
|
16084
|
-
|
|
16085
|
-
|
|
16086
|
-
|
|
16087
|
-
if (agentBinding === void 0) {
|
|
16088
|
-
void this.deps.sessionWorkspaces.record(session.sessionRef, {
|
|
16089
|
-
workspaceDir,
|
|
16090
|
-
runtimeSessionId: session.sessionRef,
|
|
16091
|
-
...gitWorkspaceId ? { workspaceKind: "git", gitWorkspaceId } : {}
|
|
16092
|
-
}).catch(() => {
|
|
16093
|
-
});
|
|
16094
|
-
}
|
|
16095
|
-
if (gitWorkspaceId && this.deps.gitWorkspaceStore) {
|
|
16096
|
-
void this.deps.gitWorkspaceStore.attachSession(gitWorkspaceId, session.sessionRef).catch(() => {
|
|
16769
|
+
if (payload.limits?.maxDurationMs !== void 0) {
|
|
16770
|
+
this.armMaxDurationTimer(active, payload.limits.maxDurationMs);
|
|
16771
|
+
}
|
|
16772
|
+
void this.pump(active);
|
|
16773
|
+
if (agentBinding === void 0) {
|
|
16774
|
+
void this.deps.sessionWorkspaces.record(session.sessionRef, {
|
|
16775
|
+
workspaceDir,
|
|
16776
|
+
runtimeSessionId: session.sessionRef,
|
|
16777
|
+
...gitWorkspaceId ? { workspaceKind: "git", gitWorkspaceId } : {}
|
|
16778
|
+
}).catch(() => {
|
|
16779
|
+
});
|
|
16780
|
+
}
|
|
16781
|
+
if (gitWorkspaceId && this.deps.gitWorkspaceStore) {
|
|
16782
|
+
void this.deps.gitWorkspaceStore.attachSession(gitWorkspaceId, session.sessionRef).catch(() => {
|
|
16783
|
+
});
|
|
16784
|
+
}
|
|
16785
|
+
} catch (error) {
|
|
16786
|
+
if (this.tasks.has(taskId)) throw error;
|
|
16787
|
+
const reason = `runtime activation failed: ${errorMessage5(error)}`;
|
|
16788
|
+
if (agentBinding === void 0) await this.fail(taskId, reason, false);
|
|
16789
|
+
else await this.failClaimedAgent(taskId, reason, false, {
|
|
16790
|
+
binding: agentBinding,
|
|
16791
|
+
runtimeId: pick.descriptor.id,
|
|
16792
|
+
sessionRef: session.sessionRef
|
|
16097
16793
|
});
|
|
16098
16794
|
}
|
|
16099
16795
|
} finally {
|
|
16796
|
+
clearTimeout(startupTimer);
|
|
16797
|
+
releaseReservation();
|
|
16798
|
+
if (this.startupOwners.has(taskId)) {
|
|
16799
|
+
agentLeaseTransferred = true;
|
|
16800
|
+
await this.disposeStartupOwner(taskId);
|
|
16801
|
+
}
|
|
16100
16802
|
if (!agentLeaseTransferred) {
|
|
16101
16803
|
this.pendingMessageTasks.delete(taskId);
|
|
16102
16804
|
this.revokeAgentMessageContext(taskId);
|
|
@@ -16108,6 +16810,7 @@ var TaskRunner = class {
|
|
|
16108
16810
|
}
|
|
16109
16811
|
this.inFlightBlobAborts.delete(taskId);
|
|
16110
16812
|
this.inFlightOffers.delete(taskId);
|
|
16813
|
+
this.claimedHarnesses.delete(taskId);
|
|
16111
16814
|
}
|
|
16112
16815
|
}
|
|
16113
16816
|
withAgentMessageMcp(existing, taskId, requirement) {
|
|
@@ -16377,7 +17080,13 @@ var TaskRunner = class {
|
|
|
16377
17080
|
maxPendingBytes: 4 * 1024 * 1024
|
|
16378
17081
|
});
|
|
16379
17082
|
if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
|
|
16380
|
-
const activated = await
|
|
17083
|
+
const activated = await this.activateAgentMessage(
|
|
17084
|
+
outbox,
|
|
17085
|
+
active.taskId,
|
|
17086
|
+
active.session.sessionRef,
|
|
17087
|
+
() => this.canPublishAgentMessage(active),
|
|
17088
|
+
() => active.messageSendAttempted === true
|
|
17089
|
+
);
|
|
16381
17090
|
if (this.tasks.get(active.taskId) !== active || active.beingTornDown) return;
|
|
16382
17091
|
if (activated === void 0) throw new Error("Agent message draft disappeared before activation");
|
|
16383
17092
|
this.sendAgentMessageRecord(outbox, activated);
|
|
@@ -16484,7 +17193,16 @@ var TaskRunner = class {
|
|
|
16484
17193
|
}
|
|
16485
17194
|
let bytes;
|
|
16486
17195
|
try {
|
|
16487
|
-
|
|
17196
|
+
const limits = this.deps.artifactLimits ?? DEFAULT_ARTIFACT_LIMITS;
|
|
17197
|
+
const remaining = limits.maxTaskBytes - (active.artifactBytesSoFar ?? 0);
|
|
17198
|
+
bytes = await readArtifactBytes(
|
|
17199
|
+
opened.handle,
|
|
17200
|
+
Math.min(limits.maxFileBytes, remaining),
|
|
17201
|
+
active.blobAbort.signal,
|
|
17202
|
+
(count) => {
|
|
17203
|
+
active.artifactBytesSoFar = (active.artifactBytesSoFar ?? 0) + count;
|
|
17204
|
+
}
|
|
17205
|
+
);
|
|
16488
17206
|
} catch (err) {
|
|
16489
17207
|
this.reportArtifactError(active, name, `failed to read artifact "${name}": ${errorMessage5(err)}`);
|
|
16490
17208
|
return;
|
|
@@ -16492,6 +17210,7 @@ var TaskRunner = class {
|
|
|
16492
17210
|
await opened.handle.close().catch(() => {
|
|
16493
17211
|
});
|
|
16494
17212
|
}
|
|
17213
|
+
if (this.tasks.get(active.taskId) !== active || active.blobAbort.signal.aborted || active.beingTornDown) return;
|
|
16495
17214
|
if (bytes.length <= MAX_INLINE_ARTIFACT_BYTES) {
|
|
16496
17215
|
const inline = bytes.toString("base64");
|
|
16497
17216
|
if (new TextEncoder().encode(inline).length <= MAX_INLINE_ARTIFACT_BYTES) {
|
|
@@ -16503,6 +17222,7 @@ var TaskRunner = class {
|
|
|
16503
17222
|
const blobRef = await this.deps.blobClient.uploadArtifact(bytes, contentType, {
|
|
16504
17223
|
signal: active.blobAbort.signal
|
|
16505
17224
|
});
|
|
17225
|
+
if (this.tasks.get(active.taskId) !== active || active.blobAbort.signal.aborted || active.beingTornDown) return;
|
|
16506
17226
|
this.deps.send(createEnvelope("task.artifact", { name, contentType, blobRef }, { taskId: active.taskId }));
|
|
16507
17227
|
} catch (err) {
|
|
16508
17228
|
this.reportArtifactError(active, name, `failed to upload artifact "${name}": ${errorMessage5(err)}`);
|
|
@@ -16510,12 +17230,17 @@ var TaskRunner = class {
|
|
|
16510
17230
|
}
|
|
16511
17231
|
/** Loud, non-silent artifact failure (finding F7): logged, and folded into this task's own progress stream as an `error` AgentEvent rather than swallowed — the task itself can still complete normally, but the omission is now visible. */
|
|
16512
17232
|
reportArtifactError(active, name, reason) {
|
|
17233
|
+
if (active.blobAbort.signal.aborted || active.beingTornDown) return;
|
|
16513
17234
|
console.error(`[byok/client] artifact "${name}" for task ${active.taskId} dropped: ${reason}`);
|
|
16514
17235
|
active.batcher.push({ type: "error", message: reason });
|
|
16515
17236
|
}
|
|
16516
17237
|
async handleCancel(taskId, reason) {
|
|
16517
17238
|
const active = this.tasks.get(taskId);
|
|
16518
17239
|
if (!active) {
|
|
17240
|
+
if (this.startupOwners.has(taskId) && !this.inFlightOffers.has(taskId)) {
|
|
17241
|
+
await this.disposeStartupOwner(taskId);
|
|
17242
|
+
return;
|
|
17243
|
+
}
|
|
16519
17244
|
this.setPendingCancelled(taskId, reason);
|
|
16520
17245
|
this.inFlightBlobAborts.get(taskId)?.abort();
|
|
16521
17246
|
return;
|
|
@@ -16529,10 +17254,7 @@ var TaskRunner = class {
|
|
|
16529
17254
|
return;
|
|
16530
17255
|
}
|
|
16531
17256
|
active.blobAbort.abort();
|
|
16532
|
-
|
|
16533
|
-
await active.session.interrupt();
|
|
16534
|
-
} catch {
|
|
16535
|
-
}
|
|
17257
|
+
await this.interruptBounded(active.session);
|
|
16536
17258
|
await this.observeGit(active, "salvage");
|
|
16537
17259
|
await this.persistAgentTerminalEvidence(active, "cancelled", reason);
|
|
16538
17260
|
this.deps.send(
|
|
@@ -16974,10 +17696,7 @@ var TaskRunner = class {
|
|
|
16974
17696
|
await active.semanticTerminalSettled;
|
|
16975
17697
|
return;
|
|
16976
17698
|
}
|
|
16977
|
-
|
|
16978
|
-
await active.session.interrupt();
|
|
16979
|
-
} catch {
|
|
16980
|
-
}
|
|
17699
|
+
await this.interruptBounded(active.session);
|
|
16981
17700
|
await this.observeGit(active, "salvage");
|
|
16982
17701
|
await this.persistAgentTerminalEvidence(active, "failed", reason ?? "rejected");
|
|
16983
17702
|
this.deps.send(
|
|
@@ -17012,7 +17731,7 @@ var TaskRunner = class {
|
|
|
17012
17731
|
this.deps.send(
|
|
17013
17732
|
createEnvelope(
|
|
17014
17733
|
"task.fail",
|
|
17015
|
-
active === void 0 ? { reason, retryable } : { reason, retryable, ...this.terminalInferenceUsagePayload(active), ...this.agentTerminalPayload(active) },
|
|
17734
|
+
active === void 0 ? { reason, retryable, ...terminalIdentity(this.claimedHarnesses.get(taskId)) } : { reason, retryable, ...this.terminalInferenceUsagePayload(active), ...this.agentTerminalPayload(active) },
|
|
17016
17735
|
{ taskId }
|
|
17017
17736
|
)
|
|
17018
17737
|
);
|
|
@@ -17026,6 +17745,7 @@ var TaskRunner = class {
|
|
|
17026
17745
|
* the wire and handleOffer's finally block releases the lease.
|
|
17027
17746
|
*/
|
|
17028
17747
|
async failClaimedAgent(taskId, reason, retryable, context) {
|
|
17748
|
+
await this.pendingMessageTasks.get(taskId)?.outbox.revoke(taskId);
|
|
17029
17749
|
const agentRef = context.binding.resolution.agentRef;
|
|
17030
17750
|
const result = await this.retryAgentTerminalEvidence(
|
|
17031
17751
|
() => this.deps.agentHome.mutateExecution(context.binding, () => this.deps.agentSessionHandoffs.recordTaskTerminal({
|
|
@@ -17051,7 +17771,7 @@ var TaskRunner = class {
|
|
|
17051
17771
|
}
|
|
17052
17772
|
this.deps.send(createEnvelope(
|
|
17053
17773
|
"task.fail",
|
|
17054
|
-
{ reason, retryable, agentRef },
|
|
17774
|
+
{ reason, retryable, ...terminalIdentity(context.runtimeId, agentRef) },
|
|
17055
17775
|
{ taskId }
|
|
17056
17776
|
));
|
|
17057
17777
|
return result.ok;
|
|
@@ -17069,7 +17789,8 @@ var TaskRunner = class {
|
|
|
17069
17789
|
terminalInferenceUsagePayload(active) {
|
|
17070
17790
|
const release = this.deps.localAgentRelease;
|
|
17071
17791
|
const runtimeId = active.adapter.descriptor.id;
|
|
17072
|
-
if (
|
|
17792
|
+
if (!isKnownRuntimeId(runtimeId)) return {};
|
|
17793
|
+
if (release === void 0 || active.lastUsage === void 0) return {};
|
|
17073
17794
|
const nowMs = Date.now();
|
|
17074
17795
|
const durationMs = terminalUsageNumber(nowMs - active.startedAtMs, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS);
|
|
17075
17796
|
const promptTokens = terminalUsageNumber(active.lastUsage?.inputTokens, TERMINAL_INFERENCE_USAGE_MAX_TOKENS);
|
|
@@ -17087,7 +17808,7 @@ var TaskRunner = class {
|
|
|
17087
17808
|
}
|
|
17088
17809
|
/** Exact Agent identity projection for claim/terminal wire payloads. */
|
|
17089
17810
|
agentTerminalPayload(active) {
|
|
17090
|
-
return active.
|
|
17811
|
+
return terminalIdentity(active.adapter.descriptor.id, active.agentRef);
|
|
17091
17812
|
}
|
|
17092
17813
|
/**
|
|
17093
17814
|
* additive-minor (`task.complete.document`): the whole daemon-side gate
|
|
@@ -17244,6 +17965,14 @@ var TaskRunner = class {
|
|
|
17244
17965
|
}
|
|
17245
17966
|
/** Persist Agent terminal truth before wire when local storage is available. */
|
|
17246
17967
|
async persistAgentTerminalEvidence(active, cause, reason) {
|
|
17968
|
+
if (cause === "cancelled" || cause === "failed" && !active.messageSendAttempted) {
|
|
17969
|
+
try {
|
|
17970
|
+
await active.messageOutbox?.revoke(active.taskId);
|
|
17971
|
+
} catch (error) {
|
|
17972
|
+
active.resolveSemanticTerminalSettled?.(false);
|
|
17973
|
+
throw error;
|
|
17974
|
+
}
|
|
17975
|
+
}
|
|
17247
17976
|
active.terminalCause = cause;
|
|
17248
17977
|
active.terminalReason = reason;
|
|
17249
17978
|
if (active.agentBinding === void 0 || active.agentRef === void 0 || active.agentHandoff === void 0) {
|
|
@@ -17306,7 +18035,19 @@ var TaskRunner = class {
|
|
|
17306
18035
|
error
|
|
17307
18036
|
});
|
|
17308
18037
|
}
|
|
17309
|
-
async
|
|
18038
|
+
async retryTerminalFinalization(taskId) {
|
|
18039
|
+
const active = this.tasks.get(taskId);
|
|
18040
|
+
if (active?.finalizationStarted) await this.finish(taskId);
|
|
18041
|
+
}
|
|
18042
|
+
finalizationAttempts = /* @__PURE__ */ new Map();
|
|
18043
|
+
finish(taskId) {
|
|
18044
|
+
const existing = this.finalizationAttempts.get(taskId);
|
|
18045
|
+
if (existing) return existing;
|
|
18046
|
+
const attempt = this.finishOnce(taskId).finally(() => this.finalizationAttempts.delete(taskId));
|
|
18047
|
+
this.finalizationAttempts.set(taskId, attempt);
|
|
18048
|
+
return attempt;
|
|
18049
|
+
}
|
|
18050
|
+
async finishOnce(taskId) {
|
|
17310
18051
|
const active = this.tasks.get(taskId);
|
|
17311
18052
|
if (!active) return true;
|
|
17312
18053
|
if (!active.finalizationStarted) {
|
|
@@ -17357,6 +18098,13 @@ var TaskRunner = class {
|
|
|
17357
18098
|
return false;
|
|
17358
18099
|
}
|
|
17359
18100
|
if (this.tasks.get(taskId) !== active) return true;
|
|
18101
|
+
try {
|
|
18102
|
+
await this.deps.awaitTerminalCommit?.(taskId);
|
|
18103
|
+
} catch {
|
|
18104
|
+
active.resolveSemanticTerminalSettled?.(false);
|
|
18105
|
+
return false;
|
|
18106
|
+
}
|
|
18107
|
+
if (this.tasks.get(taskId) !== active) return true;
|
|
17360
18108
|
if (active.agentBinding !== void 0 && active.agentRef !== void 0 && active.agentHandoff !== void 0) {
|
|
17361
18109
|
const agentRef = active.agentRef;
|
|
17362
18110
|
const handoff = active.agentHandoff;
|
|
@@ -17468,7 +18216,7 @@ var TaskRunner = class {
|
|
|
17468
18216
|
* is device-specific (which runtimes happen to be installed here), so a
|
|
17469
18217
|
* different device's installed runtime set might satisfy it.
|
|
17470
18218
|
*/
|
|
17471
|
-
async pickAdapter(requestedRuntime, policyMode, requiresMcpToolsets) {
|
|
18219
|
+
async pickAdapter(requestedRuntime, policyMode, requiresMcpToolsets, signal) {
|
|
17472
18220
|
const allowlist = this.deps.runtimeAllowlist;
|
|
17473
18221
|
if (requestedRuntime) {
|
|
17474
18222
|
if (allowlist && !allowlist.includes(requestedRuntime)) {
|
|
@@ -17497,8 +18245,8 @@ var TaskRunner = class {
|
|
|
17497
18245
|
retryable: false
|
|
17498
18246
|
};
|
|
17499
18247
|
}
|
|
17500
|
-
const detected = await adapter.detect();
|
|
17501
|
-
if (
|
|
18248
|
+
const detected = validateRuntimeDetectResult(await awaitAdmission(() => adapter.detect(signal), signal));
|
|
18249
|
+
if (detected.kind !== "available") {
|
|
17502
18250
|
return {
|
|
17503
18251
|
ok: false,
|
|
17504
18252
|
reason: `runtime "${requestedRuntime}" is not installed/available on this device`,
|
|
@@ -17513,8 +18261,8 @@ var TaskRunner = class {
|
|
|
17513
18261
|
const descriptor = freezeRuntimeAdapterDescriptor(adapter.descriptor);
|
|
17514
18262
|
if (!adapterSupportsMode(descriptor, policyMode)) continue;
|
|
17515
18263
|
if (requiresMcpToolsets && !adapterSupportsMcpToolsets(descriptor)) continue;
|
|
17516
|
-
const detected = await adapter.detect();
|
|
17517
|
-
if (detected.
|
|
18264
|
+
const detected = validateRuntimeDetectResult(await awaitAdmission(() => adapter.detect(signal), signal));
|
|
18265
|
+
if (detected.kind === "available") return { ok: true, adapter, descriptor };
|
|
17518
18266
|
}
|
|
17519
18267
|
return {
|
|
17520
18268
|
ok: false,
|
|
@@ -17678,10 +18426,12 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
17678
18426
|
constructor(homeDir, spoolPath) {
|
|
17679
18427
|
this.homeDir = homeDir;
|
|
17680
18428
|
this.spoolPath = spoolPath;
|
|
18429
|
+
this.file = new DurableJsonlFile(spoolPath);
|
|
17681
18430
|
}
|
|
17682
18431
|
homeDir;
|
|
17683
18432
|
spoolPath;
|
|
17684
18433
|
pending = /* @__PURE__ */ new Map();
|
|
18434
|
+
file;
|
|
17685
18435
|
nextCursor = 1;
|
|
17686
18436
|
logEntries = 0;
|
|
17687
18437
|
writeTail = Promise.resolve();
|
|
@@ -17690,6 +18440,7 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
17690
18440
|
await ensureSecureDir(directory);
|
|
17691
18441
|
const spool = new _AgentReliableSpool(homeDir, path14__default.join(directory, AGENT_RELIABLE_SPOOL_FILENAME));
|
|
17692
18442
|
await spool.load();
|
|
18443
|
+
await spool.file.confirmRecovered();
|
|
17693
18444
|
return spool;
|
|
17694
18445
|
}
|
|
17695
18446
|
get pendingEvents() {
|
|
@@ -17709,6 +18460,25 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
17709
18460
|
const payloadJson = stableRecordJson(input.payload);
|
|
17710
18461
|
const byteCount = Buffer.byteLength(payloadJson, "utf8");
|
|
17711
18462
|
if (byteCount <= 0) throw new AgentReliableSpoolError("reliable payload must have positive serialized bytes");
|
|
18463
|
+
const eventId = input.eventId ?? randomUUID();
|
|
18464
|
+
const existing = this.pending.get(eventId);
|
|
18465
|
+
if (existing !== void 0) {
|
|
18466
|
+
if (!sameRecord(existing, {
|
|
18467
|
+
wireType: "agent.egress.reliable",
|
|
18468
|
+
agentRef: input.agentRef,
|
|
18469
|
+
tenantId: input.tenantId,
|
|
18470
|
+
policyRevision: input.policyRevision,
|
|
18471
|
+
eventId,
|
|
18472
|
+
cursor: existing.cursor,
|
|
18473
|
+
payloadHash: contentHash(payloadJson),
|
|
18474
|
+
byteCount,
|
|
18475
|
+
sessionRef: input.sessionRef,
|
|
18476
|
+
taskId: input.taskId
|
|
18477
|
+
}) || stableRecordJson(existing.payload) !== payloadJson || input.createdAt !== void 0 && input.createdAt !== existing.createdAt) {
|
|
18478
|
+
throw new AgentReliableSpoolError(`reliable eventId ${eventId} differs from its existing record`);
|
|
18479
|
+
}
|
|
18480
|
+
return existing;
|
|
18481
|
+
}
|
|
17712
18482
|
if (this.pending.size >= policy.reliable.maxPendingEventsPerAgent) {
|
|
17713
18483
|
throw new AgentReliableQuotaError("quota_exceeded", "reliable per-Agent event quota is exhausted");
|
|
17714
18484
|
}
|
|
@@ -17718,8 +18488,6 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
17718
18488
|
if (tenantPendingBytes + byteCount > policy.reliable.maxPendingBytesPerTenant) {
|
|
17719
18489
|
throw new AgentReliableQuotaError("quota_exceeded", "reliable tenant byte quota is exhausted");
|
|
17720
18490
|
}
|
|
17721
|
-
const eventId = input.eventId ?? randomUUID();
|
|
17722
|
-
if (this.pending.has(eventId)) throw new AgentReliableSpoolError(`reliable eventId ${eventId} already exists`);
|
|
17723
18491
|
const record4 = Object.freeze({
|
|
17724
18492
|
schema: 1,
|
|
17725
18493
|
wireType: "agent.egress.reliable",
|
|
@@ -17828,6 +18596,7 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
17828
18596
|
if (error.code === "ENOENT") return;
|
|
17829
18597
|
throw error;
|
|
17830
18598
|
}
|
|
18599
|
+
if (raw.length > 0 && !raw.endsWith("\n")) throw new AgentReliableSpoolError("durable JSONL log has an incomplete trailing frame; preserve the file for explicit repair");
|
|
17831
18600
|
for (const line of raw.split("\n")) {
|
|
17832
18601
|
if (line.length === 0) continue;
|
|
17833
18602
|
let entry;
|
|
@@ -17850,19 +18619,12 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
17850
18619
|
}
|
|
17851
18620
|
}
|
|
17852
18621
|
async appendEntry(entry) {
|
|
17853
|
-
|
|
17854
|
-
|
|
17855
|
-
await handle.write(`${stableRecordJson(entry)}
|
|
17856
|
-
`, void 0, "utf8");
|
|
17857
|
-
await handle.sync();
|
|
17858
|
-
this.logEntries += 1;
|
|
17859
|
-
} finally {
|
|
17860
|
-
await handle.close();
|
|
17861
|
-
}
|
|
18622
|
+
await this.file.append(stableRecordJson(entry));
|
|
18623
|
+
this.logEntries += 1;
|
|
17862
18624
|
}
|
|
17863
18625
|
async compact() {
|
|
17864
18626
|
const entries = this.records().map((record4) => ({ schema: 1, kind: "append", record: record4 }));
|
|
17865
|
-
await
|
|
18627
|
+
await this.file.replace(entries.map((entry) => stableRecordJson(entry)));
|
|
17866
18628
|
this.logEntries = entries.length;
|
|
17867
18629
|
}
|
|
17868
18630
|
async exclusive(fn) {
|
|
@@ -17873,6 +18635,7 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
17873
18635
|
});
|
|
17874
18636
|
await prior;
|
|
17875
18637
|
try {
|
|
18638
|
+
this.file.assertWritable();
|
|
17876
18639
|
return await fn();
|
|
17877
18640
|
} finally {
|
|
17878
18641
|
release();
|
|
@@ -19247,17 +20010,26 @@ function isRuntimeId(id) {
|
|
|
19247
20010
|
return id === "pi" || id === "claude" || id === "codex";
|
|
19248
20011
|
}
|
|
19249
20012
|
async function detectRuntimes(adapters) {
|
|
19250
|
-
const detections = await Promise.all(adapters.map(async (adapter) => ({ adapter, detected: await adapter.detect() })));
|
|
20013
|
+
const detections = await Promise.all(adapters.map(async (adapter) => ({ adapter, detected: validateRuntimeDetectResult(await adapter.detect()) })));
|
|
19251
20014
|
const runtimes = [];
|
|
20015
|
+
const harnesses = [];
|
|
19252
20016
|
for (const { adapter, detected } of detections) {
|
|
19253
|
-
if (
|
|
20017
|
+
if (detected.kind !== "available") continue;
|
|
20018
|
+
if (!isRuntimeId(adapter.descriptor.id)) {
|
|
20019
|
+
harnesses.push({
|
|
20020
|
+
id: HarnessIdSchema.parse(adapter.descriptor.id),
|
|
20021
|
+
...detected.version === void 0 ? {} : { version: detected.version },
|
|
20022
|
+
capabilities: toRuntimeInfoCapabilities(adapter.descriptor.capabilities)
|
|
20023
|
+
});
|
|
20024
|
+
continue;
|
|
20025
|
+
}
|
|
19254
20026
|
const info = { id: adapter.descriptor.id };
|
|
19255
20027
|
if (detected.version !== void 0) info.version = detected.version;
|
|
19256
20028
|
if (detected.authPresent !== void 0) info.authPresent = detected.authPresent;
|
|
19257
20029
|
info.capabilities = toRuntimeInfoCapabilities(adapter.descriptor.capabilities);
|
|
19258
20030
|
runtimes.push(info);
|
|
19259
20031
|
}
|
|
19260
|
-
return runtimes;
|
|
20032
|
+
return { runtimes, harnesses };
|
|
19261
20033
|
}
|
|
19262
20034
|
function computeCapabilities(adapters, agentHomeConfigured = false, strictAgentOnly = false, agentHomeProjectionConfigured = false, agentEgressConfigured = false, contentReadPolicies, providerProfileBindingConfigured = false) {
|
|
19263
20035
|
const flags = [];
|
|
@@ -19347,7 +20119,7 @@ function buildAdapter(id, config) {
|
|
|
19347
20119
|
resolveApprovalMcpBin: () => resolveApprovalMcpBin(config.sdkHelperHost)
|
|
19348
20120
|
});
|
|
19349
20121
|
case "codex":
|
|
19350
|
-
return new CodexAdapter();
|
|
20122
|
+
return new CodexAdapter({ sdkHelperHost: config.sdkHelperHost });
|
|
19351
20123
|
}
|
|
19352
20124
|
}
|
|
19353
20125
|
function buildDefaultAdapters(config) {
|
|
@@ -19424,6 +20196,12 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19424
20196
|
const localAgentRelease = resolveLocalAgentReleaseIdentity(config.localAgentRelease);
|
|
19425
20197
|
const toolsetRegistry = new McpToolsetRegistry(config.mcpToolsets);
|
|
19426
20198
|
validatePiByokLauncherConfig(config.piByokLauncher);
|
|
20199
|
+
if (config.startupTimeoutMs !== void 0 && (!Number.isSafeInteger(config.startupTimeoutMs) || config.startupTimeoutMs <= 0)) throw new Error("startupTimeoutMs must be a positive safe integer");
|
|
20200
|
+
if (config.artifactLimits !== void 0) {
|
|
20201
|
+
for (const value of [config.artifactLimits.maxFileBytes, config.artifactLimits.maxTaskBytes]) {
|
|
20202
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error("artifactLimits must contain positive safe integer byte budgets");
|
|
20203
|
+
}
|
|
20204
|
+
}
|
|
19427
20205
|
if (config.maxTaskOutputBytes !== void 0 && !(config.maxTaskOutputBytes > 0)) {
|
|
19428
20206
|
throw new Error(
|
|
19429
20207
|
`DaemonConfig.maxTaskOutputBytes must be a positive number (or omitted to use the ${DEFAULT_MAX_TASK_OUTPUT_BYTES}-byte default) \u2014 got ${config.maxTaskOutputBytes}. Pass Number.POSITIVE_INFINITY to explicitly disable the cap; 0 or a negative number is rejected rather than silently treated as "disabled".`
|
|
@@ -19553,7 +20331,12 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19553
20331
|
pressureEngine = ownedPressureEngine;
|
|
19554
20332
|
}
|
|
19555
20333
|
}
|
|
19556
|
-
|
|
20334
|
+
const terminalCommits = new TerminalCommitQueue((taskId) => {
|
|
20335
|
+
connection?.retryTaskReceipts(taskId);
|
|
20336
|
+
void runner?.retryTerminalFinalization(taskId).catch((error) => {
|
|
20337
|
+
console.error("[byok/client] terminal finalization still requires recovery", error);
|
|
20338
|
+
});
|
|
20339
|
+
});
|
|
19557
20340
|
const observer = new DaemonObserver();
|
|
19558
20341
|
const approvalRegistry = new ApprovalRegistry();
|
|
19559
20342
|
let connection;
|
|
@@ -19688,6 +20471,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19688
20471
|
await runLifecycleMutation(startUnderLease);
|
|
19689
20472
|
}
|
|
19690
20473
|
async function startUnderLease() {
|
|
20474
|
+
terminalCommits.resume();
|
|
19691
20475
|
if (!daemonOwnerLease) daemonOwnerLease = await acquireDaemonOwner(storeDir, "daemon");
|
|
19692
20476
|
try {
|
|
19693
20477
|
let record4;
|
|
@@ -19739,7 +20523,8 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19739
20523
|
const terminal = createEnvelope("task.fail", {
|
|
19740
20524
|
reason: "daemon_interrupted",
|
|
19741
20525
|
retryable: false,
|
|
19742
|
-
...
|
|
20526
|
+
...terminalIdentity(task.claimedRuntime, payload.agentRef),
|
|
20527
|
+
recovery: { kind: "daemon_interrupted", offerId: offer.id }
|
|
19743
20528
|
}, { taskId: task.taskId });
|
|
19744
20529
|
const bytes = encodeEnvelope(terminal);
|
|
19745
20530
|
await activeJournal.recordTerminal({
|
|
@@ -19767,7 +20552,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19767
20552
|
await gitWorkspaceStore?.reconcile();
|
|
19768
20553
|
}
|
|
19769
20554
|
blobLifecycleAbort = new AbortController();
|
|
19770
|
-
const [runtimes, blobClient] = await Promise.all([
|
|
20555
|
+
const [{ runtimes, harnesses }, blobClient] = await Promise.all([
|
|
19771
20556
|
detectRuntimes(adapters),
|
|
19772
20557
|
Promise.resolve(new BlobClient(config.serverUrl, auth, { signal: blobLifecycleAbort.signal }))
|
|
19773
20558
|
]);
|
|
@@ -19788,6 +20573,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19788
20573
|
tenantId: record4.tenantId,
|
|
19789
20574
|
deviceId: record4.deviceId
|
|
19790
20575
|
}) : void 0;
|
|
20576
|
+
capabilities.push("custom-harness");
|
|
19791
20577
|
const sendSanitizedEnvelope = (envelope) => {
|
|
19792
20578
|
const terminalKind = terminalKindOf(envelope.type);
|
|
19793
20579
|
if (!activeJournal || !journalIdentity || terminalKind === void 0 || envelope.task_id === void 0) {
|
|
@@ -19797,7 +20583,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19797
20583
|
}
|
|
19798
20584
|
const taskId = envelope.task_id;
|
|
19799
20585
|
const bytes = encodeEnvelope(envelope);
|
|
19800
|
-
|
|
20586
|
+
terminalCommits.enqueue(taskId, async () => {
|
|
19801
20587
|
try {
|
|
19802
20588
|
await activeJournal.recordTerminal({
|
|
19803
20589
|
taskId,
|
|
@@ -19825,7 +20611,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19825
20611
|
const failure = createEnvelope("task.fail", {
|
|
19826
20612
|
reason: "terminal_result_too_large",
|
|
19827
20613
|
retryable: false,
|
|
19828
|
-
...
|
|
20614
|
+
...terminalIdentity(task.claimedRuntime, offerPayload.agentRef)
|
|
19829
20615
|
}, { taskId });
|
|
19830
20616
|
const failureBytes = encodeEnvelope(failure);
|
|
19831
20617
|
await activeJournal.recordTerminal({
|
|
@@ -19843,8 +20629,6 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19843
20629
|
observer.handleOutboundEnvelope(decoded);
|
|
19844
20630
|
connection?.send(decoded);
|
|
19845
20631
|
}
|
|
19846
|
-
}).catch((error) => {
|
|
19847
|
-
console.error("[byok/client] terminal durability failed; execution remains unsettled", error);
|
|
19848
20632
|
});
|
|
19849
20633
|
};
|
|
19850
20634
|
const sendEnvelope = (candidate) => {
|
|
@@ -19926,7 +20710,10 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19926
20710
|
// Finding F5(a): see TaskRunnerDeps.shutdownInterruptTimeoutMs's own doc comment.
|
|
19927
20711
|
shutdownInterruptTimeoutMs: overrides.shutdown?.taskInterruptTimeoutMs,
|
|
19928
20712
|
// M5 batch-3 (workstream 2): see DaemonConfig.maxTaskOutputBytes's own doc comment — already validated above.
|
|
20713
|
+
awaitTerminalCommit: (taskId) => terminalCommits.receipt(taskId),
|
|
19929
20714
|
maxTaskOutputBytes: config.maxTaskOutputBytes,
|
|
20715
|
+
artifactLimits: config.artifactLimits,
|
|
20716
|
+
startupTimeoutMs: config.startupTimeoutMs,
|
|
19930
20717
|
// See DaemonConfig.maxInlineEventBytes's own doc comment — already validated above.
|
|
19931
20718
|
maxInlineEventBytes: config.maxInlineEventBytes,
|
|
19932
20719
|
// additive-minor (`task.complete.document`): passed through verbatim,
|
|
@@ -19974,6 +20761,20 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
19974
20761
|
if (config.agentHome !== void 0 && config.agentEgress !== void 0) {
|
|
19975
20762
|
await runner.recoverAgentMessageOutboxes(path14__default.join(config.agentHome.hostStorageRoot, "agents"));
|
|
19976
20763
|
}
|
|
20764
|
+
const replayRecoveryTerminals = async (taskId) => {
|
|
20765
|
+
if (!activeJournal || !journalIdentity) return;
|
|
20766
|
+
for (const terminal of await activeJournal.listPendingTerminals(journalIdentity)) {
|
|
20767
|
+
if (terminal.truthState !== "pending" || taskId !== void 0 && terminal.taskId !== taskId) continue;
|
|
20768
|
+
if (runner?.hasPendingRecoveredAgentMessage(terminal.taskId)) continue;
|
|
20769
|
+
const envelope = decodeEnvelope(terminal.bytes);
|
|
20770
|
+
if (envelope.task_id !== terminal.taskId || terminalKindOf(envelope.type) !== terminal.terminalType) {
|
|
20771
|
+
throw new Error("durable terminal execution binding mismatch");
|
|
20772
|
+
}
|
|
20773
|
+
overrides.executionRecoveryFault?.("terminal:before-send");
|
|
20774
|
+
observer.handleOutboundEnvelope(envelope);
|
|
20775
|
+
connection?.send(envelope);
|
|
20776
|
+
}
|
|
20777
|
+
};
|
|
19977
20778
|
const handleAgentHomeProjectionEnvelope = async (envelope) => {
|
|
19978
20779
|
if (envelope.type !== "agent.home.projection") return false;
|
|
19979
20780
|
if (agentHomeManager === void 0 || agentHomeProjectionCompletion === void 0) {
|
|
@@ -20108,6 +20909,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
20108
20909
|
capabilities,
|
|
20109
20910
|
clientVersion: localAgentRelease.version,
|
|
20110
20911
|
runtimes,
|
|
20912
|
+
harnesses,
|
|
20111
20913
|
getConfiguredToolsets: () => toolsetRegistry.snapshot().configuredToolsets,
|
|
20112
20914
|
auth,
|
|
20113
20915
|
cursorStore,
|
|
@@ -20149,12 +20951,22 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
20149
20951
|
activePressureEngine?.assertAckCriticalAllowed();
|
|
20150
20952
|
await activeJournal.appendEnvelope(toJournalEnvelopeRecord(envelope, journalIdentity));
|
|
20151
20953
|
if (isTaskOfferType(envelope.type) && envelope.task_id !== void 0) {
|
|
20954
|
+
if (terminalCommits.hasPending(envelope.task_id)) {
|
|
20955
|
+
await terminalCommits.retry(envelope.task_id);
|
|
20956
|
+
return;
|
|
20957
|
+
}
|
|
20152
20958
|
const task = await activeJournal.readTask(envelope.task_id, journalIdentity);
|
|
20153
20959
|
if (!task) throw new Error("durable offer has no execution record");
|
|
20154
|
-
if (task.localState !== "received")
|
|
20960
|
+
if (task.localState !== "received") {
|
|
20961
|
+
await terminalCommits.retry(envelope.task_id);
|
|
20962
|
+
return;
|
|
20963
|
+
}
|
|
20155
20964
|
}
|
|
20156
20965
|
await runner?.handleEnvelope(envelope);
|
|
20157
|
-
|
|
20966
|
+
if (envelope.type === "agent.message.disposition") {
|
|
20967
|
+
await replayRecoveryTerminals(envelope.task_id);
|
|
20968
|
+
}
|
|
20969
|
+
if (envelope.task_id !== void 0) await terminalCommits.receipt(envelope.task_id);
|
|
20158
20970
|
} : (envelope) => {
|
|
20159
20971
|
if (tenantRebinding) {
|
|
20160
20972
|
return Promise.reject(new Error("tenant enrollment is being re-paired; inbound work is blocked until restart"));
|
|
@@ -20202,18 +21014,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
20202
21014
|
});
|
|
20203
21015
|
}
|
|
20204
21016
|
});
|
|
20205
|
-
|
|
20206
|
-
for (const terminal of await activeJournal.listPendingTerminals(journalIdentity)) {
|
|
20207
|
-
if (terminal.truthState !== "pending") continue;
|
|
20208
|
-
const envelope = decodeEnvelope(terminal.bytes);
|
|
20209
|
-
if (envelope.task_id !== terminal.taskId || terminalKindOf(envelope.type) !== terminal.terminalType) {
|
|
20210
|
-
throw new Error("durable terminal execution binding mismatch");
|
|
20211
|
-
}
|
|
20212
|
-
overrides.executionRecoveryFault?.("terminal:before-send");
|
|
20213
|
-
observer.handleOutboundEnvelope(envelope);
|
|
20214
|
-
connection.send(envelope);
|
|
20215
|
-
}
|
|
20216
|
-
}
|
|
21017
|
+
await replayRecoveryTerminals();
|
|
20217
21018
|
await connection.start();
|
|
20218
21019
|
await connection.waitForConnection();
|
|
20219
21020
|
runner.retryRecoveredAgentMessages();
|
|
@@ -20328,7 +21129,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
20328
21129
|
pendingLateMutationBarriers.add(observedActiveTeardown);
|
|
20329
21130
|
void observedActiveTeardown.finally(() => pendingLateMutationBarriers.delete(observedActiveTeardown)).catch(() => void 0);
|
|
20330
21131
|
}
|
|
20331
|
-
const journalTailSettled = journal ? await attempt(() =>
|
|
21132
|
+
const journalTailSettled = journal ? await attempt(() => terminalCommits.stop(), true) : true;
|
|
20332
21133
|
const maintenanceSettled = await attempt(() => maintenanceStopped, true);
|
|
20333
21134
|
const drainTimeoutMs = opts.drainTimeoutMs ?? overrides.shutdown?.outboxDrainTimeoutMs ?? DEFAULT_SHUTDOWN_OUTBOX_DRAIN_TIMEOUT_MS;
|
|
20334
21135
|
const connectionStopped = await attempt(() => connection?.stop(drainTimeoutMs), true);
|
|
@@ -20848,6 +21649,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
20848
21649
|
revoked: connection?.isRevoked() ?? auth.isRevoked(),
|
|
20849
21650
|
deviceId: auth.deviceId,
|
|
20850
21651
|
activeTaskCount: runner?.activeTaskCount ?? 0,
|
|
21652
|
+
pendingTerminalCommits: terminalCommits.pendingCount,
|
|
20851
21653
|
branding: config.branding,
|
|
20852
21654
|
operationalHealth: operationalHealth.snapshot(),
|
|
20853
21655
|
toolsets: toolsetRegistry.status(),
|
|
@@ -20980,7 +21782,7 @@ ${args.map((a) => ` ${plistString(a)}`).join("\n")}
|
|
|
20980
21782
|
}
|
|
20981
21783
|
function createLaunchdLifecycle(def, deps = {}) {
|
|
20982
21784
|
const run = deps.run ?? defaultRunner;
|
|
20983
|
-
const
|
|
21785
|
+
const fs37 = deps.fs ?? promises;
|
|
20984
21786
|
const homedir = deps.homedir ?? (() => os__default.homedir());
|
|
20985
21787
|
const getuid = deps.getuid ?? (() => {
|
|
20986
21788
|
if (typeof process.getuid !== "function") {
|
|
@@ -20994,7 +21796,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
20994
21796
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
20995
21797
|
async function fileExists(p) {
|
|
20996
21798
|
try {
|
|
20997
|
-
await
|
|
21799
|
+
await fs37.stat(p);
|
|
20998
21800
|
return true;
|
|
20999
21801
|
} catch {
|
|
21000
21802
|
return false;
|
|
@@ -21002,9 +21804,9 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
21002
21804
|
}
|
|
21003
21805
|
async function writePlist(program) {
|
|
21004
21806
|
const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
|
|
21005
|
-
await
|
|
21006
|
-
await
|
|
21007
|
-
await
|
|
21807
|
+
await fs37.mkdir(path14__default.dirname(plistPath()), { recursive: true });
|
|
21808
|
+
await fs37.mkdir(def.logDir, { recursive: true });
|
|
21809
|
+
await fs37.writeFile(plistPath(), xml, "utf8");
|
|
21008
21810
|
}
|
|
21009
21811
|
async function install(opts = {}) {
|
|
21010
21812
|
await writePlist(opts.program ?? def.program);
|
|
@@ -21015,7 +21817,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
21015
21817
|
}
|
|
21016
21818
|
async function uninstall() {
|
|
21017
21819
|
await runIdempotent(run, "launchctl", ["bootout", serviceTarget()], "launchctl bootout", LAUNCHD_NOT_LOADED);
|
|
21018
|
-
await
|
|
21820
|
+
await fs37.rm(plistPath(), { force: true });
|
|
21019
21821
|
}
|
|
21020
21822
|
async function start() {
|
|
21021
21823
|
if (!await fileExists(plistPath())) {
|
|
@@ -21099,14 +21901,14 @@ WantedBy=default.target
|
|
|
21099
21901
|
}
|
|
21100
21902
|
function createSystemdLifecycle(def, deps = {}) {
|
|
21101
21903
|
const run = deps.run ?? defaultRunner;
|
|
21102
|
-
const
|
|
21904
|
+
const fs37 = deps.fs ?? promises;
|
|
21103
21905
|
const homedir = deps.homedir ?? (() => os__default.homedir());
|
|
21104
21906
|
const name = sanitizeServiceName(def.name);
|
|
21105
21907
|
const unitName = `${name}.service`;
|
|
21106
21908
|
const unitPath = () => path14__default.join(homedir(), ".config", "systemd", "user", unitName);
|
|
21107
21909
|
async function fileExists(p) {
|
|
21108
21910
|
try {
|
|
21109
|
-
await
|
|
21911
|
+
await fs37.stat(p);
|
|
21110
21912
|
return true;
|
|
21111
21913
|
} catch {
|
|
21112
21914
|
return false;
|
|
@@ -21114,9 +21916,9 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
21114
21916
|
}
|
|
21115
21917
|
async function writeUnit(program) {
|
|
21116
21918
|
const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
21117
|
-
await
|
|
21118
|
-
await
|
|
21119
|
-
await
|
|
21919
|
+
await fs37.mkdir(path14__default.dirname(unitPath()), { recursive: true });
|
|
21920
|
+
await fs37.mkdir(def.logDir, { recursive: true });
|
|
21921
|
+
await fs37.writeFile(unitPath(), unit, "utf8");
|
|
21120
21922
|
}
|
|
21121
21923
|
async function install(opts = {}) {
|
|
21122
21924
|
await writeUnit(opts.program ?? def.program);
|
|
@@ -21125,7 +21927,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
21125
21927
|
}
|
|
21126
21928
|
async function uninstall() {
|
|
21127
21929
|
await runIdempotent(run, "systemctl", ["--user", "disable", "--now", unitName], "systemctl disable --now", SYSTEMD_NOT_LOADED);
|
|
21128
|
-
await
|
|
21930
|
+
await fs37.rm(unitPath(), { force: true });
|
|
21129
21931
|
await run("systemctl", ["--user", "daemon-reload"]);
|
|
21130
21932
|
}
|
|
21131
21933
|
async function start() {
|
|
@@ -21188,7 +21990,7 @@ ${argXml}${cwdXml}
|
|
|
21188
21990
|
}
|
|
21189
21991
|
function createWinswLifecycle(def, deps = {}) {
|
|
21190
21992
|
const run = deps.run ?? defaultRunner;
|
|
21191
|
-
const
|
|
21993
|
+
const fs37 = deps.fs ?? promises;
|
|
21192
21994
|
const windows = def.windows;
|
|
21193
21995
|
if (!windows) {
|
|
21194
21996
|
throw new Error("WinSW service lifecycle requires `ServiceDefinition.windows.winswBin` (the product-bundled WinSW executable path)");
|
|
@@ -21200,7 +22002,7 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
21200
22002
|
const xmlPath = path14__default.join(installDir, `${id}.xml`);
|
|
21201
22003
|
async function fileExists(p) {
|
|
21202
22004
|
try {
|
|
21203
|
-
await
|
|
22005
|
+
await fs37.stat(p);
|
|
21204
22006
|
return true;
|
|
21205
22007
|
} catch {
|
|
21206
22008
|
return false;
|
|
@@ -21208,10 +22010,10 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
21208
22010
|
}
|
|
21209
22011
|
async function writeFiles(program) {
|
|
21210
22012
|
const xml = generateWinswXml({ id, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
21211
|
-
await
|
|
21212
|
-
await
|
|
21213
|
-
await
|
|
21214
|
-
await
|
|
22013
|
+
await fs37.mkdir(installDir, { recursive: true });
|
|
22014
|
+
await fs37.mkdir(def.logDir, { recursive: true });
|
|
22015
|
+
await fs37.copyFile(winswBin, exePath);
|
|
22016
|
+
await fs37.writeFile(xmlPath, xml, "utf8");
|
|
21215
22017
|
}
|
|
21216
22018
|
async function install(opts = {}) {
|
|
21217
22019
|
await writeFiles(opts.program ?? def.program);
|
|
@@ -21221,8 +22023,8 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
21221
22023
|
async function uninstall() {
|
|
21222
22024
|
await runIdempotent(run, exePath, ["stop"], "winsw stop", WINSW_NOT_INSTALLED);
|
|
21223
22025
|
await runIdempotent(run, exePath, ["uninstall"], "winsw uninstall", WINSW_NOT_INSTALLED);
|
|
21224
|
-
await
|
|
21225
|
-
await
|
|
22026
|
+
await fs37.rm(exePath, { force: true });
|
|
22027
|
+
await fs37.rm(xmlPath, { force: true });
|
|
21226
22028
|
}
|
|
21227
22029
|
async function start() {
|
|
21228
22030
|
if (!await fileExists(xmlPath)) {
|
|
@@ -21265,376 +22067,31 @@ function createServiceLifecycle(def, opts = {}) {
|
|
|
21265
22067
|
}
|
|
21266
22068
|
}
|
|
21267
22069
|
|
|
21268
|
-
// src/bin/
|
|
21269
|
-
|
|
21270
|
-
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
22070
|
+
// src/bin/runtime-probe.ts
|
|
22071
|
+
var ALL_RUNTIME_IDS2 = ["pi", "claude", "codex"];
|
|
22072
|
+
var RUNTIME_PROBE_TIMEOUT_MS = 5e3;
|
|
22073
|
+
var MAX_RUNTIME_ID_CHARS = 128;
|
|
22074
|
+
var MAX_RUNTIME_VERSION_CHARS = 256;
|
|
22075
|
+
var MAX_PERMISSION_MODES = 32;
|
|
22076
|
+
var MAX_PERMISSION_MODE_CHARS = 64;
|
|
22077
|
+
function boundedSingleLine(value, maxChars) {
|
|
22078
|
+
return value.replace(/[\r\n\t]/g, " ").slice(0, maxChars);
|
|
21274
22079
|
}
|
|
21275
|
-
var
|
|
21276
|
-
|
|
21277
|
-
|
|
21278
|
-
|
|
21279
|
-
|
|
21280
|
-
|
|
21281
|
-
|
|
21282
|
-
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
|
|
21287
|
-
|
|
21288
|
-
|
|
21289
|
-
|
|
21290
|
-
return `needs_approval: ${quote(event.summary)}`;
|
|
21291
|
-
case "turn_end":
|
|
21292
|
-
return "turn_end";
|
|
21293
|
-
case "error":
|
|
21294
|
-
return `error: ${quote(event.message)}`;
|
|
21295
|
-
case "usage": {
|
|
21296
|
-
const parts = [];
|
|
21297
|
-
if (event.inputTokens !== void 0) parts.push(`in=${event.inputTokens}`);
|
|
21298
|
-
if (event.outputTokens !== void 0) parts.push(`out=${event.outputTokens}`);
|
|
21299
|
-
if (event.totalTokens !== void 0) parts.push(`total=${event.totalTokens}`);
|
|
21300
|
-
return `usage: ${parts.length ? parts.join(" ") : "(no fields reported)"}`;
|
|
21301
|
-
}
|
|
21302
|
-
}
|
|
21303
|
-
}
|
|
21304
|
-
function formatDaemonEventLine(event, options = {}) {
|
|
21305
|
-
const prefix = `[${event.ts}]`;
|
|
21306
|
-
switch (event.kind) {
|
|
21307
|
-
case "offered":
|
|
21308
|
-
return `${prefix} offered taskId=${event.taskId}${event.runtime ? ` runtime=${event.runtime}` : ""}`;
|
|
21309
|
-
case "claimed":
|
|
21310
|
-
return `${prefix} claimed taskId=${event.taskId}${event.claimedRuntime !== void 0 ? ` claimedRuntime=${event.claimedRuntime}` : ""}`;
|
|
21311
|
-
case "started":
|
|
21312
|
-
return `${prefix} started taskId=${event.taskId}`;
|
|
21313
|
-
case "progress":
|
|
21314
|
-
return `${prefix} progress taskId=${event.taskId} ${formatAgentEvent(event.event)}`;
|
|
21315
|
-
case "artifact":
|
|
21316
|
-
return `${prefix} artifact taskId=${event.taskId} name=${event.name} contentType=${event.contentType}`;
|
|
21317
|
-
case "awaiting-approval": {
|
|
21318
|
-
const summary = options.redactApprovalSummary ? redactedByteCountPlaceholder(event.summary) : event.summary;
|
|
21319
|
-
return `${prefix} awaiting-approval taskId=${event.taskId}${event.approvalId ? ` approvalId=${event.approvalId}` : ""} summary=${quote(summary)}`;
|
|
21320
|
-
}
|
|
21321
|
-
case "completed":
|
|
21322
|
-
return `${prefix} completed taskId=${event.taskId} sessionRef=${event.sessionRef} summary=${quote(event.summary)}`;
|
|
21323
|
-
case "failed":
|
|
21324
|
-
return `${prefix} failed taskId=${event.taskId} retryable=${event.retryable}${event.preClaim ? " preClaim=true" : ""} reason=${quote(event.reason)}`;
|
|
21325
|
-
case "cancelled":
|
|
21326
|
-
return `${prefix} cancelled taskId=${event.taskId}${event.reason ? ` reason=${quote(event.reason)}` : ""}`;
|
|
21327
|
-
case "connection":
|
|
21328
|
-
return `${prefix} connection state=${event.state}`;
|
|
21329
|
-
case "paired":
|
|
21330
|
-
return `${prefix} paired deviceId=${event.deviceId}`;
|
|
21331
|
-
case "unpaired":
|
|
21332
|
-
return `${prefix} unpaired`;
|
|
21333
|
-
case "runtimes-detected":
|
|
21334
|
-
return `${prefix} runtimes-detected ids=${event.runtimes.map((r) => r.id).join(",") || "(none)"}`;
|
|
21335
|
-
case "shutdown-requested":
|
|
21336
|
-
return `${prefix} shutdown-requested reason=${quote(event.reason)}`;
|
|
21337
|
-
case "shutdown-complete":
|
|
21338
|
-
return `${prefix} shutdown-complete reason=${quote(event.reason)}${event.undeliveredOutboxCount !== void 0 ? ` undeliveredOutboxCount=${event.undeliveredOutboxCount}` : ""}`;
|
|
21339
|
-
case "stale-approval-decision":
|
|
21340
|
-
return `${prefix} stale-approval-decision taskId=${event.taskId} decision=${event.decision}${event.reason ? ` reason=${quote(event.reason)}` : ""}`;
|
|
21341
|
-
case "runtime-disposal-failed":
|
|
21342
|
-
return `${prefix} runtime-disposal-failed taskId=${event.taskId} runtime=${event.runtimeId} stage=${event.stage} reason=${quote(event.reason)}`;
|
|
21343
|
-
case "git-workspace": {
|
|
21344
|
-
const parts = [
|
|
21345
|
-
`${prefix} git-workspace taskId=${event.taskId}`,
|
|
21346
|
-
`workspaceId=${event.workspaceId}`,
|
|
21347
|
-
`phase=${event.phase}`,
|
|
21348
|
-
event.headChanged !== void 0 ? `headChanged=${event.headChanged}` : void 0,
|
|
21349
|
-
event.commitsSinceBaseline !== void 0 ? `commits=${event.commitsSinceBaseline}` : void 0,
|
|
21350
|
-
event.dirty ? `dirty=${event.dirty.staged}/${event.dirty.unstaged}/${event.dirty.untracked}/${event.dirty.conflicted}` : void 0,
|
|
21351
|
-
stableGitErrorCategory(event.errorCategory) ? `errorCategory=${stableGitErrorCategory(event.errorCategory)}` : void 0
|
|
21352
|
-
].filter((part) => part !== void 0);
|
|
21353
|
-
return parts.join(" ");
|
|
21354
|
-
}
|
|
21355
|
-
case "device-assertion": {
|
|
21356
|
-
const parts = event.result === "issued" ? [
|
|
21357
|
-
`${prefix} device-assertion result=issued`,
|
|
21358
|
-
`audience=${quote(event.audience)}`,
|
|
21359
|
-
`jti=${event.jti}`,
|
|
21360
|
-
`expiresAt=${event.expiresAt}`
|
|
21361
|
-
] : [
|
|
21362
|
-
`${prefix} device-assertion result=denied`,
|
|
21363
|
-
`reason=${event.reason}`,
|
|
21364
|
-
event.audienceSize !== void 0 ? `audienceSize=${event.audienceSize}` : void 0
|
|
21365
|
-
].filter((part) => part !== void 0);
|
|
21366
|
-
return parts.join(" ");
|
|
21367
|
-
}
|
|
21368
|
-
}
|
|
21369
|
-
}
|
|
21370
|
-
function formatTaskLine(task) {
|
|
21371
|
-
const git = "git" in task ? task.git : void 0;
|
|
21372
|
-
const gitStatus = git ? [
|
|
21373
|
-
`git=${git.phase}`,
|
|
21374
|
-
git.commitsSinceBaseline !== void 0 ? `commits=${git.commitsSinceBaseline}` : void 0,
|
|
21375
|
-
git.dirty ? `dirty=${git.dirty.staged}/${git.dirty.unstaged}/${git.dirty.untracked}/${git.dirty.conflicted}` : void 0
|
|
21376
|
-
].filter((part) => part !== void 0).join(" ") : void 0;
|
|
21377
|
-
const parts = [
|
|
21378
|
-
task.taskId,
|
|
21379
|
-
task.state,
|
|
21380
|
-
task.runtime ? `runtime=${task.runtime}` : void 0,
|
|
21381
|
-
gitStatus,
|
|
21382
|
-
task.claimedRuntime !== void 0 ? `claimedRuntime=${task.claimedRuntime}` : void 0,
|
|
21383
|
-
`updatedAt=${task.updatedAt}`,
|
|
21384
|
-
task.sessionRef ? `sessionRef=${task.sessionRef}` : void 0,
|
|
21385
|
-
task.declined ? "declined=true" : void 0,
|
|
21386
|
-
task.summary ? `summary=${quote(task.summary)}` : void 0
|
|
21387
|
-
].filter((part) => Boolean(part));
|
|
21388
|
-
return parts.join(" ");
|
|
21389
|
-
}
|
|
21390
|
-
function formatTaskListLines(tasks) {
|
|
21391
|
-
if (tasks.length === 0) return ["(no tasks observed yet)"];
|
|
21392
|
-
return tasks.map(formatTaskLine);
|
|
21393
|
-
}
|
|
21394
|
-
function formatRuntimeLines(runtimes) {
|
|
21395
|
-
if (runtimes.length === 0) return ["(no runtimes configured \u2014 check runtimeAllowlist)"];
|
|
21396
|
-
return runtimes.map((r) => {
|
|
21397
|
-
if (!r.present) return `${r.id}: absent`;
|
|
21398
|
-
const caps = [];
|
|
21399
|
-
if (r.steer) caps.push("steer");
|
|
21400
|
-
if (r.resume) caps.push("resume");
|
|
21401
|
-
const parts = [
|
|
21402
|
-
"present",
|
|
21403
|
-
r.version ? `version=${r.version}` : void 0,
|
|
21404
|
-
r.authPresent !== void 0 ? `authPresent=${r.authPresent}` : void 0,
|
|
21405
|
-
`capabilities=${caps.length ? caps.join(",") : "(none)"}`,
|
|
21406
|
-
`modes=${r.permissionModes.length ? r.permissionModes.join(",") : "(none)"}`
|
|
21407
|
-
].filter((part) => Boolean(part));
|
|
21408
|
-
return `${r.id}: ${parts.join(" ")}`;
|
|
21409
|
-
});
|
|
21410
|
-
}
|
|
21411
|
-
function formatStatusLines(view) {
|
|
21412
|
-
const lines = [];
|
|
21413
|
-
const label = view.branding?.displayName ?? view.productName;
|
|
21414
|
-
lines.push(`product: ${label} (${view.productId})`);
|
|
21415
|
-
lines.push(
|
|
21416
|
-
`local-agent-release: ${view.localAgentRelease.version}${view.localAgentRelease.buildId ? ` buildId=${view.localAgentRelease.buildId}` : ""}`
|
|
21417
|
-
);
|
|
21418
|
-
if (view.branding?.supportUrl) lines.push(`support: ${view.branding.supportUrl}`);
|
|
21419
|
-
lines.push(`paired: ${view.paired ? "yes" : "no"}${view.deviceId ? ` deviceId=${view.deviceId}` : ""}`);
|
|
21420
|
-
lines.push(
|
|
21421
|
-
view.connection ? `connection: last-known=${view.connection.state} at=${view.connection.ts}` : "connection: unknown (no audit log yet \u2014 run `byok-agent start` at least once to begin observing)"
|
|
21422
|
-
);
|
|
21423
|
-
const runtimeSummary = view.runtimes.map((r) => `${r.id}=${r.present ? "present" : "absent"}`).join(" ");
|
|
21424
|
-
lines.push(`runtimes: ${runtimeSummary || "(none configured)"}`);
|
|
21425
|
-
const c = view.taskCounts;
|
|
21426
|
-
lines.push(
|
|
21427
|
-
`tasks: total=${c.total} offered=${c.Offered} claimed=${c.Claimed} running=${c.Running} awaitApproval=${c.AwaitApproval} complete=${c.Complete} failed=${c.Failed} cancelled=${c.Cancelled}`
|
|
21428
|
-
);
|
|
21429
|
-
lines.push(`audit-log: ${view.auditLogPath} (${view.auditLogLineCount} event${view.auditLogLineCount === 1 ? "" : "s"})`);
|
|
21430
|
-
return lines;
|
|
21431
|
-
}
|
|
21432
|
-
function formatLiveStatusLines(live) {
|
|
21433
|
-
const liveRelease = live.localAgentRelease;
|
|
21434
|
-
const lines = [
|
|
21435
|
-
`live: pid=${live.pid} uptimeMs=${live.uptimeMs} transport=${live.transport}`,
|
|
21436
|
-
liveRelease ? `live-local-agent-release: ${liveRelease.version}${liveRelease.buildId ? ` buildId=${liveRelease.buildId}` : ""}` : "live-local-agent-release: unknown",
|
|
21437
|
-
`live-paired: ${live.paired ? "yes" : "no"}${live.deviceId ? ` deviceId=${live.deviceId}` : ""}`,
|
|
21438
|
-
`live-runtimes: ${live.runtimeIds.length ? live.runtimeIds.join(",") : "(none)"}`,
|
|
21439
|
-
`live-toolsets: revision=${live.toolsets.revision} count=${live.toolsets.toolsets.length}`
|
|
21440
|
-
];
|
|
21441
|
-
for (const toolset of live.toolsets.toolsets) lines.push(formatToolsetStatusLine("live-toolset", toolset));
|
|
21442
|
-
const health = live.operationalHealth;
|
|
21443
|
-
if (health.availability === "unavailable") {
|
|
21444
|
-
lines.push(`live-operational-health: unavailable reason=${quote(health.reason)}`);
|
|
21445
|
-
} else {
|
|
21446
|
-
lines.push(
|
|
21447
|
-
`live-operational-health: state=${health.state} failures=${health.failureCount}/${health.failureThreshold} windowMs=${health.windowMs} crashes=${health.crashCount}${health.lastCrashAt ? ` lastCrashAt=${health.lastCrashAt}` : ""}`
|
|
21448
|
-
);
|
|
21449
|
-
}
|
|
21450
|
-
if (live.activeTasks.length === 0) {
|
|
21451
|
-
lines.push("live-active-tasks: (none)");
|
|
21452
|
-
} else {
|
|
21453
|
-
for (const task of live.activeTasks) {
|
|
21454
|
-
lines.push(`live-active-task: ${task.taskId} ${task.state}`);
|
|
21455
|
-
}
|
|
21456
|
-
}
|
|
21457
|
-
lines.push(`live-approvals-pending: ${live.approvalsPending}`);
|
|
21458
|
-
if (live.approvals.length === 0) {
|
|
21459
|
-
lines.push("live-approvals: (none)");
|
|
21460
|
-
} else {
|
|
21461
|
-
for (const approval of live.approvals) {
|
|
21462
|
-
lines.push(`live-approval: ${approval.approvalId} taskId=${approval.taskId} summary=${quote(summaryExcerpt(approval.summary))}`);
|
|
21463
|
-
}
|
|
21464
|
-
}
|
|
21465
|
-
if (live.queueWatermarks.length === 0) {
|
|
21466
|
-
lines.push("live-queue-watermarks: (none)");
|
|
21467
|
-
} else {
|
|
21468
|
-
for (const watermark of live.queueWatermarks) {
|
|
21469
|
-
lines.push(
|
|
21470
|
-
`live-queue-watermark: ${watermark.taskId} progressBatcherPending=${watermark.progressBatcherPending} pendingApprovals=${watermark.pendingApprovals}`
|
|
21471
|
-
);
|
|
21472
|
-
}
|
|
21473
|
-
}
|
|
21474
|
-
const storage = live.storage;
|
|
21475
|
-
if (storage !== void 0) {
|
|
21476
|
-
lines.push(
|
|
21477
|
-
`live-storage: state=${storage.pressureState} used=${storage.usedBytes} budget=${storage.budgetBytes} free=${storage.freeBytes} measuredAt=${storage.measuredAt}`
|
|
21478
|
-
);
|
|
21479
|
-
for (const category of storage.categories) {
|
|
21480
|
-
lines.push(`live-storage-category: ${category.category} bytes=${category.bytes}${category.approximate ? " (approximate)" : ""}`);
|
|
21481
|
-
}
|
|
21482
|
-
if (storage.lastCompaction) {
|
|
21483
|
-
lines.push(
|
|
21484
|
-
`live-storage-compaction: checkpointed=${storage.lastCompaction.checkpointed} walFramesRemaining=${storage.lastCompaction.walFramesRemaining} pagesVacuumed=${storage.lastCompaction.pagesVacuumed} durationMs=${storage.lastCompaction.durationMs} at=${storage.lastCompaction.at}`
|
|
21485
|
-
);
|
|
21486
|
-
}
|
|
21487
|
-
}
|
|
21488
|
-
return lines;
|
|
21489
|
-
}
|
|
21490
|
-
function formatToolsetStatusLine(prefix, toolset) {
|
|
21491
|
-
const observation = toolset.observation;
|
|
21492
|
-
return [
|
|
21493
|
-
`${prefix}: ${toolset.id}`,
|
|
21494
|
-
`servers=${toolset.serverCount}`,
|
|
21495
|
-
`definitionRevision=${toolset.definitionRevision}`,
|
|
21496
|
-
`state=${observation?.state ?? "unobserved"}`,
|
|
21497
|
-
observation?.version === void 0 ? void 0 : `version=${quote(observation.version)}`,
|
|
21498
|
-
observation?.observedAt === void 0 ? void 0 : `observedAt=${observation.observedAt}`,
|
|
21499
|
-
observation?.reasonCode === void 0 ? void 0 : `reasonCode=${observation.reasonCode}`
|
|
21500
|
-
].filter((part) => part !== void 0).join(" ");
|
|
21501
|
-
}
|
|
21502
|
-
function formatToolsetsReloadReceiptLines(receipt) {
|
|
21503
|
-
return [
|
|
21504
|
-
`toolsets-reload: changed=${receipt.changed ? "yes" : "no"} previousRevision=${receipt.previousRevision} revision=${receipt.revision}`,
|
|
21505
|
-
...receipt.toolsets.map((toolset) => formatToolsetStatusLine("toolset", toolset))
|
|
21506
|
-
];
|
|
21507
|
-
}
|
|
21508
|
-
var SUMMARY_EXCERPT_MAX_LEN = 60;
|
|
21509
|
-
function summaryExcerpt(summary) {
|
|
21510
|
-
if (!summary) return "(no summary)";
|
|
21511
|
-
return summary.length > SUMMARY_EXCERPT_MAX_LEN ? `${summary.slice(0, SUMMARY_EXCERPT_MAX_LEN)}\u2026` : summary;
|
|
21512
|
-
}
|
|
21513
|
-
function formatAge(ms) {
|
|
21514
|
-
const totalSeconds = Math.floor(Math.max(0, ms) / 1e3);
|
|
21515
|
-
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
21516
|
-
const totalMinutes = Math.floor(totalSeconds / 60);
|
|
21517
|
-
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
21518
|
-
const totalHours = Math.floor(totalMinutes / 60);
|
|
21519
|
-
if (totalHours < 24) return `${totalHours}h`;
|
|
21520
|
-
const totalDays = Math.floor(totalHours / 24);
|
|
21521
|
-
return `${totalDays}d`;
|
|
21522
|
-
}
|
|
21523
|
-
function formatApprovalsListLines(approvals, nowMs) {
|
|
21524
|
-
if (approvals.length === 0) return ["(no pending approvals)"];
|
|
21525
|
-
return approvals.map((approval) => {
|
|
21526
|
-
const ageMs = nowMs - Date.parse(approval.createdAt);
|
|
21527
|
-
return `${approval.approvalId} taskId=${approval.taskId} age=${formatAge(ageMs)} summary=${quote(summaryExcerpt(approval.summary))}`;
|
|
21528
|
-
});
|
|
21529
|
-
}
|
|
21530
|
-
|
|
21531
|
-
// src/bin/commands/approvals.ts
|
|
21532
|
-
async function runApprovalsCommand(storeDir, productId, deps = {}) {
|
|
21533
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
21534
|
-
const error = deps.error ?? ((line) => console.error(line));
|
|
21535
|
-
const connectControl = deps.connectControl ?? connectControlClient;
|
|
21536
|
-
const now = deps.now ?? (() => Date.now());
|
|
21537
|
-
const conn = await connectControl({ storeDir, productId });
|
|
21538
|
-
if (!conn.ok) {
|
|
21539
|
-
const message = `cannot list approvals: daemon not reachable (${conn.reason}) \u2014 is \`byok-agent start\` (or the installed service) running?`;
|
|
21540
|
-
error(message);
|
|
21541
|
-
throw new Error(message);
|
|
21542
|
-
}
|
|
21543
|
-
try {
|
|
21544
|
-
const result = await conn.client.request("approvals.list");
|
|
21545
|
-
for (const line of formatApprovalsListLines(result.approvals, now())) log(line);
|
|
21546
|
-
} catch (err) {
|
|
21547
|
-
if (err instanceof ControlError) {
|
|
21548
|
-
error(`approvals list failed: ${err.message}`);
|
|
21549
|
-
} else {
|
|
21550
|
-
error(`approvals list failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21551
|
-
}
|
|
21552
|
-
throw err;
|
|
21553
|
-
} finally {
|
|
21554
|
-
conn.client.close();
|
|
21555
|
-
}
|
|
21556
|
-
}
|
|
21557
|
-
|
|
21558
|
-
// src/bin/commands/approve-reject.ts
|
|
21559
|
-
async function runApproveCommand(storeDir, productId, approvalId, deps = {}) {
|
|
21560
|
-
return resolveApproval(storeDir, productId, approvalId, "approve", void 0, deps);
|
|
21561
|
-
}
|
|
21562
|
-
async function runRejectCommand(storeDir, productId, approvalId, reason, deps = {}) {
|
|
21563
|
-
return resolveApproval(storeDir, productId, approvalId, "reject", reason, deps);
|
|
21564
|
-
}
|
|
21565
|
-
async function resolveApproval(storeDir, productId, approvalId, decision, reason, deps) {
|
|
21566
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
21567
|
-
const error = deps.error ?? ((line) => console.error(line));
|
|
21568
|
-
const connectControl = deps.connectControl ?? connectControlClient;
|
|
21569
|
-
const verb = decision === "approve" ? "approved" : "rejected";
|
|
21570
|
-
const conn = await connectControl({ storeDir, productId });
|
|
21571
|
-
if (!conn.ok) {
|
|
21572
|
-
const message = `cannot ${decision} approvalId=${approvalId}: daemon not reachable (${conn.reason}) \u2014 is \`byok-agent start\` (or the installed service) running?`;
|
|
21573
|
-
error(message);
|
|
21574
|
-
throw new Error(message);
|
|
21575
|
-
}
|
|
21576
|
-
try {
|
|
21577
|
-
await conn.client.request("approvals.resolve", { approvalId, decision, reason });
|
|
21578
|
-
log(`${verb}: approvalId=${approvalId}${reason ? ` reason=${JSON.stringify(reason)}` : ""}`);
|
|
21579
|
-
} catch (err) {
|
|
21580
|
-
if (err instanceof ControlError && err.code === "not_found") {
|
|
21581
|
-
error(`${err.message} \u2014 run \`byok-agent approvals\` to see pending approvalIds`);
|
|
21582
|
-
} else {
|
|
21583
|
-
error(`${decision} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21584
|
-
}
|
|
21585
|
-
throw err;
|
|
21586
|
-
} finally {
|
|
21587
|
-
conn.client.close();
|
|
21588
|
-
}
|
|
21589
|
-
}
|
|
21590
|
-
|
|
21591
|
-
// src/bin/commands/pair.ts
|
|
21592
|
-
async function runPairCommand(config, code, deps = {}) {
|
|
21593
|
-
const log = deps.log ?? ((line) => console.log(line));
|
|
21594
|
-
if (deps.daemon) {
|
|
21595
|
-
const result2 = await deps.daemon.pair(code);
|
|
21596
|
-
log(`paired: deviceId=${result2.deviceId}`);
|
|
21597
|
-
return;
|
|
21598
|
-
}
|
|
21599
|
-
const connectControl = deps.connectControl ?? connectControlClient;
|
|
21600
|
-
const conn = await connectControl({ storeDir: resolveStoreDir(config), productId: config.productId });
|
|
21601
|
-
if (conn.ok) {
|
|
21602
|
-
try {
|
|
21603
|
-
const result2 = await conn.client.request("enrollment.pair", { pairingCode: code });
|
|
21604
|
-
log(`paired: deviceId=${result2.deviceId}`);
|
|
21605
|
-
return;
|
|
21606
|
-
} finally {
|
|
21607
|
-
conn.client.close();
|
|
21608
|
-
}
|
|
21609
|
-
}
|
|
21610
|
-
const daemon = createDaemon(config);
|
|
21611
|
-
const result = await daemon.pair(code);
|
|
21612
|
-
log(`paired: deviceId=${result.deviceId}`);
|
|
21613
|
-
}
|
|
21614
|
-
|
|
21615
|
-
// src/bin/runtime-probe.ts
|
|
21616
|
-
var ALL_RUNTIME_IDS2 = ["pi", "claude", "codex"];
|
|
21617
|
-
var RUNTIME_PROBE_TIMEOUT_MS = 5e3;
|
|
21618
|
-
var MAX_RUNTIME_ID_CHARS = 128;
|
|
21619
|
-
var MAX_RUNTIME_VERSION_CHARS = 256;
|
|
21620
|
-
var MAX_PERMISSION_MODES = 32;
|
|
21621
|
-
var MAX_PERMISSION_MODE_CHARS = 64;
|
|
21622
|
-
function boundedSingleLine(value, maxChars) {
|
|
21623
|
-
return value.replace(/[\r\n\t]/g, " ").slice(0, maxChars);
|
|
21624
|
-
}
|
|
21625
|
-
async function detectWithTimeout(adapter, timeoutMs) {
|
|
21626
|
-
let timer;
|
|
21627
|
-
try {
|
|
21628
|
-
return await Promise.race([
|
|
21629
|
-
adapter.detect(),
|
|
21630
|
-
new Promise((_resolve, reject) => {
|
|
21631
|
-
timer = setTimeout(() => reject(new Error("runtime detection timed out")), timeoutMs);
|
|
21632
|
-
timer.unref?.();
|
|
21633
|
-
})
|
|
21634
|
-
]);
|
|
21635
|
-
} finally {
|
|
21636
|
-
if (timer) clearTimeout(timer);
|
|
21637
|
-
}
|
|
22080
|
+
var RuntimeProbeTimeout = class extends Error {
|
|
22081
|
+
};
|
|
22082
|
+
async function detectWithTimeout(adapter, timeoutMs) {
|
|
22083
|
+
let timer;
|
|
22084
|
+
try {
|
|
22085
|
+
return await Promise.race([
|
|
22086
|
+
adapter.detect(),
|
|
22087
|
+
new Promise((_resolve, reject) => {
|
|
22088
|
+
timer = setTimeout(() => reject(new RuntimeProbeTimeout()), timeoutMs);
|
|
22089
|
+
timer.unref?.();
|
|
22090
|
+
})
|
|
22091
|
+
]);
|
|
22092
|
+
} finally {
|
|
22093
|
+
if (timer) clearTimeout(timer);
|
|
22094
|
+
}
|
|
21638
22095
|
}
|
|
21639
22096
|
function defaultRuntimeAdapters(runtimeAllowlist) {
|
|
21640
22097
|
const ids = runtimeAllowlist ? ALL_RUNTIME_IDS2.filter((id) => runtimeAllowlist.includes(id)) : ALL_RUNTIME_IDS2;
|
|
@@ -21664,18 +22121,19 @@ async function probeRuntimes(adapters, options = {}) {
|
|
|
21664
22121
|
steer = caps.steer === true;
|
|
21665
22122
|
resume = caps.resume === true;
|
|
21666
22123
|
permissionModes = caps.permissionModes.slice(0, MAX_PERMISSION_MODES).map((mode) => boundedSingleLine(mode, MAX_PERMISSION_MODE_CHARS));
|
|
21667
|
-
const detected = await detectWithTimeout(adapter, timeoutMs);
|
|
22124
|
+
const detected = validateRuntimeDetectResult(await detectWithTimeout(adapter, timeoutMs));
|
|
21668
22125
|
return {
|
|
21669
22126
|
id,
|
|
21670
|
-
present: detected.
|
|
21671
|
-
|
|
21672
|
-
...
|
|
22127
|
+
present: detected.kind === "available",
|
|
22128
|
+
outcome: detected.kind,
|
|
22129
|
+
...detected.kind !== "available" || detected.version === void 0 ? {} : { version: boundedSingleLine(detected.version, MAX_RUNTIME_VERSION_CHARS) },
|
|
22130
|
+
...detected.kind === "available" && typeof detected.authPresent === "boolean" ? { authPresent: detected.authPresent } : {},
|
|
21673
22131
|
steer,
|
|
21674
22132
|
resume,
|
|
21675
22133
|
permissionModes
|
|
21676
22134
|
};
|
|
21677
|
-
} catch {
|
|
21678
|
-
return { id, present: false, steer, resume, permissionModes };
|
|
22135
|
+
} catch (error) {
|
|
22136
|
+
return { id, present: false, outcome: error instanceof RuntimeProbeTimeout ? "timeout" : "probe-failed", steer, resume, permissionModes };
|
|
21679
22137
|
}
|
|
21680
22138
|
})
|
|
21681
22139
|
);
|
|
@@ -22083,7 +22541,7 @@ function checksFor(snapshot) {
|
|
|
22083
22541
|
{
|
|
22084
22542
|
id: "runtimes",
|
|
22085
22543
|
status: snapshot.runtimes.some((runtime) => runtime.present) ? "pass" : "warn",
|
|
22086
|
-
summary: `${snapshot.runtimes.filter((runtime) => runtime.present).length}/${snapshot.runtimes.length} present`
|
|
22544
|
+
summary: `${snapshot.runtimes.filter((runtime) => runtime.present).length}/${snapshot.runtimes.length} present; ` + RUNTIME_DETECTION_FAILURE_KINDS.map((kind) => `${kind}=${snapshot.runtimes.filter((runtime) => runtime.outcome === kind).length}`).join(" ")
|
|
22087
22545
|
},
|
|
22088
22546
|
{
|
|
22089
22547
|
id: "control",
|
|
@@ -22128,6 +22586,7 @@ async function collectDiagnostics(config, storeDir, options = {}) {
|
|
|
22128
22586
|
const runtimes = probedRuntimes.map((runtime) => ({
|
|
22129
22587
|
idHash: stableIdentifierHash(runtime.id),
|
|
22130
22588
|
present: runtime.present,
|
|
22589
|
+
outcome: runtime.outcome,
|
|
22131
22590
|
versionPresent: runtime.version !== void 0,
|
|
22132
22591
|
...runtime.authPresent === void 0 ? {} : { authPresent: runtime.authPresent },
|
|
22133
22592
|
steer: runtime.steer,
|
|
@@ -22217,172 +22676,586 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
|
|
|
22217
22676
|
if (bytesRead === 0) throw new Error("operational health state ended during bounded copy");
|
|
22218
22677
|
position += bytesRead;
|
|
22219
22678
|
}
|
|
22220
|
-
const afterRead = fstatSync(sourceFd, { bigint: true });
|
|
22221
|
-
if (!sameFileState5(afterRead, sourceStat)) {
|
|
22222
|
-
throw new Error("operational health state changed during bounded copy");
|
|
22679
|
+
const afterRead = fstatSync(sourceFd, { bigint: true });
|
|
22680
|
+
if (!sameFileState5(afterRead, sourceStat)) {
|
|
22681
|
+
throw new Error("operational health state changed during bounded copy");
|
|
22682
|
+
}
|
|
22683
|
+
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
22684
|
+
evidenceTemp = `.${evidenceName}.${randomUUID()}.tmp`;
|
|
22685
|
+
const evidenceFd = openSync(evidenceTemp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 384);
|
|
22686
|
+
try {
|
|
22687
|
+
writeFileSync(evidenceFd, bytes);
|
|
22688
|
+
fchmodSync(evidenceFd, 384);
|
|
22689
|
+
fsyncSync(evidenceFd);
|
|
22690
|
+
} finally {
|
|
22691
|
+
closeSync(evidenceFd);
|
|
22692
|
+
}
|
|
22693
|
+
linkSync(evidenceTemp, evidenceName);
|
|
22694
|
+
evidenceCreated = true;
|
|
22695
|
+
const evidence = lstatSync(evidenceName, { bigint: true });
|
|
22696
|
+
const tempEvidence = lstatSync(evidenceTemp, { bigint: true });
|
|
22697
|
+
if (!evidence.isFile() || !sameInode2(evidence, tempEvidence) || evidence.size !== sourceStat.size) {
|
|
22698
|
+
throw new Error("quarantined operational health evidence does not match the bounded copy");
|
|
22699
|
+
}
|
|
22700
|
+
unlinkSync(evidenceTemp);
|
|
22701
|
+
evidenceTemp = void 0;
|
|
22702
|
+
const finalOpenSource = fstatSync(sourceFd, { bigint: true });
|
|
22703
|
+
const finalNamedSource = lstatSync(sourcePath, { bigint: true });
|
|
22704
|
+
if (!sameFileState5(finalOpenSource, sourceStat) || !sameFileState5(finalNamedSource, sourceStat)) {
|
|
22705
|
+
throw new Error("operational health source changed before removal");
|
|
22706
|
+
}
|
|
22707
|
+
const manifestBody = `${JSON.stringify(
|
|
22708
|
+
{
|
|
22709
|
+
version: 1,
|
|
22710
|
+
quarantinedAt,
|
|
22711
|
+
reason,
|
|
22712
|
+
sourcePath,
|
|
22713
|
+
evidenceFile: evidenceName,
|
|
22714
|
+
sha256,
|
|
22715
|
+
sizeBytes
|
|
22716
|
+
},
|
|
22717
|
+
null,
|
|
22718
|
+
2
|
|
22719
|
+
)}
|
|
22720
|
+
`;
|
|
22721
|
+
manifestTemp = `.${manifestName}.${randomUUID()}.tmp`;
|
|
22722
|
+
const manifestFd = openSync(manifestTemp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 384);
|
|
22723
|
+
try {
|
|
22724
|
+
writeFileSync(manifestFd, manifestBody, "utf8");
|
|
22725
|
+
fchmodSync(manifestFd, 384);
|
|
22726
|
+
fsyncSync(manifestFd);
|
|
22727
|
+
} finally {
|
|
22728
|
+
closeSync(manifestFd);
|
|
22729
|
+
}
|
|
22730
|
+
linkSync(manifestTemp, manifestName);
|
|
22731
|
+
manifestCreated = true;
|
|
22732
|
+
unlinkSync(manifestTemp);
|
|
22733
|
+
manifestTemp = void 0;
|
|
22734
|
+
if (process.platform !== "win32") {
|
|
22735
|
+
const quarantineDirectoryFd = openSync(".", constants.O_RDONLY);
|
|
22736
|
+
try {
|
|
22737
|
+
fsyncSync(quarantineDirectoryFd);
|
|
22738
|
+
} finally {
|
|
22739
|
+
closeSync(quarantineDirectoryFd);
|
|
22740
|
+
}
|
|
22741
|
+
}
|
|
22742
|
+
const removalOpenSource = fstatSync(sourceFd, { bigint: true });
|
|
22743
|
+
const removalNamedSource = lstatSync(sourcePath, { bigint: true });
|
|
22744
|
+
if (!sameFileState5(removalOpenSource, sourceStat) || !sameFileState5(removalNamedSource, sourceStat)) {
|
|
22745
|
+
throw new Error("operational health source changed before removal");
|
|
22746
|
+
}
|
|
22747
|
+
unlinkSync(sourcePath);
|
|
22748
|
+
sourceRemoved = true;
|
|
22749
|
+
if (process.platform !== "win32") {
|
|
22750
|
+
const directoryFd = openSync(path14__default.dirname(sourcePath), constants.O_RDONLY);
|
|
22751
|
+
try {
|
|
22752
|
+
fsyncSync(directoryFd);
|
|
22753
|
+
} finally {
|
|
22754
|
+
closeSync(directoryFd);
|
|
22755
|
+
}
|
|
22756
|
+
}
|
|
22757
|
+
return { sha256, sizeBytes };
|
|
22758
|
+
} catch (err) {
|
|
22759
|
+
if (evidenceTemp) {
|
|
22760
|
+
try {
|
|
22761
|
+
unlinkSync(evidenceTemp);
|
|
22762
|
+
} catch {
|
|
22763
|
+
}
|
|
22764
|
+
}
|
|
22765
|
+
if (manifestTemp) {
|
|
22766
|
+
try {
|
|
22767
|
+
unlinkSync(manifestTemp);
|
|
22768
|
+
} catch {
|
|
22769
|
+
}
|
|
22770
|
+
}
|
|
22771
|
+
if (manifestCreated && !sourceRemoved) {
|
|
22772
|
+
try {
|
|
22773
|
+
unlinkSync(manifestName);
|
|
22774
|
+
} catch {
|
|
22775
|
+
}
|
|
22776
|
+
}
|
|
22777
|
+
if (evidenceCreated && !sourceRemoved) {
|
|
22778
|
+
try {
|
|
22779
|
+
unlinkSync(evidenceName);
|
|
22780
|
+
} catch {
|
|
22781
|
+
}
|
|
22782
|
+
}
|
|
22783
|
+
throw err;
|
|
22784
|
+
} finally {
|
|
22785
|
+
if (entered) process.chdir(previousCwd);
|
|
22786
|
+
}
|
|
22787
|
+
}
|
|
22788
|
+
async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
|
|
22789
|
+
const resolvedStoreDir = path14__default.resolve(storeDir);
|
|
22790
|
+
const owner = await acquireDaemonOwner(resolvedStoreDir, "doctor", options.clock);
|
|
22791
|
+
try {
|
|
22792
|
+
const sourcePath = path14__default.join(resolvedStoreDir, OPERATIONAL_HEALTH_FILENAME);
|
|
22793
|
+
let opened;
|
|
22794
|
+
try {
|
|
22795
|
+
opened = await openOperationalHealthFile(resolvedStoreDir);
|
|
22796
|
+
} catch (err) {
|
|
22797
|
+
throw new Error("operational health state could not be opened safely; refusing quarantine", { cause: err });
|
|
22798
|
+
}
|
|
22799
|
+
if (!opened) return { status: "not-needed", reason: "missing" };
|
|
22800
|
+
const source = opened.handle;
|
|
22801
|
+
try {
|
|
22802
|
+
const inspection = await inspectOperationalHealthHandle(source, opened.stat);
|
|
22803
|
+
if (inspection.status === "valid") return { status: "not-needed", reason: "valid" };
|
|
22804
|
+
if (inspection.status === "unavailable") {
|
|
22805
|
+
throw new Error(`${inspection.reason}; refusing to quarantine unconfirmed corruption`);
|
|
22806
|
+
}
|
|
22807
|
+
const sourceStat = await source.stat({ bigint: true });
|
|
22808
|
+
if (!sourceStat.isFile()) throw new Error("operational health state is not a regular file; refusing quarantine");
|
|
22809
|
+
const quarantineDir = path14__default.join(resolvedStoreDir, JOURNAL_QUARANTINE_DIRNAME);
|
|
22810
|
+
try {
|
|
22811
|
+
const existing = await promises.lstat(quarantineDir);
|
|
22812
|
+
if (!existing.isDirectory() || existing.isSymbolicLink()) {
|
|
22813
|
+
throw new Error("quarantine path is not a real directory; refusing quarantine");
|
|
22814
|
+
}
|
|
22815
|
+
} catch (err) {
|
|
22816
|
+
if (err.code !== "ENOENT") throw err;
|
|
22817
|
+
await promises.mkdir(quarantineDir, { mode: 448 });
|
|
22818
|
+
}
|
|
22819
|
+
await ensureSecureDir(quarantineDir);
|
|
22820
|
+
const securedQuarantine = await promises.lstat(quarantineDir, { bigint: true });
|
|
22821
|
+
if (!securedQuarantine.isDirectory() || securedQuarantine.isSymbolicLink()) {
|
|
22822
|
+
throw new Error("quarantine path changed during validation; refusing quarantine");
|
|
22823
|
+
}
|
|
22824
|
+
const stamp = (options.clock ?? (() => /* @__PURE__ */ new Date()))().toISOString().replace(/[:.]/g, "-");
|
|
22825
|
+
const evidenceName = `${stamp}-${randomUUID()}-${OPERATIONAL_HEALTH_FILENAME}`;
|
|
22826
|
+
const manifestName = `${evidenceName}.manifest.json`;
|
|
22827
|
+
const hashed = publishQuarantineEvidencePinned(
|
|
22828
|
+
quarantineDir,
|
|
22829
|
+
securedQuarantine,
|
|
22830
|
+
sourcePath,
|
|
22831
|
+
source.fd,
|
|
22832
|
+
sourceStat,
|
|
22833
|
+
evidenceName,
|
|
22834
|
+
manifestName,
|
|
22835
|
+
(options.clock ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
22836
|
+
inspection.reason
|
|
22837
|
+
);
|
|
22838
|
+
return { status: "quarantined", evidenceName, manifestName, sha256: hashed.sha256, sizeBytes: hashed.sizeBytes };
|
|
22839
|
+
} finally {
|
|
22840
|
+
await source.close();
|
|
22841
|
+
}
|
|
22842
|
+
} finally {
|
|
22843
|
+
await owner.release();
|
|
22844
|
+
}
|
|
22845
|
+
}
|
|
22846
|
+
|
|
22847
|
+
// src/diagnostics/device-doctor.ts
|
|
22848
|
+
var MESSAGES = {
|
|
22849
|
+
"confirmation-required": "Explicit confirmation is required for enrollment metadata repair.",
|
|
22850
|
+
"invalid-target": "An explicit expected tenant and device are required.",
|
|
22851
|
+
"daemon-running": "Stop the daemon before enrollment metadata repair.",
|
|
22852
|
+
"store-busy": "The store is owned by another operation; enrollment metadata repair refused.",
|
|
22853
|
+
"authority-unavailable": "The OS enrollment authority could not be read.",
|
|
22854
|
+
"authority-missing": "No complete OS enrollment exists; use explicit authenticated pairing.",
|
|
22855
|
+
"target-mismatch": "The OS enrollment does not match the expected tenant and device.",
|
|
22856
|
+
"projection-unavailable": "The enrollment metadata could not be read safely; repair refused.",
|
|
22857
|
+
"repair-failed": "Enrollment metadata repair did not complete; inspect the state before retrying."
|
|
22858
|
+
};
|
|
22859
|
+
var DeviceMetadataRepairError = class extends Error {
|
|
22860
|
+
constructor(code) {
|
|
22861
|
+
super(MESSAGES[code]);
|
|
22862
|
+
this.code = code;
|
|
22863
|
+
this.name = "DeviceMetadataRepairError";
|
|
22864
|
+
}
|
|
22865
|
+
code;
|
|
22866
|
+
};
|
|
22867
|
+
async function repairDeviceEnrollmentMetadata(config, input) {
|
|
22868
|
+
if (input?.confirmed !== true) throw new DeviceMetadataRepairError("confirmation-required");
|
|
22869
|
+
if (typeof input.expectedDeviceId !== "string" || input.expectedDeviceId.trim().length === 0 || !isTenantId(input.expectedTenantId)) {
|
|
22870
|
+
throw new DeviceMetadataRepairError("invalid-target");
|
|
22871
|
+
}
|
|
22872
|
+
const { expectedDeviceId, expectedTenantId } = input;
|
|
22873
|
+
const productId = config.productId;
|
|
22874
|
+
let failureCode = "repair-failed";
|
|
22875
|
+
try {
|
|
22876
|
+
const storeDir = DeviceStore.resolveDir(productId, config.storeDir);
|
|
22877
|
+
const control = await connectControlClient({ storeDir, productId });
|
|
22878
|
+
if (control.ok) {
|
|
22879
|
+
control.client.close();
|
|
22880
|
+
throw new DeviceMetadataRepairError("daemon-running");
|
|
22223
22881
|
}
|
|
22224
|
-
const
|
|
22225
|
-
evidenceTemp = `.${evidenceName}.${randomUUID()}.tmp`;
|
|
22226
|
-
const evidenceFd = openSync(evidenceTemp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 384);
|
|
22882
|
+
const owner = await acquireDaemonOwner(storeDir, "doctor");
|
|
22227
22883
|
try {
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22884
|
+
const store = new DeviceStore(storeDir, void 0, productId);
|
|
22885
|
+
failureCode = "projection-unavailable";
|
|
22886
|
+
await store.load();
|
|
22887
|
+
failureCode = "authority-unavailable";
|
|
22888
|
+
const authority = await store.credentials.read();
|
|
22889
|
+
if (authority === void 0) throw new DeviceMetadataRepairError("authority-missing");
|
|
22890
|
+
if (authority.deviceId !== expectedDeviceId || authority.tenantId !== expectedTenantId) {
|
|
22891
|
+
throw new DeviceMetadataRepairError("target-mismatch");
|
|
22892
|
+
}
|
|
22893
|
+
failureCode = "repair-failed";
|
|
22894
|
+
const changed = await store.reconcileMetadata(authority);
|
|
22895
|
+
const readback = await store.load();
|
|
22896
|
+
if (readback === void 0 || readback.deviceId !== authority.deviceId || readback.tenantId !== authority.tenantId || readback.devicePublicKey !== authority.devicePublicKey) {
|
|
22897
|
+
throw new DeviceMetadataRepairError("repair-failed");
|
|
22898
|
+
}
|
|
22899
|
+
return { action: "restore-enrollment-metadata", scope: "device", status: changed ? "repaired" : "not-needed" };
|
|
22900
|
+
} catch (error) {
|
|
22901
|
+
if (error instanceof DeviceMetadataRepairError) throw error;
|
|
22902
|
+
throw new DeviceMetadataRepairError(failureCode);
|
|
22231
22903
|
} finally {
|
|
22232
|
-
|
|
22904
|
+
failureCode = "repair-failed";
|
|
22905
|
+
await owner.release();
|
|
22233
22906
|
}
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22907
|
+
} catch (error) {
|
|
22908
|
+
if (error instanceof DeviceMetadataRepairError) throw error;
|
|
22909
|
+
if (error instanceof DaemonOwnerActiveError) throw new DeviceMetadataRepairError("store-busy");
|
|
22910
|
+
throw new DeviceMetadataRepairError(failureCode);
|
|
22911
|
+
}
|
|
22912
|
+
}
|
|
22913
|
+
|
|
22914
|
+
// src/bin/format.ts
|
|
22915
|
+
function quote(text) {
|
|
22916
|
+
return JSON.stringify(text);
|
|
22917
|
+
}
|
|
22918
|
+
function redactedByteCountPlaceholder(text) {
|
|
22919
|
+
return `[redacted: ${Buffer.byteLength(text, "utf8")} bytes]`;
|
|
22920
|
+
}
|
|
22921
|
+
var STABLE_GIT_ERROR_CATEGORIES = new Set(GIT_ERROR_CATEGORIES);
|
|
22922
|
+
function stableGitErrorCategory(value) {
|
|
22923
|
+
return value !== void 0 && STABLE_GIT_ERROR_CATEGORIES.has(value) ? value : void 0;
|
|
22924
|
+
}
|
|
22925
|
+
function formatAgentEvent(event) {
|
|
22926
|
+
switch (event.type) {
|
|
22927
|
+
case "progress":
|
|
22928
|
+
return `progress: ${quote(event.text)}`;
|
|
22929
|
+
case "tool_use":
|
|
22930
|
+
return `tool_use: ${event.tool}`;
|
|
22931
|
+
case "tool_result":
|
|
22932
|
+
return `tool_result: ${event.tool}`;
|
|
22933
|
+
case "artifact":
|
|
22934
|
+
return `artifact: ${event.name} (${event.contentType})`;
|
|
22935
|
+
case "needs_approval":
|
|
22936
|
+
return `needs_approval: ${quote(event.summary)}`;
|
|
22937
|
+
case "turn_end":
|
|
22938
|
+
return "turn_end";
|
|
22939
|
+
case "error":
|
|
22940
|
+
return `error: ${quote(event.message)}`;
|
|
22941
|
+
case "usage": {
|
|
22942
|
+
const parts = [];
|
|
22943
|
+
if (event.inputTokens !== void 0) parts.push(`in=${event.inputTokens}`);
|
|
22944
|
+
if (event.outputTokens !== void 0) parts.push(`out=${event.outputTokens}`);
|
|
22945
|
+
if (event.totalTokens !== void 0) parts.push(`total=${event.totalTokens}`);
|
|
22946
|
+
return `usage: ${parts.length ? parts.join(" ") : "(no fields reported)"}`;
|
|
22240
22947
|
}
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22948
|
+
}
|
|
22949
|
+
}
|
|
22950
|
+
function formatDaemonEventLine(event, options = {}) {
|
|
22951
|
+
const prefix = `[${event.ts}]`;
|
|
22952
|
+
switch (event.kind) {
|
|
22953
|
+
case "offered":
|
|
22954
|
+
return `${prefix} offered taskId=${event.taskId}${event.runtime ? ` runtime=${event.runtime}` : ""}`;
|
|
22955
|
+
case "claimed":
|
|
22956
|
+
return `${prefix} claimed taskId=${event.taskId}${event.claimedRuntime !== void 0 ? ` claimedRuntime=${event.claimedRuntime}` : ""}`;
|
|
22957
|
+
case "started":
|
|
22958
|
+
return `${prefix} started taskId=${event.taskId}`;
|
|
22959
|
+
case "progress":
|
|
22960
|
+
return `${prefix} progress taskId=${event.taskId} ${formatAgentEvent(event.event)}`;
|
|
22961
|
+
case "artifact":
|
|
22962
|
+
return `${prefix} artifact taskId=${event.taskId} name=${event.name} contentType=${event.contentType}`;
|
|
22963
|
+
case "awaiting-approval": {
|
|
22964
|
+
const summary = options.redactApprovalSummary ? redactedByteCountPlaceholder(event.summary) : event.summary;
|
|
22965
|
+
return `${prefix} awaiting-approval taskId=${event.taskId}${event.approvalId ? ` approvalId=${event.approvalId}` : ""} summary=${quote(summary)}`;
|
|
22247
22966
|
}
|
|
22248
|
-
|
|
22249
|
-
{
|
|
22250
|
-
|
|
22251
|
-
|
|
22252
|
-
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
|
|
22256
|
-
|
|
22257
|
-
}
|
|
22258
|
-
|
|
22259
|
-
|
|
22260
|
-
|
|
22261
|
-
`;
|
|
22262
|
-
|
|
22263
|
-
|
|
22264
|
-
|
|
22265
|
-
|
|
22266
|
-
|
|
22267
|
-
|
|
22268
|
-
|
|
22269
|
-
|
|
22967
|
+
case "completed":
|
|
22968
|
+
return `${prefix} completed taskId=${event.taskId} sessionRef=${event.sessionRef} summary=${quote(event.summary)}`;
|
|
22969
|
+
case "failed":
|
|
22970
|
+
return `${prefix} failed taskId=${event.taskId} retryable=${event.retryable}${event.preClaim ? " preClaim=true" : ""} reason=${quote(event.reason)}`;
|
|
22971
|
+
case "cancelled":
|
|
22972
|
+
return `${prefix} cancelled taskId=${event.taskId}${event.reason ? ` reason=${quote(event.reason)}` : ""}`;
|
|
22973
|
+
case "connection":
|
|
22974
|
+
return `${prefix} connection state=${event.state}`;
|
|
22975
|
+
case "paired":
|
|
22976
|
+
return `${prefix} paired deviceId=${event.deviceId}`;
|
|
22977
|
+
case "unpaired":
|
|
22978
|
+
return `${prefix} unpaired`;
|
|
22979
|
+
case "runtimes-detected":
|
|
22980
|
+
return `${prefix} runtimes-detected ids=${event.runtimes.map((r) => r.id).join(",") || "(none)"}`;
|
|
22981
|
+
case "shutdown-requested":
|
|
22982
|
+
return `${prefix} shutdown-requested reason=${quote(event.reason)}`;
|
|
22983
|
+
case "shutdown-complete":
|
|
22984
|
+
return `${prefix} shutdown-complete reason=${quote(event.reason)}${event.undeliveredOutboxCount !== void 0 ? ` undeliveredOutboxCount=${event.undeliveredOutboxCount}` : ""}`;
|
|
22985
|
+
case "stale-approval-decision":
|
|
22986
|
+
return `${prefix} stale-approval-decision taskId=${event.taskId} decision=${event.decision}${event.reason ? ` reason=${quote(event.reason)}` : ""}`;
|
|
22987
|
+
case "runtime-disposal-failed":
|
|
22988
|
+
return `${prefix} runtime-disposal-failed taskId=${event.taskId} runtime=${event.runtimeId} stage=${event.stage} reason=${quote(event.reason)}`;
|
|
22989
|
+
case "git-workspace": {
|
|
22990
|
+
const parts = [
|
|
22991
|
+
`${prefix} git-workspace taskId=${event.taskId}`,
|
|
22992
|
+
`workspaceId=${event.workspaceId}`,
|
|
22993
|
+
`phase=${event.phase}`,
|
|
22994
|
+
event.headChanged !== void 0 ? `headChanged=${event.headChanged}` : void 0,
|
|
22995
|
+
event.commitsSinceBaseline !== void 0 ? `commits=${event.commitsSinceBaseline}` : void 0,
|
|
22996
|
+
event.dirty ? `dirty=${event.dirty.staged}/${event.dirty.unstaged}/${event.dirty.untracked}/${event.dirty.conflicted}` : void 0,
|
|
22997
|
+
stableGitErrorCategory(event.errorCategory) ? `errorCategory=${stableGitErrorCategory(event.errorCategory)}` : void 0
|
|
22998
|
+
].filter((part) => part !== void 0);
|
|
22999
|
+
return parts.join(" ");
|
|
22270
23000
|
}
|
|
22271
|
-
|
|
22272
|
-
|
|
22273
|
-
|
|
22274
|
-
|
|
22275
|
-
|
|
22276
|
-
|
|
22277
|
-
|
|
22278
|
-
|
|
22279
|
-
|
|
22280
|
-
|
|
22281
|
-
|
|
23001
|
+
case "device-assertion": {
|
|
23002
|
+
const parts = event.result === "issued" ? [
|
|
23003
|
+
`${prefix} device-assertion result=issued`,
|
|
23004
|
+
`audience=${quote(event.audience)}`,
|
|
23005
|
+
`jti=${event.jti}`,
|
|
23006
|
+
`expiresAt=${event.expiresAt}`
|
|
23007
|
+
] : [
|
|
23008
|
+
`${prefix} device-assertion result=denied`,
|
|
23009
|
+
`reason=${event.reason}`,
|
|
23010
|
+
event.audienceSize !== void 0 ? `audienceSize=${event.audienceSize}` : void 0
|
|
23011
|
+
].filter((part) => part !== void 0);
|
|
23012
|
+
return parts.join(" ");
|
|
22282
23013
|
}
|
|
22283
|
-
|
|
22284
|
-
|
|
22285
|
-
|
|
22286
|
-
|
|
23014
|
+
}
|
|
23015
|
+
}
|
|
23016
|
+
function formatTaskLine(task) {
|
|
23017
|
+
const git = "git" in task ? task.git : void 0;
|
|
23018
|
+
const gitStatus = git ? [
|
|
23019
|
+
`git=${git.phase}`,
|
|
23020
|
+
git.commitsSinceBaseline !== void 0 ? `commits=${git.commitsSinceBaseline}` : void 0,
|
|
23021
|
+
git.dirty ? `dirty=${git.dirty.staged}/${git.dirty.unstaged}/${git.dirty.untracked}/${git.dirty.conflicted}` : void 0
|
|
23022
|
+
].filter((part) => part !== void 0).join(" ") : void 0;
|
|
23023
|
+
const parts = [
|
|
23024
|
+
task.taskId,
|
|
23025
|
+
task.state,
|
|
23026
|
+
task.runtime ? `runtime=${task.runtime}` : void 0,
|
|
23027
|
+
gitStatus,
|
|
23028
|
+
task.claimedRuntime !== void 0 ? `claimedRuntime=${task.claimedRuntime}` : void 0,
|
|
23029
|
+
`updatedAt=${task.updatedAt}`,
|
|
23030
|
+
task.sessionRef ? `sessionRef=${task.sessionRef}` : void 0,
|
|
23031
|
+
task.declined ? "declined=true" : void 0,
|
|
23032
|
+
task.summary ? `summary=${quote(task.summary)}` : void 0
|
|
23033
|
+
].filter((part) => Boolean(part));
|
|
23034
|
+
return parts.join(" ");
|
|
23035
|
+
}
|
|
23036
|
+
function formatTaskListLines(tasks) {
|
|
23037
|
+
if (tasks.length === 0) return ["(no tasks observed yet)"];
|
|
23038
|
+
return tasks.map(formatTaskLine);
|
|
23039
|
+
}
|
|
23040
|
+
function formatRuntimeLines(runtimes) {
|
|
23041
|
+
if (runtimes.length === 0) return ["(no runtimes configured \u2014 check runtimeAllowlist)"];
|
|
23042
|
+
return runtimes.map((r) => {
|
|
23043
|
+
if (!r.present) return `${r.id}: ${r.outcome}`;
|
|
23044
|
+
const caps = [];
|
|
23045
|
+
if (r.steer) caps.push("steer");
|
|
23046
|
+
if (r.resume) caps.push("resume");
|
|
23047
|
+
const parts = [
|
|
23048
|
+
"present",
|
|
23049
|
+
r.version ? `version=${r.version}` : void 0,
|
|
23050
|
+
r.authPresent !== void 0 ? `authPresent=${r.authPresent}` : void 0,
|
|
23051
|
+
`capabilities=${caps.length ? caps.join(",") : "(none)"}`,
|
|
23052
|
+
`modes=${r.permissionModes.length ? r.permissionModes.join(",") : "(none)"}`
|
|
23053
|
+
].filter((part) => Boolean(part));
|
|
23054
|
+
return `${r.id}: ${parts.join(" ")}`;
|
|
23055
|
+
});
|
|
23056
|
+
}
|
|
23057
|
+
function formatStatusLines(view) {
|
|
23058
|
+
const lines = [];
|
|
23059
|
+
const label = view.branding?.displayName ?? view.productName;
|
|
23060
|
+
lines.push(`product: ${label} (${view.productId})`);
|
|
23061
|
+
lines.push(
|
|
23062
|
+
`local-agent-release: ${view.localAgentRelease.version}${view.localAgentRelease.buildId ? ` buildId=${view.localAgentRelease.buildId}` : ""}`
|
|
23063
|
+
);
|
|
23064
|
+
if (view.branding?.supportUrl) lines.push(`support: ${view.branding.supportUrl}`);
|
|
23065
|
+
lines.push(`paired: ${view.paired ? "yes" : "no"}${view.deviceId ? ` deviceId=${view.deviceId}` : ""}`);
|
|
23066
|
+
lines.push(
|
|
23067
|
+
view.connection ? `connection: last-known=${view.connection.state} at=${view.connection.ts}` : "connection: unknown (no audit log yet \u2014 run `byok-agent start` at least once to begin observing)"
|
|
23068
|
+
);
|
|
23069
|
+
const runtimeSummary = view.runtimes.map((r) => `${r.id}=${r.present ? "present" : r.outcome}`).join(" ");
|
|
23070
|
+
lines.push(`runtimes: ${runtimeSummary || "(none configured)"}`);
|
|
23071
|
+
const c = view.taskCounts;
|
|
23072
|
+
lines.push(
|
|
23073
|
+
`tasks: total=${c.total} offered=${c.Offered} claimed=${c.Claimed} running=${c.Running} awaitApproval=${c.AwaitApproval} complete=${c.Complete} failed=${c.Failed} cancelled=${c.Cancelled}`
|
|
23074
|
+
);
|
|
23075
|
+
lines.push(`audit-log: ${view.auditLogPath} (${view.auditLogLineCount} event${view.auditLogLineCount === 1 ? "" : "s"})`);
|
|
23076
|
+
return lines;
|
|
23077
|
+
}
|
|
23078
|
+
function formatLiveStatusLines(live) {
|
|
23079
|
+
const liveRelease = live.localAgentRelease;
|
|
23080
|
+
const lines = [
|
|
23081
|
+
`live: pid=${live.pid} uptimeMs=${live.uptimeMs} transport=${live.transport}`,
|
|
23082
|
+
liveRelease ? `live-local-agent-release: ${liveRelease.version}${liveRelease.buildId ? ` buildId=${liveRelease.buildId}` : ""}` : "live-local-agent-release: unknown",
|
|
23083
|
+
`live-paired: ${live.paired ? "yes" : "no"}${live.deviceId ? ` deviceId=${live.deviceId}` : ""}`,
|
|
23084
|
+
`live-runtimes: ${live.runtimeIds.length ? live.runtimeIds.join(",") : "(none)"}`,
|
|
23085
|
+
`live-toolsets: revision=${live.toolsets.revision} count=${live.toolsets.toolsets.length}`
|
|
23086
|
+
];
|
|
23087
|
+
for (const toolset of live.toolsets.toolsets) lines.push(formatToolsetStatusLine("live-toolset", toolset));
|
|
23088
|
+
const health = live.operationalHealth;
|
|
23089
|
+
if (health.availability === "unavailable") {
|
|
23090
|
+
lines.push(`live-operational-health: unavailable reason=${quote(health.reason)}`);
|
|
23091
|
+
} else {
|
|
23092
|
+
lines.push(
|
|
23093
|
+
`live-operational-health: state=${health.state} failures=${health.failureCount}/${health.failureThreshold} windowMs=${health.windowMs} crashes=${health.crashCount}${health.lastCrashAt ? ` lastCrashAt=${health.lastCrashAt}` : ""}`
|
|
23094
|
+
);
|
|
23095
|
+
}
|
|
23096
|
+
if (live.activeTasks.length === 0) {
|
|
23097
|
+
lines.push("live-active-tasks: (none)");
|
|
23098
|
+
} else {
|
|
23099
|
+
for (const task of live.activeTasks) {
|
|
23100
|
+
lines.push(`live-active-task: ${task.taskId} ${task.state}`);
|
|
22287
23101
|
}
|
|
22288
|
-
|
|
22289
|
-
|
|
22290
|
-
|
|
22291
|
-
|
|
22292
|
-
|
|
22293
|
-
|
|
22294
|
-
}
|
|
22295
|
-
closeSync(directoryFd);
|
|
22296
|
-
}
|
|
23102
|
+
}
|
|
23103
|
+
lines.push(`live-approvals-pending: ${live.approvalsPending}`);
|
|
23104
|
+
if (live.approvals.length === 0) {
|
|
23105
|
+
lines.push("live-approvals: (none)");
|
|
23106
|
+
} else {
|
|
23107
|
+
for (const approval of live.approvals) {
|
|
23108
|
+
lines.push(`live-approval: ${approval.approvalId} taskId=${approval.taskId} summary=${quote(summaryExcerpt(approval.summary))}`);
|
|
22297
23109
|
}
|
|
22298
|
-
|
|
22299
|
-
|
|
22300
|
-
|
|
22301
|
-
|
|
22302
|
-
|
|
22303
|
-
|
|
22304
|
-
|
|
23110
|
+
}
|
|
23111
|
+
if (live.queueWatermarks.length === 0) {
|
|
23112
|
+
lines.push("live-queue-watermarks: (none)");
|
|
23113
|
+
} else {
|
|
23114
|
+
for (const watermark of live.queueWatermarks) {
|
|
23115
|
+
lines.push(
|
|
23116
|
+
`live-queue-watermark: ${watermark.taskId} progressBatcherPending=${watermark.progressBatcherPending} pendingApprovals=${watermark.pendingApprovals}`
|
|
23117
|
+
);
|
|
22305
23118
|
}
|
|
22306
|
-
|
|
22307
|
-
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
}
|
|
23119
|
+
}
|
|
23120
|
+
const storage = live.storage;
|
|
23121
|
+
if (storage !== void 0) {
|
|
23122
|
+
lines.push(
|
|
23123
|
+
`live-storage: state=${storage.pressureState} used=${storage.usedBytes} budget=${storage.budgetBytes} free=${storage.freeBytes} measuredAt=${storage.measuredAt}`
|
|
23124
|
+
);
|
|
23125
|
+
for (const category of storage.categories) {
|
|
23126
|
+
lines.push(`live-storage-category: ${category.category} bytes=${category.bytes}${category.approximate ? " (approximate)" : ""}`);
|
|
22311
23127
|
}
|
|
22312
|
-
if (
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
}
|
|
23128
|
+
if (storage.lastCompaction) {
|
|
23129
|
+
lines.push(
|
|
23130
|
+
`live-storage-compaction: checkpointed=${storage.lastCompaction.checkpointed} walFramesRemaining=${storage.lastCompaction.walFramesRemaining} pagesVacuumed=${storage.lastCompaction.pagesVacuumed} durationMs=${storage.lastCompaction.durationMs} at=${storage.lastCompaction.at}`
|
|
23131
|
+
);
|
|
22317
23132
|
}
|
|
22318
|
-
|
|
22319
|
-
|
|
22320
|
-
|
|
22321
|
-
|
|
22322
|
-
|
|
23133
|
+
}
|
|
23134
|
+
return lines;
|
|
23135
|
+
}
|
|
23136
|
+
function formatToolsetStatusLine(prefix, toolset) {
|
|
23137
|
+
const observation = toolset.observation;
|
|
23138
|
+
return [
|
|
23139
|
+
`${prefix}: ${toolset.id}`,
|
|
23140
|
+
`servers=${toolset.serverCount}`,
|
|
23141
|
+
`definitionRevision=${toolset.definitionRevision}`,
|
|
23142
|
+
`state=${observation?.state ?? "unobserved"}`,
|
|
23143
|
+
observation?.version === void 0 ? void 0 : `version=${quote(observation.version)}`,
|
|
23144
|
+
observation?.observedAt === void 0 ? void 0 : `observedAt=${observation.observedAt}`,
|
|
23145
|
+
observation?.reasonCode === void 0 ? void 0 : `reasonCode=${observation.reasonCode}`
|
|
23146
|
+
].filter((part) => part !== void 0).join(" ");
|
|
23147
|
+
}
|
|
23148
|
+
function formatToolsetsReloadReceiptLines(receipt) {
|
|
23149
|
+
return [
|
|
23150
|
+
`toolsets-reload: changed=${receipt.changed ? "yes" : "no"} previousRevision=${receipt.previousRevision} revision=${receipt.revision}`,
|
|
23151
|
+
...receipt.toolsets.map((toolset) => formatToolsetStatusLine("toolset", toolset))
|
|
23152
|
+
];
|
|
23153
|
+
}
|
|
23154
|
+
var SUMMARY_EXCERPT_MAX_LEN = 60;
|
|
23155
|
+
function summaryExcerpt(summary) {
|
|
23156
|
+
if (!summary) return "(no summary)";
|
|
23157
|
+
return summary.length > SUMMARY_EXCERPT_MAX_LEN ? `${summary.slice(0, SUMMARY_EXCERPT_MAX_LEN)}\u2026` : summary;
|
|
23158
|
+
}
|
|
23159
|
+
function formatAge(ms) {
|
|
23160
|
+
const totalSeconds = Math.floor(Math.max(0, ms) / 1e3);
|
|
23161
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
23162
|
+
const totalMinutes = Math.floor(totalSeconds / 60);
|
|
23163
|
+
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
23164
|
+
const totalHours = Math.floor(totalMinutes / 60);
|
|
23165
|
+
if (totalHours < 24) return `${totalHours}h`;
|
|
23166
|
+
const totalDays = Math.floor(totalHours / 24);
|
|
23167
|
+
return `${totalDays}d`;
|
|
23168
|
+
}
|
|
23169
|
+
function formatApprovalsListLines(approvals, nowMs) {
|
|
23170
|
+
if (approvals.length === 0) return ["(no pending approvals)"];
|
|
23171
|
+
return approvals.map((approval) => {
|
|
23172
|
+
const ageMs = nowMs - Date.parse(approval.createdAt);
|
|
23173
|
+
return `${approval.approvalId} taskId=${approval.taskId} age=${formatAge(ageMs)} summary=${quote(summaryExcerpt(approval.summary))}`;
|
|
23174
|
+
});
|
|
23175
|
+
}
|
|
23176
|
+
|
|
23177
|
+
// src/bin/commands/approvals.ts
|
|
23178
|
+
async function runApprovalsCommand(storeDir, productId, deps = {}) {
|
|
23179
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
23180
|
+
const error = deps.error ?? ((line) => console.error(line));
|
|
23181
|
+
const connectControl = deps.connectControl ?? connectControlClient;
|
|
23182
|
+
const now = deps.now ?? (() => Date.now());
|
|
23183
|
+
const conn = await connectControl({ storeDir, productId });
|
|
23184
|
+
if (!conn.ok) {
|
|
23185
|
+
const message = `cannot list approvals: daemon not reachable (${conn.reason}) \u2014 is \`byok-agent start\` (or the installed service) running?`;
|
|
23186
|
+
error(message);
|
|
23187
|
+
throw new Error(message);
|
|
23188
|
+
}
|
|
23189
|
+
try {
|
|
23190
|
+
const result = await conn.client.request("approvals.list");
|
|
23191
|
+
for (const line of formatApprovalsListLines(result.approvals, now())) log(line);
|
|
23192
|
+
} catch (err) {
|
|
23193
|
+
if (err instanceof ControlError) {
|
|
23194
|
+
error(`approvals list failed: ${err.message}`);
|
|
23195
|
+
} else {
|
|
23196
|
+
error(`approvals list failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
22323
23197
|
}
|
|
22324
23198
|
throw err;
|
|
22325
23199
|
} finally {
|
|
22326
|
-
|
|
23200
|
+
conn.client.close();
|
|
22327
23201
|
}
|
|
22328
23202
|
}
|
|
22329
|
-
|
|
22330
|
-
|
|
22331
|
-
|
|
23203
|
+
|
|
23204
|
+
// src/bin/commands/approve-reject.ts
|
|
23205
|
+
async function runApproveCommand(storeDir, productId, approvalId, deps = {}) {
|
|
23206
|
+
return resolveApproval(storeDir, productId, approvalId, "approve", void 0, deps);
|
|
23207
|
+
}
|
|
23208
|
+
async function runRejectCommand(storeDir, productId, approvalId, reason, deps = {}) {
|
|
23209
|
+
return resolveApproval(storeDir, productId, approvalId, "reject", reason, deps);
|
|
23210
|
+
}
|
|
23211
|
+
async function resolveApproval(storeDir, productId, approvalId, decision, reason, deps) {
|
|
23212
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
23213
|
+
const error = deps.error ?? ((line) => console.error(line));
|
|
23214
|
+
const connectControl = deps.connectControl ?? connectControlClient;
|
|
23215
|
+
const verb = decision === "approve" ? "approved" : "rejected";
|
|
23216
|
+
const conn = await connectControl({ storeDir, productId });
|
|
23217
|
+
if (!conn.ok) {
|
|
23218
|
+
const message = `cannot ${decision} approvalId=${approvalId}: daemon not reachable (${conn.reason}) \u2014 is \`byok-agent start\` (or the installed service) running?`;
|
|
23219
|
+
error(message);
|
|
23220
|
+
throw new Error(message);
|
|
23221
|
+
}
|
|
22332
23222
|
try {
|
|
22333
|
-
|
|
22334
|
-
|
|
22335
|
-
|
|
22336
|
-
|
|
22337
|
-
|
|
22338
|
-
|
|
23223
|
+
await conn.client.request("approvals.resolve", { approvalId, decision, reason });
|
|
23224
|
+
log(`${verb}: approvalId=${approvalId}${reason ? ` reason=${JSON.stringify(reason)}` : ""}`);
|
|
23225
|
+
} catch (err) {
|
|
23226
|
+
if (err instanceof ControlError && err.code === "not_found") {
|
|
23227
|
+
error(`${err.message} \u2014 run \`byok-agent approvals\` to see pending approvalIds`);
|
|
23228
|
+
} else {
|
|
23229
|
+
error(`${decision} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
22339
23230
|
}
|
|
22340
|
-
|
|
22341
|
-
|
|
23231
|
+
throw err;
|
|
23232
|
+
} finally {
|
|
23233
|
+
conn.client.close();
|
|
23234
|
+
}
|
|
23235
|
+
}
|
|
23236
|
+
|
|
23237
|
+
// src/bin/commands/pair.ts
|
|
23238
|
+
async function runPairCommand(config, code, deps = {}) {
|
|
23239
|
+
const log = deps.log ?? ((line) => console.log(line));
|
|
23240
|
+
if (deps.daemon) {
|
|
23241
|
+
const result2 = await deps.daemon.pair(code);
|
|
23242
|
+
log(`paired: deviceId=${result2.deviceId}`);
|
|
23243
|
+
return;
|
|
23244
|
+
}
|
|
23245
|
+
const connectControl = deps.connectControl ?? connectControlClient;
|
|
23246
|
+
const conn = await connectControl({ storeDir: resolveStoreDir(config), productId: config.productId });
|
|
23247
|
+
if (conn.ok) {
|
|
22342
23248
|
try {
|
|
22343
|
-
const
|
|
22344
|
-
|
|
22345
|
-
|
|
22346
|
-
throw new Error(`${inspection.reason}; refusing to quarantine unconfirmed corruption`);
|
|
22347
|
-
}
|
|
22348
|
-
const sourceStat = await source.stat({ bigint: true });
|
|
22349
|
-
if (!sourceStat.isFile()) throw new Error("operational health state is not a regular file; refusing quarantine");
|
|
22350
|
-
const quarantineDir = path14__default.join(resolvedStoreDir, JOURNAL_QUARANTINE_DIRNAME);
|
|
22351
|
-
try {
|
|
22352
|
-
const existing = await promises.lstat(quarantineDir);
|
|
22353
|
-
if (!existing.isDirectory() || existing.isSymbolicLink()) {
|
|
22354
|
-
throw new Error("quarantine path is not a real directory; refusing quarantine");
|
|
22355
|
-
}
|
|
22356
|
-
} catch (err) {
|
|
22357
|
-
if (err.code !== "ENOENT") throw err;
|
|
22358
|
-
await promises.mkdir(quarantineDir, { mode: 448 });
|
|
22359
|
-
}
|
|
22360
|
-
await ensureSecureDir(quarantineDir);
|
|
22361
|
-
const securedQuarantine = await promises.lstat(quarantineDir, { bigint: true });
|
|
22362
|
-
if (!securedQuarantine.isDirectory() || securedQuarantine.isSymbolicLink()) {
|
|
22363
|
-
throw new Error("quarantine path changed during validation; refusing quarantine");
|
|
22364
|
-
}
|
|
22365
|
-
const stamp = (options.clock ?? (() => /* @__PURE__ */ new Date()))().toISOString().replace(/[:.]/g, "-");
|
|
22366
|
-
const evidenceName = `${stamp}-${randomUUID()}-${OPERATIONAL_HEALTH_FILENAME}`;
|
|
22367
|
-
const manifestName = `${evidenceName}.manifest.json`;
|
|
22368
|
-
const hashed = publishQuarantineEvidencePinned(
|
|
22369
|
-
quarantineDir,
|
|
22370
|
-
securedQuarantine,
|
|
22371
|
-
sourcePath,
|
|
22372
|
-
source.fd,
|
|
22373
|
-
sourceStat,
|
|
22374
|
-
evidenceName,
|
|
22375
|
-
manifestName,
|
|
22376
|
-
(options.clock ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
22377
|
-
inspection.reason
|
|
22378
|
-
);
|
|
22379
|
-
return { status: "quarantined", evidenceName, manifestName, sha256: hashed.sha256, sizeBytes: hashed.sizeBytes };
|
|
23249
|
+
const result2 = await conn.client.request("enrollment.pair", { pairingCode: code });
|
|
23250
|
+
log(`paired: deviceId=${result2.deviceId}`);
|
|
23251
|
+
return;
|
|
22380
23252
|
} finally {
|
|
22381
|
-
|
|
23253
|
+
conn.client.close();
|
|
22382
23254
|
}
|
|
22383
|
-
} finally {
|
|
22384
|
-
await owner.release();
|
|
22385
23255
|
}
|
|
23256
|
+
const daemon = createDaemon(config);
|
|
23257
|
+
const result = await daemon.pair(code);
|
|
23258
|
+
log(`paired: deviceId=${result.deviceId}`);
|
|
22386
23259
|
}
|
|
22387
23260
|
|
|
22388
23261
|
// src/bin/commands/doctor.ts
|
|
@@ -22420,6 +23293,29 @@ function formatDoctorLines(snapshot, fixResult) {
|
|
|
22420
23293
|
return lines;
|
|
22421
23294
|
}
|
|
22422
23295
|
async function runDoctorCommand(config, options = {}) {
|
|
23296
|
+
if (options.repair !== void 0) {
|
|
23297
|
+
if (options.fix || options.repair !== "restore-enrollment-metadata") {
|
|
23298
|
+
throw new Error("doctor requires one supported repair action; --fix cannot be combined with --repair");
|
|
23299
|
+
}
|
|
23300
|
+
const log2 = options.log ?? ((line) => console.log(line));
|
|
23301
|
+
try {
|
|
23302
|
+
const repair = await repairDeviceEnrollmentMetadata(config, {
|
|
23303
|
+
confirmed: options.confirmed,
|
|
23304
|
+
expectedDeviceId: options.expectedDeviceId,
|
|
23305
|
+
expectedTenantId: options.expectedTenantId
|
|
23306
|
+
});
|
|
23307
|
+
log2(options.json ? JSON.stringify({ repair }, null, 2) : `repair ${repair.action}: ${repair.status} (${repair.scope})`);
|
|
23308
|
+
return;
|
|
23309
|
+
} catch (error) {
|
|
23310
|
+
if (options.json && error instanceof DeviceMetadataRepairError) {
|
|
23311
|
+
log2(JSON.stringify({ repair: { action: "restore-enrollment-metadata", scope: "device", status: "failed", code: error.code } }, null, 2));
|
|
23312
|
+
}
|
|
23313
|
+
throw error;
|
|
23314
|
+
}
|
|
23315
|
+
}
|
|
23316
|
+
if (options.expectedDeviceId !== void 0 || options.expectedTenantId !== void 0) {
|
|
23317
|
+
throw new Error("expected identity flags require --repair restore-enrollment-metadata");
|
|
23318
|
+
}
|
|
22423
23319
|
if (options.fix && !options.confirmed) throw new DoctorConfirmationRequiredError();
|
|
22424
23320
|
const storeDir = resolveStoreDir(config);
|
|
22425
23321
|
let snapshot = await collectDiagnostics(config, storeDir, options);
|
|
@@ -23412,8 +24308,8 @@ async function runTasksFollowCommand(config, deps) {
|
|
|
23412
24308
|
});
|
|
23413
24309
|
return;
|
|
23414
24310
|
}
|
|
23415
|
-
const
|
|
23416
|
-
await followAuditLog(
|
|
24311
|
+
const path47 = auditLogPath(storeDir);
|
|
24312
|
+
await followAuditLog(path47, (event) => log(formatDaemonEventLine(event)), {
|
|
23417
24313
|
signal: deps.signal,
|
|
23418
24314
|
pollIntervalMs: deps.pollIntervalMs,
|
|
23419
24315
|
fromEnd: true
|
|
@@ -23696,6 +24592,7 @@ function usage() {
|
|
|
23696
24592
|
" byok-agent start [--config <path>] (or BYOK_CONFIG env var)",
|
|
23697
24593
|
" byok-agent status [--config <path>]",
|
|
23698
24594
|
" byok-agent doctor [--json] [--fix --yes] [--config <path>]",
|
|
24595
|
+
" byok-agent doctor --repair restore-enrollment-metadata --expected-device-id <id> --expected-tenant-id <id> --yes [--json] [--config <path>]",
|
|
23699
24596
|
" byok-agent support-bundle --output <path> [--config <path>]",
|
|
23700
24597
|
" byok-agent runtimes [--config <path>]",
|
|
23701
24598
|
" byok-agent tasks [--follow] [--config <path>]",
|
|
@@ -23765,6 +24662,9 @@ async function main() {
|
|
|
23765
24662
|
return runDoctorCommand(config, {
|
|
23766
24663
|
json: hasFlag(rest, "--json"),
|
|
23767
24664
|
fix: hasFlag(rest, "--fix"),
|
|
24665
|
+
repair: hasFlag(rest, "--repair") ? argValue(rest, "--repair") ?? "" : void 0,
|
|
24666
|
+
expectedDeviceId: hasFlag(rest, "--expected-device-id") ? argValue(rest, "--expected-device-id") ?? "" : void 0,
|
|
24667
|
+
expectedTenantId: hasFlag(rest, "--expected-tenant-id") ? argValue(rest, "--expected-tenant-id") ?? "" : void 0,
|
|
23768
24668
|
confirmed: hasFlag(rest, "--yes")
|
|
23769
24669
|
});
|
|
23770
24670
|
}
|