@slock-ai/daemon 0.52.0 → 0.52.2

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.
@@ -823,6 +823,7 @@ function buildPrompt(config, variant, opts) {
823
823
  "- Always communicate through `slock` CLI commands. This is your only output channel.",
824
824
  ...opts.extraCriticalRules,
825
825
  "- Use only the provided `slock` CLI commands for messaging.",
826
+ "- Do not combine multiple `slock` CLI commands in one shell command. Run one `slock` command per tool call, read its output, then decide the next command.",
826
827
  "- Always claim a task via `slock task claim` before starting work on it. If the claim fails, move on to a different task."
827
828
  ] : [
828
829
  `- Always communicate through ${sendCmd}. This is your only output channel.`,
@@ -1810,6 +1811,18 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
1810
1811
  });
1811
1812
  return;
1812
1813
  }
1814
+ if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "sent") {
1815
+ const messageSeq2 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
1816
+ if (sendTarget && messageSeq2 && messageSeq2 > 0) {
1817
+ coordinator.consumeVisibleMessages({
1818
+ target: sendTarget,
1819
+ messages: normalizeVisibleMessages([{ seq: messageSeq2, id: parsed.messageId }], sendTarget),
1820
+ boundarySeq: messageSeq2,
1821
+ source: "agent_api_send_commit"
1822
+ });
1823
+ }
1824
+ return;
1825
+ }
1813
1826
  if (targetUrl.pathname === "/internal/agent-api/events" && Array.isArray(parsed.events)) {
1814
1827
  const messages = normalizeVisibleMessages(parsed.events);
1815
1828
  coordinator.consumeVisibleMessages({ messages, source: "agent_api_events" });
@@ -3786,7 +3799,7 @@ function detectKimiModels(home = os4.homedir()) {
3786
3799
 
3787
3800
  // src/drivers/opencode.ts
3788
3801
  import { spawn as spawn7, spawnSync as spawnSync2 } from "child_process";
3789
- import { readFileSync as readFileSync4 } from "fs";
3802
+ import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
3790
3803
  import os5 from "os";
3791
3804
  import path10 from "path";
3792
3805
  var CHAT_MCP_SERVER_NAME = "chat";
@@ -4024,11 +4037,14 @@ function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeMode
4024
4037
  if (commandResult.error || commandResult.status !== 0) return null;
4025
4038
  return parseOpenCodeModelsOutput(commandResult.stdout);
4026
4039
  }
4027
- function runOpenCodeModelsCommand(home) {
4028
- const result = spawnSync2("opencode", ["models"], {
4040
+ function runOpenCodeModelsCommand(home, deps = {}) {
4041
+ const platform = deps.platform ?? process.platform;
4042
+ const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
4043
+ const result = spawnSyncFn("opencode", ["models"], {
4029
4044
  env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
4030
4045
  encoding: "utf8",
4031
- timeout: 5e3
4046
+ timeout: 5e3,
4047
+ shell: platform === "win32"
4032
4048
  });
4033
4049
  return {
4034
4050
  status: result.status,
@@ -4036,6 +4052,102 @@ function runOpenCodeModelsCommand(home) {
4036
4052
  error: result.error
4037
4053
  };
4038
4054
  }
4055
+ function isWindowsCommandShim(commandPath) {
4056
+ const ext = path10.win32.extname(commandPath).toLowerCase();
4057
+ return ext === ".cmd" || ext === ".bat";
4058
+ }
4059
+ function opencodePackageEntryCandidates(packageRoot) {
4060
+ const winPath = path10.win32;
4061
+ return [
4062
+ winPath.join(packageRoot, "bin", "opencode.exe"),
4063
+ winPath.join(packageRoot, "bin", "opencode.js"),
4064
+ winPath.join(packageRoot, "bin", "opencode.mjs"),
4065
+ winPath.join(packageRoot, "dist", "index.js")
4066
+ ];
4067
+ }
4068
+ function openCodeSpecForEntry(entry, commandArgs) {
4069
+ if (path10.win32.extname(entry).toLowerCase() === ".exe") {
4070
+ return { command: entry, args: commandArgs, shell: false };
4071
+ }
4072
+ return { command: process.execPath, args: [entry, ...commandArgs], shell: false };
4073
+ }
4074
+ function resolveWindowsOpenCodePackageEntry(commandPath, deps = {}) {
4075
+ const existsSyncFn = deps.existsSyncFn ?? existsSync8;
4076
+ const execFileSyncFn = deps.execFileSyncFn;
4077
+ const env = deps.env ?? process.env;
4078
+ const winPath = path10.win32;
4079
+ const candidates = [];
4080
+ if (execFileSyncFn) {
4081
+ try {
4082
+ const globalRoot = String(execFileSyncFn("npm", ["root", "-g"], {
4083
+ encoding: "utf8",
4084
+ stdio: ["ignore", "pipe", "ignore"],
4085
+ env
4086
+ })).trim();
4087
+ if (globalRoot) {
4088
+ candidates.push(...opencodePackageEntryCandidates(winPath.join(globalRoot, "opencode-ai")));
4089
+ }
4090
+ } catch {
4091
+ }
4092
+ }
4093
+ if (commandPath) {
4094
+ const commandDir = winPath.dirname(commandPath);
4095
+ candidates.push(...opencodePackageEntryCandidates(winPath.join(commandDir, "node_modules", "opencode-ai")));
4096
+ candidates.push(...extractWindowsShimTargets(commandPath, deps));
4097
+ }
4098
+ for (const candidate of candidates) {
4099
+ if (existsSyncFn(candidate)) return candidate;
4100
+ }
4101
+ return null;
4102
+ }
4103
+ function extractWindowsShimTargets(commandPath, deps = {}) {
4104
+ if (!isWindowsCommandShim(commandPath)) return [];
4105
+ const readFileSyncFn = deps.readFileSyncFn ?? readFileSync4;
4106
+ const commandDir = path10.win32.dirname(commandPath);
4107
+ let raw;
4108
+ try {
4109
+ raw = String(readFileSyncFn(commandPath, "utf8"));
4110
+ } catch {
4111
+ return [];
4112
+ }
4113
+ const candidates = [];
4114
+ const dp0Pattern = /%~dp0\\?([^"\r\n]*?opencode\.(?:exe|js|mjs|cjs))/gi;
4115
+ for (const match of raw.matchAll(dp0Pattern)) {
4116
+ const relative = match[1]?.replace(/^\\+/, "");
4117
+ if (relative) candidates.push(path10.win32.normalize(path10.win32.join(commandDir, relative)));
4118
+ }
4119
+ return candidates;
4120
+ }
4121
+ function resolveOpenCodeSpawn(commandArgs, deps = {}) {
4122
+ const platform = deps.platform ?? process.platform;
4123
+ if (platform !== "win32") {
4124
+ return {
4125
+ command: resolveCommandOnPath("opencode", deps) ?? "opencode",
4126
+ args: commandArgs,
4127
+ shell: false
4128
+ };
4129
+ }
4130
+ const command = resolveCommandOnPath("opencode", deps);
4131
+ if (command && path10.win32.extname(command).toLowerCase() === ".exe") {
4132
+ return { command, args: commandArgs, shell: false };
4133
+ }
4134
+ const packageEntry = resolveWindowsOpenCodePackageEntry(command, deps);
4135
+ if (packageEntry) return openCodeSpecForEntry(packageEntry, commandArgs);
4136
+ if (command && !isWindowsCommandShim(command)) {
4137
+ return { command, args: commandArgs, shell: false };
4138
+ }
4139
+ throw new Error(
4140
+ "Cannot resolve OpenCode CLI entry point on Windows without cmd.exe. Install the native OpenCode executable or install opencode-ai globally so Slock can launch node_modules/opencode-ai/bin/opencode.exe directly."
4141
+ );
4142
+ }
4143
+ function readOpenCodeVersion(deps = {}) {
4144
+ try {
4145
+ const launch = resolveOpenCodeSpawn([], deps);
4146
+ return readCommandVersion(launch.command, launch.args, deps);
4147
+ } catch {
4148
+ return null;
4149
+ }
4150
+ }
4039
4151
  function isSystemFirstMessageTask(message) {
4040
4152
  return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX);
4041
4153
  }
@@ -4078,7 +4190,7 @@ var OpenCodeDriver = class {
4078
4190
  model: modelId
4079
4191
  }
4080
4192
  };
4081
- const version = readCommandVersion("opencode");
4193
+ const version = readOpenCodeVersion();
4082
4194
  const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
4083
4195
  return {
4084
4196
  args: launch.args,
@@ -4099,8 +4211,13 @@ var OpenCodeDriver = class {
4099
4211
  sessionId = null;
4100
4212
  sessionAnnounced = false;
4101
4213
  probe() {
4102
- if (!resolveCommandOnPath("opencode")) return { available: false };
4103
- const version = readCommandVersion("opencode") || void 0;
4214
+ let version;
4215
+ try {
4216
+ const launch = resolveOpenCodeSpawn([]);
4217
+ version = readCommandVersion(launch.command, launch.args);
4218
+ } catch {
4219
+ return { available: false };
4220
+ }
4104
4221
  const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
4105
4222
  if (unsupportedMessage) {
4106
4223
  return {
@@ -4108,7 +4225,7 @@ var OpenCodeDriver = class {
4108
4225
  version: `${version} (requires >= ${MIN_SUPPORTED_OPENCODE_VERSION})`
4109
4226
  };
4110
4227
  }
4111
- return { available: true, version };
4228
+ return { available: true, version: version ?? void 0 };
4112
4229
  }
4113
4230
  async detectModels() {
4114
4231
  return detectOpenCodeModels();
@@ -4116,17 +4233,18 @@ var OpenCodeDriver = class {
4116
4233
  spawn(ctx) {
4117
4234
  this.sessionId = ctx.config.sessionId || null;
4118
4235
  this.sessionAnnounced = false;
4119
- const version = readCommandVersion("opencode");
4236
+ const version = readOpenCodeVersion();
4120
4237
  const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
4121
4238
  if (unsupportedMessage) {
4122
4239
  throw new Error(unsupportedMessage);
4123
4240
  }
4124
4241
  const launch = buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
4125
- const proc = spawn7("opencode", launch.args, {
4242
+ const spawnSpec = resolveOpenCodeSpawn(launch.args);
4243
+ const proc = spawn7(spawnSpec.command, spawnSpec.args, {
4126
4244
  cwd: ctx.workingDirectory,
4127
4245
  stdio: ["pipe", "pipe", "pipe"],
4128
4246
  env: launch.env,
4129
- shell: process.platform === "win32"
4247
+ shell: spawnSpec.shell
4130
4248
  });
4131
4249
  proc.stdin?.end();
4132
4250
  return { process: proc };
@@ -4364,6 +4482,7 @@ function classifyRuntimeError(message, httpStatus) {
4364
4482
  return "ProviderApiError";
4365
4483
  }
4366
4484
  if (/\btimeout|timed out\b/i.test(message)) return "TimeoutError";
4485
+ if (/stream closed before response\.completed|error decoding response body/i.test(message)) return "ProviderStreamError";
4367
4486
  if (/\brate.?limit|too many requests\b/i.test(message)) return "RateLimitError";
4368
4487
  if (/\bnot found\b/i.test(message)) return "NotFoundError";
4369
4488
  return "RuntimeError";
@@ -5238,12 +5357,21 @@ function classifyTerminalFailure(ap) {
5238
5357
  ].filter((value) => !!value);
5239
5358
  for (const text of candidates) {
5240
5359
  const lower = text.toLowerCase();
5241
- if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found")) {
5360
+ if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found") || isProviderStreamFailureText(text)) {
5242
5361
  return text;
5243
5362
  }
5244
5363
  }
5245
5364
  return null;
5246
5365
  }
5366
+ function isProviderStreamFailureText(text) {
5367
+ return /stream closed before response\.completed|error decoding response body/i.test(text);
5368
+ }
5369
+ function isCodexProviderReconnectLog(text) {
5370
+ return /Reconnecting\.\.\.\s*\d+\s*\/\s*\d+/i.test(text);
5371
+ }
5372
+ function isCodexBenignTransportLog(text) {
5373
+ return /Falling back from WebSockets/i.test(text);
5374
+ }
5247
5375
  function hasDirectStdinRecoveryEvidence(ap) {
5248
5376
  const candidates = [
5249
5377
  ap.lastRuntimeError,
@@ -6095,8 +6223,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6095
6223
  proc.stderr?.on("data", (chunk) => {
6096
6224
  const text = chunk.toString().trim();
6097
6225
  if (!text) return;
6098
- if (/Reconnecting\.\.\.|Falling back from WebSockets/i.test(text)) return;
6099
6226
  const current = this.agents.get(agentId);
6227
+ if (driver.id === "codex" && isCodexProviderReconnectLog(text)) {
6228
+ if (current) {
6229
+ current.recentStderr = pushRecentStderr(current.recentStderr, text);
6230
+ }
6231
+ this.recordDaemonTrace("daemon.agent.provider_reconnect", {
6232
+ agentId,
6233
+ launchId: current?.launchId || void 0,
6234
+ runtime: config.runtime,
6235
+ model: config.model
6236
+ });
6237
+ this.broadcastActivity(agentId, "working", "Codex reconnecting to provider\u2026", [
6238
+ { kind: "text", text }
6239
+ ]);
6240
+ logger.info(`[Agent ${agentId} stderr]: ${text}`);
6241
+ return;
6242
+ }
6243
+ if (driver.id === "codex" && isCodexBenignTransportLog(text)) return;
6100
6244
  if (current) {
6101
6245
  current.recentStderr = pushRecentStderr(current.recentStderr, text);
6102
6246
  }
@@ -6240,10 +6384,20 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6240
6384
  }
6241
6385
  this.broadcastActivity(agentId, "online", "Process idle");
6242
6386
  } else {
6243
- this.idleAgentConfigs.delete(agentId);
6244
6387
  const reason = formatCrashReason(finalCode, finalSignal, ap);
6245
- logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
6246
- this.sendAgentStatus(agentId, "inactive", ap.launchId);
6388
+ if (terminalFailureDetail && isProviderStreamFailureText(terminalFailureDetail)) {
6389
+ this.idleAgentConfigs.set(agentId, {
6390
+ config: { ...ap.config, sessionId: ap.sessionId },
6391
+ sessionId: ap.sessionId,
6392
+ launchId: ap.launchId
6393
+ });
6394
+ logger.warn(`[Agent ${agentId}] Recoverable provider stream failure (${reason}) \u2014 keeping agent wakeable`);
6395
+ this.sendAgentStatus(agentId, "active", ap.launchId);
6396
+ } else {
6397
+ this.idleAgentConfigs.delete(agentId);
6398
+ logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
6399
+ this.sendAgentStatus(agentId, "inactive", ap.launchId);
6400
+ }
6247
6401
  if (terminalFailureDetail) {
6248
6402
  this.broadcastActivity(
6249
6403
  agentId,
package/dist/core.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveSlockCliPath,
10
10
  resolveWorkspaceDirectoryPath,
11
11
  scanWorkspaceDirectories
12
- } from "./chunk-XEHIMW55.js";
12
+ } from "./chunk-HSBOURQE.js";
13
13
  import {
14
14
  subscribeDaemonLogs
15
15
  } from "./chunk-KNMCE6WB.js";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  DAEMON_CLI_USAGE,
4
4
  DaemonCore,
5
5
  parseDaemonCliArgs
6
- } from "./chunk-XEHIMW55.js";
6
+ } from "./chunk-HSBOURQE.js";
7
7
  import "./chunk-KNMCE6WB.js";
8
8
 
9
9
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/daemon",
3
- "version": "0.52.0",
3
+ "version": "0.52.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"