@byok-sdk/client 0.8.0-beta.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 +437 -238
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/daemon/agent-home-projection-client.d.ts +20 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +426 -224
- 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, AGENT_EGRESS_FRESH_SESSION_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 {
|
|
@@ -9950,8 +10068,8 @@ function estimateEventBytes(event) {
|
|
|
9950
10068
|
}
|
|
9951
10069
|
async function openArtifact(workspaceDir, name) {
|
|
9952
10070
|
const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
|
|
9953
|
-
const candidate =
|
|
9954
|
-
const prefix = realWorkspaceDir.endsWith(
|
|
10071
|
+
const candidate = path2.resolve(realWorkspaceDir, name);
|
|
10072
|
+
const prefix = realWorkspaceDir.endsWith(path2.sep) ? realWorkspaceDir : realWorkspaceDir + path2.sep;
|
|
9955
10073
|
if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
|
|
9956
10074
|
return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
|
|
9957
10075
|
}
|
|
@@ -10463,7 +10581,7 @@ var TaskRunner = class {
|
|
|
10463
10581
|
const sameProtocolTask = ledger?.taskId === taskId;
|
|
10464
10582
|
const interruptedOldTask = ledger?.phase === "interrupted" && sameProtocolTask;
|
|
10465
10583
|
const activeDifferentTask = ledger !== void 0 && ledger.taskId !== taskId && (ledger.phase === "preparing" || ledger.phase === "active");
|
|
10466
|
-
if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !== 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) {
|
|
10467
10585
|
decline("session is incompatible with Git workspace mode", true);
|
|
10468
10586
|
return;
|
|
10469
10587
|
}
|
|
@@ -10478,7 +10596,7 @@ var TaskRunner = class {
|
|
|
10478
10596
|
return;
|
|
10479
10597
|
}
|
|
10480
10598
|
} else {
|
|
10481
|
-
workspaceDir =
|
|
10599
|
+
workspaceDir = path2.join(this.deps.workspaceRoot, taskId);
|
|
10482
10600
|
gitWorkspaceId = randomUUID();
|
|
10483
10601
|
}
|
|
10484
10602
|
try {
|
|
@@ -10489,7 +10607,7 @@ var TaskRunner = class {
|
|
|
10489
10607
|
}
|
|
10490
10608
|
} else if (!this.deps.gitWorkspaceManager && !this.deps.gitWorkspaceStore) {
|
|
10491
10609
|
known = sessionRef ? await this.deps.sessionWorkspaces.get(sessionRef) : void 0;
|
|
10492
|
-
workspaceDir = known?.workspaceDir ??
|
|
10610
|
+
workspaceDir = known?.workspaceDir ?? path2.join(this.deps.workspaceRoot, taskId);
|
|
10493
10611
|
plainWorkspaceNeedsResolve = true;
|
|
10494
10612
|
} else {
|
|
10495
10613
|
decline("workspace mode is unavailable", true);
|
|
@@ -11876,7 +11994,7 @@ var TaskRunner = class {
|
|
|
11876
11994
|
}
|
|
11877
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. */
|
|
11878
11996
|
async resolveWorkspaceDir(taskId, reuseDir) {
|
|
11879
|
-
const dir = reuseDir ??
|
|
11997
|
+
const dir = reuseDir ?? path2.join(this.deps.workspaceRoot, taskId);
|
|
11880
11998
|
await promises.mkdir(dir, { recursive: true });
|
|
11881
11999
|
return dir;
|
|
11882
12000
|
}
|
|
@@ -12014,7 +12132,7 @@ var encoder2 = new TextEncoder();
|
|
|
12014
12132
|
function eventBytes(event) {
|
|
12015
12133
|
return encoder2.encode(JSON.stringify(event)).length;
|
|
12016
12134
|
}
|
|
12017
|
-
var AGENT_EGRESS_DIRECTORY =
|
|
12135
|
+
var AGENT_EGRESS_DIRECTORY = path2.join(".byok", "egress");
|
|
12018
12136
|
var AGENT_RELIABLE_SPOOL_FILENAME = "reliable-v1.jsonl";
|
|
12019
12137
|
var AgentReliableSpoolError = class extends Error {
|
|
12020
12138
|
constructor(message) {
|
|
@@ -12125,9 +12243,9 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
12125
12243
|
logEntries = 0;
|
|
12126
12244
|
writeTail = Promise.resolve();
|
|
12127
12245
|
static async open(homeDir) {
|
|
12128
|
-
const directory =
|
|
12246
|
+
const directory = path2.join(homeDir, AGENT_EGRESS_DIRECTORY);
|
|
12129
12247
|
await ensureSecureDir(directory);
|
|
12130
|
-
const spool = new _AgentReliableSpool(homeDir,
|
|
12248
|
+
const spool = new _AgentReliableSpool(homeDir, path2.join(directory, AGENT_RELIABLE_SPOOL_FILENAME));
|
|
12131
12249
|
await spool.load();
|
|
12132
12250
|
return spool;
|
|
12133
12251
|
}
|
|
@@ -12590,7 +12708,7 @@ var AgentEgressController = class {
|
|
|
12590
12708
|
}
|
|
12591
12709
|
/** Re-open every existing Agent-local spool before retrying stable records after restart. */
|
|
12592
12710
|
async recover(agentsRoot) {
|
|
12593
|
-
if (!
|
|
12711
|
+
if (!path2.isAbsolute(agentsRoot)) throw new Error("Agent egress recovery root must be absolute");
|
|
12594
12712
|
if (!this.active) throw new Error("Agent egress recovery requires an active authenticated enrollment");
|
|
12595
12713
|
if (this.options.tenantId === void 0) {
|
|
12596
12714
|
throw new Error("Agent egress recovery requires one authenticated tenant authority");
|
|
@@ -12605,14 +12723,14 @@ var AgentEgressController = class {
|
|
|
12605
12723
|
}
|
|
12606
12724
|
for (const entry of entries) {
|
|
12607
12725
|
if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
12608
|
-
const homeDir =
|
|
12726
|
+
const homeDir = path2.join(canonicalAgentsRoot, entry.name);
|
|
12609
12727
|
const canonicalHome = await promises.realpath(homeDir);
|
|
12610
|
-
const relativeHome =
|
|
12611
|
-
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)) {
|
|
12612
12730
|
throw new Error(`Agent egress recovery home escaped the canonical agents root: ${entry.name}`);
|
|
12613
12731
|
}
|
|
12614
12732
|
try {
|
|
12615
|
-
await promises.lstat(
|
|
12733
|
+
await promises.lstat(path2.join(homeDir, AGENT_EGRESS_DIRECTORY));
|
|
12616
12734
|
} catch (error) {
|
|
12617
12735
|
if (error.code === "ENOENT") continue;
|
|
12618
12736
|
throw error;
|
|
@@ -12699,12 +12817,12 @@ function isAgentRef(value) {
|
|
|
12699
12817
|
}
|
|
12700
12818
|
function isCanonicalRelativeTarget(value) {
|
|
12701
12819
|
if (value === "[invalid-target]") return true;
|
|
12702
|
-
if (
|
|
12820
|
+
if (path2.isAbsolute(value) || value.includes("\\")) return false;
|
|
12703
12821
|
const segments = value.split("/");
|
|
12704
12822
|
return value.length > 0 && segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
12705
12823
|
}
|
|
12706
12824
|
function validateIdentity(value, label) {
|
|
12707
|
-
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)) {
|
|
12708
12826
|
throw new AgentContentAuditStoreError(`${label} has an invalid exact Agent/session identity`);
|
|
12709
12827
|
}
|
|
12710
12828
|
return Object.freeze({
|
|
@@ -12714,7 +12832,7 @@ function validateIdentity(value, label) {
|
|
|
12714
12832
|
}),
|
|
12715
12833
|
sessionRef: value.sessionRef,
|
|
12716
12834
|
runtimeId: value.runtimeId,
|
|
12717
|
-
cwd:
|
|
12835
|
+
cwd: path2.resolve(value.cwd)
|
|
12718
12836
|
});
|
|
12719
12837
|
}
|
|
12720
12838
|
function validateReceipt(value) {
|
|
@@ -12796,16 +12914,16 @@ function assertUniqueRequestIds(entries) {
|
|
|
12796
12914
|
}
|
|
12797
12915
|
}
|
|
12798
12916
|
function assertAbsoluteFilePath(filePath) {
|
|
12799
|
-
if (typeof filePath !== "string" || filePath.length === 0 || !
|
|
12917
|
+
if (typeof filePath !== "string" || filePath.length === 0 || !path2.isAbsolute(filePath)) {
|
|
12800
12918
|
throw new AgentContentAuditStoreError("content audit path must be absolute");
|
|
12801
12919
|
}
|
|
12802
12920
|
if (/[\u0000\r\n]/u.test(filePath)) {
|
|
12803
12921
|
throw new AgentContentAuditStoreError("content audit path must not contain NUL or line breaks");
|
|
12804
12922
|
}
|
|
12805
|
-
return
|
|
12923
|
+
return path2.resolve(filePath);
|
|
12806
12924
|
}
|
|
12807
12925
|
async function ensureDirectoryNoSymlink2(directory) {
|
|
12808
|
-
const absolute =
|
|
12926
|
+
const absolute = path2.resolve(directory);
|
|
12809
12927
|
await promises.mkdir(absolute, { recursive: true, mode: 448 });
|
|
12810
12928
|
const stat = await promises.lstat(absolute);
|
|
12811
12929
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -12839,12 +12957,12 @@ var AgentContentAuditStore = class _AgentContentAuditStore {
|
|
|
12839
12957
|
/** The daemon may address this ledger only through an AgentHomeLayout resolution. */
|
|
12840
12958
|
static forCanonicalAgentHome(canonicalHome) {
|
|
12841
12959
|
const home = assertAbsoluteFilePath(canonicalHome);
|
|
12842
|
-
return new _AgentContentAuditStore(
|
|
12960
|
+
return new _AgentContentAuditStore(path2.join(home, AGENT_HOME_INTERNAL_DIRECTORY, AGENT_CONTENT_AUDIT_FILENAME));
|
|
12843
12961
|
}
|
|
12844
12962
|
async append(receipt) {
|
|
12845
12963
|
const validated = validateReceipt(receipt);
|
|
12846
12964
|
return this.enqueue(async () => {
|
|
12847
|
-
await ensureDirectoryNoSymlink2(
|
|
12965
|
+
await ensureDirectoryNoSymlink2(path2.dirname(this.filePath));
|
|
12848
12966
|
await assertAuditFile(this.filePath);
|
|
12849
12967
|
const entries = await this.readAllUnlocked();
|
|
12850
12968
|
const prior = entries.find((entry) => entry.requestId === validated.requestId);
|
|
@@ -12939,6 +13057,63 @@ var AgentContentAuditStore = class _AgentContentAuditStore {
|
|
|
12939
13057
|
return result;
|
|
12940
13058
|
}
|
|
12941
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
|
+
};
|
|
12942
13117
|
var AGENT_CONTENT_READ_SURFACES = ["workspace", "transcript", "artifact"];
|
|
12943
13118
|
var AGENT_CONTENT_READ_CAPABILITIES = Object.freeze({
|
|
12944
13119
|
workspace: AGENT_CONTENT_WORKSPACE_READ_CAPABILITY,
|
|
@@ -13042,8 +13217,8 @@ function normalizeIdentity(value, field) {
|
|
|
13042
13217
|
const sessionRef = nonEmptyString(value.sessionRef, `${field}.sessionRef`);
|
|
13043
13218
|
const runtimeId = nonEmptyString(value.runtimeId, `${field}.runtimeId`);
|
|
13044
13219
|
const cwd = nonEmptyString(value.cwd, `${field}.cwd`);
|
|
13045
|
-
if (!
|
|
13046
|
-
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) });
|
|
13047
13222
|
}
|
|
13048
13223
|
function createAgentContentReadPolicy(input) {
|
|
13049
13224
|
if (!isRecord5(input) || input.enabled !== true) {
|
|
@@ -13071,10 +13246,10 @@ function createAgentContentReadPolicy(input) {
|
|
|
13071
13246
|
root = Object.freeze({ kind: "agent-home" });
|
|
13072
13247
|
} else if (input.root.kind === "runtime-allowlisted") {
|
|
13073
13248
|
const configuredRoot = nonEmptyString(input.root.root, "contentRead.root.root");
|
|
13074
|
-
if (!
|
|
13249
|
+
if (!path2.isAbsolute(configuredRoot)) {
|
|
13075
13250
|
throw new AgentContentReadPolicyError("contentRead.root.root must be absolute");
|
|
13076
13251
|
}
|
|
13077
|
-
root = Object.freeze({ kind: "runtime-allowlisted", root:
|
|
13252
|
+
root = Object.freeze({ kind: "runtime-allowlisted", root: path2.resolve(configuredRoot) });
|
|
13078
13253
|
} else {
|
|
13079
13254
|
throw new AgentContentReadPolicyError("contentRead.root.kind is not supported");
|
|
13080
13255
|
}
|
|
@@ -13097,11 +13272,11 @@ function createAgentContentReadPolicy(input) {
|
|
|
13097
13272
|
});
|
|
13098
13273
|
}
|
|
13099
13274
|
function isWithin2(root, candidate) {
|
|
13100
|
-
const relative =
|
|
13101
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
13275
|
+
const relative = path2.relative(root, candidate);
|
|
13276
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
|
|
13102
13277
|
}
|
|
13103
13278
|
function isPortableAbsoluteTarget(value) {
|
|
13104
|
-
return
|
|
13279
|
+
return path2.isAbsolute(value) || /^[a-z]:[\\/]/iu.test(value) || /^[/\\]/u.test(value);
|
|
13105
13280
|
}
|
|
13106
13281
|
function canonicalAuditTarget(value) {
|
|
13107
13282
|
if (typeof value !== "string" || value.length === 0 || /[\u0000\r\n]/u.test(value) || isPortableAbsoluteTarget(value) || value.includes("\\")) {
|
|
@@ -13139,7 +13314,7 @@ function isSensitiveTarget(segments, productNames) {
|
|
|
13139
13314
|
return segments.some((segment) => patterns.some((pattern) => nameMatches(pattern, segment)));
|
|
13140
13315
|
}
|
|
13141
13316
|
async function resolveExistingAncestor2(inputPath) {
|
|
13142
|
-
let cursor =
|
|
13317
|
+
let cursor = path2.resolve(inputPath);
|
|
13143
13318
|
const tail = [];
|
|
13144
13319
|
for (; ; ) {
|
|
13145
13320
|
try {
|
|
@@ -13147,9 +13322,9 @@ async function resolveExistingAncestor2(inputPath) {
|
|
|
13147
13322
|
} catch (error) {
|
|
13148
13323
|
const code = error.code;
|
|
13149
13324
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
13150
|
-
const parent =
|
|
13325
|
+
const parent = path2.dirname(cursor);
|
|
13151
13326
|
if (parent === cursor) throw new TargetPolicyError("target-missing");
|
|
13152
|
-
tail.unshift(
|
|
13327
|
+
tail.unshift(path2.basename(cursor));
|
|
13153
13328
|
cursor = parent;
|
|
13154
13329
|
}
|
|
13155
13330
|
}
|
|
@@ -13170,11 +13345,11 @@ var RootPolicyError = class extends Error {
|
|
|
13170
13345
|
this.reason = reason;
|
|
13171
13346
|
}
|
|
13172
13347
|
};
|
|
13173
|
-
function
|
|
13348
|
+
function sameAgentRef3(left, right) {
|
|
13174
13349
|
return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
|
|
13175
13350
|
}
|
|
13176
13351
|
function sameSessionIdentity(left, right) {
|
|
13177
|
-
return
|
|
13352
|
+
return sameAgentRef3(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
|
|
13178
13353
|
}
|
|
13179
13354
|
function validateRequest(request) {
|
|
13180
13355
|
if (!isRecord5(request)) throw new AgentContentReadRequestError("content read request must be an object");
|
|
@@ -13236,18 +13411,18 @@ function normalizeRequestIdentity(value, field) {
|
|
|
13236
13411
|
const sessionRef = requestString(value.sessionRef, `${field}.sessionRef`);
|
|
13237
13412
|
const runtimeId = requestString(value.runtimeId, `${field}.runtimeId`);
|
|
13238
13413
|
const cwd = requestString(value.cwd, `${field}.cwd`);
|
|
13239
|
-
if (!
|
|
13240
|
-
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) });
|
|
13241
13416
|
}
|
|
13242
13417
|
async function inspectRegularTarget(root, target) {
|
|
13243
13418
|
const ancestor = await resolveExistingAncestor2(target);
|
|
13244
13419
|
if (!isWithin2(root, ancestor.canonical)) {
|
|
13245
13420
|
throw new TargetPolicyError("path-escape");
|
|
13246
13421
|
}
|
|
13247
|
-
const components =
|
|
13422
|
+
const components = path2.relative(root, target).split(path2.sep).filter((component) => component.length > 0);
|
|
13248
13423
|
let cursor = root;
|
|
13249
13424
|
for (const [index, component] of components.entries()) {
|
|
13250
|
-
cursor =
|
|
13425
|
+
cursor = path2.join(cursor, component);
|
|
13251
13426
|
let stat;
|
|
13252
13427
|
try {
|
|
13253
13428
|
stat = await promises.lstat(cursor);
|
|
@@ -13312,8 +13487,8 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13312
13487
|
this.capabilities = new Set(options.capabilities);
|
|
13313
13488
|
this.runtimeRoots = Object.freeze((options.runtimeAllowlistedRoots ?? []).map((root, index) => {
|
|
13314
13489
|
const value = nonEmptyString(root, `contentRead.runtimeAllowlistedRoots[${index}]`);
|
|
13315
|
-
if (!
|
|
13316
|
-
return
|
|
13490
|
+
if (!path2.isAbsolute(value)) throw new AgentContentReadPolicyError("runtime allowlisted roots must be absolute");
|
|
13491
|
+
return path2.resolve(value);
|
|
13317
13492
|
}));
|
|
13318
13493
|
this.resolveSessionIdentity = options.resolveSessionIdentity;
|
|
13319
13494
|
this.resolveTranscriptIdentity = options.resolveTranscriptIdentity;
|
|
@@ -13365,7 +13540,7 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13365
13540
|
if (request.decodeAs === "utf8" && !policy.textMimeTypes.includes(request.mimeType)) {
|
|
13366
13541
|
return this.deny(request, relativeTarget, "text-not-allowlisted");
|
|
13367
13542
|
}
|
|
13368
|
-
const target =
|
|
13543
|
+
const target = path2.resolve(root, ...segments);
|
|
13369
13544
|
if (!isWithin2(root, target)) return this.deny(request, relativeTarget, "path-escape");
|
|
13370
13545
|
try {
|
|
13371
13546
|
await inspectRegularTarget(root, target);
|
|
@@ -13446,7 +13621,7 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13446
13621
|
}
|
|
13447
13622
|
async checkSessionIdentity(request, resolver, requiredCwd) {
|
|
13448
13623
|
const session = request.session;
|
|
13449
|
-
if (session === void 0 || !
|
|
13624
|
+
if (session === void 0 || !sameAgentRef3(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
|
|
13450
13625
|
return "identity-mismatch";
|
|
13451
13626
|
}
|
|
13452
13627
|
let expected;
|
|
@@ -13562,7 +13737,7 @@ async function detectRuntimes(adapters) {
|
|
|
13562
13737
|
}
|
|
13563
13738
|
return runtimes;
|
|
13564
13739
|
}
|
|
13565
|
-
function computeCapabilities(adapters, agentHomeConfigured = false, agentEgressConfigured = false, contentReadPolicies) {
|
|
13740
|
+
function computeCapabilities(adapters, agentHomeConfigured = false, agentHomeProjectionConfigured = false, agentEgressConfigured = false, contentReadPolicies) {
|
|
13566
13741
|
const flags = [];
|
|
13567
13742
|
if (adapters.some((adapter) => adapter.descriptor.capabilities.steer)) flags.push("steer");
|
|
13568
13743
|
flags.push("blob-upload");
|
|
@@ -13577,6 +13752,7 @@ function computeCapabilities(adapters, agentHomeConfigured = false, agentEgressC
|
|
|
13577
13752
|
flags.push("toolset-selection");
|
|
13578
13753
|
}
|
|
13579
13754
|
if (agentHomeConfigured) flags.push("agent-home-contract");
|
|
13755
|
+
if (agentHomeProjectionConfigured) flags.push(AGENT_HOME_PROJECTION_CAPABILITY);
|
|
13580
13756
|
if (agentEgressConfigured) {
|
|
13581
13757
|
flags.push(
|
|
13582
13758
|
AGENT_EGRESS_POLICY_CAPABILITY,
|
|
@@ -13935,7 +14111,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
13935
14111
|
}
|
|
13936
14112
|
await agentHomeManager?.preflight();
|
|
13937
14113
|
if (config.agentEgress !== void 0 && config.agentHome !== void 0) {
|
|
13938
|
-
await agentEgress.recover(
|
|
14114
|
+
await agentEgress.recover(path2.join(config.agentHome.hostStorageRoot, "agents"));
|
|
13939
14115
|
}
|
|
13940
14116
|
fleetJitter = createFleetJitter(config.productId, record.deviceId);
|
|
13941
14117
|
if (config.permissionDefaults?.workspaceRoot !== void 0) {
|
|
@@ -13990,9 +14166,16 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
13990
14166
|
const capabilities = computeCapabilities(
|
|
13991
14167
|
adapters,
|
|
13992
14168
|
config.agentHome !== void 0,
|
|
14169
|
+
agentHomeManager?.supportsTaskFreeProjection() === true,
|
|
13993
14170
|
config.agentEgress !== void 0,
|
|
13994
14171
|
agentContentReadPolicies
|
|
13995
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;
|
|
13996
14179
|
const journalIdentity = config.hostedJournal ? { tenantId: record.tenantId, productId: config.productId, deviceId: record.deviceId } : void 0;
|
|
13997
14180
|
const sendSanitizedEnvelope = activeJournal && journalIdentity ? (envelope) => {
|
|
13998
14181
|
observer.handleOutboundEnvelope(envelope);
|
|
@@ -14129,6 +14312,20 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14129
14312
|
...activePressureEngine ? { admissionGuard: () => activePressureEngine.admissionGuard() } : {}
|
|
14130
14313
|
};
|
|
14131
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
|
+
};
|
|
14132
14329
|
const handleAgentEgressEnvelope = async (envelope) => {
|
|
14133
14330
|
if (envelope.type !== "agent.egress.ack") return false;
|
|
14134
14331
|
if (config.agentEgress === void 0) return true;
|
|
@@ -14284,6 +14481,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14284
14481
|
throw new Error("tenant enrollment is being re-paired; inbound work is blocked until restart");
|
|
14285
14482
|
}
|
|
14286
14483
|
observer.handleInboundEnvelope(envelope);
|
|
14484
|
+
if (await handleAgentHomeProjectionEnvelope(envelope)) return;
|
|
14287
14485
|
if (await handleAgentEgressEnvelope(envelope)) return;
|
|
14288
14486
|
if (await handleAgentContentReadEnvelope(envelope)) return;
|
|
14289
14487
|
activePressureEngine?.assertAckCriticalAllowed();
|
|
@@ -14294,6 +14492,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14294
14492
|
return Promise.reject(new Error("tenant enrollment is being re-paired; inbound work is blocked until restart"));
|
|
14295
14493
|
}
|
|
14296
14494
|
observer.handleInboundEnvelope(envelope);
|
|
14495
|
+
if (envelope.type === "agent.home.projection") return handleAgentHomeProjectionEnvelope(envelope).then(() => void 0);
|
|
14297
14496
|
if (envelope.type === "agent.egress.ack") return handleAgentEgressEnvelope(envelope).then(() => void 0);
|
|
14298
14497
|
if (envelope.type === "agent.content.read") return handleAgentContentReadEnvelope(envelope).then(() => void 0);
|
|
14299
14498
|
return runner?.handleEnvelope(envelope) ?? Promise.resolve();
|
|
@@ -15156,8 +15355,8 @@ function generateLaunchdPlist(def) {
|
|
|
15156
15355
|
const { label, program, logDir } = def;
|
|
15157
15356
|
const args = [program.command, ...program.args];
|
|
15158
15357
|
const cwd = program.cwd ?? os.homedir();
|
|
15159
|
-
const outLog =
|
|
15160
|
-
const errLog =
|
|
15358
|
+
const outLog = path2.join(logDir, `${label}.out.log`);
|
|
15359
|
+
const errLog = path2.join(logDir, `${label}.err.log`);
|
|
15161
15360
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
15162
15361
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
15163
15362
|
<plist version="1.0">
|
|
@@ -15198,7 +15397,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
15198
15397
|
return process.getuid();
|
|
15199
15398
|
});
|
|
15200
15399
|
const label = sanitizeServiceName(def.name);
|
|
15201
|
-
const plistPath = () =>
|
|
15400
|
+
const plistPath = () => path2.join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
|
|
15202
15401
|
const domainTarget = () => `gui/${getuid()}`;
|
|
15203
15402
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
15204
15403
|
async function fileExists(p) {
|
|
@@ -15211,7 +15410,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
15211
15410
|
}
|
|
15212
15411
|
async function writePlist(program) {
|
|
15213
15412
|
const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
|
|
15214
|
-
await fs26.mkdir(
|
|
15413
|
+
await fs26.mkdir(path2.dirname(plistPath()), { recursive: true });
|
|
15215
15414
|
await fs26.mkdir(def.logDir, { recursive: true });
|
|
15216
15415
|
await fs26.writeFile(plistPath(), xml, "utf8");
|
|
15217
15416
|
}
|
|
@@ -15285,8 +15484,8 @@ function generateSystemdUnit(def) {
|
|
|
15285
15484
|
assertNoControlChars(displayName, "displayName");
|
|
15286
15485
|
const cwd = program.cwd ?? os.homedir();
|
|
15287
15486
|
assertNoControlChars(cwd, "program.cwd");
|
|
15288
|
-
const outLog =
|
|
15289
|
-
const errLog =
|
|
15487
|
+
const outLog = path2.join(logDir, `${name}.out.log`);
|
|
15488
|
+
const errLog = path2.join(logDir, `${name}.err.log`);
|
|
15290
15489
|
assertNoControlChars(outLog, "logDir");
|
|
15291
15490
|
assertNoControlChars(errLog, "logDir");
|
|
15292
15491
|
const execStart = [program.command, ...program.args].map(quoteSystemdArg).join(" ");
|
|
@@ -15312,7 +15511,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
15312
15511
|
const homedir = deps.homedir ?? (() => os.homedir());
|
|
15313
15512
|
const name = sanitizeServiceName(def.name);
|
|
15314
15513
|
const unitName = `${name}.service`;
|
|
15315
|
-
const unitPath = () =>
|
|
15514
|
+
const unitPath = () => path2.join(homedir(), ".config", "systemd", "user", unitName);
|
|
15316
15515
|
async function fileExists(p) {
|
|
15317
15516
|
try {
|
|
15318
15517
|
await fs26.stat(p);
|
|
@@ -15323,7 +15522,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
15323
15522
|
}
|
|
15324
15523
|
async function writeUnit(program) {
|
|
15325
15524
|
const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
15326
|
-
await fs26.mkdir(
|
|
15525
|
+
await fs26.mkdir(path2.dirname(unitPath()), { recursive: true });
|
|
15327
15526
|
await fs26.mkdir(def.logDir, { recursive: true });
|
|
15328
15527
|
await fs26.writeFile(unitPath(), unit, "utf8");
|
|
15329
15528
|
}
|
|
@@ -15405,8 +15604,8 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
15405
15604
|
const winswBin = windows.winswBin;
|
|
15406
15605
|
const id = sanitizeServiceName(def.name);
|
|
15407
15606
|
const installDir = windows.installDir ?? def.logDir;
|
|
15408
|
-
const exePath =
|
|
15409
|
-
const xmlPath =
|
|
15607
|
+
const exePath = path2.join(installDir, `${id}.exe`);
|
|
15608
|
+
const xmlPath = path2.join(installDir, `${id}.xml`);
|
|
15410
15609
|
async function fileExists(p) {
|
|
15411
15610
|
try {
|
|
15412
15611
|
await fs26.stat(p);
|
|
@@ -15476,7 +15675,7 @@ function createServiceLifecycle(def, opts = {}) {
|
|
|
15476
15675
|
|
|
15477
15676
|
// src/bin/official-release.ts
|
|
15478
15677
|
var OFFICIAL_LOCAL_AGENT_RELEASE = resolveLocalAgentReleaseIdentity({
|
|
15479
|
-
version: "0.8.0
|
|
15678
|
+
version: "0.8.0"
|
|
15480
15679
|
});
|
|
15481
15680
|
|
|
15482
15681
|
// src/bin/config.ts
|
|
@@ -15961,7 +16160,7 @@ function safeProtocol(serverUrl) {
|
|
|
15961
16160
|
}
|
|
15962
16161
|
}
|
|
15963
16162
|
async function inspectDevice(storeDir) {
|
|
15964
|
-
const filePath =
|
|
16163
|
+
const filePath = path2.join(storeDir, "device.json");
|
|
15965
16164
|
let pathStat;
|
|
15966
16165
|
try {
|
|
15967
16166
|
pathStat = await promises.lstat(filePath);
|
|
@@ -16041,11 +16240,11 @@ async function copyOpenFileBounded(source, expected, destinationPath) {
|
|
|
16041
16240
|
}
|
|
16042
16241
|
}
|
|
16043
16242
|
async function inspectJournal(storeDir) {
|
|
16044
|
-
const journalPath =
|
|
16243
|
+
const journalPath = path2.join(storeDir, JOURNAL_DB_FILENAME);
|
|
16045
16244
|
try {
|
|
16046
16245
|
const mainIdentity = await regularFileIdentity(journalPath);
|
|
16047
16246
|
if (mainIdentity === void 0) return { status: "missing" };
|
|
16048
|
-
const walIdentity = await regularFileIdentity(
|
|
16247
|
+
const walIdentity = await regularFileIdentity(path2.join(storeDir, `${JOURNAL_DB_FILENAME}-wal`));
|
|
16049
16248
|
let sizeBytes = Number(mainIdentity.size);
|
|
16050
16249
|
let walBytes = walIdentity === void 0 ? void 0 : Number(walIdentity.size);
|
|
16051
16250
|
if (!isSqliteAvailable()) {
|
|
@@ -16053,7 +16252,7 @@ async function inspectJournal(storeDir) {
|
|
|
16053
16252
|
}
|
|
16054
16253
|
const componentNames = [JOURNAL_DB_FILENAME, `${JOURNAL_DB_FILENAME}-wal`, `${JOURNAL_DB_FILENAME}-shm`];
|
|
16055
16254
|
const initial = /* @__PURE__ */ new Map();
|
|
16056
|
-
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)));
|
|
16057
16256
|
const snapshotMain = initial.get(JOURNAL_DB_FILENAME);
|
|
16058
16257
|
if (!snapshotMain) return { status: "unavailable", reason: "journal changed during diagnostics snapshot" };
|
|
16059
16258
|
sizeBytes = Number(snapshotMain.size);
|
|
@@ -16065,7 +16264,7 @@ async function inspectJournal(storeDir) {
|
|
|
16065
16264
|
let handle;
|
|
16066
16265
|
try {
|
|
16067
16266
|
handle = await promises.open(
|
|
16068
|
-
|
|
16267
|
+
path2.join(storeDir, name),
|
|
16069
16268
|
constants.O_RDONLY | constants.O_NONBLOCK | (constants.O_NOFOLLOW ?? 0)
|
|
16070
16269
|
);
|
|
16071
16270
|
} catch (err) {
|
|
@@ -16094,28 +16293,28 @@ async function inspectJournal(storeDir) {
|
|
|
16094
16293
|
reason: "journal exceeds the bounded diagnostics copy limit"
|
|
16095
16294
|
};
|
|
16096
16295
|
}
|
|
16097
|
-
const tempDir = await promises.mkdtemp(
|
|
16296
|
+
const tempDir = await promises.mkdtemp(path2.join(os.tmpdir(), "byok-journal-inspect-"));
|
|
16098
16297
|
const { DatabaseSync } = loadSqliteModule();
|
|
16099
16298
|
let db;
|
|
16100
16299
|
try {
|
|
16101
16300
|
for (const name of componentNames) {
|
|
16102
16301
|
const component = opened.get(name);
|
|
16103
|
-
if (component && !await copyOpenFileBounded(component.handle, component.identity,
|
|
16302
|
+
if (component && !await copyOpenFileBounded(component.handle, component.identity, path2.join(tempDir, name))) {
|
|
16104
16303
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
16105
16304
|
}
|
|
16106
16305
|
}
|
|
16107
16306
|
for (const [name, component] of opened) {
|
|
16108
|
-
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)))) {
|
|
16109
16308
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
16110
16309
|
}
|
|
16111
16310
|
}
|
|
16112
16311
|
for (const name of componentNames) {
|
|
16113
|
-
if (!opened.has(name) && await regularFileIdentity(
|
|
16312
|
+
if (!opened.has(name) && await regularFileIdentity(path2.join(storeDir, name)) !== void 0) {
|
|
16114
16313
|
return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
|
|
16115
16314
|
}
|
|
16116
16315
|
}
|
|
16117
16316
|
const header = Buffer.alloc(16);
|
|
16118
|
-
const copiedHandle = await promises.open(
|
|
16317
|
+
const copiedHandle = await promises.open(path2.join(tempDir, JOURNAL_DB_FILENAME), "r");
|
|
16119
16318
|
try {
|
|
16120
16319
|
const { bytesRead } = await copiedHandle.read(header, 0, header.length, 0);
|
|
16121
16320
|
if (bytesRead !== 16 || header.toString("binary") !== "SQLite format 3\0") {
|
|
@@ -16124,7 +16323,7 @@ async function inspectJournal(storeDir) {
|
|
|
16124
16323
|
} finally {
|
|
16125
16324
|
await copiedHandle.close();
|
|
16126
16325
|
}
|
|
16127
|
-
db = new DatabaseSync(
|
|
16326
|
+
db = new DatabaseSync(path2.join(tempDir, JOURNAL_DB_FILENAME), { readOnly: true });
|
|
16128
16327
|
const result = db.prepare("PRAGMA quick_check(1)").get();
|
|
16129
16328
|
if (result?.quick_check !== "ok") {
|
|
16130
16329
|
return { status: "corrupt", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal quick_check failed" };
|
|
@@ -16159,7 +16358,7 @@ async function inspectWorkspace(workspaceRoot) {
|
|
|
16159
16358
|
}
|
|
16160
16359
|
}
|
|
16161
16360
|
function readPinnedQuarantineFile(name, maxBytes, includeBytes, budget) {
|
|
16162
|
-
if (
|
|
16361
|
+
if (path2.basename(name) !== name || name === "." || name === "..") {
|
|
16163
16362
|
throw new Error("quarantine manifest contains an invalid evidence name");
|
|
16164
16363
|
}
|
|
16165
16364
|
const namedBefore = lstatSync(name, { bigint: true });
|
|
@@ -16263,10 +16462,10 @@ function inspectQuarantinePinned(dir, expectedDirectory) {
|
|
|
16263
16462
|
if (isJournalQuarantineManifest(parsed)) {
|
|
16264
16463
|
const manifestBase = manifestName.slice(0, -".manifest.json".length);
|
|
16265
16464
|
const boundNames = parsed.files.map((file) => {
|
|
16266
|
-
if (
|
|
16465
|
+
if (path2.dirname(path2.resolve(file)) !== path2.resolve(".")) {
|
|
16267
16466
|
throw new Error("journal quarantine manifest points outside quarantine");
|
|
16268
16467
|
}
|
|
16269
|
-
return
|
|
16468
|
+
return path2.basename(file);
|
|
16270
16469
|
});
|
|
16271
16470
|
if (!boundNames.includes(manifestBase)) {
|
|
16272
16471
|
throw new Error("journal quarantine manifest is not bound to its primary database evidence");
|
|
@@ -16306,7 +16505,7 @@ function inspectQuarantinePinned(dir, expectedDirectory) {
|
|
|
16306
16505
|
}
|
|
16307
16506
|
}
|
|
16308
16507
|
async function inspectQuarantine(storeDir) {
|
|
16309
|
-
const dir =
|
|
16508
|
+
const dir = path2.join(storeDir, JOURNAL_QUARANTINE_DIRNAME);
|
|
16310
16509
|
let directory;
|
|
16311
16510
|
try {
|
|
16312
16511
|
directory = await promises.lstat(dir, { bigint: true });
|
|
@@ -16377,7 +16576,7 @@ function checksFor(snapshot) {
|
|
|
16377
16576
|
];
|
|
16378
16577
|
}
|
|
16379
16578
|
async function collectDiagnostics(config, storeDir, options = {}) {
|
|
16380
|
-
const resolvedStoreDir =
|
|
16579
|
+
const resolvedStoreDir = path2.resolve(storeDir);
|
|
16381
16580
|
const adapters = options.adapters ?? defaultRuntimeAdapters(config.runtimeAllowlist);
|
|
16382
16581
|
const connectControl = options.connectControl ?? connectControlClient;
|
|
16383
16582
|
const [device, probedRuntimes, health, journal, workspace, quarantine, controlConnection] = await Promise.all([
|
|
@@ -16552,7 +16751,7 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
|
|
|
16552
16751
|
unlinkSync(sourcePath);
|
|
16553
16752
|
sourceRemoved = true;
|
|
16554
16753
|
if (process.platform !== "win32") {
|
|
16555
|
-
const directoryFd = openSync(
|
|
16754
|
+
const directoryFd = openSync(path2.dirname(sourcePath), constants.O_RDONLY);
|
|
16556
16755
|
try {
|
|
16557
16756
|
fsyncSync(directoryFd);
|
|
16558
16757
|
} finally {
|
|
@@ -16591,10 +16790,10 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
|
|
|
16591
16790
|
}
|
|
16592
16791
|
}
|
|
16593
16792
|
async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
|
|
16594
|
-
const resolvedStoreDir =
|
|
16793
|
+
const resolvedStoreDir = path2.resolve(storeDir);
|
|
16595
16794
|
const owner = await acquireDaemonOwner(resolvedStoreDir, "doctor", options.clock);
|
|
16596
16795
|
try {
|
|
16597
|
-
const sourcePath =
|
|
16796
|
+
const sourcePath = path2.join(resolvedStoreDir, OPERATIONAL_HEALTH_FILENAME);
|
|
16598
16797
|
let opened;
|
|
16599
16798
|
try {
|
|
16600
16799
|
opened = await openOperationalHealthFile(resolvedStoreDir);
|
|
@@ -16611,7 +16810,7 @@ async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
|
|
|
16611
16810
|
}
|
|
16612
16811
|
const sourceStat = await source.stat({ bigint: true });
|
|
16613
16812
|
if (!sourceStat.isFile()) throw new Error("operational health state is not a regular file; refusing quarantine");
|
|
16614
|
-
const quarantineDir =
|
|
16813
|
+
const quarantineDir = path2.join(resolvedStoreDir, JOURNAL_QUARANTINE_DIRNAME);
|
|
16615
16814
|
try {
|
|
16616
16815
|
const existing = await promises.lstat(quarantineDir);
|
|
16617
16816
|
if (!existing.isDirectory() || existing.isSymbolicLink()) {
|
|
@@ -16712,8 +16911,8 @@ function buildServiceDefinition(config, configPath, rest) {
|
|
|
16712
16911
|
const name = argValue(rest, "--name") ?? config.productId;
|
|
16713
16912
|
const agentBin = argValue(rest, "--agent-bin") ?? process.argv[1] ?? "byok-agent";
|
|
16714
16913
|
const nodeBin = argValue(rest, "--node-bin") ?? process.execPath;
|
|
16715
|
-
const absoluteConfigPath =
|
|
16716
|
-
const logDir =
|
|
16914
|
+
const absoluteConfigPath = path2.resolve(configPath);
|
|
16915
|
+
const logDir = path2.join(resolveStoreDir(config), "service-logs");
|
|
16717
16916
|
const definition = {
|
|
16718
16917
|
name,
|
|
16719
16918
|
displayName: config.branding?.displayName ?? config.productName,
|
|
@@ -16767,7 +16966,7 @@ async function runServiceStatusCommand(config, configPath, rest, deps = {}) {
|
|
|
16767
16966
|
log(`detail: ${status.detail.trim() || "(none)"}`);
|
|
16768
16967
|
}
|
|
16769
16968
|
function auditLogPath(storeDir) {
|
|
16770
|
-
return
|
|
16969
|
+
return path2.join(storeDir, "audit.jsonl");
|
|
16771
16970
|
}
|
|
16772
16971
|
var AUDIT_LOG_MODE = 384;
|
|
16773
16972
|
var AUDIT_STORE_DIR_MODE = 448;
|
|
@@ -17605,11 +17804,11 @@ async function createSupportBundle(config, storeDir, options = {}) {
|
|
|
17605
17804
|
};
|
|
17606
17805
|
}
|
|
17607
17806
|
async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
|
|
17608
|
-
const dir =
|
|
17807
|
+
const dir = path2.dirname(outputPath);
|
|
17609
17808
|
const parentStat = await promises.stat(dir);
|
|
17610
17809
|
if (!parentStat.isDirectory()) throw new Error("support bundle output parent is not a directory");
|
|
17611
|
-
const privateDir =
|
|
17612
|
-
const tempPath =
|
|
17810
|
+
const privateDir = path2.join(dir, `.${path2.basename(outputPath)}.${process.pid}.${randomUUID()}.private`);
|
|
17811
|
+
const tempPath = path2.join(privateDir, "bundle.tmp");
|
|
17613
17812
|
try {
|
|
17614
17813
|
await promises.mkdir(privateDir, { mode: 448 });
|
|
17615
17814
|
await ensureSecureDir(privateDir, secureFileOptions);
|
|
@@ -17639,7 +17838,7 @@ async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
|
|
|
17639
17838
|
// src/bin/commands/support-bundle.ts
|
|
17640
17839
|
async function runSupportBundleCommand(config, options) {
|
|
17641
17840
|
if (!options.outputPath) throw new Error("support-bundle requires --output <path>");
|
|
17642
|
-
const outputPath =
|
|
17841
|
+
const outputPath = path2.resolve(options.outputPath);
|
|
17643
17842
|
const bundle = await createSupportBundle(config, resolveStoreDir(config), options);
|
|
17644
17843
|
await writeSupportBundle(outputPath, bundle);
|
|
17645
17844
|
const log = options.log ?? ((line) => console.log(line));
|