sparkecoder 0.1.117 → 0.1.118

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.
Files changed (116) hide show
  1. package/dist/agent/index.d.ts +2 -2
  2. package/dist/agent/index.js +116 -697
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +566 -1033
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-Bi8Ek02A.d.ts → index-Bcz0aCAR.d.ts} +1 -10
  8. package/dist/index.d.ts +4 -4
  9. package/dist/index.js +333 -935
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-ecQSnCMz.d.ts → schema-BWbWmfDQ.d.ts} +0 -2
  12. package/dist/server/index.js +333 -935
  13. package/dist/server/index.js.map +1 -1
  14. package/dist/skills/default/desktop-automation.md +290 -0
  15. package/dist/skills/default/recording.md +3 -3
  16. package/dist/tools/index.d.ts +1 -167
  17. package/dist/tools/index.js +5 -590
  18. package/dist/tools/index.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/skills/default/desktop-automation.md +290 -0
  21. package/src/skills/default/recording.md +3 -3
  22. package/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  24. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  25. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  26. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  27. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  42. package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  86. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  88. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  92. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  93. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  94. package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
  95. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
  96. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  97. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  98. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
  99. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  100. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  101. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  102. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  103. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  104. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  105. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  106. package/dist/skills/default/computer-use.md +0 -225
  107. package/src/skills/default/computer-use.md +0 -225
  108. /package/web/.next/standalone/web/.next/static/{static/vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
  109. /package/web/.next/standalone/web/.next/static/{static/vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
  110. /package/web/.next/standalone/web/.next/static/{static/vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
  111. /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
  112. /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
  113. /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
  114. /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
  115. /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
  116. /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
@@ -51,12 +51,6 @@ var init_types = __esm({
51
51
  skillsDirectory: z.string().optional(),
52
52
  maxContextChars: z.number().optional().default(2e5),
53
53
  task: TaskConfigSchema.optional(),
54
- // Anthropic computer use tool — opt-in. When true, the `computer` tool is
55
- // included in the toolset for Anthropic models. Default false.
56
- computerUseEnabled: z.boolean().optional(),
57
- // Display dimensions for the computer use tool (defaults: 1280x800).
58
- computerUseDisplayWidth: z.number().int().positive().optional(),
59
- computerUseDisplayHeight: z.number().int().positive().optional(),
60
54
  // 'orchestrator' = supervisor session; 'worker' = task spawned by an orchestrator.
61
55
  role: z.enum(["orchestrator", "worker", "chat"]).optional(),
62
56
  // Optional persona / extra system-prompt text appended to the orchestrator's
@@ -1642,17 +1636,17 @@ __export(conversation_archive_exports, {
1642
1636
  getHistoryDir: () => getHistoryDir,
1643
1637
  listSessionArchives: () => listSessionArchives
1644
1638
  });
1645
- import { existsSync as existsSync16, mkdirSync as mkdirSync6, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
1646
- import { join as join9 } from "path";
1639
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
1640
+ import { join as join8 } from "path";
1647
1641
  function getHistoryDir() {
1648
- const dir = join9(ensureAppDataDirectory(), "history");
1649
- if (!existsSync16(dir)) mkdirSync6(dir, { recursive: true });
1642
+ const dir = join8(ensureAppDataDirectory(), "history");
1643
+ if (!existsSync15(dir)) mkdirSync5(dir, { recursive: true });
1650
1644
  return dir;
1651
1645
  }
1652
1646
  function appendTurn(turn) {
1653
1647
  try {
1654
1648
  const dir = getHistoryDir();
1655
- const path = join9(dir, `${turn.sessionId}.jsonl`);
1649
+ const path = join8(dir, `${turn.sessionId}.jsonl`);
1656
1650
  const line = JSON.stringify({
1657
1651
  ts: turn.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
1658
1652
  sessionId: turn.sessionId,
@@ -1681,7 +1675,7 @@ function flattenContent(content) {
1681
1675
  }
1682
1676
  function listSessionArchives() {
1683
1677
  const dir = getHistoryDir();
1684
- if (!existsSync16(dir)) return [];
1678
+ if (!existsSync15(dir)) return [];
1685
1679
  return readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
1686
1680
  }
1687
1681
  var init_conversation_archive = __esm({
@@ -1897,15 +1891,15 @@ var recorder_exports = {};
1897
1891
  __export(recorder_exports, {
1898
1892
  FrameRecorder: () => FrameRecorder
1899
1893
  });
1900
- import { exec as exec6 } from "child_process";
1901
- import { promisify as promisify6 } from "util";
1894
+ import { exec as exec5 } from "child_process";
1895
+ import { promisify as promisify5 } from "util";
1902
1896
  import { writeFile as writeFile5, mkdir as mkdir4, readFile as readFile11, unlink as unlink2, readdir as readdir5, rm } from "fs/promises";
1903
- import { join as join11 } from "path";
1904
- import { tmpdir as tmpdir2 } from "os";
1905
- import { nanoid as nanoid8 } from "nanoid";
1897
+ import { join as join10 } from "path";
1898
+ import { tmpdir } from "os";
1899
+ import { nanoid as nanoid7 } from "nanoid";
1906
1900
  async function checkFfmpeg() {
1907
1901
  try {
1908
- await execAsync6("ffmpeg -version", { timeout: 5e3 });
1902
+ await execAsync5("ffmpeg -version", { timeout: 5e3 });
1909
1903
  return true;
1910
1904
  } catch {
1911
1905
  return false;
@@ -1917,11 +1911,11 @@ async function cleanup(dir) {
1917
1911
  } catch {
1918
1912
  }
1919
1913
  }
1920
- var execAsync6, FrameRecorder;
1914
+ var execAsync5, FrameRecorder;
1921
1915
  var init_recorder = __esm({
1922
1916
  "src/browser/recorder.ts"() {
1923
1917
  "use strict";
1924
- execAsync6 = promisify6(exec6);
1918
+ execAsync5 = promisify5(exec5);
1925
1919
  FrameRecorder = class {
1926
1920
  frames = [];
1927
1921
  startTime = null;
@@ -1957,21 +1951,21 @@ var init_recorder = __esm({
1957
1951
  */
1958
1952
  async encode() {
1959
1953
  if (this.frames.length === 0) return null;
1960
- const workDir = join11(tmpdir2(), `sparkecoder-recording-${nanoid8(8)}`);
1954
+ const workDir = join10(tmpdir(), `sparkecoder-recording-${nanoid7(8)}`);
1961
1955
  await mkdir4(workDir, { recursive: true });
1962
1956
  try {
1963
1957
  for (let i = 0; i < this.frames.length; i++) {
1964
- const framePath = join11(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
1958
+ const framePath = join10(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
1965
1959
  await writeFile5(framePath, this.frames[i].data);
1966
1960
  }
1967
1961
  const duration = (this.frames[this.frames.length - 1].timestamp - this.frames[0].timestamp) / 1e3;
1968
1962
  const fps = duration > 0 ? Math.round(this.frames.length / duration) : 10;
1969
1963
  const clampedFps = Math.max(1, Math.min(fps, 30));
1970
- const outputPath = join11(workDir, `recording_${this.sessionId}.mp4`);
1964
+ const outputPath = join10(workDir, `recording_${this.sessionId}.mp4`);
1971
1965
  const hasFfmpeg = await checkFfmpeg();
1972
1966
  if (hasFfmpeg) {
1973
- await execAsync6(
1974
- `ffmpeg -y -framerate ${clampedFps} -i "${join11(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
1967
+ await execAsync5(
1968
+ `ffmpeg -y -framerate ${clampedFps} -i "${join10(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
1975
1969
  { timeout: 12e4 }
1976
1970
  );
1977
1971
  } else {
@@ -1983,7 +1977,7 @@ var init_recorder = __esm({
1983
1977
  const files = await readdir5(workDir);
1984
1978
  for (const f of files) {
1985
1979
  if (f.startsWith("frame_")) {
1986
- await unlink2(join11(workDir, f)).catch(() => {
1980
+ await unlink2(join10(workDir, f)).catch(() => {
1987
1981
  });
1988
1982
  }
1989
1983
  }
@@ -2008,7 +2002,7 @@ var init_recorder = __esm({
2008
2002
  import {
2009
2003
  streamText as streamText2,
2010
2004
  generateText as generateText3,
2011
- tool as tool15,
2005
+ tool as tool14,
2012
2006
  stepCountIs as stepCountIs2
2013
2007
  } from "ai";
2014
2008
 
@@ -2216,8 +2210,8 @@ var SUBAGENT_MODELS = {
2216
2210
  // src/agent/index.ts
2217
2211
  init_db();
2218
2212
  init_config();
2219
- import { z as z16 } from "zod";
2220
- import { nanoid as nanoid9 } from "nanoid";
2213
+ import { z as z15 } from "zod";
2214
+ import { nanoid as nanoid8 } from "nanoid";
2221
2215
 
2222
2216
  // src/tools/bash.ts
2223
2217
  import { tool } from "ai";
@@ -3100,12 +3094,12 @@ function findNearestRoot(startDir, markers) {
3100
3094
  }
3101
3095
  async function commandExists(cmd) {
3102
3096
  try {
3103
- const { exec: exec7 } = await import("child_process");
3104
- const { promisify: promisify7 } = await import("util");
3105
- const execAsync7 = promisify7(exec7);
3097
+ const { exec: exec6 } = await import("child_process");
3098
+ const { promisify: promisify6 } = await import("util");
3099
+ const execAsync6 = promisify6(exec6);
3106
3100
  const isWindows = process.platform === "win32";
3107
3101
  const checkCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
3108
- await execAsync7(checkCmd);
3102
+ await execAsync6(checkCmd);
3109
3103
  return true;
3110
3104
  } catch {
3111
3105
  return false;
@@ -5896,568 +5890,6 @@ function createUploadFileTool(options) {
5896
5890
  });
5897
5891
  }
5898
5892
 
5899
- // src/tools/computer-use.ts
5900
- import { anthropic } from "@ai-sdk/anthropic";
5901
- import { exec as exec5 } from "child_process";
5902
- import { promisify as promisify5 } from "util";
5903
- import { mkdirSync as mkdirSync5, existsSync as existsSync15, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
5904
- import { join as join8 } from "path";
5905
- import { tmpdir } from "os";
5906
- import { nanoid as nanoid4 } from "nanoid";
5907
- var execAsync5 = promisify5(exec5);
5908
- var DEFAULT_WIDTH = 1280;
5909
- var DEFAULT_HEIGHT = 800;
5910
- function isMacOs() {
5911
- return process.platform === "darwin";
5912
- }
5913
- async function isCliclickInstalled() {
5914
- try {
5915
- await execAsync5("command -v cliclick", { timeout: 2e3 });
5916
- return true;
5917
- } catch {
5918
- return false;
5919
- }
5920
- }
5921
- async function runJxa(script) {
5922
- try {
5923
- const escaped = script.replace(/'/g, `'\\''`);
5924
- const { stdout } = await execAsync5(`osascript -l JavaScript -e '${escaped}'`, {
5925
- timeout: 5e3
5926
- });
5927
- return JSON.parse(stdout.trim());
5928
- } catch {
5929
- return null;
5930
- }
5931
- }
5932
- async function hasAccessibilityPermissions() {
5933
- try {
5934
- const { stderr } = await execAsync5("cliclick p:.", { timeout: 3e3 });
5935
- if (/accessibility privileges not enabled/i.test(stderr)) {
5936
- return { ok: false, error: stderr.trim().split("\n")[0] };
5937
- }
5938
- return { ok: true };
5939
- } catch (err) {
5940
- return { ok: false, error: err?.message || String(err) };
5941
- }
5942
- }
5943
- async function hasScreenRecordingPermissions() {
5944
- const result = await runJxa(
5945
- `ObjC.import("Cocoa");
5946
- ObjC.import("CoreGraphics");
5947
- ObjC.bindFunction("CGPreflightScreenCaptureAccess", ["bool", []]);
5948
- JSON.stringify({ hasAccess: !!$.CGPreflightScreenCaptureAccess() });`
5949
- );
5950
- return result?.hasAccess ?? false;
5951
- }
5952
- async function requestAccessibilityPrompt() {
5953
- const result = await runJxa(
5954
- `ObjC.import("ApplicationServices");
5955
- var key = $.kAXTrustedCheckOptionPrompt;
5956
- var dict = $.NSDictionary.dictionaryWithObjectForKey($.kCFBooleanTrue, key);
5957
- var trusted = $.AXIsProcessTrustedWithOptions(dict);
5958
- JSON.stringify({ trusted: !!trusted });`
5959
- );
5960
- return result?.trusted ?? false;
5961
- }
5962
- async function requestScreenRecordingPrompt() {
5963
- const result = await runJxa(
5964
- `ObjC.import("Cocoa");
5965
- ObjC.import("CoreGraphics");
5966
- ObjC.bindFunction("CGRequestScreenCaptureAccess", ["bool", []]);
5967
- JSON.stringify({ granted: !!$.CGRequestScreenCaptureAccess() });`
5968
- );
5969
- return result?.granted ?? false;
5970
- }
5971
- async function openSystemSettings(pane) {
5972
- const url = pane === "accessibility" ? "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" : "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
5973
- try {
5974
- await execAsync5(`open '${url}'`, { timeout: 3e3 });
5975
- } catch {
5976
- }
5977
- }
5978
- async function detectScreenSize() {
5979
- try {
5980
- const { stdout } = await execAsync5(
5981
- `osascript -e 'tell application "Finder" to get bounds of window of desktop'`,
5982
- { timeout: 3e3 }
5983
- );
5984
- const parts = stdout.trim().split(",").map((s) => parseInt(s.trim(), 10));
5985
- if (parts.length >= 4 && parts.every((n) => Number.isFinite(n))) {
5986
- const [x1, y1, x2, y2] = parts;
5987
- return { width: x2 - x1, height: y2 - y1 };
5988
- }
5989
- } catch {
5990
- }
5991
- return null;
5992
- }
5993
- async function runCliclick(args) {
5994
- const quoted = args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(" ");
5995
- const { stdout, stderr } = await execAsync5(`cliclick ${quoted}`, {
5996
- timeout: 15e3,
5997
- maxBuffer: 1024 * 1024
5998
- });
5999
- if (/accessibility privileges not enabled/i.test(stderr)) {
6000
- throw new Error(
6001
- "Accessibility permissions not granted to cliclick. Open System Settings \u2192 Privacy & Security \u2192 Accessibility, add cliclick (or the agent runtime), and toggle it on."
6002
- );
6003
- }
6004
- if (stderr && !stdout) throw new Error(stderr.trim());
6005
- return (stdout || "").trim();
6006
- }
6007
- async function runScreencapture(path) {
6008
- await execAsync5(`screencapture -x -t png '${path.replace(/'/g, `'\\''`)}'`, {
6009
- timeout: 5e3
6010
- });
6011
- }
6012
- async function resizeScreenshotToPoints(path, targetWidth, targetHeight) {
6013
- const sharpModule = await import("sharp");
6014
- const sharp2 = sharpModule.default || sharpModule;
6015
- const meta = await sharp2(path).metadata();
6016
- if (meta.width === targetWidth && meta.height === targetHeight) {
6017
- return readFileSync7(path);
6018
- }
6019
- return await sharp2(path).resize(targetWidth, targetHeight, { fit: "fill" }).png().toBuffer();
6020
- }
6021
- async function runScroll(dx, dy) {
6022
- const wheelY = -Math.round(dy);
6023
- const wheelX = -Math.round(dx);
6024
- const script = `ObjC.import('CoreGraphics');var ev = $.CGEventCreateScrollWheelEvent(null, 0, 2, ${wheelY}, ${wheelX});$.CGEventPost(0, ev);`;
6025
- await execAsync5(
6026
- `osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
6027
- { timeout: 5e3 }
6028
- );
6029
- }
6030
- function translateKeyForCliclick(key2) {
6031
- if (!key2) return [];
6032
- const parts = key2.split("+").map((p) => p.trim()).filter(Boolean);
6033
- if (parts.length === 0) return [];
6034
- const modMap = {
6035
- ctrl: "ctrl",
6036
- control: "ctrl",
6037
- alt: "alt",
6038
- option: "alt",
6039
- shift: "shift",
6040
- cmd: "cmd",
6041
- super: "cmd",
6042
- meta: "cmd",
6043
- win: "cmd",
6044
- fn: "fn"
6045
- };
6046
- const keyMap = {
6047
- return: "enter",
6048
- enter: "enter",
6049
- esc: "esc",
6050
- escape: "esc",
6051
- backspace: "delete",
6052
- back_space: "delete",
6053
- delete: "fwd-delete",
6054
- fwd_delete: "fwd-delete",
6055
- forward_delete: "fwd-delete",
6056
- tab: "tab",
6057
- space: "space",
6058
- up: "arrow-up",
6059
- arrow_up: "arrow-up",
6060
- down: "arrow-down",
6061
- arrow_down: "arrow-down",
6062
- left: "arrow-left",
6063
- arrow_left: "arrow-left",
6064
- right: "arrow-right",
6065
- arrow_right: "arrow-right",
6066
- page_up: "page-up",
6067
- pageup: "page-up",
6068
- page_down: "page-down",
6069
- pagedown: "page-down",
6070
- home: "home",
6071
- end: "end",
6072
- f1: "f1",
6073
- f2: "f2",
6074
- f3: "f3",
6075
- f4: "f4",
6076
- f5: "f5",
6077
- f6: "f6",
6078
- f7: "f7",
6079
- f8: "f8",
6080
- f9: "f9",
6081
- f10: "f10",
6082
- f11: "f11",
6083
- f12: "f12"
6084
- };
6085
- const modifiers = [];
6086
- let mainKey = null;
6087
- for (let i = 0; i < parts.length; i++) {
6088
- const lower = parts[i].toLowerCase().replace(/-/g, "_");
6089
- if (i < parts.length - 1 && modMap[lower]) {
6090
- modifiers.push(modMap[lower]);
6091
- } else {
6092
- mainKey = keyMap[lower] || lower;
6093
- }
6094
- }
6095
- const args = [];
6096
- if (modifiers.length > 0) args.push(`kd:${modifiers.join(",")}`);
6097
- if (mainKey) {
6098
- const isNamedKey = Object.values(keyMap).includes(mainKey) || /^f([1-9]|1[0-9]|20)$/.test(mainKey) || /^num-/.test(mainKey);
6099
- if (isNamedKey) {
6100
- args.push(`kp:${mainKey}`);
6101
- } else {
6102
- args.push(`t:${mainKey}`);
6103
- }
6104
- }
6105
- if (modifiers.length > 0) args.push(`ku:${modifiers.join(",")}`);
6106
- return args;
6107
- }
6108
- function modifierStringToCliclick(text) {
6109
- return text.split("+").map((p) => p.trim().toLowerCase()).map((p) => {
6110
- if (p === "ctrl" || p === "control") return "ctrl";
6111
- if (p === "alt" || p === "option") return "alt";
6112
- if (p === "shift") return "shift";
6113
- if (p === "super" || p === "meta" || p === "cmd") return "cmd";
6114
- return "";
6115
- }).filter(Boolean);
6116
- }
6117
- function createComputerUseTool(options) {
6118
- const displayWidth = options.displayWidth ?? DEFAULT_WIDTH;
6119
- const displayHeight = options.displayHeight ?? DEFAULT_HEIGHT;
6120
- return anthropic.tools.computer_20251124({
6121
- displayWidthPx: displayWidth,
6122
- displayHeightPx: displayHeight,
6123
- enableZoom: true,
6124
- execute: async (input) => {
6125
- try {
6126
- switch (input.action) {
6127
- case "screenshot": {
6128
- const path = join8(tmpdir(), `cu-${nanoid4(8)}.png`);
6129
- await runScreencapture(path);
6130
- const resized = await resizeScreenshotToPoints(path, displayWidth, displayHeight);
6131
- try {
6132
- unlinkSync2(path);
6133
- } catch {
6134
- }
6135
- return { type: "image", data: resized.toString("base64") };
6136
- }
6137
- case "left_click": {
6138
- const [x, y] = input.coordinate ?? [0, 0];
6139
- if (input.text) {
6140
- const mods = modifierStringToCliclick(input.text);
6141
- if (mods.length > 0) {
6142
- await runCliclick([`kd:${mods.join(",")}`, `c:${x},${y}`, `ku:${mods.join(",")}`]);
6143
- } else {
6144
- await runCliclick([`c:${x},${y}`]);
6145
- }
6146
- } else {
6147
- await runCliclick([`c:${x},${y}`]);
6148
- }
6149
- return `clicked at (${x}, ${y})${input.text ? ` with ${input.text}` : ""}`;
6150
- }
6151
- case "right_click": {
6152
- const [x, y] = input.coordinate ?? [0, 0];
6153
- await runCliclick([`rc:${x},${y}`]);
6154
- return `right-clicked at (${x}, ${y})`;
6155
- }
6156
- case "middle_click": {
6157
- const [x, y] = input.coordinate ?? [0, 0];
6158
- const script = `ObjC.import('CoreGraphics');var loc = $.CGPointMake(${x}, ${y});var down = $.CGEventCreateMouseEvent(null, 25, loc, 2);var up = $.CGEventCreateMouseEvent(null, 26, loc, 2);$.CGEventPost(0, down); $.CGEventPost(0, up);`;
6159
- await execAsync5(
6160
- `osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
6161
- { timeout: 3e3 }
6162
- );
6163
- return `middle-clicked at (${x}, ${y})`;
6164
- }
6165
- case "double_click": {
6166
- const [x, y] = input.coordinate ?? [0, 0];
6167
- await runCliclick([`dc:${x},${y}`]);
6168
- return `double-clicked at (${x}, ${y})`;
6169
- }
6170
- case "triple_click": {
6171
- const [x, y] = input.coordinate ?? [0, 0];
6172
- await runCliclick([`tc:${x},${y}`]);
6173
- return `triple-clicked at (${x}, ${y})`;
6174
- }
6175
- case "mouse_move": {
6176
- const [x, y] = input.coordinate ?? [0, 0];
6177
- await runCliclick([`m:${x},${y}`]);
6178
- return `moved cursor to (${x}, ${y})`;
6179
- }
6180
- case "left_mouse_down": {
6181
- const [x, y] = input.coordinate ?? [0, 0];
6182
- await runCliclick([`dd:${x},${y}`]);
6183
- return `left mouse button pressed at (${x}, ${y})`;
6184
- }
6185
- case "left_mouse_up": {
6186
- const [x, y] = input.coordinate ?? [0, 0];
6187
- await runCliclick([`du:${x},${y}`]);
6188
- return `left mouse button released at (${x}, ${y})`;
6189
- }
6190
- case "left_click_drag": {
6191
- const [sx, sy] = input.start_coordinate ?? [0, 0];
6192
- const [ex, ey] = input.coordinate ?? [0, 0];
6193
- await runCliclick([`dd:${sx},${sy}`, `m:${ex},${ey}`, `du:${ex},${ey}`]);
6194
- return `dragged from (${sx}, ${sy}) to (${ex}, ${ey})`;
6195
- }
6196
- case "type": {
6197
- const text = input.text ?? "";
6198
- await runCliclick([`t:${text}`]);
6199
- return `typed ${text.length} character(s)`;
6200
- }
6201
- case "key": {
6202
- const args = translateKeyForCliclick(input.text ?? "");
6203
- if (args.length === 0) return "no key specified";
6204
- await runCliclick(args);
6205
- return `pressed ${input.text}`;
6206
- }
6207
- case "hold_key": {
6208
- const text = (input.text ?? "").toLowerCase();
6209
- const duration = input.duration ?? 1;
6210
- const modMap = {
6211
- ctrl: "ctrl",
6212
- control: "ctrl",
6213
- alt: "alt",
6214
- option: "alt",
6215
- shift: "shift",
6216
- cmd: "cmd",
6217
- super: "cmd",
6218
- meta: "cmd",
6219
- fn: "fn"
6220
- };
6221
- const cliName = modMap[text] || text;
6222
- await runCliclick([`kd:${cliName}`]);
6223
- await new Promise((r) => setTimeout(r, duration * 1e3));
6224
- await runCliclick([`ku:${cliName}`]);
6225
- return `held ${text} for ${duration}s`;
6226
- }
6227
- case "scroll": {
6228
- const direction = input.scroll_direction ?? "down";
6229
- const amount = input.scroll_amount ?? 3;
6230
- const px = amount * 100;
6231
- const dx = direction === "left" ? -px : direction === "right" ? px : 0;
6232
- const dy = direction === "up" ? -px : direction === "down" ? px : 0;
6233
- if (input.coordinate) {
6234
- const [x, y] = input.coordinate;
6235
- await runCliclick([`m:${x},${y}`]);
6236
- }
6237
- const mods = input.text ? modifierStringToCliclick(input.text) : [];
6238
- if (mods.length > 0) {
6239
- await runCliclick([`kd:${mods.join(",")}`]);
6240
- }
6241
- await runScroll(dx, dy);
6242
- if (mods.length > 0) {
6243
- await runCliclick([`ku:${mods.join(",")}`]);
6244
- }
6245
- return `scrolled ${direction} by ${amount}`;
6246
- }
6247
- case "wait": {
6248
- const duration = input.duration ?? 1;
6249
- await new Promise((r) => setTimeout(r, duration * 1e3));
6250
- return `waited ${duration}s`;
6251
- }
6252
- case "cursor_position": {
6253
- const out = await runCliclick(["p:."]);
6254
- return `cursor at ${out}`;
6255
- }
6256
- case "zoom": {
6257
- const region = input.region ?? [0, 0, displayWidth, displayHeight];
6258
- const [x1, y1, x2, y2] = region;
6259
- const tmpPath = join8(tmpdir(), `cu-zoom-${nanoid4(8)}.png`);
6260
- await runScreencapture(tmpPath);
6261
- const sharpModule = await import("sharp");
6262
- const sharp2 = sharpModule.default || sharpModule;
6263
- const meta = await sharp2(tmpPath).metadata();
6264
- const scaleX = (meta.width || displayWidth) / displayWidth;
6265
- const scaleY = (meta.height || displayHeight) / displayHeight;
6266
- const px = {
6267
- left: Math.max(0, Math.round(x1 * scaleX)),
6268
- top: Math.max(0, Math.round(y1 * scaleY)),
6269
- width: Math.max(1, Math.round((x2 - x1) * scaleX)),
6270
- height: Math.max(1, Math.round((y2 - y1) * scaleY))
6271
- };
6272
- const buf = await sharp2(tmpPath).extract(px).png().toBuffer();
6273
- try {
6274
- unlinkSync2(tmpPath);
6275
- } catch {
6276
- }
6277
- return { type: "image", data: buf.toString("base64") };
6278
- }
6279
- default: {
6280
- const exhaustive = input.action;
6281
- return `unsupported action: ${String(exhaustive)}`;
6282
- }
6283
- }
6284
- } catch (err) {
6285
- const msg = err?.message || String(err);
6286
- let hint = "";
6287
- if (/accessibility|not authorized|tcc|operation not permitted/i.test(msg)) {
6288
- hint = " (Hint: call enable_computer_use to (re-)check permissions and open System Settings)";
6289
- } else if (/command not found/i.test(msg)) {
6290
- hint = " (Hint: install cliclick with `brew install cliclick`)";
6291
- }
6292
- return `Error: ${msg}${hint}`;
6293
- }
6294
- },
6295
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
6296
- toModelOutput({ output }) {
6297
- if (typeof output === "string") {
6298
- return { type: "content", value: [{ type: "text", text: output }] };
6299
- }
6300
- return {
6301
- type: "content",
6302
- value: [{ type: "media", data: output.data, mediaType: "image/png" }]
6303
- };
6304
- }
6305
- });
6306
- }
6307
-
6308
- // src/tools/enable-computer-use.ts
6309
- init_db();
6310
- import { tool as tool13 } from "ai";
6311
- import { z as z14 } from "zod";
6312
- var inputSchema = z14.object({
6313
- display_width: z14.number().int().positive().optional().describe("Display width in pixels (defaults to detected primary display, fallback 1280)"),
6314
- display_height: z14.number().int().positive().optional().describe("Display height in pixels (defaults to detected primary display, fallback 800)"),
6315
- request_permissions: z14.boolean().optional().default(true).describe(
6316
- "When true (default), proactively trigger macOS permission prompts and open System Settings panes for any missing permissions."
6317
- )
6318
- });
6319
- var ACCESSIBILITY_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
6320
- var SCREEN_RECORDING_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
6321
- function createEnableComputerUseTool(options) {
6322
- return tool13({
6323
- description: "Enable Anthropic's computer use beta tool for this session. macOS only. Drives the actual desktop (mouse, keyboard, screenshots) at pixel coordinates. Requires `cliclick` (brew install cliclick), Accessibility permissions, and Screen Recording permissions. When called, this tool will automatically request any missing permissions and open System Settings to the right pane. Only works on Anthropic Claude models. After this tool succeeds, you MUST stop the current turn and ask the user to send another message \u2014 the `computer` tool only becomes available on the NEXT message because the toolset is fixed for the current turn.",
6324
- inputSchema,
6325
- execute: async ({ display_width, display_height, request_permissions }) => {
6326
- try {
6327
- if (!isMacOs()) {
6328
- return {
6329
- success: false,
6330
- error: "Computer use is currently only supported on macOS.",
6331
- platform: process.platform
6332
- };
6333
- }
6334
- if (!await isCliclickInstalled()) {
6335
- return {
6336
- success: false,
6337
- error: "`cliclick` is not installed. It is required for mouse/keyboard control on macOS.",
6338
- installCommand: "brew install cliclick",
6339
- fixSteps: [
6340
- "In a terminal on this Mac, run: brew install cliclick",
6341
- "(If Homebrew is not installed, install it first from https://brew.sh)",
6342
- "Then call enable_computer_use again"
6343
- ]
6344
- };
6345
- }
6346
- const acc = await hasAccessibilityPermissions();
6347
- const screen = await hasScreenRecordingPermissions();
6348
- const missing = [];
6349
- if (!acc.ok) {
6350
- let prompted = false;
6351
- let panelOpened = false;
6352
- if (request_permissions) {
6353
- prompted = await requestAccessibilityPrompt().then(() => true).catch(() => false);
6354
- await openSystemSettings("accessibility").then(() => {
6355
- panelOpened = true;
6356
- }).catch(() => void 0);
6357
- }
6358
- missing.push({
6359
- name: "Accessibility",
6360
- reason: "cliclick failed: " + (acc.error?.split("\n")[0] || "no permission"),
6361
- pane: "accessibility",
6362
- settingsUrl: ACCESSIBILITY_URL,
6363
- fixSteps: [
6364
- "In the System Settings \u2192 Privacy & Security \u2192 Accessibility pane that opened",
6365
- "Click the + button",
6366
- "Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
6367
- "Toggle the switch ON",
6368
- "Restart the agent process so the new permission takes effect",
6369
- "Then call enable_computer_use again"
6370
- ],
6371
- prompted,
6372
- panelOpened
6373
- });
6374
- }
6375
- if (!screen) {
6376
- let prompted = false;
6377
- let panelOpened = false;
6378
- if (request_permissions) {
6379
- prompted = await requestScreenRecordingPrompt().then(() => true).catch(() => false);
6380
- await openSystemSettings("screen-recording").then(() => {
6381
- panelOpened = true;
6382
- }).catch(() => void 0);
6383
- }
6384
- missing.push({
6385
- name: "Screen Recording",
6386
- reason: "CGPreflightScreenCaptureAccess returned false",
6387
- pane: "screen-recording",
6388
- settingsUrl: SCREEN_RECORDING_URL,
6389
- fixSteps: [
6390
- "In the System Settings \u2192 Privacy & Security \u2192 Screen Recording pane that opened",
6391
- "Click the + button",
6392
- "Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
6393
- "Toggle the switch ON",
6394
- "Restart the agent process so the new permission takes effect",
6395
- "Then call enable_computer_use again"
6396
- ],
6397
- prompted,
6398
- panelOpened
6399
- });
6400
- }
6401
- if (missing.length > 0) {
6402
- return {
6403
- success: false,
6404
- error: `Missing permission${missing.length > 1 ? "s" : ""}: ` + missing.map((m) => m.name).join(" and ") + ".",
6405
- missingPermissions: missing,
6406
- note: request_permissions ? "System permission prompts have been triggered (best-effort) and System Settings has been opened to the relevant pane(s). After granting and restarting the agent, call enable_computer_use again." : "Re-run with request_permissions: true to auto-open System Settings."
6407
- };
6408
- }
6409
- let width = display_width;
6410
- let height = display_height;
6411
- let detected = null;
6412
- if (width === void 0 || height === void 0) {
6413
- detected = await detectScreenSize();
6414
- width = width ?? detected?.width ?? 1280;
6415
- height = height ?? detected?.height ?? 800;
6416
- }
6417
- const session = await sessionQueries.getById(options.sessionId);
6418
- if (!session) {
6419
- return { success: false, error: "Session not found" };
6420
- }
6421
- const config = session.config || {};
6422
- if (config.computerUseEnabled === true && config.computerUseDisplayWidth === width && config.computerUseDisplayHeight === height) {
6423
- return {
6424
- success: true,
6425
- alreadyEnabled: true,
6426
- message: "Computer use was already enabled for this session.",
6427
- displayWidth: width,
6428
- displayHeight: height
6429
- };
6430
- }
6431
- const updated = {
6432
- ...config,
6433
- computerUseEnabled: true,
6434
- computerUseDisplayWidth: width,
6435
- computerUseDisplayHeight: height
6436
- };
6437
- await sessionQueries.update(options.sessionId, { config: updated });
6438
- return {
6439
- success: true,
6440
- enabled: true,
6441
- platform: "darwin",
6442
- displayWidth: width,
6443
- displayHeight: height,
6444
- detectedScreenSize: detected || void 0,
6445
- permissions: {
6446
- accessibility: "granted",
6447
- screenRecording: "granted"
6448
- },
6449
- message: `Computer use is now enabled for this session. IMPORTANT: The \`computer\` tool is NOT yet available in this turn. Stop here, send a brief message to the user telling them computer use is enabled (display: ${width}x${height}), and ask them to send their next message to begin using it.`
6450
- };
6451
- } catch (err) {
6452
- return {
6453
- success: false,
6454
- error: err?.message || String(err)
6455
- };
6456
- }
6457
- }
6458
- });
6459
- }
6460
-
6461
5893
  // src/tools/index.ts
6462
5894
  init_semantic();
6463
5895
  init_remote();
@@ -6505,20 +5937,6 @@ async function createTools(options) {
6505
5937
  sessionId: options.sessionId
6506
5938
  });
6507
5939
  }
6508
- if (process.platform === "darwin") {
6509
- if (options.enableComputerUse) {
6510
- tools.computer = createComputerUseTool({
6511
- workingDirectory: options.workingDirectory,
6512
- sessionId: options.sessionId,
6513
- displayWidth: options.computerUseDisplayWidth,
6514
- displayHeight: options.computerUseDisplayHeight
6515
- });
6516
- } else {
6517
- tools.enable_computer_use = createEnableComputerUseTool({
6518
- sessionId: options.sessionId
6519
- });
6520
- }
6521
- }
6522
5940
  if (options.enableSemanticSearch !== false) {
6523
5941
  try {
6524
5942
  if (isVectorGatewayConfigured()) {
@@ -7020,8 +6438,7 @@ ${JSON.stringify(outputSchema, null, 2)}
7020
6438
  `;
7021
6439
  }
7022
6440
  function buildOrchestratorPromptAddendum() {
7023
- const platform2 = process.platform === "darwin" ? "darwin" : "other";
7024
- const computerUseAvailable = platform2 === "darwin";
6441
+ const desktopAvailable = process.platform === "darwin";
7025
6442
  return `
7026
6443
  ## Orchestrator Mode
7027
6444
 
@@ -7120,14 +6537,14 @@ When NOT to split (keep as one worker):
7120
6537
  When spawning a worker, push it toward the *cheapest tool that gets the job done*:
7121
6538
 
7122
6539
  1. **Bash / file tools** for anything with a CLI (git, npm, brew, builds, tests, file editing, HTTP via curl, scripting).
7123
- 2. **agent-browser** (\`load_skill browser\`) for *anything* in a web browser \u2014 refs from \`snapshot -i\` are deterministic, ~100\xD7 cheaper in tokens than pixel coordinates, work cross-platform, and don't need any host permissions.${computerUseAvailable ? `
7124
- 3. **Computer use** is the last resort \u2014 only when the task genuinely requires a native macOS GUI app with no CLI / API equivalent (System Settings, Calculator, Finder operations that don't have CLI flags, complex cross-app drag/drop, demos where the user wants to *see* the screen).
6540
+ 2. **agent-browser** (\`load_skill browser\`) for *anything* in a web browser \u2014 refs from \`snapshot -i\` are deterministic, ~100\xD7 cheaper in tokens than pixel coordinates, work cross-platform, and don't need any host permissions.${desktopAvailable ? `
6541
+ 3. **Desktop automation** (\`load_skill desktop-automation\`) is the last resort \u2014 only when the task genuinely requires a native macOS GUI app with no CLI / API equivalent (System Settings, Calculator, Finder operations that don't have CLI flags, complex cross-app drag/drop, demos where the user wants to *see* the screen). It's all shell \u2014 \`cliclick\`, \`screencapture\`, and \`osascript\` \u2014 invoked from \`bash\`. No special tool registration; no vendor lock-in.
7125
6542
 
7126
- A common anti-pattern: a worker reaches for computer use because the user phrased the request visually ("open the website and click the button"). Almost always wrong \u2014 that's a job for the browser skill, not the desktop. Coach the worker in its goal text: *"Use the browser skill (\`load_skill browser\` + \`agent-browser\` with refs from \`snapshot -i\`) to open the site and click the button. Don't use computer use for browser work."*
6543
+ A common anti-pattern: a worker reaches for desktop automation because the user phrased the request visually ("open the website and click the button"). Almost always wrong \u2014 that's a job for the browser skill, not the desktop. Coach the worker in its goal text: *"Use the browser skill (\`load_skill browser\` + \`agent-browser\` with refs from \`snapshot -i\`) to open the site and click the button. Don't use desktop automation for browser work."*
7127
6544
 
7128
- ### Serialize desktop / computer-use tasks
6545
+ ### Serialize desktop-automation tasks
7129
6546
 
7130
- There is exactly **one** desktop, mouse, and keyboard on the host. If two or more workers both need the **\`computer\` tool** (clicking, typing, taking desktop screenshots, opening apps, switching windows), they will **fight over the same screen** \u2014 windows will steal focus from each other, screenshots will catch the wrong app, mouse clicks will land on the wrong target.
6547
+ There is exactly **one** desktop, mouse, and keyboard on the host. If two or more workers both drive the desktop (clicking with \`cliclick\`, taking screenshots with \`screencapture\`, opening apps, switching windows), they will **fight over the same screen** \u2014 windows will steal focus from each other, screenshots will catch the wrong app, mouse clicks will land on the wrong target.
7131
6548
 
7132
6549
  **Rule**: when spawning workers, look at each one's goal:
7133
6550
 
@@ -7148,7 +6565,7 @@ Example: *"Take a screenshot of Calculator AND run the test suite AND open Syste
7148
6565
 
7149
6566
  Headless workers never interfere with desktop workers (they don't touch the screen), so they always run in parallel.
7150
6567
 
7151
- When you spawn a **desktop worker**, include a one-liner in the goal asking it to **record the session by default** (\`screencapture -v -V <seconds> -C\` on macOS) and return the recording path in its result, *unless* the task is long-running / boring / contains sensitive content. The recording lets you (and the user) replay what actually happened on screen. When the worker reports back, mention the recording path in your reply via the original channel.` : ""}
6568
+ When you spawn a **desktop worker**, tell it to bracket the work with \`sparkecoder record start\` / \`sparkecoder record stop\` (per the \`recording\` skill) so the user can replay what happened on screen, unless the task is long-running / boring / contains sensitive content. When the worker reports back, mention the recording path in your reply via the original channel.` : ""}
7152
6569
 
7153
6570
  Default bias: **when in doubt, decompose**. Two workers running in parallel and reporting independently is almost always better UX than one worker doing things sequentially.
7154
6571
 
@@ -7179,7 +6596,7 @@ You delegate; the worker executes. Stay at the **what** level, not the **how**.
7179
6596
  **DO** put in the goal:
7180
6597
 
7181
6598
  - The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
7182
- - Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill computer-use\`).
6599
+ - Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill desktop-automation\`).
7183
6600
  - Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
7184
6601
  - Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
7185
6602
 
@@ -7212,7 +6629,7 @@ Bad goal (don't do this):
7212
6629
  > "Start a screen recording with \`screencapture -v -V 45 -C /tmp/weather.mov &\`, then open Weather with \`open -a Weather\`, then click the search bar with cliclick, type 'Anchorage'..."
7213
6630
 
7214
6631
  Good goal (do this):
7215
- > "Capture a 30\u201360s screen recording of opening the macOS Weather app and viewing the Anchorage, AK forecast. \`load_skill recording\` and \`load_skill computer-use\` first; use the canonical commands from those skills. Verify the Anchorage temperature is visible in a final screenshot before completing. Return the recording path + a one-line summary of the forecast."
6632
+ > "Capture a 30\u201360s screen recording of opening the macOS Weather app and viewing the Anchorage, AK forecast. \`load_skill recording\` and \`load_skill desktop-automation\` first; use the canonical commands from those skills. Verify the Anchorage temperature is visible in a final screenshot before completing. Return the recording path + a one-line summary of the forecast."
7216
6633
  `;
7217
6634
  }
7218
6635
  function createSummaryPrompt(conversationHistory) {
@@ -7448,6 +6865,7 @@ ${summaryContent}`
7448
6865
  ];
7449
6866
  }
7450
6867
  messages = repairToolPairing(messages);
6868
+ messages = ensureEndsWithUserOrTool(messages);
7451
6869
  return messages;
7452
6870
  }
7453
6871
  // ---------------------------------------------------------------------------
@@ -7641,7 +7059,8 @@ ${summaryContent}`
7641
7059
  }
7642
7060
  }
7643
7061
  async addResponseMessages(messages) {
7644
- await messageQueries.addMany(this.sessionId, messages);
7062
+ const safe = repairToolPairing(messages);
7063
+ await messageQueries.addMany(this.sessionId, safe);
7645
7064
  try {
7646
7065
  const { appendTurn: appendTurn2, flattenContent: flattenContent2 } = await Promise.resolve().then(() => (init_conversation_archive(), conversation_archive_exports));
7647
7066
  const { sessionQueries: sessionQueries2 } = await Promise.resolve().then(() => (init_db(), db_exports));
@@ -7735,10 +7154,22 @@ function repairToolPairing(messages) {
7735
7154
  }
7736
7155
  return repaired;
7737
7156
  }
7157
+ function ensureEndsWithUserOrTool(messages) {
7158
+ if (!Array.isArray(messages) || messages.length === 0) return messages;
7159
+ const last = messages[messages.length - 1];
7160
+ if (last?.role !== "assistant") return messages;
7161
+ console.warn(
7162
+ "[context] Trailing assistant message detected \u2014 appending synthetic user turn to satisfy prefill restrictions"
7163
+ );
7164
+ return [
7165
+ ...messages,
7166
+ { role: "user", content: [{ type: "text", text: "Please continue." }] }
7167
+ ];
7168
+ }
7738
7169
 
7739
7170
  // src/tools/orchestrator-actions.ts
7740
- import { tool as tool14 } from "ai";
7741
- import { z as z15 } from "zod";
7171
+ import { tool as tool13 } from "ai";
7172
+ import { z as z14 } from "zod";
7742
7173
 
7743
7174
  // src/integrations/channels/web.ts
7744
7175
  var webChannel = {
@@ -7933,7 +7364,7 @@ function describeConfiguredChannels() {
7933
7364
 
7934
7365
  // src/orchestrator/schedules-store.ts
7935
7366
  init_db();
7936
- import { nanoid as nanoid5 } from "nanoid";
7367
+ import { nanoid as nanoid4 } from "nanoid";
7937
7368
  async function readOrch(orchestratorSessionId) {
7938
7369
  const s = await sessionQueries.getById(orchestratorSessionId);
7939
7370
  if (!s) return null;
@@ -7948,7 +7379,7 @@ async function createSchedule(orchestratorSessionId, input) {
7948
7379
  const data = await readOrch(orchestratorSessionId);
7949
7380
  if (!data) throw new Error("orchestrator session not found");
7950
7381
  const row = {
7951
- id: `sch_${nanoid5(10)}`,
7382
+ id: `sch_${nanoid4(10)}`,
7952
7383
  name: input.name,
7953
7384
  cron: input.cron,
7954
7385
  prompt: input.prompt,
@@ -7983,7 +7414,7 @@ init_config();
7983
7414
  // src/orchestrator/webhooks-store.ts
7984
7415
  init_db();
7985
7416
  import { randomBytes } from "crypto";
7986
- import { nanoid as nanoid6 } from "nanoid";
7417
+ import { nanoid as nanoid5 } from "nanoid";
7987
7418
  function newToken() {
7988
7419
  return randomBytes(24).toString("base64url");
7989
7420
  }
@@ -8000,7 +7431,7 @@ async function createWebhook(orchestratorSessionId, input) {
8000
7431
  const data = await readOrch2(orchestratorSessionId);
8001
7432
  if (!data) throw new Error("orchestrator session not found");
8002
7433
  const row = {
8003
- id: `whk_${nanoid6(10)}`,
7434
+ id: `whk_${nanoid5(10)}`,
8004
7435
  name: input.name,
8005
7436
  token: newToken(),
8006
7437
  wake: input.wake ?? "now",
@@ -8058,33 +7489,33 @@ function previewMessageContent(content) {
8058
7489
  }
8059
7490
  return "";
8060
7491
  }
8061
- var AGENT_STATUS_ENUM = z15.enum(["running", "needs_attention", "completed", "failed", "idle"]);
8062
- var agentInputSchema = z15.object({
8063
- action: z15.enum(["list", "get", "spawn", "message", "answer_question", "stop"]).describe("Which agent operation to perform."),
7492
+ var AGENT_STATUS_ENUM = z14.enum(["running", "needs_attention", "completed", "failed", "idle"]);
7493
+ var agentInputSchema = z14.object({
7494
+ action: z14.enum(["list", "get", "spawn", "message", "answer_question", "stop"]).describe("Which agent operation to perform."),
8064
7495
  // list
8065
7496
  status: AGENT_STATUS_ENUM.optional().describe("list only: filter to one status."),
8066
- limit: z15.number().int().min(1).max(100).optional().describe("list only: max rows."),
7497
+ limit: z14.number().int().min(1).max(100).optional().describe("list only: max rows."),
8067
7498
  // get / message / answer_question / stop
8068
- id: z15.string().optional().describe("get | message | answer_question | stop: the agent (session) id."),
8069
- recentMessages: z15.number().int().min(0).max(50).optional().describe("get only: how many recent messages to include."),
7499
+ id: z14.string().optional().describe("get | message | answer_question | stop: the agent (session) id."),
7500
+ recentMessages: z14.number().int().min(0).max(50).optional().describe("get only: how many recent messages to include."),
8070
7501
  // spawn
8071
- name: z15.string().optional().describe("spawn only: short human-readable label."),
8072
- goal: z15.string().optional().describe("spawn only: the worker's self-contained instruction."),
8073
- outputSchema: z15.record(z15.string(), z15.unknown()).optional().describe(
7502
+ name: z14.string().optional().describe("spawn only: short human-readable label."),
7503
+ goal: z14.string().optional().describe("spawn only: the worker's self-contained instruction."),
7504
+ outputSchema: z14.record(z14.string(), z14.unknown()).optional().describe(
8074
7505
  'spawn only: JSON Schema for the worker result. Defaults to {type:"object", properties:{summary:{type:"string"}}, required:["summary"]}.'
8075
7506
  ),
8076
- model: z15.string().optional().describe("spawn only: model override."),
8077
- workingDirectory: z15.string().optional().describe("spawn only: working directory override."),
8078
- maxIterations: z15.number().int().min(1).max(500).optional().describe("spawn only."),
7507
+ model: z14.string().optional().describe("spawn only: model override."),
7508
+ workingDirectory: z14.string().optional().describe("spawn only: working directory override."),
7509
+ maxIterations: z14.number().int().min(1).max(500).optional().describe("spawn only."),
8079
7510
  // message
8080
- text: z15.string().optional().describe("message only: the text to deliver to the worker."),
8081
- force: z15.boolean().optional().describe("message only: soft-interrupt the current step."),
7511
+ text: z14.string().optional().describe("message only: the text to deliver to the worker."),
7512
+ force: z14.boolean().optional().describe("message only: soft-interrupt the current step."),
8082
7513
  // answer_question
8083
- questionId: z15.string().optional().describe("answer_question only: pending question id (e.g. q_abc123)."),
8084
- answer: z15.string().optional().describe("answer_question only: your answer.")
7514
+ questionId: z14.string().optional().describe("answer_question only: pending question id (e.g. q_abc123)."),
7515
+ answer: z14.string().optional().describe("answer_question only: your answer.")
8085
7516
  });
8086
7517
  function buildAgentTool(opts) {
8087
- return tool14({
7518
+ return tool13({
8088
7519
  description: "Manage worker agents. Actions: list (browse with optional status filter), get (deep dive: status, todos, pending question, recent messages, final result; required: id), spawn (start a new worker; required: name, goal), message (post a message to a running worker; force=true to soft-interrupt; required: id, text), answer_question (resolve a worker's ask_question_to_user prompt; required: id, questionId, answer), stop (hard-cancel a running worker; required: id).",
8089
7520
  inputSchema: agentInputSchema,
8090
7521
  execute: async (input) => {
@@ -8186,17 +7617,17 @@ function buildAgentTool(opts) {
8186
7617
  }
8187
7618
  });
8188
7619
  }
8189
- var messengerInputSchema = z15.object({
8190
- action: z15.enum(["list_channels", "post"]),
7620
+ var messengerInputSchema = z14.object({
7621
+ action: z14.enum(["list_channels", "post"]),
8191
7622
  // post
8192
- channel: z15.string().optional().describe('post only: channel id (e.g. "slack").'),
8193
- to: z15.string().optional().describe('post only: destination. Slack: channel id (C0123), user id (U0123), or "#channel-name".'),
8194
- text: z15.string().optional().describe("post only: message body."),
8195
- threadTs: z15.string().optional().describe("post + slack: reply in this thread."),
8196
- subject: z15.string().optional().describe("post + email: subject (future).")
7623
+ channel: z14.string().optional().describe('post only: channel id (e.g. "slack").'),
7624
+ to: z14.string().optional().describe('post only: destination. Slack: channel id (C0123), user id (U0123), or "#channel-name".'),
7625
+ text: z14.string().optional().describe("post only: message body."),
7626
+ threadTs: z14.string().optional().describe("post + slack: reply in this thread."),
7627
+ subject: z14.string().optional().describe("post + email: subject (future).")
8197
7628
  });
8198
7629
  function buildMessengerTool() {
8199
- return tool14({
7630
+ return tool13({
8200
7631
  description: "Send messages on configured external channels. Actions: list_channels (no args; returns which integrations are configured), post (required: channel, to, text). Use this to ping the user on Slack when a worker finishes, when a schedule fires, etc.",
8201
7632
  inputSchema: messengerInputSchema,
8202
7633
  execute: async (input) => {
@@ -8217,19 +7648,19 @@ function buildMessengerTool() {
8217
7648
  }
8218
7649
  });
8219
7650
  }
8220
- var scheduleInputSchema = z15.object({
8221
- action: z15.enum(["create", "list", "update", "delete", "pause", "resume"]),
7651
+ var scheduleInputSchema = z14.object({
7652
+ action: z14.enum(["create", "list", "update", "delete", "pause", "resume"]),
8222
7653
  // create / update
8223
- name: z15.string().optional().describe("create | update"),
8224
- cron: z15.string().optional().describe('create | update: 5-field cron (e.g. "0 9 * * 1-5" = weekdays at 9am).'),
8225
- prompt: z15.string().optional().describe("create | update: the prompt injected when the schedule fires."),
8226
- replyChannel: z15.string().optional().describe("create | update: default channel id for orchestrator replies."),
7654
+ name: z14.string().optional().describe("create | update"),
7655
+ cron: z14.string().optional().describe('create | update: 5-field cron (e.g. "0 9 * * 1-5" = weekdays at 9am).'),
7656
+ prompt: z14.string().optional().describe("create | update: the prompt injected when the schedule fires."),
7657
+ replyChannel: z14.string().optional().describe("create | update: default channel id for orchestrator replies."),
8227
7658
  // update / delete / pause / resume
8228
- id: z15.string().optional().describe("update | delete | pause | resume: schedule id."),
8229
- enabled: z15.boolean().optional().describe("update only.")
7659
+ id: z14.string().optional().describe("update | delete | pause | resume: schedule id."),
7660
+ enabled: z14.boolean().optional().describe("update only.")
8230
7661
  });
8231
7662
  function buildScheduleTool(opts) {
8232
- return tool14({
7663
+ return tool13({
8233
7664
  description: "Recurring prompts. Actions: create (required: name, cron, prompt), list, update (required: id; any of name/cron/prompt/enabled/replyChannel), delete (required: id), pause (required: id), resume (required: id). Cron is standard 5-field syntax.",
8234
7665
  inputSchema: scheduleInputSchema,
8235
7666
  execute: async (input) => {
@@ -8265,13 +7696,13 @@ function buildScheduleTool(opts) {
8265
7696
  }
8266
7697
  });
8267
7698
  }
8268
- var webhookInputSchema = z15.object({
8269
- action: z15.enum(["create", "list", "update", "delete"]),
8270
- name: z15.string().optional().describe("create | update."),
8271
- wake: z15.enum(["now", "next"]).optional().describe("create | update: now = wake orchestrator immediately; next = add as context."),
8272
- template: z15.string().optional().describe("create | update: mustache-style template ({{path.to.field}}). Defaults to pretty-printed JSON."),
8273
- id: z15.string().optional().describe("update | delete: webhook id."),
8274
- rotateToken: z15.boolean().optional().describe("update only: regenerate the URL token.")
7699
+ var webhookInputSchema = z14.object({
7700
+ action: z14.enum(["create", "list", "update", "delete"]),
7701
+ name: z14.string().optional().describe("create | update."),
7702
+ wake: z14.enum(["now", "next"]).optional().describe("create | update: now = wake orchestrator immediately; next = add as context."),
7703
+ template: z14.string().optional().describe("create | update: mustache-style template ({{path.to.field}}). Defaults to pretty-printed JSON."),
7704
+ id: z14.string().optional().describe("update | delete: webhook id."),
7705
+ rotateToken: z14.boolean().optional().describe("update only: regenerate the URL token.")
8275
7706
  });
8276
7707
  function buildWebhookUrl(opts, token) {
8277
7708
  const base = opts.publicBaseUrl?.replace(/\/$/, "") || opts.baseUrl.replace(/\/$/, "");
@@ -8280,7 +7711,7 @@ function buildWebhookUrl(opts, token) {
8280
7711
  return `${base}${webhookPrefix}/inbox/${token}`;
8281
7712
  }
8282
7713
  function buildWebhookTool(opts) {
8283
- return tool14({
7714
+ return tool13({
8284
7715
  description: "Custom token-protected inbound URLs. Anyone POSTing JSON to the URL pings the orchestrator. Actions: create (required: name), list, update (required: id; optional: name, wake, template, rotateToken), delete (required: id). Use these to wire up GitHub, IFTTT, n8n, etc.",
8285
7716
  inputSchema: webhookInputSchema,
8286
7717
  execute: async (input) => {
@@ -8327,9 +7758,9 @@ import { createMCPClient } from "@ai-sdk/mcp";
8327
7758
 
8328
7759
  // src/integrations/mcp/store.ts
8329
7760
  init_config();
8330
- import { nanoid as nanoid7 } from "nanoid";
8331
- import { existsSync as existsSync17, readFileSync as readFileSync8 } from "fs";
8332
- import { resolve as resolve10, join as join10 } from "path";
7761
+ import { nanoid as nanoid6 } from "nanoid";
7762
+ import { existsSync as existsSync16, readFileSync as readFileSync7 } from "fs";
7763
+ import { resolve as resolve10, join as join9 } from "path";
8333
7764
  function readServers() {
8334
7765
  try {
8335
7766
  const cfg = getConfig();
@@ -8341,12 +7772,12 @@ function readServers() {
8341
7772
  function refreshMcpServersFromDisk() {
8342
7773
  const candidates = [
8343
7774
  resolve10(process.cwd(), "sparkecoder.config.json"),
8344
- join10(ensureAppDataDirectory(), "sparkecoder.config.json")
7775
+ join9(ensureAppDataDirectory(), "sparkecoder.config.json")
8345
7776
  ];
8346
7777
  for (const path of candidates) {
8347
- if (!existsSync17(path)) continue;
7778
+ if (!existsSync16(path)) continue;
8348
7779
  try {
8349
- const raw = JSON.parse(readFileSync8(path, "utf-8"));
7780
+ const raw = JSON.parse(readFileSync7(path, "utf-8"));
8350
7781
  const servers2 = Array.isArray(raw?.mcp?.servers) ? raw.mcp.servers : [];
8351
7782
  setMcpServers(servers2);
8352
7783
  return servers2;
@@ -8615,14 +8046,10 @@ var Agent = class _Agent {
8615
8046
  */
8616
8047
  async createToolsWithCallbacks(options) {
8617
8048
  const config = getConfig();
8618
- const sessionConfig = this.session.config || {};
8619
8049
  const tools = await createTools({
8620
8050
  sessionId: this.session.id,
8621
8051
  workingDirectory: this.session.workingDirectory,
8622
8052
  skillsDirectories: config.resolvedSkillsDirectories,
8623
- enableComputerUse: sessionConfig.computerUseEnabled === true,
8624
- computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
8625
- computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight,
8626
8053
  onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
8627
8054
  onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
8628
8055
  onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0
@@ -8672,14 +8099,10 @@ var Agent = class _Agent {
8672
8099
  keepRecentMessages: config.context?.keepRecentMessages || 10,
8673
8100
  autoSummarize: config.context?.autoSummarize ?? true
8674
8101
  });
8675
- const sessionConfig = session.config || {};
8676
8102
  const tools = await createTools({
8677
8103
  sessionId: session.id,
8678
8104
  workingDirectory: session.workingDirectory,
8679
- skillsDirectories: config.resolvedSkillsDirectories,
8680
- enableComputerUse: sessionConfig.computerUseEnabled === true,
8681
- computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
8682
- computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight
8105
+ skillsDirectories: config.resolvedSkillsDirectories
8683
8106
  });
8684
8107
  if (session.config?.role === "orchestrator") {
8685
8108
  const baseUrl = `http://127.0.0.1:${config.server?.port ?? 3141}`;
@@ -8917,14 +8340,10 @@ ${personality.trim()}`;
8917
8340
  });
8918
8341
  }
8919
8342
  };
8920
- const taskSessionConfig = this.session.config || {};
8921
8343
  const taskTools = await createTools({
8922
8344
  sessionId: this.session.id,
8923
8345
  workingDirectory: this.session.workingDirectory,
8924
8346
  skillsDirectories: config.resolvedSkillsDirectories,
8925
- enableComputerUse: taskSessionConfig.computerUseEnabled === true,
8926
- computerUseDisplayWidth: taskSessionConfig.computerUseDisplayWidth,
8927
- computerUseDisplayHeight: taskSessionConfig.computerUseDisplayHeight,
8928
8347
  onBashProgress: bashProgressHandler,
8929
8348
  onWriteFileProgress: (progress) => {
8930
8349
  options.onToolProgress?.({ toolName: "write_file", data: progress });
@@ -9272,11 +8691,11 @@ ${p.text}` : p.text;
9272
8691
  const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
9273
8692
  if (!isRemoteConfigured2()) return [];
9274
8693
  const { readFile: readFile12 } = await import("fs/promises");
9275
- const { join: join12, basename: basename5 } = await import("path");
8694
+ const { join: join11, basename: basename5 } = await import("path");
9276
8695
  const urls = [];
9277
8696
  for (const filePath of filePaths) {
9278
8697
  try {
9279
- const fullPath = filePath.startsWith("/") ? filePath : join12(this.session.workingDirectory, filePath);
8698
+ const fullPath = filePath.startsWith("/") ? filePath : join11(this.session.workingDirectory, filePath);
9280
8699
  const fileName = basename5(fullPath);
9281
8700
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
9282
8701
  const mimeMap = {
@@ -9334,11 +8753,11 @@ ${p.text}` : p.text;
9334
8753
  wrappedTools[name] = originalTool;
9335
8754
  continue;
9336
8755
  }
9337
- wrappedTools[name] = tool15({
8756
+ wrappedTools[name] = tool14({
9338
8757
  description: originalTool.description || "",
9339
- inputSchema: originalTool.inputSchema || z16.object({}),
8758
+ inputSchema: originalTool.inputSchema || z15.object({}),
9340
8759
  execute: async (input, toolOptions) => {
9341
- const toolCallId = toolOptions.toolCallId || nanoid9();
8760
+ const toolCallId = toolOptions.toolCallId || nanoid8();
9342
8761
  const execution = toolExecutionQueries.create({
9343
8762
  sessionId: this.session.id,
9344
8763
  toolName: name,
@@ -9356,10 +8775,10 @@ ${p.text}` : p.text;
9356
8775
  const resolverData = approvalResolvers.get(toolCallId);
9357
8776
  approvalResolvers.delete(toolCallId);
9358
8777
  this.pendingApprovals.delete(toolCallId);
9359
- const exec7 = await execution;
8778
+ const exec6 = await execution;
9360
8779
  if (!approved) {
9361
8780
  const reason = resolverData?.reason || "User rejected the tool execution";
9362
- await toolExecutionQueries.reject(exec7.id);
8781
+ await toolExecutionQueries.reject(exec6.id);
9363
8782
  await sessionQueries.updateStatus(this.session.id, "active");
9364
8783
  return {
9365
8784
  status: "rejected",
@@ -9369,14 +8788,14 @@ ${p.text}` : p.text;
9369
8788
  message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
9370
8789
  };
9371
8790
  }
9372
- await toolExecutionQueries.approve(exec7.id);
8791
+ await toolExecutionQueries.approve(exec6.id);
9373
8792
  await sessionQueries.updateStatus(this.session.id, "active");
9374
8793
  try {
9375
8794
  const result = await originalTool.execute(input, toolOptions);
9376
- await toolExecutionQueries.complete(exec7.id, result);
8795
+ await toolExecutionQueries.complete(exec6.id, result);
9377
8796
  return result;
9378
8797
  } catch (error) {
9379
- await toolExecutionQueries.complete(exec7.id, null, error.message);
8798
+ await toolExecutionQueries.complete(exec6.id, null, error.message);
9380
8799
  throw error;
9381
8800
  }
9382
8801
  }