local-commander-agent 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/runtime/agent.mjs CHANGED
@@ -19,8 +19,8 @@ var __commonJS = (cb, mod) => function __require2() {
19
19
  }
20
20
  };
21
21
  var __export = (target, all) => {
22
- for (var name in all)
23
- __defProp(target, name, { get: all[name], enumerable: true });
22
+ for (var name2 in all)
23
+ __defProp(target, name2, { get: all[name2], enumerable: true });
24
24
  };
25
25
  var __copyProps = (to, from, except, desc) => {
26
26
  if (from && typeof from === "object" || typeof from === "function") {
@@ -2121,9 +2121,9 @@ var require_extension = __commonJS({
2121
2121
  "node_modules/ws/lib/extension.js"(exports, module) {
2122
2122
  "use strict";
2123
2123
  var { tokenChars } = require_validation();
2124
- function push(dest, name, elem) {
2125
- if (dest[name] === void 0) dest[name] = [elem];
2126
- else dest[name].push(elem);
2124
+ function push(dest, name2, elem) {
2125
+ if (dest[name2] === void 0) dest[name2] = [elem];
2126
+ else dest[name2].push(elem);
2127
2127
  }
2128
2128
  function parse3(header) {
2129
2129
  const offers = /* @__PURE__ */ Object.create(null);
@@ -2149,12 +2149,12 @@ var require_extension = __commonJS({
2149
2149
  throw new SyntaxError(`Unexpected character at index ${i}`);
2150
2150
  }
2151
2151
  if (end === -1) end = i;
2152
- const name = header.slice(start, end);
2152
+ const name2 = header.slice(start, end);
2153
2153
  if (code === 44) {
2154
- push(offers, name, params);
2154
+ push(offers, name2, params);
2155
2155
  params = /* @__PURE__ */ Object.create(null);
2156
2156
  } else {
2157
- extensionName = name;
2157
+ extensionName = name2;
2158
2158
  }
2159
2159
  start = end = -1;
2160
2160
  } else {
@@ -2278,7 +2278,7 @@ var require_websocket = __commonJS({
2278
2278
  var http = __require("http");
2279
2279
  var net = __require("net");
2280
2280
  var tls = __require("tls");
2281
- var { randomBytes: randomBytes4, createHash: createHash9 } = __require("crypto");
2281
+ var { randomBytes: randomBytes4, createHash: createHash10 } = __require("crypto");
2282
2282
  var { Duplex, Readable } = __require("stream");
2283
2283
  var { URL: URL2 } = __require("url");
2284
2284
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -2959,7 +2959,7 @@ var require_websocket = __commonJS({
2959
2959
  abortHandshake(websocket, socket, "Invalid Upgrade header");
2960
2960
  return;
2961
2961
  }
2962
- const digest4 = createHash9("sha1").update(key + GUID).digest("base64");
2962
+ const digest4 = createHash10("sha1").update(key + GUID).digest("base64");
2963
2963
  if (res.headers["sec-websocket-accept"] !== digest4) {
2964
2964
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2965
2965
  return;
@@ -3328,7 +3328,7 @@ var require_websocket_server = __commonJS({
3328
3328
  var EventEmitter = __require("events");
3329
3329
  var http = __require("http");
3330
3330
  var { Duplex } = __require("stream");
3331
- var { createHash: createHash9 } = __require("crypto");
3331
+ var { createHash: createHash10 } = __require("crypto");
3332
3332
  var extension2 = require_extension();
3333
3333
  var PerMessageDeflate2 = require_permessage_deflate();
3334
3334
  var subprotocol2 = require_subprotocol();
@@ -3635,7 +3635,7 @@ var require_websocket_server = __commonJS({
3635
3635
  );
3636
3636
  }
3637
3637
  if (this._state > RUNNING) return abortHandshake(socket, 503);
3638
- const digest4 = createHash9("sha1").update(key + GUID).digest("base64");
3638
+ const digest4 = createHash10("sha1").update(key + GUID).digest("base64");
3639
3639
  const headers = [
3640
3640
  "HTTP/1.1 101 Switching Protocols",
3641
3641
  "Upgrade: websocket",
@@ -3723,7 +3723,7 @@ var require_websocket_server = __commonJS({
3723
3723
  });
3724
3724
 
3725
3725
  // agent/src/main.ts
3726
- import path10 from "node:path";
3726
+ import path12 from "node:path";
3727
3727
  import os3 from "node:os";
3728
3728
  import { mkdir as mkdir5, open as open6, readFile as readFile5, rm as rm4 } from "node:fs/promises";
3729
3729
  import { fileURLToPath } from "node:url";
@@ -4674,10 +4674,10 @@ function mergeDefs(...defs) {
4674
4674
  function cloneDef(schema) {
4675
4675
  return mergeDefs(schema._zod.def);
4676
4676
  }
4677
- function getElementAtPath(obj, path11) {
4678
- if (!path11)
4677
+ function getElementAtPath(obj, path13) {
4678
+ if (!path13)
4679
4679
  return obj;
4680
- return path11.reduce((acc, key) => acc?.[key], obj);
4680
+ return path13.reduce((acc, key) => acc?.[key], obj);
4681
4681
  }
4682
4682
  function promiseAllObject(promisesObj) {
4683
4683
  const keys = Object.keys(promisesObj);
@@ -4976,12 +4976,12 @@ function merge(a, b) {
4976
4976
  });
4977
4977
  return clone(a, def);
4978
4978
  }
4979
- function partial(Class2, schema, mask, name = "partial") {
4979
+ function partial(Class2, schema, mask, name2 = "partial") {
4980
4980
  const currDef = schema._zod.def;
4981
4981
  const checks = currDef.checks;
4982
4982
  const hasChecks = checks && checks.length > 0;
4983
4983
  if (hasChecks) {
4984
- throw new Error(`.${name}() cannot be used on object schemas containing refinements`);
4984
+ throw new Error(`.${name2}() cannot be used on object schemas containing refinements`);
4985
4985
  }
4986
4986
  const selected = mask ? new Set(maskedKeys(schema, mask)) : void 0;
4987
4987
  const newShape = {};
@@ -5017,11 +5017,11 @@ function explicitlyAborted(x, startIndex = 0) {
5017
5017
  }
5018
5018
  return false;
5019
5019
  }
5020
- function prefixIssues(path11, issues) {
5020
+ function prefixIssues(path13, issues) {
5021
5021
  return issues.map((iss) => {
5022
5022
  var _a3;
5023
5023
  (_a3 = iss).path ?? (_a3.path = []);
5024
- iss.path.unshift(path11);
5024
+ iss.path.unshift(path13);
5025
5025
  return iss;
5026
5026
  });
5027
5027
  }
@@ -5309,7 +5309,7 @@ function newError(Definition) {
5309
5309
  return new Definition();
5310
5310
  }
5311
5311
  // @__NO_SIDE_EFFECTS__
5312
- function $constructor(name, initializer3, proto, params) {
5312
+ function $constructor(name2, initializer3, proto, params) {
5313
5313
  const zodProto = {};
5314
5314
  function Internals(def) {
5315
5315
  this.def = def;
@@ -5327,10 +5327,10 @@ function $constructor(name, initializer3, proto, params) {
5327
5327
  } finally {
5328
5328
  _zodDesc.value = void 0;
5329
5329
  }
5330
- } else if (inst._zod.traits.has(name)) {
5330
+ } else if (inst._zod.traits.has(name2)) {
5331
5331
  return;
5332
5332
  }
5333
- inst._zod.traits.add(name);
5333
+ inst._zod.traits.add(name2);
5334
5334
  initializer3(inst, def);
5335
5335
  if (initialized) {
5336
5336
  const own2 = Object.getPrototypeOf(inst);
@@ -5356,7 +5356,7 @@ function $constructor(name, initializer3, proto, params) {
5356
5356
  const Parent = params?.Parent ?? Object;
5357
5357
  class Definition extends Parent {
5358
5358
  }
5359
- Object.defineProperty(Definition, "name", { value: name });
5359
+ Object.defineProperty(Definition, "name", { value: name2 });
5360
5360
  function _(def) {
5361
5361
  const inst = params?.Parent ? newError(Definition) : this;
5362
5362
  init(inst, def);
@@ -5377,10 +5377,10 @@ function $constructor(name, initializer3, proto, params) {
5377
5377
  value: (inst) => {
5378
5378
  if (params?.Parent && inst instanceof params.Parent)
5379
5379
  return true;
5380
- return inst?._zod?.traits?.has(name);
5380
+ return inst?._zod?.traits?.has(name2);
5381
5381
  }
5382
5382
  });
5383
- Object.defineProperty(_, "name", { value: name });
5383
+ Object.defineProperty(_, "name", { value: name2 });
5384
5384
  return _;
5385
5385
  }
5386
5386
  var $brand = /* @__PURE__ */ Symbol("zod_brand");
@@ -5390,8 +5390,8 @@ var $ZodAsyncError = class extends Error {
5390
5390
  }
5391
5391
  };
5392
5392
  var $ZodEncodeError = class extends Error {
5393
- constructor(name) {
5394
- super(`Encountered unidirectional transform during encode: ${name}`);
5393
+ constructor(name2) {
5394
+ super(`Encountered unidirectional transform during encode: ${name2}`);
5395
5395
  this.name = "ZodEncodeError";
5396
5396
  }
5397
5397
  };
@@ -5471,16 +5471,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
5471
5471
  }
5472
5472
  function formatError(error62, mapper = (issue2) => issue2.message) {
5473
5473
  const fieldErrors = { _errors: [] };
5474
- const processError = (error63, path11 = []) => {
5474
+ const processError = (error63, path13 = []) => {
5475
5475
  for (const issue2 of error63.issues) {
5476
5476
  if (issue2.code === "invalid_union" && issue2.errors.length) {
5477
- issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
5477
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
5478
5478
  } else if (issue2.code === "invalid_key") {
5479
- processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
5479
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
5480
5480
  } else if (issue2.code === "invalid_element") {
5481
- processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
5481
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
5482
5482
  } else {
5483
- const fullpath = [...path11, ...issue2.path];
5483
+ const fullpath = [...path13, ...issue2.path];
5484
5484
  if (fullpath.length === 0) {
5485
5485
  fieldErrors._errors.push(mapper(issue2));
5486
5486
  } else {
@@ -5519,17 +5519,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
5519
5519
  }
5520
5520
  function treeifyError(error62, mapper = (issue2) => issue2.message) {
5521
5521
  const result2 = { errors: [] };
5522
- const processError = (error63, path11 = []) => {
5522
+ const processError = (error63, path13 = []) => {
5523
5523
  var _a3;
5524
5524
  for (const issue2 of error63.issues) {
5525
5525
  if (issue2.code === "invalid_union" && issue2.errors.length) {
5526
- issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
5526
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
5527
5527
  } else if (issue2.code === "invalid_key") {
5528
- processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
5528
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
5529
5529
  } else if (issue2.code === "invalid_element") {
5530
- processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
5530
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
5531
5531
  } else {
5532
- const fullpath = [...path11, ...issue2.path];
5532
+ const fullpath = [...path13, ...issue2.path];
5533
5533
  if (fullpath.length === 0) {
5534
5534
  result2.errors.push(mapper(issue2));
5535
5535
  continue;
@@ -5568,8 +5568,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
5568
5568
  }
5569
5569
  function toDotPath(_path) {
5570
5570
  const segs = [];
5571
- const path11 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5572
- for (const seg of path11) {
5571
+ const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5572
+ for (const seg of path13) {
5573
5573
  if (typeof seg === "number")
5574
5574
  segs.push(`[${seg}]`);
5575
5575
  else if (typeof seg === "symbol")
@@ -16890,13 +16890,13 @@ ${code}
16890
16890
  return fn;
16891
16891
  }
16892
16892
  function addConstant(ctx, value) {
16893
- for (const [name2, v] of ctx.constants) {
16893
+ for (const [name3, v] of ctx.constants) {
16894
16894
  if (v === value)
16895
- return name2;
16895
+ return name3;
16896
16896
  }
16897
- const name = `c${ctx.constantCounter++}`;
16898
- ctx.constants.set(name, value);
16899
- return name;
16897
+ const name2 = `c${ctx.constantCounter++}`;
16898
+ ctx.constants.set(name2, value);
16899
+ return name2;
16900
16900
  }
16901
16901
  function addUserConstant(ctx, fn) {
16902
16902
  ctx.definite = false;
@@ -22671,13 +22671,13 @@ function resolveRef(ref, ctx) {
22671
22671
  if (!ref.startsWith("#")) {
22672
22672
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
22673
22673
  }
22674
- const path11 = ref.slice(1).split("/").filter(Boolean);
22675
- if (path11.length === 0) {
22674
+ const path13 = ref.slice(1).split("/").filter(Boolean);
22675
+ if (path13.length === 0) {
22676
22676
  return ctx.rootSchema;
22677
22677
  }
22678
22678
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
22679
- if (path11[0] === defsKey) {
22680
- const key = path11[1] === void 0 ? void 0 : decodeJSONPointerSegment(path11[1]);
22679
+ if (path13[0] === defsKey) {
22680
+ const key = path13[1] === void 0 ? void 0 : decodeJSONPointerSegment(path13[1]);
22681
22681
  if (!key || !ctx.defs[key]) {
22682
22682
  throw new Error(`Reference not found: ${ref}`);
22683
22683
  }
@@ -23643,9 +23643,9 @@ function isDisjoint(...headers) {
23643
23643
  }
23644
23644
  return true;
23645
23645
  }
23646
- function assertNotSet(value, name) {
23646
+ function assertNotSet(value, name2) {
23647
23647
  if (value !== void 0)
23648
- throw new TypeError(`${name} can only be called once`);
23648
+ throw new TypeError(`${name2} can only be called once`);
23649
23649
  }
23650
23650
  function decodeBase64url(value, label, ErrorClass) {
23651
23651
  try {
@@ -23806,7 +23806,7 @@ function invalidKeyType(alg, actual, secret) {
23806
23806
  const types = ["CryptoKey", "KeyObject", "JSON Web Key"];
23807
23807
  return secret && types.push("Uint8Array"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));
23808
23808
  }
23809
- var unusable = (name, prop = "algorithm.name") => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
23809
+ var unusable = (name2, prop = "algorithm.name") => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name2}`);
23810
23810
  function checkUsage(key, usage) {
23811
23811
  if (usage && !key.usages.includes(usage))
23812
23812
  throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
@@ -24547,9 +24547,9 @@ function guardAction(binding2, tool, config2, observedAt) {
24547
24547
  if (!binding2.accountId || binding2.tenantId !== config2.tenantId || binding2.deviceId !== config2.deviceId || !tool.effective) {
24548
24548
  throw Error("GUARD_IDENTITY_REQUIRED");
24549
24549
  }
24550
- const name = tool.call.name;
24551
- const kind = name === "command_run" ? "execute" : name === "localhost_request" ? "localhost-access" : name === "fs_write" || name === "fs_replace" ? "write" : name.startsWith("git_") ? "git-read" : "read";
24552
- const resourceKind = kind === "execute" ? "workspace" : kind === "localhost-access" ? "localhost-service" : kind === "git-read" ? "git-repository" : name === "fs_read" || kind === "write" ? "file" : "workspace";
24550
+ const name2 = tool.call.name;
24551
+ const kind = name2 === "command_run" ? "execute" : name2 === "localhost_request" ? "localhost-access" : name2 === "fs_write" || name2 === "fs_replace" ? "write" : name2.startsWith("git_") ? "git-read" : "read";
24552
+ const resourceKind = kind === "execute" ? "workspace" : kind === "localhost-access" ? "localhost-service" : kind === "git-read" ? "git-repository" : name2 === "fs_read" || kind === "write" ? "file" : "workspace";
24553
24553
  return { payloadHash: guardHash(tool.effective), request: BoundActionRequestSchema.parse({
24554
24554
  protocolVersion: 1,
24555
24555
  requestId: binding2.executionId,
@@ -24569,13 +24569,13 @@ function guardAction(binding2, tool, config2, observedAt) {
24569
24569
  adapter: guardAdapter,
24570
24570
  actor: { kind: "human", subjectRef: binding2.accountId, authenticationClass: "delegated" },
24571
24571
  agent: { agentRef: "grant:" + binding2.grantId },
24572
- tool: { namespace: "local-commander", name },
24572
+ tool: { namespace: "local-commander", name: name2 },
24573
24573
  resource: { kind: resourceKind, locatorFingerprint: guardHash({
24574
24574
  workspace: binding2.workspaceGrantId,
24575
24575
  resources: tool.claims,
24576
- ...name === "localhost_request" ? { url: tool.call.args.url } : {}
24576
+ ...name2 === "localhost_request" ? { url: tool.call.args.url } : {}
24577
24577
  }) },
24578
- action: { kind, verb: name, effect: kind === "execute" ? "external-mutation" : kind === "write" ? "local-mutation" : kind === "localhost-access" ? "external-mutation" : "read-only" },
24578
+ action: { kind, verb: name2, effect: kind === "execute" ? "external-mutation" : kind === "write" ? "local-mutation" : kind === "localhost-access" ? "external-mutation" : "read-only" },
24579
24579
  dataClassifications: ["unknown"],
24580
24580
  environment: config2.environment
24581
24581
  }
@@ -24585,7 +24585,7 @@ function guardSummary(tool) {
24585
24585
  if (!tool.effective) throw Error("GUARD_ACTION_INVALID");
24586
24586
  const call = tool.call;
24587
24587
  const args = { ...call.args };
24588
- if (args.headers) args.headers = Object.keys(args.headers).map((name) => name + " (value hidden)");
24588
+ if (args.headers) args.headers = Object.keys(args.headers).map((name2) => name2 + " (value hidden)");
24589
24589
  return JSON.stringify({
24590
24590
  tool: call.name,
24591
24591
  resources: tool.claims,
@@ -24695,7 +24695,7 @@ function canonicalTime(value) {
24695
24695
  }
24696
24696
  function validateInput(value) {
24697
24697
  if (!value || typeof value !== "object" || Object.keys(value).some((k) => !bindingKeys.includes(k))) throw failure2("JOURNAL_INPUT_INVALID");
24698
- for (const name of bindingKeys.slice(0, 6)) if (typeof value[name] !== "string" || !uuid11.test(value[name])) throw failure2("JOURNAL_INPUT_INVALID");
24698
+ for (const name2 of bindingKeys.slice(0, 6)) if (typeof value[name2] !== "string" || !uuid11.test(value[name2])) throw failure2("JOURNAL_INPUT_INVALID");
24699
24699
  if (value.accountId !== void 0 && !uuid11.test(value.accountId)) throw failure2("JOURNAL_INPUT_INVALID");
24700
24700
  if (typeof value.connectionEpoch !== "string" || !/^[1-9]\d{0,18}$/.test(value.connectionEpoch) || BigInt(value.connectionEpoch) > 9223372036854775807n || typeof value.payloadHash !== "string" || !hash4.test(value.payloadHash) || !canonicalTime(value.deadlineAt)) throw failure2("JOURNAL_INPUT_INVALID");
24701
24701
  if (value.workspaceGrantId === void 0 !== (value.policyVersion === void 0) || value.workspaceGrantId !== void 0 && (!uuid11.test(value.workspaceGrantId) || !Number.isInteger(value.policyVersion) || value.policyVersion < 1 || value.policyVersion > 2147483647)) throw failure2("JOURNAL_INPUT_INVALID");
@@ -24817,9 +24817,9 @@ var NativeJournal = class _NativeJournal {
24817
24817
  async load() {
24818
24818
  const names = await readdir(this.dir);
24819
24819
  if (names.length > this.options.maxEntries + 256) throw failure2("JOURNAL_CAPACITY_EXCEEDED");
24820
- for (const name of names) {
24821
- if (!name.endsWith(".entry")) continue;
24822
- const id = name.slice(0, -6);
24820
+ for (const name2 of names) {
24821
+ if (!name2.endsWith(".entry")) continue;
24822
+ const id = name2.slice(0, -6);
24823
24823
  if (!uuid11.test(id)) throw failure2("JOURNAL_CORRUPT");
24824
24824
  const envelope = await this.readOwned(id);
24825
24825
  this.totalBytes += envelope.length;
@@ -25083,10 +25083,10 @@ var NativeJournal = class _NativeJournal {
25083
25083
 
25084
25084
  // agent/src/private-store.ts
25085
25085
  import { constants as constants2 } from "node:fs";
25086
- import { lstat as lstat2, open as open3, readFile as readFile2, readdir as readdir2, realpath as realpath2, rename as rename2, rm as rm2, writeFile } from "node:fs/promises";
25087
- import path3 from "node:path";
25086
+ import { lstat as lstat3, open as open3, readFile as readFile2, readdir as readdir2, realpath as realpath3, rename as rename2, rm as rm2, writeFile } from "node:fs/promises";
25087
+ import path5 from "node:path";
25088
25088
  import os from "node:os";
25089
- import { createHash as createHash7, createPrivateKey, createPublicKey as createPublicKey3, generateKeyPairSync, randomBytes as randomBytes3, randomUUID as randomUUID3 } from "node:crypto";
25089
+ import { createHash as createHash8, createPrivateKey, createPublicKey as createPublicKey3, generateKeyPairSync, randomBytes as randomBytes3, randomUUID as randomUUID3 } from "node:crypto";
25090
25090
  import { spawn } from "node:child_process";
25091
25091
 
25092
25092
  // src/child-environment.ts
@@ -25203,13 +25203,123 @@ function buildChildEnvironment(source, platform = process.platform) {
25203
25203
  return result2;
25204
25204
  }
25205
25205
 
25206
+ // agent/src/access-selection.ts
25207
+ import path3 from "node:path";
25208
+ function parseAccessSelection(value) {
25209
+ const fail5 = () => {
25210
+ throw Error("ACCESS_SELECTION_INVALID");
25211
+ };
25212
+ if (!value || typeof value !== "object" || Array.isArray(value)) return fail5();
25213
+ const input2 = value;
25214
+ if (!Array.isArray(input2.paths)) return fail5();
25215
+ if (input2.mode === "cancelled") {
25216
+ if (input2.paths.length !== 0) return fail5();
25217
+ return null;
25218
+ }
25219
+ if (input2.mode !== "folders" && input2.mode !== "all-drives") return fail5();
25220
+ if (input2.paths.length < 1 || input2.paths.length > (input2.mode === "folders" ? 1 : 26)) return fail5();
25221
+ const roots = [];
25222
+ for (const candidate of input2.paths) {
25223
+ if (typeof candidate !== "string" || candidate.length > 4096 || /[\u0000-\u001f]/.test(candidate) || !path3.win32.isAbsolute(candidate)) return fail5();
25224
+ if (input2.mode === "all-drives" && !/^[a-z]:\\$/i.test(candidate)) return fail5();
25225
+ const normalized = path3.win32.normalize(candidate).toLowerCase();
25226
+ if (roots.some((root) => path3.win32.normalize(root).toLowerCase() === normalized)) return fail5();
25227
+ roots.push(candidate);
25228
+ }
25229
+ return { mode: input2.mode, paths: roots };
25230
+ }
25231
+
25232
+ // agent/src/owner-permissions.ts
25233
+ import { createHash as createHash4 } from "node:crypto";
25234
+ import { lstat as lstat2, realpath as realpath2 } from "node:fs/promises";
25235
+ import path4 from "node:path";
25236
+ var uuid12 = external_exports.string().uuid().toLowerCase();
25237
+ var name = external_exports.string().regex(/^[a-zA-Z0-9_.-]{1,64}$/).toLowerCase();
25238
+ var changeSchema = external_exports.discriminatedUnion("kind", [
25239
+ external_exports.object({ workspaceGrantId: uuid12, kind: external_exports.literal("write"), enabled: external_exports.boolean() }).strict(),
25240
+ external_exports.object({ workspaceGrantId: uuid12, kind: external_exports.literal("localhost"), enabled: external_exports.boolean(), port: external_exports.number().int().min(1).max(65535) }).strict(),
25241
+ external_exports.object({ workspaceGrantId: uuid12, kind: external_exports.literal("command"), enabled: external_exports.boolean(), name, executable: external_exports.string().min(1).max(4096).optional() }).strict()
25242
+ ]);
25243
+ var PERMISSION_HISTORY_LIMIT = 64;
25244
+ var PermissionHistorySchema = external_exports.array(external_exports.object({
25245
+ id: uuid12,
25246
+ changedAt: external_exports.string().datetime(),
25247
+ workspaceGrantId: uuid12,
25248
+ kind: external_exports.enum(["write", "command", "localhost"]),
25249
+ enabled: external_exports.boolean(),
25250
+ resourceHash: external_exports.string().regex(/^[a-f0-9]{64}$/),
25251
+ ownerSid: external_exports.string().max(184).regex(/^S-1-(?:[0-9]+-)+[0-9]+$/)
25252
+ }).strict()).max(PERMISSION_HISTORY_LIMIT);
25253
+ function permissionHash(value) {
25254
+ return createHash4("sha256").update(JSON.stringify(value)).digest("hex");
25255
+ }
25256
+ function invalid2(code = "PERMISSION_INVALID") {
25257
+ throw Object.assign(new Error(code), { code });
25258
+ }
25259
+ function parsePermissionChange(value) {
25260
+ const parsed = changeSchema.safeParse(value);
25261
+ if (!parsed.success) invalid2();
25262
+ const change = parsed.data;
25263
+ if (change.kind === "command" && (change.enabled ? !change.executable : change.executable !== void 0)) invalid2();
25264
+ return change;
25265
+ }
25266
+ function equalPath(a, b) {
25267
+ return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
25268
+ }
25269
+ function permissionLabel(value) {
25270
+ return JSON.stringify(value).replace(
25271
+ /[\u007f-\u009f\u061c\u200b-\u200f\u2028-\u202e\u2060-\u2069\ufeff]/gu,
25272
+ (ch) => "\\u" + ch.charCodeAt(0).toString(16).padStart(4, "0")
25273
+ );
25274
+ }
25275
+ function nonAsciiNotice(value) {
25276
+ if (!value || !/[^\x20-\x7e]/.test(value)) return "";
25277
+ return "\nWarning: this path contains non-ASCII characters: " + value.replace(/[^\x20-\x7e]/g, (ch) => "\\u" + ch.charCodeAt(0).toString(16).padStart(4, "0"));
25278
+ }
25279
+ async function planPermissionChange(current2, programs, input2) {
25280
+ const change = parsePermissionChange(input2);
25281
+ if (current2.workspaceGrantId !== change.workspaceGrantId) invalid2("WORKSPACE_UNKNOWN");
25282
+ const workspace = structuredClone(current2), nextPrograms = { ...programs };
25283
+ let resource = { rootHash: workspace.rootHash, kind: change.kind };
25284
+ let detail;
25285
+ if (change.kind === "write") {
25286
+ workspace.permissions.writeFiles = change.enabled;
25287
+ detail = "File writes: " + (change.enabled ? "ALLOW" : "DENY");
25288
+ } else if (change.kind === "localhost") {
25289
+ workspace.localhostPorts = change.enabled ? [.../* @__PURE__ */ new Set([...workspace.localhostPorts, change.port])].sort((a, b) => a - b) : workspace.localhostPorts.filter((port) => port !== change.port);
25290
+ if (workspace.localhostPorts.length > 32) invalid2("PERMISSION_LIMIT");
25291
+ workspace.permissions.localhost = workspace.localhostPorts.length > 0;
25292
+ resource = { rootHash: workspace.rootHash, kind: change.kind, port: change.port };
25293
+ detail = "Localhost port " + change.port + ": " + (change.enabled ? "ALLOW" : "DENY");
25294
+ } else {
25295
+ let executableIdentity = null;
25296
+ if (change.enabled) {
25297
+ const executable = change.executable;
25298
+ if (!path4.isAbsolute(executable) || path4.extname(executable).toLowerCase() !== ".exe" || /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u2028-\u202e\u2060-\u2069\ufeff]/u.test(executable)) invalid2();
25299
+ const stat = await lstat2(executable).catch(() => invalid2());
25300
+ const canonical2 = await realpath2(executable).catch(() => invalid2());
25301
+ if (!stat.isFile() || stat.isSymbolicLink() || !equalPath(path4.resolve(executable), canonical2)) invalid2();
25302
+ if (Object.hasOwn(nextPrograms, change.name) && !equalPath(nextPrograms[change.name], canonical2)) invalid2("COMMAND_CONFLICT");
25303
+ nextPrograms[change.name] = canonical2;
25304
+ workspace.allowedCommands = [.../* @__PURE__ */ new Set([...workspace.allowedCommands, change.name])].sort();
25305
+ if (workspace.allowedCommands.length > 32 || Object.keys(nextPrograms).length > 32) invalid2("PERMISSION_LIMIT");
25306
+ executableIdentity = { executable: canonical2, dev: stat.dev, ino: stat.ino, size: stat.size, mtimeMs: stat.mtimeMs, ctimeMs: stat.ctimeMs };
25307
+ } else workspace.allowedCommands = workspace.allowedCommands.filter((value) => value !== change.name);
25308
+ workspace.permissions.executeCommands = workspace.allowedCommands.length > 0;
25309
+ resource = { rootHash: workspace.rootHash, kind: change.kind, name: change.name, executable: nextPrograms[change.name] ?? null, executableIdentity };
25310
+ detail = "Command " + permissionLabel(change.name) + ": " + (change.enabled ? "ALLOW" : "DENY") + "\nExact executable: " + permissionLabel(nextPrograms[change.name] ?? "not registered") + nonAsciiNotice(nextPrograms[change.name]) + "\nA permitted program can access resources available to your Windows account, including outside this folder. This is not an OS sandbox.";
25311
+ }
25312
+ const summary = "Change Local Commander permissions\nWorkspace: " + permissionLabel(current2.displayName) + "\nWorkspace ID: " + current2.workspaceGrantId + "\nFolder: " + permissionLabel(current2.root) + "\n\n" + detail + "\n\nThis change persists until you revoke it. Other workspaces are not granted this permission.\nReview before approving. Closing this window, rejecting or timing out changes nothing.\nA running agent may restart after the change; running work can be interrupted. Revocation does not undo completed effects.";
25313
+ return { workspace, programs: nextPrograms, change, summary, resourceHash: permissionHash(resource) };
25314
+ }
25315
+
25206
25316
  // packages/policy/src/signed-bundle.ts
25207
- import { createHash as createHash5, KeyObject as KeyObject2 } from "node:crypto";
25317
+ import { createHash as createHash6, KeyObject as KeyObject2 } from "node:crypto";
25208
25318
 
25209
25319
  // packages/policy/src/evaluator.ts
25210
- import { createHash as createHash4 } from "node:crypto";
25320
+ import { createHash as createHash5 } from "node:crypto";
25211
25321
  import { isDeepStrictEqual } from "node:util";
25212
- var uuid12 = external_exports.string().uuid().toLowerCase();
25322
+ var uuid13 = external_exports.string().uuid().toLowerCase();
25213
25323
  var slug2 = external_exports.string().regex(/^[a-z][a-z0-9._-]{0,63}$/);
25214
25324
  var utc = external_exports.iso.datetime({ offset: false });
25215
25325
  var risk = external_exports.enum(["low", "moderate", "high", "critical"]);
@@ -25234,7 +25344,7 @@ var PolicyRuleSchema = external_exports.object({
25234
25344
  }
25235
25345
  });
25236
25346
  var PolicySnapshotSchema = external_exports.object({
25237
- tenantId: uuid12,
25347
+ tenantId: uuid13,
25238
25348
  policyId: slug2,
25239
25349
  policyVersion: external_exports.string().trim().min(1).max(64),
25240
25350
  issuedAt: utc,
@@ -25264,7 +25374,7 @@ function matches(rule, request, requestRisk) {
25264
25374
  return (!rule.actionKinds || rule.actionKinds.includes(action.kind)) && (!rule.toolNamespaces || rule.toolNamespaces.includes(tool.namespace)) && (!rule.environments || rule.environments.includes(environment.environment)) && (!rule.anyDataClassifications || rule.anyDataClassifications.some((value) => dataClassifications.includes(value))) && (!rule.resourceKinds || rule.resourceKinds.includes(resource.kind)) && (!rule.resourceFingerprints || rule.resourceFingerprints.includes(resource.locatorFingerprint)) && (!rule.riskClasses || rule.riskClasses.includes(requestRisk));
25265
25375
  }
25266
25376
  function decisionId(requestId, policyId, policyVersion, ruleId) {
25267
- const bytes3 = createHash4("sha256").update(JSON.stringify([requestId, policyId, policyVersion, ruleId])).digest().subarray(0, 16);
25377
+ const bytes3 = createHash5("sha256").update(JSON.stringify([requestId, policyId, policyVersion, ruleId])).digest().subarray(0, 16);
25268
25378
  bytes3[6] = bytes3[6] & 15 | 64;
25269
25379
  bytes3[8] = bytes3[8] & 63 | 128;
25270
25380
  const hex3 = bytes3.toString("hex");
@@ -25333,7 +25443,7 @@ async function verifySignedPolicyBundle(candidate, authority, checkpointInput, n
25333
25443
  if (payload.byteLength > 256 * 1024) throw new Error();
25334
25444
  const bundle = BundleSchema.parse(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(payload)));
25335
25445
  const { snapshot: snapshot2 } = bundle;
25336
- const payloadSha256 = createHash5("sha256").update(payload).digest("hex");
25446
+ const payloadSha256 = createHash6("sha256").update(payload).digest("hex");
25337
25447
  if (snapshot2.tenantId !== expected.tenantId || snapshot2.policyId !== expected.policyId || at < Date.parse(snapshot2.issuedAt) || at >= Date.parse(snapshot2.expiresAt) || bundle.sequence < checkpoint.sequence || bundle.sequence === checkpoint.sequence && payloadSha256 !== checkpoint.payloadSha256) throw new Error();
25338
25448
  return { snapshot: snapshot2, checkpoint: {
25339
25449
  ...expected,
@@ -25347,15 +25457,15 @@ async function verifySignedPolicyBundle(candidate, authority, checkpointInput, n
25347
25457
  }
25348
25458
 
25349
25459
  // packages/policy/src/approvals.ts
25350
- import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
25351
- var uuid13 = TrustedActionBindingSchema.shape.tenantId;
25460
+ import { createHash as createHash7, randomUUID as randomUUID2 } from "node:crypto";
25461
+ var uuid14 = TrustedActionBindingSchema.shape.tenantId;
25352
25462
  var utc3 = TrustedActionBindingSchema.shape.expiresAt;
25353
25463
  var digest3 = ResourceRefSchema.shape.locatorFingerprint;
25354
25464
  var requirement = PolicyDecisionSchema.shape.approval.unwrap();
25355
25465
  var ApprovalActionSchema = external_exports.object({ request: BoundActionRequestSchema, payloadHash: digest3 }).strict();
25356
25466
  var ApprovalAuthoritySchema = external_exports.object({
25357
- tenantId: uuid13,
25358
- accountId: uuid13,
25467
+ tenantId: uuid14,
25468
+ accountId: uuid14,
25359
25469
  approvalClass: requirement.shape.approvalClass,
25360
25470
  authenticatedAt: utc3,
25361
25471
  expiresAt: utc3
@@ -25367,11 +25477,11 @@ var ContextSchema = external_exports.object({
25367
25477
  policyExpiresAt: utc3
25368
25478
  }).strict();
25369
25479
  var GrantSchema = external_exports.object({
25370
- approvalId: uuid13,
25371
- requestId: uuid13,
25372
- decisionId: uuid13,
25480
+ approvalId: uuid14,
25481
+ requestId: uuid14,
25482
+ decisionId: uuid14,
25373
25483
  bindingHash: digest3,
25374
- approverAccountId: uuid13,
25484
+ approverAccountId: uuid14,
25375
25485
  approvalClass: requirement.shape.approvalClass,
25376
25486
  scope: requirement.shape.scope,
25377
25487
  issuedAt: utc3,
@@ -25380,8 +25490,8 @@ var GrantSchema = external_exports.object({
25380
25490
  uses: external_exports.number().int().min(0).max(Number.MAX_SAFE_INTEGER)
25381
25491
  }).strict().refine((value) => Date.parse(value.issuedAt) < Date.parse(value.expiresAt) && (value.scope === "once" ? value.uses === (value.state === "consumed" ? 1 : 0) : value.state !== "consumed"));
25382
25492
  var ApprovalLedgerSchema = external_exports.object({
25383
- tenantId: uuid13,
25384
- deviceId: uuid13,
25493
+ tenantId: uuid14,
25494
+ deviceId: uuid14,
25385
25495
  lastChangedAt: utc3,
25386
25496
  grants: external_exports.array(GrantSchema).max(128)
25387
25497
  }).strict().refine((value) => new Set(value.grants.map((grant) => grant.approvalId)).size === value.grants.length && new Set(value.grants.map((grant) => grant.requestId)).size === value.grants.length && value.grants.every((grant) => Date.parse(grant.issuedAt) <= Date.parse(value.lastChangedAt)));
@@ -25402,7 +25512,7 @@ function binding(ledger, input2, now) {
25402
25512
  approval.expiresAt ? Date.parse(approval.expiresAt) : Infinity
25403
25513
  );
25404
25514
  if (at >= deadline) throw Error();
25405
- const bindingHash = createHash6("sha256").update(JSON.stringify([
25515
+ const bindingHash = createHash7("sha256").update(JSON.stringify([
25406
25516
  owner.bindingSource,
25407
25517
  owner.tenantId,
25408
25518
  owner.accountId,
@@ -25458,7 +25568,7 @@ function issueApproval(ledgerInput, context, authorityInput, now, expiresAt) {
25458
25568
  }
25459
25569
  function consumeApproval(ledgerInput, context, approvalId, now) {
25460
25570
  try {
25461
- const ledger = current(ledgerInput, now), bound = binding(ledger, context, now), id = uuid13.parse(approvalId);
25571
+ const ledger = current(ledgerInput, now), bound = binding(ledger, context, now), id = uuid14.parse(approvalId);
25462
25572
  const grant = ledger.grants.find((candidate) => candidate.approvalId === id);
25463
25573
  if (!grant || grant.state !== "active" || grant.bindingHash !== bound.bindingHash || grant.uses === Number.MAX_SAFE_INTEGER || Date.parse(now) < Date.parse(grant.issuedAt) || grant.scope === "once" && (grant.requestId !== bound.request.requestId || grant.decisionId !== bound.decision.decisionId)) throw Error();
25464
25574
  const consumed = { ...grant, uses: grant.uses + 1, state: grant.scope === "once" ? "consumed" : "active" };
@@ -25469,7 +25579,7 @@ function consumeApproval(ledgerInput, context, approvalId, now) {
25469
25579
  }
25470
25580
  function revokeApproval(ledgerInput, approvalId, now) {
25471
25581
  try {
25472
- const ledger = current(ledgerInput, now), id = uuid13.parse(approvalId);
25582
+ const ledger = current(ledgerInput, now), id = uuid14.parse(approvalId);
25473
25583
  return { ...ledger, grants: ledger.grants.map((grant) => grant.approvalId === id && grant.state === "active" ? { ...grant, state: "revoked" } : grant) };
25474
25584
  } catch {
25475
25585
  throw Error("APPROVAL_INVALID");
@@ -25484,12 +25594,12 @@ var MAX_STATE_BYTES = 256 * 1024;
25484
25594
  function fail(code) {
25485
25595
  return Object.assign(new Error(code), { code });
25486
25596
  }
25487
- function equalPath(a, b) {
25488
- return path3.resolve(a).toLowerCase() === path3.resolve(b).toLowerCase();
25597
+ function equalPath2(a, b) {
25598
+ return path5.resolve(a).toLowerCase() === path5.resolve(b).toLowerCase();
25489
25599
  }
25490
25600
  function under(child, parent) {
25491
- const rel = path3.relative(parent, child);
25492
- return rel !== "" && !rel.startsWith("..") && !path3.isAbsolute(rel);
25601
+ const rel = path5.relative(parent, child);
25602
+ return rel !== "" && !rel.startsWith("..") && !path5.isAbsolute(rel);
25493
25603
  }
25494
25604
  function canonicalPublicKey(value) {
25495
25605
  try {
@@ -25501,7 +25611,7 @@ function canonicalPublicKey(value) {
25501
25611
  }
25502
25612
  }
25503
25613
  function fingerprint(publicKeyPem) {
25504
- return createHash7("sha256").update(canonicalPublicKey(publicKeyPem).der).digest("hex");
25614
+ return createHash8("sha256").update(canonicalPublicKey(publicKeyPem).der).digest("hex");
25505
25615
  }
25506
25616
  function publicFromPrivate(privateKeyPem) {
25507
25617
  try {
@@ -25560,55 +25670,57 @@ function validateState(value) {
25560
25670
  }
25561
25671
  const approvals = value.approvals === void 0 ? void 0 : ApprovalLedgerSchema.safeParse(value.approvals);
25562
25672
  if (approvals && (!approvals.success || approvals.data.tenantId !== enrollment?.tenantId || approvals.data.deviceId !== enrollment.deviceId)) throw fail("STATE_CORRUPT");
25673
+ const permissionHistory = PermissionHistorySchema.safeParse(value.permissionHistory ?? []);
25674
+ if (!permissionHistory.success) throw fail("STATE_CORRUPT");
25563
25675
  const workspaces = value.workspaces ?? [];
25564
25676
  const programs = value.programs ?? {};
25565
25677
  const pendingWorkspaceRevokes = value.pendingWorkspaceRevokes ?? [];
25566
25678
  if (!Array.isArray(pendingWorkspaceRevokes) || pendingWorkspaceRevokes.length > 32 || pendingWorkspaceRevokes.some((id) => typeof id !== "string" || !UUID.test(id) || id !== id.toLowerCase()) || new Set(pendingWorkspaceRevokes).size !== pendingWorkspaceRevokes.length) throw fail("STATE_CORRUPT");
25567
- if (!programs || typeof programs !== "object" || Array.isArray(programs) || Object.keys(programs).length > 32 || Object.entries(programs).some(([name, executable]) => !/^[a-z0-9_.-]{1,64}$/.test(name) || typeof executable !== "string" || !path3.isAbsolute(executable) || executable.length > 4096 || /[\u0000-\u001f]/.test(executable))) throw fail("STATE_CORRUPT");
25568
- if (!Array.isArray(workspaces) || workspaces.length > 32 || workspaces.some((w) => !w || !UUID.test(w.workspaceGrantId) || typeof w.root !== "string" || !path3.isAbsolute(w.root) || w.root.length > 4096 || /[\u0000-\u001f]/.test(w.root) || !/^[a-f0-9]{64}$/.test(w.rootHash) || typeof w.displayName !== "string" || !w.displayName || w.displayName.length > 120 || w.policyVersion !== 1 || !w.permissions || w.permissions.readFiles !== true || typeof w.permissions.writeFiles !== "boolean" || typeof w.permissions.executeCommands !== "boolean" || typeof w.permissions.localhost !== "boolean" || !Array.isArray(w.allowedCommands) || w.allowedCommands.length > 32 || w.allowedCommands.some((name) => typeof name !== "string" || !Object.hasOwn(programs, name)) || new Set(w.allowedCommands).size !== w.allowedCommands.length || w.permissions.executeCommands !== w.allowedCommands.length > 0 || !Array.isArray(w.localhostPorts) || w.localhostPorts.length > 32 || w.localhostPorts.some((port) => !Number.isInteger(port) || port < 1 || port > 65535) || new Set(w.localhostPorts).size !== w.localhostPorts.length || w.permissions.localhost !== w.localhostPorts.length > 0)) throw fail("STATE_CORRUPT");
25679
+ if (!programs || typeof programs !== "object" || Array.isArray(programs) || Object.keys(programs).length > 32 || Object.entries(programs).some(([name2, executable]) => !/^[a-z0-9_.-]{1,64}$/.test(name2) || typeof executable !== "string" || !path5.isAbsolute(executable) || executable.length > 4096 || /[\u0000-\u001f]/.test(executable))) throw fail("STATE_CORRUPT");
25680
+ if (!Array.isArray(workspaces) || workspaces.length > 32 || workspaces.some((w) => !w || !UUID.test(w.workspaceGrantId) || typeof w.root !== "string" || !path5.isAbsolute(w.root) || w.root.length > 4096 || /[\u0000-\u001f]/.test(w.root) || !/^[a-f0-9]{64}$/.test(w.rootHash) || typeof w.displayName !== "string" || !w.displayName || w.displayName.length > 120 || w.policyVersion !== 1 || !w.permissions || w.permissions.readFiles !== true || typeof w.permissions.writeFiles !== "boolean" || typeof w.permissions.executeCommands !== "boolean" || typeof w.permissions.localhost !== "boolean" || !Array.isArray(w.allowedCommands) || w.allowedCommands.length > 32 || w.allowedCommands.some((name2) => typeof name2 !== "string" || !Object.hasOwn(programs, name2)) || new Set(w.allowedCommands).size !== w.allowedCommands.length || w.permissions.executeCommands !== w.allowedCommands.length > 0 || !Array.isArray(w.localhostPorts) || w.localhostPorts.length > 32 || w.localhostPorts.some((port) => !Number.isInteger(port) || port < 1 || port > 65535) || new Set(w.localhostPorts).size !== w.localhostPorts.length || w.permissions.localhost !== w.localhostPorts.length > 0)) throw fail("STATE_CORRUPT");
25569
25681
  if (new Set(workspaces.map((w) => w.workspaceGrantId)).size !== workspaces.length || new Set(workspaces.map((w) => w.rootHash)).size !== workspaces.length) throw fail("STATE_CORRUPT");
25570
25682
  if (workspaces.some((w) => pendingWorkspaceRevokes.includes(w.workspaceGrantId))) throw fail("STATE_CORRUPT");
25571
- return { version: 1, publicKeyPem: value.publicKeyPem, privateKeyPem: value.privateKeyPem, fingerprint: value.fingerprint, dataKey: value.dataKey, ...enrollment ? { enrollment } : {}, workspaces, programs, pendingWorkspaceRevokes, ...policyBundle ? { policyBundle } : {}, ...approvals?.success ? { approvals: approvals.data } : {}, ...value.guardRequired ? { guardRequired: true } : {}, ...guard ? { guard } : {} };
25683
+ return { version: 1, publicKeyPem: value.publicKeyPem, privateKeyPem: value.privateKeyPem, fingerprint: value.fingerprint, dataKey: value.dataKey, ...enrollment ? { enrollment } : {}, workspaces, programs, pendingWorkspaceRevokes, ...value.permissionHistory ? { permissionHistory: permissionHistory.data } : {}, ...policyBundle ? { policyBundle } : {}, ...approvals?.success ? { approvals: approvals.data } : {}, ...value.guardRequired ? { guardRequired: true } : {}, ...guard ? { guard } : {} };
25572
25684
  }
25573
25685
  async function noSymlinkChain(target) {
25574
- if (!path3.isAbsolute(target)) throw fail("STORE_DIRECTORY_INVALID");
25575
- const absolute2 = path3.resolve(target);
25576
- if (absolute2 === path3.parse(absolute2).root) throw fail("STORE_DIRECTORY_INVALID");
25686
+ if (!path5.isAbsolute(target)) throw fail("STORE_DIRECTORY_INVALID");
25687
+ const absolute2 = path5.resolve(target);
25688
+ if (absolute2 === path5.parse(absolute2).root) throw fail("STORE_DIRECTORY_INVALID");
25577
25689
  const missing = [];
25578
25690
  let cursor = absolute2;
25579
25691
  for (; ; ) {
25580
25692
  try {
25581
- const stat = await lstat2(cursor);
25693
+ const stat = await lstat3(cursor);
25582
25694
  if (stat.isSymbolicLink()) throw fail("STORE_DIRECTORY_INVALID");
25583
25695
  break;
25584
25696
  } catch (error62) {
25585
25697
  if (error62.code !== "ENOENT") throw error62;
25586
25698
  missing.push(cursor);
25587
- const parent = path3.dirname(cursor);
25699
+ const parent = path5.dirname(cursor);
25588
25700
  if (parent === cursor) throw fail("STORE_DIRECTORY_INVALID");
25589
25701
  cursor = parent;
25590
25702
  }
25591
25703
  }
25592
- const canonicalParent = await realpath2(cursor);
25593
- if (!equalPath(cursor, canonicalParent)) throw fail("STORE_DIRECTORY_INVALID");
25704
+ const canonicalParent = await realpath3(cursor);
25705
+ if (!equalPath2(cursor, canonicalParent)) throw fail("STORE_DIRECTORY_INVALID");
25594
25706
  return absolute2;
25595
25707
  }
25596
25708
  function allowedDirectory(absolute2) {
25597
- const localAppData = process.env.LOCALAPPDATA || path3.join(os.homedir(), "AppData", "Local");
25598
- const appRoot = path3.resolve(localAppData, "LocalCommander");
25599
- const tempRoot = path3.resolve(os.tmpdir());
25709
+ const localAppData = process.env.LOCALAPPDATA || path5.join(os.homedir(), "AppData", "Local");
25710
+ const appRoot = path5.resolve(localAppData, "LocalCommander");
25711
+ const tempRoot = path5.resolve(os.tmpdir());
25600
25712
  if (under(absolute2, appRoot)) return;
25601
- if (under(absolute2, tempRoot) && /^lc-private-[A-Za-z0-9_.-]+$/i.test(path3.basename(absolute2))) return;
25713
+ if (under(absolute2, tempRoot) && /^lc-private-[A-Za-z0-9_.-]+$/i.test(path5.basename(absolute2))) return;
25602
25714
  throw fail("STORE_DIRECTORY_POLICY");
25603
25715
  }
25604
25716
  async function canonicalBridge(value) {
25605
- if (typeof value !== "string" || !path3.isAbsolute(value) || path3.extname(value).toLowerCase() !== ".exe") throw fail("BRIDGE_INVALID");
25606
- const stat = await lstat2(value).catch(() => {
25717
+ if (typeof value !== "string" || !path5.isAbsolute(value) || path5.extname(value).toLowerCase() !== ".exe") throw fail("BRIDGE_INVALID");
25718
+ const stat = await lstat3(value).catch(() => {
25607
25719
  throw fail("BRIDGE_INVALID");
25608
25720
  });
25609
25721
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size < 1 || stat.size > 4 * 1024 * 1024) throw fail("BRIDGE_INVALID");
25610
- const canonical2 = await realpath2(value);
25611
- if (!equalPath(value, canonical2)) throw fail("BRIDGE_INVALID");
25722
+ const canonical2 = await realpath3(value);
25723
+ if (!equalPath2(value, canonical2)) throw fail("BRIDGE_INVALID");
25612
25724
  return canonical2;
25613
25725
  }
25614
25726
  function parseBridgeData(value, max = MAX_STATE_BYTES * 4) {
@@ -25640,11 +25752,11 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
25640
25752
  }
25641
25753
  const approved = /* @__PURE__ */ new Set([MARKER, "owner-state.dat", "owner-state.lock", "journal"]);
25642
25754
  if (names.length && !names.includes(MARKER)) throw fail("STORE_DIRECTORY_UNKNOWN");
25643
- if (names.some((name) => !approved.has(name))) throw fail("STORE_DIRECTORY_UNKNOWN");
25755
+ if (names.some((name2) => !approved.has(name2))) throw fail("STORE_DIRECTORY_UNKNOWN");
25644
25756
  await _WindowsPrivateStore.invoke(bridge, "init-directory", { path: absolute2 }, 5e3, false);
25645
- const canonical2 = await realpath2(absolute2);
25646
- if (!equalPath(absolute2, canonical2) || (await lstat2(canonical2)).isSymbolicLink()) throw fail("STORE_DIRECTORY_INVALID");
25647
- const markerPath = path3.join(canonical2, MARKER);
25757
+ const canonical2 = await realpath3(absolute2);
25758
+ if (!equalPath2(absolute2, canonical2) || (await lstat3(canonical2)).isSymbolicLink()) throw fail("STORE_DIRECTORY_INVALID");
25759
+ const markerPath = path5.join(canonical2, MARKER);
25648
25760
  try {
25649
25761
  await writeFile(markerPath, "LocalCommander private state\n", { flag: "wx", mode: 384 });
25650
25762
  } catch (error62) {
@@ -25659,10 +25771,10 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
25659
25771
  return result2;
25660
25772
  }
25661
25773
  statePath() {
25662
- return path3.join(this.dir, "owner-state.dat");
25774
+ return path5.join(this.dir, "owner-state.dat");
25663
25775
  }
25664
25776
  lockPath() {
25665
- return path3.join(this.dir, "owner-state.lock");
25777
+ return path5.join(this.dir, "owner-state.lock");
25666
25778
  }
25667
25779
  static invoke(bridge, arg, body, timeoutMs2, interactive, signal) {
25668
25780
  return new Promise((resolve, reject) => {
@@ -25757,9 +25869,21 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
25757
25869
  }
25758
25870
  }
25759
25871
  }
25872
+ /** After an owner approval, a briefly busy lock (the running agent) must not discard the decision. */
25873
+ async withLockWait(fn, waitMs) {
25874
+ const deadline = Date.now() + waitMs;
25875
+ for (; ; ) {
25876
+ try {
25877
+ return await this.withLock(fn);
25878
+ } catch (error62) {
25879
+ if (error62.code !== "STORE_LOCKED" || Date.now() >= deadline) throw error62;
25880
+ await new Promise((resolve) => setTimeout(resolve, 100));
25881
+ }
25882
+ }
25883
+ }
25760
25884
  async readState() {
25761
25885
  try {
25762
- const stat = await lstat2(this.statePath());
25886
+ const stat = await lstat3(this.statePath());
25763
25887
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size < 1 || stat.size > MAX_STATE_BYTES * 4) throw fail("STATE_CORRUPT");
25764
25888
  const encrypted = await readFile2(this.statePath());
25765
25889
  const plaintext = await this.unprotect(encrypted);
@@ -25773,9 +25897,9 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
25773
25897
  }
25774
25898
  async rawHash() {
25775
25899
  try {
25776
- const stat = await lstat2(this.statePath());
25900
+ const stat = await lstat3(this.statePath());
25777
25901
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_STATE_BYTES * 4) throw fail("STATE_CORRUPT");
25778
- return createHash7("sha256").update(await readFile2(this.statePath())).digest("hex");
25902
+ return createHash8("sha256").update(await readFile2(this.statePath())).digest("hex");
25779
25903
  } catch (error62) {
25780
25904
  if (error62.code === "ENOENT") return null;
25781
25905
  throw error62;
@@ -25786,7 +25910,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
25786
25910
  const plaintext = Buffer.from(JSON.stringify(validateState(state)), "utf8");
25787
25911
  if (plaintext.length > MAX_STATE_BYTES) throw fail("STATE_TOO_LARGE");
25788
25912
  const encrypted = await this.protect(plaintext);
25789
- const temp = path3.join(this.dir, "owner-state." + randomBytes3(8).toString("hex") + ".tmp");
25913
+ const temp = path5.join(this.dir, "owner-state." + randomBytes3(8).toString("hex") + ".tmp");
25790
25914
  const handle = await open3(temp, constants2.O_CREAT | constants2.O_EXCL | constants2.O_WRONLY, 384);
25791
25915
  let closed = false;
25792
25916
  try {
@@ -25838,6 +25962,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
25838
25962
  state.workspaces = [];
25839
25963
  state.programs = {};
25840
25964
  state.pendingWorkspaceRevokes = [];
25965
+ delete state.permissionHistory;
25841
25966
  }
25842
25967
  state.enrollment = next;
25843
25968
  await this.writeState(state, before);
@@ -25855,6 +25980,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
25855
25980
  state.workspaces = [];
25856
25981
  state.programs = {};
25857
25982
  state.pendingWorkspaceRevokes = [];
25983
+ delete state.permissionHistory;
25858
25984
  await this.writeState(state, before);
25859
25985
  }));
25860
25986
  }
@@ -25974,7 +26100,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
25974
26100
  "guard-review",
25975
26101
  { data: Buffer.from(summary).toString("base64") },
25976
26102
  timeoutMs2,
25977
- false,
26103
+ true,
25978
26104
  signal
25979
26105
  );
25980
26106
  if (typeof answer?.approved !== "boolean" || typeof answer.ownerSid !== "string" || !/^S-1-(?:[0-9]+-)+[0-9]+$/.test(answer.ownerSid)) throw fail("GUARD_REVIEW_INVALID");
@@ -26016,7 +26142,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
26016
26142
  });
26017
26143
  }
26018
26144
  loadPrograms() {
26019
- return this.run(async () => Object.fromEntries(Object.entries((await this.readState())?.programs ?? {}).map(([name, executable]) => [name, { executable }])));
26145
+ return this.run(async () => Object.fromEntries(Object.entries((await this.readState())?.programs ?? {}).map(([name2, executable]) => [name2, { executable }])));
26020
26146
  }
26021
26147
  loadPendingWorkspaceRevokes() {
26022
26148
  return this.run(async () => [...(await this.readState())?.pendingWorkspaceRevokes ?? []]);
@@ -26036,7 +26162,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
26036
26162
  const before = await this.rawHash();
26037
26163
  state.workspaces = state.workspaces.filter((w) => w.workspaceGrantId !== id);
26038
26164
  state.pendingWorkspaceRevokes = [...state.pendingWorkspaceRevokes ?? [], id];
26039
- for (const name of Object.keys(state.programs ?? {})) if (!state.workspaces.some((w) => w.allowedCommands.includes(name))) delete state.programs[name];
26165
+ for (const name2 of Object.keys(state.programs ?? {})) if (!state.workspaces.some((w) => w.allowedCommands.includes(name2))) delete state.programs[name2];
26040
26166
  await this.writeState(state, before);
26041
26167
  }));
26042
26168
  }
@@ -26049,51 +26175,112 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
26049
26175
  await this.writeState(state, before);
26050
26176
  }));
26051
26177
  }
26052
- setWorkspacePermissions(change) {
26053
- return this.run(() => this.withLock(async () => {
26054
- const state = await this.readState();
26178
+ async setWorkspacePermissions(change) {
26179
+ const request = parsePermissionChange(change);
26180
+ const context = (state) => {
26055
26181
  if (!state?.enrollment) throw fail("ENROLLMENT_REQUIRED");
26056
- if (typeof change.workspaceGrantId !== "string" || !UUID.test(change.workspaceGrantId) || typeof change.enabled !== "boolean") throw fail("PERMISSION_INVALID");
26057
- const current2 = state.workspaces?.find((w) => w.workspaceGrantId === change.workspaceGrantId.toLowerCase());
26058
- if (!current2) throw fail("WORKSPACE_UNKNOWN");
26059
- const workspace = structuredClone(current2), programs = { ...state.programs ?? {} };
26060
- if (change.kind === "write") workspace.permissions.writeFiles = change.enabled;
26061
- else if (change.kind === "localhost") {
26062
- if (!Number.isInteger(change.port) || change.port < 1 || change.port > 65535) throw fail("PERMISSION_INVALID");
26063
- workspace.localhostPorts = change.enabled ? [.../* @__PURE__ */ new Set([...workspace.localhostPorts, change.port])].sort((a, b) => a - b) : workspace.localhostPorts.filter((port) => port !== change.port);
26064
- workspace.permissions.localhost = workspace.localhostPorts.length > 0;
26065
- } else if (change.kind === "command") {
26066
- const name = change.name?.toLowerCase();
26067
- if (!name || !/^[a-z0-9_.-]{1,64}$/.test(name)) throw fail("PERMISSION_INVALID");
26068
- if (change.enabled) {
26069
- const executable = change.executable;
26070
- if (typeof executable !== "string" || !path3.isAbsolute(executable) || path3.extname(executable).toLowerCase() !== ".exe" || executable.length > 4096 || /[\u0000-\u001f]/.test(executable)) throw fail("PERMISSION_INVALID");
26071
- const stat = await lstat2(executable).catch(() => {
26072
- throw fail("PERMISSION_INVALID");
26073
- });
26074
- if (!stat.isFile() || stat.isSymbolicLink() || !equalPath(executable, await realpath2(executable))) throw fail("PERMISSION_INVALID");
26075
- if (Object.hasOwn(programs, name) && !equalPath(programs[name], executable)) throw fail("COMMAND_CONFLICT");
26076
- programs[name] = executable;
26077
- workspace.allowedCommands = [.../* @__PURE__ */ new Set([...workspace.allowedCommands, name])].sort();
26078
- } else workspace.allowedCommands = workspace.allowedCommands.filter((value) => value !== name);
26079
- workspace.permissions.executeCommands = workspace.allowedCommands.length > 0;
26080
- } else throw fail("PERMISSION_INVALID");
26081
- state.workspaces = state.workspaces.map((w) => w.workspaceGrantId === workspace.workspaceGrantId ? workspace : w);
26082
- for (const name of Object.keys(programs)) if (!state.workspaces.some((w) => w.allowedCommands.includes(name))) delete programs[name];
26083
- state.programs = programs;
26182
+ const workspace = state.workspaces?.find((w) => w.workspaceGrantId === request.workspaceGrantId);
26183
+ if (!workspace) throw fail("WORKSPACE_UNKNOWN");
26184
+ const programs = state.programs ?? {};
26185
+ return { workspace, programs, enrollment: state.enrollment, fingerprint: permissionHash({
26186
+ enrollment: state.enrollment,
26187
+ workspace,
26188
+ programs: Object.entries(programs).sort(([a], [b]) => a.localeCompare(b)),
26189
+ guard: state.guard ?? null,
26190
+ guardRequired: state.guardRequired ?? false
26191
+ }) };
26192
+ };
26193
+ const initial = await this.run(async () => context(await this.readState()));
26194
+ const proposal = await planPermissionChange(initial.workspace, initial.programs, request);
26195
+ const review = await this.reviewGuard("Account: " + permissionLabel(initial.enrollment.accountEmail) + "\n" + proposal.summary, void 0, 6e4);
26196
+ if (!review.approved) throw fail("PERMISSION_REJECTED");
26197
+ return this.run(() => this.withLockWait(async () => {
26198
+ const state = await this.readState();
26199
+ const current2 = context(state);
26200
+ if (current2.fingerprint !== initial.fingerprint) throw fail("PERMISSION_STATE_CHANGED");
26201
+ if (state.guard && state.guard.ownerSid !== review.ownerSid) throw fail("PERMISSION_OWNER_MISMATCH");
26202
+ const verified = await planPermissionChange(current2.workspace, current2.programs, request);
26203
+ if (verified.resourceHash !== proposal.resourceHash) throw fail("PERMISSION_STATE_CHANGED");
26084
26204
  const before = await this.rawHash();
26205
+ state.workspaces = state.workspaces.map((w) => w.workspaceGrantId === verified.workspace.workspaceGrantId ? verified.workspace : w);
26206
+ state.programs = verified.programs;
26207
+ for (const name2 of Object.keys(state.programs)) if (!state.workspaces.some((w) => w.allowedCommands.includes(name2))) delete state.programs[name2];
26208
+ const record2 = {
26209
+ id: randomUUID3(),
26210
+ changedAt: (/* @__PURE__ */ new Date()).toISOString(),
26211
+ workspaceGrantId: request.workspaceGrantId,
26212
+ kind: request.kind,
26213
+ enabled: request.enabled,
26214
+ resourceHash: verified.resourceHash,
26215
+ ownerSid: review.ownerSid
26216
+ };
26217
+ state.permissionHistory = PermissionHistorySchema.parse([...state.permissionHistory ?? [], record2].slice(-PERMISSION_HISTORY_LIMIT));
26085
26218
  await this.writeState(state, before);
26086
- return workspace;
26219
+ return structuredClone(verified.workspace);
26220
+ }, 5e3));
26221
+ }
26222
+ /** Replace scope only after an explicit local dialog; updates never call this implicitly. */
26223
+ async configureAccess() {
26224
+ const expected = await this.run(() => this.withLock(async () => {
26225
+ if (!(await this.readState())?.enrollment) throw fail("ENROLLMENT_REQUIRED");
26226
+ return this.rawHash();
26087
26227
  }));
26228
+ const decision = await this.chooseAccess();
26229
+ if (decision === null) return null;
26230
+ const selected = parseAccessSelection(decision);
26231
+ if (!selected) return null;
26232
+ const roots = [];
26233
+ for (const chosen of selected.paths) {
26234
+ const stat = await lstat3(chosen);
26235
+ if (!stat.isDirectory() || stat.isSymbolicLink()) throw fail("WORKSPACE_INVALID");
26236
+ const root = await realpath3(chosen);
26237
+ const isDrive = equalPath2(root, path5.parse(root).root);
26238
+ if (!equalPath2(root, chosen) || (selected.mode === "all-drives" ? !isDrive : isDrive) || equalPath2(root, this.dir) || under(root, this.dir) || selected.mode === "folders" && under(this.dir, root)) throw fail("WORKSPACE_INVALID");
26239
+ roots.push(root);
26240
+ }
26241
+ return this.run(() => this.withLock(async () => {
26242
+ if (await this.rawHash() !== expected) throw fail("STATE_CHANGED");
26243
+ const state = await this.readState();
26244
+ if (!state?.enrollment) throw fail("ENROLLMENT_REQUIRED");
26245
+ const workspaces = roots.map((root) => {
26246
+ const rootHash = createHash8("sha256").update(process.platform === "win32" ? root.toLowerCase() : root).digest("hex");
26247
+ const existing = state.workspaces?.find((w) => w.rootHash === rootHash);
26248
+ return {
26249
+ workspaceGrantId: existing?.workspaceGrantId ?? randomUUID3(),
26250
+ root,
26251
+ rootHash,
26252
+ displayName: (path5.basename(root) || root).slice(0, 120),
26253
+ policyVersion: 1,
26254
+ permissions: { readFiles: true, writeFiles: true, executeCommands: false, localhost: false },
26255
+ allowedCommands: [],
26256
+ localhostPorts: []
26257
+ };
26258
+ });
26259
+ const retained = new Set(workspaces.map((w) => w.workspaceGrantId));
26260
+ const removed = (state.workspaces ?? []).filter((w) => !retained.has(w.workspaceGrantId)).map((w) => w.workspaceGrantId);
26261
+ const pending = [.../* @__PURE__ */ new Set([...state.pendingWorkspaceRevokes ?? [], ...removed])];
26262
+ if (pending.length > 32) throw fail("WORKSPACE_REVOKE_LIMIT");
26263
+ state.workspaces = workspaces;
26264
+ state.programs = {};
26265
+ state.pendingWorkspaceRevokes = pending;
26266
+ await this.writeState(state, expected);
26267
+ return structuredClone(workspaces);
26268
+ }));
26269
+ }
26270
+ chooseAccess() {
26271
+ return this.run(async () => parseAccessSelection(await _WindowsPrivateStore.invoke(this.bridge, "choose-access", {}, 24e4, true)));
26272
+ }
26273
+ loadPermissionHistory() {
26274
+ return this.run(async () => structuredClone((await this.readState())?.permissionHistory ?? []));
26088
26275
  }
26089
26276
  async approveWorkspace() {
26090
26277
  const chosen = await this.chooseFolder();
26091
26278
  if (chosen === null) return null;
26092
- const stat = await lstat2(chosen);
26279
+ const stat = await lstat3(chosen);
26093
26280
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw fail("WORKSPACE_INVALID");
26094
- const root = await realpath2(chosen);
26095
- if (!equalPath(root, chosen) || equalPath(root, path3.parse(root).root) || equalPath(root, this.dir) || under(this.dir, root)) throw fail("WORKSPACE_INVALID");
26096
- const rootHash = createHash7("sha256").update(process.platform === "win32" ? root.toLowerCase() : root).digest("hex");
26281
+ const root = await realpath3(chosen);
26282
+ if (!equalPath2(root, chosen) || equalPath2(root, path5.parse(root).root) || equalPath2(root, this.dir) || under(this.dir, root)) throw fail("WORKSPACE_INVALID");
26283
+ const rootHash = createHash8("sha256").update(process.platform === "win32" ? root.toLowerCase() : root).digest("hex");
26097
26284
  return this.run(() => this.withLock(async () => {
26098
26285
  const state = await this.readState();
26099
26286
  if (!state?.enrollment) throw fail("ENROLLMENT_REQUIRED");
@@ -26101,7 +26288,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
26101
26288
  if (existing) return existing;
26102
26289
  if ((state.workspaces?.length ?? 0) >= 32) throw fail("WORKSPACE_LIMIT");
26103
26290
  const before = await this.rawHash();
26104
- const workspace = { workspaceGrantId: randomUUID3(), root, rootHash, displayName: path3.basename(root).slice(0, 120), policyVersion: 1, permissions: { readFiles: true, writeFiles: false, executeCommands: false, localhost: false }, allowedCommands: [], localhostPorts: [] };
26291
+ const workspace = { workspaceGrantId: randomUUID3(), root, rootHash, displayName: path5.basename(root).slice(0, 120), policyVersion: 1, permissions: { readFiles: true, writeFiles: true, executeCommands: false, localhost: false }, allowedCommands: [], localhostPorts: [] };
26105
26292
  state.workspaces = [...state.workspaces ?? [], workspace];
26106
26293
  await this.writeState(state, before);
26107
26294
  return workspace;
@@ -26111,7 +26298,7 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
26111
26298
  return this.run(async () => {
26112
26299
  const result2 = await _WindowsPrivateStore.invoke(this.bridge, "choose-folder", {}, 12e4, true);
26113
26300
  if (result2.path === null) return null;
26114
- if (typeof result2.path !== "string" || !path3.isAbsolute(result2.path) || result2.path.length > 4096 || /[\u0000-\u001f]/.test(result2.path)) throw fail("BRIDGE_OUTPUT_INVALID");
26301
+ if (typeof result2.path !== "string" || !path5.isAbsolute(result2.path) || result2.path.length > 4096 || /[\u0000-\u001f]/.test(result2.path)) throw fail("BRIDGE_OUTPUT_INVALID");
26115
26302
  return result2.path;
26116
26303
  });
26117
26304
  }
@@ -26119,22 +26306,40 @@ var WindowsPrivateStore = class _WindowsPrivateStore {
26119
26306
 
26120
26307
  // agent/src/tools.ts
26121
26308
  import { constants as constants3 } from "node:fs";
26122
- import { lstat as lstat4, open as open4, readdir as readdir3, realpath as realpath4, rename as rename3, rm as rm3 } from "node:fs/promises";
26123
- import path7 from "node:path";
26124
- import { createHash as createHash8, randomUUID as randomUUID4 } from "node:crypto";
26309
+ import { lstat as lstat5, open as open4, readdir as readdir3, realpath as realpath5, rename as rename3, rm as rm3 } from "node:fs/promises";
26310
+ import path9 from "node:path";
26311
+ import { createHash as createHash9, randomUUID as randomUUID4 } from "node:crypto";
26125
26312
 
26126
26313
  // src/guard.ts
26127
- import { lstat as lstat3, realpath as realpath3 } from "node:fs/promises";
26128
- import path4 from "node:path";
26314
+ import { lstat as lstat4, realpath as realpath4 } from "node:fs/promises";
26315
+ import path6 from "node:path";
26129
26316
  var SENSITIVE_NAMES = /* @__PURE__ */ new Set([
26130
26317
  ".npmrc",
26131
26318
  ".pypirc",
26132
26319
  ".git-credentials",
26133
26320
  "id_rsa",
26134
26321
  "id_ed25519",
26135
- "credentials.json"
26322
+ "id_ecdsa",
26323
+ "id_dsa",
26324
+ ".netrc",
26325
+ "_netrc",
26326
+ ".pgpass",
26327
+ "credentials.json",
26328
+ "application_default_credentials.json"
26136
26329
  ]);
26137
- var SENSITIVE_EXTENSIONS = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx"]);
26330
+ var SENSITIVE_EXTENSIONS = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx", ".ppk", ".kdbx"]);
26331
+ var SENSITIVE_IN_DIRECTORY = [
26332
+ [".aws", "credentials"],
26333
+ [".kube", "config"],
26334
+ [".docker", "config.json"],
26335
+ [".claude", ".credentials.json"],
26336
+ [".codex", "auth.json"],
26337
+ [".azure", "msal_token_cache.json"],
26338
+ [".azure", "accesstokens.json"],
26339
+ ["gh", "hosts.yml"],
26340
+ ["github cli", "hosts.yml"]
26341
+ ];
26342
+ var SENSITIVE_DIRECTORIES = /* @__PURE__ */ new Set([".ssh", ".gnupg"]);
26138
26343
  var PROTECTED_FILE_NAMES = /* @__PURE__ */ new Set([
26139
26344
  ".local-commander-oauth.json",
26140
26345
  "local-commander.config.json"
@@ -26144,12 +26349,12 @@ function comparePath(value) {
26144
26349
  return process.platform === "win32" ? value.toLowerCase() : value;
26145
26350
  }
26146
26351
  function isInside(root, candidate) {
26147
- const relative = path4.relative(root, candidate);
26148
- return relative === "" || !path4.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path4.sep}`);
26352
+ const relative = path6.relative(root, candidate);
26353
+ return relative === "" || !path6.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path6.sep}`);
26149
26354
  }
26150
26355
  function splitPathSegments(filePath) {
26151
- const parsed = path4.parse(path4.resolve(filePath));
26152
- return path4.relative(parsed.root, path4.resolve(filePath)).split(path4.sep).filter((segment) => segment.length > 0);
26356
+ const parsed = path6.parse(path6.resolve(filePath));
26357
+ return path6.relative(parsed.root, path6.resolve(filePath)).split(path6.sep).filter((segment) => segment.length > 0);
26153
26358
  }
26154
26359
  function isProtectedAuditFile(base) {
26155
26360
  return base === ".local-commander-audit.jsonl" || base.startsWith(".local-commander-audit.jsonl.");
@@ -26164,37 +26369,40 @@ function looksProtected(filePath) {
26164
26369
  return false;
26165
26370
  }
26166
26371
  function looksSensitive(filePath) {
26167
- const base = path4.basename(filePath).toLowerCase();
26372
+ const base = path6.basename(filePath).toLowerCase();
26168
26373
  if (base === ".env" || base.startsWith(".env.") && base !== ".env.example") return true;
26169
26374
  if (SENSITIVE_NAMES.has(base)) return true;
26170
- if (SENSITIVE_EXTENSIONS.has(path4.extname(base))) return true;
26171
- return false;
26375
+ if (SENSITIVE_EXTENSIONS.has(path6.extname(base))) return true;
26376
+ const segments = splitPathSegments(filePath).map((segment) => segment.toLowerCase());
26377
+ if (segments.slice(0, -1).some((segment) => SENSITIVE_DIRECTORIES.has(segment))) return true;
26378
+ const parent = segments.at(-2);
26379
+ return SENSITIVE_IN_DIRECTORY.some(([directory, name2]) => directory === parent && name2 === base);
26172
26380
  }
26173
26381
  async function canonicalizePossiblyMissing(inputPath, base) {
26174
- const absolute2 = path4.isAbsolute(inputPath) ? path4.resolve(inputPath) : path4.resolve(base, inputPath);
26175
- const parsed = path4.parse(absolute2);
26382
+ const absolute2 = path6.isAbsolute(inputPath) ? path6.resolve(inputPath) : path6.resolve(base, inputPath);
26383
+ const parsed = path6.parse(absolute2);
26176
26384
  let current2 = absolute2;
26177
26385
  const missing = [];
26178
26386
  while (current2 !== parsed.root) {
26179
26387
  try {
26180
- const resolved = await realpath3(current2);
26181
- return path4.join(resolved, ...missing.reverse());
26388
+ const resolved = await realpath4(current2);
26389
+ return path6.join(resolved, ...missing.reverse());
26182
26390
  } catch (error62) {
26183
26391
  if (error62.code !== "ENOENT") throw error62;
26184
26392
  let exists = false;
26185
26393
  try {
26186
- await lstat3(current2);
26394
+ await lstat4(current2);
26187
26395
  exists = true;
26188
26396
  } catch (statError) {
26189
26397
  if (statError.code !== "ENOENT") throw statError;
26190
26398
  }
26191
26399
  if (exists) throw error62;
26192
- missing.push(path4.basename(current2));
26193
- current2 = path4.dirname(current2);
26400
+ missing.push(path6.basename(current2));
26401
+ current2 = path6.dirname(current2);
26194
26402
  }
26195
26403
  }
26196
- const root = await realpath3(parsed.root);
26197
- return path4.join(root, ...missing.reverse());
26404
+ const root = await realpath4(parsed.root);
26405
+ return path6.join(root, ...missing.reverse());
26198
26406
  }
26199
26407
  var PathGuard = class _PathGuard {
26200
26408
  constructor(roots, allowSensitiveFiles, protectedPaths) {
@@ -26210,11 +26418,11 @@ var PathGuard = class _PathGuard {
26210
26418
  for (const root of roots) {
26211
26419
  let resolved;
26212
26420
  try {
26213
- resolved = await realpath3(path4.resolve(root));
26421
+ resolved = await realpath4(path6.resolve(root));
26214
26422
  } catch {
26215
26423
  throw new Error(`Configured root does not exist: ${root}`);
26216
26424
  }
26217
- const stat = await lstat3(resolved);
26425
+ const stat = await lstat4(resolved);
26218
26426
  if (!stat.isDirectory()) throw new Error(`Configured root is not a directory: ${root}`);
26219
26427
  if (!canonical2.some((existing) => comparePath(existing) === comparePath(resolved))) {
26220
26428
  canonical2.push(resolved);
@@ -26223,8 +26431,8 @@ var PathGuard = class _PathGuard {
26223
26431
  if (canonical2.length === 0) throw new Error("At least one allowed root is required");
26224
26432
  const protectedPaths = [];
26225
26433
  for (const protectedPath of options.protectedPaths ?? []) {
26226
- const base = path4.isAbsolute(protectedPath) ? path4.parse(path4.resolve(protectedPath)).root : canonical2[0];
26227
- const absolute2 = path4.resolve(base, protectedPath);
26434
+ const base = path6.isAbsolute(protectedPath) ? path6.parse(path6.resolve(protectedPath)).root : canonical2[0];
26435
+ const absolute2 = path6.resolve(base, protectedPath);
26228
26436
  if (!protectedPaths.some((existing) => comparePath(existing) === comparePath(absolute2))) protectedPaths.push(absolute2);
26229
26437
  const resolved = await canonicalizePossiblyMissing(protectedPath, base);
26230
26438
  if (!protectedPaths.some((existing) => comparePath(existing) === comparePath(resolved))) {
@@ -26240,7 +26448,7 @@ var PathGuard = class _PathGuard {
26240
26448
  }
26241
26449
  assertNotProtected(candidate) {
26242
26450
  if (looksProtected(candidate)) {
26243
- throw new Error(`Local Commander protected state is blocked: ${path4.basename(candidate)}`);
26451
+ throw new Error(`Local Commander protected state is blocked: ${path6.basename(candidate)}`);
26244
26452
  }
26245
26453
  const normalizedCandidate = comparePath(candidate);
26246
26454
  for (const protectedPath of this.protectedPaths) {
@@ -26253,37 +26461,37 @@ var PathGuard = class _PathGuard {
26253
26461
  this.assertNotProtected(filePath);
26254
26462
  if (!this.allowSensitiveFiles && looksSensitive(filePath)) {
26255
26463
  throw new Error(
26256
- `Sensitive file access is blocked by default: ${path4.basename(filePath)}. Enable files.allowSensitiveFiles only if you really need it.`
26464
+ `Sensitive file access is blocked by default: ${path6.basename(filePath)}. Enable files.allowSensitiveFiles only if you really need it.`
26257
26465
  );
26258
26466
  }
26259
26467
  }
26260
26468
  async resolveExisting(inputPath, cwd) {
26261
26469
  const base = cwd ? await this.resolveExisting(cwd) : this.roots[0];
26262
- const candidate = path4.isAbsolute(inputPath) ? path4.resolve(inputPath) : path4.resolve(base, inputPath);
26470
+ const candidate = path6.isAbsolute(inputPath) ? path6.resolve(inputPath) : path6.resolve(base, inputPath);
26263
26471
  this.assertNotProtected(candidate);
26264
- const resolved = await realpath3(candidate);
26472
+ const resolved = await realpath4(candidate);
26265
26473
  this.assertWithinRoots(resolved);
26266
26474
  this.assertNotProtected(resolved);
26267
26475
  return resolved;
26268
26476
  }
26269
26477
  async resolveDirectory(inputPath, cwd) {
26270
26478
  const resolved = await this.resolveExisting(inputPath, cwd);
26271
- const stat = await lstat3(resolved);
26479
+ const stat = await lstat4(resolved);
26272
26480
  if (!stat.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
26273
26481
  return resolved;
26274
26482
  }
26275
26483
  async resolveForWrite(inputPath, cwd) {
26276
26484
  const base = cwd ? await this.resolveDirectory(cwd) : this.roots[0];
26277
- const candidate = path4.isAbsolute(inputPath) ? path4.resolve(inputPath) : path4.resolve(base, inputPath);
26485
+ const candidate = path6.isAbsolute(inputPath) ? path6.resolve(inputPath) : path6.resolve(base, inputPath);
26278
26486
  this.assertNotProtected(candidate);
26279
26487
  let existing;
26280
26488
  try {
26281
- existing = await realpath3(candidate);
26489
+ existing = await realpath4(candidate);
26282
26490
  } catch (error62) {
26283
26491
  if (error62.code !== "ENOENT") throw error62;
26284
26492
  let exists = false;
26285
26493
  try {
26286
- await lstat3(candidate);
26494
+ await lstat4(candidate);
26287
26495
  exists = true;
26288
26496
  } catch (statError) {
26289
26497
  if (statError.code !== "ENOENT") throw statError;
@@ -26296,11 +26504,11 @@ var PathGuard = class _PathGuard {
26296
26504
  this.assertSafeFile(existing);
26297
26505
  return existing;
26298
26506
  }
26299
- const parent = await realpath3(path4.dirname(candidate));
26507
+ const parent = await realpath4(path6.dirname(candidate));
26300
26508
  this.assertWithinRoots(parent);
26301
26509
  this.assertNotProtected(parent);
26302
26510
  this.assertSafeFile(candidate);
26303
- const target = path4.join(parent, path4.basename(candidate));
26511
+ const target = path6.join(parent, path6.basename(candidate));
26304
26512
  this.assertSafeFile(target);
26305
26513
  return target;
26306
26514
  }
@@ -26311,12 +26519,12 @@ var PathGuard = class _PathGuard {
26311
26519
 
26312
26520
  // src/windows-file-read.ts
26313
26521
  import { execFile } from "node:child_process";
26314
- import path5 from "node:path";
26522
+ import path7 from "node:path";
26315
26523
  function readWindowsFile(bridge, file2, maxBytes, signal) {
26316
26524
  return invokeRead(bridge, file2, maxBytes, false, signal);
26317
26525
  }
26318
26526
  function invokeRead(bridge, file2, maxBytes, prefix, signal) {
26319
- if (process.platform !== "win32" || !path5.isAbsolute(bridge) || !path5.isAbsolute(file2) || !Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > (prefix ? 16777216 : 1048576)) {
26527
+ if (process.platform !== "win32" || !path7.isAbsolute(bridge) || !path7.isAbsolute(file2) || !Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > (prefix ? 16777216 : 1048576)) {
26320
26528
  return Promise.reject(new Error("FILE_READ_ARGUMENT_INVALID"));
26321
26529
  }
26322
26530
  if (signal?.aborted) return Promise.reject(new Error("CANCELLED"));
@@ -26345,9 +26553,9 @@ function invokeRead(bridge, file2, maxBytes, prefix, signal) {
26345
26553
 
26346
26554
  // agent/src/job-supervisor.ts
26347
26555
  import { spawn as spawn2 } from "node:child_process";
26348
- import path6 from "node:path";
26556
+ import path8 from "node:path";
26349
26557
  function absolute(value) {
26350
- return typeof value === "string" && value.length > 0 && value.length < 32767 && !value.includes("\0") && path6.isAbsolute(value) && path6.resolve(value) === value;
26558
+ return typeof value === "string" && value.length > 0 && value.length < 32767 && !value.includes("\0") && path8.isAbsolute(value) && path8.resolve(value) === value;
26351
26559
  }
26352
26560
  async function runSupervised(helper, request, options = {}) {
26353
26561
  if (process.platform !== "win32") throw Error("WINDOWS_SUPERVISOR_REQUIRED");
@@ -26363,7 +26571,7 @@ async function runSupervised(helper, request, options = {}) {
26363
26571
  let pid = null, outcome = null;
26364
26572
  let buffer = "", failure5 = null, closed = false, cancelSent = false;
26365
26573
  let killTimer, hardTimer;
26366
- const child = spawn2(helper, [], { cwd: path6.dirname(helper), stdio: ["pipe", "pipe", "pipe"], windowsHide: true, shell: false, env: buildChildEnvironment(process.env) });
26574
+ const child = spawn2(helper, [], { cwd: path8.dirname(helper), stdio: ["pipe", "pipe", "pipe"], windowsHide: true, shell: false, env: buildChildEnvironment(process.env) });
26367
26575
  const cancel = () => {
26368
26576
  if (closed || cancelSent) return;
26369
26577
  cancelSent = true;
@@ -26465,9 +26673,9 @@ function fail2(code) {
26465
26673
  throw new NativeToolError(code);
26466
26674
  }
26467
26675
  function hash5(value) {
26468
- return createHash8("sha256").update(value).digest("hex");
26676
+ return createHash9("sha256").update(value).digest("hex");
26469
26677
  }
26470
- function equalPath2(a, b) {
26678
+ function equalPath3(a, b) {
26471
26679
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
26472
26680
  }
26473
26681
  function result(body) {
@@ -26479,7 +26687,7 @@ function stopped(signal) {
26479
26687
  if (signal.aborted) fail2("CANCELLED");
26480
26688
  }
26481
26689
  async function readBoundedFile(file2, maxBytes, bridge, signal) {
26482
- const stat = await lstat4(file2);
26690
+ const stat = await lstat5(file2);
26483
26691
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > maxBytes) fail2("FILE_UNSUPPORTED_OR_TOO_LARGE");
26484
26692
  if (process.platform === "win32") return readWindowsFile(bridge, file2, maxBytes, signal);
26485
26693
  const handle = await open4(file2, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
@@ -26517,7 +26725,7 @@ var NativeTools = class {
26517
26725
  options;
26518
26726
  async readFile(file2, maxBytes, signal) {
26519
26727
  try {
26520
- return await readBoundedFile(file2, maxBytes, this.options.ownerBridgePath ?? path7.join(path7.dirname(this.options.helperPath), "OwnerBridge.exe"), signal);
26728
+ return await readBoundedFile(file2, maxBytes, this.options.ownerBridgePath ?? path9.join(path9.dirname(this.options.helperPath), "OwnerBridge.exe"), signal);
26521
26729
  } catch (error62) {
26522
26730
  if (error62.code === "ENOENT") throw error62;
26523
26731
  if (error62 instanceof Error && /^[A-Z_]{1,40}$/.test(error62.message)) fail2(error62.message);
@@ -26540,9 +26748,19 @@ var NativeTools = class {
26540
26748
  if (scope2 === "files:read" && !permission.readFiles || scope2 === "files:write" && !permission.writeFiles || scope2 === "commands:execute" && !permission.executeCommands || scope2 === "localhost:access" && !permission.localhost) fail2("LOCAL_PERMISSION_REQUIRED");
26541
26749
  let guard;
26542
26750
  try {
26543
- const canonical2 = await realpath4(workspace.root);
26544
- if (!equalPath2(canonical2, workspace.root) || hash5(process.platform === "win32" ? canonical2.toLowerCase() : canonical2) !== workspace.rootHash) fail2("WORKSPACE_CHANGED");
26545
- guard = await PathGuard.create([canonical2], false, { protectedPaths: [this.options.stateDirectory] });
26751
+ const canonical2 = await realpath5(workspace.root);
26752
+ if (!equalPath3(canonical2, workspace.root) || hash5(process.platform === "win32" ? canonical2.toLowerCase() : canonical2) !== workspace.rootHash) fail2("WORKSPACE_CHANGED");
26753
+ const runtimeDirectory = path9.dirname(this.options.helperPath);
26754
+ const protectedPaths = [
26755
+ this.options.stateDirectory,
26756
+ this.options.helperPath,
26757
+ ...["agent.mjs", "node.exe", "OwnerBridge.exe", "LocalCommanderAgent.exe"].map((name2) => path9.join(runtimeDirectory, name2))
26758
+ ];
26759
+ if (this.options.ownerBridgePath) protectedPaths.push(this.options.ownerBridgePath);
26760
+ if (process.platform === "win32" && process.env.LOCALAPPDATA && path9.isAbsolute(process.env.LOCALAPPDATA)) {
26761
+ protectedPaths.push(path9.join(process.env.LOCALAPPDATA, "LocalCommander"));
26762
+ }
26763
+ guard = await PathGuard.create([canonical2], false, { protectedPaths });
26546
26764
  } catch (e) {
26547
26765
  if (e instanceof NativeToolError) throw e;
26548
26766
  fail2("WORKSPACE_UNAVAILABLE");
@@ -26568,11 +26786,11 @@ var NativeTools = class {
26568
26786
  return { call, claims: readClaim(filename), run: async (signal) => {
26569
26787
  stopped(signal);
26570
26788
  const verified = await resolve(call.args.path);
26571
- if (!equalPath2(verified, filename)) fail2("FILE_CHANGED");
26789
+ if (!equalPath3(verified, filename)) fail2("FILE_CHANGED");
26572
26790
  const bytes3 = await this.readFile(filename, 1048576, signal), contents = utf8(bytes3), lines = contents.split(/\r?\n/);
26573
26791
  const selected = lines.slice(first - 1, Math.min(last, lines.length)).join("\n");
26574
26792
  if (Buffer.byteLength(selected) > 12e4) fail2("OUTPUT_LIMIT_EXCEEDED");
26575
- return result({ path: path7.relative(workspace.root, filename), sha256: hash5(bytes3), startLine: first, endLine: Math.min(last, lines.length), totalLines: lines.length, content: selected, truncated: last < lines.length });
26793
+ return result({ path: path9.relative(workspace.root, filename), sha256: hash5(bytes3), startLine: first, endLine: Math.min(last, lines.length), totalLines: lines.length, content: selected, truncated: last < lines.length });
26576
26794
  } };
26577
26795
  }
26578
26796
  if (call.name === "fs_write" || call.name === "fs_replace") {
@@ -26587,7 +26805,7 @@ var NativeTools = class {
26587
26805
  return { call, claims: writeClaim(filename), run: async (signal) => {
26588
26806
  stopped(signal);
26589
26807
  const target = await guard.resolveForWrite(args.path);
26590
- if (!equalPath2(filename, target)) fail2("FILE_CHANGED");
26808
+ if (!equalPath3(filename, target)) fail2("FILE_CHANGED");
26591
26809
  let old = null;
26592
26810
  try {
26593
26811
  old = await this.readFile(target, 1048576, signal);
@@ -26608,7 +26826,7 @@ var NativeTools = class {
26608
26826
  }
26609
26827
  const bytes3 = Buffer.from(contents, "utf8");
26610
26828
  if (bytes3.length > MAX_FILE_WRITE_BYTES) fail2("FILE_WRITE_LIMIT");
26611
- const temp = path7.join(path7.dirname(target), ".lc-write-" + randomUUID4() + ".tmp");
26829
+ const temp = path9.join(path9.dirname(target), ".lc-write-" + randomUUID4() + ".tmp");
26612
26830
  const handle = await open4(temp, constants3.O_CREAT | constants3.O_EXCL | constants3.O_WRONLY, 384);
26613
26831
  try {
26614
26832
  await handle.writeFile(bytes3);
@@ -26616,7 +26834,7 @@ var NativeTools = class {
26616
26834
  await handle.close();
26617
26835
  stopped(signal);
26618
26836
  const again = await guard.resolveForWrite(args.path);
26619
- if (!equalPath2(target, again)) fail2("FILE_CHANGED");
26837
+ if (!equalPath3(target, again)) fail2("FILE_CHANGED");
26620
26838
  if (old) {
26621
26839
  if (hash5(await this.readFile(target, 1048576, signal)) !== hash5(old)) fail2("FILE_CHANGED");
26622
26840
  await rename3(temp, target);
@@ -26625,7 +26843,7 @@ var NativeTools = class {
26625
26843
  await link(temp, target);
26626
26844
  await rm3(temp);
26627
26845
  }
26628
- return result({ path: path7.relative(workspace.root, target), bytes: bytes3.length, sha256: hash5(bytes3), ...call.name === "fs_replace" ? { replacements } : {} });
26846
+ return result({ path: path9.relative(workspace.root, target), bytes: bytes3.length, sha256: hash5(bytes3), ...call.name === "fs_replace" ? { replacements } : {} });
26629
26847
  } catch (e) {
26630
26848
  await handle.close().catch(() => {
26631
26849
  });
@@ -26646,12 +26864,12 @@ var NativeTools = class {
26646
26864
  stopped(signal);
26647
26865
  if (matches2.length >= call.args.maxResults || scanned >= 300) break;
26648
26866
  try {
26649
- const filename = await guard.resolveExisting(path7.join(base, entry.path));
26867
+ const filename = await guard.resolveExisting(path9.join(base, entry.path));
26650
26868
  guard.assertSafeFile(filename);
26651
26869
  const data = utf8(await this.readFile(filename, 256e3, signal));
26652
26870
  scanned++;
26653
26871
  const lines = data.split(/\r?\n/);
26654
- for (let i = 0; i < lines.length && matches2.length < call.args.maxResults; i++) if ((call.args.caseSensitive ? lines[i] : lines[i].toLowerCase()).includes(query)) matches2.push({ path: path7.relative(workspace.root, filename), line: i + 1, text: lines[i].slice(0, 500) });
26872
+ for (let i = 0; i < lines.length && matches2.length < call.args.maxResults; i++) if ((call.args.caseSensitive ? lines[i] : lines[i].toLowerCase()).includes(query)) matches2.push({ path: path9.relative(workspace.root, filename), line: i + 1, text: lines[i].slice(0, 500) });
26655
26873
  } catch (error62) {
26656
26874
  if (error62 instanceof NativeToolError && ["FILE_READ_FAILED", "CANCELLED"].includes(error62.code)) throw error62;
26657
26875
  }
@@ -26663,9 +26881,9 @@ var NativeTools = class {
26663
26881
  const url2 = new URL(call.args.url), port = Number(url2.port || (url2.protocol === "https:" ? 443 : 80));
26664
26882
  if (!["http:", "https:"].includes(url2.protocol) || !["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname) || url2.username || url2.password || url2.hash || !workspace.localhostPorts.includes(port) || this.options.protectedPorts?.includes(port)) fail2("LOCALHOST_NOT_ALLOWED");
26665
26883
  const headers = {};
26666
- for (const [name, value] of Object.entries(call.args.headers ?? {})) {
26667
- if (!/^[A-Za-z0-9-]+$/.test(name) || /[\r\n]/.test(value) || ["host", "connection", "content-length", "transfer-encoding", "upgrade", "proxy-authorization", "proxy-connection"].includes(name.toLowerCase()) || Object.keys(headers).some((k) => k.toLowerCase() === name.toLowerCase())) fail2("INVALID_HTTP_HEADER");
26668
- headers[name] = value;
26884
+ for (const [name2, value] of Object.entries(call.args.headers ?? {})) {
26885
+ if (!/^[A-Za-z0-9-]+$/.test(name2) || /[\r\n]/.test(value) || ["host", "connection", "content-length", "transfer-encoding", "upgrade", "proxy-authorization", "proxy-connection"].includes(name2.toLowerCase()) || Object.keys(headers).some((k) => k.toLowerCase() === name2.toLowerCase())) fail2("INVALID_HTTP_HEADER");
26886
+ headers[name2] = value;
26669
26887
  }
26670
26888
  if (Object.keys(headers).length > 32 || ["GET", "HEAD"].includes(call.args.method) && call.args.body !== void 0) fail2("INVALID_HTTP_ARGUMENTS");
26671
26889
  if (url2.hostname === "localhost") url2.hostname = "127.0.0.1";
@@ -26673,9 +26891,9 @@ var NativeTools = class {
26673
26891
  const combined = AbortSignal.any([signal, AbortSignal.timeout(1e4)]);
26674
26892
  const response = await fetch(url2, { method: call.args.method, headers, body: call.args.body, redirect: "manual", signal: combined });
26675
26893
  const selectedHeaders = {};
26676
- for (const name of ["content-type", "content-length", "location", "etag", "last-modified"]) {
26677
- const value = response.headers.get(name);
26678
- if (value) selectedHeaders[name] = value.slice(0, 2048);
26894
+ for (const name2 of ["content-type", "content-length", "location", "etag", "last-modified"]) {
26895
+ const value = response.headers.get(name2);
26896
+ if (value) selectedHeaders[name2] = value.slice(0, 2048);
26679
26897
  }
26680
26898
  const reader = response.body?.getReader(), chunks = [];
26681
26899
  let count = 0, truncated = false;
@@ -26720,9 +26938,9 @@ var NativeTools = class {
26720
26938
  else if (call.name === "git_diff") {
26721
26939
  argv = ["diff", "--no-ext-diff", "--no-textconv", ...call.args.staged ? ["--cached"] : []];
26722
26940
  if (call.args.file) {
26723
- const file2 = await resolve(path7.resolve(cwd, call.args.file));
26724
- const relative = path7.relative(cwd, file2);
26725
- if (relative.startsWith(".." + path7.sep) || relative === ".." || path7.isAbsolute(relative)) fail2("PATH_NOT_ALLOWED");
26941
+ const file2 = await resolve(path9.resolve(cwd, call.args.file));
26942
+ const relative = path9.relative(cwd, file2);
26943
+ if (relative.startsWith(".." + path9.sep) || relative === ".." || path9.isAbsolute(relative)) fail2("PATH_NOT_ALLOWED");
26726
26944
  argv.push("--", relative);
26727
26945
  } else argv.push("--stat");
26728
26946
  } else fail2("TOOL_MISSING");
@@ -26733,9 +26951,9 @@ var NativeTools = class {
26733
26951
  const listing = await this.list(guard, cwd, 1, 150, signal);
26734
26952
  let packageInfo = null;
26735
26953
  try {
26736
- const name = await guard.resolveExisting(path7.join(cwd, "package.json"));
26737
- guard.assertSafeFile(name);
26738
- const value = JSON.parse(utf8(await this.readFile(name, 64e3, signal)));
26954
+ const name2 = await guard.resolveExisting(path9.join(cwd, "package.json"));
26955
+ guard.assertSafeFile(name2);
26956
+ const value = JSON.parse(utf8(await this.readFile(name2, 64e3, signal)));
26739
26957
  packageInfo = { name: typeof value.name === "string" ? value.name : null, version: typeof value.version === "string" ? value.version : null, scripts: value.scripts && typeof value.scripts === "object" ? Object.keys(value.scripts).slice(0, 100) : [] };
26740
26958
  } catch (error62) {
26741
26959
  if (error62 instanceof NativeToolError && ["FILE_READ_FAILED", "CANCELLED"].includes(error62.code)) throw error62;
@@ -26749,14 +26967,14 @@ var NativeTools = class {
26749
26967
  return { ...output2, resultCode: r.exitCode === 0 ? "SUCCESS" : "GIT_FAILED" };
26750
26968
  } };
26751
26969
  }
26752
- resolveProgram(name, workspace) {
26753
- const entry = Object.entries(this.options.programs).find(([key, value]) => key.toLowerCase() === name.toLowerCase() || path7.isAbsolute(name) && equalPath2(value.executable, path7.resolve(name)));
26754
- if (!entry || !workspace.allowedCommands.some((v) => v.toLowerCase() === entry[0].toLowerCase()) || !path7.isAbsolute(entry[1].executable)) fail2("COMMAND_NOT_ALLOWED");
26970
+ resolveProgram(name2, workspace) {
26971
+ const entry = Object.entries(this.options.programs).find(([key, value]) => key.toLowerCase() === name2.toLowerCase() || path9.isAbsolute(name2) && equalPath3(value.executable, path9.resolve(name2)));
26972
+ if (!entry || !workspace.allowedCommands.some((v) => v.toLowerCase() === entry[0].toLowerCase()) || !path9.isAbsolute(entry[1].executable)) fail2("COMMAND_NOT_ALLOWED");
26755
26973
  return { executable: entry[1].executable, prefixArgs: [...entry[1].prefixArgs ?? []] };
26756
26974
  }
26757
26975
  gitExecution(cwd, args) {
26758
26976
  const program = this.options.programs.git;
26759
- if (!program || !path7.isAbsolute(program.executable)) fail2("GIT_NOT_INSTALLED");
26977
+ if (!program || !path9.isAbsolute(program.executable)) fail2("GIT_NOT_INSTALLED");
26760
26978
  return { executable: program.executable, args: ["--no-pager", "--no-optional-locks", "-c", "core.fsmonitor=false", "-c", "core.hooksPath=" + (process.platform === "win32" ? "NUL" : "/dev/null"), "-c", "diff.external=", ...args], cwd };
26761
26979
  }
26762
26980
  async list(guard, root, depth, maxEntries, signal) {
@@ -26774,7 +26992,7 @@ var NativeTools = class {
26774
26992
  break;
26775
26993
  }
26776
26994
  if (item.isSymbolicLink() || excluded.has(item.name.toLowerCase())) continue;
26777
- const candidate = path7.join(current2.path, item.name);
26995
+ const candidate = path9.join(current2.path, item.name);
26778
26996
  let resolved;
26779
26997
  try {
26780
26998
  resolved = await guard.resolveExisting(candidate);
@@ -26782,13 +27000,13 @@ var NativeTools = class {
26782
27000
  } catch {
26783
27001
  continue;
26784
27002
  }
26785
- if (!equalPath2(candidate, resolved)) continue;
27003
+ if (!equalPath3(candidate, resolved)) continue;
26786
27004
  if (item.isDirectory()) {
26787
- entries.push({ path: path7.relative(root, resolved), type: "directory" });
27005
+ entries.push({ path: path9.relative(root, resolved), type: "directory" });
26788
27006
  if (current2.level < depth) queue.push({ path: resolved, level: current2.level + 1 });
26789
27007
  } else if (item.isFile()) {
26790
- const stat = await lstat4(resolved);
26791
- entries.push({ path: path7.relative(root, resolved), type: "file", size: stat.size });
27008
+ const stat = await lstat5(resolved);
27009
+ entries.push({ path: path9.relative(root, resolved), type: "file", size: stat.size });
26792
27010
  }
26793
27011
  }
26794
27012
  if (truncated) break;
@@ -26798,49 +27016,49 @@ var NativeTools = class {
26798
27016
  async gitCommonDir(cwd, workspace, guard) {
26799
27017
  let directory = cwd;
26800
27018
  while (guard.isWithin(directory, workspace.root)) {
26801
- const dotGit = path7.join(directory, ".git");
27019
+ const dotGit = path9.join(directory, ".git");
26802
27020
  try {
26803
- const stat = await lstat4(dotGit);
27021
+ const stat = await lstat5(dotGit);
26804
27022
  if (stat.isSymbolicLink()) fail2("GIT_METADATA_CHANGED");
26805
27023
  let gitDir;
26806
- if (stat.isDirectory()) gitDir = await realpath4(dotGit);
27024
+ if (stat.isDirectory()) gitDir = await realpath5(dotGit);
26807
27025
  else {
26808
27026
  const content = utf8(await this.readFile(dotGit, 4096)).trim();
26809
27027
  if (!content.startsWith("gitdir: ")) fail2("GIT_METADATA_CHANGED");
26810
- gitDir = await realpath4(path7.resolve(directory, content.slice(8)));
27028
+ gitDir = await realpath5(path9.resolve(directory, content.slice(8)));
26811
27029
  }
26812
27030
  if (guard.isWithin(gitDir, this.options.stateDirectory) || !guard.isWithin(gitDir, workspace.root) && (!workspace.gitCommonDir || !guard.isWithin(gitDir, workspace.gitCommonDir))) fail2("GIT_COMMON_DIRECTORY_NOT_APPROVED");
26813
27031
  let common = gitDir;
26814
27032
  try {
26815
- const content = utf8(await this.readFile(path7.join(gitDir, "commondir"), 4096)).trim();
27033
+ const content = utf8(await this.readFile(path9.join(gitDir, "commondir"), 4096)).trim();
26816
27034
  if (!content) fail2("GIT_METADATA_CHANGED");
26817
- common = await realpath4(path7.resolve(gitDir, content));
27035
+ common = await realpath5(path9.resolve(gitDir, content));
26818
27036
  } catch (e) {
26819
27037
  if (e.code !== "ENOENT") throw e;
26820
27038
  }
26821
- if (!(await lstat4(common)).isDirectory() || guard.isWithin(common, this.options.stateDirectory)) fail2("GIT_METADATA_CHANGED");
26822
- if (!guard.isWithin(common, workspace.root) && (!workspace.gitCommonDir || !equalPath2(common, workspace.gitCommonDir))) fail2("GIT_COMMON_DIRECTORY_NOT_APPROVED");
27039
+ if (!(await lstat5(common)).isDirectory() || guard.isWithin(common, this.options.stateDirectory)) fail2("GIT_METADATA_CHANGED");
27040
+ if (!guard.isWithin(common, workspace.root) && (!workspace.gitCommonDir || !equalPath3(common, workspace.gitCommonDir))) fail2("GIT_COMMON_DIRECTORY_NOT_APPROVED");
26823
27041
  return common;
26824
27042
  } catch (e) {
26825
27043
  if (e.code !== "ENOENT") throw e;
26826
27044
  }
26827
- if (equalPath2(directory, workspace.root)) break;
26828
- directory = path7.dirname(directory);
27045
+ if (equalPath3(directory, workspace.root)) break;
27046
+ directory = path9.dirname(directory);
26829
27047
  }
26830
27048
  return null;
26831
27049
  }
26832
27050
  };
26833
27051
 
26834
27052
  // agent/src/scheduler.ts
26835
- import path8 from "node:path";
27053
+ import path10 from "node:path";
26836
27054
  import os2 from "node:os";
26837
27055
  function canonical(value) {
26838
- const p = path8.resolve(value);
27056
+ const p = path10.resolve(value);
26839
27057
  return process.platform === "win32" ? p.toLowerCase() : p;
26840
27058
  }
26841
27059
  function ancestor(a, b) {
26842
- const relative = path8.relative(a, b);
26843
- return relative === "" || !path8.isAbsolute(relative) && relative !== ".." && !relative.startsWith(".." + path8.sep);
27060
+ const relative = path10.relative(a, b);
27061
+ return relative === "" || !path10.isAbsolute(relative) && relative !== ".." && !relative.startsWith(".." + path10.sep);
26844
27062
  }
26845
27063
  function claimsConflict(a, b) {
26846
27064
  return a.some((x) => b.some((y) => (x.namespace ?? "filesystem") === (y.namespace ?? "filesystem") && (x.mode === "write" || y.mode === "write") && (ancestor(x.path, y.path) || ancestor(y.path, x.path))));
@@ -26860,7 +27078,7 @@ var NativeScheduler = class {
26860
27078
  schedule(id, claims, deadlineAt, run, signal) {
26861
27079
  if (!this.accepting) return Promise.reject(Error("AGENT_PAUSED"));
26862
27080
  if (!id || id.length > 100 || this.active.has(id) || this.waiting.some((j) => j.id === id)) return Promise.reject(Error("DUPLICATE_SCHEDULED_EXECUTION"));
26863
- if (!Array.isArray(claims) || !claims.length || claims.length > 32 || claims.some((c) => !c || !path8.isAbsolute(c.path) || !["read", "write"].includes(c.mode))) return Promise.reject(Error("INVALID_RESOURCE_CLAIM"));
27081
+ if (!Array.isArray(claims) || !claims.length || claims.length > 32 || claims.some((c) => !c || !path10.isAbsolute(c.path) || !["read", "write"].includes(c.mode))) return Promise.reject(Error("INVALID_RESOURCE_CLAIM"));
26864
27082
  const deadline = deadlineAt.getTime();
26865
27083
  if (!Number.isFinite(deadline) || deadline <= Date.now()) return Promise.reject(Error("ADMISSION_DEADLINE_EXPIRED"));
26866
27084
  if (signal?.aborted) return Promise.reject(Error("CANCELLED_BEFORE_ACCEPTANCE"));
@@ -27320,17 +27538,17 @@ function unshapedResultTelemetry(result2) {
27320
27538
 
27321
27539
  // packages/optimization/src/live-telemetry.ts
27322
27540
  var LIVE_EFFICIENCY_EVENT_VERSION = 1;
27323
- var uuid14 = external_exports.string().uuid().toLowerCase();
27541
+ var uuid15 = external_exports.string().uuid().toLowerCase();
27324
27542
  var bytes2 = external_exports.number().int().min(0).max(1e9);
27325
27543
  var utc4 = external_exports.iso.datetime({ offset: false });
27326
27544
  var LiveEfficiencyEventSchema = external_exports.object({
27327
27545
  version: external_exports.literal(LIVE_EFFICIENCY_EVENT_VERSION),
27328
27546
  occurredAt: utc4,
27329
- tenantId: uuid14,
27330
- accountId: uuid14.nullable(),
27331
- sessionId: uuid14,
27332
- operationId: uuid14,
27333
- executionId: uuid14,
27547
+ tenantId: uuid15,
27548
+ accountId: uuid15.nullable(),
27549
+ sessionId: uuid15,
27550
+ operationId: uuid15,
27551
+ executionId: uuid15,
27334
27552
  toolName: external_exports.string().trim().min(1).max(120),
27335
27553
  outcome: external_exports.enum(["success", "failed", "timed_out", "cancelled", "unknown"]),
27336
27554
  parserId: external_exports.string().trim().min(1).max(120).nullable(),
@@ -27812,10 +28030,10 @@ var NativeRelayBridge = class {
27812
28030
  const { binding: binding2, tool } = parsed2.data;
27813
28031
  this.binding(request, binding2);
27814
28032
  const prepared = await this.wait(this.execution, binding2.executionId, request, () => this.runtime.receive({ type: "prepare", version: 1, binding: binding2, tool }));
27815
- if (prepared.type === "not_accepted") return { accepted: false, provedNotStarted: prepared.provedNotStarted };
28033
+ if (prepared.type === "not_accepted") return { accepted: false, provedNotStarted: prepared.provedNotStarted, code: prepared.code };
27816
28034
  if (prepared.type !== "prepared") throw failure3("INFRA_UNAVAILABLE");
27817
28035
  const accepted = await this.wait(this.execution, binding2.executionId, request, () => this.runtime.receive({ type: "execute", version: 1, binding: binding2 }));
27818
- if (accepted.type === "not_accepted") return { accepted: false, provedNotStarted: accepted.provedNotStarted };
28036
+ if (accepted.type === "not_accepted") return { accepted: false, provedNotStarted: accepted.provedNotStarted, code: accepted.code };
27819
28037
  if (accepted.type !== "accepted") throw failure3("INFRA_UNAVAILABLE");
27820
28038
  return { accepted: true };
27821
28039
  }
@@ -27878,7 +28096,7 @@ function relayUrlFromEnrollment(enrollment) {
27878
28096
  url2.pathname = "/api/agent/relay";
27879
28097
  return url2.toString();
27880
28098
  }
27881
- function uuid15(value, code) {
28099
+ function uuid16(value, code) {
27882
28100
  if (typeof value !== "string" || !UUID2.test(value)) fail3(code);
27883
28101
  return value.toLowerCase();
27884
28102
  }
@@ -28008,37 +28226,37 @@ function sendJson(socket, value, maxBytes) {
28008
28226
  function validateChallenge(frame, expectedProtocol) {
28009
28227
  if (frame.type !== "challenge") fail3("RELAY_HANDSHAKE_INVALID");
28010
28228
  protocol(frame.protocolVersion, expectedProtocol);
28011
- uuid15(frame.gatewayId, "RELAY_HANDSHAKE_INVALID");
28229
+ uuid16(frame.gatewayId, "RELAY_HANDSHAKE_INVALID");
28012
28230
  if (typeof frame.nonce !== "string" || frame.nonce.length < 32 || frame.nonce.length > 512 || /[\u0000-\u001f\u007f]/u.test(frame.nonce)) fail3("RELAY_HANDSHAKE_INVALID");
28013
28231
  if (typeof frame.expiresAt !== "string" || !Number.isFinite(Date.parse(frame.expiresAt)) || Date.parse(frame.expiresAt) <= Date.now()) fail3("RELAY_HANDSHAKE_INVALID");
28014
28232
  return {
28015
- challengeId: uuid15(frame.challengeId, "RELAY_HANDSHAKE_INVALID"),
28233
+ challengeId: uuid16(frame.challengeId, "RELAY_HANDSHAKE_INVALID"),
28016
28234
  nonce: frame.nonce
28017
28235
  };
28018
28236
  }
28019
28237
  function validateReady(frame, enrollment, expectedProtocol) {
28020
28238
  if (frame.type !== "connected") fail3("RELAY_HANDSHAKE_INVALID");
28021
28239
  protocol(frame.protocolVersion, expectedProtocol);
28022
- if (uuid15(frame.tenantId, "RELAY_HANDSHAKE_INVALID") !== enrollment.tenantId.toLowerCase()) fail3("RELAY_ID_MISMATCH");
28023
- if (uuid15(frame.deviceId, "RELAY_HANDSHAKE_INVALID") !== enrollment.deviceId.toLowerCase()) fail3("RELAY_ID_MISMATCH");
28240
+ if (uuid16(frame.tenantId, "RELAY_HANDSHAKE_INVALID") !== enrollment.tenantId.toLowerCase()) fail3("RELAY_ID_MISMATCH");
28241
+ if (uuid16(frame.deviceId, "RELAY_HANDSHAKE_INVALID") !== enrollment.deviceId.toLowerCase()) fail3("RELAY_ID_MISMATCH");
28024
28242
  return epoch2(frame.connectionEpoch);
28025
28243
  }
28026
28244
  function validateRequest(frame, enrollment, expectedProtocol, connectionEpoch) {
28027
28245
  if (frame.type !== "relayFrame") fail3("RELAY_REQUEST_INVALID");
28028
28246
  protocol(frame.protocolVersion, expectedProtocol);
28029
- const tenantId = uuid15(frame.tenantId, "RELAY_REQUEST_INVALID");
28030
- const deviceId = uuid15(frame.deviceId, "RELAY_REQUEST_INVALID");
28247
+ const tenantId = uuid16(frame.tenantId, "RELAY_REQUEST_INVALID");
28248
+ const deviceId = uuid16(frame.deviceId, "RELAY_REQUEST_INVALID");
28031
28249
  if (tenantId !== enrollment.tenantId.toLowerCase() || deviceId !== enrollment.deviceId.toLowerCase()) fail3("RELAY_ID_MISMATCH");
28032
28250
  if (epoch2(frame.connectionEpoch) !== connectionEpoch) fail3("RELAY_EPOCH_MISMATCH");
28033
28251
  const kind = frame.kind;
28034
28252
  if (typeof kind !== "string" || !REQUEST_KINDS.has(kind)) fail3("RELAY_REQUEST_INVALID");
28035
28253
  return {
28036
- requestId: uuid15(frame.requestId, "RELAY_REQUEST_INVALID"),
28254
+ requestId: uuid16(frame.requestId, "RELAY_REQUEST_INVALID"),
28037
28255
  tenantId,
28038
28256
  deviceId,
28039
28257
  connectionEpoch,
28040
- operationId: uuid15(frame.operationId, "RELAY_REQUEST_INVALID"),
28041
- executionId: uuid15(frame.executionId, "RELAY_REQUEST_INVALID"),
28258
+ operationId: uuid16(frame.operationId, "RELAY_REQUEST_INVALID"),
28259
+ executionId: uuid16(frame.executionId, "RELAY_REQUEST_INVALID"),
28042
28260
  kind,
28043
28261
  payload: frame.payload,
28044
28262
  deadlineAt: isoDate(frame.deadlineAt)
@@ -28078,8 +28296,8 @@ async function connectOnce(options) {
28078
28296
  const identity = await options.store.load();
28079
28297
  if (!identity || !identity.enrollment) fail3("ENROLLMENT_MISSING");
28080
28298
  const enrollment = identity.enrollment;
28081
- const tenantId = uuid15(enrollment.tenantId, "ENROLLMENT_INVALID");
28082
- const deviceId = uuid15(enrollment.deviceId, "ENROLLMENT_INVALID");
28299
+ const tenantId = uuid16(enrollment.tenantId, "ENROLLMENT_INVALID");
28300
+ const deviceId = uuid16(enrollment.deviceId, "ENROLLMENT_INVALID");
28083
28301
  const url2 = relayUrlFromEnrollment(enrollment);
28084
28302
  const socket = new wrapper_default(url2, [], {
28085
28303
  followRedirects: false,
@@ -28299,10 +28517,10 @@ function canonicalOrigin2(value) {
28299
28517
  fail4("ORIGIN_INVALID");
28300
28518
  }
28301
28519
  }
28302
- function endpoint(origin, path11) {
28303
- return origin + path11;
28520
+ function endpoint(origin, path13) {
28521
+ return origin + path13;
28304
28522
  }
28305
- function uuid16(value, code) {
28523
+ function uuid17(value, code) {
28306
28524
  if (typeof value !== "string" || !UUID3.test(value)) fail4(code);
28307
28525
  return value.toLowerCase();
28308
28526
  }
@@ -28422,7 +28640,7 @@ var PairingClient = class {
28422
28640
  publicKey: identity.publicKeyPem,
28423
28641
  agentVersion: this.version
28424
28642
  }));
28425
- const pairingId = uuid16(response.pairingId, "PAIRING_RESPONSE_INVALID");
28643
+ const pairingId = uuid17(response.pairingId, "PAIRING_RESPONSE_INVALID");
28426
28644
  const expiresAt = parseDate(response.expiresAt, "PAIRING_RESPONSE_INVALID");
28427
28645
  const now = this.nowMs();
28428
28646
  if (expiresAt.getTime() <= now || expiresAt.getTime() - now > 30 * 6e4) fail4("PAIRING_RESPONSE_INVALID");
@@ -28456,9 +28674,9 @@ var PairingClient = class {
28456
28674
  }
28457
28675
  async completeOnce(beginResult, approved, displayNameValue) {
28458
28676
  const pairing = this.validateBeginResult(beginResult);
28459
- const tenantId = uuid16(approved.tenantId, "PAIRING_RESPONSE_INVALID");
28677
+ const tenantId = uuid17(approved.tenantId, "PAIRING_RESPONSE_INVALID");
28460
28678
  const email3 = accountEmail(approved.accountEmail);
28461
- const name = displayName(displayNameValue);
28679
+ const name2 = displayName(displayNameValue);
28462
28680
  const identity = await this.currentIdentity();
28463
28681
  const keyFingerprint = fingerprint2(identity.fingerprint);
28464
28682
  if (keyFingerprint !== pairing.keyFingerprint) fail4("IDENTITY_CHANGED");
@@ -28467,18 +28685,18 @@ var PairingClient = class {
28467
28685
  const response = assertJsonObject(await this.request(endpoint(this.origin, "/api/agent/pairings/" + pairing.pairingId + "/complete"), {
28468
28686
  pairingSecret: pairing.pairingSecret,
28469
28687
  signature,
28470
- displayName: name,
28688
+ displayName: name2,
28471
28689
  agentVersion: this.version
28472
28690
  }));
28473
- const completedTenant = uuid16(response.tenantId, "PAIRING_RESPONSE_INVALID");
28691
+ const completedTenant = uuid17(response.tenantId, "PAIRING_RESPONSE_INVALID");
28474
28692
  if (completedTenant !== tenantId) fail4("PAIRING_RESPONSE_INVALID");
28475
- const deviceId = uuid16(response.deviceId, "PAIRING_RESPONSE_INVALID");
28693
+ const deviceId = uuid17(response.deviceId, "PAIRING_RESPONSE_INVALID");
28476
28694
  const enrollment = {
28477
28695
  tenantId,
28478
28696
  deviceId,
28479
28697
  serverOrigin: this.origin,
28480
28698
  accountEmail: email3,
28481
- ...response.accountId !== void 0 ? { accountId: uuid16(response.accountId, "PAIRING_RESPONSE_INVALID") } : {}
28699
+ ...response.accountId !== void 0 ? { accountId: uuid17(response.accountId, "PAIRING_RESPONSE_INVALID") } : {}
28482
28700
  };
28483
28701
  await this.store.saveEnrollment(enrollment);
28484
28702
  return { ...enrollment };
@@ -28492,7 +28710,7 @@ var PairingClient = class {
28492
28710
  state: response.state,
28493
28711
  expiresAt: parseDate(response.expiresAt, "PAIRING_RESPONSE_INVALID")
28494
28712
  };
28495
- if (response.tenantId !== void 0) result2.tenantId = uuid16(response.tenantId, "PAIRING_RESPONSE_INVALID");
28713
+ if (response.tenantId !== void 0) result2.tenantId = uuid17(response.tenantId, "PAIRING_RESPONSE_INVALID");
28496
28714
  if (response.accountEmail !== void 0) result2.accountEmail = accountEmail(response.accountEmail);
28497
28715
  return result2;
28498
28716
  }
@@ -28504,10 +28722,10 @@ var PairingClient = class {
28504
28722
  }
28505
28723
  validateBeginResult(value) {
28506
28724
  return {
28507
- pairingId: uuid16(value.pairingId, "PAIRING_INVALID"),
28725
+ pairingId: uuid17(value.pairingId, "PAIRING_INVALID"),
28508
28726
  pairingSecret: pairingSecret(value.pairingSecret),
28509
28727
  confirmationCode: confirmationCode(value.confirmationCode),
28510
- approvalUrl: approvalUrl(value.approvalUrl, this.origin, uuid16(value.pairingId, "PAIRING_INVALID")),
28728
+ approvalUrl: approvalUrl(value.approvalUrl, this.origin, uuid17(value.pairingId, "PAIRING_INVALID")),
28511
28729
  expiresAt: value.expiresAt instanceof Date ? new Date(value.expiresAt.getTime()) : parseDate(value.expiresAt, "PAIRING_INVALID"),
28512
28730
  keyFingerprint: fingerprint2(value.keyFingerprint)
28513
28731
  };
@@ -28802,30 +29020,30 @@ async function deliverGuardEvidence(target, packet, options = {}) {
28802
29020
 
28803
29021
  // agent/src/optimization-ledger.ts
28804
29022
  import { constants as constants4 } from "node:fs";
28805
- import { lstat as lstat5, mkdir as mkdir4, open as open5, readFile as readFile4, realpath as realpath5 } from "node:fs/promises";
28806
- import path9 from "node:path";
29023
+ import { lstat as lstat6, mkdir as mkdir4, open as open5, readFile as readFile4, realpath as realpath6 } from "node:fs/promises";
29024
+ import path11 from "node:path";
28807
29025
  var MAX_LEDGER_BYTES = 64 * 1024 * 1024;
28808
29026
  var MAX_NOTICE_LEDGER_BYTES = 8 * 1024 * 1024;
28809
29027
  function failure4(code) {
28810
29028
  return Object.assign(new Error(code), { code });
28811
29029
  }
28812
29030
  async function safeDirectory2(directory) {
28813
- const absolute2 = path9.resolve(directory);
29031
+ const absolute2 = path11.resolve(directory);
28814
29032
  let current2 = absolute2;
28815
29033
  for (; ; ) {
28816
29034
  try {
28817
- if ((await lstat5(current2)).isSymbolicLink()) throw failure4("OPTIMIZATION_LEDGER_DIRECTORY_INVALID");
29035
+ if ((await lstat6(current2)).isSymbolicLink()) throw failure4("OPTIMIZATION_LEDGER_DIRECTORY_INVALID");
28818
29036
  } catch (error62) {
28819
29037
  if (error62.code !== "ENOENT") throw error62;
28820
29038
  }
28821
- const parent = path9.dirname(current2);
29039
+ const parent = path11.dirname(current2);
28822
29040
  if (parent === current2) break;
28823
29041
  current2 = parent;
28824
29042
  }
28825
29043
  await mkdir4(absolute2, { recursive: true, mode: 448 });
28826
- const stat = await lstat5(absolute2);
29044
+ const stat = await lstat6(absolute2);
28827
29045
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw failure4("OPTIMIZATION_LEDGER_DIRECTORY_INVALID");
28828
- return realpath5(absolute2);
29046
+ return realpath6(absolute2);
28829
29047
  }
28830
29048
  var OptimizationLedger = class _OptimizationLedger {
28831
29049
  constructor(filename, handle, noticesFilename) {
@@ -28843,11 +29061,11 @@ var OptimizationLedger = class _OptimizationLedger {
28843
29061
  closed = false;
28844
29062
  static async open(directory) {
28845
29063
  const dir = await safeDirectory2(directory);
28846
- const filename = path9.join(dir, "live-efficiency.v1.jsonl");
28847
- const noticesFilename = path9.join(dir, "live-efficiency-notices.v1.jsonl");
29064
+ const filename = path11.join(dir, "live-efficiency.v1.jsonl");
29065
+ const noticesFilename = path11.join(dir, "live-efficiency-notices.v1.jsonl");
28848
29066
  let existing = "";
28849
29067
  try {
28850
- const stat = await lstat5(filename);
29068
+ const stat = await lstat6(filename);
28851
29069
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_LEDGER_BYTES) {
28852
29070
  throw failure4("OPTIMIZATION_LEDGER_INVALID");
28853
29071
  }
@@ -28863,7 +29081,7 @@ var OptimizationLedger = class _OptimizationLedger {
28863
29081
  const ledger = new _OptimizationLedger(filename, handle, noticesFilename);
28864
29082
  try {
28865
29083
  try {
28866
- const noticeStat = await lstat5(noticesFilename);
29084
+ const noticeStat = await lstat6(noticesFilename);
28867
29085
  if (!noticeStat.isFile() || noticeStat.isSymbolicLink() || noticeStat.size > MAX_NOTICE_LEDGER_BYTES) throw failure4("OPTIMIZATION_NOTICE_LEDGER_INVALID");
28868
29086
  const notices = await readFile4(noticesFilename, "utf8");
28869
29087
  for (const line of notices.split("\n")) {
@@ -28930,7 +29148,7 @@ var OptimizationLedger = class _OptimizationLedger {
28930
29148
  const marker = JSON.stringify({ sessionId: event.sessionId, checkpointCalls, deliveredAt: (/* @__PURE__ */ new Date()).toISOString() }) + "\n";
28931
29149
  let currentBytes = 0;
28932
29150
  try {
28933
- const stat = await lstat5(this.noticesFilename);
29151
+ const stat = await lstat6(this.noticesFilename);
28934
29152
  if (!stat.isFile() || stat.isSymbolicLink()) throw failure4("OPTIMIZATION_NOTICE_LEDGER_INVALID");
28935
29153
  currentBytes = stat.size;
28936
29154
  } catch (error62) {
@@ -28977,7 +29195,7 @@ var OptimizationLedger = class _OptimizationLedger {
28977
29195
  };
28978
29196
 
28979
29197
  // agent/src/main.ts
28980
- var AGENT_VERSION2 = true ? "0.3.3" : "0.3.2";
29198
+ var AGENT_VERSION2 = true ? "0.3.4" : "0.3.2";
28981
29199
  function processAlive(pid) {
28982
29200
  if (!Number.isInteger(pid) || pid <= 0) return false;
28983
29201
  try {
@@ -28989,7 +29207,7 @@ function processAlive(pid) {
28989
29207
  }
28990
29208
  async function claimAgentProcess(stateDir, runtimeDir, version2) {
28991
29209
  await mkdir5(stateDir, { recursive: true });
28992
- const file2 = path10.join(stateDir, "agent-process.json");
29210
+ const file2 = path12.join(stateDir, "agent-process.json");
28993
29211
  for (let attempt = 0; attempt < 2; attempt += 1) {
28994
29212
  try {
28995
29213
  const handle = await open6(file2, "wx", 384);
@@ -29100,10 +29318,10 @@ async function runAgent(options) {
29100
29318
  async function main() {
29101
29319
  if (process.platform !== "win32") throw Error("WINDOWS_REQUIRED");
29102
29320
  const appData = process.env.LOCALAPPDATA;
29103
- if (!appData || !path10.isAbsolute(appData)) throw Error("APP_DATA_MISSING");
29104
- const stateDir = process.env.LC_AGENT_STATE_DIR ?? path10.join(appData, "LocalCommander", "Agent");
29105
- const binaryDir = path10.dirname(process.execPath);
29106
- const ownerBridgePath = process.env.LC_OWNER_BRIDGE ?? path10.join(binaryDir, "OwnerBridge.exe");
29321
+ if (!appData || !path12.isAbsolute(appData)) throw Error("APP_DATA_MISSING");
29322
+ const stateDir = process.env.LC_AGENT_STATE_DIR ?? path12.join(appData, "LocalCommander", "Agent");
29323
+ const binaryDir = path12.dirname(process.execPath);
29324
+ const ownerBridgePath = process.env.LC_OWNER_BRIDGE ?? path12.join(binaryDir, "OwnerBridge.exe");
29107
29325
  const store = await WindowsPrivateStore.open(stateDir, ownerBridgePath);
29108
29326
  const command = process.argv[2] ?? "run";
29109
29327
  if (command === "pair") {
@@ -29125,18 +29343,33 @@ async function main() {
29125
29343
  }) + "\n");
29126
29344
  return;
29127
29345
  }
29346
+ if (command === "configure-access") {
29347
+ if (process.argv.length !== 3) throw Error("AGENT_COMMAND_INVALID");
29348
+ process.stdout.write(await store.configureAccess() ? "ACCESS_CONFIGURED\n" : "ACCESS_CANCELLED\n");
29349
+ return;
29350
+ }
29128
29351
  if (command === "approve-workspace") {
29129
29352
  process.stdout.write(await store.approveWorkspace() ? "WORKSPACE_APPROVED\n" : "WORKSPACE_CANCELLED\n");
29130
29353
  return;
29131
29354
  }
29132
29355
  if (command === "list-workspaces") {
29133
- process.stdout.write(JSON.stringify((await store.loadWorkspaces()).map((w) => ({ workspaceGrantId: w.workspaceGrantId, displayName: w.displayName, permissions: w.permissions, allowedCommands: w.allowedCommands, localhostPorts: w.localhostPorts }))) + "\n");
29356
+ const programs = await store.loadPrograms(), history = await store.loadPermissionHistory();
29357
+ process.stdout.write(JSON.stringify((await store.loadWorkspaces()).map((w) => ({
29358
+ workspaceGrantId: w.workspaceGrantId,
29359
+ displayName: w.displayName,
29360
+ path: w.root,
29361
+ permissions: w.permissions,
29362
+ allowedCommands: w.allowedCommands,
29363
+ executables: Object.fromEntries(w.allowedCommands.map((name2) => [name2, programs[name2]?.executable ?? null])),
29364
+ localhostPorts: w.localhostPorts,
29365
+ permissionHistory: history.filter((entry) => entry.workspaceGrantId === w.workspaceGrantId)
29366
+ }))) + "\n");
29134
29367
  return;
29135
29368
  }
29136
29369
  const args = process.argv.slice(3);
29137
29370
  if (command === "efficiency-report") {
29138
29371
  if (args.length) throw Error("AGENT_COMMAND_INVALID");
29139
- const ledger = await OptimizationLedger.open(path10.join(stateDir, "optimization"));
29372
+ const ledger = await OptimizationLedger.open(path12.join(stateDir, "optimization"));
29140
29373
  try {
29141
29374
  process.stdout.write(JSON.stringify(await ledger.report(), null, 2) + "\n");
29142
29375
  } finally {
@@ -29146,7 +29379,7 @@ async function main() {
29146
29379
  }
29147
29380
  if (command === "guard-configure") {
29148
29381
  if (args.length !== 1) throw Error("AGENT_COMMAND_INVALID");
29149
- const filename = await (await import("node:fs/promises")).realpath(path10.resolve(args[0]));
29382
+ const filename = await (await import("node:fs/promises")).realpath(path12.resolve(args[0]));
29150
29383
  const input2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readWindowsFile(ownerBridgePath, filename, 2e5)));
29151
29384
  if (!input2 || typeof input2 !== "object" || Object.keys(input2).sort().join(",") !== "compact,settings") throw Error("GUARD_CONFIGURATION_INVALID");
29152
29385
  await store.configureGuard(input2.settings, input2.compact);
@@ -29165,7 +29398,7 @@ async function main() {
29165
29398
  }
29166
29399
  if (command === "guard-evidence") {
29167
29400
  if (args.length) throw Error("AGENT_COMMAND_INVALID");
29168
- const journal = await NativeJournal.open(path10.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
29401
+ const journal = await NativeJournal.open(path12.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
29169
29402
  try {
29170
29403
  process.stdout.write(JSON.stringify(await journal.guardEvidence()) + "\n");
29171
29404
  } finally {
@@ -29177,7 +29410,7 @@ async function main() {
29177
29410
  if (args.length !== (command === "guard-export" ? 0 : 1)) throw Error("AGENT_COMMAND_INVALID");
29178
29411
  const identity = await store.load();
29179
29412
  if (!identity?.enrollment) throw Error("ENROLLMENT_MISSING");
29180
- const journal = await NativeJournal.open(path10.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
29413
+ const journal = await NativeJournal.open(path12.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
29181
29414
  try {
29182
29415
  const { tenantId, deviceId } = identity.enrollment;
29183
29416
  const records = (await journal.guardEvidence()).filter((record2) => record2.tenantId === tenantId && record2.deviceId === deviceId);
@@ -29216,10 +29449,10 @@ async function main() {
29216
29449
  if (!(await store.load())?.enrollment) throw Error("ENROLLMENT_MISSING");
29217
29450
  const releaseProcess = await claimAgentProcess(stateDir, binaryDir, AGENT_VERSION2);
29218
29451
  try {
29219
- const journal = await NativeJournal.open(path10.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
29220
- const efficiencyLedger = await OptimizationLedger.open(path10.join(stateDir, "optimization"));
29452
+ const journal = await NativeJournal.open(path12.join(stateDir, "journal"), new SecretBox({ "1": await store.loadDataKey() }));
29453
+ const efficiencyLedger = await OptimizationLedger.open(path12.join(stateDir, "optimization"));
29221
29454
  try {
29222
- const tools = new NativeTools({ stateDirectory: stateDir, helperPath: process.env.LC_JOB_HOST ?? path10.join(binaryDir, "JobHost.exe"), ownerBridgePath, programs: await store.loadPrograms() });
29455
+ const tools = new NativeTools({ stateDirectory: stateDir, helperPath: process.env.LC_JOB_HOST ?? path12.join(binaryDir, "JobHost.exe"), ownerBridgePath, programs: await store.loadPrograms() });
29223
29456
  const stop = new AbortController();
29224
29457
  process.once("SIGINT", () => stop.abort());
29225
29458
  process.once("SIGTERM", () => stop.abort());
@@ -29233,7 +29466,7 @@ async function main() {
29233
29466
  await releaseProcess();
29234
29467
  }
29235
29468
  }
29236
- if (process.argv[1] && path10.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
29469
+ if (process.argv[1] && path12.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
29237
29470
  void main().catch((error62) => {
29238
29471
  const code = error62 instanceof Error && /^[A-Z0-9_]{1,64}$/.test(error62.message) ? error62.message : "AGENT_START_FAILED";
29239
29472
  process.stderr.write(code + "\n");