@slock-ai/daemon 0.46.0 → 0.46.1

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.
@@ -34,7 +34,7 @@ var searchMessagesDeprecatedSchema = {
34
34
  };
35
35
  var listTasksDeprecatedSchema = {
36
36
  channel: z.string().describe("Deprecated channel argument."),
37
- status: z.enum(["all", "todo", "in_progress", "in_review", "done"]).optional().describe("Deprecated status argument.")
37
+ status: z.enum(["all", "todo", "in_progress", "in_review", "done", "closed"]).optional().describe("Deprecated status argument.")
38
38
  };
39
39
  var claimTasksDeprecatedSchema = {
40
40
  channel: z.string().describe("Deprecated channel argument."),
@@ -48,7 +48,7 @@ var unclaimTaskDeprecatedSchema = {
48
48
  var updateTaskStatusDeprecatedSchema = {
49
49
  channel: z.string().describe("Deprecated channel argument."),
50
50
  task_number: z.number().describe("Deprecated task number."),
51
- status: z.enum(["todo", "in_progress", "in_review", "done"]).describe("Deprecated status argument.")
51
+ status: z.enum(["todo", "in_progress", "in_review", "done", "closed"]).describe("Deprecated status argument.")
52
52
  };
53
53
  var DEPRECATED_MCP_TOOL_DEFINITIONS = [
54
54
  {
@@ -1087,7 +1087,7 @@ ${formatted}${footer}`
1087
1087
  "List all tasks in a channel. Returns each task's number, title, status, assignee, and message ID. Use this to see what work exists before claiming. Tasks marked as legacy are from an older system and cannot be claimed or modified.",
1088
1088
  {
1089
1089
  channel: z2.string().describe("The channel whose task board to view \u2014 e.g. '#engineering', '#proj-slock'"),
1090
- status: z2.enum(["all", "todo", "in_progress", "in_review", "done"]).default("all").describe("Filter by status (default: all)")
1090
+ status: z2.enum(["all", "todo", "in_progress", "in_review", "done", "closed"]).default("all").describe("Filter by status (default: all)")
1091
1091
  },
1092
1092
  async ({ channel, status }) => {
1093
1093
  try {
@@ -1311,11 +1311,11 @@ ${lines.join("\n")}${threadHint}`
1311
1311
  );
1312
1312
  server.tool(
1313
1313
  "update_task_status",
1314
- "Update a task's progress status. You must be the task's assignee to update it. Use in_review when your work is ready for human validation. Only set done for trivial tasks or after explicit approval. Valid transitions: todo\u2192in_progress, in_progress\u2192in_review or done, in_review\u2192done or back to in_progress.",
1314
+ "Update a task's progress status. You must be the task's assignee to update it. Use in_review when your work is ready for human validation. Only set done for trivial tasks or after explicit approval. Use closed to mark a task as won't-do (cancelled / abandoned / out-of-scope) \u2014 distinct from done. Valid transitions: todo\u2192{in_progress,closed}, in_progress\u2192{in_review,done,closed}, in_review\u2192{done,in_progress,closed}, done\u2192{todo,in_progress,in_review,closed}, closed\u2192todo (reopen).",
1315
1315
  {
1316
1316
  channel: z2.string().describe("The channel \u2014 e.g. '#engineering'"),
1317
1317
  task_number: z2.number().describe("The task number to update (e.g. 3)"),
1318
- status: z2.enum(["todo", "in_progress", "in_review", "done"]).describe("The new status")
1318
+ status: z2.enum(["todo", "in_progress", "in_review", "done", "closed"]).describe("The new status")
1319
1319
  },
1320
1320
  async ({ channel, task_number, status }) => {
1321
1321
  try {
@@ -2558,8 +2558,8 @@ function runCursorModelsCommand() {
2558
2558
  }
2559
2559
 
2560
2560
  // src/drivers/gemini.ts
2561
- import { spawn as spawn5 } from "child_process";
2562
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync3 } from "fs";
2561
+ import { execFileSync as execFileSync2, spawn as spawn5 } from "child_process";
2562
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
2563
2563
  import path7 from "path";
2564
2564
  function buildGeminiSpawnEnv(ctx, platform = process.platform) {
2565
2565
  const { spawnEnv } = prepareCliTransport(ctx, { NO_COLOR: "1" }, platform);
@@ -2568,6 +2568,71 @@ function buildGeminiSpawnEnv(ctx, platform = process.platform) {
2568
2568
  }
2569
2569
  return spawnEnv;
2570
2570
  }
2571
+ function normalizeExecOutput2(raw) {
2572
+ return Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw ?? "");
2573
+ }
2574
+ function buildGeminiArgs(config) {
2575
+ const args = [
2576
+ "--output-format",
2577
+ "stream-json",
2578
+ "--yolo",
2579
+ // Gemini CLI headless mode is selected by -p/--prompt. Keep the actual
2580
+ // prompt off argv and feed it through stdin below; this avoids Windows
2581
+ // cmd.exe's 8191-character command-line limit and keeps long wake payloads
2582
+ // below CreateProcess argv pressure too.
2583
+ "-p",
2584
+ ""
2585
+ ];
2586
+ if (config.model && config.model !== "default") {
2587
+ args.push("--model", config.model);
2588
+ }
2589
+ if (config.sessionId) {
2590
+ args.push("--resume", config.sessionId);
2591
+ }
2592
+ return args;
2593
+ }
2594
+ function resolveGeminiSpawn(commandArgs, deps = {}) {
2595
+ const platform = deps.platform ?? process.platform;
2596
+ if (platform !== "win32") {
2597
+ return { command: resolveCommandOnPath("gemini", deps) ?? "gemini", args: commandArgs };
2598
+ }
2599
+ const execFileSyncFn = deps.execFileSyncFn ?? execFileSync2;
2600
+ const existsSyncFn = deps.existsSyncFn ?? existsSync5;
2601
+ const env = deps.env ?? process.env;
2602
+ const winPath = path7.win32;
2603
+ let geminiEntry = null;
2604
+ try {
2605
+ const globalRoot = normalizeExecOutput2(execFileSyncFn("npm", ["root", "-g"], {
2606
+ encoding: "utf8",
2607
+ stdio: ["ignore", "pipe", "pipe"],
2608
+ env
2609
+ })).trim();
2610
+ const candidate = winPath.join(globalRoot, "@google", "gemini-cli", "bundle", "gemini.js");
2611
+ if (existsSyncFn(candidate)) geminiEntry = candidate;
2612
+ } catch {
2613
+ }
2614
+ if (!geminiEntry) {
2615
+ try {
2616
+ const cmdPath = normalizeExecOutput2(execFileSyncFn("where.exe", ["gemini"], {
2617
+ encoding: "utf8",
2618
+ stdio: ["ignore", "pipe", "pipe"],
2619
+ env
2620
+ })).trim().split(/\r?\n/)[0];
2621
+ const candidate = winPath.join(winPath.dirname(cmdPath), "node_modules", "@google", "gemini-cli", "bundle", "gemini.js");
2622
+ if (existsSyncFn(candidate)) geminiEntry = candidate;
2623
+ } catch {
2624
+ }
2625
+ }
2626
+ if (!geminiEntry) {
2627
+ throw new Error(
2628
+ "Cannot resolve Gemini CLI entry point on Windows. Ensure @google/gemini-cli is installed globally via npm (npm i -g @google/gemini-cli)."
2629
+ );
2630
+ }
2631
+ return {
2632
+ command: process.execPath,
2633
+ args: [geminiEntry, ...commandArgs]
2634
+ };
2635
+ }
2571
2636
  var GeminiDriver = class {
2572
2637
  id = "gemini";
2573
2638
  lifecycle = {
@@ -2597,26 +2662,15 @@ var GeminiDriver = class {
2597
2662
  this.sessionId = ctx.config.sessionId || null;
2598
2663
  this.sessionAnnounced = false;
2599
2664
  this.writeGeminiSettings(ctx);
2600
- const args = [
2601
- "--output-format",
2602
- "stream-json",
2603
- "--yolo",
2604
- "-p",
2605
- ctx.prompt
2606
- ];
2607
- if (ctx.config.model && ctx.config.model !== "default") {
2608
- args.push("--model", ctx.config.model);
2609
- }
2610
- if (ctx.config.sessionId) {
2611
- args.push("--resume", ctx.config.sessionId);
2612
- }
2665
+ const { command, args } = resolveGeminiSpawn(buildGeminiArgs(ctx.config));
2613
2666
  const spawnEnv = buildGeminiSpawnEnv(ctx);
2614
- const proc = spawn5("gemini", args, {
2667
+ const proc = spawn5(command, args, {
2615
2668
  cwd: ctx.workingDirectory,
2616
2669
  stdio: ["pipe", "pipe", "pipe"],
2617
2670
  env: spawnEnv,
2618
- shell: process.platform === "win32"
2671
+ shell: false
2619
2672
  });
2673
+ proc.stdin?.end(ctx.prompt);
2620
2674
  return { process: proc };
2621
2675
  }
2622
2676
  parseLine(line) {
@@ -2718,7 +2772,7 @@ var GeminiDriver = class {
2718
2772
  // src/drivers/kimi.ts
2719
2773
  import { randomUUID } from "crypto";
2720
2774
  import { spawn as spawn6 } from "child_process";
2721
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2775
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2722
2776
  import os3 from "os";
2723
2777
  import path8 from "path";
2724
2778
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
@@ -2783,7 +2837,7 @@ var KimiDriver = class {
2783
2837
  const systemPromptPath = path8.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
2784
2838
  const agentFilePath = path8.join(ctx.workingDirectory, KIMI_AGENT_FILE);
2785
2839
  const mcpConfigPath = path8.join(ctx.workingDirectory, KIMI_MCP_FILE);
2786
- if (!isResume || !existsSync5(systemPromptPath)) {
2840
+ if (!isResume || !existsSync6(systemPromptPath)) {
2787
2841
  writeFileSync6(systemPromptPath, ctx.prompt, "utf8");
2788
2842
  }
2789
2843
  writeFileSync6(agentFilePath, [
@@ -2972,6 +3026,14 @@ var SLOCK_AGENT_NAME = "slock";
2972
3026
  var NO_MESSAGE_PROMPT = "No new messages are pending. Stop now.";
2973
3027
  var FIRST_MESSAGE_TASK_PREFIX = "First message task (system-triggered):";
2974
3028
  var MIN_SUPPORTED_OPENCODE_VERSION = "1.14.30";
3029
+ var OPENCODE_PROVIDER_LABELS = {
3030
+ opencode: "OpenCode",
3031
+ "opencode-go": "OpenCode Go",
3032
+ openai: "OpenAI",
3033
+ openrouter: "OpenRouter",
3034
+ deepseek: "DeepSeek",
3035
+ fusecode: "FuseCode"
3036
+ };
2975
3037
  function buildChatBridgeCommand(ctx) {
2976
3038
  const isTsSource = ctx.chatBridgePath.endsWith(".ts");
2977
3039
  return [
@@ -3118,12 +3180,62 @@ function parseOpenCodeModelsOutput(output) {
3118
3180
  seen.add(line);
3119
3181
  models.push({
3120
3182
  id: line,
3121
- label: line,
3183
+ label: formatOpenCodeModelLabel(line),
3122
3184
  verified: "launchable"
3123
3185
  });
3124
3186
  }
3125
3187
  return models.length > 0 ? { models } : null;
3126
3188
  }
3189
+ function formatOpenCodeModelLabel(modelId) {
3190
+ const separatorIndex = modelId.indexOf("/");
3191
+ if (separatorIndex <= 0 || separatorIndex === modelId.length - 1) return modelId;
3192
+ const providerId = modelId.slice(0, separatorIndex);
3193
+ const modelName = modelId.slice(separatorIndex + 1);
3194
+ const providerLabel = OPENCODE_PROVIDER_LABELS[providerId] || humanizeOpenCodeSegment(providerId);
3195
+ const modelParts = modelName.split("/");
3196
+ const modelLabel = humanizeOpenCodeSegment(modelParts[modelParts.length - 1] || modelName);
3197
+ if (modelParts.length === 1) return `${modelLabel} \xB7 ${providerLabel}`;
3198
+ const upstreamLabel = modelParts.slice(0, -1).map(humanizeOpenCodeSegment).join(" / ");
3199
+ return `${modelLabel} \xB7 ${upstreamLabel} via ${providerLabel}`;
3200
+ }
3201
+ function humanizeOpenCodeSegment(value) {
3202
+ return value.replace(/\[(\d+)m\]/gi, "-$1m").split(/[-_]/).filter(Boolean).map(formatOpenCodeLabelToken).join(" ");
3203
+ }
3204
+ function formatOpenCodeLabelToken(token) {
3205
+ const normalized = token.toLowerCase();
3206
+ const specialCases = {
3207
+ ai: "AI",
3208
+ api: "API",
3209
+ b: "B",
3210
+ chatgpt: "ChatGPT",
3211
+ claude: "Claude",
3212
+ codestral: "Codestral",
3213
+ deepseek: "DeepSeek",
3214
+ flash: "Flash",
3215
+ free: "Free",
3216
+ gemini: "Gemini",
3217
+ glm: "GLM",
3218
+ gpt: "GPT",
3219
+ hy3: "HY3",
3220
+ kimi: "Kimi",
3221
+ m: "M",
3222
+ minimax: "MiniMax",
3223
+ nano: "Nano",
3224
+ nemotron: "Nemotron",
3225
+ omni: "Omni",
3226
+ opus: "Opus",
3227
+ pro: "Pro",
3228
+ sonnet: "Sonnet",
3229
+ super: "Super"
3230
+ };
3231
+ if (specialCases[normalized]) return specialCases[normalized];
3232
+ if (/^v\d+(\.\d+)?$/.test(normalized)) return normalized.toUpperCase();
3233
+ if (/^\d+m$/i.test(token)) return token.toUpperCase();
3234
+ if (/^\d+[bk]$/i.test(token)) return token.toUpperCase();
3235
+ if (/^m\d+(\.\d+)?$/i.test(token)) return token.toUpperCase();
3236
+ if (/^\d/.test(token)) return token;
3237
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
3238
+ }
3127
3239
  function detectOpenCodeModels(home = os4.homedir(), runCommand = runOpenCodeModelsCommand) {
3128
3240
  const commandResult = runCommand(home);
3129
3241
  if (commandResult.error || commandResult.status !== 0) return null;
@@ -4183,6 +4295,66 @@ function isMissingResumeSession(ap) {
4183
4295
  }
4184
4296
  return false;
4185
4297
  }
4298
+ function classifyActivityDetailForTrace(detail) {
4299
+ if (!detail) return void 0;
4300
+ if (detail === "Message received") return "message_received";
4301
+ if (detail === "Starting\u2026") return "starting";
4302
+ if (detail === "Running command\u2026") return "running_command";
4303
+ if (detail === "Checking messages\u2026") return "checking_messages";
4304
+ if (detail === "Compacting context") return "compacting_context";
4305
+ if (detail === "Context compaction finished") return "compaction_finished";
4306
+ if (detail === "Context compaction still running; no finish event observed") return "compaction_stale";
4307
+ if (detail === "Idle" || detail === "Process idle") return "idle";
4308
+ if (detail.startsWith("Restarting stalled ") && detail.endsWith(" runtime for queued message")) return "stalled_recovery";
4309
+ if (detail.startsWith("Runtime stalled: no runtime events for ")) return "runtime_stalled";
4310
+ return "other";
4311
+ }
4312
+ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
4313
+ const context = [];
4314
+ if (ap.lastActivityDetail) {
4315
+ context.push(`after ${ap.lastActivityDetail}`);
4316
+ }
4317
+ if (ap.driver.busyDeliveryMode === "gated") {
4318
+ context.push(`phase=${ap.gatedSteering.phase}`);
4319
+ }
4320
+ if (ap.gatedSteering.outstandingToolUses > 0) {
4321
+ context.push(`tools=${ap.gatedSteering.outstandingToolUses}`);
4322
+ }
4323
+ if (ap.gatedSteering.compacting) {
4324
+ context.push("compacting");
4325
+ }
4326
+ if (ap.inbox.length > 0) {
4327
+ context.push(`queued=${ap.inbox.length}`);
4328
+ }
4329
+ const detail = [
4330
+ `Runtime stalled: no runtime events for ${staleForMinutes}m`,
4331
+ context.length > 0 ? ` (${context.join(", ")})` : ""
4332
+ ].join("");
4333
+ return {
4334
+ detail,
4335
+ traceAttrs: {
4336
+ ageMs: staleForMs,
4337
+ staleForMinutes,
4338
+ lastActivity: ap.lastActivity,
4339
+ lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
4340
+ lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
4341
+ runtime: ap.config.runtime,
4342
+ model: ap.config.model,
4343
+ launchId: ap.launchId || void 0,
4344
+ sessionIdPresent: Boolean(ap.sessionId),
4345
+ inboxCount: ap.inbox.length,
4346
+ pendingNotificationCount: ap.pendingNotificationCount,
4347
+ processPidPresent: typeof ap.process.pid === "number",
4348
+ busyDeliveryMode: ap.driver.busyDeliveryMode,
4349
+ supportsStdinNotification: ap.driver.supportsStdinNotification,
4350
+ gatedPhase: ap.driver.busyDeliveryMode === "gated" ? ap.gatedSteering.phase : void 0,
4351
+ outstandingToolUses: ap.gatedSteering.outstandingToolUses,
4352
+ compacting: ap.gatedSteering.compacting,
4353
+ recentStderrCount: ap.recentStderr.length,
4354
+ recentStdoutCount: ap.recentStdout.length
4355
+ }
4356
+ };
4357
+ }
4186
4358
  function getMessageDeliveryText(driver) {
4187
4359
  return driver.supportsStdinNotification ? "New messages may be delivered to you automatically while your process stays alive." : "The daemon will automatically restart you when new messages arrive.";
4188
4360
  }
@@ -5855,17 +6027,16 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5855
6027
  if (staleForMs < RUNTIME_PROGRESS_STALE_MS) return false;
5856
6028
  ap.runtimeProgressStaleSince = Date.now();
5857
6029
  const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
5858
- this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.stalled", {
5859
- ageMs: staleForMs,
5860
- staleForMinutes,
5861
- lastActivity: ap.lastActivity
5862
- });
6030
+ const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
6031
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.stalled", diagnostic.traceAttrs);
5863
6032
  this.endRuntimeTrace(ap, "error", {
5864
6033
  outcome: "runtime-stalled",
5865
6034
  ageMs: staleForMs,
5866
- lastActivity: ap.lastActivity
6035
+ lastActivity: ap.lastActivity,
6036
+ lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
6037
+ lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail)
5867
6038
  });
5868
- this.broadcastActivity(agentId, "error", `Runtime stalled: no runtime events for ${staleForMinutes}m`);
6039
+ this.broadcastActivity(agentId, "error", diagnostic.detail);
5869
6040
  return true;
5870
6041
  }
5871
6042
  recoverStaleProcessForQueuedMessageIfNeeded(agentId, ap) {
@@ -5881,10 +6052,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5881
6052
  if (staleForMs < RUNTIME_PROGRESS_STALE_MS && !ap.runtimeProgressStaleSince) return false;
5882
6053
  const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
5883
6054
  ap.runtimeProgressStaleSince ??= Date.now();
6055
+ const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
5884
6056
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.stalled", {
5885
- ageMs: staleForMs,
5886
- staleForMinutes,
5887
- lastActivity: ap.lastActivity,
6057
+ ...diagnostic.traceAttrs,
5888
6058
  pendingMessages: ap.inbox.length,
5889
6059
  recovery: "terminate_for_queued_message"
5890
6060
  });
@@ -5892,6 +6062,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
5892
6062
  outcome: "runtime-stalled",
5893
6063
  ageMs: staleForMs,
5894
6064
  lastActivity: ap.lastActivity,
6065
+ lastActivityDetailPresent: Boolean(ap.lastActivityDetail),
6066
+ lastActivityDetailKind: classifyActivityDetailForTrace(ap.lastActivityDetail),
5895
6067
  pendingMessages: ap.inbox.length,
5896
6068
  recovery: "terminate_for_queued_message"
5897
6069
  });
package/dist/cli/index.js CHANGED
@@ -1119,7 +1119,7 @@ function formatTaskStatusUpdated(taskNumber, status) {
1119
1119
  }
1120
1120
 
1121
1121
  // src/commands/task/list.ts
1122
- var VALID_STATUSES = /* @__PURE__ */ new Set(["all", "todo", "in_progress", "in_review", "done"]);
1122
+ var VALID_STATUSES = /* @__PURE__ */ new Set(["all", "todo", "in_progress", "in_review", "done", "closed"]);
1123
1123
  function registerTaskListCommand(parent) {
1124
1124
  parent.command("list").description("List tasks in a channel").requiredOption("--channel <target>", "Channel target: '#channel'").option("--status <s>", "Filter: all|todo|in_progress|in_review|done (default: server-side)").action(async (opts) => {
1125
1125
  let ctx;
@@ -1253,7 +1253,7 @@ function registerTaskUnclaimCommand(parent) {
1253
1253
  }
1254
1254
 
1255
1255
  // src/commands/task/update.ts
1256
- var STATUSES = ["todo", "in_progress", "in_review", "done"];
1256
+ var STATUSES = ["todo", "in_progress", "in_review", "done", "closed"];
1257
1257
  function registerTaskUpdateCommand(parent) {
1258
1258
  parent.command("update").description("Update task status").requiredOption("--channel <target>", "Channel target: '#channel'").requiredOption("--number <n>", "Task number to update").requiredOption(
1259
1259
  "--status <status>",
package/dist/core.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveSlockCliPath,
10
10
  resolveWorkspaceDirectoryPath,
11
11
  scanWorkspaceDirectories
12
- } from "./chunk-Q4XUZB34.js";
12
+ } from "./chunk-XW57NR6Y.js";
13
13
  import {
14
14
  subscribeDaemonLogs
15
15
  } from "./chunk-Z3PCMYZO.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-Q4XUZB34.js";
6
+ } from "./chunk-XW57NR6Y.js";
7
7
  import "./chunk-Z3PCMYZO.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.46.0",
3
+ "version": "0.46.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"