sparkecoder 0.1.116 → 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 (117) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +136 -697
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +621 -1038
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-Biy5JTop.d.ts → index-Bcz0aCAR.d.ts} +104 -113
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +353 -935
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-CYSKJZ3m.d.ts → schema-BWbWmfDQ.d.ts} +3 -5
  12. package/dist/{search-CVVfuBPZ.d.ts → search-DOzC4ojH.d.ts} +4 -4
  13. package/dist/server/index.js +353 -935
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/skills/default/desktop-automation.md +290 -0
  16. package/dist/skills/default/recording.md +3 -3
  17. package/dist/tools/index.d.ts +4 -170
  18. package/dist/tools/index.js +5 -590
  19. package/dist/tools/index.js.map +1 -1
  20. package/package.json +1 -1
  21. package/src/skills/default/desktop-automation.md +290 -0
  22. package/src/skills/default/recording.md +3 -3
  23. package/web/.next/BUILD_ID +1 -1
  24. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  25. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  26. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  27. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  28. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  43. package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  52. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  78. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  88. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  92. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  93. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  94. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  95. package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
  96. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
  97. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  98. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  99. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
  100. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  101. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  102. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  103. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  104. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  105. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  106. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  107. package/dist/skills/default/computer-use.md +0 -225
  108. package/src/skills/default/computer-use.md +0 -225
  109. /package/web/.next/standalone/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
  110. /package/web/.next/standalone/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
  111. /package/web/.next/standalone/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
  112. /package/web/.next/standalone/web/.next/static/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
  113. /package/web/.next/standalone/web/.next/static/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
  114. /package/web/.next/standalone/web/.next/static/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
  115. /package/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
  116. /package/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
  117. /package/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → 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,10 +6565,30 @@ 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
 
6572
+ ### How to TALK to the user (versus how you reason internally)
6573
+
6574
+ All of the rules below \u2014 decomposition, parallel spawning, "don't invent commands", load-the-skill, etc. \u2014 are **your internal operating procedure**. They are how you decide what to do. They are NOT something to recite back to the user.
6575
+
6576
+ When replying to the user (Slack, web, or any channel), be a normal helpful assistant:
6577
+
6578
+ - Tell them *what you're doing*, not *how you're doing it internally*.
6579
+ - Don't quote your own prompt language ("just the goal", "read the skill", "no step-by-step from me", "decomposing into parallel workers"). The user doesn't care about your delegation pattern.
6580
+ - Don't narrate "handing off to a worker now" unless it's actually relevant (e.g. they asked how you work, or the work is going to take a noticeably long time).
6581
+ - Skip ceremony. A short acknowledgement + a brief description of the actual task + (optionally) a heads-up if there's anything they need to do or avoid is plenty.
6582
+
6583
+ | What you might be tempted to say | What you should say instead |
6584
+ |---|---|
6585
+ | *"Got it \u2014 handing it off with just the goal + 'read the skills and figure it out.' No step-by-step from me."* | *"On it. Recording a screen capture of the Weather app showing Anchorage. Don't touch the keyboard while it's running \u2014 I'll post the video when it's done."* |
6586
+ | *"Decomposing into two parallel workers: one headless, one desktop."* | *"Running the tests and pulling the diff at the same time. Back in ~30s."* |
6587
+ | *"Spawning worker \`screenshot-calc\` with goal: \u2026"* | *"Taking a screenshot of Calculator now."* |
6588
+ | *"I'll relay the user's instructions to the worker verbatim."* | *(say nothing \u2014 just do it)* |
6589
+
6590
+ If the user explicitly asks how you work, *then* you can explain the orchestrator/worker split. Otherwise: less is more.
6591
+
7155
6592
  ### How to write a worker goal (and what NOT to put in it)
7156
6593
 
7157
6594
  You delegate; the worker executes. Stay at the **what** level, not the **how**.
@@ -7159,7 +6596,7 @@ You delegate; the worker executes. Stay at the **what** level, not the **how**.
7159
6596
  **DO** put in the goal:
7160
6597
 
7161
6598
  - The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
7162
- - 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\`).
7163
6600
  - Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
7164
6601
  - Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
7165
6602
 
@@ -7192,7 +6629,7 @@ Bad goal (don't do this):
7192
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'..."
7193
6630
 
7194
6631
  Good goal (do this):
7195
- > "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."
7196
6633
  `;
7197
6634
  }
7198
6635
  function createSummaryPrompt(conversationHistory) {
@@ -7428,6 +6865,7 @@ ${summaryContent}`
7428
6865
  ];
7429
6866
  }
7430
6867
  messages = repairToolPairing(messages);
6868
+ messages = ensureEndsWithUserOrTool(messages);
7431
6869
  return messages;
7432
6870
  }
7433
6871
  // ---------------------------------------------------------------------------
@@ -7621,7 +7059,8 @@ ${summaryContent}`
7621
7059
  }
7622
7060
  }
7623
7061
  async addResponseMessages(messages) {
7624
- await messageQueries.addMany(this.sessionId, messages);
7062
+ const safe = repairToolPairing(messages);
7063
+ await messageQueries.addMany(this.sessionId, safe);
7625
7064
  try {
7626
7065
  const { appendTurn: appendTurn2, flattenContent: flattenContent2 } = await Promise.resolve().then(() => (init_conversation_archive(), conversation_archive_exports));
7627
7066
  const { sessionQueries: sessionQueries2 } = await Promise.resolve().then(() => (init_db(), db_exports));
@@ -7715,10 +7154,22 @@ function repairToolPairing(messages) {
7715
7154
  }
7716
7155
  return repaired;
7717
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
+ }
7718
7169
 
7719
7170
  // src/tools/orchestrator-actions.ts
7720
- import { tool as tool14 } from "ai";
7721
- import { z as z15 } from "zod";
7171
+ import { tool as tool13 } from "ai";
7172
+ import { z as z14 } from "zod";
7722
7173
 
7723
7174
  // src/integrations/channels/web.ts
7724
7175
  var webChannel = {
@@ -7913,7 +7364,7 @@ function describeConfiguredChannels() {
7913
7364
 
7914
7365
  // src/orchestrator/schedules-store.ts
7915
7366
  init_db();
7916
- import { nanoid as nanoid5 } from "nanoid";
7367
+ import { nanoid as nanoid4 } from "nanoid";
7917
7368
  async function readOrch(orchestratorSessionId) {
7918
7369
  const s = await sessionQueries.getById(orchestratorSessionId);
7919
7370
  if (!s) return null;
@@ -7928,7 +7379,7 @@ async function createSchedule(orchestratorSessionId, input) {
7928
7379
  const data = await readOrch(orchestratorSessionId);
7929
7380
  if (!data) throw new Error("orchestrator session not found");
7930
7381
  const row = {
7931
- id: `sch_${nanoid5(10)}`,
7382
+ id: `sch_${nanoid4(10)}`,
7932
7383
  name: input.name,
7933
7384
  cron: input.cron,
7934
7385
  prompt: input.prompt,
@@ -7963,7 +7414,7 @@ init_config();
7963
7414
  // src/orchestrator/webhooks-store.ts
7964
7415
  init_db();
7965
7416
  import { randomBytes } from "crypto";
7966
- import { nanoid as nanoid6 } from "nanoid";
7417
+ import { nanoid as nanoid5 } from "nanoid";
7967
7418
  function newToken() {
7968
7419
  return randomBytes(24).toString("base64url");
7969
7420
  }
@@ -7980,7 +7431,7 @@ async function createWebhook(orchestratorSessionId, input) {
7980
7431
  const data = await readOrch2(orchestratorSessionId);
7981
7432
  if (!data) throw new Error("orchestrator session not found");
7982
7433
  const row = {
7983
- id: `whk_${nanoid6(10)}`,
7434
+ id: `whk_${nanoid5(10)}`,
7984
7435
  name: input.name,
7985
7436
  token: newToken(),
7986
7437
  wake: input.wake ?? "now",
@@ -8038,33 +7489,33 @@ function previewMessageContent(content) {
8038
7489
  }
8039
7490
  return "";
8040
7491
  }
8041
- var AGENT_STATUS_ENUM = z15.enum(["running", "needs_attention", "completed", "failed", "idle"]);
8042
- var agentInputSchema = z15.object({
8043
- 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."),
8044
7495
  // list
8045
7496
  status: AGENT_STATUS_ENUM.optional().describe("list only: filter to one status."),
8046
- 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."),
8047
7498
  // get / message / answer_question / stop
8048
- id: z15.string().optional().describe("get | message | answer_question | stop: the agent (session) id."),
8049
- 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."),
8050
7501
  // spawn
8051
- name: z15.string().optional().describe("spawn only: short human-readable label."),
8052
- goal: z15.string().optional().describe("spawn only: the worker's self-contained instruction."),
8053
- 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(
8054
7505
  'spawn only: JSON Schema for the worker result. Defaults to {type:"object", properties:{summary:{type:"string"}}, required:["summary"]}.'
8055
7506
  ),
8056
- model: z15.string().optional().describe("spawn only: model override."),
8057
- workingDirectory: z15.string().optional().describe("spawn only: working directory override."),
8058
- 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."),
8059
7510
  // message
8060
- text: z15.string().optional().describe("message only: the text to deliver to the worker."),
8061
- 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."),
8062
7513
  // answer_question
8063
- questionId: z15.string().optional().describe("answer_question only: pending question id (e.g. q_abc123)."),
8064
- 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.")
8065
7516
  });
8066
7517
  function buildAgentTool(opts) {
8067
- return tool14({
7518
+ return tool13({
8068
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).",
8069
7520
  inputSchema: agentInputSchema,
8070
7521
  execute: async (input) => {
@@ -8166,17 +7617,17 @@ function buildAgentTool(opts) {
8166
7617
  }
8167
7618
  });
8168
7619
  }
8169
- var messengerInputSchema = z15.object({
8170
- action: z15.enum(["list_channels", "post"]),
7620
+ var messengerInputSchema = z14.object({
7621
+ action: z14.enum(["list_channels", "post"]),
8171
7622
  // post
8172
- channel: z15.string().optional().describe('post only: channel id (e.g. "slack").'),
8173
- to: z15.string().optional().describe('post only: destination. Slack: channel id (C0123), user id (U0123), or "#channel-name".'),
8174
- text: z15.string().optional().describe("post only: message body."),
8175
- threadTs: z15.string().optional().describe("post + slack: reply in this thread."),
8176
- 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).")
8177
7628
  });
8178
7629
  function buildMessengerTool() {
8179
- return tool14({
7630
+ return tool13({
8180
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.",
8181
7632
  inputSchema: messengerInputSchema,
8182
7633
  execute: async (input) => {
@@ -8197,19 +7648,19 @@ function buildMessengerTool() {
8197
7648
  }
8198
7649
  });
8199
7650
  }
8200
- var scheduleInputSchema = z15.object({
8201
- action: z15.enum(["create", "list", "update", "delete", "pause", "resume"]),
7651
+ var scheduleInputSchema = z14.object({
7652
+ action: z14.enum(["create", "list", "update", "delete", "pause", "resume"]),
8202
7653
  // create / update
8203
- name: z15.string().optional().describe("create | update"),
8204
- cron: z15.string().optional().describe('create | update: 5-field cron (e.g. "0 9 * * 1-5" = weekdays at 9am).'),
8205
- prompt: z15.string().optional().describe("create | update: the prompt injected when the schedule fires."),
8206
- 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."),
8207
7658
  // update / delete / pause / resume
8208
- id: z15.string().optional().describe("update | delete | pause | resume: schedule id."),
8209
- 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.")
8210
7661
  });
8211
7662
  function buildScheduleTool(opts) {
8212
- return tool14({
7663
+ return tool13({
8213
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.",
8214
7665
  inputSchema: scheduleInputSchema,
8215
7666
  execute: async (input) => {
@@ -8245,13 +7696,13 @@ function buildScheduleTool(opts) {
8245
7696
  }
8246
7697
  });
8247
7698
  }
8248
- var webhookInputSchema = z15.object({
8249
- action: z15.enum(["create", "list", "update", "delete"]),
8250
- name: z15.string().optional().describe("create | update."),
8251
- wake: z15.enum(["now", "next"]).optional().describe("create | update: now = wake orchestrator immediately; next = add as context."),
8252
- template: z15.string().optional().describe("create | update: mustache-style template ({{path.to.field}}). Defaults to pretty-printed JSON."),
8253
- id: z15.string().optional().describe("update | delete: webhook id."),
8254
- 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.")
8255
7706
  });
8256
7707
  function buildWebhookUrl(opts, token) {
8257
7708
  const base = opts.publicBaseUrl?.replace(/\/$/, "") || opts.baseUrl.replace(/\/$/, "");
@@ -8260,7 +7711,7 @@ function buildWebhookUrl(opts, token) {
8260
7711
  return `${base}${webhookPrefix}/inbox/${token}`;
8261
7712
  }
8262
7713
  function buildWebhookTool(opts) {
8263
- return tool14({
7714
+ return tool13({
8264
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.",
8265
7716
  inputSchema: webhookInputSchema,
8266
7717
  execute: async (input) => {
@@ -8307,9 +7758,9 @@ import { createMCPClient } from "@ai-sdk/mcp";
8307
7758
 
8308
7759
  // src/integrations/mcp/store.ts
8309
7760
  init_config();
8310
- import { nanoid as nanoid7 } from "nanoid";
8311
- import { existsSync as existsSync17, readFileSync as readFileSync8 } from "fs";
8312
- 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";
8313
7764
  function readServers() {
8314
7765
  try {
8315
7766
  const cfg = getConfig();
@@ -8321,12 +7772,12 @@ function readServers() {
8321
7772
  function refreshMcpServersFromDisk() {
8322
7773
  const candidates = [
8323
7774
  resolve10(process.cwd(), "sparkecoder.config.json"),
8324
- join10(ensureAppDataDirectory(), "sparkecoder.config.json")
7775
+ join9(ensureAppDataDirectory(), "sparkecoder.config.json")
8325
7776
  ];
8326
7777
  for (const path of candidates) {
8327
- if (!existsSync17(path)) continue;
7778
+ if (!existsSync16(path)) continue;
8328
7779
  try {
8329
- const raw = JSON.parse(readFileSync8(path, "utf-8"));
7780
+ const raw = JSON.parse(readFileSync7(path, "utf-8"));
8330
7781
  const servers2 = Array.isArray(raw?.mcp?.servers) ? raw.mcp.servers : [];
8331
7782
  setMcpServers(servers2);
8332
7783
  return servers2;
@@ -8595,14 +8046,10 @@ var Agent = class _Agent {
8595
8046
  */
8596
8047
  async createToolsWithCallbacks(options) {
8597
8048
  const config = getConfig();
8598
- const sessionConfig = this.session.config || {};
8599
8049
  const tools = await createTools({
8600
8050
  sessionId: this.session.id,
8601
8051
  workingDirectory: this.session.workingDirectory,
8602
8052
  skillsDirectories: config.resolvedSkillsDirectories,
8603
- enableComputerUse: sessionConfig.computerUseEnabled === true,
8604
- computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
8605
- computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight,
8606
8053
  onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
8607
8054
  onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
8608
8055
  onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0
@@ -8652,14 +8099,10 @@ var Agent = class _Agent {
8652
8099
  keepRecentMessages: config.context?.keepRecentMessages || 10,
8653
8100
  autoSummarize: config.context?.autoSummarize ?? true
8654
8101
  });
8655
- const sessionConfig = session.config || {};
8656
8102
  const tools = await createTools({
8657
8103
  sessionId: session.id,
8658
8104
  workingDirectory: session.workingDirectory,
8659
- skillsDirectories: config.resolvedSkillsDirectories,
8660
- enableComputerUse: sessionConfig.computerUseEnabled === true,
8661
- computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
8662
- computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight
8105
+ skillsDirectories: config.resolvedSkillsDirectories
8663
8106
  });
8664
8107
  if (session.config?.role === "orchestrator") {
8665
8108
  const baseUrl = `http://127.0.0.1:${config.server?.port ?? 3141}`;
@@ -8897,14 +8340,10 @@ ${personality.trim()}`;
8897
8340
  });
8898
8341
  }
8899
8342
  };
8900
- const taskSessionConfig = this.session.config || {};
8901
8343
  const taskTools = await createTools({
8902
8344
  sessionId: this.session.id,
8903
8345
  workingDirectory: this.session.workingDirectory,
8904
8346
  skillsDirectories: config.resolvedSkillsDirectories,
8905
- enableComputerUse: taskSessionConfig.computerUseEnabled === true,
8906
- computerUseDisplayWidth: taskSessionConfig.computerUseDisplayWidth,
8907
- computerUseDisplayHeight: taskSessionConfig.computerUseDisplayHeight,
8908
8347
  onBashProgress: bashProgressHandler,
8909
8348
  onWriteFileProgress: (progress) => {
8910
8349
  options.onToolProgress?.({ toolName: "write_file", data: progress });
@@ -9252,11 +8691,11 @@ ${p.text}` : p.text;
9252
8691
  const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
9253
8692
  if (!isRemoteConfigured2()) return [];
9254
8693
  const { readFile: readFile12 } = await import("fs/promises");
9255
- const { join: join12, basename: basename5 } = await import("path");
8694
+ const { join: join11, basename: basename5 } = await import("path");
9256
8695
  const urls = [];
9257
8696
  for (const filePath of filePaths) {
9258
8697
  try {
9259
- const fullPath = filePath.startsWith("/") ? filePath : join12(this.session.workingDirectory, filePath);
8698
+ const fullPath = filePath.startsWith("/") ? filePath : join11(this.session.workingDirectory, filePath);
9260
8699
  const fileName = basename5(fullPath);
9261
8700
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
9262
8701
  const mimeMap = {
@@ -9314,11 +8753,11 @@ ${p.text}` : p.text;
9314
8753
  wrappedTools[name] = originalTool;
9315
8754
  continue;
9316
8755
  }
9317
- wrappedTools[name] = tool15({
8756
+ wrappedTools[name] = tool14({
9318
8757
  description: originalTool.description || "",
9319
- inputSchema: originalTool.inputSchema || z16.object({}),
8758
+ inputSchema: originalTool.inputSchema || z15.object({}),
9320
8759
  execute: async (input, toolOptions) => {
9321
- const toolCallId = toolOptions.toolCallId || nanoid9();
8760
+ const toolCallId = toolOptions.toolCallId || nanoid8();
9322
8761
  const execution = toolExecutionQueries.create({
9323
8762
  sessionId: this.session.id,
9324
8763
  toolName: name,
@@ -9336,10 +8775,10 @@ ${p.text}` : p.text;
9336
8775
  const resolverData = approvalResolvers.get(toolCallId);
9337
8776
  approvalResolvers.delete(toolCallId);
9338
8777
  this.pendingApprovals.delete(toolCallId);
9339
- const exec7 = await execution;
8778
+ const exec6 = await execution;
9340
8779
  if (!approved) {
9341
8780
  const reason = resolverData?.reason || "User rejected the tool execution";
9342
- await toolExecutionQueries.reject(exec7.id);
8781
+ await toolExecutionQueries.reject(exec6.id);
9343
8782
  await sessionQueries.updateStatus(this.session.id, "active");
9344
8783
  return {
9345
8784
  status: "rejected",
@@ -9349,14 +8788,14 @@ ${p.text}` : p.text;
9349
8788
  message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
9350
8789
  };
9351
8790
  }
9352
- await toolExecutionQueries.approve(exec7.id);
8791
+ await toolExecutionQueries.approve(exec6.id);
9353
8792
  await sessionQueries.updateStatus(this.session.id, "active");
9354
8793
  try {
9355
8794
  const result = await originalTool.execute(input, toolOptions);
9356
- await toolExecutionQueries.complete(exec7.id, result);
8795
+ await toolExecutionQueries.complete(exec6.id, result);
9357
8796
  return result;
9358
8797
  } catch (error) {
9359
- await toolExecutionQueries.complete(exec7.id, null, error.message);
8798
+ await toolExecutionQueries.complete(exec6.id, null, error.message);
9360
8799
  throw error;
9361
8800
  }
9362
8801
  }