@slock-ai/daemon 0.52.1 → 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";
@@ -4039,6 +4052,102 @@ function runOpenCodeModelsCommand(home, deps = {}) {
4039
4052
  error: result.error
4040
4053
  };
4041
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
+ }
4042
4151
  function isSystemFirstMessageTask(message) {
4043
4152
  return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX);
4044
4153
  }
@@ -4081,7 +4190,7 @@ var OpenCodeDriver = class {
4081
4190
  model: modelId
4082
4191
  }
4083
4192
  };
4084
- const version = readCommandVersion("opencode");
4193
+ const version = readOpenCodeVersion();
4085
4194
  const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home, version);
4086
4195
  return {
4087
4196
  args: launch.args,
@@ -4102,8 +4211,13 @@ var OpenCodeDriver = class {
4102
4211
  sessionId = null;
4103
4212
  sessionAnnounced = false;
4104
4213
  probe() {
4105
- if (!resolveCommandOnPath("opencode")) return { available: false };
4106
- 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
+ }
4107
4221
  const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
4108
4222
  if (unsupportedMessage) {
4109
4223
  return {
@@ -4111,7 +4225,7 @@ var OpenCodeDriver = class {
4111
4225
  version: `${version} (requires >= ${MIN_SUPPORTED_OPENCODE_VERSION})`
4112
4226
  };
4113
4227
  }
4114
- return { available: true, version };
4228
+ return { available: true, version: version ?? void 0 };
4115
4229
  }
4116
4230
  async detectModels() {
4117
4231
  return detectOpenCodeModels();
@@ -4119,17 +4233,18 @@ var OpenCodeDriver = class {
4119
4233
  spawn(ctx) {
4120
4234
  this.sessionId = ctx.config.sessionId || null;
4121
4235
  this.sessionAnnounced = false;
4122
- const version = readCommandVersion("opencode");
4236
+ const version = readOpenCodeVersion();
4123
4237
  const unsupportedMessage = unsupportedOpenCodeVersionMessage(version);
4124
4238
  if (unsupportedMessage) {
4125
4239
  throw new Error(unsupportedMessage);
4126
4240
  }
4127
4241
  const launch = buildOpenCodeLaunchOptions(ctx, os5.homedir(), version);
4128
- const proc = spawn7("opencode", launch.args, {
4242
+ const spawnSpec = resolveOpenCodeSpawn(launch.args);
4243
+ const proc = spawn7(spawnSpec.command, spawnSpec.args, {
4129
4244
  cwd: ctx.workingDirectory,
4130
4245
  stdio: ["pipe", "pipe", "pipe"],
4131
4246
  env: launch.env,
4132
- shell: process.platform === "win32"
4247
+ shell: spawnSpec.shell
4133
4248
  });
4134
4249
  proc.stdin?.end();
4135
4250
  return { process: proc };
@@ -4367,6 +4482,7 @@ function classifyRuntimeError(message, httpStatus) {
4367
4482
  return "ProviderApiError";
4368
4483
  }
4369
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";
4370
4486
  if (/\brate.?limit|too many requests\b/i.test(message)) return "RateLimitError";
4371
4487
  if (/\bnot found\b/i.test(message)) return "NotFoundError";
4372
4488
  return "RuntimeError";
@@ -5241,12 +5357,21 @@ function classifyTerminalFailure(ap) {
5241
5357
  ].filter((value) => !!value);
5242
5358
  for (const text of candidates) {
5243
5359
  const lower = text.toLowerCase();
5244
- 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)) {
5245
5361
  return text;
5246
5362
  }
5247
5363
  }
5248
5364
  return null;
5249
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
+ }
5250
5375
  function hasDirectStdinRecoveryEvidence(ap) {
5251
5376
  const candidates = [
5252
5377
  ap.lastRuntimeError,
@@ -6098,8 +6223,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6098
6223
  proc.stderr?.on("data", (chunk) => {
6099
6224
  const text = chunk.toString().trim();
6100
6225
  if (!text) return;
6101
- if (/Reconnecting\.\.\.|Falling back from WebSockets/i.test(text)) return;
6102
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;
6103
6244
  if (current) {
6104
6245
  current.recentStderr = pushRecentStderr(current.recentStderr, text);
6105
6246
  }
@@ -6243,10 +6384,20 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6243
6384
  }
6244
6385
  this.broadcastActivity(agentId, "online", "Process idle");
6245
6386
  } else {
6246
- this.idleAgentConfigs.delete(agentId);
6247
6387
  const reason = formatCrashReason(finalCode, finalSignal, ap);
6248
- logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
6249
- 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
+ }
6250
6401
  if (terminalFailureDetail) {
6251
6402
  this.broadcastActivity(
6252
6403
  agentId,
package/dist/core.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveSlockCliPath,
10
10
  resolveWorkspaceDirectoryPath,
11
11
  scanWorkspaceDirectories
12
- } from "./chunk-WGO5H7XX.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-WGO5H7XX.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.1",
3
+ "version": "0.52.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"