@slock-ai/daemon 0.57.5-play.20260611173452 → 0.57.6

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.
@@ -1879,7 +1879,7 @@ function buildPrompt(config, opts) {
1879
1879
  const checkCmd = "`slock message check`";
1880
1880
  const inboxCheckCmd = "`slock inbox check`";
1881
1881
  const messageDeliveryText = opts.includeStdinNotificationSection ? "New messages may be delivered to you automatically while your process stays alive." : "The daemon will automatically restart you when new messages arrive.";
1882
- const noConcreteMessageStartupStep = opts.includeStdinNotificationSection ? `3. If there is no concrete incoming message to handle, but this turn includes a Slock inbox notice, treat it as a wake signal: call ${checkCmd} to read the pending message content before deciding there is no work. If there is neither a concrete message nor an inbox notice, stop and wait. ${messageDeliveryText}` : `3. If there is no concrete incoming message to handle, stop and wait. ${messageDeliveryText}`;
1882
+ const noConcreteMessageStartupStep = opts.includeStdinNotificationSection ? `3. If there is no concrete incoming message to handle but this turn includes a Slock inbox notice: the notice means messages exist that you have not seen \u2014 their bodies are withheld to avoid flooding you, not absent (unobserved is not the same as nonexistent). Whether and when to read them is your judgment, now or later; ${checkCmd} reads them and the notice metadata (who, where, how many) helps you triage. Never derive "no work" from a content-free notice alone \u2014 if you choose not to read, that is a deferral to report honestly, not a conclusion that nothing is pending. If there is neither a concrete message nor an inbox notice, stop and wait. ${messageDeliveryText}` : `3. If there is no concrete incoming message to handle, stop and wait. ${messageDeliveryText}`;
1883
1883
  const criticalRules = [
1884
1884
  "- Always communicate through `slock` CLI commands. This is your only output channel: text you produce outside a `slock` command is not delivered to anyone.",
1885
1885
  ...opts.extraCriticalRules,
@@ -2025,7 +2025,7 @@ While you are working, the daemon may write a batched, content-free inbox update
2025
2025
 
2026
2026
  How to handle these:
2027
2027
  - Treat the notification as a non-urgent signal that new Slock messages are waiting; it does not include the message content and does not require an immediate interruption.
2028
- - If an inbox notice is the only input that woke this turn, call ${checkCmd} to read the pending message content before deciding there is no action to take.
2028
+ - A content-free notice means messages exist that you have not seen \u2014 not that there is no content or no action. Whether and when to read them is your judgment, now or later; ${checkCmd} is one cheap command and the notice metadata helps you triage. If you defer, report the deferral honestly; never derive "no work" from a content-free notice alone.
2029
2029
  - Keep working until a natural breakpoint. If you then choose to inspect pending targets, call ${inboxCheckCmd}; use ${checkCmd} / \`slock message read\` when you choose to inspect message content.
2030
2030
  - If a message you explicitly read is higher priority, pivot to it. If not, continue your current work.`;
2031
2031
  } else {
@@ -2040,7 +2040,7 @@ ${notifyExample}
2040
2040
 
2041
2041
  How to handle these:
2042
2042
  - The notice is target-scoped and content-free. Its header may show total unread/pending count, while detail rows list the targets changed by this update; it never includes message bodies.
2043
- - If an inbox notice is the only input that woke this turn, call ${checkCmd} to read the pending message content before deciding there is no action to take.
2043
+ - A content-free notice means messages exist that you have not seen \u2014 not that there is no content or no action. Whether and when to read them is your judgment, now or later; ${checkCmd} is one cheap command and the notice metadata helps you triage. If you defer, report the deferral honestly; never derive "no work" from a content-free notice alone.
2044
2044
  - Do not interrupt the current step just because a notice arrived. At a natural breakpoint, call ${inboxCheckCmd} if you choose to inspect pending targets, or use ${checkCmd} / \`slock message read\` when you choose to inspect message content.
2045
2045
  - If a message you explicitly read is higher priority, you may pivot to it. If not, continue your current work.`;
2046
2046
  }
@@ -2096,19 +2096,6 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
2096
2096
  return candidates.filter((candidate) => existsSync(candidate.path));
2097
2097
  }
2098
2098
 
2099
- // src/authEnv.ts
2100
- var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
2101
- var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
2102
- function scrubDaemonAuthEnv(env) {
2103
- delete env[DAEMON_API_KEY_ENV];
2104
- return env;
2105
- }
2106
- function scrubDaemonChildEnv(env) {
2107
- delete env[DAEMON_API_KEY_ENV];
2108
- delete env[SLOCK_AGENT_TOKEN_ENV];
2109
- return env;
2110
- }
2111
-
2112
2099
  // src/agentCredentialProxy.ts
2113
2100
  import { randomBytes } from "crypto";
2114
2101
  import http from "http";
@@ -3297,6 +3284,7 @@ function routeFamilyForPath(pathname) {
3297
3284
  if (pathname === "/internal/agent-api/tasks/claim") return "tasks/claim";
3298
3285
  if (pathname === "/internal/agent-api/tasks/update-status") return "tasks/update";
3299
3286
  if (pathname === "/internal/agent-api/tasks" || pathname.startsWith("/internal/agent-api/tasks/")) return "tasks";
3287
+ if (/^\/internal\/agent-api\/attachments\/[^/]+\/comments/.test(pathname)) return "agent-api/attachments/comments";
3300
3288
  if (pathname.startsWith("/internal/agent-api/attachments/")) return "agent-api/attachments";
3301
3289
  if (/^\/internal\/agent-api\/messages\/[^/]+\/resolve$/.test(pathname)) return "agent-api/messages/resolve";
3302
3290
  if (/^\/internal\/agent-api\/messages\/[^/]+\/reactions$/.test(pathname)) return "agent-api/messages/reactions";
@@ -3595,9 +3583,7 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
3595
3583
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
3596
3584
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
3597
3585
  var RAW_CREDENTIAL_ENV_DENYLIST = [
3598
- "SLOCK_AGENT_TOKEN",
3599
- "SLOCK_AGENT_CREDENTIAL_KEY",
3600
- "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
3586
+ "SLOCK_AGENT_CREDENTIAL_KEY"
3601
3587
  ];
3602
3588
  var cachedOpencliBinPath;
3603
3589
  function resolveOpencliBinPath() {
@@ -3812,7 +3798,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
3812
3798
  ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
3813
3799
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
3814
3800
  };
3815
- scrubDaemonChildEnv(spawnEnv);
3801
+ delete spawnEnv.SLOCK_AGENT_TOKEN;
3816
3802
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
3817
3803
  delete spawnEnv[key];
3818
3804
  }
@@ -4241,7 +4227,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
4241
4227
  }
4242
4228
  function resolveCommandOnPath(command, deps = {}) {
4243
4229
  const platform = deps.platform ?? process.platform;
4244
- const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
4230
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
4245
4231
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
4246
4232
  const existsSyncFn = deps.existsSyncFn ?? existsSync2;
4247
4233
  if (platform === "win32") {
@@ -4267,7 +4253,7 @@ function firstExistingPath(candidates, deps = {}) {
4267
4253
  return null;
4268
4254
  }
4269
4255
  function readCommandVersion(command, args = [], deps = {}) {
4270
- const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
4256
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
4271
4257
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
4272
4258
  try {
4273
4259
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -5656,11 +5642,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
5656
5642
  return parseCursorModelsOutput(String(result.stdout || ""));
5657
5643
  }
5658
5644
  function buildCursorModelProbeEnv(deps = {}) {
5659
- return scrubDaemonChildEnv(withWindowsUserEnvironment({
5645
+ return withWindowsUserEnvironment({
5660
5646
  ...deps.env ?? process.env,
5661
5647
  FORCE_COLOR: "0",
5662
5648
  NO_COLOR: "1"
5663
- }, deps));
5649
+ }, deps);
5664
5650
  }
5665
5651
  function runCursorModelsCommand() {
5666
5652
  return spawnSync("cursor-agent", ["models"], {
@@ -5716,7 +5702,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
5716
5702
  }
5717
5703
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
5718
5704
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5719
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
5705
+ const env = deps.env ?? process.env;
5720
5706
  const winPath = path7.win32;
5721
5707
  let geminiEntry = null;
5722
5708
  try {
@@ -5853,15 +5839,12 @@ var GeminiDriver = class {
5853
5839
  // src/drivers/kimi.ts
5854
5840
  import { randomUUID as randomUUID2 } from "crypto";
5855
5841
  import { spawn as spawn7 } from "child_process";
5856
- import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5842
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5857
5843
  import os4 from "os";
5858
5844
  import path8 from "path";
5859
5845
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
5860
5846
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
5861
5847
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
5862
- var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
5863
- var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
5864
- var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
5865
5848
  function parseToolArguments(raw) {
5866
5849
  if (typeof raw !== "string") return raw;
5867
5850
  try {
@@ -5870,73 +5853,6 @@ function parseToolArguments(raw) {
5870
5853
  return raw;
5871
5854
  }
5872
5855
  }
5873
- function readKimiConfigSource(home = os4.homedir(), env = process.env) {
5874
- const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5875
- if (inlineConfig && inlineConfig.trim()) {
5876
- return {
5877
- raw: inlineConfig,
5878
- explicitPath: null,
5879
- sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
5880
- };
5881
- }
5882
- const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
5883
- const configPath = explicitPath && explicitPath.trim() ? explicitPath : path8.join(home, ".kimi", "config.toml");
5884
- try {
5885
- return {
5886
- raw: readFileSync3(configPath, "utf8"),
5887
- explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5888
- sourcePath: configPath
5889
- };
5890
- } catch {
5891
- return {
5892
- raw: null,
5893
- explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5894
- sourcePath: configPath
5895
- };
5896
- }
5897
- }
5898
- function buildKimiSpawnEnv(env = process.env) {
5899
- const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
5900
- delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5901
- delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
5902
- return scrubDaemonChildEnv(spawnEnv);
5903
- }
5904
- function buildKimiEffectiveEnv(ctx, overrideEnv) {
5905
- return {
5906
- ...process.env,
5907
- ...ctx.config.envVars || {},
5908
- ...overrideEnv || {}
5909
- };
5910
- }
5911
- function buildKimiLaunchOptions(ctx, opts = {}) {
5912
- const env = buildKimiEffectiveEnv(ctx, opts.env);
5913
- const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
5914
- const args = [];
5915
- let configFilePath = null;
5916
- let configContent = null;
5917
- if (source.explicitPath) {
5918
- configFilePath = source.explicitPath;
5919
- } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
5920
- configFilePath = path8.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
5921
- configContent = source.raw;
5922
- if (opts.writeGeneratedConfig !== false) {
5923
- writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
5924
- chmodSync(configFilePath, 384);
5925
- }
5926
- }
5927
- if (configFilePath) {
5928
- args.push("--config-file", configFilePath);
5929
- }
5930
- if (ctx.config.model && ctx.config.model !== "default") {
5931
- args.push("--model", ctx.config.model);
5932
- }
5933
- return {
5934
- args,
5935
- env: buildKimiSpawnEnv(env),
5936
- configFilePath,
5937
- configContent
5938
- };
5939
- }
5940
5856
  function resolveKimiSpawn(commandArgs, deps = {}) {
5941
5857
  return {
5942
5858
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -5960,25 +5876,7 @@ var KimiDriver = class {
5960
5876
  };
5961
5877
  model = {
5962
5878
  detectedModelsVerifiedAs: "launchable",
5963
- toLaunchSpec: (modelId, ctx, opts) => {
5964
- if (!ctx) return { args: ["--model", modelId] };
5965
- const launchCtx = {
5966
- ...ctx,
5967
- config: {
5968
- ...ctx.config,
5969
- model: modelId
5970
- }
5971
- };
5972
- const launch = buildKimiLaunchOptions(launchCtx, {
5973
- home: opts?.home,
5974
- writeGeneratedConfig: false
5975
- });
5976
- return {
5977
- args: launch.args,
5978
- env: launch.env,
5979
- configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
5980
- };
5981
- }
5879
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
5982
5880
  };
5983
5881
  supportsStdinNotification = true;
5984
5882
  busyDeliveryMode = "direct";
@@ -6002,23 +5900,21 @@ var KimiDriver = class {
6002
5900
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
6003
5901
  ""
6004
5902
  ].join("\n"), "utf8");
6005
- const launch = buildKimiLaunchOptions(ctx);
6006
5903
  const args = [
6007
5904
  "--wire",
6008
5905
  "--yolo",
6009
5906
  "--agent-file",
6010
5907
  agentFilePath,
6011
5908
  "--session",
6012
- this.sessionId,
6013
- ...launch.args
5909
+ this.sessionId
6014
5910
  ];
6015
5911
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6016
5912
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
6017
5913
  args.push("--model", launchRuntimeFields.model);
6018
5914
  }
6019
5915
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
6020
- const spawnTarget = resolveKimiSpawn(args);
6021
- const proc = spawn7(spawnTarget.command, spawnTarget.args, {
5916
+ const launch = resolveKimiSpawn(args);
5917
+ const proc = spawn7(launch.command, launch.args, {
6022
5918
  cwd: ctx.workingDirectory,
6023
5919
  stdio: ["pipe", "pipe", "pipe"],
6024
5920
  env: spawnEnv,
@@ -6026,7 +5922,7 @@ var KimiDriver = class {
6026
5922
  // and has an 8191-character command-line limit. Kimi's official
6027
5923
  // installer/uv entrypoint is an executable, so launch it directly and
6028
5924
  // keep prompts on stdin / files instead of routing through cmd.exe.
6029
- shell: spawnTarget.shell
5925
+ shell: launch.shell
6030
5926
  });
6031
5927
  proc.stdin?.write(JSON.stringify({
6032
5928
  jsonrpc: "2.0",
@@ -6139,9 +6035,14 @@ var KimiDriver = class {
6139
6035
  return detectKimiModels();
6140
6036
  }
6141
6037
  };
6142
- function detectKimiModels(home = os4.homedir(), opts = {}) {
6143
- const raw = readKimiConfigSource(home, opts.env).raw;
6144
- if (raw === null) return null;
6038
+ function detectKimiModels(home = os4.homedir()) {
6039
+ const configPath = path8.join(home, ".kimi", "config.toml");
6040
+ let raw;
6041
+ try {
6042
+ raw = readFileSync3(configPath, "utf8");
6043
+ } catch {
6044
+ return null;
6045
+ }
6145
6046
  const models = [];
6146
6047
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
6147
6048
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -6379,7 +6280,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
6379
6280
  const platform = deps.platform ?? process.platform;
6380
6281
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
6381
6282
  const result = spawnSyncFn("opencode", ["models"], {
6382
- env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
6283
+ env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
6383
6284
  encoding: "utf8",
6384
6285
  timeout: 5e3,
6385
6286
  shell: platform === "win32"
@@ -14184,7 +14085,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
14184
14085
  spanAttrs: ["running_agents_count", "idle_agents_count"]
14185
14086
  }
14186
14087
  };
14187
- var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
14088
+ var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
14188
14089
  var RunnerCredentialMintError2 = class extends Error {
14189
14090
  code;
14190
14091
  retryable;
@@ -14220,9 +14121,9 @@ function runnerCredentialErrorDetail2(error) {
14220
14121
  async function waitForRunnerCredentialRetry2() {
14221
14122
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
14222
14123
  }
14223
- function parseDaemonCliArgs(args, env = {}) {
14124
+ function parseDaemonCliArgs(args) {
14224
14125
  let serverUrl = "";
14225
- let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
14126
+ let apiKey = "";
14226
14127
  for (let i = 0; i < args.length; i++) {
14227
14128
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
14228
14129
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -14250,6 +14151,17 @@ function resolveSlockCliPath(moduleUrl = import.meta.url) {
14250
14151
  return workspaceDistPath;
14251
14152
  }
14252
14153
  }
14154
+ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
14155
+ try {
14156
+ return resolveSlockCliPath(moduleUrl);
14157
+ } catch {
14158
+ return "";
14159
+ }
14160
+ }
14161
+ async function runBundledSlockCli(argv) {
14162
+ process.argv = [process.execPath, "slock", ...argv];
14163
+ await import("./dist-CYKZS5JA.js");
14164
+ }
14253
14165
  function detectRuntimes(tracer = noopTracer) {
14254
14166
  const ids = [];
14255
14167
  const versions = {};
@@ -14399,7 +14311,7 @@ var DaemonCore = class {
14399
14311
  this.options = options;
14400
14312
  this.daemonVersion = options.daemonVersion ?? readDaemonVersion();
14401
14313
  this.computerVersion = (process.env.SLOCK_COMPUTER_VERSION || "").trim() || null;
14402
- this.slockCliPath = options.slockCliPath ?? resolveSlockCliPath();
14314
+ this.slockCliPath = options.slockCliPath ?? resolveSlockCliPathOrEmpty();
14403
14315
  this.slockHome = resolveSlockHome();
14404
14316
  process.env[SLOCK_HOME_ENV] = this.slockHome;
14405
14317
  this.injectedTracer = Boolean(options.tracer);
@@ -14897,18 +14809,29 @@ var DaemonCore = class {
14897
14809
  case "computer:restart":
14898
14810
  case "computer:upgrade": {
14899
14811
  const action = msg.type === "computer:restart" ? "restart" : "upgrade";
14812
+ const requestId = msg.requestId;
14900
14813
  this.recordDaemonTrace("daemon.computer_control.received", {
14901
14814
  action,
14902
- handled: Boolean(this.options.onComputerControl)
14815
+ handled: Boolean(this.options.onComputerControl),
14816
+ ...requestId ? { request_id: requestId } : {}
14903
14817
  });
14904
14818
  if (this.options.onComputerControl) {
14905
- try {
14906
- this.options.onComputerControl(action);
14907
- } catch (err) {
14819
+ const ctx = {
14820
+ requestId,
14821
+ emitUpgradeProgress: (ev) => {
14822
+ if (!requestId) return;
14823
+ this.connection.send({ type: "computer:upgrade:progress", requestId, ...ev });
14824
+ },
14825
+ emitUpgradeDone: (ev) => {
14826
+ if (!requestId) return;
14827
+ this.connection.send({ type: "computer:upgrade:done", requestId, ...ev });
14828
+ }
14829
+ };
14830
+ void Promise.resolve().then(() => this.options.onComputerControl(action, ctx)).catch((err) => {
14908
14831
  logger.error(
14909
14832
  `[Daemon] computer:${action} control handler failed: ${err instanceof Error ? err.message : String(err)}`
14910
14833
  );
14911
- }
14834
+ });
14912
14835
  } else {
14913
14836
  logger.info(`[Daemon] Ignoring computer:${action} \u2014 not launched by a Computer service.`);
14914
14837
  }
@@ -14949,6 +14872,22 @@ var DaemonCore = class {
14949
14872
  idle_agents_count: idleAgentSessions.length,
14950
14873
  runtime_profile_reports_count: runtimeProfileReports.length
14951
14874
  });
14875
+ if (this.options.onComputerUpgradeReconcile) {
14876
+ void Promise.resolve().then(
14877
+ () => this.options.onComputerUpgradeReconcile((done) => {
14878
+ this.connection.send({ type: "computer:upgrade:done", ...done });
14879
+ this.recordDaemonTrace("daemon.computer_upgrade.reconciled", {
14880
+ request_id: done.requestId,
14881
+ ok: done.ok,
14882
+ ...done.newVersion ? { new_version: done.newVersion } : {}
14883
+ });
14884
+ })
14885
+ ).catch((err) => {
14886
+ logger.error(
14887
+ `[Daemon] computer upgrade reconcile failed: ${err instanceof Error ? err.message : String(err)}`
14888
+ );
14889
+ });
14890
+ }
14952
14891
  for (const agentId of runningAgentIds) {
14953
14892
  const sessionId = this.agentManager.getAgentSessionId(agentId);
14954
14893
  const launchId = this.agentManager.getAgentLaunchId(agentId);
@@ -15003,8 +14942,6 @@ var DaemonCore = class {
15003
14942
  };
15004
14943
 
15005
14944
  export {
15006
- DAEMON_API_KEY_ENV,
15007
- scrubDaemonAuthEnv,
15008
14945
  subscribeDaemonLogs,
15009
14946
  resolveWorkspaceDirectoryPath,
15010
14947
  scanWorkspaceDirectories,
@@ -15013,6 +14950,8 @@ export {
15013
14950
  parseDaemonCliArgs,
15014
14951
  readDaemonVersion,
15015
14952
  resolveSlockCliPath,
14953
+ resolveSlockCliPathOrEmpty,
14954
+ runBundledSlockCli,
15016
14955
  detectRuntimes,
15017
14956
  DaemonCore
15018
14957
  };
package/dist/cli/index.js CHANGED
@@ -166,6 +166,8 @@ function routeFamilyForPath(pathname) {
166
166
  if (normalized === "/internal/agent-api/tasks" || normalized.startsWith("/internal/agent-api/tasks/")) {
167
167
  return "tasks";
168
168
  }
169
+ if (/^\/internal\/agent-api\/attachments\/[^/]+\/comments/.test(normalized)) return "agent-api/attachments/comments";
170
+ if (/^\/api\/attachments\/[^/]+\/comments/.test(normalized)) return "agent-api/attachments/comments";
169
171
  if (normalized.startsWith("/internal/agent-api/attachments/")) return "agent-api/attachments";
170
172
  if (normalized.startsWith("/api/attachments/")) return "attachments/download";
171
173
  if (/^\/internal\/agent-api\/messages\/[^/]+\/reactions$/.test(normalized)) return "agent-api/messages/reactions";
@@ -18751,6 +18753,76 @@ function registerAttachmentViewCommand(parent, runtimeOptions) {
18751
18753
  registerCliCommand(parent, attachmentViewCommand, runtimeOptions);
18752
18754
  }
18753
18755
 
18756
+ // src/commands/attachment/comments.ts
18757
+ function anchorSummary(anchor) {
18758
+ if (anchor.type === "md-section") {
18759
+ const title = anchor.data.headingTitle ?? anchor.data.headingId;
18760
+ return typeof title === "string" && title ? `\xA7 ${title}` : "\xA7 section";
18761
+ }
18762
+ if (anchor.type === "lines" || anchor.type === "csv-rows") {
18763
+ const start = Number(anchor.data.start);
18764
+ const end = Number(anchor.data.end ?? start);
18765
+ if (!Number.isFinite(start)) return anchor.type;
18766
+ const prefix = anchor.type === "lines" ? "L" : "rows ";
18767
+ return start === (Number.isFinite(end) ? end : start) ? `${prefix}${start}` : `${prefix}${start}\u2013${end}`;
18768
+ }
18769
+ if (anchor.type === "html-region") {
18770
+ const quote = anchor.data.quote;
18771
+ if (typeof quote === "string" && quote.trim().length > 0) {
18772
+ const trimmed = quote.trim();
18773
+ return trimmed.length > 60 ? `${trimmed.slice(0, 60)}\u2026` : trimmed;
18774
+ }
18775
+ return "HTML region";
18776
+ }
18777
+ return anchor.type;
18778
+ }
18779
+ var attachmentCommentsCommand = defineCommand(
18780
+ {
18781
+ name: "comments",
18782
+ description: "List comments scoped to an attachment",
18783
+ options: [
18784
+ { flags: "--id <attachmentId>", description: "Attachment UUID" },
18785
+ { flags: "--limit <n>", description: "Max comments to return (default 200)" }
18786
+ ]
18787
+ },
18788
+ async (ctx, opts) => {
18789
+ const id = opts.id?.trim();
18790
+ if (!id) {
18791
+ throw new CliError({ code: "INVALID_ARG", message: "--id is required" });
18792
+ }
18793
+ const agentContext = ctx.loadAgentContext();
18794
+ const client = ctx.createApiClient(agentContext);
18795
+ const limitParam = opts.limit && Number.isFinite(Number(opts.limit)) ? `?limit=${Number(opts.limit)}` : "";
18796
+ const res = await client.request(
18797
+ "GET",
18798
+ `/api/attachments/${encodeURIComponent(id)}/comments${limitParam}`
18799
+ );
18800
+ if (!res.ok || !res.data) {
18801
+ const code = res.status >= 500 ? "SERVER_5XX" : "COMMENTS_FAILED";
18802
+ throw new CliError({ code, message: res.error ?? `HTTP ${res.status}` });
18803
+ }
18804
+ const { comments, threadChannelId } = res.data;
18805
+ if (comments.length === 0) {
18806
+ writeText(ctx.io, `No comments on attachment ${id.slice(0, 8)}.
18807
+ `);
18808
+ return;
18809
+ }
18810
+ const lines = [`## Comments on attachment ${id.slice(0, 8)} (${comments.length})`];
18811
+ for (const c of comments) {
18812
+ const check2 = c.reactions.some((r) => r.emoji === "\u2705") ? " \u2705" : "";
18813
+ const anchor = c.anchor ? ` [anchor: ${anchorSummary(c.anchor)}]` : "";
18814
+ lines.push(`[msg=${c.id.slice(0, 8)} time=${c.createdAt} type=${c.senderType}]${check2}${anchor} @${c.senderName}: ${c.content}`);
18815
+ }
18816
+ if (threadChannelId) {
18817
+ lines.push(`(full conversation lives in thread channel ${threadChannelId})`);
18818
+ }
18819
+ writeText(ctx.io, lines.join("\n") + "\n");
18820
+ }
18821
+ );
18822
+ function registerAttachmentCommentsCommand(parent, runtimeOptions) {
18823
+ registerCliCommand(parent, attachmentCommentsCommand, runtimeOptions);
18824
+ }
18825
+
18754
18826
  // src/commands/task/_format.ts
18755
18827
  function formatTaskList(channel, data, statusFilter) {
18756
18828
  if (!data.tasks || data.tasks.length === 0) {
@@ -20390,6 +20462,7 @@ registerReactCommand(messageCmd);
20390
20462
  var attachmentCmd = program.command("attachment").description("Attachment operations");
20391
20463
  registerAttachmentUploadCommand(attachmentCmd);
20392
20464
  registerAttachmentViewCommand(attachmentCmd);
20465
+ registerAttachmentCommentsCommand(attachmentCmd);
20393
20466
  var taskCmd = program.command("task").description("Task board operations");
20394
20467
  registerTaskListCommand(taskCmd);
20395
20468
  registerTaskCreateCommand(taskCmd);
package/dist/core.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import {
2
- DAEMON_API_KEY_ENV,
3
2
  DAEMON_CLI_USAGE,
4
3
  DaemonCore,
5
4
  deleteWorkspaceDirectory,
@@ -7,13 +6,13 @@ import {
7
6
  parseDaemonCliArgs,
8
7
  readDaemonVersion,
9
8
  resolveSlockCliPath,
9
+ resolveSlockCliPathOrEmpty,
10
10
  resolveWorkspaceDirectoryPath,
11
+ runBundledSlockCli,
11
12
  scanWorkspaceDirectories,
12
- scrubDaemonAuthEnv,
13
13
  subscribeDaemonLogs
14
- } from "./chunk-NHANGBTA.js";
14
+ } from "./chunk-XQ3HLMCZ.js";
15
15
  export {
16
- DAEMON_API_KEY_ENV,
17
16
  DAEMON_CLI_USAGE,
18
17
  DaemonCore,
19
18
  deleteWorkspaceDirectory,
@@ -21,8 +20,9 @@ export {
21
20
  parseDaemonCliArgs,
22
21
  readDaemonVersion,
23
22
  resolveSlockCliPath,
23
+ resolveSlockCliPathOrEmpty,
24
24
  resolveWorkspaceDirectoryPath,
25
+ runBundledSlockCli,
25
26
  scanWorkspaceDirectories,
26
- scrubDaemonAuthEnv,
27
27
  subscribeDaemonLogs
28
28
  };