@wrongstack/tools 0.282.2 → 0.283.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.
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { batchToolUseTool } from './batch-tool-use.js';
5
5
  export { OPTIONAL_TOOLS, TIER1_TOOLS, TIER2_TOOLS, TIER3_TOOLS, builtinTools } from './builtin.js';
6
6
  export { CircuitBreaker, CircuitBreakerConfig, CircuitBreakerSnapshot } from './circuit-breaker.js';
7
7
  export { C as CircuitOpenError, e as CircuitSnapshot, f as CircuitState, h as IndexCircuitBreaker, i as IndexTimeoutError, k as cancelPendingReindexes, l as codebaseIndexStats, m as codebaseIndexTool, n as codebaseSearchTool, o as codebaseStatsTool, p as enqueueReindex, q as getIndexState, r as indexCircuitBreaker, s as isIndexReady, t as isIndexableFile, u as isIndexing, v as onIndexStateChange, w as resetIndexCircuitBreaker, x as runStartupIndex, y as searchCodebaseIndex, z as shutdownCodebaseIndexHost } from './background-indexer-BeDBxfSh.js';
8
- import { Tool, AssignKanbanTaskInput, KanbanTaskPriority, KanbanTaskStatus, KanbanAgentRunStatus, KanbanBoard, KanbanBoardSummary, KanbanTask, KanbanSearchResult, KanbanOrchestrationSnapshot, SerializedTaskGraph, SkillLoader } from '@wrongstack/core';
8
+ import { Tool, AssignKanbanTaskInput, KanbanTaskPriority, KanbanTaskStatus, KanbanAgentRunStatus, KanbanBoard, KanbanBoardSummary, KanbanTask, KanbanSearchResult, KanbanEvent, KanbanQueueHealth, KanbanOrchestrationSnapshot, SerializedTaskGraph, SkillLoader } from '@wrongstack/core';
9
9
  export { diffTool } from './diff.js';
10
10
  export { documentTool } from './document.js';
11
11
  export { editTool } from './edit.js';
@@ -184,7 +184,7 @@ interface DesignOutput {
184
184
  */
185
185
  declare const designTool: Tool<DesignInput, DesignOutput>;
186
186
 
187
- type KanbanAction = 'list_boards' | 'get_board' | 'create_board' | 'duplicate_board' | 'update_board' | 'delete_board' | 'generate_board' | 'export_markdown' | 'export_task_graph' | 'sync_task_graph' | 'search_tasks' | 'ready_tasks' | 'snapshot' | 'add_column' | 'update_column' | 'delete_column' | 'add_task' | 'split_task' | 'merge_tasks' | 'copy_task' | 'transfer_task' | 'get_task' | 'update_task' | 'move_task' | 'delete_task' | 'set_chain' | 'get_chain' | 'claim_task' | 'release_task' | 'assign_task' | 'mark_assignment' | 'add_dependency' | 'add_goal_metric' | 'update_goal_metric' | 'add_check' | 'update_check' | 'add_note' | 'add_link';
187
+ type KanbanAction = 'list_boards' | 'get_board' | 'create_board' | 'duplicate_board' | 'update_board' | 'delete_board' | 'generate_board' | 'export_markdown' | 'export_task_graph' | 'sync_task_graph' | 'search_tasks' | 'ready_tasks' | 'snapshot' | 'add_column' | 'update_column' | 'delete_column' | 'add_task' | 'split_task' | 'merge_tasks' | 'copy_task' | 'transfer_task' | 'get_task' | 'update_task' | 'move_task' | 'delete_task' | 'set_chain' | 'get_chain' | 'claim_task' | 'release_task' | 'assign_task' | 'mark_assignment' | 'heartbeat_assignment' | 'recover_stale' | 'events' | 'queue_health' | 'add_dependency' | 'add_goal_metric' | 'update_goal_metric' | 'add_check' | 'update_check' | 'add_note' | 'add_link';
188
188
  interface KanbanToolInput extends Omit<AssignKanbanTaskInput, 'status'> {
189
189
  action: KanbanAction;
190
190
  boardId?: string | undefined;
@@ -237,6 +237,15 @@ interface KanbanToolInput extends Omit<AssignKanbanTaskInput, 'status'> {
237
237
  includeCompletedTasks?: boolean | undefined;
238
238
  preserveAssignment?: boolean | undefined;
239
239
  preserveDependencies?: boolean | undefined;
240
+ leaseId?: string | undefined;
241
+ claimedAt?: string | undefined;
242
+ heartbeatAt?: string | undefined;
243
+ leaseExpiresAt?: string | undefined;
244
+ attempt?: number | undefined;
245
+ maxAttempts?: number | undefined;
246
+ costCeilingUsd?: number | undefined;
247
+ retryPolicy?: 'off' | 'incremental' | 'exponential' | undefined;
248
+ lastFailureKind?: string | undefined;
240
249
  subagentId?: string | undefined;
241
250
  runTaskId?: string | undefined;
242
251
  lastResult?: string | undefined;
@@ -245,6 +254,12 @@ interface KanbanToolInput extends Omit<AssignKanbanTaskInput, 'status'> {
245
254
  releaseStatus?: 'pending' | 'ready' | 'blocked' | undefined;
246
255
  releaseReason?: string | undefined;
247
256
  clearAssignee?: boolean | undefined;
257
+ recoveryMode?: 'auto' | 'release' | 'retry' | 'fail' | undefined;
258
+ recoveryNow?: string | undefined;
259
+ recoveryPolicyFailOnCostCeiling?: boolean | undefined;
260
+ recoveryPolicyReleaseOnFailureKinds?: string[] | undefined;
261
+ recoveryPolicyReleaseOnHeartbeatDue?: boolean | undefined;
262
+ recoveryPolicyRetryPolicyOverride?: 'off' | 'incremental' | 'exponential' | undefined;
248
263
  taskGraph?: unknown;
249
264
  graphId?: string | undefined;
250
265
  specId?: string | undefined;
@@ -262,6 +277,9 @@ interface KanbanToolOutput {
262
277
  boards?: KanbanBoardSummary[] | undefined;
263
278
  task?: KanbanTask | undefined;
264
279
  tasks?: KanbanSearchResult[] | undefined;
280
+ recoveredTasks?: KanbanTask[] | undefined;
281
+ events?: KanbanEvent[] | undefined;
282
+ queueHealth?: KanbanQueueHealth | undefined;
265
283
  children?: KanbanTask[] | undefined;
266
284
  chain?: KanbanTask[] | undefined;
267
285
  snapshot?: KanbanOrchestrationSnapshot | undefined;
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import * as path3 from 'node:path';
4
4
  import { resolve, sep, dirname, join } from 'node:path';
5
5
  import { spawn, execFileSync } from 'node:child_process';
6
6
  import * as Core from '@wrongstack/core';
7
- import { SKILL_LIMITS, buildChildEnv, getDesignKitLoader, isDesignStack, loadActiveKit, applyTokenOverrides, setActiveKit, recordKitChoice, recordOverrides, setDesignOverrides, materializeTokens, runDesignVerify, ToolValidationError, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, ToolError, FetchError, isPrivateIPv4, isPrivateIPv6, assessCommitSafety, compileGlob, expectDefined, recordPackageAction, detectPackageEcosystem, deepMerge, addLinkToTask, addNoteToTask, updateCheckOnTask, addCheckToTask, updateGoalMetricOnTask, addGoalMetricToTask, addDependency, updateTaskAssignment, assignTask, releaseTaskClaim, claimReadyTask, getTaskChain, setTaskChain, removeTask, moveTask, updateTask, getTask, transferTaskToBoard, copyTaskToBoard, mergeTasks, splitTask, addTask, removeColumn, updateColumn, addColumn, getKanbanOrchestrationSnapshot, listReadyTasks, searchKanban, deserializeTaskGraph, syncBoardFromTaskGraph, exportBoardToTaskGraph, serializeTaskGraph, exportBoardAsMarkdown, generateBoardFromDescription, createBoard, parseLinesIntoTasks, getBoard, removeBoard, duplicateBoard, updateBoard, listBoards, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, mutateTasks, formatTaskList, formatPlan, FsError, toErrorMessage as toErrorMessage$2, computeTaskItemProgress, loadPlan, savePlan, loadTasks, saveTasks, stripFrontmatter, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
7
+ import { SKILL_LIMITS, buildChildEnv, getDesignKitLoader, isDesignStack, loadActiveKit, applyTokenOverrides, setActiveKit, recordKitChoice, recordOverrides, setDesignOverrides, materializeTokens, runDesignVerify, ToolValidationError, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, ToolError, FetchError, isPrivateIPv4, isPrivateIPv6, assessCommitSafety, compileGlob, expectDefined, recordPackageAction, detectPackageEcosystem, deepMerge, addLinkToTask, addNoteToTask, updateCheckOnTask, addCheckToTask, updateGoalMetricOnTask, addGoalMetricToTask, addDependency, getKanbanQueueHealth, listKanbanEvents, recoverStaleTaskAssignments, heartbeatTaskAssignment, updateTaskAssignment, assignTask, releaseTaskClaim, claimReadyTask, getTaskChain, setTaskChain, removeTask, moveTask, updateTask, getTask, transferTaskToBoard, copyTaskToBoard, mergeTasks, splitTask, addTask, removeColumn, updateColumn, addColumn, getKanbanOrchestrationSnapshot, listReadyTasks, searchKanban, deserializeTaskGraph, syncBoardFromTaskGraph, exportBoardToTaskGraph, serializeTaskGraph, exportBoardAsMarkdown, generateBoardFromDescription, createBoard, parseLinesIntoTasks, getBoard, removeBoard, duplicateBoard, updateBoard, listBoards, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, mutateTasks, formatTaskList, formatPlan, FsError, toErrorMessage as toErrorMessage$2, computeTaskItemProgress, loadPlan, savePlan, loadTasks, saveTasks, stripFrontmatter, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
8
8
  import * as fs2 from 'node:fs/promises';
9
9
  import * as os2 from 'node:os';
10
10
  import { toErrorMessage as toErrorMessage$3 } from '@wrongstack/core/utils';
@@ -2999,6 +2999,24 @@ var IndexStore = class {
2999
2999
  * When false, ranked search falls back to the LIKE + in-process BM25 path.
3000
3000
  */
3001
3001
  ftsAvailable = false;
3002
+ /**
3003
+ * Cache of prepared statements keyed by their SQL text. `DatabaseSync`
3004
+ * compiles SQL on every `.prepare()` call; for the fixed-SQL methods
3005
+ * (upsertFile, getFileMeta, deleteFile, insertRefs, …) that runs thousands
3006
+ * of times during a full reindex. `StatementSync` objects are reusable
3007
+ * across calls on the same connection, so we compile each distinct SQL once
3008
+ * and reuse it. Cleared in {@link close} when the connection is torn down.
3009
+ */
3010
+ stmtCache = /* @__PURE__ */ new Map();
3011
+ /** Prepare-once helper: compile `sql` on first use, reuse thereafter. */
3012
+ stmt(sql) {
3013
+ let s = this.stmtCache.get(sql);
3014
+ if (s === void 0) {
3015
+ s = this.db.prepare(sql);
3016
+ this.stmtCache.set(sql, s);
3017
+ }
3018
+ return s;
3019
+ }
3002
3020
  /**
3003
3021
  * Execute a SQLite write operation with automatic retry on lock conflicts.
3004
3022
  *
@@ -3168,9 +3186,9 @@ var IndexStore = class {
3168
3186
  deleteSymbolsForFile(file) {
3169
3187
  this.runWithRetry(() => {
3170
3188
  if (this.ftsAvailable) {
3171
- this.db.prepare("DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)").run(file);
3189
+ this.stmt("DELETE FROM symbols_fts WHERE rowid IN (SELECT id FROM symbols WHERE file_fk = ?)").run(file);
3172
3190
  }
3173
- this.db.prepare("DELETE FROM symbols WHERE file_fk = ?").run(file);
3191
+ this.stmt("DELETE FROM symbols WHERE file_fk = ?").run(file);
3174
3192
  });
3175
3193
  }
3176
3194
  /**
@@ -3182,13 +3200,13 @@ var IndexStore = class {
3182
3200
  this.runWithRetry(() => {
3183
3201
  this.deleteRefsForFile(file);
3184
3202
  this.deleteSymbolsForFile(file);
3185
- this.db.prepare("DELETE FROM files WHERE file = ?").run(file);
3203
+ this.stmt("DELETE FROM files WHERE file = ?").run(file);
3186
3204
  });
3187
3205
  }
3188
3206
  // ─── File metadata ──────────────────────────────────────────────────────────
3189
3207
  upsertFile(meta) {
3190
3208
  this.runWithRetry(() => {
3191
- this.db.prepare(
3209
+ this.stmt(
3192
3210
  `INSERT INTO files(file, lang, mtime_ms, symbol_count, last_indexed)
3193
3211
  VALUES (?, ?, ?, ?, ?)
3194
3212
  ON CONFLICT(file) DO UPDATE SET
@@ -3200,7 +3218,7 @@ var IndexStore = class {
3200
3218
  });
3201
3219
  }
3202
3220
  getFileMeta(file) {
3203
- const rows = this.db.prepare(
3221
+ const rows = this.stmt(
3204
3222
  "SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?"
3205
3223
  ).all(file);
3206
3224
  if (!rows.length) return null;
@@ -3423,7 +3441,7 @@ var IndexStore = class {
3423
3441
  */
3424
3442
  insertRefs(fromId, refs) {
3425
3443
  this.runWithRetry(() => {
3426
- this.db.prepare("DELETE FROM refs WHERE from_id = ?").run(fromId);
3444
+ this.stmt("DELETE FROM refs WHERE from_id = ?").run(fromId);
3427
3445
  if (refs.length === 0) return;
3428
3446
  const stmt = this.db.prepare(
3429
3447
  `INSERT INTO refs(from_id, to_name, to_id, call_type, line)
@@ -3624,6 +3642,7 @@ var IndexStore = class {
3624
3642
  }
3625
3643
  }
3626
3644
  close() {
3645
+ this.stmtCache.clear();
3627
3646
  try {
3628
3647
  this.db.close();
3629
3648
  } catch {
@@ -9454,6 +9473,10 @@ var kanbanTool = {
9454
9473
  "release_task",
9455
9474
  "assign_task",
9456
9475
  "mark_assignment",
9476
+ "heartbeat_assignment",
9477
+ "recover_stale",
9478
+ "events",
9479
+ "queue_health",
9457
9480
  "add_dependency",
9458
9481
  "add_goal_metric",
9459
9482
  "update_goal_metric",
@@ -9500,6 +9523,12 @@ var kanbanTool = {
9500
9523
  fallbackModels: { type: "array", items: { type: "string" } },
9501
9524
  tools: { type: "array", items: { type: "string" } },
9502
9525
  allowedCapabilities: { type: "array", items: { type: "string" } },
9526
+ leaseId: { type: "string" },
9527
+ claimedAt: { type: "string" },
9528
+ heartbeatAt: { type: "string" },
9529
+ leaseExpiresAt: { type: "string" },
9530
+ attempt: { type: "number" },
9531
+ maxAttempts: { type: "number" },
9503
9532
  subagentId: { type: "string" },
9504
9533
  runTaskId: { type: "string" },
9505
9534
  lastResult: { type: "string" },
@@ -9511,6 +9540,8 @@ var kanbanTool = {
9511
9540
  releaseStatus: { type: "string", enum: ["pending", "ready", "blocked"] },
9512
9541
  releaseReason: { type: "string" },
9513
9542
  clearAssignee: { type: "boolean" },
9543
+ recoveryMode: { type: "string", enum: ["release", "retry", "fail"] },
9544
+ recoveryNow: { type: "string" },
9514
9545
  taskGraph: { type: "object" },
9515
9546
  graphId: { type: "string" },
9516
9547
  specId: { type: "string" },
@@ -9926,10 +9957,80 @@ var kanbanTool = {
9926
9957
  ...input.runTaskId !== void 0 ? { runTaskId: input.runTaskId } : {},
9927
9958
  ...input.lastResult !== void 0 ? { lastResult: input.lastResult } : {},
9928
9959
  ...input.error !== void 0 ? { error: input.error } : {},
9929
- ...input.agentId !== void 0 ? { agentId: input.agentId } : {}
9960
+ ...input.agentId !== void 0 ? { agentId: input.agentId } : {},
9961
+ ...input.leaseId !== void 0 ? { leaseId: input.leaseId } : {},
9962
+ ...input.claimedAt !== void 0 ? { claimedAt: input.claimedAt } : {},
9963
+ ...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
9964
+ ...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {},
9965
+ ...input.attempt !== void 0 ? { attempt: input.attempt } : {},
9966
+ ...input.maxAttempts !== void 0 ? { maxAttempts: input.maxAttempts } : {}
9930
9967
  });
9931
9968
  return board ? okBoard(board, "Assignment updated.") : fail("Task not found.");
9932
9969
  }
9970
+ case "heartbeat_assignment": {
9971
+ if (!input.boardId || !input.taskId) {
9972
+ return fail("heartbeat_assignment requires boardId and taskId.");
9973
+ }
9974
+ const board = await heartbeatTaskAssignment(projectRoot, input.boardId, input.taskId, {
9975
+ ...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
9976
+ ...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {}
9977
+ });
9978
+ return board ? okBoard(board, "Assignment heartbeat updated.") : fail("Task assignment not found.");
9979
+ }
9980
+ case "recover_stale": {
9981
+ if (!input.boardId) return fail("recover_stale requires boardId.");
9982
+ const policyFields = [
9983
+ input.recoveryPolicyFailOnCostCeiling !== void 0,
9984
+ input.recoveryPolicyReleaseOnFailureKinds !== void 0,
9985
+ input.recoveryPolicyReleaseOnHeartbeatDue !== void 0,
9986
+ input.recoveryPolicyRetryPolicyOverride !== void 0
9987
+ ].some(Boolean);
9988
+ const result = await recoverStaleTaskAssignments(projectRoot, input.boardId, {
9989
+ ...input.recoveryMode !== void 0 ? { mode: input.recoveryMode } : {},
9990
+ ...input.recoveryNow !== void 0 ? { now: input.recoveryNow } : {},
9991
+ ...input.releaseReason !== void 0 ? { reason: input.releaseReason } : {},
9992
+ ...input.clearAssignee !== void 0 ? { clearAssignee: input.clearAssignee } : {},
9993
+ ...policyFields ? {
9994
+ policy: {
9995
+ ...input.recoveryPolicyFailOnCostCeiling !== void 0 ? { failWhenCostCeilingSet: input.recoveryPolicyFailOnCostCeiling } : {},
9996
+ ...input.recoveryPolicyReleaseOnFailureKinds !== void 0 ? {
9997
+ releaseOnFailureKinds: input.recoveryPolicyReleaseOnFailureKinds
9998
+ } : {},
9999
+ ...input.recoveryPolicyReleaseOnHeartbeatDue !== void 0 ? {
10000
+ releaseOnHeartbeatDue: input.recoveryPolicyReleaseOnHeartbeatDue
10001
+ } : {},
10002
+ ...input.recoveryPolicyRetryPolicyOverride !== void 0 ? {
10003
+ retryPolicyOverride: input.recoveryPolicyRetryPolicyOverride
10004
+ } : {}
10005
+ }
10006
+ } : {}
10007
+ });
10008
+ return result ? {
10009
+ ok: true,
10010
+ message: `Recovered ${result.tasks.length} stale assignment(s).`,
10011
+ board: result.board,
10012
+ recoveredTasks: result.tasks
10013
+ } : { ok: true, message: "No stale assignment matched.", recoveredTasks: [] };
10014
+ }
10015
+ case "events": {
10016
+ if (!input.boardId) return fail("events requires boardId.");
10017
+ const eventList = await listKanbanEvents(projectRoot, input.boardId);
10018
+ return {
10019
+ ok: true,
10020
+ message: `${eventList.length} event(s).`,
10021
+ events: eventList
10022
+ };
10023
+ }
10024
+ case "queue_health": {
10025
+ const health = await getKanbanQueueHealth(projectRoot, {
10026
+ ...input.boardId !== void 0 ? { boardId: input.boardId } : {}
10027
+ });
10028
+ return {
10029
+ ok: true,
10030
+ message: `Counts: ready=${health.counts.ready}, running=${health.counts.running}, stale=${health.staleAssignments.count}.`,
10031
+ queueHealth: health
10032
+ };
10033
+ }
9933
10034
  case "add_dependency": {
9934
10035
  if (!input.boardId || !input.taskId || !input.dependencyTaskId) {
9935
10036
  return fail("add_dependency requires boardId, taskId, and dependencyTaskId.");
@@ -10081,11 +10182,20 @@ function assignmentInput(input) {
10081
10182
  fallbackModels: input.fallbackModels,
10082
10183
  tools: input.tools,
10083
10184
  allowedCapabilities: input.allowedCapabilities,
10084
- assignee: input.assignee
10185
+ assignee: input.assignee,
10186
+ leaseId: input.leaseId,
10187
+ claimedAt: input.claimedAt,
10188
+ heartbeatAt: input.heartbeatAt,
10189
+ leaseExpiresAt: input.leaseExpiresAt,
10190
+ attempt: input.attempt,
10191
+ maxAttempts: input.maxAttempts,
10192
+ costCeilingUsd: input.costCeilingUsd,
10193
+ retryPolicy: input.retryPolicy,
10194
+ lastFailureKind: input.lastFailureKind
10085
10195
  };
10086
10196
  }
10087
10197
  function hasAssignmentInput(input) {
10088
- return input.agentId !== void 0 || input.name !== void 0 || input.role !== void 0 || input.provider !== void 0 || input.model !== void 0 || input.fallbackProfile !== void 0 || input.fallbackModels !== void 0 || input.tools !== void 0 || input.allowedCapabilities !== void 0 || input.assignee !== void 0 || input.assignmentStatus !== void 0;
10198
+ return input.agentId !== void 0 || input.name !== void 0 || input.role !== void 0 || input.provider !== void 0 || input.model !== void 0 || input.fallbackProfile !== void 0 || input.fallbackModels !== void 0 || input.tools !== void 0 || input.allowedCapabilities !== void 0 || input.assignee !== void 0 || input.leaseId !== void 0 || input.claimedAt !== void 0 || input.heartbeatAt !== void 0 || input.leaseExpiresAt !== void 0 || input.attempt !== void 0 || input.maxAttempts !== void 0 || input.costCeilingUsd !== void 0 || input.retryPolicy !== void 0 || input.lastFailureKind !== void 0 || input.assignmentStatus !== void 0;
10089
10199
  }
10090
10200
  function assignmentForTaskCreate(input) {
10091
10201
  return {
@@ -10098,7 +10208,16 @@ function assignmentForTaskCreate(input) {
10098
10208
  ...input.fallbackProfile !== void 0 ? { fallbackProfile: input.fallbackProfile } : {},
10099
10209
  ...input.fallbackModels !== void 0 ? { fallbackModels: input.fallbackModels } : {},
10100
10210
  ...input.tools !== void 0 ? { tools: input.tools } : {},
10101
- ...input.allowedCapabilities !== void 0 ? { allowedCapabilities: input.allowedCapabilities } : {}
10211
+ ...input.allowedCapabilities !== void 0 ? { allowedCapabilities: input.allowedCapabilities } : {},
10212
+ ...input.leaseId !== void 0 ? { leaseId: input.leaseId } : {},
10213
+ ...input.claimedAt !== void 0 ? { claimedAt: input.claimedAt } : {},
10214
+ ...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
10215
+ ...input.leaseExpiresAt !== void 0 ? { leaseExpiresAt: input.leaseExpiresAt } : {},
10216
+ ...input.attempt !== void 0 ? { attempt: input.attempt } : {},
10217
+ ...input.maxAttempts !== void 0 ? { maxAttempts: input.maxAttempts } : {},
10218
+ ...input.costCeilingUsd !== void 0 ? { costCeilingUsd: input.costCeilingUsd } : {},
10219
+ ...input.retryPolicy !== void 0 ? { retryPolicy: input.retryPolicy } : {},
10220
+ ...input.lastFailureKind !== void 0 ? { lastFailureKind: input.lastFailureKind } : {}
10102
10221
  };
10103
10222
  }
10104
10223
 
@@ -13490,6 +13609,12 @@ function relatedMemoryTool(memory) {
13490
13609
  }
13491
13610
 
13492
13611
  // src/mode.ts
13612
+ function modeFamily(mode) {
13613
+ if (mode.tags?.includes("lite")) return "lite";
13614
+ if (mode.tags?.includes("deep")) return "deep";
13615
+ if (mode.tags?.includes("balanced") || mode.id === "default") return "balanced";
13616
+ return "custom";
13617
+ }
13493
13618
  function createModeTool(modeStore) {
13494
13619
  return {
13495
13620
  name: "mode",
@@ -13529,10 +13654,16 @@ function createModeTool(modeStore) {
13529
13654
  }
13530
13655
  case "list": {
13531
13656
  const modes = await modeStore.listModes();
13532
- const lines = modes.map((m) => ` ${m.id.padEnd(20)} ${m.name} \u2014 ${m.description}`).join("\n");
13657
+ const lines = modes.map((m) => ` ${m.id.padEnd(20)} [${modeFamily(m)}] ${m.name} \u2014 ${m.description}`).join("\n");
13533
13658
  return {
13534
13659
  action: "list",
13535
- modes: modes.map((m) => ({ id: m.id, name: m.name, description: m.description })),
13660
+ modes: modes.map((m) => ({
13661
+ id: m.id,
13662
+ name: m.name,
13663
+ description: m.description,
13664
+ family: modeFamily(m),
13665
+ tags: m.tags ?? []
13666
+ })),
13536
13667
  success: true,
13537
13668
  message: lines
13538
13669
  };