@standardagents/code 0.13.7 → 0.13.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -13,7 +13,8 @@ import * as transcript_delivery_star from '@standardagents/code-network/transcri
13
13
  import * as approvals_star from '@standardagents/code-network/approvals';
14
14
  import net from 'net';
15
15
  import { setImmediate } from 'timers';
16
- import fs11 from 'fs';
16
+ import fs12 from 'fs';
17
+ import { isTranscriptMetaTool } from '@standardagents/code-network/tool-activity';
17
18
  import { permissionKey, Bridge as Bridge$1 } from '@standardagents/code-network/bridge';
18
19
  import readline from 'readline';
19
20
  import * as types_star from '@standardagents/code-network/types';
@@ -103,10 +104,10 @@ var AccountUserStream = class {
103
104
  options.uniqueConnectionId
104
105
  );
105
106
  }
106
- start() {
107
+ async start() {
107
108
  if (this.started) return;
108
109
  this.started = true;
109
- this.hub.start();
110
+ await this.hub.start();
110
111
  }
111
112
  onEvent(listener) {
112
113
  this.eventListeners.add(listener);
@@ -119,7 +120,7 @@ var AccountUserStream = class {
119
120
  /** Wait for a live socket. The timeout is local and performs no network work. */
120
121
  waitUntilConnected(timeoutMs = 15e3) {
121
122
  if (this.connected) return Promise.resolve();
122
- this.start();
123
+ void this.start();
123
124
  return new Promise((resolve, reject) => {
124
125
  let timer;
125
126
  const remove = this.onConnection((state) => {
@@ -217,6 +218,7 @@ function riskCeiling(state) {
217
218
  function decide(state, tool, risk, hasPermissionRequest) {
218
219
  const effectiveRisk = typeof risk === "number" ? Math.min(5, Math.max(1, risk)) : hasPermissionRequest ? 3 : 1;
219
220
  if (state.alwaysAllow.has(tool)) return "allow";
221
+ if (hasPermissionRequest) return "ask";
220
222
  return effectiveRisk <= riskCeiling(state) ? "allow" : "ask";
221
223
  }
222
224
  var CATASTROPHIC_PATTERNS = [
@@ -420,7 +422,7 @@ var ToolLedger = class {
420
422
  if (this.loaded) return;
421
423
  this.loaded = true;
422
424
  try {
423
- const raw = fs11.readFileSync(this.file, "utf8");
425
+ const raw = fs12.readFileSync(this.file, "utf8");
424
426
  const parsed = JSON.parse(raw);
425
427
  for (const [id, entry] of Object.entries(parsed)) {
426
428
  if (entry && typeof entry.ok === "boolean") this.entries.set(id, entry);
@@ -452,13 +454,13 @@ var ToolLedger = class {
452
454
  }
453
455
  persist() {
454
456
  try {
455
- fs11.mkdirSync(ledgerDir(), { recursive: true });
457
+ fs12.mkdirSync(ledgerDir(), { recursive: true });
456
458
  const out = {};
457
459
  for (const [id, entry] of this.entries) {
458
460
  const size = entry.result ? Buffer.byteLength(entry.result) : 0;
459
461
  out[id] = size > MAX_RESULT_BYTES ? { ...entry, result: void 0 } : entry;
460
462
  }
461
- fs11.writeFileSync(this.file, JSON.stringify(out), { mode: 384 });
463
+ fs12.writeFileSync(this.file, JSON.stringify(out), { mode: 384 });
462
464
  } catch {
463
465
  }
464
466
  }
@@ -686,7 +688,10 @@ var Bridge = class extends Bridge$1 {
686
688
  isCatastrophic: (command) => isCatastrophic(command),
687
689
  decide: (permKey, risk, requestPermission) => decide(perm, permKey, risk, requestPermission),
688
690
  grantAlways: (permKey) => void perm.alwaysAllow.add(permKey),
689
- grantRisk: (risk) => void perm.allowRisk.add(risk),
691
+ grantRisk: (risk) => {
692
+ perm.level = Math.max(perm.level, risk);
693
+ perm.allowRisk.clear();
694
+ },
690
695
  persistApprovals: () => void (0, approvals_exports.saveApprovals)(api, threadId, perm)
691
696
  },
692
697
  {
@@ -797,7 +802,6 @@ function prettyAgentName(id) {
797
802
  if (!key) return "subagent";
798
803
  return AGENT_NAMES[key] || key.replace(/[_-]+/g, " ").replace(/\b\w/g, (ch) => ch.toUpperCase());
799
804
  }
800
- var META_TOOLS = /* @__PURE__ */ new Set(["set_goal", "update_goal_step", "reasoning"]);
801
805
  var APPROVAL_PENDING_MARKER = "Awaiting your approval";
802
806
  var ToolActivityFeed = class {
803
807
  calls = /* @__PURE__ */ new Map();
@@ -832,7 +836,7 @@ var ToolActivityFeed = class {
832
836
  const text = (0, session_state_exports.messageText)(message.content);
833
837
  if (text.includes(session_state_exports.WAITING_FOR_CLIENT_MARKER) || text.includes(APPROVAL_PENDING_MARKER)) return null;
834
838
  this.settled.add(callId);
835
- if (META_TOOLS.has(req.tool)) return null;
839
+ if (isTranscriptMetaTool(req.tool)) return null;
836
840
  const summary = describe(req);
837
841
  if (toolResultFailed(message, text)) {
838
842
  return { line: `\u2717 ${summary} \u2014 ${text.trim() || "failed"}` };
@@ -1546,6 +1550,46 @@ var WordMill = class {
1546
1550
  // src/types.ts
1547
1551
  var types_exports = {};
1548
1552
  __reExport(types_exports, types_star);
1553
+
1554
+ // src/subprocess-env.ts
1555
+ var INHERITED_ENV_ALLOWLIST = /* @__PURE__ */ new Set([
1556
+ "APPDATA",
1557
+ "COMSPEC",
1558
+ "HOME",
1559
+ "LANG",
1560
+ "LC_ALL",
1561
+ "LC_CTYPE",
1562
+ "LOCALAPPDATA",
1563
+ "LOGNAME",
1564
+ "PATH",
1565
+ "PATHEXT",
1566
+ "PROGRAMDATA",
1567
+ "SHELL",
1568
+ "SYSTEMROOT",
1569
+ "TEMP",
1570
+ "TERM",
1571
+ "TMP",
1572
+ "TMPDIR",
1573
+ "USER",
1574
+ "USERPROFILE",
1575
+ "WINDIR"
1576
+ ]);
1577
+ function inheritedSubprocessEnv(ambient = process.env) {
1578
+ const child = { ...ambient };
1579
+ delete child.NODE_TLS_REJECT_UNAUTHORIZED;
1580
+ return child;
1581
+ }
1582
+ function restrictedSubprocessEnv(explicit = {}, ambient = process.env) {
1583
+ const inherited = {};
1584
+ for (const [name, value] of Object.entries(ambient)) {
1585
+ if (value !== void 0 && INHERITED_ENV_ALLOWLIST.has(name.toUpperCase())) {
1586
+ inherited[name] = value;
1587
+ }
1588
+ }
1589
+ return { ...inherited, ...explicit };
1590
+ }
1591
+
1592
+ // src/clipboard.ts
1549
1593
  var FILE_MIMES = {
1550
1594
  ".png": "image/png",
1551
1595
  ".jpg": "image/jpeg",
@@ -1556,7 +1600,11 @@ var FILE_MIMES = {
1556
1600
  var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
1557
1601
  function run(cmd, args, maxBuffer = MAX_IMAGE_BYTES * 2) {
1558
1602
  return new Promise((resolve) => {
1559
- execFile(cmd, args, { encoding: "buffer", maxBuffer }, (err, stdout) => {
1603
+ execFile(cmd, args, {
1604
+ encoding: "buffer",
1605
+ maxBuffer,
1606
+ env: inheritedSubprocessEnv()
1607
+ }, (err, stdout) => {
1560
1608
  resolve({ ok: !err, stdout: stdout ?? Buffer.alloc(0) });
1561
1609
  });
1562
1610
  });
@@ -1565,9 +1613,9 @@ function fromFile(filePath) {
1565
1613
  const mime = FILE_MIMES[path9.extname(filePath).toLowerCase()];
1566
1614
  if (!mime) return null;
1567
1615
  try {
1568
- const stat = fs11.statSync(filePath);
1616
+ const stat = fs12.statSync(filePath);
1569
1617
  if (!stat.isFile() || stat.size === 0 || stat.size > MAX_IMAGE_BYTES) return null;
1570
- return { data: fs11.readFileSync(filePath).toString("base64"), mime };
1618
+ return { data: fs12.readFileSync(filePath).toString("base64"), mime };
1571
1619
  } catch {
1572
1620
  return null;
1573
1621
  }
@@ -1585,7 +1633,7 @@ async function readDarwin() {
1585
1633
  if (png.ok) {
1586
1634
  const img = fromFile(tmp);
1587
1635
  try {
1588
- fs11.unlinkSync(tmp);
1636
+ fs12.unlinkSync(tmp);
1589
1637
  } catch {
1590
1638
  }
1591
1639
  if (img) return img;
@@ -1619,7 +1667,7 @@ async function readWindows() {
1619
1667
  await run("powershell", ["-NoProfile", "-STA", "-Command", ps]);
1620
1668
  const img = fromFile(tmp);
1621
1669
  try {
1622
- fs11.unlinkSync(tmp);
1670
+ fs12.unlinkSync(tmp);
1623
1671
  } catch {
1624
1672
  }
1625
1673
  return img;
@@ -3816,6 +3864,18 @@ __reExport(shared_messaging_exports, shared_messaging_star);
3816
3864
  var history_exports = {};
3817
3865
  __reExport(history_exports, history_star);
3818
3866
  var LOG_DIR = path9.join(os6.homedir(), ".standardagents", "process-logs");
3867
+ function openPrivateProcessLog(logPath) {
3868
+ const directory = path9.dirname(logPath);
3869
+ fs12.mkdirSync(directory, { recursive: true, mode: 448 });
3870
+ fs12.chmodSync(directory, 448);
3871
+ const descriptor = fs12.openSync(
3872
+ logPath,
3873
+ fs12.constants.O_WRONLY | fs12.constants.O_CREAT | fs12.constants.O_APPEND,
3874
+ 384
3875
+ );
3876
+ fs12.fchmodSync(descriptor, 384);
3877
+ return descriptor;
3878
+ }
3819
3879
  function isAlive(pid) {
3820
3880
  try {
3821
3881
  process.kill(pid, 0);
@@ -3834,7 +3894,7 @@ function configFile() {
3834
3894
  }
3835
3895
  function loadMcpConfig() {
3836
3896
  try {
3837
- const raw = fs11.readFileSync(configFile(), "utf8");
3897
+ const raw = fs12.readFileSync(configFile(), "utf8");
3838
3898
  const parsed = JSON.parse(raw);
3839
3899
  if (!parsed.servers || typeof parsed.servers !== "object") parsed.servers = {};
3840
3900
  return parsed;
@@ -3865,8 +3925,8 @@ function setMcpServerEnabled(name, enabled) {
3865
3925
  }
3866
3926
  function write(cfg) {
3867
3927
  const file2 = configFile();
3868
- fs11.mkdirSync(path9.dirname(file2), { recursive: true });
3869
- fs11.writeFileSync(file2, JSON.stringify(cfg, null, 2), { mode: 384 });
3928
+ fs12.mkdirSync(path9.dirname(file2), { recursive: true });
3929
+ fs12.writeFileSync(file2, JSON.stringify(cfg, null, 2), { mode: 384 });
3870
3930
  }
3871
3931
  function parseServerSpec(spec) {
3872
3932
  const trimmed = spec.trim();
@@ -3937,7 +3997,11 @@ function openUrl(url) {
3937
3997
  const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
3938
3998
  const args = platform === "win32" ? ["/c", "start", "", url] : [url];
3939
3999
  try {
3940
- const child = spawn(cmd, args, { stdio: "ignore", detached: true });
4000
+ const child = spawn(cmd, args, {
4001
+ stdio: "ignore",
4002
+ detached: true,
4003
+ env: inheritedSubprocessEnv()
4004
+ });
3941
4005
  child.unref();
3942
4006
  } catch {
3943
4007
  }
@@ -3949,11 +4013,11 @@ __reExport(recorder_exports, recorder_star);
3949
4013
  __reExport(recorder_exports, recorder_node_star);
3950
4014
  function readVersion() {
3951
4015
  try {
3952
- const pkg = JSON.parse(fs11.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
4016
+ const pkg = JSON.parse(fs12.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
3953
4017
  if (typeof pkg.version === "string" && pkg.version) return pkg.version;
3954
4018
  } catch {
3955
4019
  }
3956
- return "0.13.7" ;
4020
+ return "0.13.9" ;
3957
4021
  }
3958
4022
  function isLocalHost(host) {
3959
4023
  return host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.endsWith(".localhost") || /^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host);
@@ -3979,7 +4043,7 @@ var dir = () => path9.join(os6.homedir(), ".standardagents");
3979
4043
  var file = () => path9.join(dir(), "machine.json");
3980
4044
  function loadMachineIdentity() {
3981
4045
  try {
3982
- const parsed = JSON.parse(fs11.readFileSync(file(), "utf8"));
4046
+ const parsed = JSON.parse(fs12.readFileSync(file(), "utf8"));
3983
4047
  if (typeof parsed.machine_id === "string" && parsed.machine_id.length > 0) {
3984
4048
  return { machine_id: parsed.machine_id, created_at: parsed.created_at ?? Date.now() };
3985
4049
  }
@@ -3993,8 +4057,8 @@ function loadMachineIdentity() {
3993
4057
  return identity;
3994
4058
  }
3995
4059
  function saveMachineIdentity(identity) {
3996
- fs11.mkdirSync(dir(), { recursive: true });
3997
- fs11.writeFileSync(file(), JSON.stringify(identity, null, 2), { mode: 384 });
4060
+ fs12.mkdirSync(dir(), { recursive: true });
4061
+ fs12.writeFileSync(file(), JSON.stringify(identity, null, 2), { mode: 384 });
3998
4062
  }
3999
4063
  function daemonClientId(identity) {
4000
4064
  return `daemon:${identity.machine_id}`;
@@ -4325,7 +4389,8 @@ function projectRepository(projectDir) {
4325
4389
  const url = execFileSync("git", ["-C", projectDir, "remote", "get-url", "origin"], {
4326
4390
  encoding: "utf8",
4327
4391
  timeout: 3e3,
4328
- stdio: ["ignore", "pipe", "ignore"]
4392
+ stdio: ["ignore", "pipe", "ignore"],
4393
+ env: inheritedSubprocessEnv()
4329
4394
  }).trim();
4330
4395
  return url || null;
4331
4396
  } catch {
@@ -4504,7 +4569,7 @@ function markers(dirPath) {
4504
4569
  for (const marker of PROJECT_MARKERS) {
4505
4570
  let hit = false;
4506
4571
  try {
4507
- hit = fs11.existsSync(path9.join(dirPath, marker));
4572
+ hit = fs12.existsSync(path9.join(dirPath, marker));
4508
4573
  } catch {
4509
4574
  hit = false;
4510
4575
  }
@@ -4526,11 +4591,11 @@ function browseDirectory(input3, opts = {}) {
4526
4591
  };
4527
4592
  let dirents;
4528
4593
  try {
4529
- const stat = fs11.statSync(abs);
4594
+ const stat = fs12.statSync(abs);
4530
4595
  if (!stat.isDirectory()) {
4531
4596
  return { ...base, entries: [], truncated: false, error: "Not a directory" };
4532
4597
  }
4533
- dirents = fs11.readdirSync(abs, { withFileTypes: true });
4598
+ dirents = fs12.readdirSync(abs, { withFileTypes: true });
4534
4599
  } catch (err) {
4535
4600
  const code = err?.code;
4536
4601
  const message = code === "EACCES" || code === "EPERM" ? "Permission denied" : code === "ENOENT" ? "Folder not found" : "Could not read this folder";
@@ -4545,7 +4610,7 @@ function browseDirectory(input3, opts = {}) {
4545
4610
  let isDir = d.isDirectory();
4546
4611
  if (d.isSymbolicLink()) {
4547
4612
  try {
4548
- isDir = fs11.statSync(path9.join(abs, name)).isDirectory();
4613
+ isDir = fs12.statSync(path9.join(abs, name)).isDirectory();
4549
4614
  } catch {
4550
4615
  isDir = false;
4551
4616
  }
@@ -4576,7 +4641,7 @@ function mkdirDirectory(parentInput, name, opts = {}) {
4576
4641
  return { ...listParent(), error: "Invalid folder name." };
4577
4642
  }
4578
4643
  try {
4579
- fs11.mkdirSync(target, { recursive: false });
4644
+ fs12.mkdirSync(target, { recursive: false });
4580
4645
  } catch (err) {
4581
4646
  const code = err?.code;
4582
4647
  const message = code === "EEXIST" ? "A folder with that name already exists." : code === "EACCES" || code === "EPERM" ? "Permission denied." : code === "ENOENT" ? "The parent folder no longer exists." : "Could not create the folder.";
@@ -4791,7 +4856,10 @@ function probe(cmd) {
4791
4856
  return new Promise((resolve) => {
4792
4857
  let child;
4793
4858
  try {
4794
- child = spawn(cmd, ["--version"], { stdio: "ignore" });
4859
+ child = spawn(cmd, ["--version"], {
4860
+ stdio: "ignore",
4861
+ env: inheritedSubprocessEnv()
4862
+ });
4795
4863
  } catch {
4796
4864
  resolve(false);
4797
4865
  return;
@@ -4827,7 +4895,7 @@ function download(url, dest, redirects = 5) {
4827
4895
  reject(new Error(`HTTP ${status} for ${url}`));
4828
4896
  return;
4829
4897
  }
4830
- const out = fs11.createWriteStream(dest);
4898
+ const out = fs12.createWriteStream(dest);
4831
4899
  res.pipe(out);
4832
4900
  out.on("finish", () => out.close(() => resolve()));
4833
4901
  out.on("error", reject);
@@ -4838,7 +4906,7 @@ function download(url, dest, redirects = 5) {
4838
4906
  async function sha256File(file2) {
4839
4907
  const hash = crypto.createHash("sha256");
4840
4908
  await new Promise((resolve, reject) => {
4841
- const stream = fs11.createReadStream(file2);
4909
+ const stream = fs12.createReadStream(file2);
4842
4910
  stream.on("data", (chunk) => hash.update(chunk));
4843
4911
  stream.on("end", () => resolve());
4844
4912
  stream.on("error", reject);
@@ -4849,7 +4917,11 @@ function runOk(cmd, args, cwd) {
4849
4917
  return new Promise((resolve) => {
4850
4918
  let child;
4851
4919
  try {
4852
- child = spawn(cmd, args, { cwd, stdio: "ignore" });
4920
+ child = spawn(cmd, args, {
4921
+ cwd,
4922
+ stdio: "ignore",
4923
+ env: inheritedSubprocessEnv()
4924
+ });
4853
4925
  } catch {
4854
4926
  resolve(false);
4855
4927
  return;
@@ -4876,7 +4948,7 @@ async function provision(target) {
4876
4948
  let extracted = null;
4877
4949
  for (const entry of entries) {
4878
4950
  const candidate = entry.isDirectory() ? path9.join(work, entry.name, binName) : entry.name === binName ? path9.join(work, entry.name) : null;
4879
- if (candidate && fs11.existsSync(candidate)) {
4951
+ if (candidate && fs12.existsSync(candidate)) {
4880
4952
  extracted = candidate;
4881
4953
  break;
4882
4954
  }
@@ -4901,7 +4973,7 @@ function resolveRg(log) {
4901
4973
  const override = process.env.STANDARDCODE_RG_PATH;
4902
4974
  if (override && await probe(override)) return override;
4903
4975
  if (await probe("rg")) return "rg";
4904
- if (fs11.existsSync(RG_BIN) && await probe(RG_BIN)) return RG_BIN;
4976
+ if (fs12.existsSync(RG_BIN) && await probe(RG_BIN)) return RG_BIN;
4905
4977
  const target = RG_TARGETS[`${process.platform}-${process.arch}`];
4906
4978
  if (!target) return null;
4907
4979
  try {
@@ -4960,16 +5032,36 @@ var HostTools = class {
4960
5032
  mcp;
4961
5033
  onMcpCatalogChange;
4962
5034
  api;
4963
- /** Resolve a user/model-supplied path against the project directory. */
5035
+ /** Resolve a user/model-supplied path against the project directory.
5036
+ * A leading `~` expands to the host home directory — without this, a
5037
+ * model-supplied `~/Desktop/x` silently lands at `<project>/~/Desktop/x`.
5038
+ * Outside-project containment checks run on the resolved result, so an
5039
+ * expanded home path still requires a fresh human decision. */
4964
5040
  resolve(p) {
4965
5041
  if (!p || p === ".") return this.projectDir;
5042
+ if (p === "~" || p.startsWith("~/")) {
5043
+ p = path9.join(os6.homedir(), p.slice(1));
5044
+ }
4966
5045
  return path9.resolve(this.projectDir, p);
4967
5046
  }
4968
- /** True when the resolved path escapes the project directory. */
5047
+ /** Resolve symlinks in an existing target or its nearest existing ancestor. */
5048
+ realPathForContainment(target) {
5049
+ const missing = [];
5050
+ let existing = path9.resolve(target);
5051
+ while (!fs12.existsSync(existing)) {
5052
+ const parent = path9.dirname(existing);
5053
+ if (parent === existing) return path9.resolve(target);
5054
+ missing.unshift(path9.basename(existing));
5055
+ existing = parent;
5056
+ }
5057
+ return path9.join(fs12.realpathSync.native(existing), ...missing);
5058
+ }
5059
+ /** True when the resolved path escapes the real project directory. */
4969
5060
  isOutsideProject(p) {
4970
- const abs = this.resolve(p);
4971
- const rel = path9.relative(this.projectDir, abs);
4972
- return rel.startsWith("..") || path9.isAbsolute(rel);
5061
+ const project = this.realPathForContainment(this.projectDir);
5062
+ const candidate = this.realPathForContainment(this.resolve(p));
5063
+ const rel = path9.relative(project, candidate);
5064
+ return rel === ".." || rel.startsWith(`..${path9.sep}`) || path9.isAbsolute(rel);
4973
5065
  }
4974
5066
  async execute(tool, args) {
4975
5067
  try {
@@ -5065,7 +5157,7 @@ var HostTools = class {
5065
5157
  const file2 = this.resolve(String(args.path || ""));
5066
5158
  const content = String(args.content ?? "");
5067
5159
  await fsp2.mkdir(path9.dirname(file2), { recursive: true });
5068
- const existed = fs11.existsSync(file2);
5160
+ const existed = fs12.existsSync(file2);
5069
5161
  await fsp2.writeFile(file2, content, "utf8");
5070
5162
  return {
5071
5163
  ok: true,
@@ -5107,7 +5199,7 @@ var HostTools = class {
5107
5199
  if (!bytes) return { ok: false, error: `Could not download ${source} from the thread filesystem.` };
5108
5200
  const dest = this.resolve(destArg);
5109
5201
  await fsp2.mkdir(path9.dirname(dest), { recursive: true });
5110
- const existed = fs11.existsSync(dest);
5202
+ const existed = fs12.existsSync(dest);
5111
5203
  await fsp2.writeFile(dest, bytes);
5112
5204
  return {
5113
5205
  ok: true,
@@ -5184,7 +5276,7 @@ var HostTools = class {
5184
5276
  return await new Promise((resolvePromise) => {
5185
5277
  const child = spawn(cmd, argv, {
5186
5278
  cwd: this.projectDir,
5187
- env: { ...process.env, SKILL_DIR: skillDir },
5279
+ env: restrictedSubprocessEnv({ SKILL_DIR: skillDir }),
5188
5280
  stdio: ["ignore", "pipe", "pipe"]
5189
5281
  });
5190
5282
  let out = "";
@@ -5237,30 +5329,34 @@ ${truncated}`
5237
5329
  const command = String(args.command || "");
5238
5330
  if (!command.trim()) return { ok: false, error: "command is required" };
5239
5331
  const cwd = args.cwd ? this.resolve(String(args.cwd)) : this.projectDir;
5240
- if (!fs11.existsSync(cwd)) {
5332
+ if (!fs12.existsSync(cwd)) {
5241
5333
  return { ok: false, error: `cwd does not exist: ${cwd}` };
5242
5334
  }
5243
5335
  const id = crypto.randomUUID().slice(0, 8);
5244
5336
  const logPath = path9.join(LOG_DIR, `${id}.log`);
5245
5337
  let out;
5246
5338
  try {
5247
- await fsp2.mkdir(LOG_DIR, { recursive: true });
5248
- out = fs11.openSync(logPath, "a");
5339
+ out = openPrivateProcessLog(logPath);
5249
5340
  } catch (err) {
5250
5341
  return { ok: false, error: `Could not open log file: ${err instanceof Error ? err.message : String(err)}` };
5251
5342
  }
5252
5343
  let child;
5253
5344
  try {
5254
- child = spawn("bash", ["-lc", command], { cwd, detached: true, stdio: ["ignore", out, out] });
5345
+ child = spawn("bash", ["-lc", command], {
5346
+ cwd,
5347
+ detached: true,
5348
+ stdio: ["ignore", out, out],
5349
+ env: inheritedSubprocessEnv()
5350
+ });
5255
5351
  } catch (err) {
5256
- fs11.closeSync(out);
5352
+ fs12.closeSync(out);
5257
5353
  return { ok: false, error: `Failed to start: ${err instanceof Error ? err.message : String(err)}` };
5258
5354
  }
5259
5355
  let spawnError = null;
5260
5356
  child.on("error", (err) => {
5261
5357
  spawnError = err;
5262
5358
  });
5263
- fs11.closeSync(out);
5359
+ fs12.closeSync(out);
5264
5360
  const pid = child.pid;
5265
5361
  let earlyExit;
5266
5362
  const onEarlyExit = (code) => {
@@ -5466,7 +5562,11 @@ ${tail2}` : " No output was captured.")
5466
5562
  const MAX_OUTPUT = 256 * 1024;
5467
5563
  let child;
5468
5564
  try {
5469
- child = spawn(cmd, args, { cwd, detached: true });
5565
+ child = spawn(cmd, args, {
5566
+ cwd,
5567
+ detached: true,
5568
+ env: inheritedSubprocessEnv()
5569
+ });
5470
5570
  } catch {
5471
5571
  resolve({ stdout: "", stderr: "", code: 127, timedOut: false });
5472
5572
  return;
@@ -5566,7 +5666,7 @@ var McpClient = class {
5566
5666
  async connect(defaultCwd) {
5567
5667
  const child = spawn(this.config.command, this.config.args, {
5568
5668
  cwd: this.config.cwd || defaultCwd,
5569
- env: { ...process.env, ...this.config.env || {} },
5669
+ env: restrictedSubprocessEnv(this.config.env),
5570
5670
  stdio: ["pipe", "pipe", "pipe"]
5571
5671
  });
5572
5672
  this.child = child;
@@ -6030,7 +6130,7 @@ var DaemonUserStream = class {
6030
6130
  if (hooks.onConnection) this.stream.onConnection(hooks.onConnection);
6031
6131
  }
6032
6132
  start() {
6033
- this.stream.start();
6133
+ return this.stream.start();
6034
6134
  }
6035
6135
  close() {
6036
6136
  this.stream.close();
@@ -6073,7 +6173,7 @@ function cachePath() {
6073
6173
  }
6074
6174
  function readCache() {
6075
6175
  try {
6076
- const raw = fs11.readFileSync(cachePath(), "utf-8");
6176
+ const raw = fs12.readFileSync(cachePath(), "utf-8");
6077
6177
  return JSON.parse(raw);
6078
6178
  } catch {
6079
6179
  return null;
@@ -6082,14 +6182,14 @@ function readCache() {
6082
6182
  function writeCache(latest) {
6083
6183
  try {
6084
6184
  const dir2 = cacheDir();
6085
- if (!fs11.existsSync(dir2)) fs11.mkdirSync(dir2, { recursive: true });
6086
- fs11.writeFileSync(cachePath(), JSON.stringify({ latest, timestamp: Date.now() }));
6185
+ if (!fs12.existsSync(dir2)) fs12.mkdirSync(dir2, { recursive: true });
6186
+ fs12.writeFileSync(cachePath(), JSON.stringify({ latest, timestamp: Date.now() }));
6087
6187
  } catch {
6088
6188
  }
6089
6189
  }
6090
6190
  function readAutoUpdateState(dir2 = cacheDir()) {
6091
6191
  try {
6092
- const raw = fs11.readFileSync(path9.join(dir2, STATE_FILE), "utf-8");
6192
+ const raw = fs12.readFileSync(path9.join(dir2, STATE_FILE), "utf-8");
6093
6193
  const state = JSON.parse(raw);
6094
6194
  return typeof state?.version === "string" ? state : null;
6095
6195
  } catch {
@@ -6098,14 +6198,14 @@ function readAutoUpdateState(dir2 = cacheDir()) {
6098
6198
  }
6099
6199
  function writeAutoUpdateState(state, dir2 = cacheDir()) {
6100
6200
  try {
6101
- if (!fs11.existsSync(dir2)) fs11.mkdirSync(dir2, { recursive: true });
6102
- fs11.writeFileSync(path9.join(dir2, STATE_FILE), JSON.stringify(state));
6201
+ if (!fs12.existsSync(dir2)) fs12.mkdirSync(dir2, { recursive: true });
6202
+ fs12.writeFileSync(path9.join(dir2, STATE_FILE), JSON.stringify(state));
6103
6203
  } catch {
6104
6204
  }
6105
6205
  }
6106
6206
  function clearAutoUpdateState(dir2 = cacheDir()) {
6107
6207
  try {
6108
- fs11.unlinkSync(path9.join(dir2, STATE_FILE));
6208
+ fs12.unlinkSync(path9.join(dir2, STATE_FILE));
6109
6209
  } catch {
6110
6210
  }
6111
6211
  }
@@ -6152,7 +6252,11 @@ function startBackgroundUpdate(latest, pm, dir2 = cacheDir()) {
6152
6252
  const { cmd, args } = updateCommand(pm);
6153
6253
  const script = `const cp=require('child_process');const fs=require('fs');const r=cp.spawnSync(${JSON.stringify(cmd)},${JSON.stringify(args)},{shell:process.platform==='win32',encoding:'utf8'});const out=((r.stdout||'')+(r.stderr||'')).slice(-2000);fs.writeFileSync(${JSON.stringify(stateFile)},JSON.stringify({version:${JSON.stringify(latest)},startedAt:${startedAt},exitCode:r.status==null?-1:r.status,finishedAt:Date.now(),output:out}));`;
6154
6254
  try {
6155
- const child = spawn(process.execPath, ["-e", script], { detached: true, stdio: "ignore" });
6255
+ const child = spawn(process.execPath, ["-e", script], {
6256
+ detached: true,
6257
+ stdio: "ignore",
6258
+ env: inheritedSubprocessEnv()
6259
+ });
6156
6260
  child.unref();
6157
6261
  return true;
6158
6262
  } catch {
@@ -6224,7 +6328,8 @@ function runUpdate(pm) {
6224
6328
  const { cmd, args } = updateCommand(pm);
6225
6329
  const child = spawn(cmd, args, {
6226
6330
  shell: process.platform === "win32",
6227
- stdio: ["ignore", "pipe", "pipe"]
6331
+ stdio: ["ignore", "pipe", "pipe"],
6332
+ env: inheritedSubprocessEnv()
6228
6333
  });
6229
6334
  let out = "";
6230
6335
  child.stdout?.on("data", (d) => out += d);
@@ -6240,22 +6345,30 @@ var UPDATE_CHECK_MS = 6 * 60 * 6e4;
6240
6345
  var MAX_WORKERS = 30;
6241
6346
  var BRIDGE_ATTACH_GRACE_MS = 15e3;
6242
6347
  var BRIDGE_IDLE_MS = 2e3;
6348
+ var LIVE_FRAME_APPROVAL_TIMEOUT_MS = 45e3;
6243
6349
  var LOG_MAX_BYTES = 1e6;
6244
6350
  var LOG_FILE = path9.join(os6.homedir(), ".standardagents", "daemon.log");
6245
- function daemonLog(line) {
6351
+ function appendPrivateLog(file2, line, maxBytes) {
6246
6352
  try {
6247
- fs11.mkdirSync(path9.dirname(LOG_FILE), { recursive: true });
6353
+ const directory = path9.dirname(file2);
6354
+ fs12.mkdirSync(directory, { recursive: true, mode: 448 });
6355
+ fs12.chmodSync(directory, 448);
6248
6356
  try {
6249
- if (fs11.statSync(LOG_FILE).size > LOG_MAX_BYTES) {
6250
- fs11.renameSync(LOG_FILE, `${LOG_FILE}.old`);
6357
+ fs12.chmodSync(file2, 384);
6358
+ if (fs12.statSync(file2).size > maxBytes) {
6359
+ fs12.renameSync(file2, `${file2}.old`);
6251
6360
  }
6252
6361
  } catch {
6253
6362
  }
6254
- fs11.appendFileSync(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
6255
- `);
6363
+ fs12.appendFileSync(file2, line, { mode: 384 });
6364
+ fs12.chmodSync(file2, 384);
6256
6365
  } catch {
6257
6366
  }
6258
6367
  }
6368
+ function daemonLog(line) {
6369
+ appendPrivateLog(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
6370
+ `, LOG_MAX_BYTES);
6371
+ }
6259
6372
  function pathFromTags(tags) {
6260
6373
  const tag = tags.find((t) => t.startsWith("path:"));
6261
6374
  if (!tag) return null;
@@ -6320,7 +6433,8 @@ var ThreadWorker = class {
6320
6433
  const permKey = permissionKey(req);
6321
6434
  const fresh = decide(this.perm, permKey, effectiveRisk, !!req.requestPermission);
6322
6435
  if (fresh === "allow") return { choice: "allow" };
6323
- if (!req.toolCallId) {
6436
+ const relayId = req.toolCallId ?? req.id;
6437
+ if (!relayId) {
6324
6438
  return { choice: "deny", reason: "No one is available to approve this right now." };
6325
6439
  }
6326
6440
  daemonLog(`[${threadId.slice(0, 8)}] relaying approval: ${summary} (risk ${effectiveRisk})`);
@@ -6331,7 +6445,7 @@ var ThreadWorker = class {
6331
6445
  api,
6332
6446
  threadId,
6333
6447
  {
6334
- tool_call_id: req.toolCallId,
6448
+ tool_call_id: relayId,
6335
6449
  tool: req.tool,
6336
6450
  summary,
6337
6451
  permission: req.requestPermission,
@@ -6339,13 +6453,24 @@ var ThreadWorker = class {
6339
6453
  machine: machineName,
6340
6454
  requested_at: Date.now()
6341
6455
  },
6342
- { subscribe: (wake) => approvalWakes.subscribe(threadId, wake) }
6456
+ {
6457
+ subscribe: (wake) => approvalWakes.subscribe(threadId, wake),
6458
+ // A live frame is answered over the socket and the server fails
6459
+ // the call on its own frame timeout — bound the human wait so a
6460
+ // late "allow" can never execute a side effect into a frame the
6461
+ // server already gave up on. Durable calls park server-side and
6462
+ // keep the long default.
6463
+ timeoutMs: req.toolCallId ? void 0 : LIVE_FRAME_APPROVAL_TIMEOUT_MS
6464
+ }
6343
6465
  );
6344
6466
  if (!response) {
6345
6467
  return { choice: "deny", reason: "The approval request timed out with no one to approve it." };
6346
6468
  }
6347
6469
  if (response.choice === "always") this.perm.alwaysAllow.add(permKey);
6348
- if (response.choice === "always_risk") this.perm.allowRisk.add(effectiveRisk);
6470
+ if (response.choice === "always_risk") {
6471
+ this.perm.level = Math.max(this.perm.level, effectiveRisk);
6472
+ this.perm.allowRisk.clear();
6473
+ }
6349
6474
  if (response.choice === "always" || response.choice === "always_risk") {
6350
6475
  (0, approvals_exports.saveApprovals)(api, threadId, this.perm);
6351
6476
  }
@@ -6514,7 +6639,7 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
6514
6639
  daemonLog(`detached ${oldest.threadId.slice(0, 8)} (worker cap)`);
6515
6640
  }
6516
6641
  try {
6517
- fs11.mkdirSync(projectDir, { recursive: true });
6642
+ fs12.mkdirSync(projectDir, { recursive: true });
6518
6643
  } catch (e) {
6519
6644
  daemonLog(`cannot prepare project dir ${projectDir}: ${e instanceof Error ? e.message : String(e)}`);
6520
6645
  return;
@@ -6817,7 +6942,7 @@ function resolveDaemonCommand(extraArgs = []) {
6817
6942
  let tsx = null;
6818
6943
  for (let i = 0; i < 6 && dir2 !== path9.dirname(dir2); i++) {
6819
6944
  const candidate = path9.join(dir2, "node_modules", "tsx", "dist", "cli.mjs");
6820
- if (fs11.existsSync(candidate)) {
6945
+ if (fs12.existsSync(candidate)) {
6821
6946
  tsx = candidate;
6822
6947
  break;
6823
6948
  }
@@ -6847,13 +6972,25 @@ function servicePath() {
6847
6972
  return [.../* @__PURE__ */ new Set([...current, ...parts])].join(":");
6848
6973
  }
6849
6974
  function run2(cmd, args) {
6850
- const res = spawnSync(cmd, args, { encoding: "utf8" });
6975
+ const res = spawnSync(cmd, args, {
6976
+ encoding: "utf8",
6977
+ env: inheritedSubprocessEnv()
6978
+ });
6851
6979
  const output4 = `${res.stdout ?? ""}${res.stderr ?? ""}`.trim();
6852
6980
  return { ok: res.status === 0, output: output4 };
6853
6981
  }
6854
6982
  var plistPath = () => path9.join(os6.homedir(), "Library", "LaunchAgents", `${SERVICE_LABEL}.plist`);
6855
6983
  var unitPath = () => path9.join(os6.homedir(), ".config", "systemd", "user", SYSTEMD_UNIT);
6856
6984
  var xmlEscape = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
6985
+ function restartServiceInvocation(platform, uid = 501) {
6986
+ if (platform === "darwin") {
6987
+ return { cmd: "launchctl", args: ["kickstart", "-k", `gui/${uid}/${SERVICE_LABEL}`] };
6988
+ }
6989
+ if (platform === "linux") {
6990
+ return { cmd: "systemctl", args: ["--user", "restart", SYSTEMD_UNIT] };
6991
+ }
6992
+ return null;
6993
+ }
6857
6994
  function installService(command, endpoint) {
6858
6995
  if (process.platform === "darwin") return installLaunchd(command, endpoint);
6859
6996
  if (process.platform === "linux") return installSystemd(command, endpoint);
@@ -6865,8 +7002,8 @@ function installService(command, endpoint) {
6865
7002
  }
6866
7003
  function installLaunchd(command, endpoint) {
6867
7004
  const logDir = path9.join(os6.homedir(), ".standardagents");
6868
- fs11.mkdirSync(logDir, { recursive: true });
6869
- fs11.mkdirSync(path9.dirname(plistPath()), { recursive: true });
7005
+ fs12.mkdirSync(logDir, { recursive: true });
7006
+ fs12.mkdirSync(path9.dirname(plistPath()), { recursive: true });
6870
7007
  const envEntries = [
6871
7008
  ` <key>PATH</key><string>${xmlEscape(servicePath())}</string>`,
6872
7009
  ...endpoint ? [` <key>STANDARD_CODE_DAEMON_ENDPOINT</key><string>${xmlEscape(endpoint)}</string>`] : []
@@ -6892,7 +7029,7 @@ ${envEntries}
6892
7029
  </dict>
6893
7030
  </plist>
6894
7031
  `;
6895
- fs11.writeFileSync(plistPath(), plist);
7032
+ fs12.writeFileSync(plistPath(), plist);
6896
7033
  const uid = typeof process.getuid === "function" ? process.getuid() : 501;
6897
7034
  run2("launchctl", ["bootout", `gui/${uid}`, plistPath()]);
6898
7035
  const boot = run2("launchctl", ["bootstrap", `gui/${uid}`, plistPath()]);
@@ -6909,7 +7046,7 @@ ${envEntries}
6909
7046
  return { ok: true, detail: `LaunchAgent installed (${plistPath()})` };
6910
7047
  }
6911
7048
  function installSystemd(command, endpoint) {
6912
- fs11.mkdirSync(path9.dirname(unitPath()), { recursive: true });
7049
+ fs12.mkdirSync(path9.dirname(unitPath()), { recursive: true });
6913
7050
  const unit = `[Unit]
6914
7051
  Description=Standard Code daemon (headless coding-agent execution client)
6915
7052
  After=network-online.target
@@ -6924,7 +7061,7 @@ ${endpoint ? `Environment=STANDARD_CODE_DAEMON_ENDPOINT=${endpoint}
6924
7061
  [Install]
6925
7062
  WantedBy=default.target
6926
7063
  `;
6927
- fs11.writeFileSync(unitPath(), unit);
7064
+ fs12.writeFileSync(unitPath(), unit);
6928
7065
  const reload = run2("systemctl", ["--user", "daemon-reload"]);
6929
7066
  if (!reload.ok) {
6930
7067
  return {
@@ -6948,7 +7085,7 @@ function uninstallService() {
6948
7085
  const uid = typeof process.getuid === "function" ? process.getuid() : 501;
6949
7086
  run2("launchctl", ["bootout", `gui/${uid}`, plistPath()]);
6950
7087
  try {
6951
- fs11.unlinkSync(plistPath());
7088
+ fs12.unlinkSync(plistPath());
6952
7089
  } catch {
6953
7090
  }
6954
7091
  return { ok: true, detail: "LaunchAgent removed" };
@@ -6956,7 +7093,7 @@ function uninstallService() {
6956
7093
  if (process.platform === "linux") {
6957
7094
  run2("systemctl", ["--user", "disable", "--now", SYSTEMD_UNIT]);
6958
7095
  try {
6959
- fs11.unlinkSync(unitPath());
7096
+ fs12.unlinkSync(unitPath());
6960
7097
  } catch {
6961
7098
  }
6962
7099
  run2("systemctl", ["--user", "daemon-reload"]);
@@ -6964,9 +7101,42 @@ function uninstallService() {
6964
7101
  }
6965
7102
  return { ok: false, detail: `Unsupported platform: ${process.platform}` };
6966
7103
  }
7104
+ function restartService() {
7105
+ if (process.platform === "darwin") {
7106
+ if (!fs12.existsSync(plistPath())) {
7107
+ return {
7108
+ ok: false,
7109
+ detail: "The daemon service is not installed. Run `standardcode daemon install` first."
7110
+ };
7111
+ }
7112
+ const uid = typeof process.getuid === "function" ? process.getuid() : 501;
7113
+ const invocation = restartServiceInvocation(process.platform, uid);
7114
+ const restart = run2(invocation.cmd, invocation.args);
7115
+ if (restart.ok) return { ok: true, detail: "LaunchAgent restarted" };
7116
+ const boot = run2("launchctl", ["bootstrap", `gui/${uid}`, plistPath()]);
7117
+ if (boot.ok) return { ok: true, detail: "LaunchAgent loaded and started" };
7118
+ return {
7119
+ ok: false,
7120
+ detail: `launchctl failed: ${restart.output || boot.output}`,
7121
+ manualHint: `Load it manually: launchctl bootstrap gui/${uid} ${plistPath()}`
7122
+ };
7123
+ }
7124
+ if (process.platform === "linux") {
7125
+ if (!fs12.existsSync(unitPath())) {
7126
+ return {
7127
+ ok: false,
7128
+ detail: "The daemon service is not installed. Run `standardcode daemon install` first."
7129
+ };
7130
+ }
7131
+ const invocation = restartServiceInvocation(process.platform);
7132
+ const restart = run2(invocation.cmd, invocation.args);
7133
+ return restart.ok ? { ok: true, detail: "systemd user service restarted" } : { ok: false, detail: `systemctl restart failed: ${restart.output}` };
7134
+ }
7135
+ return { ok: false, detail: `Unsupported platform: ${process.platform}` };
7136
+ }
6967
7137
  function serviceStatus() {
6968
7138
  if (process.platform === "darwin") {
6969
- const installed = fs11.existsSync(plistPath());
7139
+ const installed = fs12.existsSync(plistPath());
6970
7140
  const list = run2("launchctl", ["list", SERVICE_LABEL]);
6971
7141
  const pidMatch = list.output.match(/"PID"\s*=\s*(\d+)/);
6972
7142
  return {
@@ -6976,7 +7146,7 @@ function serviceStatus() {
6976
7146
  };
6977
7147
  }
6978
7148
  if (process.platform === "linux") {
6979
- const installed = fs11.existsSync(unitPath());
7149
+ const installed = fs12.existsSync(unitPath());
6980
7150
  const active = run2("systemctl", ["--user", "is-active", SYSTEMD_UNIT]);
6981
7151
  return {
6982
7152
  installed,
@@ -7011,6 +7181,8 @@ function usage() {
7011
7181
  " e.g. when another app drives the install).",
7012
7182
  " uninstall Stop and remove the service.",
7013
7183
  " status Service + registry status for this machine.",
7184
+ " restart Restart the installed service immediately.",
7185
+ " update [--endpoint url] Install the latest CLI, then restart the service.",
7014
7186
  " run [--endpoint url] Run the daemon in the foreground (what the service runs).",
7015
7187
  " add-project <path> Register a project directory for remote sessions.",
7016
7188
  " remove-project <path> Remove a registered project directory.",
@@ -7140,9 +7312,10 @@ async function installCommand(endpointFlag, opts = {}) {
7140
7312
  stdout.write(
7141
7313
  `${c3.green}\u2713${c3.reset} ${c3.bold}${displayName}${c3.reset} is online.
7142
7314
 
7143
- Sessions started elsewhere can now run on this machine.
7144
- ${c3.dim}Projects register automatically when you run standardcode in a directory here,
7145
- or add one now: standardcode daemon add-project <path>${c3.reset}
7315
+ Standard Code sessions can now edit files and run tools on this machine while you drive them from any device signed in to your account.
7316
+ ${c3.dim}Projects are added automatically when you start Standard Code from their directory.
7317
+ To add a project manually, run:${c3.reset}
7318
+ ${c3.bold}standardcode daemon add-project /absolute/path/to/project${c3.reset}
7146
7319
  `
7147
7320
  );
7148
7321
  } else {
@@ -7192,6 +7365,55 @@ async function statusCommand() {
7192
7365
  for (const p of projects.sort()) stdout.write(` ${c3.dim}${p}${c3.reset}
7193
7366
  `);
7194
7367
  }
7368
+ async function updateDaemonCommand(endpointFlag) {
7369
+ const status = serviceStatus();
7370
+ if (!status.installed) {
7371
+ stdout.write(
7372
+ `${c3.red}\u2717${c3.reset} The daemon service is not installed. Run \`standardcode daemon install\` first.
7373
+ `
7374
+ );
7375
+ process.exit(1);
7376
+ }
7377
+ const endpoint = resolveEndpoint(endpointFlag);
7378
+ const api = await ensureSignedIn(endpoint);
7379
+ const identity = loadMachineIdentity();
7380
+ const record = await loadMachine(api, identity.machine_id).catch(() => null);
7381
+ if (status.running && record && daemonOnline(record)) {
7382
+ await enqueueMachineCommand(api, identity.machine_id, "update");
7383
+ stdout.write(
7384
+ `${c3.green}\u2713${c3.reset} Update requested. The daemon will finish active tool calls, install the latest Standard Code, and restart.
7385
+ `
7386
+ );
7387
+ return;
7388
+ }
7389
+ const pm = detectPackageManager();
7390
+ if (!pm) {
7391
+ stdout.write(
7392
+ `${c3.red}\u2717${c3.reset} This source checkout cannot update the installed CLI. Run \`standardcode daemon update\` through your global Standard Code command.
7393
+ `
7394
+ );
7395
+ process.exit(1);
7396
+ }
7397
+ const { display } = updateCommand(pm);
7398
+ stdout.write(`${c3.dim}Running ${display}\u2026${c3.reset}
7399
+ `);
7400
+ const result = await runUpdate(pm);
7401
+ if (!result.ok) {
7402
+ stdout.write(`${c3.red}\u2717${c3.reset} Update failed.
7403
+ `);
7404
+ for (const line of result.output.trim().split("\n").slice(-6)) {
7405
+ if (line) stdout.write(`${c3.dim}${line}${c3.reset}
7406
+ `);
7407
+ }
7408
+ process.exit(1);
7409
+ }
7410
+ const restarted = restartService();
7411
+ stdout.write(
7412
+ `${restarted.ok ? c3.green + "\u2713" : c3.red + "\u2717"}${c3.reset} ${restarted.ok ? "Standard Code updated. " : "Standard Code updated, but the daemon restart failed. "}${restarted.detail}
7413
+ `
7414
+ );
7415
+ if (!restarted.ok) process.exit(1);
7416
+ }
7195
7417
  async function projectCommand(action, target) {
7196
7418
  if (!target) {
7197
7419
  stdout.write(`${c3.red}\u2717${c3.reset} Expected a project path.
@@ -7199,7 +7421,7 @@ async function projectCommand(action, target) {
7199
7421
  process.exit(1);
7200
7422
  }
7201
7423
  const dir2 = path9.resolve(target);
7202
- if (action === "add" && !fs11.existsSync(dir2)) {
7424
+ if (action === "add" && !fs12.existsSync(dir2)) {
7203
7425
  stdout.write(`${c3.red}\u2717${c3.reset} ${dir2} does not exist on this machine.
7204
7426
  `);
7205
7427
  process.exit(1);
@@ -7247,6 +7469,18 @@ async function runDaemonCommand(argv) {
7247
7469
  case "status":
7248
7470
  await statusCommand();
7249
7471
  return;
7472
+ case "restart": {
7473
+ const result = restartService();
7474
+ stdout.write(`${result.ok ? c3.green + "\u2713" : c3.red + "\u2717"}${c3.reset} ${result.detail}
7475
+ `);
7476
+ if (result.manualHint) stdout.write(`${c3.dim}${result.manualHint}${c3.reset}
7477
+ `);
7478
+ if (!result.ok) process.exit(1);
7479
+ return;
7480
+ }
7481
+ case "update":
7482
+ await updateDaemonCommand(endpoint);
7483
+ return;
7250
7484
  case "add-project":
7251
7485
  await projectCommand("add", rest[0]);
7252
7486
  return;
@@ -7268,15 +7502,15 @@ var DIR = path9.join(os6.homedir(), ".standardagents");
7268
7502
  var FILE = path9.join(DIR, "prefs.json");
7269
7503
  function loadPrefs(file2 = FILE) {
7270
7504
  try {
7271
- return JSON.parse(fs11.readFileSync(file2, "utf8"));
7505
+ return JSON.parse(fs12.readFileSync(file2, "utf8"));
7272
7506
  } catch {
7273
7507
  return {};
7274
7508
  }
7275
7509
  }
7276
7510
  function savePrefs(update, file2 = FILE) {
7277
7511
  const merged = { ...loadPrefs(file2), ...update };
7278
- fs11.mkdirSync(path9.dirname(file2), { recursive: true });
7279
- fs11.writeFileSync(file2, JSON.stringify(merged, null, 2));
7512
+ fs12.mkdirSync(path9.dirname(file2), { recursive: true });
7513
+ fs12.writeFileSync(file2, JSON.stringify(merged, null, 2));
7280
7514
  }
7281
7515
  function shouldOfferDaemonInstall(facts) {
7282
7516
  if (facts.optedOutEnv) return false;
@@ -8464,13 +8698,9 @@ async function runInteractive(tui, api, accountStream, threadId, projectDir, mac
8464
8698
  const pendingSent = /* @__PURE__ */ new Map();
8465
8699
  let lastSent = null;
8466
8700
  let upgradeInFlight = false;
8467
- let tokensIn = 0;
8468
- let tokensOut = 0;
8469
8701
  let liveOut = 0;
8470
- const countedLogs = /* @__PURE__ */ new Set();
8471
8702
  const activeSteps = /* @__PURE__ */ new Map();
8472
8703
  const refreshStatus = () => {
8473
- tui.setTokens(tokensIn, tokensOut + liveOut);
8474
8704
  let label = null;
8475
8705
  for (const v of activeSteps.values()) label = v;
8476
8706
  tui.setStep(label, liveOut);
@@ -9310,24 +9540,6 @@ ${c5.dim}runs on ${request.machine || runnerName}${c5.reset}`,
9310
9540
  refreshBgCount();
9311
9541
  void relayApprovals().catch(() => {
9312
9542
  });
9313
- try {
9314
- const logs = await api.getLogs(threadId, 100);
9315
- let landed = 0;
9316
- for (const l of logs) {
9317
- if (!l.is_complete) continue;
9318
- const id = l.id ?? `${l.created_at}:${l.total_tokens}`;
9319
- if (countedLogs.has(id)) continue;
9320
- countedLogs.add(id);
9321
- const inT = Number(l.input_tokens) || 0;
9322
- const outT = Number(l.output_tokens) || 0;
9323
- tokensIn += inT;
9324
- tokensOut += outT;
9325
- landed += outT;
9326
- }
9327
- if (landed > 0) liveOut = 0;
9328
- refreshStatus();
9329
- } catch {
9330
- }
9331
9543
  const COMPACTION_TRIGGER_FRACTION = 0.5;
9332
9544
  const COMPACTION_TRIGGER_CAP = 5e4;
9333
9545
  try {
@@ -9611,7 +9823,8 @@ function showDaemonInfo(tui, session) {
9611
9823
  `${c5.dim}The daemon is the executor on every machine: it runs the agent's tools, keeps working after you close the terminal, self-restarts, self-updates, and picks up sessions you start from anywhere.${c5.reset}`
9612
9824
  );
9613
9825
  } else {
9614
- tui.print(`${c5.dim}Manage it with standardcode daemon status | uninstall | add-project <path>.${c5.reset}`);
9826
+ tui.print(`${c5.dim}Daemon commands: standardcode daemon status | restart | update | uninstall.${c5.reset}`);
9827
+ tui.print(`${c5.dim}Add a project: standardcode daemon add-project /absolute/path/to/project${c5.reset}`);
9615
9828
  }
9616
9829
  }
9617
9830
  function showKeybindings(tui) {