@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/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID, createHash, sign, createPrivateKey, generateKeyPairSync, randomBytes, timingSafeEqual, createHmac } from 'crypto';
|
|
2
2
|
import { promises, mkdirSync, existsSync, renameSync, writeFileSync, chmodSync, statSync, readdirSync, linkSync, fstatSync, lstatSync, unlinkSync, constants, readFileSync, realpathSync } from 'fs';
|
|
3
|
-
import
|
|
4
|
-
import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AgentRefSchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, partitionAgentEvents, BYOK_SKILL_PACKS_PATH, byokSkillPackFilePath, BYOK_RECORDS_PATH, byokRecordPath, TASK_TRANSITIONS, AgentEgressPolicySchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentContentReceiptPayloadSchema, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, 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';
|
|
3
|
+
import path2, { join, isAbsolute } from 'path';
|
|
4
|
+
import { AGENT_CONTENT_ARTIFACT_READ_CAPABILITY, AGENT_CONTENT_TRANSCRIPT_READ_CAPABILITY, AGENT_CONTENT_WORKSPACE_READ_CAPABILITY, AgentRefSchema, AgentHomeProjectionPayloadSchema, BYOK_PAIR_PATH, PairResponseSchema, BYOK_CHALLENGE_PATH, BYOK_TOKEN_PATH, byokBlobUrlPath, BYOK_BLOBS_PATH, byokBlobFinalizePath, partitionAgentEvents, BYOK_SKILL_PACKS_PATH, byokSkillPackFilePath, BYOK_RECORDS_PATH, byokRecordPath, TASK_TRANSITIONS, AgentEgressPolicySchema, CONFIGURED_TOOLSETS_MAX_ITEMS, ToolsetIdSchema, AgentContentReceiptPayloadSchema, encodeEnvelope, PROTOCOL_VERSION, createEnvelope, AGENT_EGRESS_RELIABLE_ACK_CAPABILITY, 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';
|
|
5
5
|
import { execFile, spawn } from 'child_process';
|
|
6
6
|
import os6 from 'os';
|
|
7
7
|
import { parseDeviceAssertionEnvelope, tenantId, DeviceProofProtectedClaimsSchema, deviceProofSigningInput, DEVICE_PROOF_SCHEMA_ID, SKILL_PACK_MAX_BYTES, hasCapability, parseSkillPackManifest, checkSkillPackManifest, skillPackContentHashInput, checkSkillPackFileContent, SKILL_PACK_ENTRY_PATH, checkSkillPackEntry, isSkillPackPathSafe, DEVICE_PROOF_HEADER, contentHash as contentHash$1, TRUTH_RECORD_KINDS, nonceSigningBytes, DEVICE_ASSERTION_AUDIENCE_MAX_BYTES, DEVICE_ASSERTION_DEFAULT_TTL_MS, DEVICE_ASSERTION_MAX_TTL_MS, CONTENT_HASH_PATTERN, isTenantId, CapabilityDeclarationSchema, DeviceAssertionClaimsSchema, deviceAssertionSigningInput, DEVICE_ASSERTION_SCHEMA_ID } from '@byok-sdk/core';
|
|
@@ -12,9 +12,80 @@ import net, { createServer, createConnection } from 'net';
|
|
|
12
12
|
import { WebSocket } from 'ws';
|
|
13
13
|
import { createRequire } from 'module';
|
|
14
14
|
|
|
15
|
+
// src/agent-home.ts
|
|
16
|
+
var tmpSeq = 0;
|
|
17
|
+
async function atomicWriteFile(filePath, data, options = {}) {
|
|
18
|
+
const tmpPath = `${filePath}.${process.pid}-${tmpSeq++}.tmp`;
|
|
19
|
+
try {
|
|
20
|
+
const handle = await promises.open(tmpPath, "w", options.mode);
|
|
21
|
+
try {
|
|
22
|
+
await handle.writeFile(data);
|
|
23
|
+
if (options.mode !== void 0) {
|
|
24
|
+
await handle.chmod(options.mode);
|
|
25
|
+
}
|
|
26
|
+
if (options.fsync) {
|
|
27
|
+
await handle.sync();
|
|
28
|
+
}
|
|
29
|
+
} finally {
|
|
30
|
+
await handle.close();
|
|
31
|
+
}
|
|
32
|
+
} catch (err) {
|
|
33
|
+
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
34
|
+
});
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
await renameOnto(tmpPath, filePath);
|
|
38
|
+
if (options.mode !== void 0) {
|
|
39
|
+
await promises.chmod(filePath, options.mode);
|
|
40
|
+
}
|
|
41
|
+
if (options.fsync) {
|
|
42
|
+
const target = await promises.open(filePath, process.platform === "win32" ? "r+" : "r");
|
|
43
|
+
try {
|
|
44
|
+
await target.sync();
|
|
45
|
+
} finally {
|
|
46
|
+
await target.close();
|
|
47
|
+
}
|
|
48
|
+
if (process.platform !== "win32") {
|
|
49
|
+
const directory = await promises.open(path2.dirname(filePath), "r");
|
|
50
|
+
try {
|
|
51
|
+
await directory.sync();
|
|
52
|
+
} finally {
|
|
53
|
+
await directory.close();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
var RENAME_RETRY_ATTEMPTS = 5;
|
|
59
|
+
var RENAME_RETRY_DELAY_MS = 20;
|
|
60
|
+
function delay(ms) {
|
|
61
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
62
|
+
}
|
|
63
|
+
async function renameOnto(tmpPath, targetPath) {
|
|
64
|
+
for (let attempt = 1; attempt <= RENAME_RETRY_ATTEMPTS; attempt++) {
|
|
65
|
+
try {
|
|
66
|
+
await promises.rename(tmpPath, targetPath);
|
|
67
|
+
return;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
const code = err.code;
|
|
70
|
+
if (code !== "EPERM" && code !== "EEXIST") {
|
|
71
|
+
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
72
|
+
});
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
if (attempt === RENAME_RETRY_ATTEMPTS) {
|
|
76
|
+
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
77
|
+
});
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
await delay(RENAME_RETRY_DELAY_MS * attempt);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
15
85
|
// src/agent-home.ts
|
|
16
86
|
var AGENT_HOME_DIRECTORY = "agents";
|
|
17
87
|
var AGENT_HOME_INTERNAL_DIRECTORY = ".byok";
|
|
88
|
+
var AGENT_HOME_PROJECTION_STATE_FILE = "agent-home-projection.json";
|
|
18
89
|
var AgentHomeError = class extends Error {
|
|
19
90
|
constructor(message) {
|
|
20
91
|
super(message);
|
|
@@ -63,11 +134,11 @@ function validateAgentRef(value) {
|
|
|
63
134
|
return Object.freeze({ agentId: candidate.agentId, profileRevision: candidate.profileRevision });
|
|
64
135
|
}
|
|
65
136
|
function isWithin(root, candidate) {
|
|
66
|
-
const relative =
|
|
67
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
137
|
+
const relative = path2.relative(root, candidate);
|
|
138
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
|
|
68
139
|
}
|
|
69
140
|
function assertAbsolutePath(value, label) {
|
|
70
|
-
if (typeof value !== "string" || value.length === 0 || !
|
|
141
|
+
if (typeof value !== "string" || value.length === 0 || !path2.isAbsolute(value)) {
|
|
71
142
|
throw new AgentHomeResolutionError(`${label} must be an absolute path`);
|
|
72
143
|
}
|
|
73
144
|
if (/[\u0000\r\n]/u.test(value)) {
|
|
@@ -75,7 +146,7 @@ function assertAbsolutePath(value, label) {
|
|
|
75
146
|
}
|
|
76
147
|
}
|
|
77
148
|
async function resolveExistingAncestor(inputPath) {
|
|
78
|
-
let cursor =
|
|
149
|
+
let cursor = path2.resolve(inputPath);
|
|
79
150
|
const tail = [];
|
|
80
151
|
for (; ; ) {
|
|
81
152
|
try {
|
|
@@ -83,9 +154,9 @@ async function resolveExistingAncestor(inputPath) {
|
|
|
83
154
|
} catch (error) {
|
|
84
155
|
const code = error.code;
|
|
85
156
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
86
|
-
const parent =
|
|
157
|
+
const parent = path2.dirname(cursor);
|
|
87
158
|
if (parent === cursor) throw new AgentHomeResolutionError(`no existing ancestor for ${inputPath}`);
|
|
88
|
-
tail.unshift(
|
|
159
|
+
tail.unshift(path2.basename(cursor));
|
|
89
160
|
cursor = parent;
|
|
90
161
|
}
|
|
91
162
|
}
|
|
@@ -94,7 +165,7 @@ async function materializeDirectory(inputPath) {
|
|
|
94
165
|
const { canonical: canonical2, tail } = await resolveExistingAncestor(inputPath);
|
|
95
166
|
let cursor = canonical2;
|
|
96
167
|
for (const component of tail) {
|
|
97
|
-
cursor =
|
|
168
|
+
cursor = path2.join(cursor, component);
|
|
98
169
|
try {
|
|
99
170
|
const stat = await promises.lstat(cursor);
|
|
100
171
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -113,11 +184,11 @@ async function materializeDirectory(inputPath) {
|
|
|
113
184
|
}
|
|
114
185
|
async function ensureDirectoryNoSymlink(root, target) {
|
|
115
186
|
if (!isWithin(root, target)) throw new AgentHomeResolutionError("Agent home is outside hostStorageRoot");
|
|
116
|
-
const relative =
|
|
117
|
-
const components = relative === "" ? [] : relative.split(
|
|
187
|
+
const relative = path2.relative(root, target);
|
|
188
|
+
const components = relative === "" ? [] : relative.split(path2.sep);
|
|
118
189
|
let cursor = root;
|
|
119
190
|
for (const component of components) {
|
|
120
|
-
cursor =
|
|
191
|
+
cursor = path2.join(cursor, component);
|
|
121
192
|
try {
|
|
122
193
|
const stat = await promises.lstat(cursor);
|
|
123
194
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -153,16 +224,16 @@ var AgentHomeLayout = class {
|
|
|
153
224
|
canonicalRoot;
|
|
154
225
|
constructor(hostStorageRoot) {
|
|
155
226
|
assertAbsolutePath(hostStorageRoot, "agentHome.hostStorageRoot");
|
|
156
|
-
this.hostStorageRootInput =
|
|
227
|
+
this.hostStorageRootInput = path2.resolve(hostStorageRoot);
|
|
157
228
|
}
|
|
158
229
|
async resolve(agentRefInput) {
|
|
159
230
|
const agentRef = validateAgentRef(agentRefInput);
|
|
160
231
|
const hostStorageRoot = await this.resolveRoot();
|
|
161
232
|
const agentsRoot = await ensureDirectoryNoSymlink(
|
|
162
233
|
hostStorageRoot,
|
|
163
|
-
|
|
234
|
+
path2.join(hostStorageRoot, AGENT_HOME_DIRECTORY)
|
|
164
235
|
);
|
|
165
|
-
const lexicalHome =
|
|
236
|
+
const lexicalHome = path2.join(agentsRoot, agentRef.agentId);
|
|
166
237
|
const canonicalHome = await ensureDirectoryNoSymlink(agentsRoot, lexicalHome);
|
|
167
238
|
const priorAgentId = this.agentIdByCanonicalHome.get(canonicalHome);
|
|
168
239
|
if (priorAgentId !== void 0 && priorAgentId !== agentRef.agentId) {
|
|
@@ -192,9 +263,9 @@ var AgentHomeLayout = class {
|
|
|
192
263
|
const hostStorageRoot = await this.resolveRoot();
|
|
193
264
|
const agentsRoot = await ensureDirectoryNoSymlink(
|
|
194
265
|
hostStorageRoot,
|
|
195
|
-
|
|
266
|
+
path2.join(hostStorageRoot, AGENT_HOME_DIRECTORY)
|
|
196
267
|
);
|
|
197
|
-
probePath =
|
|
268
|
+
probePath = path2.join(agentsRoot, `.byok-agent-home-preflight-${randomUUID()}`);
|
|
198
269
|
handle = await promises.open(probePath, "wx", 384);
|
|
199
270
|
created = true;
|
|
200
271
|
await handle.sync();
|
|
@@ -225,7 +296,7 @@ var AgentHomeLayout = class {
|
|
|
225
296
|
}
|
|
226
297
|
};
|
|
227
298
|
function stableAgentHomeOwnerId(storeDir, productId) {
|
|
228
|
-
const identity = `${
|
|
299
|
+
const identity = `${path2.resolve(storeDir)}\0${productId}`;
|
|
229
300
|
return `store-product:${createHash("sha256").update(identity).digest("hex")}`;
|
|
230
301
|
}
|
|
231
302
|
function parseLeaseMarker(value, lockPath) {
|
|
@@ -235,7 +306,7 @@ function parseLeaseMarker(value, lockPath) {
|
|
|
235
306
|
} catch {
|
|
236
307
|
throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} is corrupt`);
|
|
237
308
|
}
|
|
238
|
-
if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.ownerId !== "string" || typeof parsed.leaseId !== "string" || typeof parsed.canonicalHome !== "string" || !
|
|
309
|
+
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)) {
|
|
239
310
|
throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} has an invalid shape`);
|
|
240
311
|
}
|
|
241
312
|
let agentRef;
|
|
@@ -245,7 +316,7 @@ function parseLeaseMarker(value, lockPath) {
|
|
|
245
316
|
throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} has an invalid AgentRef`);
|
|
246
317
|
}
|
|
247
318
|
const marker = parsed;
|
|
248
|
-
return { ...marker, agentRef, canonicalHome:
|
|
319
|
+
return { ...marker, agentRef, canonicalHome: path2.resolve(marker.canonicalHome) };
|
|
249
320
|
}
|
|
250
321
|
var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
|
|
251
322
|
static held = /* @__PURE__ */ new Map();
|
|
@@ -267,9 +338,9 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
|
|
|
267
338
|
await ensureDirectoryNoSymlink(resolution.agentsRoot, canonicalHome);
|
|
268
339
|
const internalDir = await ensureDirectoryNoSymlink(
|
|
269
340
|
canonicalHome,
|
|
270
|
-
|
|
341
|
+
path2.join(canonicalHome, AGENT_HOME_INTERNAL_DIRECTORY)
|
|
271
342
|
);
|
|
272
|
-
lockPath =
|
|
343
|
+
lockPath = path2.join(internalDir, "agent-home.lease");
|
|
273
344
|
handle = await this.openLeaseMarker(lockPath, canonicalHome, agentRef.agentId);
|
|
274
345
|
ownsMarker = true;
|
|
275
346
|
const marker = {
|
|
@@ -368,9 +439,73 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
|
|
|
368
439
|
async function initializeAgentHome(resolution) {
|
|
369
440
|
await ensureDirectoryNoSymlink(
|
|
370
441
|
resolution.canonicalHome,
|
|
371
|
-
|
|
442
|
+
path2.join(resolution.canonicalHome, "notes")
|
|
372
443
|
);
|
|
373
|
-
await ensurePreservedFile(
|
|
444
|
+
await ensurePreservedFile(path2.join(resolution.canonicalHome, "MEMORY.md"));
|
|
445
|
+
}
|
|
446
|
+
function projectionStatePath(resolution) {
|
|
447
|
+
return path2.join(
|
|
448
|
+
resolution.canonicalHome,
|
|
449
|
+
AGENT_HOME_INTERNAL_DIRECTORY,
|
|
450
|
+
AGENT_HOME_PROJECTION_STATE_FILE
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
async function readProjectionState(resolution) {
|
|
454
|
+
const filePath = projectionStatePath(resolution);
|
|
455
|
+
try {
|
|
456
|
+
const stat = await promises.lstat(filePath);
|
|
457
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
458
|
+
throw new AgentHomeResolutionError(`Agent projection state is not a regular file: ${filePath}`);
|
|
459
|
+
}
|
|
460
|
+
} catch (error) {
|
|
461
|
+
if (error.code === "ENOENT") return void 0;
|
|
462
|
+
throw error;
|
|
463
|
+
}
|
|
464
|
+
let parsed;
|
|
465
|
+
try {
|
|
466
|
+
parsed = JSON.parse(await promises.readFile(filePath, "utf8"));
|
|
467
|
+
} catch (error) {
|
|
468
|
+
throw new AgentHomeResolutionError(
|
|
469
|
+
`Agent projection state is corrupt: ${error instanceof Error ? error.message : String(error)}`
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.requestId !== "string" || typeof parsed.projectionHash !== "string") {
|
|
473
|
+
throw new AgentHomeResolutionError("Agent projection state has an invalid shape");
|
|
474
|
+
}
|
|
475
|
+
const candidate = parsed;
|
|
476
|
+
const agentRef = validateAgentRef(candidate.agentRef);
|
|
477
|
+
if (agentRef.agentId !== resolution.agentRef.agentId) {
|
|
478
|
+
throw new AgentHomeCollisionError("Agent projection state belongs to a different Agent home");
|
|
479
|
+
}
|
|
480
|
+
return Object.freeze({
|
|
481
|
+
version: 1,
|
|
482
|
+
agentRef,
|
|
483
|
+
requestId: candidate.requestId,
|
|
484
|
+
projectionHash: candidate.projectionHash
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
async function writeProjectionState(resolution, payload) {
|
|
488
|
+
const filePath = projectionStatePath(resolution);
|
|
489
|
+
const existing = await promises.lstat(filePath).catch((error) => {
|
|
490
|
+
if (error.code === "ENOENT") return void 0;
|
|
491
|
+
throw error;
|
|
492
|
+
});
|
|
493
|
+
if (existing !== void 0 && (!existing.isFile() || existing.isSymbolicLink())) {
|
|
494
|
+
throw new AgentHomeResolutionError(`Agent projection state is not a regular file: ${filePath}`);
|
|
495
|
+
}
|
|
496
|
+
const state = {
|
|
497
|
+
version: 1,
|
|
498
|
+
agentRef: payload.agentRef,
|
|
499
|
+
requestId: payload.requestId,
|
|
500
|
+
projectionHash: payload.projectionHash
|
|
501
|
+
};
|
|
502
|
+
await atomicWriteFile(filePath, `${JSON.stringify(state)}
|
|
503
|
+
`, { mode: 384, fsync: true });
|
|
504
|
+
}
|
|
505
|
+
function compareProjectionRevision(left, right) {
|
|
506
|
+
const leftRevision = BigInt(left);
|
|
507
|
+
const rightRevision = BigInt(right);
|
|
508
|
+
return leftRevision < rightRevision ? -1 : leftRevision > rightRevision ? 1 : 0;
|
|
374
509
|
}
|
|
375
510
|
var AgentHomeManager = class {
|
|
376
511
|
layout;
|
|
@@ -406,16 +541,66 @@ var AgentHomeManager = class {
|
|
|
406
541
|
async initialize(binding) {
|
|
407
542
|
const { resolution, lease } = binding;
|
|
408
543
|
await initializeAgentHome(resolution);
|
|
409
|
-
|
|
544
|
+
const prepare = this.projection?.prepare;
|
|
545
|
+
if (prepare !== void 0) await prepare({ ...resolution, cwd: lease.cwd });
|
|
410
546
|
if (await promises.realpath(resolution.homeDir) !== resolution.canonicalHome) {
|
|
411
547
|
throw new AgentHomeResolutionError("Agent projection changed the canonical home path");
|
|
412
548
|
}
|
|
413
549
|
await initializeAgentHome(resolution);
|
|
414
550
|
}
|
|
551
|
+
supportsTaskFreeProjection() {
|
|
552
|
+
return this.projection?.apply !== void 0;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Apply one task-free projection under the same canonical-home writer lease
|
|
556
|
+
* used by Agent execution. Only a successful host hook followed by the
|
|
557
|
+
* SDK-owned fsynced ordering record can return `applied`.
|
|
558
|
+
*/
|
|
559
|
+
async project(input) {
|
|
560
|
+
const payload = AgentHomeProjectionPayloadSchema.parse(input);
|
|
561
|
+
const binding = await this.acquire(payload.agentRef);
|
|
562
|
+
try {
|
|
563
|
+
const { resolution, lease } = binding;
|
|
564
|
+
await initializeAgentHome(resolution);
|
|
565
|
+
const current = await readProjectionState(resolution);
|
|
566
|
+
if (current !== void 0) {
|
|
567
|
+
const order = compareProjectionRevision(
|
|
568
|
+
payload.agentRef.profileRevision,
|
|
569
|
+
current.agentRef.profileRevision
|
|
570
|
+
);
|
|
571
|
+
if (order < 0) return "stale";
|
|
572
|
+
if (order === 0) {
|
|
573
|
+
return payload.projectionHash === current.projectionHash ? "idempotent" : "conflict";
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const apply = this.projection?.apply;
|
|
577
|
+
if (apply === void 0) {
|
|
578
|
+
throw new AgentHomeError("task-free Agent-home projection is not configured");
|
|
579
|
+
}
|
|
580
|
+
await apply({
|
|
581
|
+
...resolution,
|
|
582
|
+
cwd: lease.cwd,
|
|
583
|
+
requestId: payload.requestId,
|
|
584
|
+
projectionHash: payload.projectionHash,
|
|
585
|
+
projection: payload.projection
|
|
586
|
+
});
|
|
587
|
+
if (await promises.realpath(resolution.homeDir) !== resolution.canonicalHome) {
|
|
588
|
+
throw new AgentHomeResolutionError("Agent projection changed the canonical home path");
|
|
589
|
+
}
|
|
590
|
+
await initializeAgentHome(resolution);
|
|
591
|
+
await writeProjectionState(resolution, payload);
|
|
592
|
+
return "applied";
|
|
593
|
+
} finally {
|
|
594
|
+
await binding.lease.release();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
415
597
|
};
|
|
416
598
|
function createAgentHomeProjection(prepare) {
|
|
417
599
|
return Object.freeze({ prepare });
|
|
418
600
|
}
|
|
601
|
+
function createAgentHomeProjectionConsumer(apply) {
|
|
602
|
+
return Object.freeze({ apply });
|
|
603
|
+
}
|
|
419
604
|
var AgentSessionHandoffStoreError = class extends Error {
|
|
420
605
|
constructor(message) {
|
|
421
606
|
super(message);
|
|
@@ -455,7 +640,7 @@ function parseTaskTerminalEntry(value) {
|
|
|
455
640
|
assertNonEmptyString(value.terminalReason, "taskTerminal.terminalReason");
|
|
456
641
|
assertNonEmptyString(value.updatedAt, "taskTerminal.updatedAt");
|
|
457
642
|
if (value.sessionRef !== void 0) assertNonEmptyString(value.sessionRef, "taskTerminal.sessionRef");
|
|
458
|
-
if (typeof value.cwd !== "string" || !
|
|
643
|
+
if (typeof value.cwd !== "string" || !path2.isAbsolute(value.cwd)) {
|
|
459
644
|
throw new AgentSessionHandoffCorruptError("taskTerminal.cwd must be an absolute path");
|
|
460
645
|
}
|
|
461
646
|
if (value.terminalCause !== "failed") {
|
|
@@ -468,7 +653,7 @@ function parseTaskTerminalEntry(value) {
|
|
|
468
653
|
agentRef,
|
|
469
654
|
taskId: value.taskId,
|
|
470
655
|
runtimeId: value.runtimeId,
|
|
471
|
-
cwd:
|
|
656
|
+
cwd: path2.resolve(value.cwd),
|
|
472
657
|
leaseId: value.leaseId,
|
|
473
658
|
...value.sessionRef === void 0 ? {} : { sessionRef: value.sessionRef },
|
|
474
659
|
terminalCause: "failed",
|
|
@@ -498,7 +683,7 @@ function parseEntry(value) {
|
|
|
498
683
|
assertNonEmptyString(value.runtimeId, "handoff.runtimeId");
|
|
499
684
|
assertNonEmptyString(value.leaseId, "handoff.leaseId");
|
|
500
685
|
assertNonEmptyString(value.updatedAt, "handoff.updatedAt");
|
|
501
|
-
if (typeof value.cwd !== "string" || !
|
|
686
|
+
if (typeof value.cwd !== "string" || !path2.isAbsolute(value.cwd)) {
|
|
502
687
|
throw new AgentSessionHandoffCorruptError("handoff.cwd must be an absolute path");
|
|
503
688
|
}
|
|
504
689
|
if (Number.isNaN(Date.parse(value.updatedAt))) {
|
|
@@ -515,7 +700,7 @@ function parseEntry(value) {
|
|
|
515
700
|
taskId: value.taskId,
|
|
516
701
|
sessionRef: value.sessionRef,
|
|
517
702
|
runtimeId: value.runtimeId,
|
|
518
|
-
cwd:
|
|
703
|
+
cwd: path2.resolve(value.cwd),
|
|
519
704
|
leaseId: value.leaseId,
|
|
520
705
|
...value.terminalCause === void 0 ? {} : { terminalCause: value.terminalCause },
|
|
521
706
|
...value.terminalReason === void 0 ? {} : { terminalReason: value.terminalReason },
|
|
@@ -526,10 +711,10 @@ function sameRef(left, right) {
|
|
|
526
711
|
return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
|
|
527
712
|
}
|
|
528
713
|
function sameMatch(entry, expected) {
|
|
529
|
-
return sameRef(entry.agentRef, expected.agentRef) && entry.sessionRef === expected.sessionRef && entry.runtimeId === expected.runtimeId && entry.cwd ===
|
|
714
|
+
return sameRef(entry.agentRef, expected.agentRef) && entry.sessionRef === expected.sessionRef && entry.runtimeId === expected.runtimeId && entry.cwd === path2.resolve(expected.cwd);
|
|
530
715
|
}
|
|
531
716
|
function sameTaskTerminalMatch(entry, expected) {
|
|
532
|
-
return sameRef(entry.agentRef, expected.agentRef) && entry.taskId === expected.taskId && entry.runtimeId === expected.runtimeId && entry.cwd ===
|
|
717
|
+
return sameRef(entry.agentRef, expected.agentRef) && entry.taskId === expected.taskId && entry.runtimeId === expected.runtimeId && entry.cwd === path2.resolve(expected.cwd);
|
|
533
718
|
}
|
|
534
719
|
function sessionFileName(runtimeId, sessionRef) {
|
|
535
720
|
const digest2 = createHash("sha256").update(sessionRef, "utf8").digest("hex");
|
|
@@ -542,13 +727,13 @@ function taskTerminalFileName(runtimeId, taskId) {
|
|
|
542
727
|
return `${runtime}-task-${digest2}.jsonl`;
|
|
543
728
|
}
|
|
544
729
|
async function evidenceDirectory(cwdInput) {
|
|
545
|
-
if (!
|
|
730
|
+
if (!path2.isAbsolute(cwdInput)) {
|
|
546
731
|
throw new AgentSessionHandoffStoreError("Agent session cwd must be absolute");
|
|
547
732
|
}
|
|
548
733
|
const cwd = await promises.realpath(cwdInput);
|
|
549
734
|
let cursor = cwd;
|
|
550
735
|
for (const component of [".byok", "runtime-sessions"]) {
|
|
551
|
-
cursor =
|
|
736
|
+
cursor = path2.join(cursor, component);
|
|
552
737
|
try {
|
|
553
738
|
const stat = await promises.lstat(cursor);
|
|
554
739
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -560,8 +745,8 @@ async function evidenceDirectory(cwdInput) {
|
|
|
560
745
|
}
|
|
561
746
|
}
|
|
562
747
|
const canonical2 = await promises.realpath(cursor);
|
|
563
|
-
const relative =
|
|
564
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
748
|
+
const relative = path2.relative(cwd, canonical2);
|
|
749
|
+
if (relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative)) {
|
|
565
750
|
throw new AgentSessionHandoffStoreError("Agent session evidence path escaped the canonical Agent home");
|
|
566
751
|
}
|
|
567
752
|
return canonical2;
|
|
@@ -607,7 +792,7 @@ var AgentSessionHandoffStore = class {
|
|
|
607
792
|
taskId: input.taskId,
|
|
608
793
|
sessionRef: input.sessionRef,
|
|
609
794
|
runtimeId: input.runtimeId,
|
|
610
|
-
cwd:
|
|
795
|
+
cwd: path2.resolve(input.cwd),
|
|
611
796
|
leaseId: input.leaseId,
|
|
612
797
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
613
798
|
});
|
|
@@ -650,7 +835,7 @@ var AgentSessionHandoffStore = class {
|
|
|
650
835
|
agentRef: validateAgentRef(input.agentRef),
|
|
651
836
|
taskId: input.taskId,
|
|
652
837
|
runtimeId: input.runtimeId,
|
|
653
|
-
cwd:
|
|
838
|
+
cwd: path2.resolve(input.cwd)
|
|
654
839
|
};
|
|
655
840
|
const filePath = await this.taskTerminalFilePath(expected);
|
|
656
841
|
return this.enqueue(filePath, async () => {
|
|
@@ -677,7 +862,7 @@ var AgentSessionHandoffStore = class {
|
|
|
677
862
|
agentRef: validateAgentRef(expectedInput.agentRef),
|
|
678
863
|
taskId: expectedInput.taskId,
|
|
679
864
|
runtimeId: expectedInput.runtimeId,
|
|
680
|
-
cwd:
|
|
865
|
+
cwd: path2.resolve(expectedInput.cwd)
|
|
681
866
|
};
|
|
682
867
|
const filePath = await this.taskTerminalFilePath(expected);
|
|
683
868
|
return this.enqueue(filePath, async () => {
|
|
@@ -695,14 +880,14 @@ var AgentSessionHandoffStore = class {
|
|
|
695
880
|
assertNonEmptyString(match.sessionRef, "handoff.sessionRef");
|
|
696
881
|
assertNonEmptyString(match.runtimeId, "handoff.runtimeId");
|
|
697
882
|
const directory = await evidenceDirectory(match.cwd);
|
|
698
|
-
return
|
|
883
|
+
return path2.join(directory, sessionFileName(match.runtimeId, match.sessionRef));
|
|
699
884
|
}
|
|
700
885
|
async taskTerminalFilePath(match) {
|
|
701
886
|
validateAgentRef(match.agentRef);
|
|
702
887
|
assertNonEmptyString(match.taskId, "taskTerminal.taskId");
|
|
703
888
|
assertNonEmptyString(match.runtimeId, "taskTerminal.runtimeId");
|
|
704
889
|
const directory = await evidenceDirectory(match.cwd);
|
|
705
|
-
return
|
|
890
|
+
return path2.join(directory, taskTerminalFileName(match.runtimeId, match.taskId));
|
|
706
891
|
}
|
|
707
892
|
enqueue(key, task) {
|
|
708
893
|
const previous = this.queues.get(key) ?? Promise.resolve();
|
|
@@ -991,7 +1176,7 @@ function gitEnvironment(readOnly) {
|
|
|
991
1176
|
return env;
|
|
992
1177
|
}
|
|
993
1178
|
function stableGitWorkspaceOwnerId(storeDir, productId) {
|
|
994
|
-
const identity = `${
|
|
1179
|
+
const identity = `${path2.resolve(storeDir)}\\0${productId}`;
|
|
995
1180
|
return `store-product:${createHash("sha256").update(identity).digest("hex")}`;
|
|
996
1181
|
}
|
|
997
1182
|
var GUIDANCE = [
|
|
@@ -1003,11 +1188,11 @@ var GUIDANCE = [
|
|
|
1003
1188
|
"Leave incomplete work visible for recovery."
|
|
1004
1189
|
].join("\n");
|
|
1005
1190
|
function canonical(value) {
|
|
1006
|
-
return
|
|
1191
|
+
return path2.resolve(value);
|
|
1007
1192
|
}
|
|
1008
1193
|
function isContained(root, candidate) {
|
|
1009
|
-
const relative =
|
|
1010
|
-
return relative === "" || !relative.startsWith(`..${
|
|
1194
|
+
const relative = path2.relative(root, candidate);
|
|
1195
|
+
return relative === "" || !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
|
|
1011
1196
|
}
|
|
1012
1197
|
function bounded(value, max) {
|
|
1013
1198
|
return Buffer.byteLength(value, "utf8") <= max ? value : value.slice(0, max);
|
|
@@ -1112,7 +1297,7 @@ var GitWorkspaceManager = class {
|
|
|
1112
1297
|
await this.ensureOwnerMarker();
|
|
1113
1298
|
}
|
|
1114
1299
|
async ensureOwnerMarker() {
|
|
1115
|
-
const markerPath =
|
|
1300
|
+
const markerPath = path2.join(this.workspaceRoot, OWNER_MARKER);
|
|
1116
1301
|
let existing;
|
|
1117
1302
|
try {
|
|
1118
1303
|
existing = JSON.parse(await promises.readFile(markerPath, "utf8"));
|
|
@@ -1271,7 +1456,7 @@ ${instruction}`;
|
|
|
1271
1456
|
if (error instanceof GitWorkspaceError || code !== "ENOENT" && code !== "ENOTDIR") {
|
|
1272
1457
|
throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
|
|
1273
1458
|
}
|
|
1274
|
-
const parent =
|
|
1459
|
+
const parent = path2.dirname(current);
|
|
1275
1460
|
if (parent === current) {
|
|
1276
1461
|
throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
|
|
1277
1462
|
}
|
|
@@ -1303,74 +1488,6 @@ function isGitWorkspaceConfig(value) {
|
|
|
1303
1488
|
return false;
|
|
1304
1489
|
}
|
|
1305
1490
|
}
|
|
1306
|
-
var tmpSeq = 0;
|
|
1307
|
-
async function atomicWriteFile(filePath, data, options = {}) {
|
|
1308
|
-
const tmpPath = `${filePath}.${process.pid}-${tmpSeq++}.tmp`;
|
|
1309
|
-
try {
|
|
1310
|
-
const handle = await promises.open(tmpPath, "w", options.mode);
|
|
1311
|
-
try {
|
|
1312
|
-
await handle.writeFile(data);
|
|
1313
|
-
if (options.mode !== void 0) {
|
|
1314
|
-
await handle.chmod(options.mode);
|
|
1315
|
-
}
|
|
1316
|
-
if (options.fsync) {
|
|
1317
|
-
await handle.sync();
|
|
1318
|
-
}
|
|
1319
|
-
} finally {
|
|
1320
|
-
await handle.close();
|
|
1321
|
-
}
|
|
1322
|
-
} catch (err) {
|
|
1323
|
-
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
1324
|
-
});
|
|
1325
|
-
throw err;
|
|
1326
|
-
}
|
|
1327
|
-
await renameOnto(tmpPath, filePath);
|
|
1328
|
-
if (options.mode !== void 0) {
|
|
1329
|
-
await promises.chmod(filePath, options.mode);
|
|
1330
|
-
}
|
|
1331
|
-
if (options.fsync) {
|
|
1332
|
-
const target = await promises.open(filePath, process.platform === "win32" ? "r+" : "r");
|
|
1333
|
-
try {
|
|
1334
|
-
await target.sync();
|
|
1335
|
-
} finally {
|
|
1336
|
-
await target.close();
|
|
1337
|
-
}
|
|
1338
|
-
if (process.platform !== "win32") {
|
|
1339
|
-
const directory = await promises.open(path.dirname(filePath), "r");
|
|
1340
|
-
try {
|
|
1341
|
-
await directory.sync();
|
|
1342
|
-
} finally {
|
|
1343
|
-
await directory.close();
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
var RENAME_RETRY_ATTEMPTS = 5;
|
|
1349
|
-
var RENAME_RETRY_DELAY_MS = 20;
|
|
1350
|
-
function delay(ms) {
|
|
1351
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1352
|
-
}
|
|
1353
|
-
async function renameOnto(tmpPath, targetPath) {
|
|
1354
|
-
for (let attempt = 1; attempt <= RENAME_RETRY_ATTEMPTS; attempt++) {
|
|
1355
|
-
try {
|
|
1356
|
-
await promises.rename(tmpPath, targetPath);
|
|
1357
|
-
return;
|
|
1358
|
-
} catch (err) {
|
|
1359
|
-
const code = err.code;
|
|
1360
|
-
if (code !== "EPERM" && code !== "EEXIST") {
|
|
1361
|
-
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
1362
|
-
});
|
|
1363
|
-
throw err;
|
|
1364
|
-
}
|
|
1365
|
-
if (attempt === RENAME_RETRY_ATTEMPTS) {
|
|
1366
|
-
await promises.rm(tmpPath, { force: true }).catch(() => {
|
|
1367
|
-
});
|
|
1368
|
-
throw err;
|
|
1369
|
-
}
|
|
1370
|
-
await delay(RENAME_RETRY_DELAY_MS * attempt);
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
1491
|
var defaultRunner = (command, args) => new Promise((resolve, reject) => {
|
|
1375
1492
|
execFile(command, args, (error, stdout, stderr) => {
|
|
1376
1493
|
if (error && typeof error.code !== "number") {
|
|
@@ -1461,7 +1578,7 @@ function isProtected(record) {
|
|
|
1461
1578
|
var GitWorkspaceStore = class {
|
|
1462
1579
|
constructor(storeDir, options = {}) {
|
|
1463
1580
|
this.storeDir = storeDir;
|
|
1464
|
-
this.filePath =
|
|
1581
|
+
this.filePath = path2.join(storeDir, FILE_NAME);
|
|
1465
1582
|
this.maxRecords = Math.max(1, Math.floor(options.maxRecords ?? MAX_RECORDS));
|
|
1466
1583
|
}
|
|
1467
1584
|
storeDir;
|
|
@@ -1596,7 +1713,7 @@ var GitWorkspaceStore = class {
|
|
|
1596
1713
|
var BYOK_PI_MCP_CONFIG_PATH = "BYOK_PI_MCP_CONFIG_PATH";
|
|
1597
1714
|
var PI_PACKAGE_NAME = "@earendil-works/pi-coding-agent";
|
|
1598
1715
|
function readPackageJson(dir) {
|
|
1599
|
-
const candidate =
|
|
1716
|
+
const candidate = path2.join(dir, "package.json");
|
|
1600
1717
|
if (!existsSync(candidate)) return void 0;
|
|
1601
1718
|
try {
|
|
1602
1719
|
return JSON.parse(readFileSync(candidate, "utf8"));
|
|
@@ -1611,17 +1728,17 @@ function resolvePiBin() {
|
|
|
1611
1728
|
}
|
|
1612
1729
|
try {
|
|
1613
1730
|
const mainEntryUrl = import.meta.resolve(PI_PACKAGE_NAME);
|
|
1614
|
-
let dir =
|
|
1731
|
+
let dir = path2.dirname(fileURLToPath(mainEntryUrl));
|
|
1615
1732
|
for (let depth = 0; depth < 6; depth++) {
|
|
1616
1733
|
const pkg = readPackageJson(dir);
|
|
1617
1734
|
if (pkg?.name === PI_PACKAGE_NAME) {
|
|
1618
1735
|
const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.pi;
|
|
1619
1736
|
if (binRel) {
|
|
1620
|
-
return { command:
|
|
1737
|
+
return { command: path2.join(dir, binRel), source: "package" };
|
|
1621
1738
|
}
|
|
1622
1739
|
break;
|
|
1623
1740
|
}
|
|
1624
|
-
const parent =
|
|
1741
|
+
const parent = path2.dirname(dir);
|
|
1625
1742
|
if (parent === dir) break;
|
|
1626
1743
|
dir = parent;
|
|
1627
1744
|
}
|
|
@@ -1639,7 +1756,7 @@ function resolvePiExtensions() {
|
|
|
1639
1756
|
const clientManifest = fileURLToPath(import.meta.resolve("@byok-sdk/client/package.json"));
|
|
1640
1757
|
return {
|
|
1641
1758
|
webAccess: fileURLToPath(import.meta.resolve("pi-web-access/index.ts")),
|
|
1642
|
-
mcpAdapter:
|
|
1759
|
+
mcpAdapter: path2.join(path2.dirname(clientManifest), "dist", "adapters", "pi", "mcp-extension.js")
|
|
1643
1760
|
};
|
|
1644
1761
|
}
|
|
1645
1762
|
|
|
@@ -2565,10 +2682,10 @@ var PiAdapter = class {
|
|
|
2565
2682
|
const taskMcpServers = startInput.mcpServers ?? {};
|
|
2566
2683
|
const hasMcpServers = Object.keys(taskMcpServers).length > 0;
|
|
2567
2684
|
if (hasMcpServers) {
|
|
2568
|
-
mcpConfigDir = await promises.mkdtemp(
|
|
2685
|
+
mcpConfigDir = await promises.mkdtemp(path2.join(os6.tmpdir(), "byok-pi-mcp-"));
|
|
2569
2686
|
await promises.chmod(mcpConfigDir, 448).catch(() => {
|
|
2570
2687
|
});
|
|
2571
|
-
const mcpConfigPath =
|
|
2688
|
+
const mcpConfigPath = path2.join(mcpConfigDir, "mcp-config.json");
|
|
2572
2689
|
await promises.writeFile(mcpConfigPath, JSON.stringify({ mcpServers: taskMcpServers }), { mode: 384 });
|
|
2573
2690
|
runtimeEnv = { ...runtimeEnv, [BYOK_PI_MCP_CONFIG_PATH]: mcpConfigPath };
|
|
2574
2691
|
}
|
|
@@ -2799,7 +2916,7 @@ function resolveApprovalMcpBin() {
|
|
|
2799
2916
|
if (override) {
|
|
2800
2917
|
return { command: override, args: [], source: "env" };
|
|
2801
2918
|
}
|
|
2802
|
-
const distBin =
|
|
2919
|
+
const distBin = path2.join(path2.dirname(fileURLToPath(import.meta.url)), "bin", "byok-approval-mcp.js");
|
|
2803
2920
|
return { command: process.execPath, args: [distBin], source: "dist" };
|
|
2804
2921
|
}
|
|
2805
2922
|
|
|
@@ -2890,7 +3007,7 @@ var EXTENSION_CONTENT_TYPES = {
|
|
|
2890
3007
|
".yml": "application/yaml"
|
|
2891
3008
|
};
|
|
2892
3009
|
function guessContentType(filePath) {
|
|
2893
|
-
const ext =
|
|
3010
|
+
const ext = path2.extname(filePath).toLowerCase();
|
|
2894
3011
|
return EXTENSION_CONTENT_TYPES[ext] ?? "application/octet-stream";
|
|
2895
3012
|
}
|
|
2896
3013
|
function mapAssistant(msg, correlation) {
|
|
@@ -2974,11 +3091,11 @@ function tryBuildArtifactEvent(msg, workspaceDir) {
|
|
|
2974
3091
|
const filePath = toolUseResult && typeof toolUseResult.filePath === "string" ? toolUseResult.filePath : void 0;
|
|
2975
3092
|
if (!filePath) return void 0;
|
|
2976
3093
|
const realWorkspaceDir = tryRealpath(workspaceDir) ?? workspaceDir;
|
|
2977
|
-
const fileDir =
|
|
3094
|
+
const fileDir = path2.dirname(filePath);
|
|
2978
3095
|
const realFileDir = tryRealpath(fileDir) ?? fileDir;
|
|
2979
|
-
const realFilePath =
|
|
2980
|
-
const relative =
|
|
2981
|
-
if (relative === "" || relative.startsWith("..") ||
|
|
3096
|
+
const realFilePath = path2.join(realFileDir, path2.basename(filePath));
|
|
3097
|
+
const relative = path2.relative(realWorkspaceDir, realFilePath);
|
|
3098
|
+
if (relative === "" || relative.startsWith("..") || path2.isAbsolute(relative)) {
|
|
2982
3099
|
return void 0;
|
|
2983
3100
|
}
|
|
2984
3101
|
return { type: "artifact", name: relative, contentType: guessContentType(filePath) };
|
|
@@ -3372,10 +3489,10 @@ var ClaudeAdapter = class {
|
|
|
3372
3489
|
});
|
|
3373
3490
|
}
|
|
3374
3491
|
if (needsMcpConfig) {
|
|
3375
|
-
mcpConfigDir = await promises.mkdtemp(
|
|
3492
|
+
mcpConfigDir = await promises.mkdtemp(path2.join(os6.tmpdir(), "byok-mcp-"));
|
|
3376
3493
|
await promises.chmod(mcpConfigDir, 448).catch(() => {
|
|
3377
3494
|
});
|
|
3378
|
-
const mcpConfigPath =
|
|
3495
|
+
const mcpConfigPath = path2.join(mcpConfigDir, "mcp-config.json");
|
|
3379
3496
|
const mcpServers = { ...taskMcpServers };
|
|
3380
3497
|
if (mapping.needsApprovalMcp) {
|
|
3381
3498
|
const approvalChannel = startInput.approvalChannel;
|
|
@@ -3861,8 +3978,8 @@ function extractArtifactEvents(changes, workspaceDir) {
|
|
|
3861
3978
|
const absolutePath = typeof change.path === "string" ? change.path : void 0;
|
|
3862
3979
|
const kind = typeof change.kind === "string" ? change.kind : void 0;
|
|
3863
3980
|
if (!absolutePath || kind === "delete") continue;
|
|
3864
|
-
const relative =
|
|
3865
|
-
if (relative.length === 0 || relative.startsWith("..") ||
|
|
3981
|
+
const relative = path2.relative(workspaceDir, absolutePath);
|
|
3982
|
+
if (relative.length === 0 || relative.startsWith("..") || path2.isAbsolute(relative)) continue;
|
|
3866
3983
|
events.push({ type: "artifact", name: relative, contentType: guessContentType2(relative) });
|
|
3867
3984
|
}
|
|
3868
3985
|
return events;
|
|
@@ -3883,7 +4000,7 @@ var CONTENT_TYPE_BY_EXTENSION = {
|
|
|
3883
4000
|
".csv": "text/csv"
|
|
3884
4001
|
};
|
|
3885
4002
|
function guessContentType2(relativePath) {
|
|
3886
|
-
return CONTENT_TYPE_BY_EXTENSION[
|
|
4003
|
+
return CONTENT_TYPE_BY_EXTENSION[path2.extname(relativePath).toLowerCase()] ?? "application/octet-stream";
|
|
3887
4004
|
}
|
|
3888
4005
|
function extractErrorMessage(rawError) {
|
|
3889
4006
|
if (typeof rawError === "string") return rawError;
|
|
@@ -4739,12 +4856,12 @@ var DeviceStore = class _DeviceStore {
|
|
|
4739
4856
|
*/
|
|
4740
4857
|
constructor(storeDir, secureDirOptions) {
|
|
4741
4858
|
this.secureDirOptions = secureDirOptions;
|
|
4742
|
-
this.filePath =
|
|
4859
|
+
this.filePath = path2.join(storeDir, "device.json");
|
|
4743
4860
|
}
|
|
4744
4861
|
secureDirOptions;
|
|
4745
4862
|
filePath;
|
|
4746
4863
|
static defaultDir(productId) {
|
|
4747
|
-
return
|
|
4864
|
+
return path2.join(os6.homedir(), ".byok", productId);
|
|
4748
4865
|
}
|
|
4749
4866
|
/**
|
|
4750
4867
|
* Resolve the one store pathname every daemon/CLI component must share.
|
|
@@ -4753,7 +4870,7 @@ var DeviceStore = class _DeviceStore {
|
|
|
4753
4870
|
* cwd to pin a quarantine directory inode.
|
|
4754
4871
|
*/
|
|
4755
4872
|
static resolveDir(productId, configured) {
|
|
4756
|
-
return
|
|
4873
|
+
return path2.resolve(configured ?? _DeviceStore.defaultDir(productId));
|
|
4757
4874
|
}
|
|
4758
4875
|
async load() {
|
|
4759
4876
|
const opened = await this.openBounded();
|
|
@@ -4795,7 +4912,7 @@ var DeviceStore = class _DeviceStore {
|
|
|
4795
4912
|
}
|
|
4796
4913
|
async save(record) {
|
|
4797
4914
|
assertDeviceRecord(record);
|
|
4798
|
-
const storeDir =
|
|
4915
|
+
const storeDir = path2.dirname(this.filePath);
|
|
4799
4916
|
await ensureSecureDir(storeDir, this.secureDirOptions);
|
|
4800
4917
|
await atomicWriteFile(this.filePath, JSON.stringify(record, null, 2), { mode: 384 });
|
|
4801
4918
|
}
|
|
@@ -5449,19 +5566,19 @@ function shortHash(input) {
|
|
|
5449
5566
|
return createHash("sha256").update(input, "utf8").digest("hex").slice(0, 16);
|
|
5450
5567
|
}
|
|
5451
5568
|
function controlSocketPath(storeDir) {
|
|
5452
|
-
const candidate =
|
|
5569
|
+
const candidate = path2.join(storeDir, "control.sock");
|
|
5453
5570
|
if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT) return candidate;
|
|
5454
|
-
return
|
|
5571
|
+
return path2.join(CONTROL_SOCKET_FALLBACK_ROOT, `byok-${shortHash(storeDir)}`, "sock");
|
|
5455
5572
|
}
|
|
5456
5573
|
function controlPipeName(productId, storeDir) {
|
|
5457
|
-
const id = shortHash(`${productId}|${
|
|
5574
|
+
const id = shortHash(`${productId}|${path2.resolve(storeDir)}`);
|
|
5458
5575
|
return `\\\\.\\pipe\\byok-${id}`;
|
|
5459
5576
|
}
|
|
5460
5577
|
function controlEndpointPath(productId, storeDir, platform = process.platform) {
|
|
5461
5578
|
return platform === "win32" ? controlPipeName(productId, storeDir) : controlSocketPath(storeDir);
|
|
5462
5579
|
}
|
|
5463
5580
|
function controlTokenPath(storeDir) {
|
|
5464
|
-
return
|
|
5581
|
+
return path2.join(storeDir, "control.token");
|
|
5465
5582
|
}
|
|
5466
5583
|
var SERVER_PROOF_LABEL = "byok-control-server|";
|
|
5467
5584
|
var CLIENT_AUTH_LABEL = "byok-control-client|";
|
|
@@ -5640,7 +5757,7 @@ async function assertOwnedPrivateDir(dir) {
|
|
|
5640
5757
|
}
|
|
5641
5758
|
async function bindControlEndpoint(server, endpoint) {
|
|
5642
5759
|
if (process.platform !== "win32") {
|
|
5643
|
-
const endpointDir =
|
|
5760
|
+
const endpointDir = path2.dirname(endpoint);
|
|
5644
5761
|
await promises.mkdir(endpointDir, { recursive: true, mode: 448 });
|
|
5645
5762
|
await promises.chmod(endpointDir, 448).catch(() => {
|
|
5646
5763
|
});
|
|
@@ -6539,7 +6656,7 @@ function toBytes(data, _isBinary) {
|
|
|
6539
6656
|
|
|
6540
6657
|
// src/daemon/connection-manager.ts
|
|
6541
6658
|
function isCursorEnvelopeType(type) {
|
|
6542
|
-
return type.startsWith("task.") || type === "agent.egress.ack" || type === "agent.content.read";
|
|
6659
|
+
return type.startsWith("task.") || type === "agent.egress.ack" || type === "agent.content.read" || type === "agent.home.projection";
|
|
6543
6660
|
}
|
|
6544
6661
|
var ConnectionManager = class {
|
|
6545
6662
|
constructor(opts) {
|
|
@@ -7054,6 +7171,10 @@ var ConnectionManager = class {
|
|
|
7054
7171
|
async process(envelope, tracked) {
|
|
7055
7172
|
const seq = tracked ? envelope.seq : void 0;
|
|
7056
7173
|
try {
|
|
7174
|
+
if (tracked && this.cursor === void 0) {
|
|
7175
|
+
await this.opts.cursorStore.save(this.opts.serverUrl, this.opts.deviceId, 0);
|
|
7176
|
+
this.cursor = 0;
|
|
7177
|
+
}
|
|
7057
7178
|
await this.opts.onEnvelope(envelope);
|
|
7058
7179
|
if (!tracked) return;
|
|
7059
7180
|
this.processedSeqs.add(seq);
|
|
@@ -7283,7 +7404,7 @@ function sameFileState2(left, right) {
|
|
|
7283
7404
|
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
7284
7405
|
}
|
|
7285
7406
|
async function openOperationalHealthFile(storeDir) {
|
|
7286
|
-
const filePath =
|
|
7407
|
+
const filePath = path2.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
|
|
7287
7408
|
let namedBefore;
|
|
7288
7409
|
try {
|
|
7289
7410
|
namedBefore = await promises.lstat(filePath, { bigint: true });
|
|
@@ -7325,7 +7446,7 @@ var OperationalHealthTracker = class {
|
|
|
7325
7446
|
#writeTail = Promise.resolve();
|
|
7326
7447
|
#started = false;
|
|
7327
7448
|
constructor(storeDir, options = {}) {
|
|
7328
|
-
this.#filePath =
|
|
7449
|
+
this.#filePath = path2.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
|
|
7329
7450
|
this.#windowMs = options.windowMs ?? 6e4;
|
|
7330
7451
|
this.#failureThreshold = options.failureThreshold ?? 3;
|
|
7331
7452
|
this.#maxFailures = options.maxFailures ?? 128;
|
|
@@ -7412,7 +7533,7 @@ var OperationalHealthTracker = class {
|
|
|
7412
7533
|
async #load() {
|
|
7413
7534
|
let opened;
|
|
7414
7535
|
try {
|
|
7415
|
-
opened = await openOperationalHealthFile(
|
|
7536
|
+
opened = await openOperationalHealthFile(path2.dirname(this.#filePath));
|
|
7416
7537
|
} catch (err) {
|
|
7417
7538
|
throw new Error("operational health state could not be read");
|
|
7418
7539
|
}
|
|
@@ -7448,7 +7569,7 @@ var OperationalHealthTracker = class {
|
|
|
7448
7569
|
if (!this.#state) return;
|
|
7449
7570
|
const body = JSON.stringify(this.#state, null, 2);
|
|
7450
7571
|
this.#writeTail = this.#writeTail.then(async () => {
|
|
7451
|
-
await ensureSecureDir(
|
|
7572
|
+
await ensureSecureDir(path2.dirname(this.#filePath));
|
|
7452
7573
|
await atomicWriteFile(this.#filePath, body, { mode: 384, fsync: true });
|
|
7453
7574
|
});
|
|
7454
7575
|
try {
|
|
@@ -7539,9 +7660,9 @@ function storeMutexIdentity(canonicalStoreDir) {
|
|
|
7539
7660
|
}
|
|
7540
7661
|
function storeMutexEndpoint(canonicalStoreDir, identity, platform = process.platform) {
|
|
7541
7662
|
if (platform === "win32") return `\\\\.\\pipe\\byok-store-mutex-${identity.slice(0, 16)}`;
|
|
7542
|
-
const candidate =
|
|
7663
|
+
const candidate = path2.join(canonicalStoreDir, STORE_MUTEX_SOCKET_FILENAME);
|
|
7543
7664
|
if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT2) return candidate;
|
|
7544
|
-
return
|
|
7665
|
+
return path2.join(STORE_MUTEX_FALLBACK_ROOT, `byok-store-mutex-${identity.slice(0, 16)}`, "sock");
|
|
7545
7666
|
}
|
|
7546
7667
|
var DaemonOwnerActiveError = class extends Error {
|
|
7547
7668
|
constructor(role) {
|
|
@@ -7713,7 +7834,7 @@ async function acquireStoreMutex(canonicalStoreDir) {
|
|
|
7713
7834
|
const endpoint = storeMutexEndpoint(canonicalStoreDir, identity);
|
|
7714
7835
|
const isPipe = process.platform === "win32";
|
|
7715
7836
|
if (!isPipe) {
|
|
7716
|
-
const endpointDir =
|
|
7837
|
+
const endpointDir = path2.dirname(endpoint);
|
|
7717
7838
|
if (endpointDir !== canonicalStoreDir) {
|
|
7718
7839
|
await ensureSecureDir(endpointDir);
|
|
7719
7840
|
await assertOwnedPrivateDir2(endpointDir);
|
|
@@ -7793,8 +7914,8 @@ async function acquireDaemonOwner(storeDir, role, clock = () => /* @__PURE__ */
|
|
|
7793
7914
|
await mutex.close().catch(() => void 0);
|
|
7794
7915
|
throw err;
|
|
7795
7916
|
}
|
|
7796
|
-
const ownerPath =
|
|
7797
|
-
const reclaimPath =
|
|
7917
|
+
const ownerPath = path2.join(storeDir, DAEMON_OWNER_FILENAME);
|
|
7918
|
+
const reclaimPath = path2.join(storeDir, RECLAIM_FILENAME);
|
|
7798
7919
|
const record = {
|
|
7799
7920
|
version: 2,
|
|
7800
7921
|
pid: process.pid,
|
|
@@ -7872,7 +7993,7 @@ var CursorStore = class {
|
|
|
7872
7993
|
storeDir;
|
|
7873
7994
|
fileFor(serverUrl, deviceId) {
|
|
7874
7995
|
const key = createHash("sha256").update(`${serverUrl}::${deviceId}`).digest("hex").slice(0, 32);
|
|
7875
|
-
return
|
|
7996
|
+
return path2.join(this.storeDir, `cursor-${key}.json`);
|
|
7876
7997
|
}
|
|
7877
7998
|
async load(serverUrl, deviceId) {
|
|
7878
7999
|
let raw;
|
|
@@ -7892,7 +8013,7 @@ var CursorStore = class {
|
|
|
7892
8013
|
}
|
|
7893
8014
|
async save(serverUrl, deviceId, cursor) {
|
|
7894
8015
|
const file = this.fileFor(serverUrl, deviceId);
|
|
7895
|
-
await promises.mkdir(
|
|
8016
|
+
await promises.mkdir(path2.dirname(file), { recursive: true, mode: 448 });
|
|
7896
8017
|
await atomicWriteFile(file, JSON.stringify({ cursor }));
|
|
7897
8018
|
}
|
|
7898
8019
|
/** 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. */
|
|
@@ -8222,7 +8343,7 @@ var SessionWorkspaceStore = class {
|
|
|
8222
8343
|
*/
|
|
8223
8344
|
queue = Promise.resolve();
|
|
8224
8345
|
constructor(storeDir) {
|
|
8225
|
-
this.filePath =
|
|
8346
|
+
this.filePath = path2.join(storeDir, "session-workspaces.json");
|
|
8226
8347
|
}
|
|
8227
8348
|
async get(sessionRef) {
|
|
8228
8349
|
return this.enqueue(async () => {
|
|
@@ -8280,7 +8401,7 @@ var SessionWorkspaceStore = class {
|
|
|
8280
8401
|
}
|
|
8281
8402
|
}
|
|
8282
8403
|
async save(all) {
|
|
8283
|
-
const dir =
|
|
8404
|
+
const dir = path2.dirname(this.filePath);
|
|
8284
8405
|
await promises.mkdir(dir, { recursive: true, mode: 448 });
|
|
8285
8406
|
const tmpPath = `${this.filePath}.${process.pid}-${tmpSeq2++}.tmp`;
|
|
8286
8407
|
try {
|
|
@@ -9867,8 +9988,8 @@ function estimateEventBytes(event) {
|
|
|
9867
9988
|
}
|
|
9868
9989
|
async function openArtifact(workspaceDir, name) {
|
|
9869
9990
|
const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
|
|
9870
|
-
const candidate =
|
|
9871
|
-
const prefix = realWorkspaceDir.endsWith(
|
|
9991
|
+
const candidate = path2.resolve(realWorkspaceDir, name);
|
|
9992
|
+
const prefix = realWorkspaceDir.endsWith(path2.sep) ? realWorkspaceDir : realWorkspaceDir + path2.sep;
|
|
9872
9993
|
if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
|
|
9873
9994
|
return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
|
|
9874
9995
|
}
|
|
@@ -10380,7 +10501,7 @@ var TaskRunner = class {
|
|
|
10380
10501
|
const sameProtocolTask = ledger?.taskId === taskId;
|
|
10381
10502
|
const interruptedOldTask = ledger?.phase === "interrupted" && sameProtocolTask;
|
|
10382
10503
|
const activeDifferentTask = ledger !== void 0 && ledger.taskId !== taskId && (ledger.phase === "preparing" || ledger.phase === "active");
|
|
10383
|
-
if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !== sessionRef ||
|
|
10504
|
+
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) {
|
|
10384
10505
|
decline("session is incompatible with Git workspace mode", true);
|
|
10385
10506
|
return;
|
|
10386
10507
|
}
|
|
@@ -10395,7 +10516,7 @@ var TaskRunner = class {
|
|
|
10395
10516
|
return;
|
|
10396
10517
|
}
|
|
10397
10518
|
} else {
|
|
10398
|
-
workspaceDir =
|
|
10519
|
+
workspaceDir = path2.join(this.deps.workspaceRoot, taskId);
|
|
10399
10520
|
gitWorkspaceId = randomUUID();
|
|
10400
10521
|
}
|
|
10401
10522
|
try {
|
|
@@ -10406,7 +10527,7 @@ var TaskRunner = class {
|
|
|
10406
10527
|
}
|
|
10407
10528
|
} else if (!this.deps.gitWorkspaceManager && !this.deps.gitWorkspaceStore) {
|
|
10408
10529
|
known = sessionRef ? await this.deps.sessionWorkspaces.get(sessionRef) : void 0;
|
|
10409
|
-
workspaceDir = known?.workspaceDir ??
|
|
10530
|
+
workspaceDir = known?.workspaceDir ?? path2.join(this.deps.workspaceRoot, taskId);
|
|
10410
10531
|
plainWorkspaceNeedsResolve = true;
|
|
10411
10532
|
} else {
|
|
10412
10533
|
decline("workspace mode is unavailable", true);
|
|
@@ -11793,7 +11914,7 @@ var TaskRunner = class {
|
|
|
11793
11914
|
}
|
|
11794
11915
|
/** `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. */
|
|
11795
11916
|
async resolveWorkspaceDir(taskId, reuseDir) {
|
|
11796
|
-
const dir = reuseDir ??
|
|
11917
|
+
const dir = reuseDir ?? path2.join(this.deps.workspaceRoot, taskId);
|
|
11797
11918
|
await promises.mkdir(dir, { recursive: true });
|
|
11798
11919
|
return dir;
|
|
11799
11920
|
}
|
|
@@ -11931,7 +12052,7 @@ var encoder2 = new TextEncoder();
|
|
|
11931
12052
|
function eventBytes(event) {
|
|
11932
12053
|
return encoder2.encode(JSON.stringify(event)).length;
|
|
11933
12054
|
}
|
|
11934
|
-
var AGENT_EGRESS_DIRECTORY =
|
|
12055
|
+
var AGENT_EGRESS_DIRECTORY = path2.join(".byok", "egress");
|
|
11935
12056
|
var AGENT_RELIABLE_SPOOL_FILENAME = "reliable-v1.jsonl";
|
|
11936
12057
|
var AgentReliableSpoolError = class extends Error {
|
|
11937
12058
|
constructor(message) {
|
|
@@ -12042,9 +12163,9 @@ var AgentReliableSpool = class _AgentReliableSpool {
|
|
|
12042
12163
|
logEntries = 0;
|
|
12043
12164
|
writeTail = Promise.resolve();
|
|
12044
12165
|
static async open(homeDir) {
|
|
12045
|
-
const directory =
|
|
12166
|
+
const directory = path2.join(homeDir, AGENT_EGRESS_DIRECTORY);
|
|
12046
12167
|
await ensureSecureDir(directory);
|
|
12047
|
-
const spool = new _AgentReliableSpool(homeDir,
|
|
12168
|
+
const spool = new _AgentReliableSpool(homeDir, path2.join(directory, AGENT_RELIABLE_SPOOL_FILENAME));
|
|
12048
12169
|
await spool.load();
|
|
12049
12170
|
return spool;
|
|
12050
12171
|
}
|
|
@@ -12507,7 +12628,7 @@ var AgentEgressController = class {
|
|
|
12507
12628
|
}
|
|
12508
12629
|
/** Re-open every existing Agent-local spool before retrying stable records after restart. */
|
|
12509
12630
|
async recover(agentsRoot) {
|
|
12510
|
-
if (!
|
|
12631
|
+
if (!path2.isAbsolute(agentsRoot)) throw new Error("Agent egress recovery root must be absolute");
|
|
12511
12632
|
if (!this.active) throw new Error("Agent egress recovery requires an active authenticated enrollment");
|
|
12512
12633
|
if (this.options.tenantId === void 0) {
|
|
12513
12634
|
throw new Error("Agent egress recovery requires one authenticated tenant authority");
|
|
@@ -12522,14 +12643,14 @@ var AgentEgressController = class {
|
|
|
12522
12643
|
}
|
|
12523
12644
|
for (const entry of entries) {
|
|
12524
12645
|
if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
12525
|
-
const homeDir =
|
|
12646
|
+
const homeDir = path2.join(canonicalAgentsRoot, entry.name);
|
|
12526
12647
|
const canonicalHome = await promises.realpath(homeDir);
|
|
12527
|
-
const relativeHome =
|
|
12528
|
-
if (relativeHome !== entry.name || relativeHome.includes(
|
|
12648
|
+
const relativeHome = path2.relative(canonicalAgentsRoot, canonicalHome);
|
|
12649
|
+
if (relativeHome !== entry.name || relativeHome.includes(path2.sep) || path2.isAbsolute(relativeHome)) {
|
|
12529
12650
|
throw new Error(`Agent egress recovery home escaped the canonical agents root: ${entry.name}`);
|
|
12530
12651
|
}
|
|
12531
12652
|
try {
|
|
12532
|
-
await promises.lstat(
|
|
12653
|
+
await promises.lstat(path2.join(homeDir, AGENT_EGRESS_DIRECTORY));
|
|
12533
12654
|
} catch (error) {
|
|
12534
12655
|
if (error.code === "ENOENT") continue;
|
|
12535
12656
|
throw error;
|
|
@@ -12616,12 +12737,12 @@ function isAgentRef(value) {
|
|
|
12616
12737
|
}
|
|
12617
12738
|
function isCanonicalRelativeTarget(value) {
|
|
12618
12739
|
if (value === "[invalid-target]") return true;
|
|
12619
|
-
if (
|
|
12740
|
+
if (path2.isAbsolute(value) || value.includes("\\")) return false;
|
|
12620
12741
|
const segments = value.split("/");
|
|
12621
12742
|
return value.length > 0 && segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
12622
12743
|
}
|
|
12623
12744
|
function validateIdentity(value, label) {
|
|
12624
|
-
if (!isRecord4(value) || !isAgentRef(value.agentRef) || !isNonEmptyString(value.sessionRef) || !isNonEmptyString(value.runtimeId) || !isNonEmptyString(value.cwd) || !
|
|
12745
|
+
if (!isRecord4(value) || !isAgentRef(value.agentRef) || !isNonEmptyString(value.sessionRef) || !isNonEmptyString(value.runtimeId) || !isNonEmptyString(value.cwd) || !path2.isAbsolute(value.cwd)) {
|
|
12625
12746
|
throw new AgentContentAuditStoreError(`${label} has an invalid exact Agent/session identity`);
|
|
12626
12747
|
}
|
|
12627
12748
|
return Object.freeze({
|
|
@@ -12631,7 +12752,7 @@ function validateIdentity(value, label) {
|
|
|
12631
12752
|
}),
|
|
12632
12753
|
sessionRef: value.sessionRef,
|
|
12633
12754
|
runtimeId: value.runtimeId,
|
|
12634
|
-
cwd:
|
|
12755
|
+
cwd: path2.resolve(value.cwd)
|
|
12635
12756
|
});
|
|
12636
12757
|
}
|
|
12637
12758
|
function validateReceipt(value) {
|
|
@@ -12713,16 +12834,16 @@ function assertUniqueRequestIds(entries) {
|
|
|
12713
12834
|
}
|
|
12714
12835
|
}
|
|
12715
12836
|
function assertAbsoluteFilePath(filePath) {
|
|
12716
|
-
if (typeof filePath !== "string" || filePath.length === 0 || !
|
|
12837
|
+
if (typeof filePath !== "string" || filePath.length === 0 || !path2.isAbsolute(filePath)) {
|
|
12717
12838
|
throw new AgentContentAuditStoreError("content audit path must be absolute");
|
|
12718
12839
|
}
|
|
12719
12840
|
if (/[\u0000\r\n]/u.test(filePath)) {
|
|
12720
12841
|
throw new AgentContentAuditStoreError("content audit path must not contain NUL or line breaks");
|
|
12721
12842
|
}
|
|
12722
|
-
return
|
|
12843
|
+
return path2.resolve(filePath);
|
|
12723
12844
|
}
|
|
12724
12845
|
async function ensureDirectoryNoSymlink2(directory) {
|
|
12725
|
-
const absolute =
|
|
12846
|
+
const absolute = path2.resolve(directory);
|
|
12726
12847
|
await promises.mkdir(absolute, { recursive: true, mode: 448 });
|
|
12727
12848
|
const stat = await promises.lstat(absolute);
|
|
12728
12849
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -12756,12 +12877,12 @@ var AgentContentAuditStore = class _AgentContentAuditStore {
|
|
|
12756
12877
|
/** The daemon may address this ledger only through an AgentHomeLayout resolution. */
|
|
12757
12878
|
static forCanonicalAgentHome(canonicalHome) {
|
|
12758
12879
|
const home = assertAbsoluteFilePath(canonicalHome);
|
|
12759
|
-
return new _AgentContentAuditStore(
|
|
12880
|
+
return new _AgentContentAuditStore(path2.join(home, AGENT_HOME_INTERNAL_DIRECTORY, AGENT_CONTENT_AUDIT_FILENAME));
|
|
12760
12881
|
}
|
|
12761
12882
|
async append(receipt) {
|
|
12762
12883
|
const validated = validateReceipt(receipt);
|
|
12763
12884
|
return this.enqueue(async () => {
|
|
12764
|
-
await ensureDirectoryNoSymlink2(
|
|
12885
|
+
await ensureDirectoryNoSymlink2(path2.dirname(this.filePath));
|
|
12765
12886
|
await assertAuditFile(this.filePath);
|
|
12766
12887
|
const entries = await this.readAllUnlocked();
|
|
12767
12888
|
const prior = entries.find((entry) => entry.requestId === validated.requestId);
|
|
@@ -12856,6 +12977,63 @@ var AgentContentAuditStore = class _AgentContentAuditStore {
|
|
|
12856
12977
|
return result;
|
|
12857
12978
|
}
|
|
12858
12979
|
};
|
|
12980
|
+
var AgentHomeProjectionCompletionError = class extends Error {
|
|
12981
|
+
constructor(message, options) {
|
|
12982
|
+
super(message, options);
|
|
12983
|
+
this.name = "AgentHomeProjectionCompletionError";
|
|
12984
|
+
}
|
|
12985
|
+
};
|
|
12986
|
+
function sameAgentRef2(left, right) {
|
|
12987
|
+
return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
|
|
12988
|
+
}
|
|
12989
|
+
var AgentHomeProjectionCompletionClient = class {
|
|
12990
|
+
constructor(options) {
|
|
12991
|
+
this.options = options;
|
|
12992
|
+
}
|
|
12993
|
+
options;
|
|
12994
|
+
async complete(input) {
|
|
12995
|
+
const completion = AgentHomeProjectionCompletionRequestSchema.parse(input);
|
|
12996
|
+
const url = new URL(
|
|
12997
|
+
byokAgentHomeProjectionCompletionPath(completion.requestId),
|
|
12998
|
+
toHttpBase(this.options.serverUrl)
|
|
12999
|
+
);
|
|
13000
|
+
let response;
|
|
13001
|
+
try {
|
|
13002
|
+
response = await authedFetch(
|
|
13003
|
+
url,
|
|
13004
|
+
{
|
|
13005
|
+
method: "PUT",
|
|
13006
|
+
headers: { "content-type": "application/json" },
|
|
13007
|
+
body: JSON.stringify(completion)
|
|
13008
|
+
},
|
|
13009
|
+
this.options.auth
|
|
13010
|
+
);
|
|
13011
|
+
} catch (error) {
|
|
13012
|
+
throw new AgentHomeProjectionCompletionError("Agent-home projection completion transport failed", {
|
|
13013
|
+
cause: error
|
|
13014
|
+
});
|
|
13015
|
+
}
|
|
13016
|
+
if (!response.ok) {
|
|
13017
|
+
throw new AgentHomeProjectionCompletionError(
|
|
13018
|
+
`Agent-home projection completion was rejected with HTTP ${response.status}`
|
|
13019
|
+
);
|
|
13020
|
+
}
|
|
13021
|
+
let readback;
|
|
13022
|
+
try {
|
|
13023
|
+
readback = AgentHomeProjectionReadbackSchema.parse(await response.json());
|
|
13024
|
+
} catch (error) {
|
|
13025
|
+
throw new AgentHomeProjectionCompletionError("Agent-home projection completion readback is invalid", {
|
|
13026
|
+
cause: error
|
|
13027
|
+
});
|
|
13028
|
+
}
|
|
13029
|
+
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) {
|
|
13030
|
+
throw new AgentHomeProjectionCompletionError(
|
|
13031
|
+
"Agent-home projection completion readback does not exactly match the authenticated request"
|
|
13032
|
+
);
|
|
13033
|
+
}
|
|
13034
|
+
return readback;
|
|
13035
|
+
}
|
|
13036
|
+
};
|
|
12859
13037
|
var AGENT_CONTENT_READ_SURFACES = ["workspace", "transcript", "artifact"];
|
|
12860
13038
|
var AGENT_CONTENT_READ_CAPABILITIES = Object.freeze({
|
|
12861
13039
|
workspace: AGENT_CONTENT_WORKSPACE_READ_CAPABILITY,
|
|
@@ -12959,8 +13137,8 @@ function normalizeIdentity(value, field) {
|
|
|
12959
13137
|
const sessionRef = nonEmptyString(value.sessionRef, `${field}.sessionRef`);
|
|
12960
13138
|
const runtimeId = nonEmptyString(value.runtimeId, `${field}.runtimeId`);
|
|
12961
13139
|
const cwd = nonEmptyString(value.cwd, `${field}.cwd`);
|
|
12962
|
-
if (!
|
|
12963
|
-
return Object.freeze({ agentRef, sessionRef, runtimeId, cwd:
|
|
13140
|
+
if (!path2.isAbsolute(cwd)) throw new AgentContentReadPolicyError(`${field}.cwd must be absolute`);
|
|
13141
|
+
return Object.freeze({ agentRef, sessionRef, runtimeId, cwd: path2.resolve(cwd) });
|
|
12964
13142
|
}
|
|
12965
13143
|
function createAgentContentReadPolicy(input) {
|
|
12966
13144
|
if (!isRecord5(input) || input.enabled !== true) {
|
|
@@ -12988,10 +13166,10 @@ function createAgentContentReadPolicy(input) {
|
|
|
12988
13166
|
root = Object.freeze({ kind: "agent-home" });
|
|
12989
13167
|
} else if (input.root.kind === "runtime-allowlisted") {
|
|
12990
13168
|
const configuredRoot = nonEmptyString(input.root.root, "contentRead.root.root");
|
|
12991
|
-
if (!
|
|
13169
|
+
if (!path2.isAbsolute(configuredRoot)) {
|
|
12992
13170
|
throw new AgentContentReadPolicyError("contentRead.root.root must be absolute");
|
|
12993
13171
|
}
|
|
12994
|
-
root = Object.freeze({ kind: "runtime-allowlisted", root:
|
|
13172
|
+
root = Object.freeze({ kind: "runtime-allowlisted", root: path2.resolve(configuredRoot) });
|
|
12995
13173
|
} else {
|
|
12996
13174
|
throw new AgentContentReadPolicyError("contentRead.root.kind is not supported");
|
|
12997
13175
|
}
|
|
@@ -13014,11 +13192,11 @@ function createAgentContentReadPolicy(input) {
|
|
|
13014
13192
|
});
|
|
13015
13193
|
}
|
|
13016
13194
|
function isWithin2(root, candidate) {
|
|
13017
|
-
const relative =
|
|
13018
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
13195
|
+
const relative = path2.relative(root, candidate);
|
|
13196
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path2.sep}`) && !path2.isAbsolute(relative);
|
|
13019
13197
|
}
|
|
13020
13198
|
function isPortableAbsoluteTarget(value) {
|
|
13021
|
-
return
|
|
13199
|
+
return path2.isAbsolute(value) || /^[a-z]:[\\/]/iu.test(value) || /^[/\\]/u.test(value);
|
|
13022
13200
|
}
|
|
13023
13201
|
function canonicalAuditTarget(value) {
|
|
13024
13202
|
if (typeof value !== "string" || value.length === 0 || /[\u0000\r\n]/u.test(value) || isPortableAbsoluteTarget(value) || value.includes("\\")) {
|
|
@@ -13056,7 +13234,7 @@ function isSensitiveTarget(segments, productNames) {
|
|
|
13056
13234
|
return segments.some((segment) => patterns.some((pattern) => nameMatches(pattern, segment)));
|
|
13057
13235
|
}
|
|
13058
13236
|
async function resolveExistingAncestor2(inputPath) {
|
|
13059
|
-
let cursor =
|
|
13237
|
+
let cursor = path2.resolve(inputPath);
|
|
13060
13238
|
const tail = [];
|
|
13061
13239
|
for (; ; ) {
|
|
13062
13240
|
try {
|
|
@@ -13064,9 +13242,9 @@ async function resolveExistingAncestor2(inputPath) {
|
|
|
13064
13242
|
} catch (error) {
|
|
13065
13243
|
const code = error.code;
|
|
13066
13244
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
13067
|
-
const parent =
|
|
13245
|
+
const parent = path2.dirname(cursor);
|
|
13068
13246
|
if (parent === cursor) throw new TargetPolicyError("target-missing");
|
|
13069
|
-
tail.unshift(
|
|
13247
|
+
tail.unshift(path2.basename(cursor));
|
|
13070
13248
|
cursor = parent;
|
|
13071
13249
|
}
|
|
13072
13250
|
}
|
|
@@ -13087,11 +13265,11 @@ var RootPolicyError = class extends Error {
|
|
|
13087
13265
|
this.reason = reason;
|
|
13088
13266
|
}
|
|
13089
13267
|
};
|
|
13090
|
-
function
|
|
13268
|
+
function sameAgentRef3(left, right) {
|
|
13091
13269
|
return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
|
|
13092
13270
|
}
|
|
13093
13271
|
function sameSessionIdentity(left, right) {
|
|
13094
|
-
return
|
|
13272
|
+
return sameAgentRef3(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
|
|
13095
13273
|
}
|
|
13096
13274
|
function validateRequest(request) {
|
|
13097
13275
|
if (!isRecord5(request)) throw new AgentContentReadRequestError("content read request must be an object");
|
|
@@ -13153,18 +13331,18 @@ function normalizeRequestIdentity(value, field) {
|
|
|
13153
13331
|
const sessionRef = requestString(value.sessionRef, `${field}.sessionRef`);
|
|
13154
13332
|
const runtimeId = requestString(value.runtimeId, `${field}.runtimeId`);
|
|
13155
13333
|
const cwd = requestString(value.cwd, `${field}.cwd`);
|
|
13156
|
-
if (!
|
|
13157
|
-
return Object.freeze({ agentRef, sessionRef, runtimeId, cwd:
|
|
13334
|
+
if (!path2.isAbsolute(cwd)) throw new AgentContentReadRequestError(`${field}.cwd must be absolute`);
|
|
13335
|
+
return Object.freeze({ agentRef, sessionRef, runtimeId, cwd: path2.resolve(cwd) });
|
|
13158
13336
|
}
|
|
13159
13337
|
async function inspectRegularTarget(root, target) {
|
|
13160
13338
|
const ancestor = await resolveExistingAncestor2(target);
|
|
13161
13339
|
if (!isWithin2(root, ancestor.canonical)) {
|
|
13162
13340
|
throw new TargetPolicyError("path-escape");
|
|
13163
13341
|
}
|
|
13164
|
-
const components =
|
|
13342
|
+
const components = path2.relative(root, target).split(path2.sep).filter((component) => component.length > 0);
|
|
13165
13343
|
let cursor = root;
|
|
13166
13344
|
for (const [index, component] of components.entries()) {
|
|
13167
|
-
cursor =
|
|
13345
|
+
cursor = path2.join(cursor, component);
|
|
13168
13346
|
let stat;
|
|
13169
13347
|
try {
|
|
13170
13348
|
stat = await promises.lstat(cursor);
|
|
@@ -13229,8 +13407,8 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13229
13407
|
this.capabilities = new Set(options.capabilities);
|
|
13230
13408
|
this.runtimeRoots = Object.freeze((options.runtimeAllowlistedRoots ?? []).map((root, index) => {
|
|
13231
13409
|
const value = nonEmptyString(root, `contentRead.runtimeAllowlistedRoots[${index}]`);
|
|
13232
|
-
if (!
|
|
13233
|
-
return
|
|
13410
|
+
if (!path2.isAbsolute(value)) throw new AgentContentReadPolicyError("runtime allowlisted roots must be absolute");
|
|
13411
|
+
return path2.resolve(value);
|
|
13234
13412
|
}));
|
|
13235
13413
|
this.resolveSessionIdentity = options.resolveSessionIdentity;
|
|
13236
13414
|
this.resolveTranscriptIdentity = options.resolveTranscriptIdentity;
|
|
@@ -13282,7 +13460,7 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13282
13460
|
if (request.decodeAs === "utf8" && !policy.textMimeTypes.includes(request.mimeType)) {
|
|
13283
13461
|
return this.deny(request, relativeTarget, "text-not-allowlisted");
|
|
13284
13462
|
}
|
|
13285
|
-
const target =
|
|
13463
|
+
const target = path2.resolve(root, ...segments);
|
|
13286
13464
|
if (!isWithin2(root, target)) return this.deny(request, relativeTarget, "path-escape");
|
|
13287
13465
|
try {
|
|
13288
13466
|
await inspectRegularTarget(root, target);
|
|
@@ -13363,7 +13541,7 @@ var AgentContentReadPolicyEngine = class {
|
|
|
13363
13541
|
}
|
|
13364
13542
|
async checkSessionIdentity(request, resolver, requiredCwd) {
|
|
13365
13543
|
const session = request.session;
|
|
13366
|
-
if (session === void 0 || !
|
|
13544
|
+
if (session === void 0 || !sameAgentRef3(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
|
|
13367
13545
|
return "identity-mismatch";
|
|
13368
13546
|
}
|
|
13369
13547
|
let expected;
|
|
@@ -13479,7 +13657,7 @@ async function detectRuntimes(adapters) {
|
|
|
13479
13657
|
}
|
|
13480
13658
|
return runtimes;
|
|
13481
13659
|
}
|
|
13482
|
-
function computeCapabilities(adapters, agentHomeConfigured = false, agentEgressConfigured = false, contentReadPolicies) {
|
|
13660
|
+
function computeCapabilities(adapters, agentHomeConfigured = false, agentHomeProjectionConfigured = false, agentEgressConfigured = false, contentReadPolicies) {
|
|
13483
13661
|
const flags = [];
|
|
13484
13662
|
if (adapters.some((adapter) => adapter.descriptor.capabilities.steer)) flags.push("steer");
|
|
13485
13663
|
flags.push("blob-upload");
|
|
@@ -13494,6 +13672,7 @@ function computeCapabilities(adapters, agentHomeConfigured = false, agentEgressC
|
|
|
13494
13672
|
flags.push("toolset-selection");
|
|
13495
13673
|
}
|
|
13496
13674
|
if (agentHomeConfigured) flags.push("agent-home-contract");
|
|
13675
|
+
if (agentHomeProjectionConfigured) flags.push(AGENT_HOME_PROJECTION_CAPABILITY);
|
|
13497
13676
|
if (agentEgressConfigured) {
|
|
13498
13677
|
flags.push(
|
|
13499
13678
|
AGENT_EGRESS_POLICY_CAPABILITY,
|
|
@@ -13852,7 +14031,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
13852
14031
|
}
|
|
13853
14032
|
await agentHomeManager?.preflight();
|
|
13854
14033
|
if (config.agentEgress !== void 0 && config.agentHome !== void 0) {
|
|
13855
|
-
await agentEgress.recover(
|
|
14034
|
+
await agentEgress.recover(path2.join(config.agentHome.hostStorageRoot, "agents"));
|
|
13856
14035
|
}
|
|
13857
14036
|
fleetJitter = createFleetJitter(config.productId, record.deviceId);
|
|
13858
14037
|
if (config.permissionDefaults?.workspaceRoot !== void 0) {
|
|
@@ -13907,9 +14086,16 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
13907
14086
|
const capabilities = computeCapabilities(
|
|
13908
14087
|
adapters,
|
|
13909
14088
|
config.agentHome !== void 0,
|
|
14089
|
+
agentHomeManager?.supportsTaskFreeProjection() === true,
|
|
13910
14090
|
config.agentEgress !== void 0,
|
|
13911
14091
|
agentContentReadPolicies
|
|
13912
14092
|
);
|
|
14093
|
+
const agentHomeProjectionCompletion = agentHomeManager?.supportsTaskFreeProjection() === true ? new AgentHomeProjectionCompletionClient({
|
|
14094
|
+
serverUrl: config.serverUrl,
|
|
14095
|
+
auth,
|
|
14096
|
+
tenantId: record.tenantId,
|
|
14097
|
+
deviceId: record.deviceId
|
|
14098
|
+
}) : void 0;
|
|
13913
14099
|
const journalIdentity = config.hostedJournal ? { tenantId: record.tenantId, productId: config.productId, deviceId: record.deviceId } : void 0;
|
|
13914
14100
|
const sendSanitizedEnvelope = activeJournal && journalIdentity ? (envelope) => {
|
|
13915
14101
|
observer.handleOutboundEnvelope(envelope);
|
|
@@ -14046,6 +14232,20 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14046
14232
|
...activePressureEngine ? { admissionGuard: () => activePressureEngine.admissionGuard() } : {}
|
|
14047
14233
|
};
|
|
14048
14234
|
runner = new TaskRunner(deps);
|
|
14235
|
+
const handleAgentHomeProjectionEnvelope = async (envelope) => {
|
|
14236
|
+
if (envelope.type !== "agent.home.projection") return false;
|
|
14237
|
+
if (agentHomeManager === void 0 || agentHomeProjectionCompletion === void 0) {
|
|
14238
|
+
throw new Error("task-free Agent-home projection is not configured on this daemon");
|
|
14239
|
+
}
|
|
14240
|
+
const outcome = await agentHomeManager.project(envelope.payload);
|
|
14241
|
+
await agentHomeProjectionCompletion.complete({
|
|
14242
|
+
requestId: envelope.payload.requestId,
|
|
14243
|
+
agentRef: envelope.payload.agentRef,
|
|
14244
|
+
projectionHash: envelope.payload.projectionHash,
|
|
14245
|
+
outcome
|
|
14246
|
+
});
|
|
14247
|
+
return true;
|
|
14248
|
+
};
|
|
14049
14249
|
const handleAgentEgressEnvelope = async (envelope) => {
|
|
14050
14250
|
if (envelope.type !== "agent.egress.ack") return false;
|
|
14051
14251
|
if (config.agentEgress === void 0) return true;
|
|
@@ -14201,6 +14401,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14201
14401
|
throw new Error("tenant enrollment is being re-paired; inbound work is blocked until restart");
|
|
14202
14402
|
}
|
|
14203
14403
|
observer.handleInboundEnvelope(envelope);
|
|
14404
|
+
if (await handleAgentHomeProjectionEnvelope(envelope)) return;
|
|
14204
14405
|
if (await handleAgentEgressEnvelope(envelope)) return;
|
|
14205
14406
|
if (await handleAgentContentReadEnvelope(envelope)) return;
|
|
14206
14407
|
activePressureEngine?.assertAckCriticalAllowed();
|
|
@@ -14211,6 +14412,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
|
|
|
14211
14412
|
return Promise.reject(new Error("tenant enrollment is being re-paired; inbound work is blocked until restart"));
|
|
14212
14413
|
}
|
|
14213
14414
|
observer.handleInboundEnvelope(envelope);
|
|
14415
|
+
if (envelope.type === "agent.home.projection") return handleAgentHomeProjectionEnvelope(envelope).then(() => void 0);
|
|
14214
14416
|
if (envelope.type === "agent.egress.ack") return handleAgentEgressEnvelope(envelope).then(() => void 0);
|
|
14215
14417
|
if (envelope.type === "agent.content.read") return handleAgentContentReadEnvelope(envelope).then(() => void 0);
|
|
14216
14418
|
return runner?.handleEnvelope(envelope) ?? Promise.resolve();
|
|
@@ -15173,14 +15375,14 @@ async function getJson(url, auth, signal, what) {
|
|
|
15173
15375
|
return readBoundedJson(response, what);
|
|
15174
15376
|
}
|
|
15175
15377
|
function skillPacksRoot(dataDir) {
|
|
15176
|
-
return
|
|
15378
|
+
return path2.join(dataDir, SKILL_PACKS_DIRNAME);
|
|
15177
15379
|
}
|
|
15178
15380
|
function resolveInside(baseDir, relative) {
|
|
15179
15381
|
if (!isSkillPackPathSafe(relative)) {
|
|
15180
15382
|
throw new SkillPackInstallError("store_unsafe", `${JSON.stringify(relative)} is not a safe pack-relative path.`);
|
|
15181
15383
|
}
|
|
15182
|
-
const resolved =
|
|
15183
|
-
const prefix =
|
|
15384
|
+
const resolved = path2.resolve(baseDir, relative);
|
|
15385
|
+
const prefix = path2.resolve(baseDir) + path2.sep;
|
|
15184
15386
|
if (!resolved.startsWith(prefix)) {
|
|
15185
15387
|
throw new SkillPackInstallError(
|
|
15186
15388
|
"store_unsafe",
|
|
@@ -15206,7 +15408,7 @@ async function appendAuditLine(dataDir, record) {
|
|
|
15206
15408
|
await promises.mkdir(root, { recursive: true, mode: DIR_MODE });
|
|
15207
15409
|
await promises.chmod(root, DIR_MODE).catch(() => {
|
|
15208
15410
|
});
|
|
15209
|
-
const filePath =
|
|
15411
|
+
const filePath = path2.join(root, SKILL_PACK_AUDIT_FILENAME);
|
|
15210
15412
|
const handle = await promises.open(filePath, "a", FILE_MODE);
|
|
15211
15413
|
try {
|
|
15212
15414
|
await handle.chmod(FILE_MODE);
|
|
@@ -15321,12 +15523,12 @@ async function installOne(options, manifest, source, base) {
|
|
|
15321
15523
|
if (!entryCheck.ok) {
|
|
15322
15524
|
await refuse("content_rejected", `skill pack ${JSON.stringify(manifest.name)}: ${entryCheck.reason} \u2014 ${entryCheck.detail}`);
|
|
15323
15525
|
}
|
|
15324
|
-
const packRoot =
|
|
15325
|
-
const revisionDir =
|
|
15526
|
+
const packRoot = path2.join(skillPacksRoot(options.dataDir), manifest.name);
|
|
15527
|
+
const revisionDir = path2.join(packRoot, manifest.contentHash.slice("sha256:".length));
|
|
15326
15528
|
await promises.mkdir(revisionDir, { recursive: true, mode: DIR_MODE });
|
|
15327
15529
|
for (const [relative, content] of bodies) {
|
|
15328
15530
|
const target = resolveInside(revisionDir, relative);
|
|
15329
|
-
await promises.mkdir(
|
|
15531
|
+
await promises.mkdir(path2.dirname(target), { recursive: true, mode: DIR_MODE });
|
|
15330
15532
|
await assertNotSymlink(target);
|
|
15331
15533
|
await atomicWriteFile(target, content, { mode: FILE_MODE });
|
|
15332
15534
|
}
|
|
@@ -15344,7 +15546,7 @@ async function installOne(options, manifest, source, base) {
|
|
|
15344
15546
|
bytes: file.byteSize
|
|
15345
15547
|
}))
|
|
15346
15548
|
};
|
|
15347
|
-
await atomicWriteFile(
|
|
15549
|
+
await atomicWriteFile(path2.join(packRoot, SKILL_PACK_LOCK_FILENAME), `${JSON.stringify(lock, null, 2)}
|
|
15348
15550
|
`, {
|
|
15349
15551
|
mode: FILE_MODE
|
|
15350
15552
|
});
|
|
@@ -15366,10 +15568,10 @@ function isLockShaped(value) {
|
|
|
15366
15568
|
}
|
|
15367
15569
|
async function readLock(dataDir, name) {
|
|
15368
15570
|
if (!isSkillPackPathSafe(name)) return void 0;
|
|
15369
|
-
const packRoot =
|
|
15571
|
+
const packRoot = path2.join(skillPacksRoot(dataDir), name);
|
|
15370
15572
|
let raw;
|
|
15371
15573
|
try {
|
|
15372
|
-
raw = await promises.readFile(
|
|
15574
|
+
raw = await promises.readFile(path2.join(packRoot, SKILL_PACK_LOCK_FILENAME), "utf8");
|
|
15373
15575
|
} catch (err) {
|
|
15374
15576
|
if (err.code === "ENOENT") return void 0;
|
|
15375
15577
|
throw err;
|
|
@@ -15384,7 +15586,7 @@ async function readLock(dataDir, name) {
|
|
|
15384
15586
|
return {
|
|
15385
15587
|
name,
|
|
15386
15588
|
lock: parsed,
|
|
15387
|
-
directory:
|
|
15589
|
+
directory: path2.join(packRoot, parsed.content_hash.slice("sha256:".length))
|
|
15388
15590
|
};
|
|
15389
15591
|
}
|
|
15390
15592
|
async function listInstalledSkillPacks(dataDir) {
|
|
@@ -15433,7 +15635,7 @@ async function projectSkillPack(dataDir, name, targetDir) {
|
|
|
15433
15635
|
);
|
|
15434
15636
|
}
|
|
15435
15637
|
const destination = resolveInside(targetDir, file.path);
|
|
15436
|
-
await promises.mkdir(
|
|
15638
|
+
await promises.mkdir(path2.dirname(destination), { recursive: true, mode: DIR_MODE });
|
|
15437
15639
|
await assertNotSymlink(destination);
|
|
15438
15640
|
await atomicWriteFile(destination, bytes, { mode: FILE_MODE });
|
|
15439
15641
|
copied.push(file.path);
|
|
@@ -15448,7 +15650,7 @@ async function projectSkillPack(dataDir, name, targetDir) {
|
|
|
15448
15650
|
return {
|
|
15449
15651
|
name: installed.name,
|
|
15450
15652
|
contentHash: installed.lock.content_hash,
|
|
15451
|
-
targetDir:
|
|
15653
|
+
targetDir: path2.resolve(targetDir),
|
|
15452
15654
|
files: copied
|
|
15453
15655
|
};
|
|
15454
15656
|
}
|
|
@@ -15957,8 +16159,8 @@ function generateLaunchdPlist(def) {
|
|
|
15957
16159
|
const { label, program, logDir } = def;
|
|
15958
16160
|
const args = [program.command, ...program.args];
|
|
15959
16161
|
const cwd = program.cwd ?? os6.homedir();
|
|
15960
|
-
const outLog =
|
|
15961
|
-
const errLog =
|
|
16162
|
+
const outLog = path2.join(logDir, `${label}.out.log`);
|
|
16163
|
+
const errLog = path2.join(logDir, `${label}.err.log`);
|
|
15962
16164
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
15963
16165
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
15964
16166
|
<plist version="1.0">
|
|
@@ -15999,7 +16201,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
15999
16201
|
return process.getuid();
|
|
16000
16202
|
});
|
|
16001
16203
|
const label = sanitizeServiceName(def.name);
|
|
16002
|
-
const plistPath = () =>
|
|
16204
|
+
const plistPath = () => path2.join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
|
|
16003
16205
|
const domainTarget = () => `gui/${getuid()}`;
|
|
16004
16206
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
16005
16207
|
async function fileExists(p) {
|
|
@@ -16012,7 +16214,7 @@ function createLaunchdLifecycle(def, deps = {}) {
|
|
|
16012
16214
|
}
|
|
16013
16215
|
async function writePlist(program) {
|
|
16014
16216
|
const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
|
|
16015
|
-
await fs24.mkdir(
|
|
16217
|
+
await fs24.mkdir(path2.dirname(plistPath()), { recursive: true });
|
|
16016
16218
|
await fs24.mkdir(def.logDir, { recursive: true });
|
|
16017
16219
|
await fs24.writeFile(plistPath(), xml, "utf8");
|
|
16018
16220
|
}
|
|
@@ -16086,8 +16288,8 @@ function generateSystemdUnit(def) {
|
|
|
16086
16288
|
assertNoControlChars(displayName, "displayName");
|
|
16087
16289
|
const cwd = program.cwd ?? os6.homedir();
|
|
16088
16290
|
assertNoControlChars(cwd, "program.cwd");
|
|
16089
|
-
const outLog =
|
|
16090
|
-
const errLog =
|
|
16291
|
+
const outLog = path2.join(logDir, `${name}.out.log`);
|
|
16292
|
+
const errLog = path2.join(logDir, `${name}.err.log`);
|
|
16091
16293
|
assertNoControlChars(outLog, "logDir");
|
|
16092
16294
|
assertNoControlChars(errLog, "logDir");
|
|
16093
16295
|
const execStart = [program.command, ...program.args].map(quoteSystemdArg).join(" ");
|
|
@@ -16113,7 +16315,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
16113
16315
|
const homedir = deps.homedir ?? (() => os6.homedir());
|
|
16114
16316
|
const name = sanitizeServiceName(def.name);
|
|
16115
16317
|
const unitName = `${name}.service`;
|
|
16116
|
-
const unitPath = () =>
|
|
16318
|
+
const unitPath = () => path2.join(homedir(), ".config", "systemd", "user", unitName);
|
|
16117
16319
|
async function fileExists(p) {
|
|
16118
16320
|
try {
|
|
16119
16321
|
await fs24.stat(p);
|
|
@@ -16124,7 +16326,7 @@ function createSystemdLifecycle(def, deps = {}) {
|
|
|
16124
16326
|
}
|
|
16125
16327
|
async function writeUnit(program) {
|
|
16126
16328
|
const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
|
|
16127
|
-
await fs24.mkdir(
|
|
16329
|
+
await fs24.mkdir(path2.dirname(unitPath()), { recursive: true });
|
|
16128
16330
|
await fs24.mkdir(def.logDir, { recursive: true });
|
|
16129
16331
|
await fs24.writeFile(unitPath(), unit, "utf8");
|
|
16130
16332
|
}
|
|
@@ -16206,8 +16408,8 @@ function createWinswLifecycle(def, deps = {}) {
|
|
|
16206
16408
|
const winswBin = windows.winswBin;
|
|
16207
16409
|
const id = sanitizeServiceName(def.name);
|
|
16208
16410
|
const installDir = windows.installDir ?? def.logDir;
|
|
16209
|
-
const exePath =
|
|
16210
|
-
const xmlPath =
|
|
16411
|
+
const exePath = path2.join(installDir, `${id}.exe`);
|
|
16412
|
+
const xmlPath = path2.join(installDir, `${id}.xml`);
|
|
16211
16413
|
async function fileExists(p) {
|
|
16212
16414
|
try {
|
|
16213
16415
|
await fs24.stat(p);
|
|
@@ -16275,6 +16477,6 @@ function createServiceLifecycle(def, opts = {}) {
|
|
|
16275
16477
|
}
|
|
16276
16478
|
}
|
|
16277
16479
|
|
|
16278
|
-
export { AGENT_CONTENT_READ_CAPABILITIES, AGENT_CONTENT_READ_CAPABILITY_ARTIFACT, AGENT_CONTENT_READ_CAPABILITY_TRANSCRIPT, AGENT_CONTENT_READ_CAPABILITY_WORKSPACE, AgentHomeBusyError, AgentHomeCollisionError, AgentHomeError, AgentHomeLayout, AgentHomeLeaseCorruptError, AgentHomeLeaseManager, AgentHomeManager, AgentHomeResolutionError, AgentRefValidationError, AgentSessionHandoffCorruptError, AgentSessionHandoffMismatchError, AgentSessionHandoffStore, AgentSessionHandoffStoreError, AuthManager, BlobClient, ClaudeAdapter, CodexAdapter, DEFAULT_ACK_CRITICAL_RESERVE_BYTES, DEFAULT_CLEANUP_BATCH_LIMIT, DEFAULT_HARD_BUDGET_RATIO, DEFAULT_INCREMENTAL_VACUUM_PAGES, DEFAULT_LOG_ROTATION, DEFAULT_NORMAL_COMPACTION_INTERVAL_MS, DEFAULT_PRESSURE_COMPACTION_INTERVAL_MS, DEFAULT_RETENTION_MS, DEFAULT_SOFT_BUDGET_RATIO, DaemonObserver, DeviceRevokedError, GitWorkspaceError, GitWorkspaceManager, GitWorkspaceStore, JOURNAL_DB_FILENAME, JOURNAL_QUARANTINE_DIRNAME, JOURNAL_TASK_REF_PREFIX, JournalClosedError, JournalCorruptError, JournalRecordTooLargeError, JournalUnavailableError, JournalUnknownTaskError, LocalStorageEmergencyError, LocalStoragePolicyError, LocalStoragePressureEngine, McpToolsetDefinitionRevisionConflictError, McpToolsetRevisionConflictError, PI_PACKAGE_NAME, PiAdapter, PolicyUnsupportedError, RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON, RuntimeDisposalFailure, RuntimeExecutionFailure, SKILL_PACKS_CAPABILITY, SKILL_PACKS_DIRNAME, SKILL_PACK_AUDIT_FILENAME, SKILL_PACK_INSTALL_ERROR_CODES, SKILL_PACK_LOCK_FILENAME, SKILL_PACK_LOCK_SCHEMA, SKILL_PACK_RESPONSE_MAX_BYTES, SecureDirHardeningError, SkillPackInstallError, SqliteLocalTaskJournal, SteerUnsupportedError, StoredDeviceProofSigner, TruthMemoryClient, TruthMemoryClientError, UnsupportedServicePlatformError, buildIcaclsArgs, cleanupEligibleAt, cleanupOrderFor, computePressureState, createAgentHomeProjection, createDaemon, createDaemonWithAdapters, createFilesystemCleanupExecutor, createServiceLifecycle, createStatfsFreeBytesProvider, ensureSecureDir, freezeRuntimeAdapterDescriptor, generateLaunchdPlist, generateSystemdUnit, generateWinswXml, installSkillPacks, isGitWorkspaceConfig, isRuntimeDisposalFailure, isRuntimeExecutionFailure, journalHash, listInstalledSkillPacks, nodeAgentProgram, prependGitWorkspaceGuidance, projectRuntimeBoundaryFailure, projectRuntimeExecutionFailure, projectSkillPack, requestDeviceAssertion, resolveLocalAgentReleaseIdentity, resolveLocalStoragePolicy, sanitizeServiceName, sealRuntimeOperationManifest, skillPacksRoot, stableAgentHomeOwnerId, validateAgentRef };
|
|
16480
|
+
export { AGENT_CONTENT_READ_CAPABILITIES, AGENT_CONTENT_READ_CAPABILITY_ARTIFACT, AGENT_CONTENT_READ_CAPABILITY_TRANSCRIPT, AGENT_CONTENT_READ_CAPABILITY_WORKSPACE, AGENT_HOME_PROJECTION_STATE_FILE, AgentHomeBusyError, AgentHomeCollisionError, AgentHomeError, AgentHomeLayout, AgentHomeLeaseCorruptError, AgentHomeLeaseManager, AgentHomeManager, AgentHomeResolutionError, AgentRefValidationError, AgentSessionHandoffCorruptError, AgentSessionHandoffMismatchError, AgentSessionHandoffStore, AgentSessionHandoffStoreError, AuthManager, BlobClient, ClaudeAdapter, CodexAdapter, DEFAULT_ACK_CRITICAL_RESERVE_BYTES, DEFAULT_CLEANUP_BATCH_LIMIT, DEFAULT_HARD_BUDGET_RATIO, DEFAULT_INCREMENTAL_VACUUM_PAGES, DEFAULT_LOG_ROTATION, DEFAULT_NORMAL_COMPACTION_INTERVAL_MS, DEFAULT_PRESSURE_COMPACTION_INTERVAL_MS, DEFAULT_RETENTION_MS, DEFAULT_SOFT_BUDGET_RATIO, DaemonObserver, DeviceRevokedError, GitWorkspaceError, GitWorkspaceManager, GitWorkspaceStore, JOURNAL_DB_FILENAME, JOURNAL_QUARANTINE_DIRNAME, JOURNAL_TASK_REF_PREFIX, JournalClosedError, JournalCorruptError, JournalRecordTooLargeError, JournalUnavailableError, JournalUnknownTaskError, LocalStorageEmergencyError, LocalStoragePolicyError, LocalStoragePressureEngine, McpToolsetDefinitionRevisionConflictError, McpToolsetRevisionConflictError, PI_PACKAGE_NAME, PiAdapter, PolicyUnsupportedError, RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON, RuntimeDisposalFailure, RuntimeExecutionFailure, SKILL_PACKS_CAPABILITY, SKILL_PACKS_DIRNAME, SKILL_PACK_AUDIT_FILENAME, SKILL_PACK_INSTALL_ERROR_CODES, SKILL_PACK_LOCK_FILENAME, SKILL_PACK_LOCK_SCHEMA, SKILL_PACK_RESPONSE_MAX_BYTES, SecureDirHardeningError, SkillPackInstallError, SqliteLocalTaskJournal, SteerUnsupportedError, StoredDeviceProofSigner, TruthMemoryClient, TruthMemoryClientError, UnsupportedServicePlatformError, buildIcaclsArgs, cleanupEligibleAt, cleanupOrderFor, computePressureState, createAgentHomeProjection, createAgentHomeProjectionConsumer, createDaemon, createDaemonWithAdapters, createFilesystemCleanupExecutor, createServiceLifecycle, createStatfsFreeBytesProvider, ensureSecureDir, freezeRuntimeAdapterDescriptor, generateLaunchdPlist, generateSystemdUnit, generateWinswXml, installSkillPacks, isGitWorkspaceConfig, isRuntimeDisposalFailure, isRuntimeExecutionFailure, journalHash, listInstalledSkillPacks, nodeAgentProgram, prependGitWorkspaceGuidance, projectRuntimeBoundaryFailure, projectRuntimeExecutionFailure, projectSkillPack, requestDeviceAssertion, resolveLocalAgentReleaseIdentity, resolveLocalStoragePolicy, sanitizeServiceName, sealRuntimeOperationManifest, skillPacksRoot, stableAgentHomeOwnerId, validateAgentRef };
|
|
16279
16481
|
//# sourceMappingURL=index.js.map
|
|
16280
16482
|
//# sourceMappingURL=index.js.map
|