@byok-sdk/client 0.8.0-beta.0 → 0.8.1

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.
@@ -1,184 +1,543 @@
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 path, { isAbsolute, join } from 'path';
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 * as path3 from 'path';
5
+ import path3__default, { isAbsolute, join } from 'path';
6
+ 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, STRICT_AGENT_ONLY_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';
7
+ import net, { createServer, createConnection } from 'net';
8
+ import * as os from 'os';
9
+ import os__default from 'os';
6
10
  import { execFile, spawn } from 'child_process';
7
- import os from 'os';
8
11
  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';
9
12
  import { promisify } from 'util';
10
13
  import { fileURLToPath } from 'url';
11
14
  import 'readline';
12
- import net, { createServer, createConnection } from 'net';
15
+ import * as fs12 from 'fs/promises';
13
16
  import { WebSocket } from 'ws';
14
17
  import { createRequire } from 'module';
15
18
  import { createInterface } from 'readline/promises';
16
19
 
17
- var AGENT_HOME_DIRECTORY = "agents";
18
- var AGENT_HOME_INTERNAL_DIRECTORY = ".byok";
19
- var AgentHomeError = class extends Error {
20
- constructor(message) {
21
- super(message);
22
- this.name = "AgentHomeError";
20
+ var tmpSeq = 0;
21
+ async function atomicWriteFile(filePath, data, options = {}) {
22
+ const tmpPath = `${filePath}.${process.pid}-${tmpSeq++}.tmp`;
23
+ try {
24
+ const handle = await promises.open(tmpPath, "w", options.mode);
25
+ try {
26
+ await handle.writeFile(data);
27
+ if (options.mode !== void 0) {
28
+ await handle.chmod(options.mode);
29
+ }
30
+ if (options.fsync) {
31
+ await handle.sync();
32
+ }
33
+ } finally {
34
+ await handle.close();
35
+ }
36
+ } catch (err) {
37
+ await promises.rm(tmpPath, { force: true }).catch(() => {
38
+ });
39
+ throw err;
23
40
  }
24
- };
25
- var AgentRefValidationError = class extends AgentHomeError {
26
- constructor(message) {
27
- super(message);
28
- this.name = "AgentRefValidationError";
41
+ await renameOnto(tmpPath, filePath);
42
+ if (options.mode !== void 0) {
43
+ await promises.chmod(filePath, options.mode);
29
44
  }
30
- };
31
- var AgentHomeResolutionError = class extends AgentHomeError {
32
- constructor(message) {
33
- super(message);
34
- this.name = "AgentHomeResolutionError";
45
+ if (options.fsync) {
46
+ const target = await promises.open(filePath, process.platform === "win32" ? "r+" : "r");
47
+ try {
48
+ await target.sync();
49
+ } finally {
50
+ await target.close();
51
+ }
52
+ if (process.platform !== "win32") {
53
+ const directory = await promises.open(path3__default.dirname(filePath), "r");
54
+ try {
55
+ await directory.sync();
56
+ } finally {
57
+ await directory.close();
58
+ }
59
+ }
35
60
  }
36
- };
37
- var AgentHomeCollisionError = class extends AgentHomeResolutionError {
38
- constructor(message) {
39
- super(message);
40
- this.name = "AgentHomeCollisionError";
61
+ }
62
+ var RENAME_RETRY_ATTEMPTS = 5;
63
+ var RENAME_RETRY_DELAY_MS = 20;
64
+ function delay(ms) {
65
+ return new Promise((resolve) => setTimeout(resolve, ms));
66
+ }
67
+ async function renameOnto(tmpPath, targetPath) {
68
+ for (let attempt = 1; attempt <= RENAME_RETRY_ATTEMPTS; attempt++) {
69
+ try {
70
+ await promises.rename(tmpPath, targetPath);
71
+ return;
72
+ } catch (err) {
73
+ const code = err.code;
74
+ if (code !== "EPERM" && code !== "EEXIST") {
75
+ await promises.rm(tmpPath, { force: true }).catch(() => {
76
+ });
77
+ throw err;
78
+ }
79
+ if (attempt === RENAME_RETRY_ATTEMPTS) {
80
+ await promises.rm(tmpPath, { force: true }).catch(() => {
81
+ });
82
+ throw err;
83
+ }
84
+ await delay(RENAME_RETRY_DELAY_MS * attempt);
85
+ }
41
86
  }
42
- };
43
- var AgentHomeBusyError = class extends AgentHomeError {
44
- constructor(message) {
45
- super(message);
46
- this.name = "AgentHomeBusyError";
87
+ }
88
+ var defaultRunner = (command, args) => new Promise((resolve, reject) => {
89
+ execFile(command, args, (error, stdout, stderr) => {
90
+ if (error && typeof error.code !== "number") {
91
+ reject(error);
92
+ return;
93
+ }
94
+ resolve({ code: error ? error.code : 0, stdout, stderr });
95
+ });
96
+ });
97
+ async function runOrThrow(run, command, args, label) {
98
+ const result = await run(command, args);
99
+ if (result.code !== 0) {
100
+ const detail = (result.stderr || result.stdout).trim();
101
+ throw new Error(`${label} failed (exit ${result.code})${detail ? `: ${detail}` : ""}`);
102
+ }
103
+ return result;
104
+ }
105
+ function isIdempotentAbsence(result, absence) {
106
+ if (result.code === 0) return true;
107
+ const detail = `${result.stdout}
108
+ ${result.stderr}`;
109
+ if (absence.neverAbsence?.some((pattern) => pattern.test(detail))) return false;
110
+ if (absence.codes?.includes(result.code)) return true;
111
+ return absence.patterns.some((pattern) => pattern.test(detail));
112
+ }
113
+ async function runIdempotent(run, command, args, label, absence) {
114
+ const result = await run(command, args);
115
+ if (!isIdempotentAbsence(result, absence)) {
116
+ const detail = (result.stderr || result.stdout).trim();
117
+ throw new Error(`${label} failed (exit ${result.code})${detail ? `: ${detail}` : ""}`);
118
+ }
119
+ return result;
120
+ }
121
+
122
+ // src/util/secure-dir.ts
123
+ var SYSTEM_SID = "*S-1-5-18";
124
+ var ADMINISTRATORS_SID = "*S-1-5-32-544";
125
+ function buildIcaclsArgs(dir, username) {
126
+ return [dir, "/inheritance:r", "/grant:r", `${username}:(OI)(CI)F`, "/grant", `${SYSTEM_SID}:(OI)(CI)F`, "/grant", `${ADMINISTRATORS_SID}:(OI)(CI)F`];
127
+ }
128
+ function buildIcaclsFileArgs(filePath, username) {
129
+ return [
130
+ filePath,
131
+ "/inheritance:r",
132
+ "/grant:r",
133
+ `${username}:F`,
134
+ "/grant",
135
+ `${SYSTEM_SID}:F`,
136
+ "/grant",
137
+ `${ADMINISTRATORS_SID}:F`
138
+ ];
139
+ }
140
+ var SecureDirHardeningError = class extends Error {
141
+ constructor(dir, reason) {
142
+ super(
143
+ `failed to apply a restrictive Windows ACL to "${dir}": ${reason} \u2014 refusing to leave this directory unprotected (it holds device credentials and/or the control-socket token, otherwise readable by any other local user); see docs/security.md`
144
+ );
145
+ this.dir = dir;
146
+ this.name = "SecureDirHardeningError";
47
147
  }
148
+ dir;
48
149
  };
49
- var AgentHomeLeaseCorruptError = class extends AgentHomeResolutionError {
50
- constructor(message) {
51
- super(message);
52
- this.name = "AgentHomeLeaseCorruptError";
150
+ var SecureFileHardeningError = class extends Error {
151
+ constructor(filePath, reason) {
152
+ super(`failed to apply a restrictive Windows ACL to "${filePath}": ${reason}`);
153
+ this.filePath = filePath;
154
+ this.name = "SecureFileHardeningError";
53
155
  }
156
+ filePath;
54
157
  };
55
- function validateAgentRef(value) {
56
- let candidate;
158
+ async function ensureSecureDir(dir, opts = {}) {
159
+ const platform = opts.platform ?? process.platform;
160
+ const run = opts.run ?? defaultRunner;
161
+ await promises.mkdir(dir, { recursive: true, mode: 448 });
162
+ await promises.chmod(dir, 448).catch(() => {
163
+ });
164
+ if (platform !== "win32") return;
165
+ const { username } = os__default.userInfo();
166
+ let result;
57
167
  try {
58
- candidate = AgentRefSchema.parse(value);
59
- } catch (error) {
60
- throw new AgentRefValidationError(
61
- `AgentRef does not match the protocol contract: ${error instanceof Error ? error.message : String(error)}`
62
- );
168
+ result = await run("icacls", buildIcaclsArgs(dir, username));
169
+ } catch (err) {
170
+ throw new SecureDirHardeningError(dir, `could not run icacls: ${err instanceof Error ? err.message : String(err)}`);
171
+ }
172
+ if (result.code !== 0) {
173
+ throw new SecureDirHardeningError(dir, `icacls exited ${result.code}: ${(result.stderr || result.stdout).trim()}`);
63
174
  }
64
- return Object.freeze({ agentId: candidate.agentId, profileRevision: candidate.profileRevision });
65
- }
66
- function isWithin(root, candidate) {
67
- const relative = path.relative(root, candidate);
68
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
69
175
  }
70
- function assertAbsolutePath(value, label) {
71
- if (typeof value !== "string" || value.length === 0 || !path.isAbsolute(value)) {
72
- throw new AgentHomeResolutionError(`${label} must be an absolute path`);
176
+ async function ensureSecureFile(filePath, opts = {}) {
177
+ const platform = opts.platform ?? process.platform;
178
+ const run = opts.run ?? defaultRunner;
179
+ await promises.chmod(filePath, 384);
180
+ if (platform !== "win32") return;
181
+ const { username } = os__default.userInfo();
182
+ let result;
183
+ try {
184
+ result = await run("icacls", buildIcaclsFileArgs(filePath, username));
185
+ } catch (err) {
186
+ throw new SecureFileHardeningError(filePath, `could not run icacls: ${err instanceof Error ? err.message : String(err)}`);
73
187
  }
74
- if (/[\u0000\r\n]/u.test(value)) {
75
- throw new AgentHomeResolutionError(`${label} must not contain NUL or line breaks`);
188
+ if (result.code !== 0) {
189
+ throw new SecureFileHardeningError(filePath, `icacls exited ${result.code}: ${(result.stderr || result.stdout).trim()}`);
76
190
  }
77
191
  }
78
- async function resolveExistingAncestor(inputPath) {
79
- let cursor = path.resolve(inputPath);
80
- const tail = [];
192
+
193
+ // src/daemon/path-mutation-gate.ts
194
+ var PathMutationGateBusyError = class extends Error {
195
+ constructor(scope, targetPath) {
196
+ super(`${scope} mutation gate is already held for ${targetPath}`);
197
+ this.scope = scope;
198
+ this.targetPath = targetPath;
199
+ this.name = "PathMutationGateBusyError";
200
+ }
201
+ scope;
202
+ targetPath;
203
+ };
204
+ var GATE_PROTOCOL_PREFIX = "byok-path-mutation-v1:";
205
+ var GATE_PROBE_TIMEOUT_MS = 1e3;
206
+ var POSIX_GATE_ROOT = `/tmp/byok-pm-${process.getuid?.() ?? "unknown"}`;
207
+ function assertAbsolutePath(value) {
208
+ if (!path3__default.isAbsolute(value)) throw new Error("path mutation gate target must be absolute");
209
+ if (/[\x00\r\n]/u.test(value)) throw new Error("path mutation gate target must not contain NUL or line breaks");
210
+ }
211
+ async function resolvePathWithoutCreate(input) {
212
+ assertAbsolutePath(input);
213
+ let cursor = path3__default.resolve(input);
214
+ const missing = [];
81
215
  for (; ; ) {
82
216
  try {
83
- return { canonical: await promises.realpath(cursor), tail };
217
+ return path3__default.resolve(await promises.realpath(cursor), ...missing);
84
218
  } catch (error) {
85
219
  const code = error.code;
86
220
  if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
87
- const parent = path.dirname(cursor);
88
- if (parent === cursor) throw new AgentHomeResolutionError(`no existing ancestor for ${inputPath}`);
89
- tail.unshift(path.basename(cursor));
221
+ const parent = path3__default.dirname(cursor);
222
+ if (parent === cursor) throw new Error(`no existing ancestor for ${input}`);
223
+ missing.unshift(path3__default.basename(cursor));
90
224
  cursor = parent;
91
225
  }
92
226
  }
93
227
  }
94
- async function materializeDirectory(inputPath) {
95
- const { canonical: canonical2, tail } = await resolveExistingAncestor(inputPath);
96
- let cursor = canonical2;
97
- for (const component of tail) {
98
- cursor = path.join(cursor, component);
99
- try {
100
- const stat = await promises.lstat(cursor);
101
- if (!stat.isDirectory() || stat.isSymbolicLink()) {
102
- throw new AgentHomeResolutionError(`Agent home path component is not a real directory: ${cursor}`);
103
- }
104
- } catch (error) {
105
- if (error.code !== "ENOENT") throw error;
106
- await promises.mkdir(cursor, { mode: 448 });
107
- }
108
- }
109
- const realized = await promises.realpath(inputPath);
110
- if (realized !== cursor) {
111
- throw new AgentHomeResolutionError(`Agent home path changed through a symlink while being created: ${inputPath}`);
112
- }
113
- return realized;
228
+ function gateIdentity(scope, canonicalTarget) {
229
+ return createHash("sha256").update(`${scope}\0${canonicalTarget}`).digest("hex");
114
230
  }
115
- async function ensureDirectoryNoSymlink(root, target) {
116
- if (!isWithin(root, target)) throw new AgentHomeResolutionError("Agent home is outside hostStorageRoot");
117
- const relative = path.relative(root, target);
118
- const components = relative === "" ? [] : relative.split(path.sep);
119
- let cursor = root;
120
- for (const component of components) {
121
- cursor = path.join(cursor, component);
122
- try {
123
- const stat = await promises.lstat(cursor);
124
- if (!stat.isDirectory() || stat.isSymbolicLink()) {
125
- throw new AgentHomeResolutionError(`Agent home path component is not a real directory: ${cursor}`);
126
- }
127
- } catch (error) {
128
- if (error.code !== "ENOENT") throw error;
129
- await promises.mkdir(cursor, { mode: 448 });
130
- }
131
- }
132
- const realized = await promises.realpath(target);
133
- if (!isWithin(root, realized) || realized !== target) {
134
- throw new AgentHomeResolutionError("Agent home changed through a symlink while it was being prepared");
135
- }
136
- return realized;
231
+ function gateEndpoint(identity) {
232
+ if (process.platform === "win32") return `\\\\.\\pipe\\byok-path-mutation-${identity}`;
233
+ return path3__default.join(POSIX_GATE_ROOT, `${identity}.sock`);
137
234
  }
138
- async function ensurePreservedFile(filePath) {
139
- try {
140
- const handle = await promises.open(filePath, "wx", 384);
141
- await handle.close();
142
- return;
143
- } catch (error) {
144
- if (error.code !== "EEXIST") throw error;
145
- }
146
- const stat = await promises.lstat(filePath);
147
- if (!stat.isFile() || stat.isSymbolicLink()) {
148
- throw new AgentHomeResolutionError(`Agent home preserved file is not a regular file: ${filePath}`);
149
- }
235
+ function endProbe(socket, identity) {
236
+ socket.on("error", () => {
237
+ });
238
+ socket.end(`${GATE_PROTOCOL_PREFIX}${identity}
239
+ `);
240
+ }
241
+ async function probeEndpoint(endpoint, identity) {
242
+ return new Promise((resolve) => {
243
+ const socket = createConnection(endpoint);
244
+ let settled = false;
245
+ let raw = "";
246
+ const finish = (result) => {
247
+ if (settled) return;
248
+ settled = true;
249
+ clearTimeout(timer);
250
+ socket.removeAllListeners();
251
+ socket.destroy();
252
+ resolve(result);
253
+ };
254
+ const timer = setTimeout(() => finish("occupied"), GATE_PROBE_TIMEOUT_MS);
255
+ socket.setEncoding("utf8");
256
+ socket.on("data", (chunk) => {
257
+ raw += chunk;
258
+ if (raw.length > GATE_PROTOCOL_PREFIX.length + 65) finish("occupied");
259
+ });
260
+ socket.once("end", () => {
261
+ finish(raw.trimEnd() === `${GATE_PROTOCOL_PREFIX}${identity}` ? "holder" : "occupied");
262
+ });
263
+ socket.once("error", (error) => {
264
+ finish(error.code === "ECONNREFUSED" || error.code === "ENOENT" ? "unbound" : "occupied");
265
+ });
266
+ });
267
+ }
268
+ async function assertPrivateGateRoot() {
269
+ if (process.platform === "win32") return;
270
+ await ensureSecureDir(POSIX_GATE_ROOT);
271
+ const stat = await promises.lstat(POSIX_GATE_ROOT);
272
+ const uid = process.getuid?.();
273
+ if (!stat.isDirectory() || stat.isSymbolicLink() || uid !== void 0 && stat.uid !== uid) {
274
+ throw new Error(`path mutation gate root is not a private owned directory: ${POSIX_GATE_ROOT}`);
275
+ }
276
+ }
277
+ async function clearProvenStaleEndpoint(endpoint, identity, scope, targetPath) {
278
+ if (process.platform === "win32") return;
279
+ let stat;
280
+ try {
281
+ stat = await promises.lstat(endpoint);
282
+ } catch (error) {
283
+ if (error.code === "ENOENT") return;
284
+ throw error;
285
+ }
286
+ if (!stat.isSocket()) throw new Error(`path mutation gate endpoint is not a socket: ${endpoint}`);
287
+ if (await probeEndpoint(endpoint, identity) !== "unbound") {
288
+ throw new PathMutationGateBusyError(scope, targetPath);
289
+ }
290
+ await promises.rm(endpoint, { force: true });
291
+ }
292
+ async function acquireOne(input) {
293
+ const canonicalTarget = await resolvePathWithoutCreate(input.targetPath);
294
+ const identity = gateIdentity(input.scope, canonicalTarget);
295
+ const endpoint = gateEndpoint(identity);
296
+ await assertPrivateGateRoot();
297
+ await clearProvenStaleEndpoint(endpoint, identity, input.scope, canonicalTarget);
298
+ const server = createServer((socket) => endProbe(socket, identity));
299
+ try {
300
+ await new Promise((resolve, reject) => {
301
+ server.once("error", reject);
302
+ server.listen(endpoint, () => {
303
+ server.removeListener("error", reject);
304
+ resolve();
305
+ });
306
+ });
307
+ } catch (error) {
308
+ server.close();
309
+ if (error.code === "EADDRINUSE") {
310
+ throw new PathMutationGateBusyError(input.scope, canonicalTarget);
311
+ }
312
+ throw error;
313
+ }
314
+ if (process.platform !== "win32") await promises.chmod(endpoint, 384).catch(() => void 0);
315
+ server.unref();
316
+ try {
317
+ const rechecked = await resolvePathWithoutCreate(input.targetPath);
318
+ if (rechecked !== canonicalTarget) {
319
+ throw new Error(`path mutation gate target changed during acquisition: ${input.targetPath}`);
320
+ }
321
+ } catch (error) {
322
+ await new Promise((resolve) => server.close(() => resolve()));
323
+ if (process.platform !== "win32") await promises.rm(endpoint, { force: true }).catch(() => void 0);
324
+ throw error;
325
+ }
326
+ let serverClosed = false;
327
+ let released = false;
328
+ let releaseAttempt;
329
+ return Object.freeze({
330
+ scope: input.scope,
331
+ targetPath: canonicalTarget,
332
+ identity,
333
+ release: () => {
334
+ if (released) return Promise.resolve();
335
+ if (releaseAttempt !== void 0) return releaseAttempt;
336
+ releaseAttempt = (async () => {
337
+ if (!serverClosed) {
338
+ await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
339
+ serverClosed = true;
340
+ }
341
+ if (process.platform !== "win32") await promises.rm(endpoint, { force: true });
342
+ released = true;
343
+ })().catch((error) => {
344
+ releaseAttempt = void 0;
345
+ throw error;
346
+ });
347
+ return releaseAttempt;
348
+ }
349
+ });
350
+ }
351
+ async function acquirePathMutationGate(input, options = {}) {
352
+ const waitMs = options.waitMs ?? 0;
353
+ if (!Number.isSafeInteger(waitMs) || waitMs < 0 || waitMs > 3e4) {
354
+ throw new Error("path mutation gate waitMs must be an integer from 0 through 30000");
355
+ }
356
+ const deadline = Date.now() + waitMs;
357
+ for (; ; ) {
358
+ try {
359
+ return await acquireOne(input);
360
+ } catch (error) {
361
+ if (!(error instanceof PathMutationGateBusyError) || Date.now() >= deadline) throw error;
362
+ await new Promise((resolve) => {
363
+ setTimeout(resolve, Math.min(10, Math.max(1, deadline - Date.now())));
364
+ });
365
+ }
366
+ }
367
+ }
368
+
369
+ // src/agent-home.ts
370
+ var AGENT_HOME_DIRECTORY = "agents";
371
+ var AGENT_HOME_INTERNAL_DIRECTORY = ".byok";
372
+ var AGENT_HOME_PROJECTION_STATE_FILE = "agent-home-projection.json";
373
+ var AgentHomeError = class extends Error {
374
+ constructor(message) {
375
+ super(message);
376
+ this.name = "AgentHomeError";
377
+ }
378
+ };
379
+ var AgentRefValidationError = class extends AgentHomeError {
380
+ constructor(message) {
381
+ super(message);
382
+ this.name = "AgentRefValidationError";
383
+ }
384
+ };
385
+ var AgentHomeResolutionError = class extends AgentHomeError {
386
+ constructor(message) {
387
+ super(message);
388
+ this.name = "AgentHomeResolutionError";
389
+ }
390
+ };
391
+ var AgentHomeCollisionError = class extends AgentHomeResolutionError {
392
+ constructor(message) {
393
+ super(message);
394
+ this.name = "AgentHomeCollisionError";
395
+ }
396
+ };
397
+ var AgentHomeBusyError = class extends AgentHomeError {
398
+ constructor(message) {
399
+ super(message);
400
+ this.name = "AgentHomeBusyError";
401
+ }
402
+ };
403
+ var AgentHomeLeaseCorruptError = class extends AgentHomeResolutionError {
404
+ constructor(message) {
405
+ super(message);
406
+ this.name = "AgentHomeLeaseCorruptError";
407
+ }
408
+ };
409
+ function validateAgentRef(value) {
410
+ let candidate;
411
+ try {
412
+ candidate = AgentRefSchema.parse(value);
413
+ } catch (error) {
414
+ throw new AgentRefValidationError(
415
+ `AgentRef does not match the protocol contract: ${error instanceof Error ? error.message : String(error)}`
416
+ );
417
+ }
418
+ return Object.freeze({ agentId: candidate.agentId, profileRevision: candidate.profileRevision });
419
+ }
420
+ function isWithin(root, candidate) {
421
+ const relative = path3__default.relative(root, candidate);
422
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path3__default.sep}`) && !path3__default.isAbsolute(relative);
423
+ }
424
+ function assertAbsolutePath2(value, label) {
425
+ if (typeof value !== "string" || value.length === 0 || !path3__default.isAbsolute(value)) {
426
+ throw new AgentHomeResolutionError(`${label} must be an absolute path`);
427
+ }
428
+ if (/[\u0000\r\n]/u.test(value)) {
429
+ throw new AgentHomeResolutionError(`${label} must not contain NUL or line breaks`);
430
+ }
431
+ }
432
+ async function resolveExistingAncestor(inputPath) {
433
+ let cursor = path3__default.resolve(inputPath);
434
+ const tail = [];
435
+ for (; ; ) {
436
+ try {
437
+ return { canonical: await promises.realpath(cursor), tail };
438
+ } catch (error) {
439
+ const code = error.code;
440
+ if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
441
+ const parent = path3__default.dirname(cursor);
442
+ if (parent === cursor) throw new AgentHomeResolutionError(`no existing ancestor for ${inputPath}`);
443
+ tail.unshift(path3__default.basename(cursor));
444
+ cursor = parent;
445
+ }
446
+ }
447
+ }
448
+ async function materializeDirectory(inputPath) {
449
+ const { canonical: canonical2, tail } = await resolveExistingAncestor(inputPath);
450
+ let cursor = canonical2;
451
+ for (const component of tail) {
452
+ cursor = path3__default.join(cursor, component);
453
+ try {
454
+ const stat = await promises.lstat(cursor);
455
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
456
+ throw new AgentHomeResolutionError(`Agent home path component is not a real directory: ${cursor}`);
457
+ }
458
+ } catch (error) {
459
+ if (error.code !== "ENOENT") throw error;
460
+ await promises.mkdir(cursor, { mode: 448 });
461
+ }
462
+ }
463
+ const realized = await promises.realpath(inputPath);
464
+ if (realized !== cursor) {
465
+ throw new AgentHomeResolutionError(`Agent home path changed through a symlink while being created: ${inputPath}`);
466
+ }
467
+ return realized;
468
+ }
469
+ async function ensureDirectoryNoSymlink(root, target) {
470
+ if (!isWithin(root, target)) throw new AgentHomeResolutionError("Agent home is outside hostStorageRoot");
471
+ const relative = path3__default.relative(root, target);
472
+ const components = relative === "" ? [] : relative.split(path3__default.sep);
473
+ let cursor = root;
474
+ for (const component of components) {
475
+ cursor = path3__default.join(cursor, component);
476
+ try {
477
+ const stat = await promises.lstat(cursor);
478
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
479
+ throw new AgentHomeResolutionError(`Agent home path component is not a real directory: ${cursor}`);
480
+ }
481
+ } catch (error) {
482
+ if (error.code !== "ENOENT") throw error;
483
+ await promises.mkdir(cursor, { mode: 448 });
484
+ }
485
+ }
486
+ const realized = await promises.realpath(target);
487
+ if (!isWithin(root, realized) || realized !== target) {
488
+ throw new AgentHomeResolutionError("Agent home changed through a symlink while it was being prepared");
489
+ }
490
+ return realized;
491
+ }
492
+ async function ensurePreservedFile(filePath) {
493
+ try {
494
+ const handle = await promises.open(filePath, "wx", 384);
495
+ await handle.close();
496
+ return;
497
+ } catch (error) {
498
+ if (error.code !== "EEXIST") throw error;
499
+ }
500
+ const stat = await promises.lstat(filePath);
501
+ if (!stat.isFile() || stat.isSymbolicLink()) {
502
+ throw new AgentHomeResolutionError(`Agent home preserved file is not a regular file: ${filePath}`);
503
+ }
150
504
  }
151
505
  var AgentHomeLayout = class {
152
506
  hostStorageRootInput;
153
507
  agentIdByCanonicalHome = /* @__PURE__ */ new Map();
154
508
  canonicalRoot;
155
509
  constructor(hostStorageRoot) {
156
- assertAbsolutePath(hostStorageRoot, "agentHome.hostStorageRoot");
157
- this.hostStorageRootInput = path.resolve(hostStorageRoot);
510
+ assertAbsolutePath2(hostStorageRoot, "agentHome.hostStorageRoot");
511
+ this.hostStorageRootInput = path3__default.resolve(hostStorageRoot);
158
512
  }
159
513
  async resolve(agentRefInput) {
160
- const agentRef = validateAgentRef(agentRefInput);
161
- const hostStorageRoot = await this.resolveRoot();
162
- const agentsRoot = await ensureDirectoryNoSymlink(
163
- hostStorageRoot,
164
- path.join(hostStorageRoot, AGENT_HOME_DIRECTORY)
165
- );
166
- const lexicalHome = path.join(agentsRoot, agentRef.agentId);
167
- const canonicalHome = await ensureDirectoryNoSymlink(agentsRoot, lexicalHome);
168
- const priorAgentId = this.agentIdByCanonicalHome.get(canonicalHome);
169
- if (priorAgentId !== void 0 && priorAgentId !== agentRef.agentId) {
170
- throw new AgentHomeCollisionError(
171
- `canonical Agent home ${canonicalHome} is already bound to Agent ${priorAgentId}`
514
+ const gate = await this.acquireRootMutationGate();
515
+ try {
516
+ const agentRef = validateAgentRef(agentRefInput);
517
+ const hostStorageRoot = await this.resolveRoot();
518
+ const agentsRoot = await ensureDirectoryNoSymlink(
519
+ hostStorageRoot,
520
+ path3__default.join(hostStorageRoot, AGENT_HOME_DIRECTORY)
172
521
  );
522
+ const lexicalHome = path3__default.join(agentsRoot, agentRef.agentId);
523
+ const canonicalHome = await ensureDirectoryNoSymlink(agentsRoot, lexicalHome);
524
+ const priorAgentId = this.agentIdByCanonicalHome.get(canonicalHome);
525
+ if (priorAgentId !== void 0 && priorAgentId !== agentRef.agentId) {
526
+ throw new AgentHomeCollisionError(
527
+ `canonical Agent home ${canonicalHome} is already bound to Agent ${priorAgentId}`
528
+ );
529
+ }
530
+ this.agentIdByCanonicalHome.set(canonicalHome, agentRef.agentId);
531
+ return Object.freeze({
532
+ agentRef,
533
+ hostStorageRoot,
534
+ agentsRoot,
535
+ homeDir: canonicalHome,
536
+ canonicalHome
537
+ });
538
+ } finally {
539
+ await gate.release();
173
540
  }
174
- this.agentIdByCanonicalHome.set(canonicalHome, agentRef.agentId);
175
- return Object.freeze({
176
- agentRef,
177
- hostStorageRoot,
178
- agentsRoot,
179
- homeDir: canonicalHome,
180
- canonicalHome
181
- });
182
541
  }
183
542
  /**
184
543
  * Prove the canonical root is materializable and writable before the daemon
@@ -186,6 +545,7 @@ var AgentHomeLayout = class {
186
545
  * file is created by this preflight.
187
546
  */
188
547
  async preflight() {
548
+ const gate = await this.acquireRootMutationGate();
189
549
  let probePath;
190
550
  let handle;
191
551
  let created = false;
@@ -193,9 +553,9 @@ var AgentHomeLayout = class {
193
553
  const hostStorageRoot = await this.resolveRoot();
194
554
  const agentsRoot = await ensureDirectoryNoSymlink(
195
555
  hostStorageRoot,
196
- path.join(hostStorageRoot, AGENT_HOME_DIRECTORY)
556
+ path3__default.join(hostStorageRoot, AGENT_HOME_DIRECTORY)
197
557
  );
198
- probePath = path.join(agentsRoot, `.byok-agent-home-preflight-${randomUUID()}`);
558
+ probePath = path3__default.join(agentsRoot, `.byok-agent-home-preflight-${randomUUID()}`);
199
559
  handle = await promises.open(probePath, "wx", 384);
200
560
  created = true;
201
561
  await handle.sync();
@@ -211,8 +571,19 @@ var AgentHomeLayout = class {
211
571
  throw new AgentHomeResolutionError(
212
572
  `agentHome.hostStorageRoot preflight failed: ${error instanceof Error ? error.message : String(error)}`
213
573
  );
574
+ } finally {
575
+ await gate.release();
214
576
  }
215
577
  }
578
+ /**
579
+ * Construction-time validation is deliberately non-mutating. The actual
580
+ * writable preflight runs asynchronously after daemon ownership is acquired
581
+ * and before transport/capability publication, where it can participate in
582
+ * the cross-process relocation gate without a sync shadow lock.
583
+ */
584
+ preflightSync() {
585
+ assertAbsolutePath2(this.hostStorageRootInput, "agentHome.hostStorageRoot");
586
+ }
216
587
  async resolveRoot() {
217
588
  if (this.canonicalRoot !== void 0) return this.canonicalRoot;
218
589
  try {
@@ -224,9 +595,22 @@ var AgentHomeLayout = class {
224
595
  }
225
596
  return this.canonicalRoot;
226
597
  }
598
+ async acquireRootMutationGate() {
599
+ try {
600
+ return await acquirePathMutationGate({
601
+ scope: "agent-home-root",
602
+ targetPath: path3__default.join(this.hostStorageRootInput, AGENT_HOME_DIRECTORY)
603
+ }, { waitMs: 1e3 });
604
+ } catch (error) {
605
+ if (error instanceof PathMutationGateBusyError) {
606
+ throw new AgentHomeBusyError("Agent-home root is reserved for local-state relocation");
607
+ }
608
+ throw error;
609
+ }
610
+ }
227
611
  };
228
612
  function stableAgentHomeOwnerId(storeDir, productId) {
229
- const identity = `${path.resolve(storeDir)}\0${productId}`;
613
+ const identity = `${path3__default.resolve(storeDir)}\0${productId}`;
230
614
  return `store-product:${createHash("sha256").update(identity).digest("hex")}`;
231
615
  }
232
616
  function parseLeaseMarker(value, lockPath) {
@@ -236,7 +620,7 @@ function parseLeaseMarker(value, lockPath) {
236
620
  } catch {
237
621
  throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} is corrupt`);
238
622
  }
239
- if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.ownerId !== "string" || typeof parsed.leaseId !== "string" || typeof parsed.canonicalHome !== "string" || !path.isAbsolute(parsed.canonicalHome)) {
623
+ if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.ownerId !== "string" || typeof parsed.leaseId !== "string" || typeof parsed.canonicalHome !== "string" || !path3__default.isAbsolute(parsed.canonicalHome)) {
240
624
  throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} has an invalid shape`);
241
625
  }
242
626
  let agentRef;
@@ -246,7 +630,7 @@ function parseLeaseMarker(value, lockPath) {
246
630
  throw new AgentHomeLeaseCorruptError(`Agent home lease marker ${lockPath} has an invalid AgentRef`);
247
631
  }
248
632
  const marker = parsed;
249
- return { ...marker, agentRef, canonicalHome: path.resolve(marker.canonicalHome) };
633
+ return { ...marker, agentRef, canonicalHome: path3__default.resolve(marker.canonicalHome) };
250
634
  }
251
635
  var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
252
636
  static held = /* @__PURE__ */ new Map();
@@ -263,14 +647,19 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
263
647
  _AgentHomeLeaseManager.held.set(canonicalHome, leaseId);
264
648
  let lockPath;
265
649
  let handle;
650
+ let rootGate;
266
651
  let ownsMarker = false;
267
652
  try {
653
+ rootGate = await acquirePathMutationGate({
654
+ scope: "agent-home-root",
655
+ targetPath: resolution.agentsRoot
656
+ }, { waitMs: 1e3 });
268
657
  await ensureDirectoryNoSymlink(resolution.agentsRoot, canonicalHome);
269
658
  const internalDir = await ensureDirectoryNoSymlink(
270
659
  canonicalHome,
271
- path.join(canonicalHome, AGENT_HOME_INTERNAL_DIRECTORY)
660
+ path3__default.join(canonicalHome, AGENT_HOME_INTERNAL_DIRECTORY)
272
661
  );
273
- lockPath = path.join(internalDir, "agent-home.lease");
662
+ lockPath = path3__default.join(internalDir, "agent-home.lease");
274
663
  handle = await this.openLeaseMarker(lockPath, canonicalHome, agentRef.agentId);
275
664
  ownsMarker = true;
276
665
  const marker = {
@@ -284,14 +673,21 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
284
673
  await handle.sync();
285
674
  await handle.close();
286
675
  handle = void 0;
676
+ await rootGate.release();
677
+ rootGate = void 0;
287
678
  } catch (error) {
288
679
  await handle?.close().catch(() => {
289
680
  });
290
681
  if (ownsMarker && lockPath !== void 0) await promises.rm(lockPath, { force: true }).catch(() => {
291
682
  });
683
+ await rootGate?.release().catch(() => {
684
+ });
292
685
  if (_AgentHomeLeaseManager.held.get(canonicalHome) === leaseId) {
293
686
  _AgentHomeLeaseManager.held.delete(canonicalHome);
294
687
  }
688
+ if (error instanceof PathMutationGateBusyError) {
689
+ throw new AgentHomeBusyError("Agent-home root is reserved for local-state relocation");
690
+ }
295
691
  if (error instanceof AgentHomeError) throw error;
296
692
  throw new AgentHomeError(`could not acquire Agent home lease: ${error instanceof Error ? error.message : String(error)}`);
297
693
  }
@@ -369,9 +765,73 @@ var AgentHomeLeaseManager = class _AgentHomeLeaseManager {
369
765
  async function initializeAgentHome(resolution) {
370
766
  await ensureDirectoryNoSymlink(
371
767
  resolution.canonicalHome,
372
- path.join(resolution.canonicalHome, "notes")
768
+ path3__default.join(resolution.canonicalHome, "notes")
373
769
  );
374
- await ensurePreservedFile(path.join(resolution.canonicalHome, "MEMORY.md"));
770
+ await ensurePreservedFile(path3__default.join(resolution.canonicalHome, "MEMORY.md"));
771
+ }
772
+ function projectionStatePath(resolution) {
773
+ return path3__default.join(
774
+ resolution.canonicalHome,
775
+ AGENT_HOME_INTERNAL_DIRECTORY,
776
+ AGENT_HOME_PROJECTION_STATE_FILE
777
+ );
778
+ }
779
+ async function readProjectionState(resolution) {
780
+ const filePath = projectionStatePath(resolution);
781
+ try {
782
+ const stat = await promises.lstat(filePath);
783
+ if (!stat.isFile() || stat.isSymbolicLink()) {
784
+ throw new AgentHomeResolutionError(`Agent projection state is not a regular file: ${filePath}`);
785
+ }
786
+ } catch (error) {
787
+ if (error.code === "ENOENT") return void 0;
788
+ throw error;
789
+ }
790
+ let parsed;
791
+ try {
792
+ parsed = JSON.parse(await promises.readFile(filePath, "utf8"));
793
+ } catch (error) {
794
+ throw new AgentHomeResolutionError(
795
+ `Agent projection state is corrupt: ${error instanceof Error ? error.message : String(error)}`
796
+ );
797
+ }
798
+ if (typeof parsed !== "object" || parsed === null || parsed.version !== 1 || typeof parsed.requestId !== "string" || typeof parsed.projectionHash !== "string") {
799
+ throw new AgentHomeResolutionError("Agent projection state has an invalid shape");
800
+ }
801
+ const candidate = parsed;
802
+ const agentRef = validateAgentRef(candidate.agentRef);
803
+ if (agentRef.agentId !== resolution.agentRef.agentId) {
804
+ throw new AgentHomeCollisionError("Agent projection state belongs to a different Agent home");
805
+ }
806
+ return Object.freeze({
807
+ version: 1,
808
+ agentRef,
809
+ requestId: candidate.requestId,
810
+ projectionHash: candidate.projectionHash
811
+ });
812
+ }
813
+ async function writeProjectionState(resolution, payload) {
814
+ const filePath = projectionStatePath(resolution);
815
+ const existing = await promises.lstat(filePath).catch((error) => {
816
+ if (error.code === "ENOENT") return void 0;
817
+ throw error;
818
+ });
819
+ if (existing !== void 0 && (!existing.isFile() || existing.isSymbolicLink())) {
820
+ throw new AgentHomeResolutionError(`Agent projection state is not a regular file: ${filePath}`);
821
+ }
822
+ const state = {
823
+ version: 1,
824
+ agentRef: payload.agentRef,
825
+ requestId: payload.requestId,
826
+ projectionHash: payload.projectionHash
827
+ };
828
+ await atomicWriteFile(filePath, `${JSON.stringify(state)}
829
+ `, { mode: 384, fsync: true });
830
+ }
831
+ function compareProjectionRevision(left, right) {
832
+ const leftRevision = BigInt(left);
833
+ const rightRevision = BigInt(right);
834
+ return leftRevision < rightRevision ? -1 : leftRevision > rightRevision ? 1 : 0;
375
835
  }
376
836
  var AgentHomeManager = class {
377
837
  layout;
@@ -397,6 +857,10 @@ var AgentHomeManager = class {
397
857
  async preflight() {
398
858
  await this.layout.preflight();
399
859
  }
860
+ /** Synchronous construction-time preflight for strict Agent-only admission. */
861
+ preflightSync() {
862
+ this.layout.preflightSync();
863
+ }
400
864
  /** Resolve and lease without applying downstream projection side effects. */
401
865
  async acquire(agentRef) {
402
866
  const resolution = await this.layout.resolve(agentRef);
@@ -407,14 +871,68 @@ var AgentHomeManager = class {
407
871
  async initialize(binding) {
408
872
  const { resolution, lease } = binding;
409
873
  await initializeAgentHome(resolution);
410
- await this.projection?.prepare({ ...resolution, cwd: lease.cwd });
874
+ const prepare = this.projection?.prepare;
875
+ if (prepare !== void 0) await prepare({ ...resolution, cwd: lease.cwd });
411
876
  if (await promises.realpath(resolution.homeDir) !== resolution.canonicalHome) {
412
877
  throw new AgentHomeResolutionError("Agent projection changed the canonical home path");
413
878
  }
414
879
  await initializeAgentHome(resolution);
415
880
  }
416
- };
417
- var AgentSessionHandoffStoreError = class extends Error {
881
+ supportsTaskFreeProjection() {
882
+ return this.projection?.apply !== void 0;
883
+ }
884
+ /**
885
+ * Apply one task-free projection under the same canonical-home writer lease
886
+ * used by Agent execution. The host hook owns an atomic/idempotent ensure of
887
+ * its opaque product bytes, so an exact desired-state replay invokes it again
888
+ * before returning `idempotent`. Only a successful new-state hook followed
889
+ * by the SDK-owned fsynced ordering record can return `applied`.
890
+ */
891
+ async project(input) {
892
+ const payload = AgentHomeProjectionPayloadSchema.parse(input);
893
+ const binding = await this.acquire(payload.agentRef);
894
+ try {
895
+ const { resolution, lease } = binding;
896
+ await initializeAgentHome(resolution);
897
+ const applyProjection = async () => {
898
+ const apply = this.projection?.apply;
899
+ if (apply === void 0) {
900
+ throw new AgentHomeError("task-free Agent-home projection is not configured");
901
+ }
902
+ await apply({
903
+ ...resolution,
904
+ cwd: lease.cwd,
905
+ requestId: payload.requestId,
906
+ projectionHash: payload.projectionHash,
907
+ projection: payload.projection
908
+ });
909
+ if (await promises.realpath(resolution.homeDir) !== resolution.canonicalHome) {
910
+ throw new AgentHomeResolutionError("Agent projection changed the canonical home path");
911
+ }
912
+ await initializeAgentHome(resolution);
913
+ };
914
+ const current = await readProjectionState(resolution);
915
+ if (current !== void 0) {
916
+ const order = compareProjectionRevision(
917
+ payload.agentRef.profileRevision,
918
+ current.agentRef.profileRevision
919
+ );
920
+ if (order < 0) return "stale";
921
+ if (order === 0) {
922
+ if (payload.projectionHash !== current.projectionHash) return "conflict";
923
+ await applyProjection();
924
+ return "idempotent";
925
+ }
926
+ }
927
+ await applyProjection();
928
+ await writeProjectionState(resolution, payload);
929
+ return "applied";
930
+ } finally {
931
+ await binding.lease.release();
932
+ }
933
+ }
934
+ };
935
+ var AgentSessionHandoffStoreError = class extends Error {
418
936
  constructor(message) {
419
937
  super(message);
420
938
  this.name = "AgentSessionHandoffStoreError";
@@ -453,7 +971,7 @@ function parseTaskTerminalEntry(value) {
453
971
  assertNonEmptyString(value.terminalReason, "taskTerminal.terminalReason");
454
972
  assertNonEmptyString(value.updatedAt, "taskTerminal.updatedAt");
455
973
  if (value.sessionRef !== void 0) assertNonEmptyString(value.sessionRef, "taskTerminal.sessionRef");
456
- if (typeof value.cwd !== "string" || !path.isAbsolute(value.cwd)) {
974
+ if (typeof value.cwd !== "string" || !path3__default.isAbsolute(value.cwd)) {
457
975
  throw new AgentSessionHandoffCorruptError("taskTerminal.cwd must be an absolute path");
458
976
  }
459
977
  if (value.terminalCause !== "failed") {
@@ -466,7 +984,7 @@ function parseTaskTerminalEntry(value) {
466
984
  agentRef,
467
985
  taskId: value.taskId,
468
986
  runtimeId: value.runtimeId,
469
- cwd: path.resolve(value.cwd),
987
+ cwd: path3__default.resolve(value.cwd),
470
988
  leaseId: value.leaseId,
471
989
  ...value.sessionRef === void 0 ? {} : { sessionRef: value.sessionRef },
472
990
  terminalCause: "failed",
@@ -496,7 +1014,7 @@ function parseEntry(value) {
496
1014
  assertNonEmptyString(value.runtimeId, "handoff.runtimeId");
497
1015
  assertNonEmptyString(value.leaseId, "handoff.leaseId");
498
1016
  assertNonEmptyString(value.updatedAt, "handoff.updatedAt");
499
- if (typeof value.cwd !== "string" || !path.isAbsolute(value.cwd)) {
1017
+ if (typeof value.cwd !== "string" || !path3__default.isAbsolute(value.cwd)) {
500
1018
  throw new AgentSessionHandoffCorruptError("handoff.cwd must be an absolute path");
501
1019
  }
502
1020
  if (Number.isNaN(Date.parse(value.updatedAt))) {
@@ -513,7 +1031,7 @@ function parseEntry(value) {
513
1031
  taskId: value.taskId,
514
1032
  sessionRef: value.sessionRef,
515
1033
  runtimeId: value.runtimeId,
516
- cwd: path.resolve(value.cwd),
1034
+ cwd: path3__default.resolve(value.cwd),
517
1035
  leaseId: value.leaseId,
518
1036
  ...value.terminalCause === void 0 ? {} : { terminalCause: value.terminalCause },
519
1037
  ...value.terminalReason === void 0 ? {} : { terminalReason: value.terminalReason },
@@ -524,10 +1042,10 @@ function sameRef(left, right) {
524
1042
  return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
525
1043
  }
526
1044
  function sameMatch(entry, expected) {
527
- return sameRef(entry.agentRef, expected.agentRef) && entry.sessionRef === expected.sessionRef && entry.runtimeId === expected.runtimeId && entry.cwd === path.resolve(expected.cwd);
1045
+ return sameRef(entry.agentRef, expected.agentRef) && entry.sessionRef === expected.sessionRef && entry.runtimeId === expected.runtimeId && entry.cwd === path3__default.resolve(expected.cwd);
528
1046
  }
529
1047
  function sameTaskTerminalMatch(entry, expected) {
530
- return sameRef(entry.agentRef, expected.agentRef) && entry.taskId === expected.taskId && entry.runtimeId === expected.runtimeId && entry.cwd === path.resolve(expected.cwd);
1048
+ return sameRef(entry.agentRef, expected.agentRef) && entry.taskId === expected.taskId && entry.runtimeId === expected.runtimeId && entry.cwd === path3__default.resolve(expected.cwd);
531
1049
  }
532
1050
  function sessionFileName(runtimeId, sessionRef) {
533
1051
  const digest2 = createHash("sha256").update(sessionRef, "utf8").digest("hex");
@@ -540,13 +1058,13 @@ function taskTerminalFileName(runtimeId, taskId) {
540
1058
  return `${runtime}-task-${digest2}.jsonl`;
541
1059
  }
542
1060
  async function evidenceDirectory(cwdInput) {
543
- if (!path.isAbsolute(cwdInput)) {
1061
+ if (!path3__default.isAbsolute(cwdInput)) {
544
1062
  throw new AgentSessionHandoffStoreError("Agent session cwd must be absolute");
545
1063
  }
546
1064
  const cwd = await promises.realpath(cwdInput);
547
1065
  let cursor = cwd;
548
1066
  for (const component of [".byok", "runtime-sessions"]) {
549
- cursor = path.join(cursor, component);
1067
+ cursor = path3__default.join(cursor, component);
550
1068
  try {
551
1069
  const stat = await promises.lstat(cursor);
552
1070
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
@@ -558,8 +1076,8 @@ async function evidenceDirectory(cwdInput) {
558
1076
  }
559
1077
  }
560
1078
  const canonical2 = await promises.realpath(cursor);
561
- const relative = path.relative(cwd, canonical2);
562
- if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
1079
+ const relative = path3__default.relative(cwd, canonical2);
1080
+ if (relative === ".." || relative.startsWith(`..${path3__default.sep}`) || path3__default.isAbsolute(relative)) {
563
1081
  throw new AgentSessionHandoffStoreError("Agent session evidence path escaped the canonical Agent home");
564
1082
  }
565
1083
  return canonical2;
@@ -605,7 +1123,7 @@ var AgentSessionHandoffStore = class {
605
1123
  taskId: input.taskId,
606
1124
  sessionRef: input.sessionRef,
607
1125
  runtimeId: input.runtimeId,
608
- cwd: path.resolve(input.cwd),
1126
+ cwd: path3__default.resolve(input.cwd),
609
1127
  leaseId: input.leaseId,
610
1128
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
611
1129
  });
@@ -648,7 +1166,7 @@ var AgentSessionHandoffStore = class {
648
1166
  agentRef: validateAgentRef(input.agentRef),
649
1167
  taskId: input.taskId,
650
1168
  runtimeId: input.runtimeId,
651
- cwd: path.resolve(input.cwd)
1169
+ cwd: path3__default.resolve(input.cwd)
652
1170
  };
653
1171
  const filePath = await this.taskTerminalFilePath(expected);
654
1172
  return this.enqueue(filePath, async () => {
@@ -675,7 +1193,7 @@ var AgentSessionHandoffStore = class {
675
1193
  agentRef: validateAgentRef(expectedInput.agentRef),
676
1194
  taskId: expectedInput.taskId,
677
1195
  runtimeId: expectedInput.runtimeId,
678
- cwd: path.resolve(expectedInput.cwd)
1196
+ cwd: path3__default.resolve(expectedInput.cwd)
679
1197
  };
680
1198
  const filePath = await this.taskTerminalFilePath(expected);
681
1199
  return this.enqueue(filePath, async () => {
@@ -693,14 +1211,14 @@ var AgentSessionHandoffStore = class {
693
1211
  assertNonEmptyString(match.sessionRef, "handoff.sessionRef");
694
1212
  assertNonEmptyString(match.runtimeId, "handoff.runtimeId");
695
1213
  const directory = await evidenceDirectory(match.cwd);
696
- return path.join(directory, sessionFileName(match.runtimeId, match.sessionRef));
1214
+ return path3__default.join(directory, sessionFileName(match.runtimeId, match.sessionRef));
697
1215
  }
698
1216
  async taskTerminalFilePath(match) {
699
1217
  validateAgentRef(match.agentRef);
700
1218
  assertNonEmptyString(match.taskId, "taskTerminal.taskId");
701
1219
  assertNonEmptyString(match.runtimeId, "taskTerminal.runtimeId");
702
1220
  const directory = await evidenceDirectory(match.cwd);
703
- return path.join(directory, taskTerminalFileName(match.runtimeId, match.taskId));
1221
+ return path3__default.join(directory, taskTerminalFileName(match.runtimeId, match.taskId));
704
1222
  }
705
1223
  enqueue(key, task) {
706
1224
  const previous = this.queues.get(key) ?? Promise.resolve();
@@ -774,183 +1292,537 @@ var AgentSessionHandoffStore = class {
774
1292
  }
775
1293
  }
776
1294
  };
777
-
778
- // src/types.ts
779
- function frozenStrings(values) {
780
- return values === void 0 ? void 0 : Object.freeze([...values]);
781
- }
782
- function frozenPolicy(policy) {
783
- const allowTools = policy.allowTools === void 0 ? void 0 : Object.freeze([...policy.allowTools]);
784
- const denyTools = policy.denyTools === void 0 ? void 0 : Object.freeze([...policy.denyTools]);
785
- return Object.freeze({
786
- mode: policy.mode,
787
- ...allowTools === void 0 ? {} : { allowTools },
788
- ...denyTools === void 0 ? {} : { denyTools },
789
- ...policy.workspaceRoot === void 0 ? {} : { workspaceRoot: policy.workspaceRoot },
790
- ...policy.network === void 0 ? {} : { network: policy.network }
1295
+ var DAEMON_OWNER_FILENAME = "daemon-owner.json";
1296
+ var RECLAIM_FILENAME = `${DAEMON_OWNER_FILENAME}.reclaim`;
1297
+ var MAX_OWNER_BYTES = 4096;
1298
+ var RECLAIM_MALFORMED_GRACE_MS = 3e4;
1299
+ var SELF_PROCESS_STARTED_AT = new Date(Date.now() - process.uptime() * 1e3).toISOString();
1300
+ function endOwnershipProbe(socket, response) {
1301
+ socket.on("error", () => {
791
1302
  });
1303
+ if (response === void 0) socket.end();
1304
+ else socket.end(response);
792
1305
  }
793
- function freezeRuntimeAdapterDescriptor(descriptor) {
794
- const baseNames = frozenStrings(descriptor.environmentRequirements.baseNames);
795
- const credentialNames = frozenStrings(descriptor.environmentRequirements.credentialNames);
796
- return Object.freeze({
797
- id: descriptor.id,
798
- supportsDispatchSelection: descriptor.supportsDispatchSelection === true,
799
- capabilities: Object.freeze({
800
- steer: descriptor.capabilities.steer === true,
801
- resume: descriptor.capabilities.resume === true,
802
- approvalInteractive: descriptor.capabilities.approvalInteractive === true,
803
- ...descriptor.capabilities.mcpToolsets === void 0 ? {} : { mcpToolsets: descriptor.capabilities.mcpToolsets === true },
804
- permissionModes: Object.freeze([...descriptor.capabilities.permissionModes])
805
- }),
806
- environmentRequirements: Object.freeze({
807
- ...baseNames === void 0 ? {} : { baseNames },
808
- ...credentialNames === void 0 ? {} : { credentialNames }
809
- })
810
- });
1306
+ var STORE_MUTEX_ID_PREFIX = "byok-store-mutex-v1:";
1307
+ var STORE_MUTEX_PROBE_TIMEOUT_MS = 1e3;
1308
+ var STORE_MUTEX_SOCKET_FILENAME = "mutex.sock";
1309
+ var UNIX_SOCKET_PATH_SOFT_LIMIT = 100;
1310
+ var STORE_MUTEX_FALLBACK_ROOT = "/tmp";
1311
+ function storeMutexIdentity(canonicalStoreDir) {
1312
+ return createHash("sha256").update(canonicalStoreDir).digest("hex");
811
1313
  }
812
- function sealRuntimeOperationManifest(manifest) {
813
- return Object.freeze({
814
- taskId: manifest.taskId,
815
- runtimeId: manifest.runtimeId,
816
- descriptor: freezeRuntimeAdapterDescriptor(manifest.descriptor),
817
- policy: frozenPolicy(manifest.policy),
818
- requiredToolsetIds: Object.freeze([...manifest.requiredToolsetIds]),
819
- ...manifest.dispatchSelection === void 0 ? {} : { dispatchSelection: Object.freeze({ ...manifest.dispatchSelection }) },
820
- ...manifest.sessionRef === void 0 ? {} : { sessionRef: manifest.sessionRef },
821
- ...manifest.agentRef === void 0 ? {} : { agentRef: Object.freeze({ agentId: manifest.agentRef.agentId, profileRevision: manifest.agentRef.profileRevision }) },
822
- cwd: manifest.cwd ?? manifest.workspace.workspaceDir,
823
- ...manifest.lease === void 0 ? {} : { lease: Object.freeze({ leaseId: manifest.lease.leaseId, canonicalHome: manifest.lease.canonicalHome }) },
824
- workspace: Object.freeze({ ...manifest.workspace }),
825
- forwardedEnvironmentNames: Object.freeze([...manifest.forwardedEnvironmentNames])
826
- });
1314
+ function storeMutexEndpoint(canonicalStoreDir, identity, platform = process.platform) {
1315
+ if (platform === "win32") return `\\\\.\\pipe\\byok-store-mutex-${identity.slice(0, 16)}`;
1316
+ const candidate = path3__default.join(canonicalStoreDir, STORE_MUTEX_SOCKET_FILENAME);
1317
+ if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT) return candidate;
1318
+ return path3__default.join(STORE_MUTEX_FALLBACK_ROOT, `byok-store-mutex-${identity.slice(0, 16)}`, "sock");
827
1319
  }
828
- var PolicyUnsupportedError = class extends Error {
829
- constructor(message) {
830
- super(message);
831
- this.name = "PolicyUnsupportedError";
1320
+ var DaemonOwnerActiveError = class extends Error {
1321
+ constructor(role) {
1322
+ super(`store mutation lease is already held by an active ${role} process`);
1323
+ this.role = role;
1324
+ this.name = "DaemonOwnerActiveError";
832
1325
  }
1326
+ role;
833
1327
  };
834
- var SteerUnsupportedError = class extends Error {
835
- /** The `RuntimeAdapter.descriptor.id` that cannot steer (e.g. `claude`, `codex`). */
836
- runtimeId;
837
- constructor(runtimeId, message) {
838
- super(message);
839
- this.name = "SteerUnsupportedError";
840
- this.runtimeId = runtimeId;
1328
+ function isOwnerRecord(value) {
1329
+ if (typeof value !== "object" || value === null) return false;
1330
+ const candidate = value;
1331
+ return candidate.version === 2 && Number.isSafeInteger(candidate.pid) && (candidate.pid ?? 0) > 0 && typeof candidate.nonce === "string" && candidate.nonce.length === 36 && (candidate.role === "daemon" || candidate.role === "doctor") && typeof candidate.acquiredAt === "string" && Number.isFinite(Date.parse(candidate.acquiredAt)) && typeof candidate.processStartedAt === "string" && Number.isFinite(Date.parse(candidate.processStartedAt)) && Number.isSafeInteger(candidate.livenessPort) && (candidate.livenessPort ?? 0) > 0 && (candidate.livenessPort ?? 0) <= 65535;
1332
+ }
1333
+ async function readOwner(filePath) {
1334
+ let namedBefore;
1335
+ try {
1336
+ namedBefore = await promises.lstat(filePath, { bigint: true });
1337
+ } catch (err) {
1338
+ if (err.code === "ENOENT") return void 0;
1339
+ throw err;
841
1340
  }
842
- };
843
-
844
- // src/release-identity.ts
845
- var LOCAL_AGENT_RELEASE_VERSION_MAX_LENGTH = 128;
846
- var LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH = 128;
847
- var STRICT_SEMVER_PATTERN = new RegExp(
848
- "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$"
849
- );
850
- var BUILD_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
851
- function resolveLocalAgentReleaseIdentity(input) {
852
- if (input === void 0 || typeof input.version !== "string" || input.version.length > LOCAL_AGENT_RELEASE_VERSION_MAX_LENGTH || !STRICT_SEMVER_PATTERN.test(input.version)) {
853
- throw new Error("DaemonConfig.localAgentRelease.version must be canonical strict SemVer");
1341
+ if (!namedBefore.isFile() || namedBefore.isSymbolicLink()) {
1342
+ throw new Error("store mutation owner path is not a real regular file");
854
1343
  }
855
- if (input.buildId !== void 0 && (typeof input.buildId !== "string" || input.buildId.length > LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH || !BUILD_ID_PATTERN.test(input.buildId))) {
856
- throw new Error(
857
- `DaemonConfig.localAgentRelease.buildId must be 1-${LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH} safe opaque characters`
1344
+ let handle;
1345
+ try {
1346
+ handle = await promises.open(
1347
+ filePath,
1348
+ constants.O_RDONLY | (constants.O_NONBLOCK ?? 0) | (constants.O_NOFOLLOW ?? 0)
858
1349
  );
1350
+ } catch (err) {
1351
+ if (err.code === "ENOENT") return void 0;
1352
+ throw err;
859
1353
  }
860
- return Object.freeze({
861
- version: input.version,
862
- ...input.buildId === void 0 ? {} : { buildId: input.buildId }
863
- });
864
- }
865
-
866
- // src/runtime-failure.ts
867
- var RUNTIME_EXECUTION_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeExecutionFailure/v1");
868
- var RUNTIME_DISPOSAL_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeDisposalFailure/v1");
869
- var RuntimeDisposalFailure = class extends Error {
870
- stage;
871
- constructor(input, options) {
872
- if (!isRuntimeDisposalStage(input.stage) || typeof input.reason !== "string" || input.reason.length === 0) {
873
- throw new TypeError("invalid RuntimeDisposalFailure input");
1354
+ try {
1355
+ const stat = await handle.stat({ bigint: true });
1356
+ const namedAfterOpen = await promises.lstat(filePath, { bigint: true });
1357
+ if (!stat.isFile() || !namedAfterOpen.isFile() || namedAfterOpen.isSymbolicLink() || stat.dev !== namedBefore.dev || stat.ino !== namedBefore.ino || stat.size !== namedBefore.size || stat.mtimeNs !== namedBefore.mtimeNs || stat.ctimeNs !== namedBefore.ctimeNs || stat.dev !== namedAfterOpen.dev || stat.ino !== namedAfterOpen.ino || stat.size !== namedAfterOpen.size || stat.mtimeNs !== namedAfterOpen.mtimeNs || stat.ctimeNs !== namedAfterOpen.ctimeNs) {
1358
+ throw new Error("store mutation owner path changed before safe open");
874
1359
  }
875
- super(input.reason, options);
876
- this.name = "RuntimeDisposalFailure";
877
- this.stage = input.stage;
878
- Object.defineProperty(this, RUNTIME_DISPOSAL_FAILURE_BRAND, { value: true });
879
- Object.freeze(this);
880
- }
881
- };
882
- function isRuntimeDisposalStage(value) {
883
- return value === "signal" || value === "quiescence" || value === "cleanup";
884
- }
885
- function isRuntimeDisposalFailure(value) {
886
- if (typeof value !== "object" || value === null) return false;
887
- const candidate = value;
888
- return candidate[RUNTIME_DISPOSAL_FAILURE_BRAND] === true && isRuntimeDisposalStage(candidate.stage) && typeof candidate.message === "string" && candidate.message.length > 0;
889
- }
890
- var RuntimeExecutionFailure = class extends Error {
891
- phase;
892
- category;
893
- retry;
894
- constructor(input, options) {
895
- if (!isRuntimeFailurePhase(input.phase) || !isRuntimeFailureCategory(input.category) || !isRuntimeRetryDisposition(input.retry) || typeof input.reason !== "string" || input.reason.length === 0) {
896
- throw new TypeError("invalid RuntimeExecutionFailure input");
1360
+ if (stat.size <= 0 || stat.size > MAX_OWNER_BYTES) return void 0;
1361
+ const size = Number(stat.size);
1362
+ const buffer = Buffer.alloc(size);
1363
+ const { bytesRead } = await handle.read(buffer, 0, size, 0);
1364
+ const after = await handle.stat({ bigint: true });
1365
+ const namedAfterRead = await promises.lstat(filePath, { bigint: true });
1366
+ if (bytesRead !== size || after.dev !== stat.dev || after.ino !== stat.ino || after.size !== stat.size || after.mtimeNs !== stat.mtimeNs || after.ctimeNs !== stat.ctimeNs || namedAfterRead.dev !== stat.dev || namedAfterRead.ino !== stat.ino || namedAfterRead.size !== stat.size || namedAfterRead.mtimeNs !== stat.mtimeNs || namedAfterRead.ctimeNs !== stat.ctimeNs) {
1367
+ throw new Error("store mutation owner path changed during inspection");
897
1368
  }
898
- super(input.reason, options);
899
- this.name = "RuntimeExecutionFailure";
900
- this.phase = input.phase;
901
- this.category = input.category;
902
- this.retry = input.retry;
903
- Object.defineProperty(this, RUNTIME_EXECUTION_FAILURE_BRAND, { value: true });
904
- Object.freeze(this);
1369
+ const raw = buffer.toString("utf8");
1370
+ const parsed = JSON.parse(raw);
1371
+ return isOwnerRecord(parsed) ? parsed : void 0;
1372
+ } catch (err) {
1373
+ if (err instanceof Error && err.message.startsWith("store mutation owner path changed")) throw err;
1374
+ return void 0;
1375
+ } finally {
1376
+ await handle.close();
905
1377
  }
906
- };
907
- function isRuntimeFailurePhase(value) {
908
- return value === "start" || value === "run";
909
- }
910
- function isRuntimeFailureCategory(value) {
911
- return value === "semantic" || value === "infrastructure" || value === "authority";
912
1378
  }
913
- function isRuntimeRetryDisposition(value) {
914
- return value === "retryable" || value === "non-retryable";
915
- }
916
- function isRuntimeExecutionFailure(value) {
917
- if (typeof value !== "object" || value === null) return false;
918
- const candidate = value;
919
- return candidate[RUNTIME_EXECUTION_FAILURE_BRAND] === true && isRuntimeFailurePhase(candidate.phase) && isRuntimeFailureCategory(candidate.category) && isRuntimeRetryDisposition(candidate.retry) && typeof candidate.message === "string" && candidate.message.length > 0;
920
- }
921
- function retryableFromDisposition(disposition) {
922
- switch (disposition) {
923
- case "retryable":
924
- return true;
925
- case "non-retryable":
926
- return false;
1379
+ function pidIsAlive(pid) {
1380
+ try {
1381
+ process.kill(pid, 0);
1382
+ return true;
1383
+ } catch (err) {
1384
+ return err.code === "EPERM";
927
1385
  }
928
1386
  }
929
- function projectRuntimeExecutionFailure(failure) {
930
- return {
931
- reason: failure.message,
932
- retryable: retryableFromDisposition(failure.retry)
933
- };
934
- }
935
- var RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON = Object.freeze({
936
- start: "runtime adapter contract violation during start",
937
- run: "runtime adapter contract violation during run"
938
- });
939
- function projectRuntimeBoundaryFailure(value, expectedPhase) {
940
- if (isRuntimeExecutionFailure(value) && value.phase === expectedPhase) {
941
- return { ...projectRuntimeExecutionFailure(value), contractViolation: false };
942
- }
943
- return {
944
- reason: RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON[expectedPhase],
945
- retryable: false,
946
- contractViolation: true
947
- };
1387
+ async function processOwnsRecord(record) {
1388
+ if (!pidIsAlive(record.pid)) return false;
1389
+ if (record.pid === process.pid && record.processStartedAt !== SELF_PROCESS_STARTED_AT) return false;
1390
+ return portIsBound(record.livenessPort);
948
1391
  }
949
- var GIT_ERROR_CATEGORIES = [
950
- "git-unavailable",
951
- "git-timeout",
952
- "git-output-limit",
953
- "git-command-failed",
1392
+ async function portIsBound(port) {
1393
+ const probe = createServer();
1394
+ return new Promise((resolve, reject) => {
1395
+ const finish = (result) => {
1396
+ probe.removeAllListeners();
1397
+ resolve(result);
1398
+ };
1399
+ probe.once("error", (err) => {
1400
+ if (err.code === "EADDRINUSE") finish(true);
1401
+ else reject(err);
1402
+ });
1403
+ probe.listen({ host: "127.0.0.1", port, exclusive: true }, () => {
1404
+ probe.close((err) => {
1405
+ if (err) reject(err);
1406
+ else finish(false);
1407
+ });
1408
+ });
1409
+ });
1410
+ }
1411
+ async function createLivenessListener() {
1412
+ const server = createServer((socket) => endOwnershipProbe(socket));
1413
+ const port = await new Promise((resolve, reject) => {
1414
+ server.once("error", reject);
1415
+ server.listen({ host: "127.0.0.1", port: 0, exclusive: true }, () => {
1416
+ server.removeListener("error", reject);
1417
+ const address = server.address();
1418
+ if (!address || typeof address === "string") {
1419
+ reject(new Error("store mutation liveness listener did not expose a TCP port"));
1420
+ return;
1421
+ }
1422
+ resolve(address.port);
1423
+ });
1424
+ });
1425
+ server.unref();
1426
+ let closed = false;
1427
+ return {
1428
+ port,
1429
+ close: () => new Promise((resolve, reject) => {
1430
+ if (closed) {
1431
+ resolve();
1432
+ return;
1433
+ }
1434
+ closed = true;
1435
+ server.close((err) => err ? reject(err) : resolve());
1436
+ })
1437
+ };
1438
+ }
1439
+ async function probeStoreMutex(endpoint, identity) {
1440
+ return new Promise((resolve) => {
1441
+ const socket = createConnection(endpoint);
1442
+ let settled = false;
1443
+ let raw = "";
1444
+ const finish = (result) => {
1445
+ if (settled) return;
1446
+ settled = true;
1447
+ clearTimeout(timer);
1448
+ socket.removeAllListeners();
1449
+ socket.destroy();
1450
+ resolve(result);
1451
+ };
1452
+ const timer = setTimeout(() => finish({ kind: "occupied" }), STORE_MUTEX_PROBE_TIMEOUT_MS);
1453
+ socket.setEncoding("utf8");
1454
+ socket.on("data", (chunk) => {
1455
+ raw += chunk;
1456
+ if (raw.length > STORE_MUTEX_ID_PREFIX.length + 64 + 1) finish({ kind: "occupied" });
1457
+ });
1458
+ socket.once("end", () => finish(raw.trimEnd() === `${STORE_MUTEX_ID_PREFIX}${identity}` ? { kind: "holder" } : { kind: "occupied" }));
1459
+ socket.once(
1460
+ "error",
1461
+ (err) => finish(err.code === "ECONNREFUSED" || err.code === "ENOENT" ? { kind: "unbound" } : { kind: "occupied" })
1462
+ );
1463
+ });
1464
+ }
1465
+ async function clearStaleStoreMutexSocket(endpoint, identity) {
1466
+ let stat;
1467
+ try {
1468
+ stat = await promises.lstat(endpoint);
1469
+ } catch (err) {
1470
+ if (err.code === "ENOENT") return;
1471
+ throw err;
1472
+ }
1473
+ if (!stat.isSocket()) throw new Error("store mutation lock path exists but is not a socket");
1474
+ if ((await probeStoreMutex(endpoint, identity)).kind !== "unbound") throw new DaemonOwnerActiveError("unknown");
1475
+ await promises.rm(endpoint, { force: true });
1476
+ }
1477
+ async function assertOwnedPrivateDir(dir) {
1478
+ const uid = process.getuid?.();
1479
+ if (uid === void 0) return;
1480
+ const stat = await promises.lstat(dir);
1481
+ if (stat.isSymbolicLink() || stat.uid !== uid) {
1482
+ throw new Error(`refusing to bind the store mutation lock under "${dir}": not a real directory owned by this process's own uid`);
1483
+ }
1484
+ }
1485
+ async function acquireStoreMutex(canonicalStoreDir) {
1486
+ const identity = storeMutexIdentity(canonicalStoreDir);
1487
+ const endpoint = storeMutexEndpoint(canonicalStoreDir, identity);
1488
+ const isPipe = process.platform === "win32";
1489
+ if (!isPipe) {
1490
+ const endpointDir = path3__default.dirname(endpoint);
1491
+ if (endpointDir !== canonicalStoreDir) {
1492
+ await ensureSecureDir(endpointDir);
1493
+ await assertOwnedPrivateDir(endpointDir);
1494
+ }
1495
+ await clearStaleStoreMutexSocket(endpoint, identity);
1496
+ }
1497
+ const server = createServer((socket) => endOwnershipProbe(socket, `${STORE_MUTEX_ID_PREFIX}${identity}
1498
+ `));
1499
+ try {
1500
+ await new Promise((resolve, reject) => {
1501
+ server.once("error", reject);
1502
+ server.listen(endpoint, () => {
1503
+ server.removeListener("error", reject);
1504
+ resolve();
1505
+ });
1506
+ });
1507
+ } catch (err) {
1508
+ if (err.code === "EADDRINUSE") throw new DaemonOwnerActiveError("unknown");
1509
+ throw err;
1510
+ }
1511
+ if (!isPipe) await promises.chmod(endpoint, 384).catch(() => void 0);
1512
+ server.unref();
1513
+ let closed = false;
1514
+ return {
1515
+ endpoint,
1516
+ close: async () => {
1517
+ if (closed) return;
1518
+ closed = true;
1519
+ await new Promise((resolve, reject) => server.close((err) => err ? reject(err) : resolve()));
1520
+ if (!isPipe) await promises.rm(endpoint, { force: true }).catch(() => void 0);
1521
+ }
1522
+ };
1523
+ }
1524
+ async function reclaimExistsAndIsActive(reclaimPath) {
1525
+ let stat;
1526
+ try {
1527
+ stat = await promises.lstat(reclaimPath);
1528
+ } catch (err) {
1529
+ if (err.code === "ENOENT") return false;
1530
+ throw err;
1531
+ }
1532
+ if (!stat.isFile()) throw new Error("store mutation reclaim marker is not a regular file");
1533
+ const owner = await readOwner(reclaimPath);
1534
+ if (owner) return processOwnsRecord(owner);
1535
+ return Date.now() - stat.mtimeMs <= RECLAIM_MALFORMED_GRACE_MS;
1536
+ }
1537
+ async function createOwner(filePath, record) {
1538
+ const tempPath = `${filePath}.${record.pid}.${record.nonce}.tmp`;
1539
+ let handle;
1540
+ try {
1541
+ handle = await promises.open(tempPath, "wx", 384);
1542
+ await handle.writeFile(`${JSON.stringify(record)}
1543
+ `, "utf8");
1544
+ await handle.chmod(384);
1545
+ await handle.sync();
1546
+ await handle.close();
1547
+ try {
1548
+ await promises.link(tempPath, filePath);
1549
+ return true;
1550
+ } catch (err) {
1551
+ if (err.code === "EEXIST") return false;
1552
+ throw err;
1553
+ }
1554
+ } finally {
1555
+ await handle?.close().catch(() => void 0);
1556
+ await promises.rm(tempPath, { force: true }).catch(() => void 0);
1557
+ }
1558
+ }
1559
+ async function acquireDaemonOwner(storeDir, role, clock = () => /* @__PURE__ */ new Date()) {
1560
+ let pathGate;
1561
+ try {
1562
+ pathGate = await acquirePathMutationGate({ scope: "store", targetPath: storeDir }, { waitMs: 1e3 });
1563
+ } catch (error) {
1564
+ if (error instanceof PathMutationGateBusyError) throw new DaemonOwnerActiveError("unknown");
1565
+ throw error;
1566
+ }
1567
+ let mutex;
1568
+ let liveness;
1569
+ try {
1570
+ await ensureSecureDir(storeDir);
1571
+ const canonicalStoreDir = await promises.realpath(storeDir);
1572
+ mutex = await acquireStoreMutex(canonicalStoreDir);
1573
+ liveness = await createLivenessListener();
1574
+ const ownerPath = path3__default.join(canonicalStoreDir, DAEMON_OWNER_FILENAME);
1575
+ const reclaimPath = path3__default.join(canonicalStoreDir, RECLAIM_FILENAME);
1576
+ const record = {
1577
+ version: 2,
1578
+ pid: process.pid,
1579
+ nonce: randomUUID(),
1580
+ role,
1581
+ acquiredAt: clock().toISOString(),
1582
+ processStartedAt: SELF_PROCESS_STARTED_AT,
1583
+ livenessPort: liveness.port
1584
+ };
1585
+ for (; ; ) {
1586
+ if (await reclaimExistsAndIsActive(reclaimPath)) {
1587
+ throw new Error("store mutation lease is being reclaimed; retry after the current operation finishes");
1588
+ }
1589
+ await promises.rm(reclaimPath, { force: true });
1590
+ if (await createOwner(ownerPath, record)) {
1591
+ try {
1592
+ await pathGate.release();
1593
+ pathGate = void 0;
1594
+ } catch (error) {
1595
+ await promises.rm(ownerPath, { force: true }).catch(() => void 0);
1596
+ throw error;
1597
+ }
1598
+ const ownedLiveness = liveness;
1599
+ const ownedMutex = mutex;
1600
+ let released = false;
1601
+ return {
1602
+ release: async () => {
1603
+ if (released) return;
1604
+ const releaseGate = await acquirePathMutationGate({
1605
+ scope: "store",
1606
+ targetPath: canonicalStoreDir
1607
+ }, { waitMs: 2e3 });
1608
+ try {
1609
+ const current = await readOwner(ownerPath);
1610
+ if (current?.nonce !== record.nonce) {
1611
+ throw new Error("store mutation lease identity changed before release");
1612
+ }
1613
+ await ownedLiveness.close();
1614
+ await ownedMutex.close();
1615
+ await promises.rm(ownerPath);
1616
+ released = true;
1617
+ } finally {
1618
+ await releaseGate.release();
1619
+ }
1620
+ }
1621
+ };
1622
+ }
1623
+ const existing = await readOwner(ownerPath);
1624
+ if (existing && await processOwnsRecord(existing)) throw new DaemonOwnerActiveError(existing.role);
1625
+ if (!await createOwner(reclaimPath, record)) {
1626
+ throw new Error("store mutation lease is being reclaimed; retry after the current operation finishes");
1627
+ }
1628
+ try {
1629
+ const rechecked = await readOwner(ownerPath);
1630
+ if (rechecked && await processOwnsRecord(rechecked)) throw new DaemonOwnerActiveError(rechecked.role);
1631
+ if (rechecked || await promises.stat(ownerPath).then(() => true, (err) => {
1632
+ if (err.code === "ENOENT") return false;
1633
+ throw err;
1634
+ })) {
1635
+ await promises.rm(ownerPath);
1636
+ }
1637
+ } finally {
1638
+ const currentReclaim = await readOwner(reclaimPath);
1639
+ if (currentReclaim?.nonce === record.nonce) await promises.rm(reclaimPath, { force: true });
1640
+ }
1641
+ }
1642
+ } catch (err) {
1643
+ await liveness?.close().catch(() => void 0);
1644
+ await mutex?.close().catch(() => void 0);
1645
+ await pathGate?.release().catch(() => void 0);
1646
+ throw err;
1647
+ }
1648
+ }
1649
+
1650
+ // src/types.ts
1651
+ function frozenStrings(values) {
1652
+ return values === void 0 ? void 0 : Object.freeze([...values]);
1653
+ }
1654
+ function frozenPolicy(policy) {
1655
+ const allowTools = policy.allowTools === void 0 ? void 0 : Object.freeze([...policy.allowTools]);
1656
+ const denyTools = policy.denyTools === void 0 ? void 0 : Object.freeze([...policy.denyTools]);
1657
+ return Object.freeze({
1658
+ mode: policy.mode,
1659
+ ...allowTools === void 0 ? {} : { allowTools },
1660
+ ...denyTools === void 0 ? {} : { denyTools },
1661
+ ...policy.workspaceRoot === void 0 ? {} : { workspaceRoot: policy.workspaceRoot },
1662
+ ...policy.network === void 0 ? {} : { network: policy.network }
1663
+ });
1664
+ }
1665
+ function freezeRuntimeAdapterDescriptor(descriptor) {
1666
+ const baseNames = frozenStrings(descriptor.environmentRequirements.baseNames);
1667
+ const credentialNames = frozenStrings(descriptor.environmentRequirements.credentialNames);
1668
+ return Object.freeze({
1669
+ id: descriptor.id,
1670
+ supportsDispatchSelection: descriptor.supportsDispatchSelection === true,
1671
+ capabilities: Object.freeze({
1672
+ steer: descriptor.capabilities.steer === true,
1673
+ resume: descriptor.capabilities.resume === true,
1674
+ approvalInteractive: descriptor.capabilities.approvalInteractive === true,
1675
+ ...descriptor.capabilities.mcpToolsets === void 0 ? {} : { mcpToolsets: descriptor.capabilities.mcpToolsets === true },
1676
+ permissionModes: Object.freeze([...descriptor.capabilities.permissionModes])
1677
+ }),
1678
+ environmentRequirements: Object.freeze({
1679
+ ...baseNames === void 0 ? {} : { baseNames },
1680
+ ...credentialNames === void 0 ? {} : { credentialNames }
1681
+ })
1682
+ });
1683
+ }
1684
+ function sealRuntimeOperationManifest(manifest) {
1685
+ return Object.freeze({
1686
+ taskId: manifest.taskId,
1687
+ runtimeId: manifest.runtimeId,
1688
+ descriptor: freezeRuntimeAdapterDescriptor(manifest.descriptor),
1689
+ policy: frozenPolicy(manifest.policy),
1690
+ requiredToolsetIds: Object.freeze([...manifest.requiredToolsetIds]),
1691
+ ...manifest.dispatchSelection === void 0 ? {} : { dispatchSelection: Object.freeze({ ...manifest.dispatchSelection }) },
1692
+ ...manifest.sessionRef === void 0 ? {} : { sessionRef: manifest.sessionRef },
1693
+ ...manifest.agentRef === void 0 ? {} : { agentRef: Object.freeze({ agentId: manifest.agentRef.agentId, profileRevision: manifest.agentRef.profileRevision }) },
1694
+ cwd: manifest.cwd ?? manifest.workspace.workspaceDir,
1695
+ ...manifest.lease === void 0 ? {} : { lease: Object.freeze({ leaseId: manifest.lease.leaseId, canonicalHome: manifest.lease.canonicalHome }) },
1696
+ workspace: Object.freeze({ ...manifest.workspace }),
1697
+ forwardedEnvironmentNames: Object.freeze([...manifest.forwardedEnvironmentNames])
1698
+ });
1699
+ }
1700
+ var PolicyUnsupportedError = class extends Error {
1701
+ constructor(message) {
1702
+ super(message);
1703
+ this.name = "PolicyUnsupportedError";
1704
+ }
1705
+ };
1706
+ var SteerUnsupportedError = class extends Error {
1707
+ /** The `RuntimeAdapter.descriptor.id` that cannot steer (e.g. `claude`, `codex`). */
1708
+ runtimeId;
1709
+ constructor(runtimeId, message) {
1710
+ super(message);
1711
+ this.name = "SteerUnsupportedError";
1712
+ this.runtimeId = runtimeId;
1713
+ }
1714
+ };
1715
+
1716
+ // src/release-identity.ts
1717
+ var LOCAL_AGENT_RELEASE_VERSION_MAX_LENGTH = 128;
1718
+ var LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH = 128;
1719
+ var STRICT_SEMVER_PATTERN = new RegExp(
1720
+ "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$"
1721
+ );
1722
+ var BUILD_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
1723
+ function resolveLocalAgentReleaseIdentity(input) {
1724
+ if (input === void 0 || typeof input.version !== "string" || input.version.length > LOCAL_AGENT_RELEASE_VERSION_MAX_LENGTH || !STRICT_SEMVER_PATTERN.test(input.version)) {
1725
+ throw new Error("DaemonConfig.localAgentRelease.version must be canonical strict SemVer");
1726
+ }
1727
+ if (input.buildId !== void 0 && (typeof input.buildId !== "string" || input.buildId.length > LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH || !BUILD_ID_PATTERN.test(input.buildId))) {
1728
+ throw new Error(
1729
+ `DaemonConfig.localAgentRelease.buildId must be 1-${LOCAL_AGENT_RELEASE_BUILD_ID_MAX_LENGTH} safe opaque characters`
1730
+ );
1731
+ }
1732
+ return Object.freeze({
1733
+ version: input.version,
1734
+ ...input.buildId === void 0 ? {} : { buildId: input.buildId }
1735
+ });
1736
+ }
1737
+
1738
+ // src/runtime-failure.ts
1739
+ var RUNTIME_EXECUTION_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeExecutionFailure/v1");
1740
+ var RUNTIME_DISPOSAL_FAILURE_BRAND = /* @__PURE__ */ Symbol.for("@byok-sdk/client/RuntimeDisposalFailure/v1");
1741
+ var RuntimeDisposalFailure = class extends Error {
1742
+ stage;
1743
+ constructor(input, options) {
1744
+ if (!isRuntimeDisposalStage(input.stage) || typeof input.reason !== "string" || input.reason.length === 0) {
1745
+ throw new TypeError("invalid RuntimeDisposalFailure input");
1746
+ }
1747
+ super(input.reason, options);
1748
+ this.name = "RuntimeDisposalFailure";
1749
+ this.stage = input.stage;
1750
+ Object.defineProperty(this, RUNTIME_DISPOSAL_FAILURE_BRAND, { value: true });
1751
+ Object.freeze(this);
1752
+ }
1753
+ };
1754
+ function isRuntimeDisposalStage(value) {
1755
+ return value === "signal" || value === "quiescence" || value === "cleanup";
1756
+ }
1757
+ function isRuntimeDisposalFailure(value) {
1758
+ if (typeof value !== "object" || value === null) return false;
1759
+ const candidate = value;
1760
+ return candidate[RUNTIME_DISPOSAL_FAILURE_BRAND] === true && isRuntimeDisposalStage(candidate.stage) && typeof candidate.message === "string" && candidate.message.length > 0;
1761
+ }
1762
+ var RuntimeExecutionFailure = class extends Error {
1763
+ phase;
1764
+ category;
1765
+ retry;
1766
+ constructor(input, options) {
1767
+ if (!isRuntimeFailurePhase(input.phase) || !isRuntimeFailureCategory(input.category) || !isRuntimeRetryDisposition(input.retry) || typeof input.reason !== "string" || input.reason.length === 0) {
1768
+ throw new TypeError("invalid RuntimeExecutionFailure input");
1769
+ }
1770
+ super(input.reason, options);
1771
+ this.name = "RuntimeExecutionFailure";
1772
+ this.phase = input.phase;
1773
+ this.category = input.category;
1774
+ this.retry = input.retry;
1775
+ Object.defineProperty(this, RUNTIME_EXECUTION_FAILURE_BRAND, { value: true });
1776
+ Object.freeze(this);
1777
+ }
1778
+ };
1779
+ function isRuntimeFailurePhase(value) {
1780
+ return value === "start" || value === "run";
1781
+ }
1782
+ function isRuntimeFailureCategory(value) {
1783
+ return value === "semantic" || value === "infrastructure" || value === "authority";
1784
+ }
1785
+ function isRuntimeRetryDisposition(value) {
1786
+ return value === "retryable" || value === "non-retryable";
1787
+ }
1788
+ function isRuntimeExecutionFailure(value) {
1789
+ if (typeof value !== "object" || value === null) return false;
1790
+ const candidate = value;
1791
+ return candidate[RUNTIME_EXECUTION_FAILURE_BRAND] === true && isRuntimeFailurePhase(candidate.phase) && isRuntimeFailureCategory(candidate.category) && isRuntimeRetryDisposition(candidate.retry) && typeof candidate.message === "string" && candidate.message.length > 0;
1792
+ }
1793
+ function retryableFromDisposition(disposition) {
1794
+ switch (disposition) {
1795
+ case "retryable":
1796
+ return true;
1797
+ case "non-retryable":
1798
+ return false;
1799
+ }
1800
+ }
1801
+ function projectRuntimeExecutionFailure(failure) {
1802
+ return {
1803
+ reason: failure.message,
1804
+ retryable: retryableFromDisposition(failure.retry)
1805
+ };
1806
+ }
1807
+ var RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON = Object.freeze({
1808
+ start: "runtime adapter contract violation during start",
1809
+ run: "runtime adapter contract violation during run"
1810
+ });
1811
+ function projectRuntimeBoundaryFailure(value, expectedPhase) {
1812
+ if (isRuntimeExecutionFailure(value) && value.phase === expectedPhase) {
1813
+ return { ...projectRuntimeExecutionFailure(value), contractViolation: false };
1814
+ }
1815
+ return {
1816
+ reason: RUNTIME_ADAPTER_CONTRACT_VIOLATION_REASON[expectedPhase],
1817
+ retryable: false,
1818
+ contractViolation: true
1819
+ };
1820
+ }
1821
+ var GIT_ERROR_CATEGORIES = [
1822
+ "git-unavailable",
1823
+ "git-timeout",
1824
+ "git-output-limit",
1825
+ "git-command-failed",
954
1826
  "workspace-root-invalid",
955
1827
  "workspace-root-conflict",
956
1828
  "workspace-not-owned",
@@ -1011,7 +1883,7 @@ function gitEnvironment(readOnly) {
1011
1883
  return env;
1012
1884
  }
1013
1885
  function stableGitWorkspaceOwnerId(storeDir, productId) {
1014
- const identity = `${path.resolve(storeDir)}\\0${productId}`;
1886
+ const identity = `${path3__default.resolve(storeDir)}\\0${productId}`;
1015
1887
  return `store-product:${createHash("sha256").update(identity).digest("hex")}`;
1016
1888
  }
1017
1889
  var GUIDANCE = [
@@ -1023,11 +1895,11 @@ var GUIDANCE = [
1023
1895
  "Leave incomplete work visible for recovery."
1024
1896
  ].join("\n");
1025
1897
  function canonical(value) {
1026
- return path.resolve(value);
1898
+ return path3__default.resolve(value);
1027
1899
  }
1028
1900
  function isContained(root, candidate) {
1029
- const relative = path.relative(root, candidate);
1030
- return relative === "" || !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
1901
+ const relative = path3__default.relative(root, candidate);
1902
+ return relative === "" || !relative.startsWith(`..${path3__default.sep}`) && !path3__default.isAbsolute(relative);
1031
1903
  }
1032
1904
  function bounded(value, max) {
1033
1905
  return Buffer.byteLength(value, "utf8") <= max ? value : value.slice(0, max);
@@ -1132,7 +2004,7 @@ var GitWorkspaceManager = class {
1132
2004
  await this.ensureOwnerMarker();
1133
2005
  }
1134
2006
  async ensureOwnerMarker() {
1135
- const markerPath = path.join(this.workspaceRoot, OWNER_MARKER);
2007
+ const markerPath = path3__default.join(this.workspaceRoot, OWNER_MARKER);
1136
2008
  let existing;
1137
2009
  try {
1138
2010
  existing = JSON.parse(await promises.readFile(markerPath, "utf8"));
@@ -1226,270 +2098,96 @@ var GitWorkspaceManager = class {
1226
2098
  }
1227
2099
  };
1228
2100
  workspaceLeases.set(root, lease);
1229
- if (sessionRef) sessionLeases.set(sessionRef, lease);
1230
- return lease;
1231
- }
1232
- static guidance() {
1233
- return GUIDANCE;
1234
- }
1235
- static prependGuidance(instruction) {
1236
- return `${GUIDANCE}
1237
-
1238
- ${instruction}`;
1239
- }
1240
- commandOptions(cwd, readOnly = false) {
1241
- return {
1242
- cwd,
1243
- timeout: this.timeoutMs,
1244
- maxBuffer: this.maxOutputBytes,
1245
- env: gitEnvironment(readOnly)
1246
- };
1247
- }
1248
- async read(args, cwd) {
1249
- try {
1250
- return await this.run(args, this.commandOptions(cwd, true));
1251
- } catch (error) {
1252
- throw asGitError(error, "git-command-failed", "Git observation failed");
1253
- }
1254
- }
1255
- async readTopLevel(root) {
1256
- const result = await this.read(["rev-parse", "--show-toplevel"], root);
1257
- if (result.code !== 0) throw new GitWorkspaceError("repository-invalid", "workspace is not a Git repository");
1258
- return canonical(result.stdout.trim());
1259
- }
1260
- async assertTaskRoot(workspaceDir) {
1261
- const candidate = canonical(workspaceDir);
1262
- const realWorkspaceRoot = await promises.realpath(this.workspaceRoot).catch(() => {
1263
- throw new GitWorkspaceError("workspace-root-invalid", "workspace root is unavailable");
1264
- });
1265
- if (!isContained(this.workspaceRoot, candidate) && !isContained(realWorkspaceRoot, candidate)) {
1266
- throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
1267
- }
1268
- await this.assertExistingAncestry(candidate, realWorkspaceRoot);
1269
- await promises.mkdir(candidate, { recursive: true, mode: 448 }).catch(() => {
1270
- throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is unavailable");
1271
- });
1272
- const realCandidate = await promises.realpath(candidate).catch(() => {
1273
- throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is unavailable");
1274
- });
1275
- if (!isContained(realWorkspaceRoot, realCandidate)) {
1276
- throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
1277
- }
1278
- return realCandidate;
1279
- }
1280
- async assertExistingAncestry(candidate, realWorkspaceRoot) {
1281
- let current = candidate;
1282
- while (true) {
1283
- try {
1284
- const realCurrent = await promises.realpath(current);
1285
- if (!isContained(realWorkspaceRoot, realCurrent)) {
1286
- throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
1287
- }
1288
- return;
1289
- } catch (error) {
1290
- const code = error.code;
1291
- if (error instanceof GitWorkspaceError || code !== "ENOENT" && code !== "ENOTDIR") {
1292
- throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
1293
- }
1294
- const parent = path.dirname(current);
1295
- if (parent === current) {
1296
- throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
1297
- }
1298
- current = parent;
1299
- }
1300
- }
1301
- }
1302
- async assertExistingTaskRoot(workspaceDir) {
1303
- const candidate = canonical(workspaceDir);
1304
- const realWorkspaceRoot = await promises.realpath(this.workspaceRoot).catch(() => {
1305
- throw new GitWorkspaceError("workspace-root-invalid", "workspace root is unavailable");
1306
- });
1307
- const realCandidate = await promises.realpath(candidate).catch(() => {
1308
- throw new GitWorkspaceError("repository-invalid", "workspace directory is unavailable");
1309
- });
1310
- if (!isContained(realWorkspaceRoot, realCandidate)) {
1311
- throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
1312
- }
1313
- return realCandidate;
1314
- }
1315
- };
1316
- function prependGitWorkspaceGuidance(instruction) {
1317
- return GitWorkspaceManager.prependGuidance(instruction);
1318
- }
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
- var defaultRunner = (command, args) => new Promise((resolve, reject) => {
1388
- execFile(command, args, (error, stdout, stderr) => {
1389
- if (error && typeof error.code !== "number") {
1390
- reject(error);
1391
- return;
1392
- }
1393
- resolve({ code: error ? error.code : 0, stdout, stderr });
1394
- });
1395
- });
1396
- async function runOrThrow(run, command, args, label) {
1397
- const result = await run(command, args);
1398
- if (result.code !== 0) {
1399
- const detail = (result.stderr || result.stdout).trim();
1400
- throw new Error(`${label} failed (exit ${result.code})${detail ? `: ${detail}` : ""}`);
1401
- }
1402
- return result;
1403
- }
1404
- function isIdempotentAbsence(result, absence) {
1405
- if (result.code === 0) return true;
1406
- const detail = `${result.stdout}
1407
- ${result.stderr}`;
1408
- if (absence.neverAbsence?.some((pattern) => pattern.test(detail))) return false;
1409
- if (absence.codes?.includes(result.code)) return true;
1410
- return absence.patterns.some((pattern) => pattern.test(detail));
1411
- }
1412
- async function runIdempotent(run, command, args, label, absence) {
1413
- const result = await run(command, args);
1414
- if (!isIdempotentAbsence(result, absence)) {
1415
- const detail = (result.stderr || result.stdout).trim();
1416
- throw new Error(`${label} failed (exit ${result.code})${detail ? `: ${detail}` : ""}`);
2101
+ if (sessionRef) sessionLeases.set(sessionRef, lease);
2102
+ return lease;
1417
2103
  }
1418
- return result;
1419
- }
2104
+ static guidance() {
2105
+ return GUIDANCE;
2106
+ }
2107
+ static prependGuidance(instruction) {
2108
+ return `${GUIDANCE}
1420
2109
 
1421
- // src/util/secure-dir.ts
1422
- var SYSTEM_SID = "*S-1-5-18";
1423
- var ADMINISTRATORS_SID = "*S-1-5-32-544";
1424
- function buildIcaclsArgs(dir, username) {
1425
- return [dir, "/inheritance:r", "/grant:r", `${username}:(OI)(CI)F`, "/grant", `${SYSTEM_SID}:(OI)(CI)F`, "/grant", `${ADMINISTRATORS_SID}:(OI)(CI)F`];
1426
- }
1427
- function buildIcaclsFileArgs(filePath, username) {
1428
- return [
1429
- filePath,
1430
- "/inheritance:r",
1431
- "/grant:r",
1432
- `${username}:F`,
1433
- "/grant",
1434
- `${SYSTEM_SID}:F`,
1435
- "/grant",
1436
- `${ADMINISTRATORS_SID}:F`
1437
- ];
1438
- }
1439
- var SecureDirHardeningError = class extends Error {
1440
- constructor(dir, reason) {
1441
- super(
1442
- `failed to apply a restrictive Windows ACL to "${dir}": ${reason} \u2014 refusing to leave this directory unprotected (it holds device credentials and/or the control-socket token, otherwise readable by any other local user); see docs/security.md`
1443
- );
1444
- this.dir = dir;
1445
- this.name = "SecureDirHardeningError";
2110
+ ${instruction}`;
1446
2111
  }
1447
- dir;
1448
- };
1449
- var SecureFileHardeningError = class extends Error {
1450
- constructor(filePath, reason) {
1451
- super(`failed to apply a restrictive Windows ACL to "${filePath}": ${reason}`);
1452
- this.filePath = filePath;
1453
- this.name = "SecureFileHardeningError";
2112
+ commandOptions(cwd, readOnly = false) {
2113
+ return {
2114
+ cwd,
2115
+ timeout: this.timeoutMs,
2116
+ maxBuffer: this.maxOutputBytes,
2117
+ env: gitEnvironment(readOnly)
2118
+ };
1454
2119
  }
1455
- filePath;
1456
- };
1457
- async function ensureSecureDir(dir, opts = {}) {
1458
- const platform = opts.platform ?? process.platform;
1459
- const run = opts.run ?? defaultRunner;
1460
- await promises.mkdir(dir, { recursive: true, mode: 448 });
1461
- await promises.chmod(dir, 448).catch(() => {
1462
- });
1463
- if (platform !== "win32") return;
1464
- const { username } = os.userInfo();
1465
- let result;
1466
- try {
1467
- result = await run("icacls", buildIcaclsArgs(dir, username));
1468
- } catch (err) {
1469
- throw new SecureDirHardeningError(dir, `could not run icacls: ${err instanceof Error ? err.message : String(err)}`);
2120
+ async read(args, cwd) {
2121
+ try {
2122
+ return await this.run(args, this.commandOptions(cwd, true));
2123
+ } catch (error) {
2124
+ throw asGitError(error, "git-command-failed", "Git observation failed");
2125
+ }
1470
2126
  }
1471
- if (result.code !== 0) {
1472
- throw new SecureDirHardeningError(dir, `icacls exited ${result.code}: ${(result.stderr || result.stdout).trim()}`);
2127
+ async readTopLevel(root) {
2128
+ const result = await this.read(["rev-parse", "--show-toplevel"], root);
2129
+ if (result.code !== 0) throw new GitWorkspaceError("repository-invalid", "workspace is not a Git repository");
2130
+ return canonical(result.stdout.trim());
1473
2131
  }
1474
- }
1475
- async function ensureSecureFile(filePath, opts = {}) {
1476
- const platform = opts.platform ?? process.platform;
1477
- const run = opts.run ?? defaultRunner;
1478
- await promises.chmod(filePath, 384);
1479
- if (platform !== "win32") return;
1480
- const { username } = os.userInfo();
1481
- let result;
1482
- try {
1483
- result = await run("icacls", buildIcaclsFileArgs(filePath, username));
1484
- } catch (err) {
1485
- throw new SecureFileHardeningError(filePath, `could not run icacls: ${err instanceof Error ? err.message : String(err)}`);
2132
+ async assertTaskRoot(workspaceDir) {
2133
+ const candidate = canonical(workspaceDir);
2134
+ const realWorkspaceRoot = await promises.realpath(this.workspaceRoot).catch(() => {
2135
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace root is unavailable");
2136
+ });
2137
+ if (!isContained(this.workspaceRoot, candidate) && !isContained(realWorkspaceRoot, candidate)) {
2138
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
2139
+ }
2140
+ await this.assertExistingAncestry(candidate, realWorkspaceRoot);
2141
+ await promises.mkdir(candidate, { recursive: true, mode: 448 }).catch(() => {
2142
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is unavailable");
2143
+ });
2144
+ const realCandidate = await promises.realpath(candidate).catch(() => {
2145
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is unavailable");
2146
+ });
2147
+ if (!isContained(realWorkspaceRoot, realCandidate)) {
2148
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
2149
+ }
2150
+ return realCandidate;
1486
2151
  }
1487
- if (result.code !== 0) {
1488
- throw new SecureFileHardeningError(filePath, `icacls exited ${result.code}: ${(result.stderr || result.stdout).trim()}`);
2152
+ async assertExistingAncestry(candidate, realWorkspaceRoot) {
2153
+ let current = candidate;
2154
+ while (true) {
2155
+ try {
2156
+ const realCurrent = await promises.realpath(current);
2157
+ if (!isContained(realWorkspaceRoot, realCurrent)) {
2158
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
2159
+ }
2160
+ return;
2161
+ } catch (error) {
2162
+ const code = error.code;
2163
+ if (error instanceof GitWorkspaceError || code !== "ENOENT" && code !== "ENOTDIR") {
2164
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
2165
+ }
2166
+ const parent = path3__default.dirname(current);
2167
+ if (parent === current) {
2168
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
2169
+ }
2170
+ current = parent;
2171
+ }
2172
+ }
2173
+ }
2174
+ async assertExistingTaskRoot(workspaceDir) {
2175
+ const candidate = canonical(workspaceDir);
2176
+ const realWorkspaceRoot = await promises.realpath(this.workspaceRoot).catch(() => {
2177
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace root is unavailable");
2178
+ });
2179
+ const realCandidate = await promises.realpath(candidate).catch(() => {
2180
+ throw new GitWorkspaceError("repository-invalid", "workspace directory is unavailable");
2181
+ });
2182
+ if (!isContained(realWorkspaceRoot, realCandidate)) {
2183
+ throw new GitWorkspaceError("workspace-root-invalid", "workspace directory is invalid");
2184
+ }
2185
+ return realCandidate;
1489
2186
  }
2187
+ };
2188
+ function prependGitWorkspaceGuidance(instruction) {
2189
+ return GitWorkspaceManager.prependGuidance(instruction);
1490
2190
  }
1491
-
1492
- // src/daemon/git-workspace-store.ts
1493
2191
  var FILE_NAME = "git-workspaces.json";
1494
2192
  var MAX_RECORDS = 500;
1495
2193
  var PHASES = /* @__PURE__ */ new Set(["preparing", "active", "completed", "failed", "cancelled", "interrupted", "salvage"]);
@@ -1510,7 +2208,7 @@ function isProtected(record) {
1510
2208
  var GitWorkspaceStore = class {
1511
2209
  constructor(storeDir, options = {}) {
1512
2210
  this.storeDir = storeDir;
1513
- this.filePath = path.join(storeDir, FILE_NAME);
2211
+ this.filePath = path3__default.join(storeDir, FILE_NAME);
1514
2212
  this.maxRecords = Math.max(1, Math.floor(options.maxRecords ?? MAX_RECORDS));
1515
2213
  }
1516
2214
  storeDir;
@@ -1645,7 +2343,7 @@ var GitWorkspaceStore = class {
1645
2343
  var BYOK_PI_MCP_CONFIG_PATH = "BYOK_PI_MCP_CONFIG_PATH";
1646
2344
  var PI_PACKAGE_NAME = "@earendil-works/pi-coding-agent";
1647
2345
  function readPackageJson(dir) {
1648
- const candidate = path.join(dir, "package.json");
2346
+ const candidate = path3__default.join(dir, "package.json");
1649
2347
  if (!existsSync(candidate)) return void 0;
1650
2348
  try {
1651
2349
  return JSON.parse(readFileSync(candidate, "utf8"));
@@ -1660,17 +2358,17 @@ function resolvePiBin() {
1660
2358
  }
1661
2359
  try {
1662
2360
  const mainEntryUrl = import.meta.resolve(PI_PACKAGE_NAME);
1663
- let dir = path.dirname(fileURLToPath(mainEntryUrl));
2361
+ let dir = path3__default.dirname(fileURLToPath(mainEntryUrl));
1664
2362
  for (let depth = 0; depth < 6; depth++) {
1665
2363
  const pkg = readPackageJson(dir);
1666
2364
  if (pkg?.name === PI_PACKAGE_NAME) {
1667
2365
  const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.pi;
1668
2366
  if (binRel) {
1669
- return { command: path.join(dir, binRel), source: "package" };
2367
+ return { command: path3__default.join(dir, binRel), source: "package" };
1670
2368
  }
1671
2369
  break;
1672
2370
  }
1673
- const parent = path.dirname(dir);
2371
+ const parent = path3__default.dirname(dir);
1674
2372
  if (parent === dir) break;
1675
2373
  dir = parent;
1676
2374
  }
@@ -1688,7 +2386,7 @@ function resolvePiExtensions() {
1688
2386
  const clientManifest = fileURLToPath(import.meta.resolve("@byok-sdk/client/package.json"));
1689
2387
  return {
1690
2388
  webAccess: fileURLToPath(import.meta.resolve("pi-web-access/index.ts")),
1691
- mcpAdapter: path.join(path.dirname(clientManifest), "dist", "adapters", "pi", "mcp-extension.js")
2389
+ mcpAdapter: path3__default.join(path3__default.dirname(clientManifest), "dist", "adapters", "pi", "mcp-extension.js")
1692
2390
  };
1693
2391
  }
1694
2392
 
@@ -2614,10 +3312,10 @@ var PiAdapter = class {
2614
3312
  const taskMcpServers = startInput.mcpServers ?? {};
2615
3313
  const hasMcpServers = Object.keys(taskMcpServers).length > 0;
2616
3314
  if (hasMcpServers) {
2617
- mcpConfigDir = await promises.mkdtemp(path.join(os.tmpdir(), "byok-pi-mcp-"));
3315
+ mcpConfigDir = await promises.mkdtemp(path3__default.join(os__default.tmpdir(), "byok-pi-mcp-"));
2618
3316
  await promises.chmod(mcpConfigDir, 448).catch(() => {
2619
3317
  });
2620
- const mcpConfigPath = path.join(mcpConfigDir, "mcp-config.json");
3318
+ const mcpConfigPath = path3__default.join(mcpConfigDir, "mcp-config.json");
2621
3319
  await promises.writeFile(mcpConfigPath, JSON.stringify({ mcpServers: taskMcpServers }), { mode: 384 });
2622
3320
  runtimeEnv = { ...runtimeEnv, [BYOK_PI_MCP_CONFIG_PATH]: mcpConfigPath };
2623
3321
  }
@@ -2848,7 +3546,7 @@ function resolveApprovalMcpBin() {
2848
3546
  if (override) {
2849
3547
  return { command: override, args: [], source: "env" };
2850
3548
  }
2851
- const distBin = path.join(path.dirname(fileURLToPath(import.meta.url)), "bin", "byok-approval-mcp.js");
3549
+ const distBin = path3__default.join(path3__default.dirname(fileURLToPath(import.meta.url)), "bin", "byok-approval-mcp.js");
2852
3550
  return { command: process.execPath, args: [distBin], source: "dist" };
2853
3551
  }
2854
3552
 
@@ -2939,7 +3637,7 @@ var EXTENSION_CONTENT_TYPES = {
2939
3637
  ".yml": "application/yaml"
2940
3638
  };
2941
3639
  function guessContentType(filePath) {
2942
- const ext = path.extname(filePath).toLowerCase();
3640
+ const ext = path3__default.extname(filePath).toLowerCase();
2943
3641
  return EXTENSION_CONTENT_TYPES[ext] ?? "application/octet-stream";
2944
3642
  }
2945
3643
  function mapAssistant(msg, correlation) {
@@ -3023,11 +3721,11 @@ function tryBuildArtifactEvent(msg, workspaceDir) {
3023
3721
  const filePath = toolUseResult && typeof toolUseResult.filePath === "string" ? toolUseResult.filePath : void 0;
3024
3722
  if (!filePath) return void 0;
3025
3723
  const realWorkspaceDir = tryRealpath(workspaceDir) ?? workspaceDir;
3026
- const fileDir = path.dirname(filePath);
3724
+ const fileDir = path3__default.dirname(filePath);
3027
3725
  const realFileDir = tryRealpath(fileDir) ?? fileDir;
3028
- const realFilePath = path.join(realFileDir, path.basename(filePath));
3029
- const relative = path.relative(realWorkspaceDir, realFilePath);
3030
- if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) {
3726
+ const realFilePath = path3__default.join(realFileDir, path3__default.basename(filePath));
3727
+ const relative = path3__default.relative(realWorkspaceDir, realFilePath);
3728
+ if (relative === "" || relative.startsWith("..") || path3__default.isAbsolute(relative)) {
3031
3729
  return void 0;
3032
3730
  }
3033
3731
  return { type: "artifact", name: relative, contentType: guessContentType(filePath) };
@@ -3421,10 +4119,10 @@ var ClaudeAdapter = class {
3421
4119
  });
3422
4120
  }
3423
4121
  if (needsMcpConfig) {
3424
- mcpConfigDir = await promises.mkdtemp(path.join(os.tmpdir(), "byok-mcp-"));
4122
+ mcpConfigDir = await promises.mkdtemp(path3__default.join(os__default.tmpdir(), "byok-mcp-"));
3425
4123
  await promises.chmod(mcpConfigDir, 448).catch(() => {
3426
4124
  });
3427
- const mcpConfigPath = path.join(mcpConfigDir, "mcp-config.json");
4125
+ const mcpConfigPath = path3__default.join(mcpConfigDir, "mcp-config.json");
3428
4126
  const mcpServers = { ...taskMcpServers };
3429
4127
  if (mapping.needsApprovalMcp) {
3430
4128
  const approvalChannel = startInput.approvalChannel;
@@ -3910,8 +4608,8 @@ function extractArtifactEvents(changes, workspaceDir) {
3910
4608
  const absolutePath = typeof change.path === "string" ? change.path : void 0;
3911
4609
  const kind = typeof change.kind === "string" ? change.kind : void 0;
3912
4610
  if (!absolutePath || kind === "delete") continue;
3913
- const relative = path.relative(workspaceDir, absolutePath);
3914
- if (relative.length === 0 || relative.startsWith("..") || path.isAbsolute(relative)) continue;
4611
+ const relative = path3__default.relative(workspaceDir, absolutePath);
4612
+ if (relative.length === 0 || relative.startsWith("..") || path3__default.isAbsolute(relative)) continue;
3915
4613
  events.push({ type: "artifact", name: relative, contentType: guessContentType2(relative) });
3916
4614
  }
3917
4615
  return events;
@@ -3932,7 +4630,7 @@ var CONTENT_TYPE_BY_EXTENSION = {
3932
4630
  ".csv": "text/csv"
3933
4631
  };
3934
4632
  function guessContentType2(relativePath) {
3935
- return CONTENT_TYPE_BY_EXTENSION[path.extname(relativePath).toLowerCase()] ?? "application/octet-stream";
4633
+ return CONTENT_TYPE_BY_EXTENSION[path3__default.extname(relativePath).toLowerCase()] ?? "application/octet-stream";
3936
4634
  }
3937
4635
  function extractErrorMessage(rawError) {
3938
4636
  if (typeof rawError === "string") return rawError;
@@ -4731,6 +5429,301 @@ var ApprovalRegistry = class {
4731
5429
  entry.onResolve(decision, reason, origin);
4732
5430
  }
4733
5431
  };
5432
+ var ENTRY_ACCOUNT = "device-enrollment";
5433
+ var ENCODED_PREFIX = "byok-device-credential-v1:";
5434
+ var NOT_FOUND = 44;
5435
+ var WINDOWS_BRIDGE_DIRECTORY_PREFIX = "byok-device-credential-";
5436
+ var WINDOWS_BRIDGE_STALE_MS = 24 * 60 * 60 * 1e3;
5437
+ function providerDiagnostic(stderr) {
5438
+ const match = /credential operation failed \((win32=\d{1,10}|hresult=-?\d{1,11}|stage=\d{1,2},kind=\d{1,2},hresult=-?\d{1,11})\)/u.exec(stderr);
5439
+ return match === null ? "" : ` (${match[1]})`;
5440
+ }
5441
+ function isNodeError(error, code) {
5442
+ return error instanceof Error && "code" in error && error.code === code;
5443
+ }
5444
+ async function scavengeStaleWindowsBridges() {
5445
+ const temporaryRoot = os.tmpdir();
5446
+ let entries;
5447
+ try {
5448
+ entries = await fs12.readdir(temporaryRoot, { withFileTypes: true });
5449
+ } catch {
5450
+ throw new DeviceCredentialStoreError("temporary Windows credential bridge root is unavailable");
5451
+ }
5452
+ const staleBefore = Date.now() - WINDOWS_BRIDGE_STALE_MS;
5453
+ for (const entry of entries) {
5454
+ if (!entry.isDirectory() || !entry.name.startsWith(WINDOWS_BRIDGE_DIRECTORY_PREFIX)) continue;
5455
+ const candidate = path3.join(temporaryRoot, entry.name);
5456
+ try {
5457
+ const info = await fs12.lstat(candidate);
5458
+ if (!info.isDirectory() || info.isSymbolicLink() || info.mtimeMs >= staleBefore) continue;
5459
+ await fs12.rm(candidate, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
5460
+ } catch (error) {
5461
+ if (isNodeError(error, "ENOENT")) continue;
5462
+ throw new DeviceCredentialStoreError("stale Windows credential bridge cleanup failed");
5463
+ }
5464
+ }
5465
+ }
5466
+ function windowsPowerShellExecutable() {
5467
+ const systemRoot = process.env.SystemRoot;
5468
+ if (systemRoot === void 0 || !path3.win32.isAbsolute(systemRoot)) {
5469
+ throw new DeviceCredentialStoreError("Windows system root is unavailable");
5470
+ }
5471
+ return path3.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
5472
+ }
5473
+ var DeviceCredentialStoreUnavailableError = class extends Error {
5474
+ constructor(message = "no supported operating-system credential provider is available") {
5475
+ super(message);
5476
+ this.name = "DeviceCredentialStoreUnavailableError";
5477
+ }
5478
+ };
5479
+ var DeviceCredentialStoreError = class extends Error {
5480
+ constructor(message) {
5481
+ super(message);
5482
+ this.name = "DeviceCredentialStoreError";
5483
+ }
5484
+ };
5485
+ function assertRecord(value) {
5486
+ if (typeof value !== "object" || value === null || Array.isArray(value) || typeof value.deviceId !== "string" || !isTenantId(value.tenantId) || typeof value.devicePublicKey !== "string" || typeof value.accessToken !== "string" || typeof value.expiresAt !== "string" || typeof value.devicePrivateKeyPem !== "string") {
5487
+ throw new DeviceCredentialStoreError("OS credential entry has an invalid device credential shape");
5488
+ }
5489
+ const credential = value;
5490
+ if (credential.deviceId.length === 0 || credential.devicePublicKey.length === 0 || credential.accessToken.length === 0 || credential.expiresAt.length === 0 || credential.devicePrivateKeyPem.length === 0) {
5491
+ throw new DeviceCredentialStoreError("OS credential entry has an incomplete device credential");
5492
+ }
5493
+ }
5494
+ function encode(record) {
5495
+ assertRecord(record);
5496
+ const encoded = Buffer.from(JSON.stringify(record), "utf8").toString("base64");
5497
+ const value = `${ENCODED_PREFIX}${encoded}`;
5498
+ if (Buffer.byteLength(value, "utf8") > 2400) {
5499
+ throw new DeviceCredentialStoreError("device credential exceeds the OS credential entry bound");
5500
+ }
5501
+ return value;
5502
+ }
5503
+ function decode(value) {
5504
+ if (!value.startsWith(ENCODED_PREFIX)) {
5505
+ throw new DeviceCredentialStoreError("OS credential entry is not owned by this client credential store");
5506
+ }
5507
+ const encoded = value.slice(ENCODED_PREFIX.length);
5508
+ if (encoded.length === 0 || encoded.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/u.test(encoded)) {
5509
+ throw new DeviceCredentialStoreError("OS credential entry is not strict base64");
5510
+ }
5511
+ const bytes = Buffer.from(encoded, "base64");
5512
+ if (bytes.toString("base64") !== encoded) {
5513
+ throw new DeviceCredentialStoreError("OS credential entry is not canonical base64");
5514
+ }
5515
+ const raw = bytes.toString("utf8");
5516
+ if (!Buffer.from(raw, "utf8").equals(bytes)) {
5517
+ throw new DeviceCredentialStoreError("OS credential entry is not valid UTF-8");
5518
+ }
5519
+ let parsed;
5520
+ try {
5521
+ parsed = JSON.parse(raw);
5522
+ } catch {
5523
+ throw new DeviceCredentialStoreError("OS credential entry is not valid JSON");
5524
+ }
5525
+ assertRecord(parsed);
5526
+ return Object.freeze({
5527
+ deviceId: parsed.deviceId,
5528
+ tenantId: parsed.tenantId,
5529
+ devicePublicKey: parsed.devicePublicKey,
5530
+ accessToken: parsed.accessToken,
5531
+ expiresAt: parsed.expiresAt,
5532
+ devicePrivateKeyPem: parsed.devicePrivateKeyPem
5533
+ });
5534
+ }
5535
+ function serviceFor(productId) {
5536
+ if (typeof productId !== "string" || productId.length === 0 || /[\u0000\r\n]/u.test(productId)) {
5537
+ throw new DeviceCredentialStoreError("productId must be a non-empty single-line string");
5538
+ }
5539
+ return `com.byok.client.device.${createHash("sha256").update(productId, "utf8").digest("hex")}`;
5540
+ }
5541
+ function quoteInteractive(value) {
5542
+ if (/[\u0000\r\n]/u.test(value)) throw new DeviceCredentialStoreError("credential command argument is invalid");
5543
+ return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
5544
+ }
5545
+ var DeviceCredentialStore = class {
5546
+ #service;
5547
+ #platform;
5548
+ #run;
5549
+ constructor(options) {
5550
+ this.#service = serviceFor(options.productId);
5551
+ this.#platform = options.platform ?? process.platform;
5552
+ this.#run = options.commandRunner ?? runDeviceCommand;
5553
+ }
5554
+ async read() {
5555
+ const result = await this.#invoke("read");
5556
+ if (result.exitCode === NOT_FOUND || this.#platform === "linux" && result.exitCode === 1 && result.stderr.trim().length === 0) return void 0;
5557
+ if (result.exitCode === 127) throw new DeviceCredentialStoreUnavailableError();
5558
+ if (result.exitCode !== 0) {
5559
+ throw new DeviceCredentialStoreError(
5560
+ `operating-system credential provider could not read device credentials${providerDiagnostic(result.stderr)}`
5561
+ );
5562
+ }
5563
+ return decode(result.stdout.trimEnd());
5564
+ }
5565
+ async replace(record) {
5566
+ const encoded = encode(record);
5567
+ const result = await this.#invoke("replace", encoded);
5568
+ if (result.exitCode === 127) throw new DeviceCredentialStoreUnavailableError();
5569
+ if (result.exitCode !== 0) {
5570
+ throw new DeviceCredentialStoreError(
5571
+ `operating-system credential provider could not replace device credentials${providerDiagnostic(result.stderr)}`
5572
+ );
5573
+ }
5574
+ }
5575
+ /** Returns true only after the sole secret authority is confirmed absent. */
5576
+ async clear() {
5577
+ const before = await this.read();
5578
+ if (before === void 0) return false;
5579
+ const result = await this.#invoke("clear");
5580
+ if (result.exitCode === 127) throw new DeviceCredentialStoreUnavailableError();
5581
+ if (result.exitCode !== 0 && result.exitCode !== NOT_FOUND) {
5582
+ throw new DeviceCredentialStoreError(
5583
+ `operating-system credential provider could not clear device credentials${providerDiagnostic(result.stderr)}`
5584
+ );
5585
+ }
5586
+ if (await this.read() !== void 0) {
5587
+ throw new DeviceCredentialStoreError("operating-system credential provider reported deletion but device credentials remain");
5588
+ }
5589
+ return true;
5590
+ }
5591
+ async #invoke(operation, encoded) {
5592
+ switch (this.#platform) {
5593
+ case "darwin":
5594
+ return this.#macos(operation, encoded);
5595
+ case "win32":
5596
+ return this.#windows(operation, encoded);
5597
+ case "linux":
5598
+ return this.#linux(operation, encoded);
5599
+ default:
5600
+ throw new DeviceCredentialStoreUnavailableError(`no operating-system credential provider is supported on ${this.#platform}`);
5601
+ }
5602
+ }
5603
+ #macos(operation, encoded) {
5604
+ if (operation === "read") return this.#run("/usr/bin/security", ["find-generic-password", "-a", ENTRY_ACCOUNT, "-s", this.#service, "-w"]);
5605
+ if (operation === "clear") return this.#run("/usr/bin/security", ["delete-generic-password", "-a", ENTRY_ACCOUNT, "-s", this.#service]);
5606
+ const command = [
5607
+ "add-generic-password",
5608
+ "-U",
5609
+ "-a",
5610
+ quoteInteractive(ENTRY_ACCOUNT),
5611
+ "-s",
5612
+ quoteInteractive(this.#service),
5613
+ "-w",
5614
+ quoteInteractive(encoded)
5615
+ ].join(" ");
5616
+ return this.#run("/usr/bin/security", ["-i"], `${command}
5617
+ `);
5618
+ }
5619
+ #linux(operation, encoded) {
5620
+ const attrs = ["service", this.#service, "account", ENTRY_ACCOUNT];
5621
+ if (operation === "read") return this.#run("secret-tool", ["lookup", ...attrs]);
5622
+ if (operation === "clear") return this.#run("secret-tool", ["clear", ...attrs]);
5623
+ return this.#run("secret-tool", ["store", "--label=BYOK device enrollment", ...attrs], encoded);
5624
+ }
5625
+ async #windows(operation, encoded) {
5626
+ await scavengeStaleWindowsBridges();
5627
+ const directory = await fs12.mkdtemp(path3.join(os.tmpdir(), WINDOWS_BRIDGE_DIRECTORY_PREFIX));
5628
+ const executable = path3.join(directory, "credential-bridge.exe");
5629
+ const request = [
5630
+ operation,
5631
+ Buffer.from(this.#service, "utf8").toString("base64"),
5632
+ Buffer.from(ENTRY_ACCOUNT, "utf8").toString("base64"),
5633
+ encoded === void 0 ? "" : Buffer.from(encoded, "utf8").toString("base64")
5634
+ ].join("\n");
5635
+ let result;
5636
+ let cleanupFailed = false;
5637
+ try {
5638
+ const compiler = await this.#run(
5639
+ windowsPowerShellExecutable(),
5640
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", WINDOWS_CREDENTIAL_COMPILER_SCRIPT],
5641
+ executable
5642
+ );
5643
+ result = compiler.exitCode === 0 ? await this.#run(executable, [], request) : { exitCode: compiler.exitCode, stdout: "", stderr: compiler.stderr };
5644
+ } finally {
5645
+ try {
5646
+ await fs12.rm(directory, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
5647
+ } catch {
5648
+ cleanupFailed = true;
5649
+ }
5650
+ }
5651
+ if (cleanupFailed) {
5652
+ throw new DeviceCredentialStoreError("temporary Windows credential bridge cleanup failed");
5653
+ }
5654
+ if (result === void 0) {
5655
+ throw new DeviceCredentialStoreError("Windows credential bridge did not produce a result");
5656
+ }
5657
+ return result;
5658
+ }
5659
+ };
5660
+ var InMemoryDeviceCredentialStore = class {
5661
+ #record;
5662
+ async read() {
5663
+ return this.#record === void 0 ? void 0 : Object.freeze({ ...this.#record });
5664
+ }
5665
+ async replace(record) {
5666
+ assertRecord(record);
5667
+ this.#record = Object.freeze({ ...record });
5668
+ }
5669
+ async clear() {
5670
+ const had = this.#record !== void 0;
5671
+ this.#record = void 0;
5672
+ return had;
5673
+ }
5674
+ };
5675
+ async function runDeviceCommand(executable, args, stdin) {
5676
+ return new Promise((resolve) => {
5677
+ const child = spawn(executable, [...args], { env: process.env, stdio: ["pipe", "pipe", "pipe"] });
5678
+ let stdout = "";
5679
+ let stderr = "";
5680
+ child.stdout.setEncoding("utf8");
5681
+ child.stderr.setEncoding("utf8");
5682
+ child.stdout.on("data", (chunk) => {
5683
+ stdout += chunk;
5684
+ });
5685
+ child.stderr.on("data", (chunk) => {
5686
+ stderr += chunk;
5687
+ });
5688
+ child.once("error", () => resolve({ exitCode: 127, stdout: "", stderr: "command unavailable" }));
5689
+ child.once("close", (code) => resolve({ exitCode: code ?? 1, stdout, stderr }));
5690
+ child.stdin.end(stdin);
5691
+ });
5692
+ }
5693
+ var WINDOWS_CREDENTIAL_COMPILER_SCRIPT = Buffer.from(String.raw`
5694
+ $assembly=[Console]::In.ReadToEnd()
5695
+ try {
5696
+ Add-Type -OutputAssembly $assembly -OutputType ConsoleApplication -ErrorAction Stop -TypeDefinition @"
5697
+ using System; using System.Runtime.InteropServices; using System.Text;
5698
+ public static class ByokDeviceCredential {
5699
+ [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] private struct C { public UInt32 Flags; public UInt32 Type; [MarshalAs(UnmanagedType.LPWStr)] public string TargetName; [MarshalAs(UnmanagedType.LPWStr)] public string Comment; public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten; public UInt32 CredentialBlobSize; public IntPtr CredentialBlob; public UInt32 Persist; public UInt32 AttributeCount; public IntPtr Attributes; [MarshalAs(UnmanagedType.LPWStr)] public string TargetAlias; [MarshalAs(UnmanagedType.LPWStr)] public string UserName; }
5700
+ [DllImport("Advapi32.dll", EntryPoint="CredWriteW", CharSet=CharSet.Unicode, SetLastError=true)] private static extern bool W(ref C c, UInt32 f);
5701
+ [DllImport("Advapi32.dll", EntryPoint="CredReadW", CharSet=CharSet.Unicode, SetLastError=true)] private static extern bool R(string t, UInt32 ty, UInt32 f, out IntPtr p);
5702
+ [DllImport("Advapi32.dll", EntryPoint="CredDeleteW", CharSet=CharSet.Unicode, SetLastError=true)] private static extern bool D(string t, UInt32 ty, UInt32 f);
5703
+ [DllImport("Advapi32.dll")] private static extern void CredFree(IntPtr p);
5704
+ private static int Set(string t,string u,byte[] b) { IntPtr p=IntPtr.Zero; try { p=Marshal.AllocHGlobal(b.Length); Marshal.Copy(b,0,p,b.Length); C c=new C { Type=1,TargetName=t,CredentialBlobSize=(UInt32)b.Length,CredentialBlob=p,Persist=2,UserName=u}; if(!W(ref c,0)) return Marshal.GetLastWin32Error(); return 0; } finally { if(p!=IntPtr.Zero) { Marshal.Copy(new byte[b.Length],0,p,b.Length); Marshal.FreeHGlobal(p); } } }
5705
+ private static int Kind(Exception e) { if(e is DllNotFoundException)return 1;if(e is EntryPointNotFoundException)return 2;if(e is BadImageFormatException)return 3;if(e is MarshalDirectiveException)return 4;if(e is SEHException)return 5;if(e is AccessViolationException)return 6;if(e is TypeInitializationException)return 7;if(e is TypeLoadException)return 8;if(e is InvalidCastException)return 9;if(e is ArgumentException)return 10;if(e is InvalidOperationException)return 11;if(e.GetType()==typeof(SystemException))return 12;if(e is SystemException)return 13;return 99; }
5706
+ private static int Failure(int stage,Exception e) { Console.Error.Write("credential operation failed (stage="+stage+",kind="+Kind(e)+",hresult="+e.HResult+")"); return -1; }
5707
+ private static int InputFailure(int kind) { Console.Error.Write("credential operation failed (stage=0,kind="+kind+",hresult=0)"); return 2; }
5708
+ private static int Get(string t) { IntPtr p=IntPtr.Zero; bool found; try { found=R(t,1,0,out p); } catch(Exception e) { return Failure(1,e); } if(!found) return Marshal.GetLastWin32Error(); int code=0; try { C c=(C)Marshal.PtrToStructure(p,typeof(C)); byte[] b=new byte[c.CredentialBlobSize]; if(b.Length>0) { Marshal.Copy(c.CredentialBlob,b,0,b.Length); using(var output=Console.OpenStandardOutput()) { output.Write(b,0,b.Length); output.Flush(); } } } catch(Exception e) { code=Failure(2,e); } try { CredFree(p); } catch(Exception e) { return Failure(3,e); } return code; }
5709
+ private static int Delete(string t) { if(D(t,1,0)) return 0; return Marshal.GetLastWin32Error(); }
5710
+ private static int ExitFor(int code,bool missingIsAbsent) { if(code==0)return 0;if(missingIsAbsent&&code==1168)return 44;if(code<0)return 1;Console.Error.Write("credential operation failed (win32="+code+")");return 1; }
5711
+ private static string Decode(string value) { return Encoding.UTF8.GetString(Convert.FromBase64String(value)); }
5712
+ public static int Main() { int stage=1;byte[] secret=null;try { string[] fields=Console.In.ReadToEnd().Split(new[]{'\n'},StringSplitOptions.None);if(fields.Length!=4)return InputFailure(fields.Length<100?fields.Length:99);string operation=fields[0];string target=Decode(fields[1]);string username=Decode(fields[2]);if(target.Length==0||username.Length==0)return InputFailure(20);if(operation=="replace") { stage=2;secret=Convert.FromBase64String(fields[3]);stage=3;return ExitFor(Set(target,username,secret),false); } if(operation=="read") { stage=4;return ExitFor(Get(target),true); } if(operation=="clear") { stage=6;return ExitFor(Delete(target),true); } return InputFailure(21); } catch(Exception e) { Failure(stage,e);return 1; } finally { if(secret!=null)Array.Clear(secret,0,secret.Length); } }
5713
+ }
5714
+ "@
5715
+ if(Test-Path -LiteralPath $assembly -PathType Leaf){exit 0}
5716
+ [Console]::Error.Write("credential operation failed (stage=8,kind=2,hresult=0)")
5717
+ } catch {
5718
+ $compilerCode=99
5719
+ $errorNumber=[string]$_.TargetObject.ErrorNumber
5720
+ if($errorNumber -match '\ACS([0-9]{4})\z'){$compilerCode=[Convert]::ToInt32($Matches[1])}
5721
+ [Console]::Error.Write("credential operation failed (stage=8,kind=3,hresult="+$compilerCode+")")
5722
+ }
5723
+ exit 1
5724
+ `, "utf16le").toString("base64");
5725
+
5726
+ // src/daemon/store.ts
4734
5727
  var MAX_DEVICE_RECORD_BYTES = 256 * 1024;
4735
5728
  var REPAIR_REQUIRED_MESSAGE = "device enrollment record is missing or has an invalid authenticated tenant binding; re-pair required";
4736
5729
  var DeviceRecordRePairRequiredError = class extends Error {
@@ -4748,30 +5741,31 @@ function sameFileState(left, right) {
4748
5741
  function sameContentState(left, right) {
4749
5742
  return sameInode(left, right) && left.size === right.size && left.mtimeNs === right.mtimeNs;
4750
5743
  }
4751
- function assertDeviceRecord(value) {
5744
+ var LEGACY_SECRET_FIELDS = /* @__PURE__ */ new Set(["accessToken", "expiresAt", "devicePrivateKeyPem"]);
5745
+ function assertDeviceMetadata(value) {
4752
5746
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
4753
5747
  throw new DeviceRecordRePairRequiredError();
4754
5748
  }
4755
5749
  const parsed = value;
4756
- if (typeof parsed.deviceId === "string" && isTenantId(parsed.tenantId) && typeof parsed.accessToken === "string" && typeof parsed.expiresAt === "string" && typeof parsed.devicePrivateKeyPem === "string" && typeof parsed.devicePublicKey === "string") {
5750
+ if (Object.keys(parsed).some((key) => LEGACY_SECRET_FIELDS.has(key))) {
5751
+ throw new DeviceRecordRePairRequiredError();
5752
+ }
5753
+ if (typeof parsed.deviceId === "string" && isTenantId(parsed.tenantId) && typeof parsed.devicePublicKey === "string") {
4757
5754
  return;
4758
5755
  }
4759
5756
  throw new DeviceRecordRePairRequiredError();
4760
5757
  }
4761
- function parseDeviceRecord(raw) {
5758
+ function parseDeviceMetadata(raw) {
4762
5759
  let parsed;
4763
5760
  try {
4764
5761
  parsed = JSON.parse(raw);
4765
5762
  } catch {
4766
5763
  throw new DeviceRecordRePairRequiredError();
4767
5764
  }
4768
- assertDeviceRecord(parsed);
5765
+ assertDeviceMetadata(parsed);
4769
5766
  return {
4770
5767
  deviceId: parsed.deviceId,
4771
5768
  tenantId: parsed.tenantId,
4772
- accessToken: parsed.accessToken,
4773
- expiresAt: parsed.expiresAt,
4774
- devicePrivateKeyPem: parsed.devicePrivateKeyPem,
4775
5769
  devicePublicKey: parsed.devicePublicKey
4776
5770
  };
4777
5771
  }
@@ -4786,14 +5780,31 @@ var DeviceStore = class _DeviceStore {
4786
5780
  * ACL-unprotected credential") is verifiable from a real `darwin`/`linux`
4787
5781
  * CI/dev machine, not just asserted.
4788
5782
  */
4789
- constructor(storeDir, secureDirOptions) {
5783
+ constructor(storeDir, secureDirOptions, productId) {
4790
5784
  this.secureDirOptions = secureDirOptions;
4791
- this.filePath = path.join(storeDir, "device.json");
5785
+ this.filePath = path3__default.join(storeDir, "device.json");
5786
+ if (productId === void 0) {
5787
+ this.credentials = new InMemoryDeviceCredentialStore();
5788
+ } else if (process.env.BYOK_TEST_DEVICE_CREDENTIAL_STORE === "1") {
5789
+ const key = `${path3__default.resolve(storeDir)}\0${productId}`;
5790
+ let credentials = _DeviceStore.testCredentials.get(key);
5791
+ if (credentials === void 0) {
5792
+ credentials = new InMemoryDeviceCredentialStore();
5793
+ _DeviceStore.testCredentials.set(key, credentials);
5794
+ }
5795
+ this.credentials = credentials;
5796
+ } else {
5797
+ this.credentials = new DeviceCredentialStore({ productId });
5798
+ }
4792
5799
  }
4793
5800
  secureDirOptions;
5801
+ /** Process-local keyed doubles preserve restart semantics in isolated tests. */
5802
+ static testCredentials = /* @__PURE__ */ new Map();
4794
5803
  filePath;
5804
+ /** Internal test seam. Product construction always supplies productId and gets an OS store. */
5805
+ credentials;
4795
5806
  static defaultDir(productId) {
4796
- return path.join(os.homedir(), ".byok", productId);
5807
+ return path3__default.join(os__default.homedir(), ".byok", productId);
4797
5808
  }
4798
5809
  /**
4799
5810
  * Resolve the one store pathname every daemon/CLI component must share.
@@ -4802,13 +5813,13 @@ var DeviceStore = class _DeviceStore {
4802
5813
  * cwd to pin a quarantine directory inode.
4803
5814
  */
4804
5815
  static resolveDir(productId, configured) {
4805
- return path.resolve(configured ?? _DeviceStore.defaultDir(productId));
5816
+ return path3__default.resolve(configured ?? _DeviceStore.defaultDir(productId));
4806
5817
  }
4807
5818
  async load() {
4808
5819
  const opened = await this.openBounded();
4809
5820
  if (!opened) return void 0;
4810
5821
  try {
4811
- return parseDeviceRecord(opened.raw);
5822
+ return parseDeviceMetadata(opened.raw);
4812
5823
  } finally {
4813
5824
  await opened.handle.close();
4814
5825
  }
@@ -4823,7 +5834,7 @@ var DeviceStore = class _DeviceStore {
4823
5834
  if (!opened) return void 0;
4824
5835
  const guardPath = `${this.filePath}.${process.pid}.${randomUUID()}.remove`;
4825
5836
  try {
4826
- const record = parseDeviceRecord(opened.raw);
5837
+ const record = parseDeviceMetadata(opened.raw);
4827
5838
  linkSync(this.filePath, guardPath);
4828
5839
  const openStat = fstatSync(opened.handle.fd, { bigint: true });
4829
5840
  const guarded = lstatSync(guardPath, { bigint: true });
@@ -4843,14 +5854,11 @@ var DeviceStore = class _DeviceStore {
4843
5854
  }
4844
5855
  }
4845
5856
  async save(record) {
4846
- assertDeviceRecord(record);
4847
- const storeDir = path.dirname(this.filePath);
5857
+ assertDeviceMetadata(record);
5858
+ const storeDir = path3__default.dirname(this.filePath);
4848
5859
  await ensureSecureDir(storeDir, this.secureDirOptions);
4849
5860
  await atomicWriteFile(this.filePath, JSON.stringify(record, null, 2), { mode: 384 });
4850
5861
  }
4851
- async clear() {
4852
- await promises.rm(this.filePath, { force: true });
4853
- }
4854
5862
  async openBounded() {
4855
5863
  let namedBefore;
4856
5864
  try {
@@ -4982,6 +5990,7 @@ var RENEW_MARGIN_MS = 60 * 1e3;
4982
5990
  var AuthManager = class {
4983
5991
  constructor(opts) {
4984
5992
  this.opts = opts;
5993
+ this.credentials = opts.credentials ?? opts.store.credentials;
4985
5994
  }
4986
5995
  opts;
4987
5996
  record;
@@ -4991,17 +6000,22 @@ var AuthManager = class {
4991
6000
  stopped = false;
4992
6001
  pairing = false;
4993
6002
  credentialMutationTail = Promise.resolve();
6003
+ credentials;
4994
6004
  get deviceId() {
4995
6005
  return this.record?.deviceId;
4996
6006
  }
4997
6007
  isRevoked() {
4998
6008
  return this.revoked;
4999
6009
  }
6010
+ /** Internal signer read: always recompose metadata with the current OS secret authority. */
6011
+ async readCurrent() {
6012
+ return this.loadRecord();
6013
+ }
5000
6014
  /** Load a previously-paired device record from disk, if any (idempotent — a second call is a no-op once loaded). */
5001
6015
  async loadExisting() {
5002
6016
  this.stopped = false;
5003
6017
  if (!this.record) {
5004
- this.record = await this.opts.store.load();
6018
+ this.record = await this.loadRecord();
5005
6019
  }
5006
6020
  if (this.record) this.scheduleProactiveRenewal();
5007
6021
  return this.record;
@@ -5017,7 +6031,7 @@ var AuthManager = class {
5017
6031
  let existing = this.record;
5018
6032
  if (!existing) {
5019
6033
  try {
5020
- existing = await this.opts.store.load();
6034
+ existing = await this.loadRecord();
5021
6035
  } catch (error) {
5022
6036
  if (!(error instanceof DeviceRecordRePairRequiredError)) throw error;
5023
6037
  }
@@ -5029,7 +6043,7 @@ var AuthManager = class {
5029
6043
  headers: { "content-type": "application/json" },
5030
6044
  body: JSON.stringify({
5031
6045
  pairingCode,
5032
- deviceName: this.opts.deviceName ?? os.hostname(),
6046
+ deviceName: this.opts.deviceName ?? os__default.hostname(),
5033
6047
  devicePublicKey: keyPair.publicKeyBase64Url
5034
6048
  })
5035
6049
  });
@@ -5037,15 +6051,19 @@ var AuthManager = class {
5037
6051
  throw new Error(`pairing failed: HTTP ${res.status} ${await safeErrorText(res)}`.trimEnd());
5038
6052
  }
5039
6053
  const body = PairResponseSchema.parse(await res.json());
5040
- const record = {
6054
+ const metadata = {
5041
6055
  deviceId: body.deviceId,
5042
6056
  tenantId: body.tenantId,
6057
+ devicePublicKey: keyPair.publicKeyBase64Url
6058
+ };
6059
+ const record = {
6060
+ ...metadata,
5043
6061
  accessToken: body.accessToken,
5044
6062
  expiresAt: resolvePairExpiry(body.refreshHint),
5045
- devicePrivateKeyPem: exportPrivateKeyPem(keyPair.privateKey),
5046
- devicePublicKey: keyPair.publicKeyBase64Url
6063
+ devicePrivateKeyPem: exportPrivateKeyPem(keyPair.privateKey)
5047
6064
  };
5048
- await this.opts.store.save(record);
6065
+ await this.opts.store.save(metadata);
6066
+ await this.credentials.replace(record);
5049
6067
  this.record = record;
5050
6068
  this.revoked = false;
5051
6069
  return record;
@@ -5110,8 +6128,12 @@ var AuthManager = class {
5110
6128
  throw new Error(`token renewal (token) failed: HTTP ${tokenRes.status} ${await safeErrorText(tokenRes)}`.trimEnd());
5111
6129
  }
5112
6130
  const body = await tokenRes.json();
5113
- const updated = { ...record, accessToken: body.accessToken, expiresAt: body.expiresAt };
5114
- await this.opts.store.save(updated);
6131
+ const updated = {
6132
+ ...record,
6133
+ accessToken: body.accessToken,
6134
+ expiresAt: body.expiresAt
6135
+ };
6136
+ await this.credentials.replace(updated);
5115
6137
  this.record = updated;
5116
6138
  this.scheduleProactiveRenewal();
5117
6139
  return updated.accessToken;
@@ -5148,6 +6170,24 @@ var AuthManager = class {
5148
6170
  release();
5149
6171
  }
5150
6172
  }
6173
+ /** Read the current paired authority afresh; metadata without its OS secret is re-pair required. */
6174
+ async loadRecord() {
6175
+ const authority = await this.credentials.read();
6176
+ if (authority === void 0) {
6177
+ if (await this.opts.store.load() === void 0) return void 0;
6178
+ throw new DeviceRecordRePairRequiredError();
6179
+ }
6180
+ const projection = {
6181
+ deviceId: authority.deviceId,
6182
+ tenantId: authority.tenantId,
6183
+ devicePublicKey: authority.devicePublicKey
6184
+ };
6185
+ const current = await this.opts.store.load();
6186
+ if (current === void 0 || current.deviceId !== projection.deviceId || current.tenantId !== projection.tenantId || current.devicePublicKey !== projection.devicePublicKey) {
6187
+ await this.opts.store.save(projection);
6188
+ }
6189
+ return Object.freeze({ ...authority });
6190
+ }
5151
6191
  };
5152
6192
  function msUntilExpiry(expiresAt) {
5153
6193
  return new Date(expiresAt).getTime() - Date.now();
@@ -5492,25 +6532,25 @@ function mintDeviceAssertion(input) {
5492
6532
  }
5493
6533
  var CONTROL_PROTOCOL_VERSION = 1;
5494
6534
  var HANDSHAKE_TIMEOUT_MS = 3e3;
5495
- var UNIX_SOCKET_PATH_SOFT_LIMIT = 100;
6535
+ var UNIX_SOCKET_PATH_SOFT_LIMIT2 = 100;
5496
6536
  var CONTROL_SOCKET_FALLBACK_ROOT = "/tmp";
5497
6537
  function shortHash(input) {
5498
6538
  return createHash("sha256").update(input, "utf8").digest("hex").slice(0, 16);
5499
6539
  }
5500
6540
  function controlSocketPath(storeDir) {
5501
- const candidate = path.join(storeDir, "control.sock");
5502
- if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT) return candidate;
5503
- return path.join(CONTROL_SOCKET_FALLBACK_ROOT, `byok-${shortHash(storeDir)}`, "sock");
6541
+ const candidate = path3__default.join(storeDir, "control.sock");
6542
+ if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT2) return candidate;
6543
+ return path3__default.join(CONTROL_SOCKET_FALLBACK_ROOT, `byok-${shortHash(storeDir)}`, "sock");
5504
6544
  }
5505
6545
  function controlPipeName(productId, storeDir) {
5506
- const id = shortHash(`${productId}|${path.resolve(storeDir)}`);
6546
+ const id = shortHash(`${productId}|${path3__default.resolve(storeDir)}`);
5507
6547
  return `\\\\.\\pipe\\byok-${id}`;
5508
6548
  }
5509
6549
  function controlEndpointPath(productId, storeDir, platform = process.platform) {
5510
6550
  return platform === "win32" ? controlPipeName(productId, storeDir) : controlSocketPath(storeDir);
5511
6551
  }
5512
6552
  function controlTokenPath(storeDir) {
5513
- return path.join(storeDir, "control.token");
6553
+ return path3__default.join(storeDir, "control.token");
5514
6554
  }
5515
6555
  var SERVER_PROOF_LABEL = "byok-control-server|";
5516
6556
  var CLIENT_AUTH_LABEL = "byok-control-client|";
@@ -5592,7 +6632,15 @@ var NdjsonLineReader = class {
5592
6632
  }
5593
6633
  return lines;
5594
6634
  }
5595
- };
6635
+ };
6636
+ var ENROLLMENT_PAIRING_CODE_MAX_BYTES = 1024;
6637
+ function parseEnrollmentPairParams(value) {
6638
+ if (!isRecord3(value) || Object.keys(value).some((key) => key !== "pairingCode")) return void 0;
6639
+ if (typeof value.pairingCode !== "string" || value.pairingCode.length === 0 || Buffer.byteLength(value.pairingCode, "utf8") > ENROLLMENT_PAIRING_CODE_MAX_BYTES) {
6640
+ return void 0;
6641
+ }
6642
+ return { pairingCode: value.pairingCode };
6643
+ }
5596
6644
  function parseToolsetsReloadParams(value) {
5597
6645
  if (!isRecord3(value) || Object.keys(value).some((key) => key !== "expectedRevision" && key !== "mcpToolsets")) {
5598
6646
  return void 0;
@@ -5672,7 +6720,7 @@ async function handleStaleUnixSocket(socketPath) {
5672
6720
  if (alive) throw new AnotherControlServerRunningError(socketPath);
5673
6721
  await promises.rm(socketPath, { force: true });
5674
6722
  }
5675
- async function assertOwnedPrivateDir(dir) {
6723
+ async function assertOwnedPrivateDir2(dir) {
5676
6724
  const uid = process.getuid?.();
5677
6725
  if (uid === void 0) return;
5678
6726
  const st = await promises.lstat(dir);
@@ -5689,11 +6737,11 @@ async function assertOwnedPrivateDir(dir) {
5689
6737
  }
5690
6738
  async function bindControlEndpoint(server, endpoint) {
5691
6739
  if (process.platform !== "win32") {
5692
- const endpointDir = path.dirname(endpoint);
6740
+ const endpointDir = path3__default.dirname(endpoint);
5693
6741
  await promises.mkdir(endpointDir, { recursive: true, mode: 448 });
5694
6742
  await promises.chmod(endpointDir, 448).catch(() => {
5695
6743
  });
5696
- await assertOwnedPrivateDir(endpointDir);
6744
+ await assertOwnedPrivateDir2(endpointDir);
5697
6745
  await handleStaleUnixSocket(endpoint);
5698
6746
  }
5699
6747
  await new Promise((resolve, reject) => {
@@ -6588,7 +7636,7 @@ function toBytes(data, _isBinary) {
6588
7636
 
6589
7637
  // src/daemon/connection-manager.ts
6590
7638
  function isCursorEnvelopeType(type) {
6591
- return type.startsWith("task.") || type === "agent.egress.ack" || type === "agent.content.read";
7639
+ return type.startsWith("task.") || type === "agent.egress.ack" || type === "agent.content.read" || type === "agent.home.projection";
6592
7640
  }
6593
7641
  var ConnectionManager = class {
6594
7642
  constructor(opts) {
@@ -7103,6 +8151,10 @@ var ConnectionManager = class {
7103
8151
  async process(envelope, tracked) {
7104
8152
  const seq = tracked ? envelope.seq : void 0;
7105
8153
  try {
8154
+ if (tracked && this.cursor === void 0) {
8155
+ await this.opts.cursorStore.save(this.opts.serverUrl, this.opts.deviceId, 0);
8156
+ this.cursor = 0;
8157
+ }
7106
8158
  await this.opts.onEnvelope(envelope);
7107
8159
  if (!tracked) return;
7108
8160
  this.processedSeqs.add(seq);
@@ -7332,7 +8384,7 @@ function sameFileState2(left, right) {
7332
8384
  return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
7333
8385
  }
7334
8386
  async function openOperationalHealthFile(storeDir) {
7335
- const filePath = path.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
8387
+ const filePath = path3__default.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
7336
8388
  let namedBefore;
7337
8389
  try {
7338
8390
  namedBefore = await promises.lstat(filePath, { bigint: true });
@@ -7374,7 +8426,7 @@ var OperationalHealthTracker = class {
7374
8426
  #writeTail = Promise.resolve();
7375
8427
  #started = false;
7376
8428
  constructor(storeDir, options = {}) {
7377
- this.#filePath = path.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
8429
+ this.#filePath = path3__default.join(storeDir, OPERATIONAL_HEALTH_FILENAME);
7378
8430
  this.#windowMs = options.windowMs ?? 6e4;
7379
8431
  this.#failureThreshold = options.failureThreshold ?? 3;
7380
8432
  this.#maxFailures = options.maxFailures ?? 128;
@@ -7407,539 +8459,203 @@ var OperationalHealthTracker = class {
7407
8459
  if (loaded.currentRun) {
7408
8460
  loaded.crashes.push({ detectedAt: now.toISOString(), previousRunStartedAt: loaded.currentRun.startedAt });
7409
8461
  loaded.crashes = loaded.crashes.slice(-this.#maxCrashes);
7410
- }
7411
- loaded.currentRun = { id: this.#runId(), startedAt: now.toISOString(), pid: this.#pid };
7412
- await this.#persist();
7413
- return this.snapshot();
7414
- }
7415
- async recordFailure(source) {
7416
- if (!this.#state || this.#unavailableReason) return;
7417
- const now = this.#clock();
7418
- this.#prune(now);
7419
- this.#state.failures.push({ at: now.toISOString(), source });
7420
- this.#state.failures = this.#state.failures.slice(-this.#maxFailures);
7421
- if (this.#state.failures.length >= this.#failureThreshold || this.#state.state === "recovering") {
7422
- this.#state.state = "degraded";
7423
- }
7424
- await this.#persist();
7425
- }
7426
- async recordSuccess(_source) {
7427
- if (!this.#state || this.#unavailableReason) return;
7428
- const now = this.#clock();
7429
- this.#prune(now);
7430
- if (this.#state.state === "healthy") return;
7431
- if (this.#state.state === "degraded") {
7432
- this.#state.state = "recovering";
7433
- } else if (this.#state.state === "recovering" && this.#state.failures.length < this.#failureThreshold) {
7434
- this.#state.state = "healthy";
7435
- } else {
7436
- return;
7437
- }
7438
- await this.#persist();
7439
- }
7440
- async markCleanStop() {
7441
- if (!this.#state || this.#unavailableReason || !this.#started) return;
7442
- this.#state.currentRun = void 0;
7443
- await this.#persist();
7444
- this.#started = false;
7445
- }
7446
- snapshot() {
7447
- if (this.#unavailableReason) return { availability: "unavailable", reason: this.#unavailableReason };
7448
- if (!this.#state) return { availability: "unavailable", reason: "operational health has not been loaded" };
7449
- this.#prune(this.#clock());
7450
- return {
7451
- availability: "available",
7452
- state: this.#state.state,
7453
- failureCount: this.#state.failures.length,
7454
- windowMs: this.#windowMs,
7455
- failureThreshold: this.#failureThreshold,
7456
- crashCount: this.#state.crashes.length,
7457
- ...this.#state.crashes.at(-1) ? { lastCrashAt: this.#state.crashes.at(-1).detectedAt } : {},
7458
- ...this.#state.currentRun ? { currentRunStartedAt: this.#state.currentRun.startedAt } : {}
7459
- };
7460
- }
7461
- async #load() {
7462
- let opened;
7463
- try {
7464
- opened = await openOperationalHealthFile(path.dirname(this.#filePath));
7465
- } catch (err) {
7466
- throw new Error("operational health state could not be read");
7467
- }
7468
- if (!opened) return { version: 1, state: "healthy", failures: [], crashes: [] };
7469
- try {
7470
- const inspected = await readOperationalHealthHandle(opened.handle, opened.stat);
7471
- if (inspected.status !== "read") throw new Error(inspected.reason);
7472
- let parsed;
7473
- try {
7474
- parsed = JSON.parse(inspected.raw);
7475
- } catch {
7476
- throw new Error("operational health state is corrupt JSON");
7477
- }
7478
- if (!isHealthFile(parsed)) throw new Error("operational health state has an invalid shape");
7479
- return parsed;
7480
- } finally {
7481
- await opened.handle.close();
7482
- }
7483
- }
7484
- #prune(now) {
7485
- if (!this.#state) return;
7486
- const nowMs = now.getTime();
7487
- const cutoff = nowMs - this.#windowMs;
7488
- this.#state.failures = this.#state.failures.filter((event) => {
7489
- const eventMs = Date.parse(event.at);
7490
- return eventMs >= cutoff && eventMs <= nowMs;
7491
- });
7492
- if (this.#state.state === "recovering" && this.#state.failures.length < this.#failureThreshold) {
7493
- this.#state.state = "healthy";
7494
- }
7495
- }
7496
- async #persist() {
7497
- if (!this.#state) return;
7498
- const body = JSON.stringify(this.#state, null, 2);
7499
- this.#writeTail = this.#writeTail.then(async () => {
7500
- await ensureSecureDir(path.dirname(this.#filePath));
7501
- await atomicWriteFile(this.#filePath, body, { mode: 384, fsync: true });
7502
- });
7503
- try {
7504
- await this.#writeTail;
7505
- } catch (err) {
7506
- this.#unavailableReason = "operational health state could not be persisted";
7507
- throw err;
7508
- }
7509
- }
7510
- };
7511
- async function inspectOperationalHealthHandle(handle, expected) {
7512
- const read = await readOperationalHealthHandle(handle, expected);
7513
- if (read.status !== "read") return read;
7514
- const { raw, sizeBytes } = read;
7515
- let parsed;
7516
- try {
7517
- parsed = JSON.parse(raw);
7518
- } catch {
7519
- return { status: "corrupt", sizeBytes, reason: "operational health state is corrupt JSON" };
7520
- }
7521
- if (!isHealthFile(parsed)) {
7522
- return { status: "corrupt", sizeBytes, reason: "operational health state has an invalid shape" };
7523
- }
7524
- return {
7525
- status: "valid",
7526
- sizeBytes,
7527
- state: parsed.state,
7528
- failureCount: parsed.failures.length,
7529
- crashCount: parsed.crashes.length,
7530
- ...parsed.currentRun ? { currentRunStartedAt: new Date(Date.parse(parsed.currentRun.startedAt)).toISOString() } : {},
7531
- ...parsed.crashes.at(-1) ? { lastCrashAt: new Date(Date.parse(parsed.crashes.at(-1).detectedAt)).toISOString() } : {}
7532
- };
7533
- }
7534
- async function readOperationalHealthHandle(handle, expected) {
7535
- let before;
7536
- try {
7537
- before = await handle.stat({ bigint: true });
7538
- } catch {
7539
- return { status: "unavailable", reason: "operational health state could not be inspected" };
7540
- }
7541
- const sizeBytes = Number(before.size);
7542
- if (expected && !sameFileState2(before, expected)) {
7543
- return { status: "unavailable", sizeBytes, reason: "operational health handle identity changed before inspection" };
7544
- }
7545
- if (!before.isFile()) {
7546
- return { status: "unavailable", sizeBytes, reason: "operational health state is not a regular file" };
7547
- }
7548
- if (sizeBytes > MAX_OPERATIONAL_HEALTH_FILE_BYTES) {
7549
- return {
7550
- status: "unavailable",
7551
- sizeBytes,
7552
- reason: "operational health state exceeds the 1 MiB read limit; corruption is unconfirmed"
7553
- };
7554
- }
7555
- try {
7556
- const buffer = Buffer.alloc(sizeBytes);
7557
- const { bytesRead } = await handle.read(buffer, 0, sizeBytes, 0);
7558
- const after = await handle.stat({ bigint: true });
7559
- if (bytesRead !== sizeBytes || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs) {
7560
- return { status: "unavailable", sizeBytes: Number(after.size), reason: "operational health state changed during inspection" };
7561
- }
7562
- return { status: "read", raw: buffer.toString("utf8"), sizeBytes };
7563
- } catch {
7564
- return { status: "unavailable", sizeBytes, reason: "operational health state could not be read" };
7565
- }
7566
- }
7567
- async function inspectOperationalHealthFile(storeDir) {
7568
- let opened;
7569
- try {
7570
- opened = await openOperationalHealthFile(storeDir);
7571
- } catch {
7572
- return { status: "unavailable", reason: "operational health state could not be opened safely" };
7573
- }
7574
- if (!opened) return { status: "missing" };
7575
- try {
7576
- return await inspectOperationalHealthHandle(opened.handle, opened.stat);
7577
- } finally {
7578
- await opened.handle.close();
7579
- }
7580
- }
7581
- function isHealthFile(value) {
7582
- if (typeof value !== "object" || value === null) return false;
7583
- const candidate = value;
7584
- const keys = Object.keys(candidate);
7585
- if (keys.some((key) => !["version", "state", "failures", "crashes", "currentRun"].includes(key))) return false;
7586
- if (candidate.version !== 1 || !["healthy", "degraded", "recovering"].includes(String(candidate.state))) return false;
7587
- if (!Array.isArray(candidate.failures) || !candidate.failures.every(isFailureEvent)) return false;
7588
- if (!Array.isArray(candidate.crashes) || !candidate.crashes.every(isCrashRecord)) return false;
7589
- if (candidate.currentRun !== void 0 && !isRunMarker(candidate.currentRun)) return false;
7590
- return true;
7591
- }
7592
- function isFailureEvent(value) {
7593
- if (typeof value !== "object" || value === null) return false;
7594
- const event = value;
7595
- return Object.keys(event).every((key) => ["at", "source"].includes(key)) && typeof event.at === "string" && Number.isFinite(Date.parse(event.at)) && ["reconnect", "upload", "maintenance", "lifecycle"].includes(String(event.source));
7596
- }
7597
- function isCrashRecord(value) {
7598
- if (typeof value !== "object" || value === null) return false;
7599
- const record = value;
7600
- return Object.keys(record).every((key) => ["detectedAt", "previousRunStartedAt"].includes(key)) && typeof record.detectedAt === "string" && Number.isFinite(Date.parse(record.detectedAt)) && typeof record.previousRunStartedAt === "string" && Number.isFinite(Date.parse(record.previousRunStartedAt));
7601
- }
7602
- function isRunMarker(value) {
7603
- if (typeof value !== "object" || value === null) return false;
7604
- const marker = value;
7605
- return Object.keys(marker).every((key) => ["id", "startedAt", "pid"].includes(key)) && typeof marker.id === "string" && typeof marker.startedAt === "string" && Number.isFinite(Date.parse(marker.startedAt)) && typeof marker.pid === "number" && Number.isSafeInteger(marker.pid);
7606
- }
7607
- var DAEMON_OWNER_FILENAME = "daemon-owner.json";
7608
- var RECLAIM_FILENAME = `${DAEMON_OWNER_FILENAME}.reclaim`;
7609
- var MAX_OWNER_BYTES = 4096;
7610
- var RECLAIM_MALFORMED_GRACE_MS = 3e4;
7611
- var SELF_PROCESS_STARTED_AT = new Date(Date.now() - process.uptime() * 1e3).toISOString();
7612
- function endOwnershipProbe(socket, response) {
7613
- socket.on("error", () => {
7614
- });
7615
- if (response === void 0) socket.end();
7616
- else socket.end(response);
7617
- }
7618
- var STORE_MUTEX_ID_PREFIX = "byok-store-mutex-v1:";
7619
- var STORE_MUTEX_PROBE_TIMEOUT_MS = 1e3;
7620
- var STORE_MUTEX_SOCKET_FILENAME = "mutex.sock";
7621
- var UNIX_SOCKET_PATH_SOFT_LIMIT2 = 100;
7622
- var STORE_MUTEX_FALLBACK_ROOT = "/tmp";
7623
- function storeMutexIdentity(canonicalStoreDir) {
7624
- return createHash("sha256").update(canonicalStoreDir).digest("hex");
7625
- }
7626
- function storeMutexEndpoint(canonicalStoreDir, identity, platform = process.platform) {
7627
- if (platform === "win32") return `\\\\.\\pipe\\byok-store-mutex-${identity.slice(0, 16)}`;
7628
- const candidate = path.join(canonicalStoreDir, STORE_MUTEX_SOCKET_FILENAME);
7629
- if (Buffer.byteLength(candidate, "utf8") <= UNIX_SOCKET_PATH_SOFT_LIMIT2) return candidate;
7630
- return path.join(STORE_MUTEX_FALLBACK_ROOT, `byok-store-mutex-${identity.slice(0, 16)}`, "sock");
7631
- }
7632
- var DaemonOwnerActiveError = class extends Error {
7633
- constructor(role) {
7634
- super(`store mutation lease is already held by an active ${role} process`);
7635
- this.role = role;
7636
- this.name = "DaemonOwnerActiveError";
7637
- }
7638
- role;
7639
- };
7640
- function isOwnerRecord(value) {
7641
- if (typeof value !== "object" || value === null) return false;
7642
- const candidate = value;
7643
- return candidate.version === 2 && Number.isSafeInteger(candidate.pid) && (candidate.pid ?? 0) > 0 && typeof candidate.nonce === "string" && candidate.nonce.length === 36 && (candidate.role === "daemon" || candidate.role === "doctor") && typeof candidate.acquiredAt === "string" && Number.isFinite(Date.parse(candidate.acquiredAt)) && typeof candidate.processStartedAt === "string" && Number.isFinite(Date.parse(candidate.processStartedAt)) && Number.isSafeInteger(candidate.livenessPort) && (candidate.livenessPort ?? 0) > 0 && (candidate.livenessPort ?? 0) <= 65535;
7644
- }
7645
- async function readOwner(filePath) {
7646
- let namedBefore;
7647
- try {
7648
- namedBefore = await promises.lstat(filePath, { bigint: true });
7649
- } catch (err) {
7650
- if (err.code === "ENOENT") return void 0;
7651
- throw err;
7652
- }
7653
- if (!namedBefore.isFile() || namedBefore.isSymbolicLink()) {
7654
- throw new Error("store mutation owner path is not a real regular file");
7655
- }
7656
- let handle;
7657
- try {
7658
- handle = await promises.open(
7659
- filePath,
7660
- constants.O_RDONLY | (constants.O_NONBLOCK ?? 0) | (constants.O_NOFOLLOW ?? 0)
7661
- );
7662
- } catch (err) {
7663
- if (err.code === "ENOENT") return void 0;
7664
- throw err;
8462
+ }
8463
+ loaded.currentRun = { id: this.#runId(), startedAt: now.toISOString(), pid: this.#pid };
8464
+ await this.#persist();
8465
+ return this.snapshot();
7665
8466
  }
7666
- try {
7667
- const stat = await handle.stat({ bigint: true });
7668
- const namedAfterOpen = await promises.lstat(filePath, { bigint: true });
7669
- if (!stat.isFile() || !namedAfterOpen.isFile() || namedAfterOpen.isSymbolicLink() || stat.dev !== namedBefore.dev || stat.ino !== namedBefore.ino || stat.size !== namedBefore.size || stat.mtimeNs !== namedBefore.mtimeNs || stat.ctimeNs !== namedBefore.ctimeNs || stat.dev !== namedAfterOpen.dev || stat.ino !== namedAfterOpen.ino || stat.size !== namedAfterOpen.size || stat.mtimeNs !== namedAfterOpen.mtimeNs || stat.ctimeNs !== namedAfterOpen.ctimeNs) {
7670
- throw new Error("store mutation owner path changed before safe open");
8467
+ async recordFailure(source) {
8468
+ if (!this.#state || this.#unavailableReason) return;
8469
+ const now = this.#clock();
8470
+ this.#prune(now);
8471
+ this.#state.failures.push({ at: now.toISOString(), source });
8472
+ this.#state.failures = this.#state.failures.slice(-this.#maxFailures);
8473
+ if (this.#state.failures.length >= this.#failureThreshold || this.#state.state === "recovering") {
8474
+ this.#state.state = "degraded";
7671
8475
  }
7672
- if (stat.size <= 0 || stat.size > MAX_OWNER_BYTES) return void 0;
7673
- const size = Number(stat.size);
7674
- const buffer = Buffer.alloc(size);
7675
- const { bytesRead } = await handle.read(buffer, 0, size, 0);
7676
- const after = await handle.stat({ bigint: true });
7677
- const namedAfterRead = await promises.lstat(filePath, { bigint: true });
7678
- if (bytesRead !== size || after.dev !== stat.dev || after.ino !== stat.ino || after.size !== stat.size || after.mtimeNs !== stat.mtimeNs || after.ctimeNs !== stat.ctimeNs || namedAfterRead.dev !== stat.dev || namedAfterRead.ino !== stat.ino || namedAfterRead.size !== stat.size || namedAfterRead.mtimeNs !== stat.mtimeNs || namedAfterRead.ctimeNs !== stat.ctimeNs) {
7679
- throw new Error("store mutation owner path changed during inspection");
8476
+ await this.#persist();
8477
+ }
8478
+ async recordSuccess(_source) {
8479
+ if (!this.#state || this.#unavailableReason) return;
8480
+ const now = this.#clock();
8481
+ this.#prune(now);
8482
+ if (this.#state.state === "healthy") return;
8483
+ if (this.#state.state === "degraded") {
8484
+ this.#state.state = "recovering";
8485
+ } else if (this.#state.state === "recovering" && this.#state.failures.length < this.#failureThreshold) {
8486
+ this.#state.state = "healthy";
8487
+ } else {
8488
+ return;
7680
8489
  }
7681
- const raw = buffer.toString("utf8");
7682
- const parsed = JSON.parse(raw);
7683
- return isOwnerRecord(parsed) ? parsed : void 0;
7684
- } catch (err) {
7685
- if (err instanceof Error && err.message.startsWith("store mutation owner path changed")) throw err;
7686
- return void 0;
7687
- } finally {
7688
- await handle.close();
8490
+ await this.#persist();
7689
8491
  }
7690
- }
7691
- function pidIsAlive(pid) {
7692
- try {
7693
- process.kill(pid, 0);
7694
- return true;
7695
- } catch (err) {
7696
- return err.code === "EPERM";
8492
+ async markCleanStop() {
8493
+ if (!this.#state || this.#unavailableReason || !this.#started) return;
8494
+ this.#state.currentRun = void 0;
8495
+ await this.#persist();
8496
+ this.#started = false;
7697
8497
  }
7698
- }
7699
- async function processOwnsRecord(record) {
7700
- if (!pidIsAlive(record.pid)) return false;
7701
- if (record.pid === process.pid && record.processStartedAt !== SELF_PROCESS_STARTED_AT) return false;
7702
- return portIsBound(record.livenessPort);
7703
- }
7704
- async function portIsBound(port) {
7705
- const probe = createServer();
7706
- return new Promise((resolve, reject) => {
7707
- const finish = (result) => {
7708
- probe.removeAllListeners();
7709
- resolve(result);
8498
+ snapshot() {
8499
+ if (this.#unavailableReason) return { availability: "unavailable", reason: this.#unavailableReason };
8500
+ if (!this.#state) return { availability: "unavailable", reason: "operational health has not been loaded" };
8501
+ this.#prune(this.#clock());
8502
+ return {
8503
+ availability: "available",
8504
+ state: this.#state.state,
8505
+ failureCount: this.#state.failures.length,
8506
+ windowMs: this.#windowMs,
8507
+ failureThreshold: this.#failureThreshold,
8508
+ crashCount: this.#state.crashes.length,
8509
+ ...this.#state.crashes.at(-1) ? { lastCrashAt: this.#state.crashes.at(-1).detectedAt } : {},
8510
+ ...this.#state.currentRun ? { currentRunStartedAt: this.#state.currentRun.startedAt } : {}
7710
8511
  };
7711
- probe.once("error", (err) => {
7712
- if (err.code === "EADDRINUSE") finish(true);
7713
- else reject(err);
7714
- });
7715
- probe.listen({ host: "127.0.0.1", port, exclusive: true }, () => {
7716
- probe.close((err) => {
7717
- if (err) reject(err);
7718
- else finish(false);
7719
- });
7720
- });
7721
- });
7722
- }
7723
- async function createLivenessListener() {
7724
- const server = createServer((socket) => endOwnershipProbe(socket));
7725
- const port = await new Promise((resolve, reject) => {
7726
- server.once("error", reject);
7727
- server.listen({ host: "127.0.0.1", port: 0, exclusive: true }, () => {
7728
- server.removeListener("error", reject);
7729
- const address = server.address();
7730
- if (!address || typeof address === "string") {
7731
- reject(new Error("store mutation liveness listener did not expose a TCP port"));
7732
- return;
7733
- }
7734
- resolve(address.port);
7735
- });
7736
- });
7737
- server.unref();
7738
- let closed = false;
7739
- return {
7740
- port,
7741
- close: () => new Promise((resolve, reject) => {
7742
- if (closed) {
7743
- resolve();
7744
- return;
8512
+ }
8513
+ async #load() {
8514
+ let opened;
8515
+ try {
8516
+ opened = await openOperationalHealthFile(path3__default.dirname(this.#filePath));
8517
+ } catch (err) {
8518
+ throw new Error("operational health state could not be read");
8519
+ }
8520
+ if (!opened) return { version: 1, state: "healthy", failures: [], crashes: [] };
8521
+ try {
8522
+ const inspected = await readOperationalHealthHandle(opened.handle, opened.stat);
8523
+ if (inspected.status !== "read") throw new Error(inspected.reason);
8524
+ let parsed;
8525
+ try {
8526
+ parsed = JSON.parse(inspected.raw);
8527
+ } catch {
8528
+ throw new Error("operational health state is corrupt JSON");
7745
8529
  }
7746
- closed = true;
7747
- server.close((err) => err ? reject(err) : resolve());
7748
- })
7749
- };
7750
- }
7751
- async function probeStoreMutex(endpoint, identity) {
7752
- return new Promise((resolve) => {
7753
- const socket = createConnection(endpoint);
7754
- let settled = false;
7755
- let raw = "";
7756
- const finish = (result) => {
7757
- if (settled) return;
7758
- settled = true;
7759
- clearTimeout(timer);
7760
- socket.removeAllListeners();
7761
- socket.destroy();
7762
- resolve(result);
7763
- };
7764
- const timer = setTimeout(() => finish({ kind: "occupied" }), STORE_MUTEX_PROBE_TIMEOUT_MS);
7765
- socket.setEncoding("utf8");
7766
- socket.on("data", (chunk) => {
7767
- raw += chunk;
7768
- if (raw.length > STORE_MUTEX_ID_PREFIX.length + 64 + 1) finish({ kind: "occupied" });
7769
- });
7770
- socket.once("end", () => finish(raw.trimEnd() === `${STORE_MUTEX_ID_PREFIX}${identity}` ? { kind: "holder" } : { kind: "occupied" }));
7771
- socket.once(
7772
- "error",
7773
- (err) => finish(err.code === "ECONNREFUSED" || err.code === "ENOENT" ? { kind: "unbound" } : { kind: "occupied" })
7774
- );
7775
- });
7776
- }
7777
- async function clearStaleStoreMutexSocket(endpoint, identity) {
7778
- let stat;
7779
- try {
7780
- stat = await promises.lstat(endpoint);
7781
- } catch (err) {
7782
- if (err.code === "ENOENT") return;
7783
- throw err;
8530
+ if (!isHealthFile(parsed)) throw new Error("operational health state has an invalid shape");
8531
+ return parsed;
8532
+ } finally {
8533
+ await opened.handle.close();
8534
+ }
7784
8535
  }
7785
- if (!stat.isSocket()) throw new Error("store mutation lock path exists but is not a socket");
7786
- if ((await probeStoreMutex(endpoint, identity)).kind !== "unbound") throw new DaemonOwnerActiveError("unknown");
7787
- await promises.rm(endpoint, { force: true });
7788
- }
7789
- async function assertOwnedPrivateDir2(dir) {
7790
- const uid = process.getuid?.();
7791
- if (uid === void 0) return;
7792
- const stat = await promises.lstat(dir);
7793
- if (stat.isSymbolicLink() || stat.uid !== uid) {
7794
- throw new Error(`refusing to bind the store mutation lock under "${dir}": not a real directory owned by this process's own uid`);
8536
+ #prune(now) {
8537
+ if (!this.#state) return;
8538
+ const nowMs = now.getTime();
8539
+ const cutoff = nowMs - this.#windowMs;
8540
+ this.#state.failures = this.#state.failures.filter((event) => {
8541
+ const eventMs = Date.parse(event.at);
8542
+ return eventMs >= cutoff && eventMs <= nowMs;
8543
+ });
8544
+ if (this.#state.state === "recovering" && this.#state.failures.length < this.#failureThreshold) {
8545
+ this.#state.state = "healthy";
8546
+ }
7795
8547
  }
7796
- }
7797
- async function acquireStoreMutex(canonicalStoreDir) {
7798
- const identity = storeMutexIdentity(canonicalStoreDir);
7799
- const endpoint = storeMutexEndpoint(canonicalStoreDir, identity);
7800
- const isPipe = process.platform === "win32";
7801
- if (!isPipe) {
7802
- const endpointDir = path.dirname(endpoint);
7803
- if (endpointDir !== canonicalStoreDir) {
7804
- await ensureSecureDir(endpointDir);
7805
- await assertOwnedPrivateDir2(endpointDir);
8548
+ async #persist() {
8549
+ if (!this.#state) return;
8550
+ const body = JSON.stringify(this.#state, null, 2);
8551
+ this.#writeTail = this.#writeTail.then(async () => {
8552
+ await ensureSecureDir(path3__default.dirname(this.#filePath));
8553
+ await atomicWriteFile(this.#filePath, body, { mode: 384, fsync: true });
8554
+ });
8555
+ try {
8556
+ await this.#writeTail;
8557
+ } catch (err) {
8558
+ this.#unavailableReason = "operational health state could not be persisted";
8559
+ throw err;
7806
8560
  }
7807
- await clearStaleStoreMutexSocket(endpoint, identity);
7808
8561
  }
7809
- const server = createServer((socket) => endOwnershipProbe(socket, `${STORE_MUTEX_ID_PREFIX}${identity}
7810
- `));
8562
+ };
8563
+ async function inspectOperationalHealthHandle(handle, expected) {
8564
+ const read = await readOperationalHealthHandle(handle, expected);
8565
+ if (read.status !== "read") return read;
8566
+ const { raw, sizeBytes } = read;
8567
+ let parsed;
7811
8568
  try {
7812
- await new Promise((resolve, reject) => {
7813
- server.once("error", reject);
7814
- server.listen(endpoint, () => {
7815
- server.removeListener("error", reject);
7816
- resolve();
7817
- });
7818
- });
7819
- } catch (err) {
7820
- if (err.code === "EADDRINUSE") throw new DaemonOwnerActiveError("unknown");
7821
- throw err;
8569
+ parsed = JSON.parse(raw);
8570
+ } catch {
8571
+ return { status: "corrupt", sizeBytes, reason: "operational health state is corrupt JSON" };
8572
+ }
8573
+ if (!isHealthFile(parsed)) {
8574
+ return { status: "corrupt", sizeBytes, reason: "operational health state has an invalid shape" };
7822
8575
  }
7823
- if (!isPipe) await promises.chmod(endpoint, 384).catch(() => void 0);
7824
- server.unref();
7825
- let closed = false;
7826
8576
  return {
7827
- endpoint,
7828
- close: async () => {
7829
- if (closed) return;
7830
- closed = true;
7831
- await new Promise((resolve, reject) => server.close((err) => err ? reject(err) : resolve()));
7832
- if (!isPipe) await promises.rm(endpoint, { force: true }).catch(() => void 0);
7833
- }
8577
+ status: "valid",
8578
+ sizeBytes,
8579
+ state: parsed.state,
8580
+ failureCount: parsed.failures.length,
8581
+ crashCount: parsed.crashes.length,
8582
+ ...parsed.currentRun ? { currentRunStartedAt: new Date(Date.parse(parsed.currentRun.startedAt)).toISOString() } : {},
8583
+ ...parsed.crashes.at(-1) ? { lastCrashAt: new Date(Date.parse(parsed.crashes.at(-1).detectedAt)).toISOString() } : {}
7834
8584
  };
7835
8585
  }
7836
- async function reclaimExistsAndIsActive(reclaimPath) {
7837
- let stat;
8586
+ async function readOperationalHealthHandle(handle, expected) {
8587
+ let before;
7838
8588
  try {
7839
- stat = await promises.lstat(reclaimPath);
7840
- } catch (err) {
7841
- if (err.code === "ENOENT") return false;
7842
- throw err;
8589
+ before = await handle.stat({ bigint: true });
8590
+ } catch {
8591
+ return { status: "unavailable", reason: "operational health state could not be inspected" };
8592
+ }
8593
+ const sizeBytes = Number(before.size);
8594
+ if (expected && !sameFileState2(before, expected)) {
8595
+ return { status: "unavailable", sizeBytes, reason: "operational health handle identity changed before inspection" };
8596
+ }
8597
+ if (!before.isFile()) {
8598
+ return { status: "unavailable", sizeBytes, reason: "operational health state is not a regular file" };
8599
+ }
8600
+ if (sizeBytes > MAX_OPERATIONAL_HEALTH_FILE_BYTES) {
8601
+ return {
8602
+ status: "unavailable",
8603
+ sizeBytes,
8604
+ reason: "operational health state exceeds the 1 MiB read limit; corruption is unconfirmed"
8605
+ };
7843
8606
  }
7844
- if (!stat.isFile()) throw new Error("store mutation reclaim marker is not a regular file");
7845
- const owner = await readOwner(reclaimPath);
7846
- if (owner) return processOwnsRecord(owner);
7847
- return Date.now() - stat.mtimeMs <= RECLAIM_MALFORMED_GRACE_MS;
7848
- }
7849
- async function createOwner(filePath, record) {
7850
- const tempPath = `${filePath}.${record.pid}.${record.nonce}.tmp`;
7851
- let handle;
7852
8607
  try {
7853
- handle = await promises.open(tempPath, "wx", 384);
7854
- await handle.writeFile(`${JSON.stringify(record)}
7855
- `, "utf8");
7856
- await handle.chmod(384);
7857
- await handle.sync();
7858
- await handle.close();
7859
- try {
7860
- await promises.link(tempPath, filePath);
7861
- return true;
7862
- } catch (err) {
7863
- if (err.code === "EEXIST") return false;
7864
- throw err;
8608
+ const buffer = Buffer.alloc(sizeBytes);
8609
+ const { bytesRead } = await handle.read(buffer, 0, sizeBytes, 0);
8610
+ const after = await handle.stat({ bigint: true });
8611
+ if (bytesRead !== sizeBytes || after.size !== before.size || after.mtimeNs !== before.mtimeNs || after.ctimeNs !== before.ctimeNs) {
8612
+ return { status: "unavailable", sizeBytes: Number(after.size), reason: "operational health state changed during inspection" };
7865
8613
  }
7866
- } finally {
7867
- await handle?.close().catch(() => void 0);
7868
- await promises.rm(tempPath, { force: true }).catch(() => void 0);
8614
+ return { status: "read", raw: buffer.toString("utf8"), sizeBytes };
8615
+ } catch {
8616
+ return { status: "unavailable", sizeBytes, reason: "operational health state could not be read" };
7869
8617
  }
7870
8618
  }
7871
- async function acquireDaemonOwner(storeDir, role, clock = () => /* @__PURE__ */ new Date()) {
7872
- await ensureSecureDir(storeDir);
7873
- const canonicalStoreDir = await promises.realpath(storeDir);
7874
- const mutex = await acquireStoreMutex(canonicalStoreDir);
7875
- let liveness;
8619
+ async function inspectOperationalHealthFile(storeDir) {
8620
+ let opened;
7876
8621
  try {
7877
- liveness = await createLivenessListener();
7878
- } catch (err) {
7879
- await mutex.close().catch(() => void 0);
7880
- throw err;
8622
+ opened = await openOperationalHealthFile(storeDir);
8623
+ } catch {
8624
+ return { status: "unavailable", reason: "operational health state could not be opened safely" };
7881
8625
  }
7882
- const ownerPath = path.join(storeDir, DAEMON_OWNER_FILENAME);
7883
- const reclaimPath = path.join(storeDir, RECLAIM_FILENAME);
7884
- const record = {
7885
- version: 2,
7886
- pid: process.pid,
7887
- nonce: randomUUID(),
7888
- role,
7889
- acquiredAt: clock().toISOString(),
7890
- processStartedAt: SELF_PROCESS_STARTED_AT,
7891
- livenessPort: liveness.port
7892
- };
8626
+ if (!opened) return { status: "missing" };
7893
8627
  try {
7894
- for (; ; ) {
7895
- if (await reclaimExistsAndIsActive(reclaimPath)) {
7896
- throw new Error("store mutation lease is being reclaimed; retry after the current operation finishes");
7897
- }
7898
- await promises.rm(reclaimPath, { force: true });
7899
- if (await createOwner(ownerPath, record)) {
7900
- let released = false;
7901
- return {
7902
- release: async () => {
7903
- if (released) return;
7904
- const current = await readOwner(ownerPath);
7905
- if (current?.nonce !== record.nonce) {
7906
- throw new Error("store mutation lease identity changed before release");
7907
- }
7908
- await promises.rm(ownerPath);
7909
- released = true;
7910
- try {
7911
- await liveness.close();
7912
- } finally {
7913
- await mutex.close();
7914
- }
7915
- }
7916
- };
7917
- }
7918
- const existing = await readOwner(ownerPath);
7919
- if (existing && await processOwnsRecord(existing)) throw new DaemonOwnerActiveError(existing.role);
7920
- if (!await createOwner(reclaimPath, record)) {
7921
- throw new Error("store mutation lease is being reclaimed; retry after the current operation finishes");
7922
- }
7923
- try {
7924
- const rechecked = await readOwner(ownerPath);
7925
- if (rechecked && await processOwnsRecord(rechecked)) throw new DaemonOwnerActiveError(rechecked.role);
7926
- if (rechecked || await promises.stat(ownerPath).then(() => true, (err) => {
7927
- if (err.code === "ENOENT") return false;
7928
- throw err;
7929
- })) {
7930
- await promises.rm(ownerPath);
7931
- }
7932
- } finally {
7933
- const currentReclaim = await readOwner(reclaimPath);
7934
- if (currentReclaim?.nonce === record.nonce) await promises.rm(reclaimPath, { force: true });
7935
- }
7936
- }
7937
- } catch (err) {
7938
- await liveness.close().catch(() => void 0);
7939
- await mutex.close().catch(() => void 0);
7940
- throw err;
8628
+ return await inspectOperationalHealthHandle(opened.handle, opened.stat);
8629
+ } finally {
8630
+ await opened.handle.close();
7941
8631
  }
7942
8632
  }
8633
+ function isHealthFile(value) {
8634
+ if (typeof value !== "object" || value === null) return false;
8635
+ const candidate = value;
8636
+ const keys = Object.keys(candidate);
8637
+ if (keys.some((key) => !["version", "state", "failures", "crashes", "currentRun"].includes(key))) return false;
8638
+ if (candidate.version !== 1 || !["healthy", "degraded", "recovering"].includes(String(candidate.state))) return false;
8639
+ if (!Array.isArray(candidate.failures) || !candidate.failures.every(isFailureEvent)) return false;
8640
+ if (!Array.isArray(candidate.crashes) || !candidate.crashes.every(isCrashRecord)) return false;
8641
+ if (candidate.currentRun !== void 0 && !isRunMarker(candidate.currentRun)) return false;
8642
+ return true;
8643
+ }
8644
+ function isFailureEvent(value) {
8645
+ if (typeof value !== "object" || value === null) return false;
8646
+ const event = value;
8647
+ return Object.keys(event).every((key) => ["at", "source"].includes(key)) && typeof event.at === "string" && Number.isFinite(Date.parse(event.at)) && ["reconnect", "upload", "maintenance", "lifecycle"].includes(String(event.source));
8648
+ }
8649
+ function isCrashRecord(value) {
8650
+ if (typeof value !== "object" || value === null) return false;
8651
+ const record = value;
8652
+ return Object.keys(record).every((key) => ["detectedAt", "previousRunStartedAt"].includes(key)) && typeof record.detectedAt === "string" && Number.isFinite(Date.parse(record.detectedAt)) && typeof record.previousRunStartedAt === "string" && Number.isFinite(Date.parse(record.previousRunStartedAt));
8653
+ }
8654
+ function isRunMarker(value) {
8655
+ if (typeof value !== "object" || value === null) return false;
8656
+ const marker = value;
8657
+ return Object.keys(marker).every((key) => ["id", "startedAt", "pid"].includes(key)) && typeof marker.id === "string" && typeof marker.startedAt === "string" && Number.isFinite(Date.parse(marker.startedAt)) && typeof marker.pid === "number" && Number.isSafeInteger(marker.pid);
8658
+ }
7943
8659
 
7944
8660
  // src/daemon/runtime-capabilities.ts
7945
8661
  function toRuntimeInfoCapabilities(caps) {
@@ -7958,7 +8674,7 @@ var CursorStore = class {
7958
8674
  storeDir;
7959
8675
  fileFor(serverUrl, deviceId) {
7960
8676
  const key = createHash("sha256").update(`${serverUrl}::${deviceId}`).digest("hex").slice(0, 32);
7961
- return path.join(this.storeDir, `cursor-${key}.json`);
8677
+ return path3__default.join(this.storeDir, `cursor-${key}.json`);
7962
8678
  }
7963
8679
  async load(serverUrl, deviceId) {
7964
8680
  let raw;
@@ -7978,7 +8694,7 @@ var CursorStore = class {
7978
8694
  }
7979
8695
  async save(serverUrl, deviceId, cursor) {
7980
8696
  const file = this.fileFor(serverUrl, deviceId);
7981
- await promises.mkdir(path.dirname(file), { recursive: true, mode: 448 });
8697
+ await promises.mkdir(path3__default.dirname(file), { recursive: true, mode: 448 });
7982
8698
  await atomicWriteFile(file, JSON.stringify({ cursor }));
7983
8699
  }
7984
8700
  /** 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 +9024,7 @@ var SessionWorkspaceStore = class {
8308
9024
  */
8309
9025
  queue = Promise.resolve();
8310
9026
  constructor(storeDir) {
8311
- this.filePath = path.join(storeDir, "session-workspaces.json");
9027
+ this.filePath = path3__default.join(storeDir, "session-workspaces.json");
8312
9028
  }
8313
9029
  async get(sessionRef) {
8314
9030
  return this.enqueue(async () => {
@@ -8366,7 +9082,7 @@ var SessionWorkspaceStore = class {
8366
9082
  }
8367
9083
  }
8368
9084
  async save(all) {
8369
- const dir = path.dirname(this.filePath);
9085
+ const dir = path3__default.dirname(this.filePath);
8370
9086
  await promises.mkdir(dir, { recursive: true, mode: 448 });
8371
9087
  const tmpPath = `${this.filePath}.${process.pid}-${tmpSeq2++}.tmp`;
8372
9088
  try {
@@ -8471,9 +9187,9 @@ function isSqliteAvailable() {
8471
9187
  }
8472
9188
  }
8473
9189
  var SECURE_FILE_MODE = 384;
8474
- function openJournalDatabase(path34, busyTimeoutMs, faults) {
9190
+ function openJournalDatabase(path36, busyTimeoutMs, faults) {
8475
9191
  const { DatabaseSync } = loadSqliteModule();
8476
- const db = new DatabaseSync(path34, { timeout: busyTimeoutMs });
9192
+ const db = new DatabaseSync(path36, { timeout: busyTimeoutMs });
8477
9193
  try {
8478
9194
  faults?.onStep?.("after-open");
8479
9195
  db.exec("PRAGMA auto_vacuum = INCREMENTAL;");
@@ -8616,9 +9332,9 @@ var RECEIVED_STATE = "received";
8616
9332
  function byteLength(value) {
8617
9333
  return Buffer.byteLength(value, "utf8");
8618
9334
  }
8619
- function fileBytes(path34) {
9335
+ function fileBytes(path36) {
8620
9336
  try {
8621
- return statSync(path34).size;
9337
+ return statSync(path36).size;
8622
9338
  } catch {
8623
9339
  return 0;
8624
9340
  }
@@ -9950,8 +10666,8 @@ function estimateEventBytes(event) {
9950
10666
  }
9951
10667
  async function openArtifact(workspaceDir, name) {
9952
10668
  const realWorkspaceDir = await promises.realpath(workspaceDir).catch(() => workspaceDir);
9953
- const candidate = path.resolve(realWorkspaceDir, name);
9954
- const prefix = realWorkspaceDir.endsWith(path.sep) ? realWorkspaceDir : realWorkspaceDir + path.sep;
10669
+ const candidate = path3__default.resolve(realWorkspaceDir, name);
10670
+ const prefix = realWorkspaceDir.endsWith(path3__default.sep) ? realWorkspaceDir : realWorkspaceDir + path3__default.sep;
9955
10671
  if (candidate !== realWorkspaceDir && !candidate.startsWith(prefix)) {
9956
10672
  return { ok: false, reason: `artifact name "${name}" resolves outside the task workspace \u2014 rejected` };
9957
10673
  }
@@ -10084,6 +10800,12 @@ var TaskRunner = class {
10084
10800
  * cancellation intent.
10085
10801
  */
10086
10802
  finishedTaskIds = /* @__PURE__ */ new Set();
10803
+ /**
10804
+ * Bounded local receive dedup for strict legacy declines. A decline is not a
10805
+ * task terminal receipt, so it must never enter `finishedTaskIds`; retaining
10806
+ * it separately keeps replay idempotent without claiming or finishing work.
10807
+ */
10808
+ strictDeclinedTaskIds = /* @__PURE__ */ new Set();
10087
10809
  /**
10088
10810
  * M4 Phase 2 (daemon control socket `shutdown` RPC): set once by
10089
10811
  * {@link stopAcceptingOffers}, checked at the very top of `handleOffer` —
@@ -10303,7 +11025,7 @@ var TaskRunner = class {
10303
11025
  }
10304
11026
  }
10305
11027
  async handleOffer(taskId, payload, strictAgentOffer) {
10306
- if (this.tasks.has(taskId) || this.finishedTaskIds.has(taskId)) {
11028
+ if (this.tasks.has(taskId) || this.finishedTaskIds.has(taskId) || this.strictDeclinedTaskIds.has(taskId)) {
10307
11029
  return;
10308
11030
  }
10309
11031
  let agentRef;
@@ -10334,15 +11056,6 @@ var TaskRunner = class {
10334
11056
  return;
10335
11057
  }
10336
11058
  }
10337
- const guarded = this.deps.admissionGuard?.({ taskId, payload });
10338
- if (guarded !== void 0 && !guarded.admit) {
10339
- decline(guarded.reason, guarded.retryable);
10340
- return;
10341
- }
10342
- if (this.stoppingOffers) {
10343
- decline("daemon is shutting down", true);
10344
- return;
10345
- }
10346
11059
  this.inFlightOffers.add(taskId);
10347
11060
  let agentBinding;
10348
11061
  let agentLeaseTransferred = false;
@@ -10353,6 +11066,20 @@ var TaskRunner = class {
10353
11066
  decline(reason ? `cancelled before claim: ${reason}` : "cancelled before claim", false);
10354
11067
  return;
10355
11068
  }
11069
+ if (this.deps.strictAgentOnly === true && !strictAgentOffer) {
11070
+ this.addStrictDeclinedTaskId(taskId);
11071
+ decline("strict Agent-only daemon refuses legacy task offers", false);
11072
+ return;
11073
+ }
11074
+ const guarded = this.deps.admissionGuard?.({ taskId, payload });
11075
+ if (guarded !== void 0 && !guarded.admit) {
11076
+ decline(guarded.reason, guarded.retryable);
11077
+ return;
11078
+ }
11079
+ if (this.stoppingOffers) {
11080
+ decline("daemon is shutting down", true);
11081
+ return;
11082
+ }
10356
11083
  if (payload.limits?.maxTokens !== void 0) {
10357
11084
  decline(
10358
11085
  `offer requests limits.maxTokens (${payload.limits.maxTokens}), which no bundled runtime adapter enforces \u2014 declining fail-closed rather than silently ignoring it`,
@@ -10463,7 +11190,7 @@ var TaskRunner = class {
10463
11190
  const sameProtocolTask = ledger?.taskId === taskId;
10464
11191
  const interruptedOldTask = ledger?.phase === "interrupted" && sameProtocolTask;
10465
11192
  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 || path.resolve(ledger.workspaceDir) !== path.resolve(known.workspaceDir) || interruptedOldTask || activeDifferentTask) {
11193
+ if (!known || known.workspaceKind !== "git" || !known.gitWorkspaceId || !ledger || ledger.workspaceId !== known.gitWorkspaceId || ledger.sessionRef !== sessionRef || path3__default.resolve(ledger.workspaceDir) !== path3__default.resolve(known.workspaceDir) || interruptedOldTask || activeDifferentTask) {
10467
11194
  decline("session is incompatible with Git workspace mode", true);
10468
11195
  return;
10469
11196
  }
@@ -10478,7 +11205,7 @@ var TaskRunner = class {
10478
11205
  return;
10479
11206
  }
10480
11207
  } else {
10481
- workspaceDir = path.join(this.deps.workspaceRoot, taskId);
11208
+ workspaceDir = path3__default.join(this.deps.workspaceRoot, taskId);
10482
11209
  gitWorkspaceId = randomUUID();
10483
11210
  }
10484
11211
  try {
@@ -10489,7 +11216,7 @@ var TaskRunner = class {
10489
11216
  }
10490
11217
  } else if (!this.deps.gitWorkspaceManager && !this.deps.gitWorkspaceStore) {
10491
11218
  known = sessionRef ? await this.deps.sessionWorkspaces.get(sessionRef) : void 0;
10492
- workspaceDir = known?.workspaceDir ?? path.join(this.deps.workspaceRoot, taskId);
11219
+ workspaceDir = known?.workspaceDir ?? path3__default.join(this.deps.workspaceRoot, taskId);
10493
11220
  plainWorkspaceNeedsResolve = true;
10494
11221
  } else {
10495
11222
  decline("workspace mode is unavailable", true);
@@ -11874,9 +12601,16 @@ var TaskRunner = class {
11874
12601
  if (oldest !== void 0) this.finishedTaskIds.delete(oldest);
11875
12602
  }
11876
12603
  }
12604
+ addStrictDeclinedTaskId(taskId) {
12605
+ this.strictDeclinedTaskIds.add(taskId);
12606
+ if (this.strictDeclinedTaskIds.size > MAX_TRACKED_TASK_IDS) {
12607
+ const oldest = this.strictDeclinedTaskIds.values().next().value;
12608
+ if (oldest !== void 0) this.strictDeclinedTaskIds.delete(oldest);
12609
+ }
12610
+ }
11877
12611
  /** `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
12612
  async resolveWorkspaceDir(taskId, reuseDir) {
11879
- const dir = reuseDir ?? path.join(this.deps.workspaceRoot, taskId);
12613
+ const dir = reuseDir ?? path3__default.join(this.deps.workspaceRoot, taskId);
11880
12614
  await promises.mkdir(dir, { recursive: true });
11881
12615
  return dir;
11882
12616
  }
@@ -12014,7 +12748,7 @@ var encoder2 = new TextEncoder();
12014
12748
  function eventBytes(event) {
12015
12749
  return encoder2.encode(JSON.stringify(event)).length;
12016
12750
  }
12017
- var AGENT_EGRESS_DIRECTORY = path.join(".byok", "egress");
12751
+ var AGENT_EGRESS_DIRECTORY = path3__default.join(".byok", "egress");
12018
12752
  var AGENT_RELIABLE_SPOOL_FILENAME = "reliable-v1.jsonl";
12019
12753
  var AgentReliableSpoolError = class extends Error {
12020
12754
  constructor(message) {
@@ -12125,9 +12859,9 @@ var AgentReliableSpool = class _AgentReliableSpool {
12125
12859
  logEntries = 0;
12126
12860
  writeTail = Promise.resolve();
12127
12861
  static async open(homeDir) {
12128
- const directory = path.join(homeDir, AGENT_EGRESS_DIRECTORY);
12862
+ const directory = path3__default.join(homeDir, AGENT_EGRESS_DIRECTORY);
12129
12863
  await ensureSecureDir(directory);
12130
- const spool = new _AgentReliableSpool(homeDir, path.join(directory, AGENT_RELIABLE_SPOOL_FILENAME));
12864
+ const spool = new _AgentReliableSpool(homeDir, path3__default.join(directory, AGENT_RELIABLE_SPOOL_FILENAME));
12131
12865
  await spool.load();
12132
12866
  return spool;
12133
12867
  }
@@ -12590,7 +13324,7 @@ var AgentEgressController = class {
12590
13324
  }
12591
13325
  /** Re-open every existing Agent-local spool before retrying stable records after restart. */
12592
13326
  async recover(agentsRoot) {
12593
- if (!path.isAbsolute(agentsRoot)) throw new Error("Agent egress recovery root must be absolute");
13327
+ if (!path3__default.isAbsolute(agentsRoot)) throw new Error("Agent egress recovery root must be absolute");
12594
13328
  if (!this.active) throw new Error("Agent egress recovery requires an active authenticated enrollment");
12595
13329
  if (this.options.tenantId === void 0) {
12596
13330
  throw new Error("Agent egress recovery requires one authenticated tenant authority");
@@ -12605,14 +13339,14 @@ var AgentEgressController = class {
12605
13339
  }
12606
13340
  for (const entry of entries) {
12607
13341
  if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
12608
- const homeDir = path.join(canonicalAgentsRoot, entry.name);
13342
+ const homeDir = path3__default.join(canonicalAgentsRoot, entry.name);
12609
13343
  const canonicalHome = await promises.realpath(homeDir);
12610
- const relativeHome = path.relative(canonicalAgentsRoot, canonicalHome);
12611
- if (relativeHome !== entry.name || relativeHome.includes(path.sep) || path.isAbsolute(relativeHome)) {
13344
+ const relativeHome = path3__default.relative(canonicalAgentsRoot, canonicalHome);
13345
+ if (relativeHome !== entry.name || relativeHome.includes(path3__default.sep) || path3__default.isAbsolute(relativeHome)) {
12612
13346
  throw new Error(`Agent egress recovery home escaped the canonical agents root: ${entry.name}`);
12613
13347
  }
12614
13348
  try {
12615
- await promises.lstat(path.join(homeDir, AGENT_EGRESS_DIRECTORY));
13349
+ await promises.lstat(path3__default.join(homeDir, AGENT_EGRESS_DIRECTORY));
12616
13350
  } catch (error) {
12617
13351
  if (error.code === "ENOENT") continue;
12618
13352
  throw error;
@@ -12699,12 +13433,12 @@ function isAgentRef(value) {
12699
13433
  }
12700
13434
  function isCanonicalRelativeTarget(value) {
12701
13435
  if (value === "[invalid-target]") return true;
12702
- if (path.isAbsolute(value) || value.includes("\\")) return false;
13436
+ if (path3__default.isAbsolute(value) || value.includes("\\")) return false;
12703
13437
  const segments = value.split("/");
12704
13438
  return value.length > 0 && segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
12705
13439
  }
12706
13440
  function validateIdentity(value, label) {
12707
- if (!isRecord4(value) || !isAgentRef(value.agentRef) || !isNonEmptyString(value.sessionRef) || !isNonEmptyString(value.runtimeId) || !isNonEmptyString(value.cwd) || !path.isAbsolute(value.cwd)) {
13441
+ if (!isRecord4(value) || !isAgentRef(value.agentRef) || !isNonEmptyString(value.sessionRef) || !isNonEmptyString(value.runtimeId) || !isNonEmptyString(value.cwd) || !path3__default.isAbsolute(value.cwd)) {
12708
13442
  throw new AgentContentAuditStoreError(`${label} has an invalid exact Agent/session identity`);
12709
13443
  }
12710
13444
  return Object.freeze({
@@ -12714,7 +13448,7 @@ function validateIdentity(value, label) {
12714
13448
  }),
12715
13449
  sessionRef: value.sessionRef,
12716
13450
  runtimeId: value.runtimeId,
12717
- cwd: path.resolve(value.cwd)
13451
+ cwd: path3__default.resolve(value.cwd)
12718
13452
  });
12719
13453
  }
12720
13454
  function validateReceipt(value) {
@@ -12796,16 +13530,16 @@ function assertUniqueRequestIds(entries) {
12796
13530
  }
12797
13531
  }
12798
13532
  function assertAbsoluteFilePath(filePath) {
12799
- if (typeof filePath !== "string" || filePath.length === 0 || !path.isAbsolute(filePath)) {
13533
+ if (typeof filePath !== "string" || filePath.length === 0 || !path3__default.isAbsolute(filePath)) {
12800
13534
  throw new AgentContentAuditStoreError("content audit path must be absolute");
12801
13535
  }
12802
13536
  if (/[\u0000\r\n]/u.test(filePath)) {
12803
13537
  throw new AgentContentAuditStoreError("content audit path must not contain NUL or line breaks");
12804
13538
  }
12805
- return path.resolve(filePath);
13539
+ return path3__default.resolve(filePath);
12806
13540
  }
12807
13541
  async function ensureDirectoryNoSymlink2(directory) {
12808
- const absolute = path.resolve(directory);
13542
+ const absolute = path3__default.resolve(directory);
12809
13543
  await promises.mkdir(absolute, { recursive: true, mode: 448 });
12810
13544
  const stat = await promises.lstat(absolute);
12811
13545
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
@@ -12839,12 +13573,12 @@ var AgentContentAuditStore = class _AgentContentAuditStore {
12839
13573
  /** The daemon may address this ledger only through an AgentHomeLayout resolution. */
12840
13574
  static forCanonicalAgentHome(canonicalHome) {
12841
13575
  const home = assertAbsoluteFilePath(canonicalHome);
12842
- return new _AgentContentAuditStore(path.join(home, AGENT_HOME_INTERNAL_DIRECTORY, AGENT_CONTENT_AUDIT_FILENAME));
13576
+ return new _AgentContentAuditStore(path3__default.join(home, AGENT_HOME_INTERNAL_DIRECTORY, AGENT_CONTENT_AUDIT_FILENAME));
12843
13577
  }
12844
13578
  async append(receipt) {
12845
13579
  const validated = validateReceipt(receipt);
12846
13580
  return this.enqueue(async () => {
12847
- await ensureDirectoryNoSymlink2(path.dirname(this.filePath));
13581
+ await ensureDirectoryNoSymlink2(path3__default.dirname(this.filePath));
12848
13582
  await assertAuditFile(this.filePath);
12849
13583
  const entries = await this.readAllUnlocked();
12850
13584
  const prior = entries.find((entry) => entry.requestId === validated.requestId);
@@ -12939,6 +13673,63 @@ var AgentContentAuditStore = class _AgentContentAuditStore {
12939
13673
  return result;
12940
13674
  }
12941
13675
  };
13676
+ var AgentHomeProjectionCompletionError = class extends Error {
13677
+ constructor(message, options) {
13678
+ super(message, options);
13679
+ this.name = "AgentHomeProjectionCompletionError";
13680
+ }
13681
+ };
13682
+ function sameAgentRef2(left, right) {
13683
+ return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
13684
+ }
13685
+ var AgentHomeProjectionCompletionClient = class {
13686
+ constructor(options) {
13687
+ this.options = options;
13688
+ }
13689
+ options;
13690
+ async complete(input) {
13691
+ const completion = AgentHomeProjectionCompletionRequestSchema.parse(input);
13692
+ const url = new URL(
13693
+ byokAgentHomeProjectionCompletionPath(completion.requestId),
13694
+ toHttpBase(this.options.serverUrl)
13695
+ );
13696
+ let response;
13697
+ try {
13698
+ response = await authedFetch(
13699
+ url,
13700
+ {
13701
+ method: "PUT",
13702
+ headers: { "content-type": "application/json" },
13703
+ body: JSON.stringify(completion)
13704
+ },
13705
+ this.options.auth
13706
+ );
13707
+ } catch (error) {
13708
+ throw new AgentHomeProjectionCompletionError("Agent-home projection completion transport failed", {
13709
+ cause: error
13710
+ });
13711
+ }
13712
+ if (!response.ok) {
13713
+ throw new AgentHomeProjectionCompletionError(
13714
+ `Agent-home projection completion was rejected with HTTP ${response.status}`
13715
+ );
13716
+ }
13717
+ let readback;
13718
+ try {
13719
+ readback = AgentHomeProjectionReadbackSchema.parse(await response.json());
13720
+ } catch (error) {
13721
+ throw new AgentHomeProjectionCompletionError("Agent-home projection completion readback is invalid", {
13722
+ cause: error
13723
+ });
13724
+ }
13725
+ 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) {
13726
+ throw new AgentHomeProjectionCompletionError(
13727
+ "Agent-home projection completion readback does not exactly match the authenticated request"
13728
+ );
13729
+ }
13730
+ return readback;
13731
+ }
13732
+ };
12942
13733
  var AGENT_CONTENT_READ_SURFACES = ["workspace", "transcript", "artifact"];
12943
13734
  var AGENT_CONTENT_READ_CAPABILITIES = Object.freeze({
12944
13735
  workspace: AGENT_CONTENT_WORKSPACE_READ_CAPABILITY,
@@ -13042,8 +13833,8 @@ function normalizeIdentity(value, field) {
13042
13833
  const sessionRef = nonEmptyString(value.sessionRef, `${field}.sessionRef`);
13043
13834
  const runtimeId = nonEmptyString(value.runtimeId, `${field}.runtimeId`);
13044
13835
  const cwd = nonEmptyString(value.cwd, `${field}.cwd`);
13045
- if (!path.isAbsolute(cwd)) throw new AgentContentReadPolicyError(`${field}.cwd must be absolute`);
13046
- return Object.freeze({ agentRef, sessionRef, runtimeId, cwd: path.resolve(cwd) });
13836
+ if (!path3__default.isAbsolute(cwd)) throw new AgentContentReadPolicyError(`${field}.cwd must be absolute`);
13837
+ return Object.freeze({ agentRef, sessionRef, runtimeId, cwd: path3__default.resolve(cwd) });
13047
13838
  }
13048
13839
  function createAgentContentReadPolicy(input) {
13049
13840
  if (!isRecord5(input) || input.enabled !== true) {
@@ -13071,10 +13862,10 @@ function createAgentContentReadPolicy(input) {
13071
13862
  root = Object.freeze({ kind: "agent-home" });
13072
13863
  } else if (input.root.kind === "runtime-allowlisted") {
13073
13864
  const configuredRoot = nonEmptyString(input.root.root, "contentRead.root.root");
13074
- if (!path.isAbsolute(configuredRoot)) {
13865
+ if (!path3__default.isAbsolute(configuredRoot)) {
13075
13866
  throw new AgentContentReadPolicyError("contentRead.root.root must be absolute");
13076
13867
  }
13077
- root = Object.freeze({ kind: "runtime-allowlisted", root: path.resolve(configuredRoot) });
13868
+ root = Object.freeze({ kind: "runtime-allowlisted", root: path3__default.resolve(configuredRoot) });
13078
13869
  } else {
13079
13870
  throw new AgentContentReadPolicyError("contentRead.root.kind is not supported");
13080
13871
  }
@@ -13097,11 +13888,11 @@ function createAgentContentReadPolicy(input) {
13097
13888
  });
13098
13889
  }
13099
13890
  function isWithin2(root, candidate) {
13100
- const relative = path.relative(root, candidate);
13101
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
13891
+ const relative = path3__default.relative(root, candidate);
13892
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path3__default.sep}`) && !path3__default.isAbsolute(relative);
13102
13893
  }
13103
13894
  function isPortableAbsoluteTarget(value) {
13104
- return path.isAbsolute(value) || /^[a-z]:[\\/]/iu.test(value) || /^[/\\]/u.test(value);
13895
+ return path3__default.isAbsolute(value) || /^[a-z]:[\\/]/iu.test(value) || /^[/\\]/u.test(value);
13105
13896
  }
13106
13897
  function canonicalAuditTarget(value) {
13107
13898
  if (typeof value !== "string" || value.length === 0 || /[\u0000\r\n]/u.test(value) || isPortableAbsoluteTarget(value) || value.includes("\\")) {
@@ -13139,7 +13930,7 @@ function isSensitiveTarget(segments, productNames) {
13139
13930
  return segments.some((segment) => patterns.some((pattern) => nameMatches(pattern, segment)));
13140
13931
  }
13141
13932
  async function resolveExistingAncestor2(inputPath) {
13142
- let cursor = path.resolve(inputPath);
13933
+ let cursor = path3__default.resolve(inputPath);
13143
13934
  const tail = [];
13144
13935
  for (; ; ) {
13145
13936
  try {
@@ -13147,9 +13938,9 @@ async function resolveExistingAncestor2(inputPath) {
13147
13938
  } catch (error) {
13148
13939
  const code = error.code;
13149
13940
  if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
13150
- const parent = path.dirname(cursor);
13941
+ const parent = path3__default.dirname(cursor);
13151
13942
  if (parent === cursor) throw new TargetPolicyError("target-missing");
13152
- tail.unshift(path.basename(cursor));
13943
+ tail.unshift(path3__default.basename(cursor));
13153
13944
  cursor = parent;
13154
13945
  }
13155
13946
  }
@@ -13170,11 +13961,11 @@ var RootPolicyError = class extends Error {
13170
13961
  this.reason = reason;
13171
13962
  }
13172
13963
  };
13173
- function sameAgentRef2(left, right) {
13964
+ function sameAgentRef3(left, right) {
13174
13965
  return left.agentId === right.agentId && left.profileRevision === right.profileRevision;
13175
13966
  }
13176
13967
  function sameSessionIdentity(left, right) {
13177
- return sameAgentRef2(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
13968
+ return sameAgentRef3(left.agentRef, right.agentRef) && left.sessionRef === right.sessionRef && left.runtimeId === right.runtimeId && left.cwd === right.cwd;
13178
13969
  }
13179
13970
  function validateRequest(request) {
13180
13971
  if (!isRecord5(request)) throw new AgentContentReadRequestError("content read request must be an object");
@@ -13236,18 +14027,18 @@ function normalizeRequestIdentity(value, field) {
13236
14027
  const sessionRef = requestString(value.sessionRef, `${field}.sessionRef`);
13237
14028
  const runtimeId = requestString(value.runtimeId, `${field}.runtimeId`);
13238
14029
  const cwd = requestString(value.cwd, `${field}.cwd`);
13239
- if (!path.isAbsolute(cwd)) throw new AgentContentReadRequestError(`${field}.cwd must be absolute`);
13240
- return Object.freeze({ agentRef, sessionRef, runtimeId, cwd: path.resolve(cwd) });
14030
+ if (!path3__default.isAbsolute(cwd)) throw new AgentContentReadRequestError(`${field}.cwd must be absolute`);
14031
+ return Object.freeze({ agentRef, sessionRef, runtimeId, cwd: path3__default.resolve(cwd) });
13241
14032
  }
13242
14033
  async function inspectRegularTarget(root, target) {
13243
14034
  const ancestor = await resolveExistingAncestor2(target);
13244
14035
  if (!isWithin2(root, ancestor.canonical)) {
13245
14036
  throw new TargetPolicyError("path-escape");
13246
14037
  }
13247
- const components = path.relative(root, target).split(path.sep).filter((component) => component.length > 0);
14038
+ const components = path3__default.relative(root, target).split(path3__default.sep).filter((component) => component.length > 0);
13248
14039
  let cursor = root;
13249
14040
  for (const [index, component] of components.entries()) {
13250
- cursor = path.join(cursor, component);
14041
+ cursor = path3__default.join(cursor, component);
13251
14042
  let stat;
13252
14043
  try {
13253
14044
  stat = await promises.lstat(cursor);
@@ -13312,8 +14103,8 @@ var AgentContentReadPolicyEngine = class {
13312
14103
  this.capabilities = new Set(options.capabilities);
13313
14104
  this.runtimeRoots = Object.freeze((options.runtimeAllowlistedRoots ?? []).map((root, index) => {
13314
14105
  const value = nonEmptyString(root, `contentRead.runtimeAllowlistedRoots[${index}]`);
13315
- if (!path.isAbsolute(value)) throw new AgentContentReadPolicyError("runtime allowlisted roots must be absolute");
13316
- return path.resolve(value);
14106
+ if (!path3__default.isAbsolute(value)) throw new AgentContentReadPolicyError("runtime allowlisted roots must be absolute");
14107
+ return path3__default.resolve(value);
13317
14108
  }));
13318
14109
  this.resolveSessionIdentity = options.resolveSessionIdentity;
13319
14110
  this.resolveTranscriptIdentity = options.resolveTranscriptIdentity;
@@ -13365,7 +14156,7 @@ var AgentContentReadPolicyEngine = class {
13365
14156
  if (request.decodeAs === "utf8" && !policy.textMimeTypes.includes(request.mimeType)) {
13366
14157
  return this.deny(request, relativeTarget, "text-not-allowlisted");
13367
14158
  }
13368
- const target = path.resolve(root, ...segments);
14159
+ const target = path3__default.resolve(root, ...segments);
13369
14160
  if (!isWithin2(root, target)) return this.deny(request, relativeTarget, "path-escape");
13370
14161
  try {
13371
14162
  await inspectRegularTarget(root, target);
@@ -13446,7 +14237,7 @@ var AgentContentReadPolicyEngine = class {
13446
14237
  }
13447
14238
  async checkSessionIdentity(request, resolver, requiredCwd) {
13448
14239
  const session = request.session;
13449
- if (session === void 0 || !sameAgentRef2(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
14240
+ if (session === void 0 || !sameAgentRef3(session.agentRef, request.agentRef) || requiredCwd !== void 0 && session.cwd !== requiredCwd) {
13450
14241
  return "identity-mismatch";
13451
14242
  }
13452
14243
  let expected;
@@ -13562,7 +14353,7 @@ async function detectRuntimes(adapters) {
13562
14353
  }
13563
14354
  return runtimes;
13564
14355
  }
13565
- function computeCapabilities(adapters, agentHomeConfigured = false, agentEgressConfigured = false, contentReadPolicies) {
14356
+ function computeCapabilities(adapters, agentHomeConfigured = false, strictAgentOnly = false, agentHomeProjectionConfigured = false, agentEgressConfigured = false, contentReadPolicies) {
13566
14357
  const flags = [];
13567
14358
  if (adapters.some((adapter) => adapter.descriptor.capabilities.steer)) flags.push("steer");
13568
14359
  flags.push("blob-upload");
@@ -13577,6 +14368,8 @@ function computeCapabilities(adapters, agentHomeConfigured = false, agentEgressC
13577
14368
  flags.push("toolset-selection");
13578
14369
  }
13579
14370
  if (agentHomeConfigured) flags.push("agent-home-contract");
14371
+ if (strictAgentOnly) flags.push(STRICT_AGENT_ONLY_CAPABILITY);
14372
+ if (agentHomeProjectionConfigured) flags.push(AGENT_HOME_PROJECTION_CAPABILITY);
13580
14373
  if (agentEgressConfigured) {
13581
14374
  flags.push(
13582
14375
  AGENT_EGRESS_POLICY_CAPABILITY,
@@ -13705,6 +14498,9 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13705
14498
  if (config.agentEgress !== void 0 && config.agentHome === void 0) {
13706
14499
  throw new Error("DaemonConfig.agentEgress requires DaemonConfig.agentHome for the per-Agent local spool");
13707
14500
  }
14501
+ if (config.strictAgentOnly === true && config.agentHome === void 0) {
14502
+ throw new Error("DaemonConfig.strictAgentOnly requires DaemonConfig.agentHome");
14503
+ }
13708
14504
  const egressPolicy = resolveAgentEgressPolicy(config.agentEgress?.policy);
13709
14505
  const egressBatcherOptions = egressPolicy.activity.mode === "contentful-trajectory" ? {
13710
14506
  ...config.progressBatch,
@@ -13720,7 +14516,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13720
14516
  const deviceAssertionTtlMs = resolveDeviceAssertionTtlMs(config.deviceAssertion);
13721
14517
  let shuttingDown = false;
13722
14518
  const storeDir = DeviceStore.resolveDir(config.productId, config.storeDir);
13723
- const store = new DeviceStore(storeDir);
14519
+ const store = new DeviceStore(storeDir, void 0, config.productId);
13724
14520
  const operationalHealth = new OperationalHealthTracker(storeDir);
13725
14521
  let fleetJitter;
13726
14522
  let maintenanceSequence = 0;
@@ -13734,6 +14530,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13734
14530
  })
13735
14531
  });
13736
14532
  const agentSessionHandoffs = config.agentHome === void 0 ? void 0 : new AgentSessionHandoffStore();
14533
+ if (config.strictAgentOnly === true) agentHomeManager?.preflightSync();
13737
14534
  const agentContentReadPolicies = resolveContentReadPolicies(egressPolicy, config.agentEgress?.contentRead);
13738
14535
  let agentEgress = new AgentEgressController({
13739
14536
  policy: egressPolicy,
@@ -13818,6 +14615,8 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13818
14615
  let tenantRebinding = false;
13819
14616
  let runner;
13820
14617
  let controlServerHandle;
14618
+ let serviceEnrollmentWaiting = false;
14619
+ let serviceEnrollmentTransitioning = false;
13821
14620
  let daemonOwnerLease;
13822
14621
  let presencePublisher;
13823
14622
  let detectedRuntimeFacts = [];
@@ -13865,7 +14664,8 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13865
14664
  }
13866
14665
  async function pair(pairingCode) {
13867
14666
  checkServerUrl();
13868
- return runLifecycleMutation(() => pairUnderLease(pairingCode));
14667
+ const record = await runLifecycleMutation(() => pairUnderLease(pairingCode));
14668
+ return Object.freeze({ deviceId: record.deviceId });
13869
14669
  }
13870
14670
  async function pairUnderLease(pairingCode) {
13871
14671
  const wasRunning = daemonStarted;
@@ -13876,7 +14676,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13876
14676
  try {
13877
14677
  let previous;
13878
14678
  try {
13879
- previous = await store.load();
14679
+ previous = await auth.readCurrent();
13880
14680
  } catch (error) {
13881
14681
  if (!(error instanceof DeviceRecordRePairRequiredError)) throw error;
13882
14682
  }
@@ -13911,6 +14711,28 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13911
14711
  throw err;
13912
14712
  }
13913
14713
  }
14714
+ async function replaceControlServer(required, enrollmentOnly = false) {
14715
+ if (controlServerHandle) {
14716
+ await controlServerHandle.close();
14717
+ controlServerHandle = void 0;
14718
+ }
14719
+ try {
14720
+ const methods = enrollmentOnly ? {
14721
+ unary: {
14722
+ status: controlMethods.unary.status,
14723
+ "enrollment.pair": controlMethods.unary["enrollment.pair"]
14724
+ },
14725
+ stream: {}
14726
+ } : controlMethods;
14727
+ controlServerHandle = await startControlServer({ storeDir, productId: config.productId, methods });
14728
+ } catch (err) {
14729
+ if (required || err instanceof AnotherControlServerRunningError) throw err;
14730
+ console.warn(
14731
+ `[byok/client] control socket failed to start (continuing without it): ${err instanceof Error ? err.message : String(err)}`
14732
+ );
14733
+ controlServerHandle = void 0;
14734
+ }
14735
+ }
13914
14736
  async function start() {
13915
14737
  checkServerUrl();
13916
14738
  await runLifecycleMutation(startUnderLease);
@@ -13918,14 +14740,27 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13918
14740
  async function startUnderLease() {
13919
14741
  if (!daemonOwnerLease) daemonOwnerLease = await acquireDaemonOwner(storeDir, "daemon");
13920
14742
  try {
14743
+ let record;
14744
+ if (config.serviceEnrollment?.enabled === true) {
14745
+ record = await auth.loadExisting();
14746
+ if (!record) {
14747
+ startedAt = Date.now();
14748
+ serviceEnrollmentWaiting = true;
14749
+ serviceEnrollmentTransitioning = false;
14750
+ await replaceControlServer(true, true);
14751
+ return;
14752
+ }
14753
+ }
14754
+ serviceEnrollmentWaiting = false;
14755
+ serviceEnrollmentTransitioning = false;
13921
14756
  initializeOwnedHostedStorage();
13922
- const activeJournal = journal;
13923
- const activePressureEngine = pressureEngine;
13924
- const activeOwnedPressureEngine = ownedPressureEngine;
13925
- const record = await auth.loadExisting();
14757
+ record ??= await auth.loadExisting();
13926
14758
  if (!record) {
13927
14759
  throw new Error("device is not paired yet; call pair(pairingCode) first");
13928
14760
  }
14761
+ const activeJournal = journal;
14762
+ const activePressureEngine = pressureEngine;
14763
+ const activeOwnedPressureEngine = ownedPressureEngine;
13929
14764
  if (config.agentEgress !== void 0) {
13930
14765
  agentEgress = new AgentEgressController({
13931
14766
  policy: egressPolicy,
@@ -13935,7 +14770,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13935
14770
  }
13936
14771
  await agentHomeManager?.preflight();
13937
14772
  if (config.agentEgress !== void 0 && config.agentHome !== void 0) {
13938
- await agentEgress.recover(path.join(config.agentHome.hostStorageRoot, "agents"));
14773
+ await agentEgress.recover(path3__default.join(config.agentHome.hostStorageRoot, "agents"));
13939
14774
  }
13940
14775
  fleetJitter = createFleetJitter(config.productId, record.deviceId);
13941
14776
  if (config.permissionDefaults?.workspaceRoot !== void 0) {
@@ -13962,19 +14797,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13962
14797
  activeOwnedPressureEngine.start();
13963
14798
  }
13964
14799
  startedAt = Date.now();
13965
- if (controlServerHandle) {
13966
- await controlServerHandle.close();
13967
- controlServerHandle = void 0;
13968
- }
13969
- try {
13970
- controlServerHandle = await startControlServer({ storeDir, productId: config.productId, methods: controlMethods });
13971
- } catch (err) {
13972
- if (err instanceof AnotherControlServerRunningError) throw err;
13973
- console.warn(
13974
- `[byok/client] control socket failed to start (continuing without it): ${err instanceof Error ? err.message : String(err)}`
13975
- );
13976
- controlServerHandle = void 0;
13977
- }
14800
+ await replaceControlServer(false);
13978
14801
  await operationalHealth.startRun();
13979
14802
  if (gitWorkspaceManager) {
13980
14803
  await gitWorkspaceManager.preflight();
@@ -13990,9 +14813,17 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
13990
14813
  const capabilities = computeCapabilities(
13991
14814
  adapters,
13992
14815
  config.agentHome !== void 0,
14816
+ config.strictAgentOnly === true,
14817
+ agentHomeManager?.supportsTaskFreeProjection() === true,
13993
14818
  config.agentEgress !== void 0,
13994
14819
  agentContentReadPolicies
13995
14820
  );
14821
+ const agentHomeProjectionCompletion = agentHomeManager?.supportsTaskFreeProjection() === true ? new AgentHomeProjectionCompletionClient({
14822
+ serverUrl: config.serverUrl,
14823
+ auth,
14824
+ tenantId: record.tenantId,
14825
+ deviceId: record.deviceId
14826
+ }) : void 0;
13996
14827
  const journalIdentity = config.hostedJournal ? { tenantId: record.tenantId, productId: config.productId, deviceId: record.deviceId } : void 0;
13997
14828
  const sendSanitizedEnvelope = activeJournal && journalIdentity ? (envelope) => {
13998
14829
  observer.handleOutboundEnvelope(envelope);
@@ -14043,6 +14874,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14043
14874
  permissionDefaults: config.permissionDefaults,
14044
14875
  workspaceRoot: config.workspaceRoot,
14045
14876
  ...agentHomeManager === void 0 ? {} : { agentHome: agentHomeManager },
14877
+ ...config.strictAgentOnly === true ? { strictAgentOnly: true } : {},
14046
14878
  ...agentSessionHandoffs === void 0 ? {} : { agentSessionHandoffs },
14047
14879
  deviceId: record.deviceId,
14048
14880
  // M5: see `DaemonConfig.runtimeEnvironment`'s own doc comment above.
@@ -14129,6 +14961,20 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14129
14961
  ...activePressureEngine ? { admissionGuard: () => activePressureEngine.admissionGuard() } : {}
14130
14962
  };
14131
14963
  runner = new TaskRunner(deps);
14964
+ const handleAgentHomeProjectionEnvelope = async (envelope) => {
14965
+ if (envelope.type !== "agent.home.projection") return false;
14966
+ if (agentHomeManager === void 0 || agentHomeProjectionCompletion === void 0) {
14967
+ throw new Error("task-free Agent-home projection is not configured on this daemon");
14968
+ }
14969
+ const outcome = await agentHomeManager.project(envelope.payload);
14970
+ await agentHomeProjectionCompletion.complete({
14971
+ requestId: envelope.payload.requestId,
14972
+ agentRef: envelope.payload.agentRef,
14973
+ projectionHash: envelope.payload.projectionHash,
14974
+ outcome
14975
+ });
14976
+ return true;
14977
+ };
14132
14978
  const handleAgentEgressEnvelope = async (envelope) => {
14133
14979
  if (envelope.type !== "agent.egress.ack") return false;
14134
14980
  if (config.agentEgress === void 0) return true;
@@ -14284,6 +15130,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14284
15130
  throw new Error("tenant enrollment is being re-paired; inbound work is blocked until restart");
14285
15131
  }
14286
15132
  observer.handleInboundEnvelope(envelope);
15133
+ if (await handleAgentHomeProjectionEnvelope(envelope)) return;
14287
15134
  if (await handleAgentEgressEnvelope(envelope)) return;
14288
15135
  if (await handleAgentContentReadEnvelope(envelope)) return;
14289
15136
  activePressureEngine?.assertAckCriticalAllowed();
@@ -14294,6 +15141,7 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14294
15141
  return Promise.reject(new Error("tenant enrollment is being re-paired; inbound work is blocked until restart"));
14295
15142
  }
14296
15143
  observer.handleInboundEnvelope(envelope);
15144
+ if (envelope.type === "agent.home.projection") return handleAgentHomeProjectionEnvelope(envelope).then(() => void 0);
14297
15145
  if (envelope.type === "agent.egress.ack") return handleAgentEgressEnvelope(envelope).then(() => void 0);
14298
15146
  if (envelope.type === "agent.content.read") return handleAgentContentReadEnvelope(envelope).then(() => void 0);
14299
15147
  return runner?.handleEnvelope(envelope) ?? Promise.resolve();
@@ -14492,10 +15340,24 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14492
15340
  await runShutdownSequence("operator");
14493
15341
  const cleanupLease = await acquireDaemonOwner(storeDir, "daemon");
14494
15342
  try {
14495
- const current = await store.remove();
14496
- if (current) {
14497
- await cursorStore.clear(config.serverUrl, current.deviceId);
15343
+ let current;
15344
+ try {
15345
+ current = await auth.readCurrent();
15346
+ } catch (error) {
15347
+ if (!(error instanceof DeviceRecordRePairRequiredError)) throw error;
15348
+ }
15349
+ try {
15350
+ await store.credentials.clear();
15351
+ } catch {
15352
+ throw new Error("device credential could not be cleared; enrollment remains paired and fail-closed");
15353
+ }
15354
+ try {
15355
+ await store.remove();
15356
+ } catch (error) {
15357
+ auth = buildAuthManager();
15358
+ throw error;
14498
15359
  }
15360
+ if (current) await cursorStore.clear(config.serverUrl, current.deviceId);
14499
15361
  } finally {
14500
15362
  await cleanupLease.release();
14501
15363
  }
@@ -14577,6 +15439,38 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14577
15439
  const controlMethods = {
14578
15440
  unary: {
14579
15441
  status: () => buildControlStatus(),
15442
+ "enrollment.pair": async (params) => {
15443
+ if (config.serviceEnrollment?.enabled !== true) {
15444
+ throw new ControlError("enrollment_disabled", "service enrollment is not enabled for this daemon");
15445
+ }
15446
+ const parsed = parseEnrollmentPairParams(params);
15447
+ if (!parsed) {
15448
+ throw new ControlError(
15449
+ "bad_request",
15450
+ `enrollment.pair requires exactly {pairingCode} with 1-${ENROLLMENT_PAIRING_CODE_MAX_BYTES} UTF-8 bytes`
15451
+ );
15452
+ }
15453
+ if (!serviceEnrollmentWaiting || daemonStarted) {
15454
+ throw new ControlError("already_paired", "this service daemon is not waiting for initial enrollment");
15455
+ }
15456
+ if (serviceEnrollmentTransitioning) {
15457
+ throw new ControlError("pairing_in_progress", "service enrollment is already in progress");
15458
+ }
15459
+ serviceEnrollmentTransitioning = true;
15460
+ let record;
15461
+ try {
15462
+ record = await runLifecycleMutation(() => pairUnderLease(parsed.pairingCode));
15463
+ } catch (err) {
15464
+ serviceEnrollmentTransitioning = false;
15465
+ throw err;
15466
+ }
15467
+ setImmediate(() => {
15468
+ void runLifecycleMutation(startUnderLease).catch(() => {
15469
+ console.error("[byok/client] service enrollment persisted but normal daemon startup failed");
15470
+ });
15471
+ });
15472
+ return { deviceId: record.deviceId };
15473
+ },
14580
15474
  "toolsets.reload": (params) => {
14581
15475
  const parsed = parseToolsetsReloadParams(params);
14582
15476
  if (!parsed) {
@@ -14687,7 +15581,12 @@ function buildDaemonWithAdapters(config, adapters, overrides = {}, assertionProb
14687
15581
  observer.noteDeviceAssertion({ result: "denied", reason: "revoked", audience: parsed.audience });
14688
15582
  throw new ControlError("revoked", "this device has been revoked by the server; re-pair required");
14689
15583
  }
14690
- const record = await store.load();
15584
+ let record;
15585
+ try {
15586
+ record = await auth.readCurrent();
15587
+ } catch (error) {
15588
+ if (!(error instanceof DeviceRecordRePairRequiredError)) throw error;
15589
+ }
14691
15590
  if (record === void 0) {
14692
15591
  observer.noteDeviceAssertion({ result: "denied", reason: "not_paired", audience: parsed.audience });
14693
15592
  throw new ControlError("not_paired", "this device is not paired; nothing can be asserted about it");
@@ -15155,9 +16054,9 @@ function plistString(value) {
15155
16054
  function generateLaunchdPlist(def) {
15156
16055
  const { label, program, logDir } = def;
15157
16056
  const args = [program.command, ...program.args];
15158
- const cwd = program.cwd ?? os.homedir();
15159
- const outLog = path.join(logDir, `${label}.out.log`);
15160
- const errLog = path.join(logDir, `${label}.err.log`);
16057
+ const cwd = program.cwd ?? os__default.homedir();
16058
+ const outLog = path3__default.join(logDir, `${label}.out.log`);
16059
+ const errLog = path3__default.join(logDir, `${label}.err.log`);
15161
16060
  return `<?xml version="1.0" encoding="UTF-8"?>
15162
16061
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
15163
16062
  <plist version="1.0">
@@ -15189,8 +16088,8 @@ ${args.map((a) => ` ${plistString(a)}`).join("\n")}
15189
16088
  }
15190
16089
  function createLaunchdLifecycle(def, deps = {}) {
15191
16090
  const run = deps.run ?? defaultRunner;
15192
- const fs26 = deps.fs ?? promises;
15193
- const homedir = deps.homedir ?? (() => os.homedir());
16091
+ const fs28 = deps.fs ?? promises;
16092
+ const homedir = deps.homedir ?? (() => os__default.homedir());
15194
16093
  const getuid = deps.getuid ?? (() => {
15195
16094
  if (typeof process.getuid !== "function") {
15196
16095
  throw new Error("launchd lifecycle requires a POSIX uid (process.getuid unavailable) \u2014 this module only runs on macOS");
@@ -15198,12 +16097,12 @@ function createLaunchdLifecycle(def, deps = {}) {
15198
16097
  return process.getuid();
15199
16098
  });
15200
16099
  const label = sanitizeServiceName(def.name);
15201
- const plistPath = () => path.join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
16100
+ const plistPath = () => path3__default.join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
15202
16101
  const domainTarget = () => `gui/${getuid()}`;
15203
16102
  const serviceTarget = () => `${domainTarget()}/${label}`;
15204
16103
  async function fileExists(p) {
15205
16104
  try {
15206
- await fs26.stat(p);
16105
+ await fs28.stat(p);
15207
16106
  return true;
15208
16107
  } catch {
15209
16108
  return false;
@@ -15211,9 +16110,9 @@ function createLaunchdLifecycle(def, deps = {}) {
15211
16110
  }
15212
16111
  async function writePlist(program) {
15213
16112
  const xml = generateLaunchdPlist({ label, program, logDir: def.logDir });
15214
- await fs26.mkdir(path.dirname(plistPath()), { recursive: true });
15215
- await fs26.mkdir(def.logDir, { recursive: true });
15216
- await fs26.writeFile(plistPath(), xml, "utf8");
16113
+ await fs28.mkdir(path3__default.dirname(plistPath()), { recursive: true });
16114
+ await fs28.mkdir(def.logDir, { recursive: true });
16115
+ await fs28.writeFile(plistPath(), xml, "utf8");
15217
16116
  }
15218
16117
  async function install(opts = {}) {
15219
16118
  await writePlist(opts.program ?? def.program);
@@ -15224,7 +16123,7 @@ function createLaunchdLifecycle(def, deps = {}) {
15224
16123
  }
15225
16124
  async function uninstall() {
15226
16125
  await runIdempotent(run, "launchctl", ["bootout", serviceTarget()], "launchctl bootout", LAUNCHD_NOT_LOADED);
15227
- await fs26.rm(plistPath(), { force: true });
16126
+ await fs28.rm(plistPath(), { force: true });
15228
16127
  }
15229
16128
  async function start() {
15230
16129
  if (!await fileExists(plistPath())) {
@@ -15283,10 +16182,10 @@ function generateSystemdUnit(def) {
15283
16182
  const { name, displayName, program, logDir } = def;
15284
16183
  assertNoControlChars(name, "name");
15285
16184
  assertNoControlChars(displayName, "displayName");
15286
- const cwd = program.cwd ?? os.homedir();
16185
+ const cwd = program.cwd ?? os__default.homedir();
15287
16186
  assertNoControlChars(cwd, "program.cwd");
15288
- const outLog = path.join(logDir, `${name}.out.log`);
15289
- const errLog = path.join(logDir, `${name}.err.log`);
16187
+ const outLog = path3__default.join(logDir, `${name}.out.log`);
16188
+ const errLog = path3__default.join(logDir, `${name}.err.log`);
15290
16189
  assertNoControlChars(outLog, "logDir");
15291
16190
  assertNoControlChars(errLog, "logDir");
15292
16191
  const execStart = [program.command, ...program.args].map(quoteSystemdArg).join(" ");
@@ -15308,14 +16207,14 @@ WantedBy=default.target
15308
16207
  }
15309
16208
  function createSystemdLifecycle(def, deps = {}) {
15310
16209
  const run = deps.run ?? defaultRunner;
15311
- const fs26 = deps.fs ?? promises;
15312
- const homedir = deps.homedir ?? (() => os.homedir());
16210
+ const fs28 = deps.fs ?? promises;
16211
+ const homedir = deps.homedir ?? (() => os__default.homedir());
15313
16212
  const name = sanitizeServiceName(def.name);
15314
16213
  const unitName = `${name}.service`;
15315
- const unitPath = () => path.join(homedir(), ".config", "systemd", "user", unitName);
16214
+ const unitPath = () => path3__default.join(homedir(), ".config", "systemd", "user", unitName);
15316
16215
  async function fileExists(p) {
15317
16216
  try {
15318
- await fs26.stat(p);
16217
+ await fs28.stat(p);
15319
16218
  return true;
15320
16219
  } catch {
15321
16220
  return false;
@@ -15323,9 +16222,9 @@ function createSystemdLifecycle(def, deps = {}) {
15323
16222
  }
15324
16223
  async function writeUnit(program) {
15325
16224
  const unit = generateSystemdUnit({ name, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
15326
- await fs26.mkdir(path.dirname(unitPath()), { recursive: true });
15327
- await fs26.mkdir(def.logDir, { recursive: true });
15328
- await fs26.writeFile(unitPath(), unit, "utf8");
16225
+ await fs28.mkdir(path3__default.dirname(unitPath()), { recursive: true });
16226
+ await fs28.mkdir(def.logDir, { recursive: true });
16227
+ await fs28.writeFile(unitPath(), unit, "utf8");
15329
16228
  }
15330
16229
  async function install(opts = {}) {
15331
16230
  await writeUnit(opts.program ?? def.program);
@@ -15334,7 +16233,7 @@ function createSystemdLifecycle(def, deps = {}) {
15334
16233
  }
15335
16234
  async function uninstall() {
15336
16235
  await runIdempotent(run, "systemctl", ["--user", "disable", "--now", unitName], "systemctl disable --now", SYSTEMD_NOT_LOADED);
15337
- await fs26.rm(unitPath(), { force: true });
16236
+ await fs28.rm(unitPath(), { force: true });
15338
16237
  await run("systemctl", ["--user", "daemon-reload"]);
15339
16238
  }
15340
16239
  async function start() {
@@ -15397,7 +16296,7 @@ ${argXml}${cwdXml}
15397
16296
  }
15398
16297
  function createWinswLifecycle(def, deps = {}) {
15399
16298
  const run = deps.run ?? defaultRunner;
15400
- const fs26 = deps.fs ?? promises;
16299
+ const fs28 = deps.fs ?? promises;
15401
16300
  const windows = def.windows;
15402
16301
  if (!windows) {
15403
16302
  throw new Error("WinSW service lifecycle requires `ServiceDefinition.windows.winswBin` (the product-bundled WinSW executable path)");
@@ -15405,11 +16304,11 @@ function createWinswLifecycle(def, deps = {}) {
15405
16304
  const winswBin = windows.winswBin;
15406
16305
  const id = sanitizeServiceName(def.name);
15407
16306
  const installDir = windows.installDir ?? def.logDir;
15408
- const exePath = path.join(installDir, `${id}.exe`);
15409
- const xmlPath = path.join(installDir, `${id}.xml`);
16307
+ const exePath = path3__default.join(installDir, `${id}.exe`);
16308
+ const xmlPath = path3__default.join(installDir, `${id}.xml`);
15410
16309
  async function fileExists(p) {
15411
16310
  try {
15412
- await fs26.stat(p);
16311
+ await fs28.stat(p);
15413
16312
  return true;
15414
16313
  } catch {
15415
16314
  return false;
@@ -15417,10 +16316,10 @@ function createWinswLifecycle(def, deps = {}) {
15417
16316
  }
15418
16317
  async function writeFiles(program) {
15419
16318
  const xml = generateWinswXml({ id, displayName: def.displayName ?? def.name, program, logDir: def.logDir });
15420
- await fs26.mkdir(installDir, { recursive: true });
15421
- await fs26.mkdir(def.logDir, { recursive: true });
15422
- await fs26.copyFile(winswBin, exePath);
15423
- await fs26.writeFile(xmlPath, xml, "utf8");
16319
+ await fs28.mkdir(installDir, { recursive: true });
16320
+ await fs28.mkdir(def.logDir, { recursive: true });
16321
+ await fs28.copyFile(winswBin, exePath);
16322
+ await fs28.writeFile(xmlPath, xml, "utf8");
15424
16323
  }
15425
16324
  async function install(opts = {}) {
15426
16325
  await writeFiles(opts.program ?? def.program);
@@ -15430,8 +16329,8 @@ function createWinswLifecycle(def, deps = {}) {
15430
16329
  async function uninstall() {
15431
16330
  await runIdempotent(run, exePath, ["stop"], "winsw stop", WINSW_NOT_INSTALLED);
15432
16331
  await runIdempotent(run, exePath, ["uninstall"], "winsw uninstall", WINSW_NOT_INSTALLED);
15433
- await fs26.rm(exePath, { force: true });
15434
- await fs26.rm(xmlPath, { force: true });
16332
+ await fs28.rm(exePath, { force: true });
16333
+ await fs28.rm(xmlPath, { force: true });
15435
16334
  }
15436
16335
  async function start() {
15437
16336
  if (!await fileExists(xmlPath)) {
@@ -15476,7 +16375,7 @@ function createServiceLifecycle(def, opts = {}) {
15476
16375
 
15477
16376
  // src/bin/official-release.ts
15478
16377
  var OFFICIAL_LOCAL_AGENT_RELEASE = resolveLocalAgentReleaseIdentity({
15479
- version: "0.8.0-beta.0"
16378
+ version: "0.8.1"
15480
16379
  });
15481
16380
 
15482
16381
  // src/bin/config.ts
@@ -15871,7 +16770,23 @@ async function resolveApproval(storeDir, productId, approvalId, decision, reason
15871
16770
  // src/bin/commands/pair.ts
15872
16771
  async function runPairCommand(config, code, deps = {}) {
15873
16772
  const log = deps.log ?? ((line) => console.log(line));
15874
- const daemon = deps.daemon ?? createDaemon(config);
16773
+ if (deps.daemon) {
16774
+ const result2 = await deps.daemon.pair(code);
16775
+ log(`paired: deviceId=${result2.deviceId}`);
16776
+ return;
16777
+ }
16778
+ const connectControl = deps.connectControl ?? connectControlClient;
16779
+ const conn = await connectControl({ storeDir: resolveStoreDir(config), productId: config.productId });
16780
+ if (conn.ok) {
16781
+ try {
16782
+ const result2 = await conn.client.request("enrollment.pair", { pairingCode: code });
16783
+ log(`paired: deviceId=${result2.deviceId}`);
16784
+ return;
16785
+ } finally {
16786
+ conn.client.close();
16787
+ }
16788
+ }
16789
+ const daemon = createDaemon(config);
15875
16790
  const result = await daemon.pair(code);
15876
16791
  log(`paired: deviceId=${result.deviceId}`);
15877
16792
  }
@@ -15961,7 +16876,7 @@ function safeProtocol(serverUrl) {
15961
16876
  }
15962
16877
  }
15963
16878
  async function inspectDevice(storeDir) {
15964
- const filePath = path.join(storeDir, "device.json");
16879
+ const filePath = path3__default.join(storeDir, "device.json");
15965
16880
  let pathStat;
15966
16881
  try {
15967
16882
  pathStat = await promises.lstat(filePath);
@@ -16041,11 +16956,11 @@ async function copyOpenFileBounded(source, expected, destinationPath) {
16041
16956
  }
16042
16957
  }
16043
16958
  async function inspectJournal(storeDir) {
16044
- const journalPath = path.join(storeDir, JOURNAL_DB_FILENAME);
16959
+ const journalPath = path3__default.join(storeDir, JOURNAL_DB_FILENAME);
16045
16960
  try {
16046
16961
  const mainIdentity = await regularFileIdentity(journalPath);
16047
16962
  if (mainIdentity === void 0) return { status: "missing" };
16048
- const walIdentity = await regularFileIdentity(path.join(storeDir, `${JOURNAL_DB_FILENAME}-wal`));
16963
+ const walIdentity = await regularFileIdentity(path3__default.join(storeDir, `${JOURNAL_DB_FILENAME}-wal`));
16049
16964
  let sizeBytes = Number(mainIdentity.size);
16050
16965
  let walBytes = walIdentity === void 0 ? void 0 : Number(walIdentity.size);
16051
16966
  if (!isSqliteAvailable()) {
@@ -16053,7 +16968,7 @@ async function inspectJournal(storeDir) {
16053
16968
  }
16054
16969
  const componentNames = [JOURNAL_DB_FILENAME, `${JOURNAL_DB_FILENAME}-wal`, `${JOURNAL_DB_FILENAME}-shm`];
16055
16970
  const initial = /* @__PURE__ */ new Map();
16056
- for (const name of componentNames) initial.set(name, await regularFileIdentity(path.join(storeDir, name)));
16971
+ for (const name of componentNames) initial.set(name, await regularFileIdentity(path3__default.join(storeDir, name)));
16057
16972
  const snapshotMain = initial.get(JOURNAL_DB_FILENAME);
16058
16973
  if (!snapshotMain) return { status: "unavailable", reason: "journal changed during diagnostics snapshot" };
16059
16974
  sizeBytes = Number(snapshotMain.size);
@@ -16065,7 +16980,7 @@ async function inspectJournal(storeDir) {
16065
16980
  let handle;
16066
16981
  try {
16067
16982
  handle = await promises.open(
16068
- path.join(storeDir, name),
16983
+ path3__default.join(storeDir, name),
16069
16984
  constants.O_RDONLY | constants.O_NONBLOCK | (constants.O_NOFOLLOW ?? 0)
16070
16985
  );
16071
16986
  } catch (err) {
@@ -16094,28 +17009,28 @@ async function inspectJournal(storeDir) {
16094
17009
  reason: "journal exceeds the bounded diagnostics copy limit"
16095
17010
  };
16096
17011
  }
16097
- const tempDir = await promises.mkdtemp(path.join(os.tmpdir(), "byok-journal-inspect-"));
17012
+ const tempDir = await promises.mkdtemp(path3__default.join(os__default.tmpdir(), "byok-journal-inspect-"));
16098
17013
  const { DatabaseSync } = loadSqliteModule();
16099
17014
  let db;
16100
17015
  try {
16101
17016
  for (const name of componentNames) {
16102
17017
  const component = opened.get(name);
16103
- if (component && !await copyOpenFileBounded(component.handle, component.identity, path.join(tempDir, name))) {
17018
+ if (component && !await copyOpenFileBounded(component.handle, component.identity, path3__default.join(tempDir, name))) {
16104
17019
  return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
16105
17020
  }
16106
17021
  }
16107
17022
  for (const [name, component] of opened) {
16108
- if (!sameIdentity(component.identity, identityFromBigIntStat(await component.handle.stat({ bigint: true }))) || !sameIdentity(component.identity, await regularFileIdentity(path.join(storeDir, name)))) {
17023
+ if (!sameIdentity(component.identity, identityFromBigIntStat(await component.handle.stat({ bigint: true }))) || !sameIdentity(component.identity, await regularFileIdentity(path3__default.join(storeDir, name)))) {
16109
17024
  return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
16110
17025
  }
16111
17026
  }
16112
17027
  for (const name of componentNames) {
16113
- if (!opened.has(name) && await regularFileIdentity(path.join(storeDir, name)) !== void 0) {
17028
+ if (!opened.has(name) && await regularFileIdentity(path3__default.join(storeDir, name)) !== void 0) {
16114
17029
  return { status: "unavailable", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal changed during diagnostics snapshot" };
16115
17030
  }
16116
17031
  }
16117
17032
  const header = Buffer.alloc(16);
16118
- const copiedHandle = await promises.open(path.join(tempDir, JOURNAL_DB_FILENAME), "r");
17033
+ const copiedHandle = await promises.open(path3__default.join(tempDir, JOURNAL_DB_FILENAME), "r");
16119
17034
  try {
16120
17035
  const { bytesRead } = await copiedHandle.read(header, 0, header.length, 0);
16121
17036
  if (bytesRead !== 16 || header.toString("binary") !== "SQLite format 3\0") {
@@ -16124,7 +17039,7 @@ async function inspectJournal(storeDir) {
16124
17039
  } finally {
16125
17040
  await copiedHandle.close();
16126
17041
  }
16127
- db = new DatabaseSync(path.join(tempDir, JOURNAL_DB_FILENAME), { readOnly: true });
17042
+ db = new DatabaseSync(path3__default.join(tempDir, JOURNAL_DB_FILENAME), { readOnly: true });
16128
17043
  const result = db.prepare("PRAGMA quick_check(1)").get();
16129
17044
  if (result?.quick_check !== "ok") {
16130
17045
  return { status: "corrupt", sizeBytes, ...walBytes === void 0 ? {} : { walBytes }, reason: "journal quick_check failed" };
@@ -16159,7 +17074,7 @@ async function inspectWorkspace(workspaceRoot) {
16159
17074
  }
16160
17075
  }
16161
17076
  function readPinnedQuarantineFile(name, maxBytes, includeBytes, budget) {
16162
- if (path.basename(name) !== name || name === "." || name === "..") {
17077
+ if (path3__default.basename(name) !== name || name === "." || name === "..") {
16163
17078
  throw new Error("quarantine manifest contains an invalid evidence name");
16164
17079
  }
16165
17080
  const namedBefore = lstatSync(name, { bigint: true });
@@ -16263,10 +17178,10 @@ function inspectQuarantinePinned(dir, expectedDirectory) {
16263
17178
  if (isJournalQuarantineManifest(parsed)) {
16264
17179
  const manifestBase = manifestName.slice(0, -".manifest.json".length);
16265
17180
  const boundNames = parsed.files.map((file) => {
16266
- if (path.dirname(path.resolve(file)) !== path.resolve(".")) {
17181
+ if (path3__default.dirname(path3__default.resolve(file)) !== path3__default.resolve(".")) {
16267
17182
  throw new Error("journal quarantine manifest points outside quarantine");
16268
17183
  }
16269
- return path.basename(file);
17184
+ return path3__default.basename(file);
16270
17185
  });
16271
17186
  if (!boundNames.includes(manifestBase)) {
16272
17187
  throw new Error("journal quarantine manifest is not bound to its primary database evidence");
@@ -16306,7 +17221,7 @@ function inspectQuarantinePinned(dir, expectedDirectory) {
16306
17221
  }
16307
17222
  }
16308
17223
  async function inspectQuarantine(storeDir) {
16309
- const dir = path.join(storeDir, JOURNAL_QUARANTINE_DIRNAME);
17224
+ const dir = path3__default.join(storeDir, JOURNAL_QUARANTINE_DIRNAME);
16310
17225
  let directory;
16311
17226
  try {
16312
17227
  directory = await promises.lstat(dir, { bigint: true });
@@ -16377,7 +17292,7 @@ function checksFor(snapshot) {
16377
17292
  ];
16378
17293
  }
16379
17294
  async function collectDiagnostics(config, storeDir, options = {}) {
16380
- const resolvedStoreDir = path.resolve(storeDir);
17295
+ const resolvedStoreDir = path3__default.resolve(storeDir);
16381
17296
  const adapters = options.adapters ?? defaultRuntimeAdapters(config.runtimeAllowlist);
16382
17297
  const connectControl = options.connectControl ?? connectControlClient;
16383
17298
  const [device, probedRuntimes, health, journal, workspace, quarantine, controlConnection] = await Promise.all([
@@ -16552,7 +17467,7 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
16552
17467
  unlinkSync(sourcePath);
16553
17468
  sourceRemoved = true;
16554
17469
  if (process.platform !== "win32") {
16555
- const directoryFd = openSync(path.dirname(sourcePath), constants.O_RDONLY);
17470
+ const directoryFd = openSync(path3__default.dirname(sourcePath), constants.O_RDONLY);
16556
17471
  try {
16557
17472
  fsyncSync(directoryFd);
16558
17473
  } finally {
@@ -16591,10 +17506,10 @@ function publishQuarantineEvidencePinned(quarantineDir, expectedDirectory, sourc
16591
17506
  }
16592
17507
  }
16593
17508
  async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
16594
- const resolvedStoreDir = path.resolve(storeDir);
17509
+ const resolvedStoreDir = path3__default.resolve(storeDir);
16595
17510
  const owner = await acquireDaemonOwner(resolvedStoreDir, "doctor", options.clock);
16596
17511
  try {
16597
- const sourcePath = path.join(resolvedStoreDir, OPERATIONAL_HEALTH_FILENAME);
17512
+ const sourcePath = path3__default.join(resolvedStoreDir, OPERATIONAL_HEALTH_FILENAME);
16598
17513
  let opened;
16599
17514
  try {
16600
17515
  opened = await openOperationalHealthFile(resolvedStoreDir);
@@ -16611,7 +17526,7 @@ async function quarantineCorruptOperationalHealth(storeDir, options = {}) {
16611
17526
  }
16612
17527
  const sourceStat = await source.stat({ bigint: true });
16613
17528
  if (!sourceStat.isFile()) throw new Error("operational health state is not a regular file; refusing quarantine");
16614
- const quarantineDir = path.join(resolvedStoreDir, JOURNAL_QUARANTINE_DIRNAME);
17529
+ const quarantineDir = path3__default.join(resolvedStoreDir, JOURNAL_QUARANTINE_DIRNAME);
16615
17530
  try {
16616
17531
  const existing = await promises.lstat(quarantineDir);
16617
17532
  if (!existing.isDirectory() || existing.isSymbolicLink()) {
@@ -16712,8 +17627,8 @@ function buildServiceDefinition(config, configPath, rest) {
16712
17627
  const name = argValue(rest, "--name") ?? config.productId;
16713
17628
  const agentBin = argValue(rest, "--agent-bin") ?? process.argv[1] ?? "byok-agent";
16714
17629
  const nodeBin = argValue(rest, "--node-bin") ?? process.execPath;
16715
- const absoluteConfigPath = path.resolve(configPath);
16716
- const logDir = path.join(resolveStoreDir(config), "service-logs");
17630
+ const absoluteConfigPath = path3__default.resolve(configPath);
17631
+ const logDir = path3__default.join(resolveStoreDir(config), "service-logs");
16717
17632
  const definition = {
16718
17633
  name,
16719
17634
  displayName: config.branding?.displayName ?? config.productName,
@@ -16767,7 +17682,7 @@ async function runServiceStatusCommand(config, configPath, rest, deps = {}) {
16767
17682
  log(`detail: ${status.detail.trim() || "(none)"}`);
16768
17683
  }
16769
17684
  function auditLogPath(storeDir) {
16770
- return path.join(storeDir, "audit.jsonl");
17685
+ return path3__default.join(storeDir, "audit.jsonl");
16771
17686
  }
16772
17687
  var AUDIT_LOG_MODE = 384;
16773
17688
  var AUDIT_STORE_DIR_MODE = 448;
@@ -17605,11 +18520,11 @@ async function createSupportBundle(config, storeDir, options = {}) {
17605
18520
  };
17606
18521
  }
17607
18522
  async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
17608
- const dir = path.dirname(outputPath);
18523
+ const dir = path3__default.dirname(outputPath);
17609
18524
  const parentStat = await promises.stat(dir);
17610
18525
  if (!parentStat.isDirectory()) throw new Error("support bundle output parent is not a directory");
17611
- const privateDir = path.join(dir, `.${path.basename(outputPath)}.${process.pid}.${randomUUID()}.private`);
17612
- const tempPath = path.join(privateDir, "bundle.tmp");
18526
+ const privateDir = path3__default.join(dir, `.${path3__default.basename(outputPath)}.${process.pid}.${randomUUID()}.private`);
18527
+ const tempPath = path3__default.join(privateDir, "bundle.tmp");
17613
18528
  try {
17614
18529
  await promises.mkdir(privateDir, { mode: 448 });
17615
18530
  await ensureSecureDir(privateDir, secureFileOptions);
@@ -17639,7 +18554,7 @@ async function writeSupportBundle(outputPath, bundle, secureFileOptions = {}) {
17639
18554
  // src/bin/commands/support-bundle.ts
17640
18555
  async function runSupportBundleCommand(config, options) {
17641
18556
  if (!options.outputPath) throw new Error("support-bundle requires --output <path>");
17642
- const outputPath = path.resolve(options.outputPath);
18557
+ const outputPath = path3__default.resolve(options.outputPath);
17643
18558
  const bundle = await createSupportBundle(config, resolveStoreDir(config), options);
17644
18559
  await writeSupportBundle(outputPath, bundle);
17645
18560
  const log = options.log ?? ((line) => console.log(line));
@@ -17675,8 +18590,8 @@ async function runTasksFollowCommand(config, deps) {
17675
18590
  });
17676
18591
  return;
17677
18592
  }
17678
- const path34 = auditLogPath(storeDir);
17679
- await followAuditLog(path34, (event) => log(formatDaemonEventLine(event)), {
18593
+ const path36 = auditLogPath(storeDir);
18594
+ await followAuditLog(path36, (event) => log(formatDaemonEventLine(event)), {
17680
18595
  signal: deps.signal,
17681
18596
  pollIntervalMs: deps.pollIntervalMs,
17682
18597
  fromEnd: true