@byok-sdk/client 0.7.0 → 0.8.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/dist/agent-home.d.ts +21 -2
- package/dist/bin/byok-agent.js +473 -250
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/daemon/agent-home-projection-client.d.ts +20 -0
- package/dist/daemon/create-daemon.d.ts +4 -1
- package/dist/daemon/task-runner.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +462 -236
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/bin/byok-agent.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { randomUUID, createHash, randomBytes, timingSafeEqual, createHmac, createPrivateKey, generateKeyPairSync, sign } from 'crypto';
|
|
3
3
|
import { readFileSync, promises, linkSync, fstatSync, lstatSync, unlinkSync, constants, readSync, openSync, writeFileSync, fchmodSync, fsyncSync, closeSync, opendirSync, existsSync, realpathSync, mkdirSync, renameSync, chmodSync, statSync, readdirSync } from 'fs';
|
|
4
|
-
import
|
|
5
|
-
import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, TASK_STATES, AgentEgressPolicySchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentRefSchema, AgentContentReceiptPayloadSchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, AGENT_EGRESS_POLICY_CAPABILITY, parseMessage, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, decodeEnvelope, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
|
|
4
|
+
import path2, { isAbsolute, join } from 'path';
|
|
5
|
+
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, partitionAgentEvents, TASK_TRANSITIONS, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, AGENT_HOME_PROJECTION_CAPABILITY, AGENT_EGRESS_POLICY_CAPABILITY, AGENT_EGRESS_FRESH_SESSION_CAPABILITY, AgentHomeProjectionCompletionRequestSchema, byokAgentHomeProjectionCompletionPath, AgentHomeProjectionReadbackSchema, parseMessage, checkResultDocument, MAX_MESSAGES_PER_BATCH, BYOK_CAPABILITIES_PATH, BYOK_PRESENCE_PATH, RuntimeIdSchema, TERMINAL_INFERENCE_USAGE_MAX_DURATION_MS, TERMINAL_INFERENCE_USAGE_MAX_TOKENS, RESULT_DOCUMENT_MAX_BYTES, decodeEnvelope, BYOK_EVENTS_PATH, BYOK_MESSAGES_PATH, MessagesSendResponseSchema, UnknownMessageTypeError, BYOK_WS_PATH } from '@byok-sdk/protocol';
|
|
6
6
|
import { execFile, spawn } from 'child_process';
|
|
7
7
|
import os from 'os';
|
|
8
8
|
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';
|
|
@@ -14,8 +14,79 @@ import { WebSocket } from 'ws';
|
|
|
14
14
|
import { createRequire } from 'module';
|
|
15
15
|
import { createInterface } from 'readline/promises';
|
|
16
16
|
|
|
17
|
+
var tmpSeq = 0;
|
|
18
|
+
async function atomicWriteFile(filePath, data, options = {}) {
|
|
19
|
+
const tmpPath = `${filePath}.${process.pid}-${tmpSeq++}.tmp`;
|
|
20
|
+
try {
|
|
21
|
+
const handle = await promises.open(tmpPath, "w", options.mode);
|
|
22
|
+
try {
|
|
23
|
+
await handle.writeFile(data);
|
|
24
|
+
if (options.mode !== void 0) {
|
|
25
|
+
await handle.chmod(options.mode);
|
|
26
|
+
}
|
|
27
|
+
if (options.fsync) {
|
|
28
|
+
await handle.sync();
|
|
29
|
+
}
|
|
30
|
+
} finally {
|
|
31
|
+
await handle.close();
|
|
32
|
+
}
|
|
33
|
+
} catch (err) {
|
|
34
|
+
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
35
|
+
});
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
await renameOnto(tmpPath, filePath);
|
|
39
|
+
if (options.mode !== void 0) {
|
|
40
|
+
await promises.chmod(filePath, options.mode);
|
|
41
|
+
}
|
|
42
|
+
if (options.fsync) {
|
|
43
|
+
const target = await promises.open(filePath, process.platform === "win32" ? "r+" : "r");
|
|
44
|
+
try {
|
|
45
|
+
await target.sync();
|
|
46
|
+
} finally {
|
|
47
|
+
await target.close();
|
|
48
|
+
}
|
|
49
|
+
if (process.platform !== "win32") {
|
|
50
|
+
const directory = await promises.open(path2.dirname(filePath), "r");
|
|
51
|
+
try {
|
|
52
|
+
await directory.sync();
|
|
53
|
+
} finally {
|
|
54
|
+
await directory.close();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var RENAME_RETRY_ATTEMPTS = 5;
|
|
60
|
+
var RENAME_RETRY_DELAY_MS = 20;
|
|
61
|
+
function delay(ms) {
|
|
62
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
63
|
+
}
|
|
64
|
+
async function renameOnto(tmpPath, targetPath) {
|
|
65
|
+
for (let attempt = 1; attempt <= RENAME_RETRY_ATTEMPTS; attempt++) {
|
|
66
|
+
try {
|
|
67
|
+
await promises.rename(tmpPath, targetPath);
|
|
68
|
+
return;
|
|
69
|
+
} catch (err) {
|
|
70
|
+
const code = err.code;
|
|
71
|
+
if (code !== "EPERM" && code !== "EEXIST") {
|
|
72
|
+
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
73
|
+
});
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
if (attempt === RENAME_RETRY_ATTEMPTS) {
|
|
77
|
+
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
78
|
+
});
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
await delay(RENAME_RETRY_DELAY_MS * attempt);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/agent-home.ts
|
|
17
87
|
var AGENT_HOME_DIRECTORY = "agents";
|
|
18
88
|
var AGENT_HOME_INTERNAL_DIRECTORY = ".byok";
|
|
89
|
+
var AGENT_HOME_PROJECTION_STATE_FILE = "agent-home-projection.json";
|
|
19
90
|
var AgentHomeError = class extends Error {
|
|
20
91
|
constructor(message) {
|
|
21
92
|
super(message);
|
|
@@ -64,11 +135,11 @@ function validateAgentRef(value) {
|
|
|
64
135
|
return Object.freeze({ agentId: candidate.agentId, profileRevision: candidate.profileRevision });
|
|
65
136
|
}
|
|
66
137
|
function isWithin(root, candidate) {
|
|
67
|
-
const relative =
|
|
68
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
138
|
+
const relative = path2.relative(root, candidate);
|
|
139
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
|
|
69
140
|
}
|
|
70
141
|
function assertAbsolutePath(value, label) {
|
|
71
|
-
if (typeof value !== "string" || value.length === 0 || !
|
|
142
|
+
if (typeof value !== "string" || value.length === 0 || !path2.isAbsolute(value)) {
|
|
72
143
|
throw new AgentHomeResolutionError(`${label} must be an absolute path`);
|
|
73
144
|
}
|
|
74
145
|
if (/[\u0000\r\n]/u.test(value)) {
|
|
@@ -76,7 +147,7 @@ function assertAbsolutePath(value, label) {
|
|
|
76
147
|
}
|
|
77
148
|
}
|
|
78
149
|
async function resolveExistingAncestor(inputPath) {
|
|
79
|
-
let cursor =
|
|
150
|
+
let cursor = path2.resolve(inputPath);
|
|
80
151
|
const tail = [];
|
|
81
152
|
for (; ; ) {
|
|
82
153
|
try {
|
|
@@ -84,9 +155,9 @@ async function resolveExistingAncestor(inputPath) {
|
|
|
84
155
|
} catch (error) {
|
|
85
156
|
const code = error.code;
|
|
86
157
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
87
|
-
const parent =
|
|
158
|
+
const parent = path2.dirname(cursor);
|
|
88
159
|
if (parent === cursor) throw new AgentHomeResolutionError(`no existing ancestor for ${inputPath}`);
|
|
89
|
-
tail.unshift(
|
|
160
|
+
tail.unshift(path2.basename(cursor));
|
|
90
161
|
cursor = parent;
|
|
91
162
|
}
|
|
92
163
|
}
|
|
@@ -95,7 +166,7 @@ async function materializeDirectory(inputPath) {
|
|
|
95
166
|
const { canonical: canonical2, tail } = await resolveExistingAncestor(inputPath);
|
|
96
167
|
let cursor = canonical2;
|
|
97
168
|
for (const component of tail) {
|
|
98
|
-
cursor =
|
|
169
|
+
cursor = path2.join(cursor, component);
|
|
99
170
|
try {
|
|
100
171
|
const stat = await promises.lstat(cursor);
|
|
101
172
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -114,11 +185,11 @@ async function materializeDirectory(inputPath) {
|
|
|
114
185
|
}
|
|
115
186
|
async function ensureDirectoryNoSymlink(root, target) {
|
|
116
187
|
if (!isWithin(root, target)) throw new AgentHomeResolutionError("Agent home is outside hostStorageRoot");
|
|
117
|
-
const relative =
|
|
118
|
-
const components = relative === "" ? [] : relative.split(
|
|
188
|
+
const relative = path2.relative(root, target);
|
|
189
|
+
const components = relative === "" ? [] : relative.split(path2.sep);
|
|
119
190
|
let cursor = root;
|
|
120
191
|
for (const component of components) {
|
|
121
|
-
cursor =
|
|
192
|
+
cursor = path2.join(cursor, component);
|
|
122
193
|
try {
|
|
123
194
|
const stat = await promises.lstat(cursor);
|
|
124
195
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -154,16 +225,16 @@ var AgentHomeLayout = class {
|
|
|
154
225
|
canonicalRoot;
|
|
155
226
|
constructor(hostStorageRoot) {
|
|
156
227
|
assertAbsolutePath(hostStorageRoot, "agentHome.hostStorageRoot");
|
|
157
|
-
this.hostStorageRootInput =
|
|
228
|
+
this.hostStorageRootInput = path2.resolve(hostStorageRoot);
|
|
158
229
|
}
|
|
159
230
|
async resolve(agentRefInput) {
|
|
160
231
|
const agentRef = validateAgentRef(agentRefInput);
|
|
161
232
|
const hostStorageRoot = await this.resolveRoot();
|
|
162
233
|
const agentsRoot = await ensureDirectoryNoSymlink(
|
|
163
234
|
hostStorageRoot,
|
|
164
|
-
|
|
235
|
+
path2.join(hostStorageRoot, AGENT_HOME_DIRECTORY)
|
|
165
236
|
);
|
|
166
|
-
const lexicalHome =
|
|
237
|
+
const lexicalHome = path2.join(agentsRoot, agentRef.agentId);
|
|
167
238
|
const canonicalHome = await ensureDirectoryNoSymlink(agentsRoot, lexicalHome);
|
|
168
239
|
const priorAgentId = this.agentIdByCanonicalHome.get(canonicalHome);
|
|
169
240
|
if (priorAgentId !== void 0 && priorAgentId !== agentRef.agentId) {
|
|
@@ -193,9 +264,9 @@ var AgentHomeLayout = class {
|
|
|
193
264
|
const hostStorageRoot = await this.resolveRoot();
|
|
194
265
|
const agentsRoot = await ensureDirectoryNoSymlink(
|
|
195
266
|
hostStorageRoot,
|
|
196
|
-
|
|
267
|
+
path2.join(hostStorageRoot, AGENT_HOME_DIRECTORY)
|
|
197
268
|
);
|
|
198
|
-
probePath =
|
|
269
|
+
probePath = path2.join(agentsRoot, `.byok-agent-home-preflight-${randomUUID()}`);
|
|
199
270
|
handle = await promises.open(probePath, "wx", 384);
|
|
200
271
|
created = true;
|
|
201
272
|
await handle.sync();
|
|
@@ -226,7 +297,7 @@ var AgentHomeLayout = class {
|
|
|
226
297
|
}
|
|
227
298
|
};
|
|
228
299
|
function stableAgentHomeOwnerId(storeDir, productId) {
|
|
229
|
-
const identity = `${
|
|
300
|
+
const identity = `${path2.resolve(storeDir)}\0${productId}`;
|
|
230
301
|
return `store-product:${createHash("sha256").update(identity).digest("hex")}`;
|
|
231
302
|
}
|
|
232
303
|
function parseLeaseMarker(value, lockPath) {
|
|
@@ -236,7 +307,7 @@ function parseLeaseMarker(value, lockPath) {
|
|
|
236
307
|
} catch {
|
|
237
308
|
throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} is corrupt`);
|
|
238
309
|
}
|
|
239
|
-
if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.ownerId !== "string" || typeof parsed.leaseId !== "string" || typeof parsed.canonicalHome !== "string" || !
|
|
310
|
+
if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.ownerId !== "string" || typeof parsed.leaseId !== "string" || typeof parsed.canonicalHome !== "string" || !path2.isAbsolute(parsed.canonicalHome)) {
|
|
240
311
|
throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} has an invalid shape`);
|
|
241
312
|
}
|
|
242
313
|
let agentRef;
|
|
@@ -246,7 +317,7 @@ function parseLeaseMarker(value, lockPath) {
|
|
|
246
317
|
throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} has an invalid AgentRef`);
|
|
247
318
|
}
|
|
248
319
|
const marker = parsed;
|
|
249
|
-
return { ...marker, agentRef, canonicalHome:
|
|
320
|
+
return { ...marker, agentRef, canonicalHome: path2.resolve(marker.canonicalHome) };
|
|
250
321
|
}
|
|
251
322
|
var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
|
|
252
323
|
static held = /* @__PURE__ */ new Map();
|
|
@@ -268,9 +339,9 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
|
|
|
268
339
|
await ensureDirectoryNoSymlink(resolution.agentsRoot, canonicalHome);
|
|
269
340
|
const internalDir = await ensureDirectoryNoSymlink(
|
|
270
341
|
canonicalHome,
|
|
271
|
-
|
|
342
|
+
path2.join(canonicalHome, AGENT_HOME_INTERNAL_DIRECTORY)
|
|
272
343
|
);
|
|
273
|
-
lockPath =
|
|
344
|
+
lockPath = path2.join(internalDir, "agent-home.lease");
|
|
274
345
|
handle = await this.openLeaseMarker(lockPath, canonicalHome, agentRef.agentId);
|
|
275
346
|
ownsMarker = true;
|
|
276
347
|
const marker = {
|
|
@@ -369,9 +440,73 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
|
|
|
369
440
|
async function initializeAgentHome(resolution) {
|
|
370
441
|
await ensureDirectoryNoSymlink(
|
|
371
442
|
resolution.canonicalHome,
|
|
372
|
-
|
|
443
|
+
path2.join(resolution.canonicalHome, "notes")
|
|
373
444
|
);
|
|
374
|
-
await ensurePreservedFile(
|
|
445
|
+
await ensurePreservedFile(path2.join(resolution.canonicalHome, "MEMORY.md"));
|
|
446
|
+
}
|
|
447
|
+
function projectionStatePath(resolution) {
|
|
448
|
+
return path2.join(
|
|
449
|
+
resolution.canonicalHome,
|
|
450
|
+
AGENT_HOME_INTERNAL_DIRECTORY,
|
|
451
|
+
AGENT_HOME_PROJECTION_STATE_FILE
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
async function readProjectionState(resolution) {
|
|
455
|
+
const filePath = projectionStatePath(resolution);
|
|
456
|
+
try {
|
|
457
|
+
const stat = await promises.lstat(filePath);
|
|
458
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
459
|
+
throw new AgentHomeResolutionError(`Agent projection state is not a regular file: ${filePath}`);
|
|
460
|
+
}
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (error.code === "ENOENT") return void 0;
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
let parsed;
|
|
466
|
+
try {
|
|
467
|
+
parsed = JSON.parse(await promises.readFile(filePath, "utf8"));
|
|
468
|
+
} catch (error) {
|
|
469
|
+
throw new AgentHomeResolutionError(
|
|
470
|
+
`Agent projection state is corrupt: ${error instanceof Error ? error.message : String(error)}`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.requestId !== "string" || typeof parsed.projectionHash !== "string") {
|
|
474
|
+
throw new AgentHomeResolutionError("Agent projection state has an invalid shape");
|
|
475
|
+
}
|
|
476
|
+
const candidate = parsed;
|
|
477
|
+
const agentRef = validateAgentRef(candidate.agentRef);
|
|
478
|
+
if (agentRef.agentId !== resolution.agentRef.agentId) {
|
|
479
|
+
throw new AgentHomeCollisionError("Agent projection state belongs to a different Agent home");
|
|
480
|
+
}
|
|
481
|
+
return Object.freeze({
|
|
482
|
+
version: 1,
|
|
483
|
+
agentRef,
|
|
484
|
+
requestId: candidate.requestId,
|
|
485
|
+
projectionHash: candidate.projectionHash
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
async function writeProjectionState(resolution, payload) {
|
|
489
|
+
const filePath = projectionStatePath(resolution);
|
|
490
|
+
const existing = await promises.lstat(filePath).catch((error) => {
|
|
491
|
+
if (error.code === "ENOENT") return void 0;
|
|
492
|
+
throw error;
|
|
493
|
+
});
|
|
494
|
+
if (existing !== void 0 && (!existing.isFile() || existing.isSymbolicLink())) {
|
|
495
|
+
throw new AgentHomeResolutionError(`Agent projection state is not a regular file: ${filePath}`);
|
|
496
|
+
}
|
|
497
|
+
const state = {
|
|
498
|
+
version: 1,
|
|
499
|
+
agentRef: payload.agentRef,
|
|
500
|
+
requestId: payload.requestId,
|
|
501
|
+
projectionHash: payload.projectionHash
|
|
502
|
+
};
|
|
503
|
+
await atomicWriteFile(filePath, `${JSON.stringify(state)}
|
|
504
|
+
`, { mode: 384, fsync: true });
|
|
505
|
+
}
|
|
506
|
+
function compareProjectionRevision(left, right) {
|
|
507
|
+
const leftRevision = BigInt(left);
|
|
508
|
+
const rightRevision = BigInt(right);
|
|
509
|
+
return leftRevision < rightRevision ? -1 : leftRevision > rightRevision ? 1 : 0;
|
|
375
510
|
}
|
|
376
511
|
var AgentHomeManager = class {
|
|
377
512
|
layout;
|
|
@@ -407,12 +542,59 @@ var AgentHomeManager = class {
|
|
|
407
542
|
async initialize(binding) {
|
|
408
543
|
const { resolution, lease } = binding;
|
|
409
544
|
await initializeAgentHome(resolution);
|
|
410
|
-
|
|
545
|
+
const prepare = this.projection?.prepare;
|
|
546
|
+
if (prepare !== void 0) await prepare({ ...resolution, cwd: lease.cwd });
|
|
411
547
|
if (await promises.realpath(resolution.homeDir) !== resolution.canonicalHome) {
|
|
412
548
|
throw new AgentHomeResolutionError("Agent projection changed the canonical home path");
|
|
413
549
|
}
|
|
414
550
|
await initializeAgentHome(resolution);
|
|
415
551
|
}
|
|
552
|
+
supportsTaskFreeProjection() {
|
|
553
|
+
return this.projection?.apply !== void 0;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Apply one task-free projection under the same canonical-home writer lease
|
|
557
|
+
* used by Agent execution. Only a successful host hook followed by the
|
|
558
|
+
* SDK-owned fsynced ordering record can return `applied`.
|
|
559
|
+
*/
|
|
560
|
+
async project(input) {
|
|
561
|
+
const payload = AgentHomeProjectionPayloadSchema.parse(input);
|
|
562
|
+
const binding = await this.acquire(payload.agentRef);
|
|
563
|
+
try {
|
|
564
|
+
const { resolution, lease } = binding;
|
|
565
|
+
await initializeAgentHome(resolution);
|
|
566
|
+
const current = await readProjectionState(resolution);
|
|
567
|
+
if (current !== void 0) {
|
|
568
|
+
const order = compareProjectionRevision(
|
|
569
|
+
payload.agentRef.profileRevision,
|
|
570
|
+
current.agentRef.profileRevision
|
|
571
|
+
);
|
|
572
|
+
if (order < 0) return "stale";
|
|
573
|
+
if (order === 0) {
|
|
574
|
+
return payload.projectionHash === current.projectionHash ? "idempotent" : "conflict";
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const apply = this.projection?.apply;
|
|
578
|
+
if (apply === void 0) {
|
|
579
|
+
throw new AgentHomeError("task-free Agent-home projection is not configured");
|
|
580
|
+
}
|
|
581
|
+
await apply({
|
|
582
|
+
...resolution,
|
|
583
|
+
cwd: lease.cwd,
|
|
584
|
+
requestId: payload.requestId,
|
|
585
|
+
projectionHash: payload.projectionHash,
|
|
586
|
+
projection: payload.projection
|
|
587
|
+
});
|
|
588
|
+
if (await promises.realpath(resolution.homeDir) !== resolution.canonicalHome) {
|
|
589
|
+
throw new AgentHomeResolutionError("Agent projection changed the canonical home path");
|
|
590
|
+
}
|
|
591
|
+
await initializeAgentHome(resolution);
|
|
592
|
+
await writeProjectionState(resolution, payload);
|
|
593
|
+
return "applied";
|
|
594
|
+
} finally {
|
|
595
|
+
await binding.lease.release();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
416
598
|
};
|
|
417
599
|
var AgentSessionHandoffStoreError = class extends Error {
|
|
418
600
|
constructor(message) {
|
|
@@ -453,7 +635,7 @@ function parseTaskTerminalEntry(value) {
|
|
|
453
635
|
assertNonEmptyString(value.terminalReason, "taskTerminal.terminalReason");
|
|
454
636
|
assertNonEmptyString(value.updatedAt, "taskTerminal.updatedAt");
|
|
455
637
|
if (value.sessionRef !== void 0) assertNonEmptyString(value.sessionRef, "taskTerminal.sessionRef");
|
|
456
|
-
if (typeof value.cwd !== "string" || !
|
|
638
|
+
if (typeof value.cwd !== "string" || !path2.isAbsolute(value.cwd)) {
|
|
457
639
|
throw new AgentSessionHandoffCorruptError("taskTerminal.cwd must be an absolute path");
|
|
458
640
|
}
|
|
459
641
|
if (value.terminalCause !== "failed") {
|
|
@@ -466,7 +648,7 @@ function parseTaskTerminalEntry(value) {
|
|
|
466
648
|
agentRef,
|
|
467
649
|
taskId: value.taskId,
|
|
468
650
|
runtimeId: value.runtimeId,
|
|
469
|
-
cwd:
|
|
651
|
+
cwd: path2.resolve(value.cwd),
|
|
470
652
|
leaseId: value.leaseId,
|
|
471
653
|
...value.sessionRef === void 0 ? {} : { sessionRef: value.sessionRef },
|
|
472
654
|
terminalCause: "failed",
|
|
@@ -496,7 +678,7 @@ function parseEntry(value) {
|
|
|
496
678
|
assertNonEmptyString(value.runtimeId, "handoff.runtimeId");
|
|
497
679
|
assertNonEmptyString(value.leaseId, "handoff.leaseId");
|
|
498
680
|
assertNonEmptyString(value.updatedAt, "handoff.updatedAt");
|
|
499
|
-
if (typeof value.cwd !== "string" || !
|
|
681
|
+
if (typeof value.cwd !== "string" || !path2.isAbsolute(value.cwd)) {
|
|
500
682
|
throw new AgentSessionHandoffCorruptError("handoff.cwd must be an absolute path");
|
|
501
683
|
}
|
|
502
684
|
if (Number.isNaN(Date.parse(value.updatedAt))) {
|
|
@@ -513,7 +695,7 @@ function parseEntry(value) {
|
|
|
513
695
|
taskId: value.taskId,
|
|
514
696
|
sessionRef: value.sessionRef,
|
|
515
697
|
runtimeId: value.runtimeId,
|
|
516
|
-
cwd:
|
|
698
|
+
cwd: path2.resolve(value.cwd),
|
|
517
699
|
leaseId: value.leaseId,
|
|
518
700
|
...value.terminalCause === void 0 ? {} : { terminalCause: value.terminalCause },
|
|
519
701
|
...value.terminalReason === void 0 ? {} : { terminalReason: value.terminalReason },
|
|
@@ -524,10 +706,10 @@ function sameRef(left, right) {
|
|
|
524
706
|
return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
|
|
525
707
|
}
|
|
526
708
|
function sameMatch(entry, expected) {
|
|
527
|
-
return sameRef(entry.agentRef, expected.agentRef) && entry.sessionRef === expected.sessionRef && entry.runtimeId === expected.runtimeId && entry.cwd ===
|
|
709
|
+
return sameRef(entry.agentRef, expected.agentRef) && entry.sessionRef === expected.sessionRef && entry.runtimeId === expected.runtimeId && entry.cwd === path2.resolve(expected.cwd);
|
|
528
710
|
}
|
|
529
711
|
function sameTaskTerminalMatch(entry, expected) {
|
|
530
|
-
return sameRef(entry.agentRef, expected.agentRef) && entry.taskId === expected.taskId && entry.runtimeId === expected.runtimeId && entry.cwd ===
|
|
712
|
+
return sameRef(entry.agentRef, expected.agentRef) && entry.taskId === expected.taskId && entry.runtimeId === expected.runtimeId && entry.cwd === path2.resolve(expected.cwd);
|
|
531
713
|
}
|
|
532
714
|
function sessionFileName(runtimeId, sessionRef) {
|
|
533
715
|
const digest2 = createHash("sha256").update(sessionRef, "utf8").digest("hex");
|
|
@@ -540,13 +722,13 @@ function taskTerminalFileName(runtimeId, taskId) {
|
|
|
540
722
|
return `${runtime}-task-${digest2}.jsonl`;
|
|
541
723
|
}
|
|
542
724
|
async function evidenceDirectory(cwdInput) {
|
|
543
|
-
if (!
|
|
725
|
+
if (!path2.isAbsolute(cwdInput)) {
|
|
544
726
|
throw new AgentSessionHandoffStoreError("Agent session cwd must be absolute");
|
|
545
727
|
}
|
|
546
728
|
const cwd = await promises.realpath(cwdInput);
|
|
547
729
|
let cursor = cwd;
|
|
548
730
|
for (const component of [".byok", "runtime-sessions"]) {
|
|
549
|
-
cursor =
|
|
731
|
+
cursor = path2.join(cursor, component);
|
|
550
732
|
try {
|
|
551
733
|
const stat = await promises.lstat(cursor);
|
|
552
734
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -558,8 +740,8 @@ async function evidenceDirectory(cwdInput) {
|
|
|
558
740
|
}
|
|
559
741
|
}
|
|
560
742
|
const canonical2 = await promises.realpath(cursor);
|
|
561
|
-
const relative =
|
|
562
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
743
|
+
const relative = path2.relative(cwd, canonical2);
|
|
744
|
+
if (relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative)) {
|
|
563
745
|
throw new AgentSessionHandoffStoreError("Agent session evidence path escaped the canonical Agent home");
|
|
564
746
|
}
|
|
565
747
|
return canonical2;
|
|
@@ -605,7 +787,7 @@ var AgentSessionHandoffStore = class {
|
|
|
605
787
|
taskId: input.taskId,
|
|
606
788
|
sessionRef: input.sessionRef,
|
|
607
789
|
runtimeId: input.runtimeId,
|
|
608
|
-
cwd:
|
|
790
|
+
cwd: path2.resolve(input.cwd),
|
|
609
791
|
leaseId: input.leaseId,
|
|
610
792
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
611
793
|
});
|
|
@@ -648,7 +830,7 @@ var AgentSessionHandoffStore = class {
|
|
|
648
830
|
agentRef: validateAgentRef(input.agentRef),
|
|
649
831
|
taskId: input.taskId,
|
|
650
832
|
runtimeId: input.runtimeId,
|
|
651
|
-
cwd:
|
|
833
|
+
cwd: path2.resolve(input.cwd)
|
|
652
834
|
};
|
|
653
835
|
const filePath = await this.taskTerminalFilePath(expected);
|
|
654
836
|
return this.enqueue(filePath, async () => {
|
|
@@ -675,7 +857,7 @@ var AgentSessionHandoffStore = class {
|
|
|
675
857
|
agentRef: validateAgentRef(expectedInput.agentRef),
|
|
676
858
|
taskId: expectedInput.taskId,
|
|
677
859
|
runtimeId: expectedInput.runtimeId,
|
|
678
|
-
cwd:
|
|
860
|
+
cwd: path2.resolve(expectedInput.cwd)
|
|
679
861
|
};
|
|
680
862
|
const filePath = await this.taskTerminalFilePath(expected);
|
|
681
863
|
return this.enqueue(filePath, async () => {
|
|
@@ -693,14 +875,14 @@ var AgentSessionHandoffStore = class {
|
|
|
693
875
|
assertNonEmptyString(match.sessionRef, "handoff.sessionRef");
|
|
694
876
|
assertNonEmptyString(match.runtimeId, "handoff.runtimeId");
|
|
695
877
|
const directory = await evidenceDirectory(match.cwd);
|
|
696
|
-
return
|
|
878
|
+
return path2.join(directory, sessionFileName(match.runtimeId, match.sessionRef));
|
|
697
879
|
}
|
|
698
880
|
async taskTerminalFilePath(match) {
|
|
699
881
|
validateAgentRef(match.agentRef);
|
|
700
882
|
assertNonEmptyString(match.taskId, "taskTerminal.taskId");
|
|
701
883
|
assertNonEmptyString(match.runtimeId, "taskTerminal.runtimeId");
|
|
702
884
|
const directory = await evidenceDirectory(match.cwd);
|
|
703
|
-
return
|
|
885
|
+
return path2.join(directory, taskTerminalFileName(match.runtimeId, match.taskId));
|
|
704
886
|
}
|
|
705
887
|
enqueue(key, task) {
|
|
706
888
|
const previous = this.queues.get(key) ?? Promise.resolve();
|
|
@@ -1011,7 +1193,7 @@ function gitEnvironment(readOnly) {
|
|
|
1011
1193
|
return env;
|
|
1012
1194
|
}
|
|
1013
1195
|
function stableGitWorkspaceOwnerId(storeDir, productId) {
|
|
1014
|
-
const identity = `${
|
|
1196
|
+
const identity = `${path2.resolve(storeDir)}\\0${productId}`;
|
|
1015
1197
|
return `store-product:${createHash("sha256").update(identity).digest("hex")}`;
|
|
1016
1198
|
}
|
|
1017
1199
|
var GUIDANCE = [
|
|
@@ -1023,11 +1205,11 @@ var GUIDANCE = [
|
|
|
1023
1205
|
"Leave incomplete work visible for recovery."
|
|
1024
1206
|
].join("\n");
|
|
1025
1207
|
function canonical(value) {
|
|
1026
|
-
return
|
|
1208
|
+
return path2.resolve(value);
|
|
1027
1209
|
}
|
|
1028
1210
|
function isContained(root, candidate) {
|
|
1029
|
-
const relative =
|
|
1030
|
-
return relative === "" || !relative.startsWith(`..${
|
|
1211
|
+
const relative = path2.relative(root, candidate);
|
|
1212
|
+
return relative === "" || !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
|
|
1031
1213
|
}
|
|
1032
1214
|
function bounded(value, max) {
|
|
1033
1215
|
return Buffer.byteLength(value, "utf8") <= max ? value : value.slice(0, max);
|
|
@@ -1132,7 +1314,7 @@ var GitWorkspaceManager = class {
|
|
|
1132
1314
|
await this.ensureOwnerMarker();
|
|
1133
1315
|
}
|
|
1134
1316
|
async ensureOwnerMarker() {
|
|
1135
|
-
const markerPath =
|
|
1317
|
+
const markerPath = path2.join(this.workspaceRoot, OWNER_MARKER);
|
|
1136
1318
|
let existing;
|
|
1137
1319
|
try {
|
|
1138
1320
|
existing = JSON.parse(await promises.readFile(markerPath, "utf8"));
|
|
@@ -1291,7 +1473,7 @@ ${instruction}`;
|
|
|
1291
1473
|
if (error instanceof GitWorkspaceError || code !== "ENOENT" && code !== "ENOTDIR") {
|
|
1292
1474
|
throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
|
|
1293
1475
|
}
|
|
1294
|
-
const parent =
|
|
1476
|
+
const parent = path2.dirname(current);
|
|
1295
1477
|
if (parent === current) {
|
|
1296
1478
|
throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
|
|
1297
1479
|
}
|
|
@@ -1316,74 +1498,6 @@ ${instruction}`;
|
|
|
1316
1498
|
function prependGitWorkspaceGuidance(instruction) {
|
|
1317
1499
|
return GitWorkspaceManager.prependGuidance(instruction);
|
|
1318
1500
|
}
|
|
1319
|
-
var tmpSeq = 0;
|
|
1320
|
-
async function atomicWriteFile(filePath, data, options = {}) {
|
|
1321
|
-
const tmpPath = `${filePath}.${process.pid}-${tmpSeq++}.tmp`;
|
|
1322
|
-
try {
|
|
1323
|
-
const handle = await promises.open(tmpPath, "w", options.mode);
|
|
1324
|
-
try {
|
|
1325
|
-
await handle.writeFile(data);
|
|
1326
|
-
if (options.mode !== void 0) {
|
|
1327
|
-
await handle.chmod(options.mode);
|
|
1328
|
-
}
|
|
1329
|
-
if (options.fsync) {
|
|
1330
|
-
await handle.sync();
|
|
1331
|
-
}
|
|
1332
|
-
} finally {
|
|
1333
|
-
await handle.close();
|
|
1334
|
-
}
|
|
1335
|
-
} catch (err) {
|
|
1336
|
-
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
1337
|
-
});
|
|
1338
|
-
throw err;
|
|
1339
|
-
}
|
|
1340
|
-
await renameOnto(tmpPath, filePath);
|
|
1341
|
-
if (options.mode !== void 0) {
|
|
1342
|
-
await promises.chmod(filePath, options.mode);
|
|
1343
|
-
}
|
|
1344
|
-
if (options.fsync) {
|
|
1345
|
-
const target = await promises.open(filePath, process.platform === "win32" ? "r+" : "r");
|
|
1346
|
-
try {
|
|
1347
|
-
await target.sync();
|
|
1348
|
-
} finally {
|
|
1349
|
-
await target.close();
|
|
1350
|
-
}
|
|
1351
|
-
if (process.platform !== "win32") {
|
|
1352
|
-
const directory = await promises.open(path.dirname(filePath), "r");
|
|
1353
|
-
try {
|
|
1354
|
-
await directory.sync();
|
|
1355
|
-
} finally {
|
|
1356
|
-
await directory.close();
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
var RENAME_RETRY_ATTEMPTS = 5;
|
|
1362
|
-
var RENAME_RETRY_DELAY_MS = 20;
|
|
1363
|
-
function delay(ms) {
|
|
1364
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1365
|
-
}
|
|
1366
|
-
async function renameOnto(tmpPath, targetPath) {
|
|
1367
|
-
for (let attempt = 1; attempt <= RENAME_RETRY_ATTEMPTS; attempt++) {
|
|
1368
|
-
try {
|
|
1369
|
-
await promises.rename(tmpPath, targetPath);
|
|
1370
|
-
return;
|
|
1371
|
-
} catch (err) {
|
|
1372
|
-
const code = err.code;
|
|
1373
|
-
if (code !== "EPERM" && code !== "EEXIST") {
|
|
1374
|
-
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
1375
|
-
});
|
|
1376
|
-
throw err;
|
|
1377
|
-
}
|
|
1378
|
-
if (attempt === RENAME_RETRY_ATTEMPTS) {
|
|
1379
|
-
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
1380
|
-
});
|
|
1381
|
-
throw err;
|
|
1382
|
-
}
|
|
1383
|
-
await delay(RENAME_RETRY_DELAY_MS * attempt);
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
1501
|
var defaultRunner = (command, args) => new Promise((resolve, reject) => {
|
|
1388
1502
|
execFile(command, args, (error, stdout, stderr) => {
|
|
1389
1503
|
if (error && typeof error.code !== "number") {
|
|
@@ -1510,7 +1624,7 @@ function isProtected(record) {
|
|
|
1510
1624
|
var GitWorkspaceStore = class {
|
|
1511
1625
|
constructor(storeDir, options = {}) {
|
|
1512
1626
|
this.storeDir = storeDir;
|
|
1513
|
-
this.filePath =
|
|
1627
|
+
this.filePath = path2.join(storeDir, FILE_NAME);
|
|
1514
1628
|
this.maxRecords = Math.max(1, Math.floor(options.maxRecords ?? MAX_RECORDS));
|
|
1515
1629
|
}
|
|
1516
1630
|
storeDir;
|
|
@@ -1645,7 +1759,7 @@ var GitWorkspaceStore = class {
|
|
|
1645
1759
|
var BYOK_PI_MCP_CONFIG_PATH = "BYOK_PI_MCP_CONFIG_PATH";
|
|
1646
1760
|
var PI_PACKAGE_NAME = "@earendil-works/pi-coding-agent";
|
|
1647
1761
|
function readPackageJson(dir) {
|
|
1648
|
-
const candidate =
|
|
1762
|
+
const candidate = path2.join(dir, "package.json");
|
|
1649
1763
|
if (!existsSync(candidate)) return void 0;
|
|
1650
1764
|
try {
|
|
1651
1765
|
return JSON.parse(readFileSync(candidate, "utf8"));
|
|
@@ -1660,17 +1774,17 @@ function resolvePiBin() {
|
|
|
1660
1774
|
}
|
|
1661
1775
|
try {
|
|
1662
1776
|
const mainEntryUrl = import.meta.resolve(PI_PACKAGE_NAME);
|
|
1663
|
-
let dir =
|
|
1777
|
+
let dir = path2.dirname(fileURLToPath(mainEntryUrl));
|
|
1664
1778
|
for (let depth = 0; depth < 6; depth++) {
|
|
1665
1779
|
const pkg = readPackageJson(dir);
|
|
1666
1780
|
if (pkg?.name === PI_PACKAGE_NAME) {
|
|
1667
1781
|
const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.pi;
|
|
1668
1782
|
if (binRel) {
|
|
1669
|
-
return { command:
|
|
1783
|
+
return { command: path2.join(dir, binRel), source: "package" };
|
|
1670
1784
|
}
|
|
1671
1785
|
break;
|
|
1672
1786
|
}
|
|
1673
|
-
const parent =
|
|
1787
|
+
const parent = path2.dirname(dir);
|
|
1674
1788
|
if (parent === dir) break;
|
|
1675
1789
|
dir = parent;
|
|
1676
1790
|
}
|
|
@@ -1688,7 +1802,7 @@ function resolvePiExtensions() {
|
|
|
1688
1802
|
const clientManifest = fileURLToPath(import.meta.resolve("@byok-sdk/client/package.json"));
|
|
1689
1803
|
return {
|
|
1690
1804
|
webAccess: fileURLToPath(import.meta.resolve("pi-web-access/index.ts")),
|
|
1691
|
-
mcpAdapter:
|
|
1805
|
+
mcpAdapter: path2.join(path2.dirname(clientManifest), "dist", "adapters", "pi", "mcp-extension.js")
|
|
1692
1806
|
};
|
|
1693
1807
|
}
|
|
1694
1808
|
|
|
@@ -2614,10 +2728,10 @@ var PiAdapter = class {
|
|
|
2614
2728
|
const taskMcpServers = startInput.mcpServers ?? {};
|
|
2615
2729
|
const hasMcpServers = Object.keys(taskMcpServers).length > 0;
|
|
2616
2730
|
if (hasMcpServers) {
|
|
2617
|
-
mcpConfigDir = await promises.mkdtemp(
|
|
2731
|
+
mcpConfigDir = await promises.mkdtemp(path2.join(os.tmpdir(), "byok-pi-mcp-"));
|
|
2618
2732
|
await promises.chmod(mcpConfigDir, 448).catch(() => {
|
|
2619
2733
|
});
|
|
2620
|
-
const mcpConfigPath =
|
|
2734
|
+
const mcpConfigPath = path2.join(mcpConfigDir, "mcp-config.json");
|
|
2621
2735
|
await promises.writeFile(mcpConfigPath, JSON.stringify({ mcpServers: taskMcpServers }), { mode: 384 });
|
|
2622
2736
|
runtimeEnv = { ...runtimeEnv, [BYOK_PI_MCP_CONFIG_PATH]: mcpConfigPath };
|
|
2623
2737
|
}
|
|
@@ -2848,7 +2962,7 @@ function resolveApprovalMcpBin() {
|
|
|
2848
2962
|
if (override) {
|
|
2849
2963
|
return { command: override, args: [], source: "env" };
|
|
2850
2964
|
}
|
|
2851
|
-
const distBin =
|
|
2965
|
+
const distBin = path2.join(path2.dirname(fileURLToPath(import.meta.url)), "bin", "byok-approval-mcp.js");
|
|
2852
2966
|
return { command: process.execPath, args: [distBin], source: "dist" };
|
|
2853
2967
|
}
|
|
2854
2968
|
|
|
@@ -2939,7 +3053,7 @@ var EXTENSION_CONTENT_TYPES = {
|
|
|
2939
3053
|
".yml": "application/yaml"
|
|
2940
3054
|
};
|
|
2941
3055
|
function guessContentType(filePath) {
|
|
2942
|
-
const ext =
|
|
3056
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
2943
3057
|
return EXTENSION_CONTENT_TYPES[ext] ?? "application/octet-stream";
|
|
2944
3058
|
}
|
|
2945
3059
|
function mapAssistant(msg, correlation) {
|
|
@@ -3023,11 +3137,11 @@ function tryBuildArtifactEvent(msg, workspaceDir) {
|
|
|
3023
3137
|
const filePath = toolUseResult && typeof toolUseResult.filePath === "string" ? toolUseResult.filePath : void 0;
|
|
3024
3138
|
if (!filePath) return void 0;
|
|
3025
3139
|
const realWorkspaceDir = tryRealpath(workspaceDir) ?? workspaceDir;
|
|
3026
|
-
const fileDir =
|
|
3140
|
+
const fileDir = path2.dirname(filePath);
|
|
3027
3141
|
const realFileDir = tryRealpath(fileDir) ?? fileDir;
|
|
3028
|
-
const realFilePath =
|
|
3029
|
-
const relative =
|
|
3030
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
3142
|
+
const realFilePath = path2.join(realFileDir, path2.basename(filePath));
|
|
3143
|
+
const relative = path2.relative(realWorkspaceDir, realFilePath);
|
|
3144
|
+
if (relative === "" || relative.startsWith("..") || path2.isAbsolute(relative)) {
|
|
3031
3145
|
return void 0;
|
|
3032
3146
|
}
|
|
3033
3147
|
return { type: "artifact", name: relative, contentType: guessContentType(filePath) };
|
|
@@ -3421,10 +3535,10 @@ var ClaudeAdapter = class {
|
|
|
3421
3535
|
});
|
|
3422
3536
|
}
|
|
3423
3537
|
if (needsMcpConfig) {
|
|
3424
|
-
mcpConfigDir = await promises.mkdtemp(
|
|
3538
|
+
mcpConfigDir = await promises.mkdtemp(path2.join(os.tmpdir(), "byok-mcp-"));
|
|
3425
3539
|
await promises.chmod(mcpConfigDir, 448).catch(() => {
|
|
3426
3540
|
});
|
|
3427
|
-
const mcpConfigPath =
|
|
3541
|
+
const mcpConfigPath = path2.join(mcpConfigDir, "mcp-config.json");
|
|
3428
3542
|
const mcpServers = { ...taskMcpServers };
|
|
3429
3543
|
if (mapping.needsApprovalMcp) {
|
|
3430
3544
|
const approvalChannel = startInput.approvalChannel;
|
|
@@ -3910,8 +4024,8 @@ function extractArtifactEvents(changes, workspaceDir) {
|
|
|
3910
4024
|
const absolutePath = typeof change.path === "string" ? change.path : void 0;
|
|
3911
4025
|
const kind = typeof change.kind === "string" ? change.kind : void 0;
|
|
3912
4026
|
if (!absolutePath || kind === "delete") continue;
|
|
3913
|
-
const relative =
|
|
3914
|
-
if (relative.length === 0 || relative.startsWith("..") ||
|
|
4027
|
+
const relative = path2.relative(workspaceDir, absolutePath);
|
|
4028
|
+
if (relative.length === 0 || relative.startsWith("..") || path2.isAbsolute(relative)) continue;
|
|
3915
4029
|
events.push({ type: "artifact", name: relative, contentType: guessContentType2(relative) });
|
|
3916
4030
|
}
|
|
3917
4031
|
return events;
|
|
@@ -3932,7 +4046,7 @@ var CONTENT_TYPE_BY_EXTENSION = {
|
|
|
3932
4046
|
".csv": "text/csv"
|
|
3933
4047
|
};
|
|
3934
4048
|
function guessContentType2(relativePath) {
|
|
3935
|
-
return CONTENT_TYPE_BY_EXTENSION[
|
|
4049
|
+
return CONTENT_TYPE_BY_EXTENSION[path2.extname(relativePath).toLowerCase()] ?? "application/octet-stream";
|
|
3936
4050
|
}
|
|
3937
4051
|
function extractErrorMessage(rawError) {
|
|
3938
4052
|
if (typeof rawError === "string") return rawError;
|
|
@@ -4788,12 +4902,12 @@ var DeviceStore = class _DeviceStore {
|
|
|
4788
4902
|
*/
|
|
4789
4903
|
constructor(storeDir, secureDirOptions) {
|
|
4790
4904
|
this.secureDirOptions = secureDirOptions;
|
|
4791
|
-
this.filePath =
|
|
4905
|
+
this.filePath = path2.join(storeDir, "device.json");
|
|
4792
4906
|
}
|
|
4793
4907
|
secureDirOptions;
|
|
4794
4908
|
filePath;
|
|
4795
4909
|
static defaultDir(productId) {
|
|
4796
|
-
return
|
|
4910
|
+
return path2.join(os.homedir(), ".byok", productId);
|
|
4797
4911
|
}
|
|
4798
4912
|
/**
|
|
4799
4913
|
* Resolve the one store pathname every daemon/CLI component must share.
|
|
@@ -4802,7 +4916,7 @@ var DeviceStore = class _DeviceStore {
|
|
|
4802
4916
|
* cwd to pin a quarantine directory inode.
|
|
4803
4917
|
*/
|
|
4804
4918
|
static resolveDir(productId, configured) {
|
|
4805
|
-
return
|
|
4919
|
+
return path2.resolve(configured ?? _DeviceStore.defaultDir(productId));
|
|
4806
4920
|
}
|
|
4807
4921
|
async load() {
|
|
4808
4922
|
const opened = await this.openBounded();
|
|
@@ -4844,7 +4958,7 @@ var DeviceStore = class _DeviceStore {
|
|
|
4844
4958
|
}
|
|
4845
4959
|
async save(record) {
|
|
4846
4960
|
assertDeviceRecord(record);
|
|
4847
|
-
const storeDir =
|
|
4961
|
+
const storeDir = path2.dirname(this.filePath);
|
|
4848
4962
|
await ensureSecureDir(storeDir, this.secureDirOptions);
|
|
4849
4963
|
await atomicWriteFile(this.filePath, JSON.stringify(record, null, 2), { mode: 384 });
|
|
4850
4964
|
}
|
|
@@ -5498,19 +5612,19 @@ function shortHash(input) {
|
|
|
5498
5612
|
return createHash("sha256").update(input, "utf8").digest("hex").slice(0, 16);
|
|
5499
5613
|
}
|
|
5500
5614
|
function controlSocketPath(storeDir) {
|
|
5501
|
-
const candidate =
|
|
5615
|
+
const candidate = path2.join(storeDir, "control.sock");
|
|
5502
5616
|
if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT) return candidate;
|
|
5503
|
-
return
|
|
5617
|
+
return path2.join(CONTROL_SOCKET_FALLBACK_ROOT, `byok-${shortHash(storeDir)}`, "sock");
|
|
5504
5618
|
}
|
|
5505
5619
|
function controlPipeName(productId, storeDir) {
|
|
5506
|
-
const id = shortHash(`${productId}|${
|
|
5620
|
+
const id = shortHash(`${productId}|${path2.resolve(storeDir)}`);
|
|
5507
5621
|
return `\\\\.\\pipe\\byok-${id}`;
|
|
5508
5622
|
}
|
|
5509
5623
|
function controlEndpointPath(productId, storeDir, platform = process.platform) {
|
|
5510
5624
|
return platform === "win32" ? controlPipeName(productId, storeDir) : controlSocketPath(storeDir);
|
|
5511
5625
|
}
|
|
5512
5626
|
function controlTokenPath(storeDir) {
|
|
5513
|
-
return
|
|
5627
|
+
return path2.join(storeDir, "control.token");
|
|
5514
5628
|
}
|
|
5515
5629
|
var SERVER_PROOF_LABEL = "byok-control-server|";
|
|
5516
5630
|
var CLIENT_AUTH_LABEL = "byok-control-client|";
|
|
@@ -5689,7 +5803,7 @@ async function assertOwnedPrivateDir(dir) {
|
|
|
5689
5803
|
}
|
|
5690
5804
|
async function bindControlEndpoint(server, endpoint) {
|
|
5691
5805
|
if (process.platform !== "win32") {
|
|
5692
|
-
const endpointDir =
|
|
5806
|
+
const endpointDir = path2.dirname(endpoint);
|
|
5693
5807
|
await promises.mkdir(endpointDir, { recursive: true, mode: 448 });
|
|
5694
5808
|
await promises.chmod(endpointDir, 448).catch(() => {
|
|
5695
5809
|
});
|
|
@@ -6588,7 +6702,7 @@ function toBytes(data, _isBinary) {
|
|
|
6588
6702
|
|
|
6589
6703
|
// src/daemon/connection-manager.ts
|
|
6590
6704
|
function isCursorEnvelopeType(type) {
|
|
6591
|
-
return type.startsWith("task.") || type === "agent.egress.ack" || type === "agent.content.read";
|
|
6705
|
+
return type.startsWith("task.") || type === "agent.egress.ack" || type === "agent.content.read" || type === "agent.home.projection";
|
|
6592
6706
|
}
|
|
6593
6707
|
var ConnectionManager = class {
|
|
6594
6708
|
constructor(opts) {
|
|
@@ -7103,6 +7217,10 @@ var ConnectionManager = class {
|
|
|
7103
7217
|
async process(envelope, tracked) {
|
|
7104
7218
|
const seq = tracked ? envelope.seq : void 0;
|
|
7105
7219
|
try {
|
|
7220
|
+
if (tracked && this.cursor === void 0) {
|
|
7221
|
+
await this.opts.cursorStore.save(this.opts.serverUrl, this.opts.deviceId, 0);
|
|
7222
|
+
this.cursor = 0;
|
|
7223
|
+
}
|
|
7106
7224
|
await this.opts.onEnvelope(envelope);
|
|
7107
7225
|
if (!tracked) return;
|
|
7108
7226
|
this.processedSeqs.add(seq);
|
|
@@ -7332,7 +7450,7 @@ function sameFileState2(left, right) {
|
|
|
7332
7450
|
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
7333
7451
|
}
|
|
7334
7452
|
async function openOperationalHealthFile(storeDir) {
|
|
7335
|
-
const filePath =
|
|
7453
|
+
const filePath = path2.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
|
|
7336
7454
|
let namedBefore;
|
|
7337
7455
|
try {
|
|
7338
7456
|
namedBefore = await promises.lstat(filePath, { bigint: true });
|
|
@@ -7374,7 +7492,7 @@ var OperationalHealthTracker = class {
|
|
|
7374
7492
|
#writeTail = Promise.resolve();
|
|
7375
7493
|
#started = false;
|
|
7376
7494
|
constructor(storeDir, options = {}) {
|
|
7377
|
-
this.#filePath =
|
|
7495
|
+
this.#filePath = path2.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
|
|
7378
7496
|
this.#windowMs = options.windowMs ?? 6e4;
|
|
7379
7497
|
this.#failureThreshold = options.failureThreshold ?? 3;
|
|
7380
7498
|
this.#maxFailures = options.maxFailures ?? 128;
|
|
@@ -7461,7 +7579,7 @@ var OperationalHealthTracker = class {
|
|
|
7461
7579
|
async #load() {
|
|
7462
7580
|
let opened;
|
|
7463
7581
|
try {
|
|
7464
|
-
opened = await openOperationalHealthFile(
|
|
7582
|
+
opened = await openOperationalHealthFile(path2.dirname(this.#filePath));
|
|
7465
7583
|
} catch (err) {
|
|
7466
7584
|
throw new Error("operational health state could not be read");
|
|
7467
7585
|
}
|
|
@@ -7497,7 +7615,7 @@ var OperationalHealthTracker = class {
|
|
|
7497
7615
|
if (!this.#state) return;
|
|
7498
7616
|
const body = JSON.stringify(this.#state, null, 2);
|
|
7499
7617
|
this.#writeTail = this.#writeTail.then(async () => {
|
|
7500
|
-
await ensureSecureDir(
|
|
7618
|
+
await ensureSecureDir(path2.dirname(this.#filePath));
|
|
7501
7619
|
await atomicWriteFile(this.#filePath, body, { mode: 384, fsync: true });
|
|
7502
7620
|
});
|
|
7503
7621
|
try {
|
|
@@ -7625,9 +7743,9 @@ function storeMutexIdentity(canonicalStoreDir) {
|
|
|
7625
7743
|
}
|
|
7626
7744
|
function storeMutexEndpoint(canonicalStoreDir, identity, platform = process.platform) {
|
|
7627
7745
|
if (platform === "win32") return `\\\\.\\pipe\\byok-store-mutex-${identity.slice(0, 16)}`;
|
|
7628
|
-
const candidate =
|
|
7746
|
+
const candidate = path2.join(canonicalStoreDir, STORE_MUTEX_SOCKET_FILENAME);
|
|
7629
7747
|
if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT2) return candidate;
|
|
7630
|
-
return
|
|
7748
|
+
return path2.join(STORE_MUTEX_FALLBACK_ROOT, `byok-store-mutex-${identity.slice(0, 16)}`, "sock");
|
|
7631
7749
|
}
|
|
7632
7750
|
var DaemonOwnerActiveError = class extends Error {
|
|
7633
7751
|
constructor(role) {
|
|
@@ -7799,7 +7917,7 @@ async function acquireStoreMutex(canonicalStoreDir) {
|
|
|
7799
7917
|
const endpoint = storeMutexEndpoint(canonicalStoreDir, identity);
|
|
7800
7918
|
const isPipe = process.platform === "win32";
|
|
7801
7919
|
if (!isPipe) {
|
|
7802
|
-
const endpointDir =
|
|
7920
|
+
const endpointDir = path2.dirname(endpoint);
|
|
7803
7921
|
if (endpointDir !== canonicalStoreDir) {
|
|
7804
7922
|
await ensureSecureDir(endpointDir);
|
|
7805
7923
|
await assertOwnedPrivateDir2(endpointDir);
|
|
@@ -7879,8 +7997,8 @@ async function acquireDaemonOwner(storeDir, role, clock = () => /* @__PURE__ */
|
|
|
7879
7997
|
await mutex.close().catch(() => void 0);
|
|
7880
7998
|
throw err;
|
|
7881
7999
|
}
|
|
7882
|
-
const ownerPath =
|
|
7883
|
-
const reclaimPath =
|
|
8000
|
+
const ownerPath = path2.join(storeDir, DAEMON_OWNER_FILENAME);
|
|
8001
|
+
const reclaimPath = path2.join(storeDir, RECLAIM_FILENAME);
|
|
7884
8002
|
const record = {
|
|
7885
8003
|
version: 2,
|
|
7886
8004
|
pid: process.pid,
|
|
@@ -7958,7 +8076,7 @@ var CursorStore = class {
|
|
|
7958
8076
|
storeDir;
|
|
7959
8077
|
fileFor(serverUrl, deviceId) {
|
|
7960
8078
|
const key = createHash("sha256").update(`${serverUrl}::${deviceId}`).digest("hex").slice(0, 32);
|
|
7961
|
-
return
|
|
8079
|
+
return path2.join(this.storeDir, `cursor-${key}.json`);
|
|
7962
8080
|
}
|
|
7963
8081
|
async load(serverUrl, deviceId) {
|
|
7964
8082
|
let raw;
|
|
@@ -7978,7 +8096,7 @@ var CursorStore = class {
|
|
|
7978
8096
|
}
|
|
7979
8097
|
async save(serverUrl, deviceId, cursor) {
|
|
7980
8098
|
const file = this.fileFor(serverUrl, deviceId);
|
|
7981
|
-
await promises.mkdir(
|
|
8099
|
+
await promises.mkdir(path2.dirname(file), { recursive: true, mode: 448 });
|
|
7982
8100
|
await atomicWriteFile(file, JSON.stringify({ cursor }));
|
|
7983
8101
|
}
|
|
7984
8102
|
/** 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. */
|
|
@@ -8308,7 +8426,7 @@ var SessionWorkspaceStore = class {
|
|
|
8308
8426
|
*/
|
|
8309
8427
|
queue = Promise.resolve();
|
|
8310
8428
|
constructor(storeDir) {
|
|
8311
|
-
this.filePath =
|
|
8429
|
+
this.filePath = path2.join(storeDir, "session-workspaces.json");
|
|
8312
8430
|
}
|
|
8313
8431
|
async get(sessionRef) {
|
|
8314
8432
|
return this.enqueue(async () => {
|
|
@@ -8366,7 +8484,7 @@ var SessionWorkspaceStore = class {
|
|
|
8366
8484
|
}
|
|
8367
8485
|
}
|
|
8368
8486
|
async save(all) {
|
|
8369
|
-
const dir =
|
|
8487
|
+
const dir = path2.dirname(this.filePath);
|
|
8370
8488
|
await promises.mkdir(dir, { recursive: true, mode: 448 });
|
|
8371
8489
|
const tmpPath = `${this.filePath}.${process.pid}-${tmpSeq2++}.tmp`;
|
|
8372
8490
|
try {
|
|
@@ -9910,6 +10028,11 @@ function offeredAgentRef(payload) {
|
|
|
9910
10028
|
if (!Object.prototype.hasOwnProperty.call(payload, "agentRef")) return void 0;
|
|
9911
10029
|
return validateAgentRef(payload.agentRef);
|
|
9912
10030
|
}
|
|
10031
|
+
function offeredSessionRef(payload) {
|
|
10032
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "sessionRef")) return void 0;
|
|
10033
|
+
const value = payload.sessionRef;
|
|
10034
|
+
return typeof value === "string" ? value : void 0;
|
|
10035
|
+
}
|
|
9913
10036
|
function errorMessage4(err) {
|
|
9914
10037
|
return err instanceof Error ? err.message : String(err);
|
|
9915
10038
|
}
|
|
@@ -9945,8 +10068,8 @@ function estimateEventBytes(event) {
|
|
|
9945
10068
|
}
|
|
9946
10069
|
async function openArtifact(workspaceDir, name) {
|
|
9947
10070
|
const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
|
|
9948
|
-
const candidate =
|
|
9949
|
-
const prefix = realWorkspaceDir.endsWith(
|
|
10071
|
+
const candidate = path2.resolve(realWorkspaceDir, name);
|
|
10072
|
+
const prefix = realWorkspaceDir.endsWith(path2.sep) ? realWorkspaceDir : realWorkspaceDir + path2.sep;
|
|
9950
10073
|
if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
|
|
9951
10074
|
return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
|
|
9952
10075
|
}
|
|
@@ -10278,6 +10401,9 @@ var TaskRunner = class {
|
|
|
10278
10401
|
case "task.offer_for_agent_with_egress":
|
|
10279
10402
|
await this.handleOffer(envelope.task_id, envelope.payload, true);
|
|
10280
10403
|
return;
|
|
10404
|
+
case "task.offer_for_agent_with_egress_fresh":
|
|
10405
|
+
await this.handleOffer(envelope.task_id, envelope.payload, true);
|
|
10406
|
+
return;
|
|
10281
10407
|
case "task.cancel":
|
|
10282
10408
|
await this.handleCancel(envelope.task_id, envelope.payload.reason);
|
|
10283
10409
|
return;
|
|
@@ -10319,6 +10445,7 @@ var TaskRunner = class {
|
|
|
10319
10445
|
const decline = (reason, retryable) => {
|
|
10320
10446
|
this.decline(taskId, reason, retryable, agentRef);
|
|
10321
10447
|
};
|
|
10448
|
+
const sessionRef = offeredSessionRef(payload);
|
|
10322
10449
|
if ("egressPolicy" in payload) {
|
|
10323
10450
|
if (this.deps.agentEgressPolicy === void 0 || !sameEgressPolicy(this.deps.agentEgressPolicy, payload.egressPolicy)) {
|
|
10324
10451
|
decline("Agent egress offer policy is not exactly enabled by this daemon", false);
|
|
@@ -10420,11 +10547,11 @@ var TaskRunner = class {
|
|
|
10420
10547
|
let plainWorkspaceNeedsResolve = false;
|
|
10421
10548
|
if (agentBinding !== void 0) {
|
|
10422
10549
|
workspaceDir = agentBinding.lease.cwd;
|
|
10423
|
-
if (
|
|
10550
|
+
if (sessionRef !== void 0) {
|
|
10424
10551
|
try {
|
|
10425
10552
|
await this.deps.agentSessionHandoffs.requireMatch({
|
|
10426
10553
|
agentRef: agentBinding.resolution.agentRef,
|
|
10427
|
-
sessionRef
|
|
10554
|
+
sessionRef,
|
|
10428
10555
|
runtimeId: pick.descriptor.id,
|
|
10429
10556
|
cwd: workspaceDir
|
|
10430
10557
|
});
|
|
@@ -10446,15 +10573,15 @@ var TaskRunner = class {
|
|
|
10446
10573
|
return;
|
|
10447
10574
|
}
|
|
10448
10575
|
} else if (this.deps.gitWorkspaceManager && this.deps.gitWorkspaceStore) {
|
|
10449
|
-
known =
|
|
10576
|
+
known = sessionRef ? await this.deps.sessionWorkspaces.get(sessionRef) : void 0;
|
|
10450
10577
|
const gitManager = this.deps.gitWorkspaceManager;
|
|
10451
10578
|
const gitStore = this.deps.gitWorkspaceStore;
|
|
10452
|
-
if (
|
|
10453
|
-
const ledger = await gitStore.findBySessionAnyPhase(
|
|
10579
|
+
if (sessionRef) {
|
|
10580
|
+
const ledger = await gitStore.findBySessionAnyPhase(sessionRef).catch(() => void 0);
|
|
10454
10581
|
const sameProtocolTask = ledger?.taskId === taskId;
|
|
10455
10582
|
const interruptedOldTask = ledger?.phase === "interrupted" && sameProtocolTask;
|
|
10456
10583
|
const activeDifferentTask = ledger !== void 0 && ledger.taskId !== taskId && (ledger.phase === "preparing" || ledger.phase === "active");
|
|
10457
|
-
if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !==
|
|
10584
|
+
if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !== sessionRef || path2.resolve(ledger.workspaceDir) !== path2.resolve(known.workspaceDir) || interruptedOldTask || activeDifferentTask) {
|
|
10458
10585
|
decline("session is incompatible with Git workspace mode", true);
|
|
10459
10586
|
return;
|
|
10460
10587
|
}
|
|
@@ -10469,18 +10596,18 @@ var TaskRunner = class {
|
|
|
10469
10596
|
return;
|
|
10470
10597
|
}
|
|
10471
10598
|
} else {
|
|
10472
|
-
workspaceDir =
|
|
10599
|
+
workspaceDir = path2.join(this.deps.workspaceRoot, taskId);
|
|
10473
10600
|
gitWorkspaceId = randomUUID();
|
|
10474
10601
|
}
|
|
10475
10602
|
try {
|
|
10476
|
-
gitLease = await gitManager.acquireLease(workspaceDir,
|
|
10603
|
+
gitLease = await gitManager.acquireLease(workspaceDir, sessionRef);
|
|
10477
10604
|
} catch {
|
|
10478
10605
|
decline("workspace is busy or unavailable", true);
|
|
10479
10606
|
return;
|
|
10480
10607
|
}
|
|
10481
10608
|
} else if (!this.deps.gitWorkspaceManager && !this.deps.gitWorkspaceStore) {
|
|
10482
|
-
known =
|
|
10483
|
-
workspaceDir = known?.workspaceDir ??
|
|
10609
|
+
known = sessionRef ? await this.deps.sessionWorkspaces.get(sessionRef) : void 0;
|
|
10610
|
+
workspaceDir = known?.workspaceDir ?? path2.join(this.deps.workspaceRoot, taskId);
|
|
10484
10611
|
plainWorkspaceNeedsResolve = true;
|
|
10485
10612
|
} else {
|
|
10486
10613
|
decline("workspace mode is unavailable", true);
|
|
@@ -10498,7 +10625,7 @@ var TaskRunner = class {
|
|
|
10498
10625
|
policy: decision.policy,
|
|
10499
10626
|
requiredToolsetIds: requiredToolsets ?? [],
|
|
10500
10627
|
...offered.dispatchSelection === void 0 ? {} : { dispatchSelection: offered.dispatchSelection },
|
|
10501
|
-
...
|
|
10628
|
+
...sessionRef === void 0 || known === void 0 && agentBinding === void 0 ? {} : { sessionRef },
|
|
10502
10629
|
...agentBinding === void 0 ? {} : {
|
|
10503
10630
|
agentRef: agentBinding.resolution.agentRef,
|
|
10504
10631
|
cwd: agentBinding.lease.cwd,
|
|
@@ -10601,7 +10728,7 @@ var TaskRunner = class {
|
|
|
10601
10728
|
workspaceId,
|
|
10602
10729
|
taskId,
|
|
10603
10730
|
workspaceDir,
|
|
10604
|
-
sessionRef
|
|
10731
|
+
sessionRef,
|
|
10605
10732
|
phase,
|
|
10606
10733
|
baseline: gitBaseline ?? observation.head,
|
|
10607
10734
|
current: observation.head,
|
|
@@ -11867,7 +11994,7 @@ var TaskRunner = class {
|
|
|
11867
11994
|
}
|
|
11868
11995
|
/** `reuseDir`, when set (a known sessionRef's recorded workspace), is used verbatim instead of a fresh `workspaceRoot/<taskId>` directory — `mkdir recursive` is idempotent either way, so ensuring-exists is safe to do unconditionally. */
|
|
11869
11996
|
async resolveWorkspaceDir(taskId, reuseDir) {
|
|
11870
|
-
const dir = reuseDir ??
|
|
11997
|
+
const dir = reuseDir ?? path2.join(this.deps.workspaceRoot, taskId);
|
|
11871
11998
|
await promises.mkdir(dir, { recursive: true });
|
|
11872
11999
|
return dir;
|
|
11873
12000
|
}
|
|
@@ -12005,7 +12132,7 @@ var encoder2 = new TextEncoder();
|
|
|
12005
12132
|
function eventBytes(event) {
|
|
12006
12133
|
return encoder2.encode(JSON.stringify(event)).length;
|
|
12007
12134
|
}
|
|
12008
|
-
var AGENT_EGRESS_DIRECTORY =
|
|
12135
|
+
var AGENT_EGRESS_DIRECTORY = path2.join(".byok", "egress");
|
|
12009
12136
|
var AGENT_RELIABLE_SPOOL_FILENAME = "reliable-v1.jsonl";
|
|
12010
12137
|
var AgentReliableSpoolError = class extends Error {
|
|
12011
12138
|
constructor(message) {
|
|
@@ -12116,9 +12243,9 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
12116
12243
|
logEntries = 0;
|
|
12117
12244
|
writeTail = Promise.resolve();
|
|
12118
12245
|
static async open(homeDir) {
|
|
12119
|
-
const directory =
|
|
12246
|
+
const directory = path2.join(homeDir, AGENT_EGRESS_DIRECTORY);
|
|
12120
12247
|
await ensureSecureDir(directory);
|
|
12121
|
-
const spool = new _AgentReliableSpool(homeDir,
|
|
12248
|
+
const spool = new _AgentReliableSpool(homeDir, path2.join(directory, AGENT_RELIABLE_SPOOL_FILENAME));
|
|
12122
12249
|
await spool.load();
|
|
12123
12250
|
return spool;
|
|
12124
12251
|
}
|
|
@@ -12581,7 +12708,7 @@ var AgentEgressController = class {
|
|
|
12581
12708
|
}
|
|
12582
12709
|
/** Re-open every existing Agent-local spool before retrying stable records after restart. */
|
|
12583
12710
|
async recover(agentsRoot) {
|
|
12584
|
-
if (!
|
|
12711
|
+
if (!path2.isAbsolute(agentsRoot)) throw new Error("Agent egress recovery root must be absolute");
|
|
12585
12712
|
if (!this.active) throw new Error("Agent egress recovery requires an active authenticated enrollment");
|
|
12586
12713
|
if (this.options.tenantId === void 0) {
|
|
12587
12714
|
throw new Error("Agent egress recovery requires one authenticated tenant authority");
|
|
@@ -12596,14 +12723,14 @@ var AgentEgressController = class {
|
|
|
12596
12723
|
}
|
|
12597
12724
|
for (const entry of entries) {
|
|
12598
12725
|
if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
12599
|
-
const homeDir =
|
|
12726
|
+
const homeDir = path2.join(canonicalAgentsRoot, entry.name);
|
|
12600
12727
|
const canonicalHome = await promises.realpath(homeDir);
|
|
12601
|
-
const relativeHome =
|
|
12602
|
-
if (relativeHome !== entry.name || relativeHome.includes(
|
|
12728
|
+
const relativeHome = path2.relative(canonicalAgentsRoot, canonicalHome);
|
|
12729
|
+
if (relativeHome !== entry.name || relativeHome.includes(path2.sep) || path2.isAbsolute(relativeHome)) {
|
|
12603
12730
|
throw new Error(`Agent egress recovery home escaped the canonical agents root: ${entry.name}`);
|
|
12604
12731
|
}
|
|
12605
12732
|
try {
|
|
12606
|
-
await promises.lstat(
|
|
12733
|
+
await promises.lstat(path2.join(homeDir, AGENT_EGRESS_DIRECTORY));
|
|
12607
12734
|
} catch (error) {
|
|
12608
12735
|
if (error.code === "ENOENT") continue;
|
|
12609
12736
|
throw error;
|
|
@@ -12690,12 +12817,12 @@ function isAgentRef(value) {
|
|
|
12690
12817
|
}
|
|
12691
12818
|
function isCanonicalRelativeTarget(value) {
|
|
12692
12819
|
if (value === "[invalid-target]") return true;
|
|
12693
|
-
if (
|
|
12820
|
+
if (path2.isAbsolute(value) || value.includes("\\")) return false;
|
|
12694
12821
|
const segments = value.split("/");
|
|
12695
12822
|
return value.length > 0 && segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
12696
12823
|
}
|
|
12697
12824
|
function validateIdentity(value, label) {
|
|
12698
|
-
if (!isRecord4(value) || !isAgentRef(value.agentRef) || !isNonEmptyString(value.sessionRef) || !isNonEmptyString(value.runtimeId) || !isNonEmptyString(value.cwd) || !
|
|
12825
|
+
if (!isRecord4(value) || !isAgentRef(value.agentRef) || !isNonEmptyString(value.sessionRef) || !isNonEmptyString(value.runtimeId) || !isNonEmptyString(value.cwd) || !path2.isAbsolute(value.cwd)) {
|
|
12699
12826
|
throw new AgentContentAuditStoreError(`${label} has an invalid exact Agent/session identity`);
|
|
12700
12827
|
}
|
|
12701
12828
|
return Object.freeze({
|
|
@@ -12705,7 +12832,7 @@ function validateIdentity(value, label) {
|
|
|
12705
12832
|
}),
|
|
12706
12833
|
sessionRef: value.sessionRef,
|
|
12707
12834
|
runtimeId: value.runtimeId,
|
|
12708
|
-
cwd:
|
|
12835
|
+
cwd: path2.resolve(value.cwd)
|
|
12709
12836
|
});
|
|
12710
12837
|
}
|
|
12711
12838
|
function validateReceipt(value) {
|
|
@@ -12787,16 +12914,16 @@ function assertUniqueRequestIds(entries) {
|
|
|
12787
12914
|
}
|
|
12788
12915
|
}
|
|
12789
12916
|
function assertAbsoluteFilePath(filePath) {
|
|
12790
|
-
if (typeof filePath !== "string" || filePath.length === 0 || !
|
|
12917
|
+
if (typeof filePath !== "string" || filePath.length === 0 || !path2.isAbsolute(filePath)) {
|
|
12791
12918
|
throw new AgentContentAuditStoreError("content audit path must be absolute");
|
|
12792
12919
|
}
|
|
12793
12920
|
if (/[\u0000\r\n]/u.test(filePath)) {
|
|
12794
12921
|
throw new AgentContentAuditStoreError("content audit path must not contain NUL or line breaks");
|
|
12795
12922
|
}
|
|
12796
|
-
return
|
|
12923
|
+
return path2.resolve(filePath);
|
|
12797
12924
|
}
|
|
12798
12925
|
async function ensureDirectoryNoSymlink2(directory) {
|
|
12799
|
-
const absolute =
|
|
12926
|
+
const absolute = path2.resolve(directory);
|
|
12800
12927
|
await promises.mkdir(absolute, { recursive: true, mode: 448 });
|
|
12801
12928
|
const stat = await promises.lstat(absolute);
|
|
12802
12929
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -12830,12 +12957,12 @@ var AgentContentAuditStore = class _AgentContentAuditStore {
|
|
|
12830
12957
|
/** The daemon may address this ledger only through an AgentHomeLayout resolution. */
|
|
12831
12958
|
static forCanonicalAgentHome(canonicalHome) {
|
|
12832
12959
|
const home = assertAbsoluteFilePath(canonicalHome);
|
|
12833
|
-
return new _AgentContentAuditStore(
|
|
12960
|
+
return new _AgentContentAuditStore(path2.join(home, AGENT_HOME_INTERNAL_DIRECTORY, AGENT_CONTENT_AUDIT_FILENAME));
|
|
12834
12961
|
}
|
|
12835
12962
|
async append(receipt) {
|
|
12836
12963
|
const validated = validateReceipt(receipt);
|
|
12837
12964
|
return this.enqueue(async () => {
|
|
12838
|
-
await ensureDirectoryNoSymlink2(
|
|
12965
|
+
await ensureDirectoryNoSymlink2(path2.dirname(this.filePath));
|
|
12839
12966
|
await assertAuditFile(this.filePath);
|
|
12840
12967
|
const entries = await this.readAllUnlocked();
|
|
12841
12968
|
const prior = entries.find((entry) => entry.requestId === validated.requestId);
|
|
@@ -12930,6 +13057,63 @@ var AgentContentAuditStore = class _AgentContentAuditStore {
|
|
|
12930
13057
|
return result;
|
|
12931
13058
|
}
|
|
12932
13059
|
};
|
|
13060
|
+
var AgentHomeProjectionCompletionError = class extends Error {
|
|
13061
|
+
constructor(message, options) {
|
|
13062
|
+
super(message, options);
|
|
13063
|
+
this.name = "AgentHomeProjectionCompletionError";
|
|
13064
|
+
}
|
|
13065
|
+
};
|
|
13066
|
+
function sameAgentRef2(left, right) {
|
|
13067
|
+
return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
|
|
13068
|
+
}
|
|
13069
|
+
var AgentHomeProjectionCompletionClient = class {
|
|
13070
|
+
constructor(options) {
|
|
13071
|
+
this.options = options;
|
|
13072
|
+
}
|
|
13073
|
+
options;
|
|
13074
|
+
async complete(input) {
|
|
13075
|
+
const completion = AgentHomeProjectionCompletionRequestSchema.parse(input);
|
|
13076
|
+
const url = new URL(
|
|
13077
|
+
byokAgentHomeProjectionCompletionPath(completion.requestId),
|
|
13078
|
+
toHttpBase(this.options.serverUrl)
|
|
13079
|
+
);
|
|
13080
|
+
let response;
|
|
13081
|
+
try {
|
|
13082
|
+
response = await authedFetch(
|
|
13083
|
+
url,
|
|
13084
|
+
{
|
|
13085
|
+
method: "PUT",
|
|
13086
|
+
headers: { "content-type": "application/json" },
|
|
13087
|
+
body: JSON.stringify(completion)
|
|
13088
|
+
},
|
|
13089
|
+
this.options.auth
|
|
13090
|
+
);
|
|
13091
|
+
} catch (error) {
|
|
13092
|
+
throw new AgentHomeProjectionCompletionError("Agent-home projection completion transport failed", {
|
|
13093
|
+
cause: error
|
|
13094
|
+
});
|
|
13095
|
+
}
|
|
13096
|
+
if (!response.ok) {
|
|
13097
|
+
throw new AgentHomeProjectionCompletionError(
|
|
13098
|
+
`Agent-home projection completion was rejected with HTTP ${response.status}`
|
|
13099
|
+
);
|
|
13100
|
+
}
|
|
13101
|
+
let readback;
|
|
13102
|
+
try {
|
|
13103
|
+
readback = AgentHomeProjectionReadbackSchema.parse(await response.json());
|
|
13104
|
+
} catch (error) {
|
|
13105
|
+
throw new AgentHomeProjectionCompletionError("Agent-home projection completion readback is invalid", {
|
|
13106
|
+
cause: error
|
|
13107
|
+
});
|
|
13108
|
+
}
|
|
13109
|
+
if (readback.tenantId !== this.options.tenantId || readback.deviceId !== this.options.deviceId || readback.requestId !== completion.requestId || !sameAgentRef2(readback.agentRef, completion.agentRef) || readback.projectionHash !== completion.projectionHash || readback.status !== completion.outcome || readback.completedAt === void 0) {
|
|
13110
|
+
throw new AgentHomeProjectionCompletionError(
|
|
13111
|
+
"Agent-home projection completion readback does not exactly match the authenticated request"
|
|
13112
|
+
);
|
|
13113
|
+
}
|
|
13114
|
+
return readback;
|
|
13115
|
+
}
|
|
13116
|
+
};
|
|
12933
13117
|
var AGENT_CONTENT_READ_SURFACES = ["workspace", "transcript", "artifact"];
|
|
12934
13118
|
var AGENT_CONTENT_READ_CAPABILITIES = Object.freeze({
|
|
12935
13119
|
workspace: AGENT_CONTENT_WORKSPACE_READ_CAPABILITY,
|
|
@@ -13033,8 +13217,8 @@ function normalizeIdentity(value, field) {
|
|
|
13033
13217
|
const sessionRef = nonEmptyString(value.sessionRef, `${field}.sessionRef`);
|
|
13034
13218
|
const runtimeId = nonEmptyString(value.runtimeId, `${field}.runtimeId`);
|
|
13035
13219
|
const cwd = nonEmptyString(value.cwd, `${field}.cwd`);
|
|
13036
|
-
if (!
|
|
13037
|
-
return Object.freeze({ agentRef, sessionRef, runtimeId, cwd:
|
|
13220
|
+
if (!path2.isAbsolute(cwd)) throw new AgentContentReadPolicyError(`${field}.cwd must be absolute`);
|
|
13221
|
+
return Object.freeze({ agentRef, sessionRef, runtimeId, cwd: path2.resolve(cwd) });
|
|
13038
13222
|
}
|
|
13039
13223
|
function createAgentContentReadPolicy(input) {
|
|
13040
13224
|
if (!isRecord5(input) || input.enabled !== true) {
|
|
@@ -13062,10 +13246,10 @@ function createAgentContentReadPolicy(input) {
|
|
|
13062
13246
|
root = Object.freeze({ kind: "agent-home" });
|
|
13063
13247
|
} else if (input.root.kind === "runtime-allowlisted") {
|
|
13064
13248
|
const configuredRoot = nonEmptyString(input.root.root, "contentRead.root.root");
|
|
13065
|
-
if (!
|
|
13249
|
+
if (!path2.isAbsolute(configuredRoot)) {
|
|
13066
13250
|
throw new AgentContentReadPolicyError("contentRead.root.root must be absolute");
|
|
13067
13251
|
}
|
|
13068
|
-
root = Object.freeze({ kind: "runtime-allowlisted", root:
|
|
13252
|
+
root = Object.freeze({ kind: "runtime-allowlisted", root: path2.resolve(configuredRoot) });
|
|
13069
13253
|
} else {
|
|
13070
13254
|
throw new AgentContentReadPolicyError("contentRead.root.kind is not supported");
|
|
13071
13255
|
}
|
|
@@ -13088,11 +13272,11 @@ function createAgentContentReadPolicy(input) {
|
|
|
13088
13272
|
});
|
|
13089
13273
|
}
|
|
13090
13274
|
function isWithin2(root, candidate) {
|
|
13091
|
-
const relative =
|
|
13092
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
13275
|
+
const relative = path2.relative(root, candidate);
|
|
13276
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
|
|
13093
13277
|
}
|
|
13094
13278
|
function isPortableAbsoluteTarget(value) {
|
|
13095
|
-
return
|
|
13279
|
+
return path2.isAbsolute(value) || /^[a-z]:[\\/]/iu.test(value) || /^[/\\]/u.test(value);
|
|
13096
13280
|
}
|
|
13097
13281
|
function canonicalAuditTarget(value) {
|
|
13098
13282
|
if (typeof value !== "string" || value.length === 0 || /[\u0000\r\n]/u.test(value) || isPortableAbsoluteTarget(value) || value.includes("\\")) {
|
|
@@ -13130,7 +13314,7 @@ function isSensitiveTarget(segments, productNames) {
|
|
|
13130
13314
|
return segments.some((segment) => patterns.some((pattern) => nameMatches(pattern, segment)));
|
|
13131
13315
|
}
|
|
13132
13316
|
async function resolveExistingAncestor2(inputPath) {
|
|
13133
|
-
let cursor =
|
|
13317
|
+
let cursor = path2.resolve(inputPath);
|
|
13134
13318
|
const tail = [];
|
|
13135
13319
|
for (; ; ) {
|
|
13136
13320
|
try {
|
|
@@ -13138,9 +13322,9 @@ async function resolveExistingAncestor2(inputPath) {
|
|
|
13138
13322
|
} catch (error) {
|
|
13139
13323
|
const code = error.code;
|
|
13140
13324
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
13141
|
-
const parent =
|
|
13325
|
+
const parent = path2.dirname(cursor);
|
|
13142
13326
|
if (parent === cursor) throw new TargetPolicyError("target-missing");
|
|
13143
|
-
tail.unshift(
|
|
13327
|
+
tail.unshift(path2.basename(cursor));
|
|
13144
13328
|
cursor = parent;
|
|
13145
13329
|
}
|
|
13146
13330
|
}
|
|
@@ -13161,11 +13345,11 @@ var RootPolicyError = class extends Error {
|
|
|
13161
13345
|
this.reason = reason;
|
|
13162
13346
|
}
|
|
13163
13347
|
};
|
|
13164
|
-
function
|
|
13348
|
+
function sameAgentRef3(left, right) {
|
|
13165
13349
|
return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
|
|
13166
13350
|
}
|
|
13167
13351
|
function sameSessionIdentity(left, right) {
|
|
13168
|
-
return
|
|
13352
|
+
return sameAgentRef3(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
|
|
13169
13353
|
}
|
|
13170
13354
|
function validateRequest(request) {
|
|
13171
13355
|
if (!isRecord5(request)) throw new AgentContentReadRequestError("content read request must be an object");
|
|
@@ -13227,18 +13411,18 @@ function normalizeRequestIdentity(value, field) {
|
|
|
13227
13411
|
const sessionRef = requestString(value.sessionRef, `${field}.sessionRef`);
|
|
13228
13412
|
const runtimeId = requestString(value.runtimeId, `${field}.runtimeId`);
|
|
13229
13413
|
const cwd = requestString(value.cwd, `${field}.cwd`);
|
|
13230
|
-
if (!
|
|
13231
|
-
return Object.freeze({ agentRef, sessionRef, runtimeId, cwd:
|
|
13414
|
+
if (!path2.isAbsolute(cwd)) throw new AgentContentReadRequestError(`${field}.cwd must be absolute`);
|
|
13415
|
+
return Object.freeze({ agentRef, sessionRef, runtimeId, cwd: path2.resolve(cwd) });
|
|
13232
13416
|
}
|
|
13233
13417
|
async function inspectRegularTarget(root, target) {
|
|
13234
13418
|
const ancestor = await resolveExistingAncestor2(target);
|
|
13235
13419
|
if (!isWithin2(root, ancestor.canonical)) {
|
|
13236
13420
|
throw new TargetPolicyError("path-escape");
|
|
13237
13421
|
}
|
|
13238
|
-
const components =
|
|
13422
|
+
const components = path2.relative(root, target).split(path2.sep).filter((component) => component.length > 0);
|
|
13239
13423
|
let cursor = root;
|
|
13240
13424
|
for (const [index, component] of components.entries()) {
|
|
13241
|
-
cursor =
|
|
13425
|
+
cursor = path2.join(cursor, component);
|
|
13242
13426
|
let stat;
|
|
13243
13427
|
try {
|
|
13244
13428
|
stat = await promises.lstat(cursor);
|
|
@@ -13303,8 +13487,8 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13303
13487
|
this.capabilities = new Set(options.capabilities);
|
|
13304
13488
|
this.runtimeRoots = Object.freeze((options.runtimeAllowlistedRoots ?? []).map((root, index) => {
|
|
13305
13489
|
const value = nonEmptyString(root, `contentRead.runtimeAllowlistedRoots[${index}]`);
|
|
13306
|
-
if (!
|
|
13307
|
-
return
|
|
13490
|
+
if (!path2.isAbsolute(value)) throw new AgentContentReadPolicyError("runtime allowlisted roots must be absolute");
|
|
13491
|
+
return path2.resolve(value);
|
|
13308
13492
|
}));
|
|
13309
13493
|
this.resolveSessionIdentity = options.resolveSessionIdentity;
|
|
13310
13494
|
this.resolveTranscriptIdentity = options.resolveTranscriptIdentity;
|
|
@@ -13356,7 +13540,7 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13356
13540
|
if (request.decodeAs === "utf8" && !policy.textMimeTypes.includes(request.mimeType)) {
|
|
13357
13541
|
return this.deny(request, relativeTarget, "text-not-allowlisted");
|
|
13358
13542
|
}
|
|
13359
|
-
const target =
|
|
13543
|
+
const target = path2.resolve(root, ...segments);
|
|
13360
13544
|
if (!isWithin2(root, target)) return this.deny(request, relativeTarget, "path-escape");
|
|
13361
13545
|
try {
|
|
13362
13546
|
await inspectRegularTarget(root, target);
|
|
@@ -13437,7 +13621,7 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13437
13621
|
}
|
|
13438
13622
|
async checkSessionIdentity(request, resolver, requiredCwd) {
|
|
13439
13623
|
const session = request.session;
|
|
13440
|
-
if (session === void 0 || !
|
|
13624
|
+
if (session === void 0 || !sameAgentRef3(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
|
|
13441
13625
|
return "identity-mismatch";
|
|
13442
13626
|
}
|
|
13443
13627
|
let expected;
|
|
@@ -13553,7 +13737,7 @@ async function detectRuntimes(adapters) {
|
|
|
13553
13737
|
}
|
|
13554
13738
|
return runtimes;
|
|
13555
13739
|
}
|
|
13556
|
-
function computeCapabilities(adapters, agentHomeConfigured = false, agentEgressConfigured = false, contentReadPolicies) {
|
|
13740
|
+
function computeCapabilities(adapters, agentHomeConfigured = false, agentHomeProjectionConfigured = false, agentEgressConfigured = false, contentReadPolicies) {
|
|
13557
13741
|
const flags = [];
|
|
13558
13742
|
if (adapters.some((adapter) => adapter.descriptor.capabilities.steer)) flags.push("steer");
|
|
13559
13743
|
flags.push("blob-upload");
|
|
@@ -13568,7 +13752,14 @@ function computeCapabilities(adapters, agentHomeConfigured = false, agentEgressC
|
|
|
13568
13752
|
flags.push("toolset-selection");
|
|
13569
13753
|
}
|
|
13570
13754
|
if (agentHomeConfigured) flags.push("agent-home-contract");
|
|
13571
|
-
if (
|
|
13755
|
+
if (agentHomeProjectionConfigured) flags.push(AGENT_HOME_PROJECTION_CAPABILITY);
|
|
13756
|
+
if (agentEgressConfigured) {
|
|
13757
|
+
flags.push(
|
|
13758
|
+
AGENT_EGRESS_POLICY_CAPABILITY,
|
|
13759
|
+
AGENT_EGRESS_RELIABLE_ACK_CAPABILITY,
|
|
13760
|
+
AGENT_EGRESS_FRESH_SESSION_CAPABILITY
|
|
13761
|
+
);
|
|
13762
|
+
}
|
|
13572
13763
|
if (contentReadPolicies !== void 0) {
|
|
13573
13764
|
for (const surface of Object.keys(AGENT_CONTENT_READ_CAPABILITIES)) {
|
|
13574
13765
|
const policy = contentReadPolicies[surface];
|
|
@@ -13920,7 +14111,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
13920
14111
|
}
|
|
13921
14112
|
await agentHomeManager?.preflight();
|
|
13922
14113
|
if (config.agentEgress !== void 0 && config.agentHome !== void 0) {
|
|
13923
|
-
await agentEgress.recover(
|
|
14114
|
+
await agentEgress.recover(path2.join(config.agentHome.hostStorageRoot, "agents"));
|
|
13924
14115
|
}
|
|
13925
14116
|
fleetJitter = createFleetJitter(config.productId, record.deviceId);
|
|
13926
14117
|
if (config.permissionDefaults?.workspaceRoot !== void 0) {
|
|
@@ -13975,9 +14166,16 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
13975
14166
|
const capabilities = computeCapabilities(
|
|
13976
14167
|
adapters,
|
|
13977
14168
|
config.agentHome !== void 0,
|
|
14169
|
+
agentHomeManager?.supportsTaskFreeProjection() === true,
|
|
13978
14170
|
config.agentEgress !== void 0,
|
|
13979
14171
|
agentContentReadPolicies
|
|
13980
14172
|
);
|
|
14173
|
+
const agentHomeProjectionCompletion = agentHomeManager?.supportsTaskFreeProjection() === true ? new AgentHomeProjectionCompletionClient({
|
|
14174
|
+
serverUrl: config.serverUrl,
|
|
14175
|
+
auth,
|
|
14176
|
+
tenantId: record.tenantId,
|
|
14177
|
+
deviceId: record.deviceId
|
|
14178
|
+
}) : void 0;
|
|
13981
14179
|
const journalIdentity = config.hostedJournal ? { tenantId: record.tenantId, productId: config.productId, deviceId: record.deviceId } : void 0;
|
|
13982
14180
|
const sendSanitizedEnvelope = activeJournal && journalIdentity ? (envelope) => {
|
|
13983
14181
|
observer.handleOutboundEnvelope(envelope);
|
|
@@ -14114,6 +14312,20 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14114
14312
|
...activePressureEngine ? { admissionGuard: () => activePressureEngine.admissionGuard() } : {}
|
|
14115
14313
|
};
|
|
14116
14314
|
runner = new TaskRunner(deps);
|
|
14315
|
+
const handleAgentHomeProjectionEnvelope = async (envelope) => {
|
|
14316
|
+
if (envelope.type !== "agent.home.projection") return false;
|
|
14317
|
+
if (agentHomeManager === void 0 || agentHomeProjectionCompletion === void 0) {
|
|
14318
|
+
throw new Error("task-free Agent-home projection is not configured on this daemon");
|
|
14319
|
+
}
|
|
14320
|
+
const outcome = await agentHomeManager.project(envelope.payload);
|
|
14321
|
+
await agentHomeProjectionCompletion.complete({
|
|
14322
|
+
requestId: envelope.payload.requestId,
|
|
14323
|
+
agentRef: envelope.payload.agentRef,
|
|
14324
|
+
projectionHash: envelope.payload.projectionHash,
|
|
14325
|
+
outcome
|
|
14326
|
+
});
|
|
14327
|
+
return true;
|
|
14328
|
+
};
|
|
14117
14329
|
const handleAgentEgressEnvelope = async (envelope) => {
|
|
14118
14330
|
if (envelope.type !== "agent.egress.ack") return false;
|
|
14119
14331
|
if (config.agentEgress === void 0) return true;
|
|
@@ -14269,6 +14481,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14269
14481
|
throw new Error("tenant enrollment is being re-paired; inbound work is blocked until restart");
|
|
14270
14482
|
}
|
|
14271
14483
|
observer.handleInboundEnvelope(envelope);
|
|
14484
|
+
if (await handleAgentHomeProjectionEnvelope(envelope)) return;
|
|
14272
14485
|
if (await handleAgentEgressEnvelope(envelope)) return;
|
|
14273
14486
|
if (await handleAgentContentReadEnvelope(envelope)) return;
|
|
14274
14487
|
activePressureEngine?.assertAckCriticalAllowed();
|
|
@@ -14279,6 +14492,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14279
14492
|
return Promise.reject(new Error("tenant enrollment is being re-paired; inbound work is blocked until restart"));
|
|
14280
14493
|
}
|
|
14281
14494
|
observer.handleInboundEnvelope(envelope);
|
|
14495
|
+
if (envelope.type === "agent.home.projection") return handleAgentHomeProjectionEnvelope(envelope).then(() => void 0);
|
|
14282
14496
|
if (envelope.type === "agent.egress.ack") return handleAgentEgressEnvelope(envelope).then(() => void 0);
|
|
14283
14497
|
if (envelope.type === "agent.content.read") return handleAgentContentReadEnvelope(envelope).then(() => void 0);
|
|
14284
14498
|
return runner?.handleEnvelope(envelope) ?? Promise.resolve();
|
|
@@ -14773,7 +14987,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14773
14987
|
connection.send(sanitized.envelope);
|
|
14774
14988
|
}
|
|
14775
14989
|
async function publishReliableAgentEgress(input) {
|
|
14776
|
-
if (config.agentEgress === void 0 || agentHomeManager === void 0) {
|
|
14990
|
+
if (config.agentEgress === void 0 || agentHomeManager === void 0 || agentSessionHandoffs === void 0) {
|
|
14777
14991
|
throw new Error("Agent reliable egress is not configured");
|
|
14778
14992
|
}
|
|
14779
14993
|
if (tenantRebinding) {
|
|
@@ -14782,12 +14996,21 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14782
14996
|
const binding = await agentHomeManager.acquire(input.agentRef);
|
|
14783
14997
|
try {
|
|
14784
14998
|
await agentHomeManager.initialize(binding);
|
|
14999
|
+
const handoff = await agentSessionHandoffs.requireMatch({
|
|
15000
|
+
agentRef: binding.resolution.agentRef,
|
|
15001
|
+
sessionRef: input.sessionRef,
|
|
15002
|
+
runtimeId: input.runtimeId,
|
|
15003
|
+
cwd: binding.resolution.canonicalHome
|
|
15004
|
+
});
|
|
15005
|
+
if (handoff.taskId !== input.taskId) {
|
|
15006
|
+
throw new Error("Agent reliable egress taskId does not match the durable session handoff");
|
|
15007
|
+
}
|
|
14785
15008
|
const appended = await agentEgress.appendReliable({
|
|
14786
15009
|
homeDir: binding.resolution.canonicalHome,
|
|
14787
15010
|
agentRef: binding.resolution.agentRef,
|
|
14788
15011
|
sessionRef: input.sessionRef,
|
|
14789
15012
|
payload: input.payload,
|
|
14790
|
-
|
|
15013
|
+
taskId: input.taskId,
|
|
14791
15014
|
...input.eventId === void 0 ? {} : { eventId: input.eventId }
|
|
14792
15015
|
});
|
|
14793
15016
|
if (appended.ok) dispatchReliableRecord(appended.record);
|
|
@@ -15132,8 +15355,8 @@ function generateLaunchdPlist(def) {
|
|
|
15132
15355
|
const { label, program, logDir } = def;
|
|
15133
15356
|
const args = [program.command, ...program.args];
|
|
15134
15357
|
const cwd = program.cwd ?? os.homedir();
|
|
15135
|
-
const outLog =
|
|
15136
|
-
const errLog =
|
|
15358
|
+
const outLog = path2.join(logDir, `${label}.out.log`);
|
|
15359
|
+
const errLog = path2.join(logDir, `${label}.err.log`);
|
|
15137
15360
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
15138
15361
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
15139
15362
|
<plist version="1.0">
|
|
@@ -15174,7 +15397,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
15174
15397
|
return process.getuid();
|
|
15175
15398
|
});
|
|
15176
15399
|
const label = sanitizeServiceName(def.name);
|
|
15177
|
-
const plistPath = () =>
|
|
15400
|
+
const plistPath = () => path2.join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
|
|
15178
15401
|
const domainTarget = () => `gui/${getuid()}`;
|
|
15179
15402
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
15180
15403
|
async function fileExists(p) {
|
|
@@ -15187,7 +15410,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
15187
15410
|
}
|
|
15188
15411
|
async function writePlist(program) {
|
|
15189
15412
|
const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
|
|
15190
|
-
await fs26.mkdir(
|
|
15413
|
+
await fs26.mkdir(path2.dirname(plistPath()), { recursive: true });
|
|
15191
15414
|
await fs26.mkdir(def.logDir, { recursive: true });
|
|
15192
15415
|
await fs26.writeFile(plistPath(), xml, "utf8");
|
|
15193
15416
|
}
|
|
@@ -15261,8 +15484,8 @@ function generateSystemdUnit(def) {
|
|
|
15261
15484
|
assertNoControlChars(displayName, "displayName");
|
|
15262
15485
|
const cwd = program.cwd ?? os.homedir();
|
|
15263
15486
|
assertNoControlChars(cwd, "program.cwd");
|
|
15264
|
-
const outLog =
|
|
15265
|
-
const errLog =
|
|
15487
|
+
const outLog = path2.join(logDir, `${name}.out.log`);
|
|
15488
|
+
const errLog = path2.join(logDir, `${name}.err.log`);
|
|
15266
15489
|
assertNoControlChars(outLog, "logDir");
|
|
15267
15490
|
assertNoControlChars(errLog, "logDir");
|
|
15268
15491
|
const execStart = [program.command, ...program.args].map(quoteSystemdArg).join(" ");
|
|
@@ -15288,7 +15511,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
15288
15511
|
const homedir = deps.homedir ?? (() => os.homedir());
|
|
15289
15512
|
const name = sanitizeServiceName(def.name);
|
|
15290
15513
|
const unitName = `${name}.service`;
|
|
15291
|
-
const unitPath = () =>
|
|
15514
|
+
const unitPath = () => path2.join(homedir(), ".config", "systemd", "user", unitName);
|
|
15292
15515
|
async function fileExists(p) {
|
|
15293
15516
|
try {
|
|
15294
15517
|
await fs26.stat(p);
|
|
@@ -15299,7 +15522,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
15299
15522
|
}
|
|
15300
15523
|
async function writeUnit(program) {
|
|
15301
15524
|
const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
15302
|
-
await fs26.mkdir(
|
|
15525
|
+
await fs26.mkdir(path2.dirname(unitPath()), { recursive: true });
|
|
15303
15526
|
await fs26.mkdir(def.logDir, { recursive: true });
|
|
15304
15527
|
await fs26.writeFile(unitPath(), unit, "utf8");
|
|
15305
15528
|
}
|
|
@@ -15381,8 +15604,8 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
15381
15604
|
const winswBin = windows.winswBin;
|
|
15382
15605
|
const id = sanitizeServiceName(def.name);
|
|
15383
15606
|
const installDir = windows.installDir ?? def.logDir;
|
|
15384
|
-
const exePath =
|
|
15385
|
-
const xmlPath =
|
|
15607
|
+
const exePath = path2.join(installDir, `${id}.exe`);
|
|
15608
|
+
const xmlPath = path2.join(installDir, `${id}.xml`);
|
|
15386
15609
|
async function fileExists(p) {
|
|
15387
15610
|
try {
|
|
15388
15611
|
await fs26.stat(p);
|
|
@@ -15452,7 +15675,7 @@ function createServiceLifecycle(def, opts = {}) {
|
|
|
15452
15675
|
|
|
15453
15676
|
// src/bin/official-release.ts
|
|
15454
15677
|
var OFFICIAL_LOCAL_AGENT_RELEASE = resolveLocalAgentReleaseIdentity({
|
|
15455
|
-
version: "0.
|
|
15678
|
+
version: "0.8.0"
|
|
15456
15679
|
});
|
|
15457
15680
|
|
|
15458
15681
|
// src/bin/config.ts
|
|
@@ -15937,7 +16160,7 @@ function safeProtocol(serverUrl) {
|
|
|
15937
16160
|
}
|
|
15938
16161
|
}
|
|
15939
16162
|
async function inspectDevice(storeDir) {
|
|
15940
|
-
const filePath =
|
|
16163
|
+
const filePath = path2.join(storeDir, "device.json");
|
|
15941
16164
|
let pathStat;
|
|
15942
16165
|
try {
|
|
15943
16166
|
pathStat = await promises.lstat(filePath);
|
|
@@ -16017,11 +16240,11 @@ async function copyOpenFileBounded(source, expected, destinationPath) {
|
|
|
16017
16240
|
}
|
|
16018
16241
|
}
|
|
16019
16242
|
async function inspectJournal(storeDir) {
|
|
16020
|
-
const journalPath =
|
|
16243
|
+
const journalPath = path2.join(storeDir, JOURNAL_DB_FILENAME);
|
|
16021
16244
|
try {
|
|
16022
16245
|
const mainIdentity = await regularFileIdentity(journalPath);
|
|
16023
16246
|
if (mainIdentity === void 0) return { status: "missing" };
|
|
16024
|
-
const walIdentity = await regularFileIdentity(
|
|
16247
|
+
const walIdentity = await regularFileIdentity(path2.join(storeDir, `${JOURNAL_DB_FILENAME}-wal`));
|
|
16025
16248
|
let sizeBytes = Number(mainIdentity.size);
|
|
16026
16249
|
let walBytes = walIdentity === void 0 ? void 0 : Number(walIdentity.size);
|
|
16027
16250
|
if (!isSqliteAvailable()) {
|
|
@@ -16029,7 +16252,7 @@ async function inspectJournal(storeDir) {
|
|
|
16029
16252
|
}
|
|
16030
16253
|
const componentNames = [JOURNAL_DB_FILENAME, `${JOURNAL_DB_FILENAME}-wal`, `${JOURNAL_DB_FILENAME}-shm`];
|
|
16031
16254
|
const initial = /* @__PURE__ */ new Map();
|
|
16032
|
-
for (const name of componentNames) initial.set(name, await regularFileIdentity(
|
|
16255
|
+
for (const name of componentNames) initial.set(name, await regularFileIdentity(path2.join(storeDir, name)));
|
|
16033
16256
|
const snapshotMain = initial.get(JOURNAL_DB_FILENAME);
|
|
16034
16257
|
if (!snapshotMain) return { status: "unavailable", reason: "journal changed during diagnostics snapshot" };
|
|
16035
16258
|
sizeBytes = Number(snapshotMain.size);
|
|
@@ -16041,7 +16264,7 @@ async function inspectJournal(storeDir) {
|
|
|
16041
16264
|
let handle;
|
|
16042
16265
|
try {
|
|
16043
16266
|
handle = await promises.open(
|
|
16044
|
-
|
|
16267
|
+
path2.join(storeDir, name),
|
|
16045
16268
|
constants.O_RDONLY | constants.O_NONBLOCK | (constants.O_NOFOLLOW ?? 0)
|
|
16046
16269
|
);
|
|
16047
16270
|
} catch (err) {
|
|
@@ -16070,28 +16293,28 @@ async function inspectJournal(storeDir) {
|
|
|
16070
16293
|
reason: "journal exceeds the bounded diagnostics copy limit"
|
|
16071
16294
|
};
|
|
16072
16295
|
}
|
|
16073
|
-
const tempDir = await promises.mkdtemp(
|
|
16296
|
+
const tempDir = await promises.mkdtemp(path2.join(os.tmpdir(), "byok-journal-inspect-"));
|
|
16074
16297
|
const { DatabaseSync } = loadSqliteModule();
|
|
16075
16298
|
let db;
|
|
16076
16299
|
try {
|
|
16077
16300
|
for (const name of componentNames) {
|
|
16078
16301
|
const component = opened.get(name);
|
|
16079
|
-
if (component && !await copyOpenFileBounded(component.handle, component.identity,
|
|
16302
|
+
if (component && !await copyOpenFileBounded(component.handle, component.identity, path2.join(tempDir, name))) {
|
|
16080
16303
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
16081
16304
|
}
|
|
16082
16305
|
}
|
|
16083
16306
|
for (const [name, component] of opened) {
|
|
16084
|
-
if (!sameIdentity(component.identity, identityFromBigIntStat(await component.handle.stat({ bigint: true }))) || !sameIdentity(component.identity, await regularFileIdentity(
|
|
16307
|
+
if (!sameIdentity(component.identity, identityFromBigIntStat(await component.handle.stat({ bigint: true }))) || !sameIdentity(component.identity, await regularFileIdentity(path2.join(storeDir, name)))) {
|
|
16085
16308
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
16086
16309
|
}
|
|
16087
16310
|
}
|
|
16088
16311
|
for (const name of componentNames) {
|
|
16089
|
-
if (!opened.has(name) && await regularFileIdentity(
|
|
16312
|
+
if (!opened.has(name) && await regularFileIdentity(path2.join(storeDir, name)) !== void 0) {
|
|
16090
16313
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
16091
16314
|
}
|
|
16092
16315
|
}
|
|
16093
16316
|
const header = Buffer.alloc(16);
|
|
16094
|
-
const copiedHandle = await promises.open(
|
|
16317
|
+
const copiedHandle = await promises.open(path2.join(tempDir, JOURNAL_DB_FILENAME), "r");
|
|
16095
16318
|
try {
|
|
16096
16319
|
const { bytesRead } = await copiedHandle.read(header, 0, header.length, 0);
|
|
16097
16320
|
if (bytesRead !== 16 || header.toString("binary") !== "SQLite format 3\0") {
|
|
@@ -16100,7 +16323,7 @@ async function inspectJournal(storeDir) {
|
|
|
16100
16323
|
} finally {
|
|
16101
16324
|
await copiedHandle.close();
|
|
16102
16325
|
}
|
|
16103
|
-
db = new DatabaseSync(
|
|
16326
|
+
db = new DatabaseSync(path2.join(tempDir, JOURNAL_DB_FILENAME), { readOnly: true });
|
|
16104
16327
|
const result = db.prepare("PRAGMA quick_check(1)").get();
|
|
16105
16328
|
if (result?.quick_check !== "ok") {
|
|
16106
16329
|
return { status: "corrupt", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal quick_check failed" };
|
|
@@ -16135,7 +16358,7 @@ async function inspectWorkspace(workspaceRoot) {
|
|
|
16135
16358
|
}
|
|
16136
16359
|
}
|
|
16137
16360
|
function readPinnedQuarantineFile(name, maxBytes, includeBytes, budget) {
|
|
16138
|
-
if (
|
|
16361
|
+
if (path2.basename(name) !== name || name === "." || name === "..") {
|
|
16139
16362
|
throw new Error("quarantine manifest contains an invalid evidence name");
|
|
16140
16363
|
}
|
|
16141
16364
|
const namedBefore = lstatSync(name, { bigint: true });
|
|
@@ -16239,10 +16462,10 @@ function inspectQuarantinePinned(dir, expectedDirectory) {
|
|
|
16239
16462
|
if (isJournalQuarantineManifest(parsed)) {
|
|
16240
16463
|
const manifestBase = manifestName.slice(0, -".manifest.json".length);
|
|
16241
16464
|
const boundNames = parsed.files.map((file) => {
|
|
16242
|
-
if (
|
|
16465
|
+
if (path2.dirname(path2.resolve(file)) !== path2.resolve(".")) {
|
|
16243
16466
|
throw new Error("journal quarantine manifest points outside quarantine");
|
|
16244
16467
|
}
|
|
16245
|
-
return
|
|
16468
|
+
return path2.basename(file);
|
|
16246
16469
|
});
|
|
16247
16470
|
if (!boundNames.includes(manifestBase)) {
|
|
16248
16471
|
throw new Error("journal quarantine manifest is not bound to its primary database evidence");
|
|
@@ -16282,7 +16505,7 @@ function inspectQuarantinePinned(dir, expectedDirectory) {
|
|
|
16282
16505
|
}
|
|
16283
16506
|
}
|
|
16284
16507
|
async function inspectQuarantine(storeDir) {
|
|
16285
|
-
const dir =
|
|
16508
|
+
const dir = path2.join(storeDir, JOURNAL_QUARANTINE_DIRNAME);
|
|
16286
16509
|
let directory;
|
|
16287
16510
|
try {
|
|
16288
16511
|
directory = await promises.lstat(dir, { bigint: true });
|
|
@@ -16353,7 +16576,7 @@ function checksFor(snapshot) {
|
|
|
16353
16576
|
];
|
|
16354
16577
|
}
|
|
16355
16578
|
async function collectDiagnostics(config, storeDir, options = {}) {
|
|
16356
|
-
const resolvedStoreDir =
|
|
16579
|
+
const resolvedStoreDir = path2.resolve(storeDir);
|
|
16357
16580
|
const adapters = options.adapters ?? defaultRuntimeAdapters(config.runtimeAllowlist);
|
|
16358
16581
|
const connectControl = options.connectControl ?? connectControlClient;
|
|
16359
16582
|
const [device, probedRuntimes, health, journal, workspace, quarantine, controlConnection] = await Promise.all([
|
|
@@ -16528,7 +16751,7 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
|
|
|
16528
16751
|
unlinkSync(sourcePath);
|
|
16529
16752
|
sourceRemoved = true;
|
|
16530
16753
|
if (process.platform !== "win32") {
|
|
16531
|
-
const directoryFd = openSync(
|
|
16754
|
+
const directoryFd = openSync(path2.dirname(sourcePath), constants.O_RDONLY);
|
|
16532
16755
|
try {
|
|
16533
16756
|
fsyncSync(directoryFd);
|
|
16534
16757
|
} finally {
|
|
@@ -16567,10 +16790,10 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
|
|
|
16567
16790
|
}
|
|
16568
16791
|
}
|
|
16569
16792
|
async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
|
|
16570
|
-
const resolvedStoreDir =
|
|
16793
|
+
const resolvedStoreDir = path2.resolve(storeDir);
|
|
16571
16794
|
const owner = await acquireDaemonOwner(resolvedStoreDir, "doctor", options.clock);
|
|
16572
16795
|
try {
|
|
16573
|
-
const sourcePath =
|
|
16796
|
+
const sourcePath = path2.join(resolvedStoreDir, OPERATIONAL_HEALTH_FILENAME);
|
|
16574
16797
|
let opened;
|
|
16575
16798
|
try {
|
|
16576
16799
|
opened = await openOperationalHealthFile(resolvedStoreDir);
|
|
@@ -16587,7 +16810,7 @@ async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
|
|
|
16587
16810
|
}
|
|
16588
16811
|
const sourceStat = await source.stat({ bigint: true });
|
|
16589
16812
|
if (!sourceStat.isFile()) throw new Error("operational health state is not a regular file; refusing quarantine");
|
|
16590
|
-
const quarantineDir =
|
|
16813
|
+
const quarantineDir = path2.join(resolvedStoreDir, JOURNAL_QUARANTINE_DIRNAME);
|
|
16591
16814
|
try {
|
|
16592
16815
|
const existing = await promises.lstat(quarantineDir);
|
|
16593
16816
|
if (!existing.isDirectory() || existing.isSymbolicLink()) {
|
|
@@ -16688,8 +16911,8 @@ function buildServiceDefinition(config, configPath, rest) {
|
|
|
16688
16911
|
const name = argValue(rest, "--name") ?? config.productId;
|
|
16689
16912
|
const agentBin = argValue(rest, "--agent-bin") ?? process.argv[1] ?? "byok-agent";
|
|
16690
16913
|
const nodeBin = argValue(rest, "--node-bin") ?? process.execPath;
|
|
16691
|
-
const absoluteConfigPath =
|
|
16692
|
-
const logDir =
|
|
16914
|
+
const absoluteConfigPath = path2.resolve(configPath);
|
|
16915
|
+
const logDir = path2.join(resolveStoreDir(config), "service-logs");
|
|
16693
16916
|
const definition = {
|
|
16694
16917
|
name,
|
|
16695
16918
|
displayName: config.branding?.displayName ?? config.productName,
|
|
@@ -16743,7 +16966,7 @@ async function runServiceStatusCommand(config, configPath, rest, deps = {}) {
|
|
|
16743
16966
|
log(`detail: ${status.detail.trim() || "(none)"}`);
|
|
16744
16967
|
}
|
|
16745
16968
|
function auditLogPath(storeDir) {
|
|
16746
|
-
return
|
|
16969
|
+
return path2.join(storeDir, "audit.jsonl");
|
|
16747
16970
|
}
|
|
16748
16971
|
var AUDIT_LOG_MODE = 384;
|
|
16749
16972
|
var AUDIT_STORE_DIR_MODE = 448;
|
|
@@ -17581,11 +17804,11 @@ async function createSupportBundle(config, storeDir, options = {}) {
|
|
|
17581
17804
|
};
|
|
17582
17805
|
}
|
|
17583
17806
|
async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
|
|
17584
|
-
const dir =
|
|
17807
|
+
const dir = path2.dirname(outputPath);
|
|
17585
17808
|
const parentStat = await promises.stat(dir);
|
|
17586
17809
|
if (!parentStat.isDirectory()) throw new Error("support bundle output parent is not a directory");
|
|
17587
|
-
const privateDir =
|
|
17588
|
-
const tempPath =
|
|
17810
|
+
const privateDir = path2.join(dir, `.${path2.basename(outputPath)}.${process.pid}.${randomUUID()}.private`);
|
|
17811
|
+
const tempPath = path2.join(privateDir, "bundle.tmp");
|
|
17589
17812
|
try {
|
|
17590
17813
|
await promises.mkdir(privateDir, { mode: 448 });
|
|
17591
17814
|
await ensureSecureDir(privateDir, secureFileOptions);
|
|
@@ -17615,7 +17838,7 @@ async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
|
|
|
17615
17838
|
// src/bin/commands/support-bundle.ts
|
|
17616
17839
|
async function runSupportBundleCommand(config, options) {
|
|
17617
17840
|
if (!options.outputPath) throw new Error("support-bundle requires --output <path>");
|
|
17618
|
-
const outputPath =
|
|
17841
|
+
const outputPath = path2.resolve(options.outputPath);
|
|
17619
17842
|
const bundle = await createSupportBundle(config, resolveStoreDir(config), options);
|
|
17620
17843
|
await writeSupportBundle(outputPath, bundle);
|
|
17621
17844
|
const log = options.log ?? ((line) => console.log(line));
|