sparkecoder 0.1.86 → 0.1.87

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 (112) hide show
  1. package/dist/agent/index.d.ts +1 -1
  2. package/dist/agent/index.js +661 -39
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +1996 -225
  5. package/dist/cli.js.map +1 -1
  6. package/dist/{index-OhuTM4a0.d.ts → index-BvIissiB.d.ts} +9 -0
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.js +1683 -199
  9. package/dist/index.js.map +1 -1
  10. package/dist/server/index.js +1683 -199
  11. package/dist/server/index.js.map +1 -1
  12. package/dist/skills/default/computer-use.md +150 -0
  13. package/dist/tools/index.d.ts +167 -1
  14. package/dist/tools/index.js +604 -10
  15. package/dist/tools/index.js.map +1 -1
  16. package/package.json +2 -1
  17. package/src/skills/default/computer-use.md +150 -0
  18. package/web/.next/BUILD_ID +1 -1
  19. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  20. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  21. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  22. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  23. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  24. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  25. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
  38. package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
  39. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  40. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  58. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  60. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
  75. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
  76. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  77. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  78. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  79. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_ecd2bdca._.js → 2374f_317b1fef._.js} +1 -1
  85. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_9adc1edb._.js → 2374f_37dd9702._.js} +1 -1
  86. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_8dc0f9aa._.js → 2374f_4d44e4ed._.js} +1 -1
  87. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_cc6c6363._.js → 2374f_54ac917f._.js} +1 -1
  88. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_00f7fe07._.js → 2374f_86585101._.js} +1 -1
  89. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_369747ce._.js → 2374f_a383a4d9._.js} +1 -1
  90. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d58d0276._.js → 2374f_c59a35bb._.js} +1 -1
  91. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__25b25c9d._.js → [root-of-the-server]__9a826344._.js} +2 -2
  92. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  93. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  94. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  95. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  96. package/web/.next/standalone/web/.next/static/chunks/275e8268daf318b2.js +7 -0
  97. package/web/.next/standalone/web/.next/static/static/chunks/275e8268daf318b2.js +7 -0
  98. package/web/.next/standalone/web/src/app/embed/[id]/page.tsx +12 -0
  99. package/web/.next/standalone/web/src/lib/embed-bootstrap.ts +108 -0
  100. package/web/.next/static/chunks/275e8268daf318b2.js +7 -0
  101. package/web/.next/standalone/web/.next/static/chunks/5383c5717758f575.js +0 -7
  102. package/web/.next/standalone/web/.next/static/static/chunks/5383c5717758f575.js +0 -7
  103. package/web/.next/static/chunks/5383c5717758f575.js +0 -7
  104. /package/web/.next/standalone/web/.next/static/{Pt6kwIO6lniM7h7I5E6kk → static/uUaN7Xe5kF_pP6zhfaeYi}/_buildManifest.js +0 -0
  105. /package/web/.next/standalone/web/.next/static/{Pt6kwIO6lniM7h7I5E6kk → static/uUaN7Xe5kF_pP6zhfaeYi}/_clientMiddlewareManifest.json +0 -0
  106. /package/web/.next/standalone/web/.next/static/{Pt6kwIO6lniM7h7I5E6kk → static/uUaN7Xe5kF_pP6zhfaeYi}/_ssgManifest.js +0 -0
  107. /package/web/.next/standalone/web/.next/static/{static/Pt6kwIO6lniM7h7I5E6kk → uUaN7Xe5kF_pP6zhfaeYi}/_buildManifest.js +0 -0
  108. /package/web/.next/standalone/web/.next/static/{static/Pt6kwIO6lniM7h7I5E6kk → uUaN7Xe5kF_pP6zhfaeYi}/_clientMiddlewareManifest.json +0 -0
  109. /package/web/.next/standalone/web/.next/static/{static/Pt6kwIO6lniM7h7I5E6kk → uUaN7Xe5kF_pP6zhfaeYi}/_ssgManifest.js +0 -0
  110. /package/web/.next/static/{Pt6kwIO6lniM7h7I5E6kk → uUaN7Xe5kF_pP6zhfaeYi}/_buildManifest.js +0 -0
  111. /package/web/.next/static/{Pt6kwIO6lniM7h7I5E6kk → uUaN7Xe5kF_pP6zhfaeYi}/_clientMiddlewareManifest.json +0 -0
  112. /package/web/.next/static/{Pt6kwIO6lniM7h7I5E6kk → uUaN7Xe5kF_pP6zhfaeYi}/_ssgManifest.js +0 -0
@@ -27,7 +27,12 @@ var init_types = __esm({
27
27
  // Whether to always inject this skill into context (vs on-demand loading)
28
28
  alwaysApply: z.boolean().optional().default(false),
29
29
  // Glob patterns - auto-inject when working with matching files
30
- globs: z.array(z.string()).optional().default([])
30
+ globs: z.array(z.string()).optional().default([]),
31
+ // Platform requirements — skill is hidden from the model on platforms
32
+ // not listed here. Values match `process.platform`
33
+ // (darwin, linux, win32, freebsd, ...). If omitted or empty, the skill is
34
+ // available on all platforms.
35
+ platforms: z.array(z.string()).optional().default([])
31
36
  });
32
37
  TaskConfigSchema = z.object({
33
38
  enabled: z.boolean(),
@@ -45,7 +50,13 @@ var init_types = __esm({
45
50
  approvalWebhook: z.string().url().optional(),
46
51
  skillsDirectory: z.string().optional(),
47
52
  maxContextChars: z.number().optional().default(2e5),
48
- task: TaskConfigSchema.optional()
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()
49
60
  });
50
61
  VectorGatewayConfigSchema = z.object({
51
62
  // Redis cluster nodes URL for Vector Gateway (or use REDIS_CLUSTER_NODES env var)
@@ -639,12 +650,13 @@ function getDb() {
639
650
  }
640
651
  return {};
641
652
  }
642
- var initialized, todoQueries, skillQueries, fileBackupQueries, subagentQueries, indexStatusQueries;
653
+ var initialized, sessionQueries, todoQueries, skillQueries, fileBackupQueries, subagentQueries, indexStatusQueries;
643
654
  var init_db = __esm({
644
655
  "src/db/index.ts"() {
645
656
  "use strict";
646
657
  init_remote();
647
658
  initialized = false;
659
+ sessionQueries = remoteSessionQueries;
648
660
  todoQueries = remoteTodoQueries;
649
661
  skillQueries = remoteSkillQueries;
650
662
  fileBackupQueries = remoteFileBackupQueries;
@@ -1932,12 +1944,12 @@ function findNearestRoot(startDir, markers) {
1932
1944
  }
1933
1945
  async function commandExists(cmd) {
1934
1946
  try {
1935
- const { exec: exec5 } = await import("child_process");
1936
- const { promisify: promisify5 } = await import("util");
1937
- const execAsync5 = promisify5(exec5);
1947
+ const { exec: exec6 } = await import("child_process");
1948
+ const { promisify: promisify6 } = await import("util");
1949
+ const execAsync6 = promisify6(exec6);
1938
1950
  const isWindows = process.platform === "win32";
1939
1951
  const checkCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
1940
- await execAsync5(checkCmd);
1952
+ await execAsync6(checkCmd);
1941
1953
  return true;
1942
1954
  } catch {
1943
1955
  return false;
@@ -3206,7 +3218,8 @@ async function loadSkillsFromDirectory(directory, options = {}) {
3206
3218
  globs: parsed.metadata.globs,
3207
3219
  loadType,
3208
3220
  priority,
3209
- sourceDir: directory
3221
+ sourceDir: directory,
3222
+ platforms: parsed.metadata.platforms
3210
3223
  });
3211
3224
  } else {
3212
3225
  const name = getSkillNameFromPath(filePath);
@@ -3219,11 +3232,14 @@ async function loadSkillsFromDirectory(directory, options = {}) {
3219
3232
  globs: [],
3220
3233
  loadType: forceAlwaysApply ? "always" : defaultLoadType,
3221
3234
  priority,
3222
- sourceDir: directory
3235
+ sourceDir: directory,
3236
+ platforms: []
3223
3237
  });
3224
3238
  }
3225
3239
  }
3226
- return skills;
3240
+ return skills.filter(
3241
+ (s) => s.platforms.length === 0 || s.platforms.includes(process.platform)
3242
+ );
3227
3243
  }
3228
3244
  async function loadAllSkills(directories) {
3229
3245
  const allSkills = [];
@@ -5041,6 +5057,568 @@ function createUploadFileTool(options) {
5041
5057
  });
5042
5058
  }
5043
5059
 
5060
+ // src/tools/computer-use.ts
5061
+ import { anthropic } from "@ai-sdk/anthropic";
5062
+ import { exec as exec5 } from "child_process";
5063
+ import { promisify as promisify5 } from "util";
5064
+ import { mkdirSync as mkdirSync5, existsSync as existsSync15, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
5065
+ import { join as join8 } from "path";
5066
+ import { tmpdir } from "os";
5067
+ import { nanoid as nanoid3 } from "nanoid";
5068
+ var execAsync5 = promisify5(exec5);
5069
+ var DEFAULT_WIDTH = 1280;
5070
+ var DEFAULT_HEIGHT = 800;
5071
+ function isMacOs() {
5072
+ return process.platform === "darwin";
5073
+ }
5074
+ async function isCliclickInstalled() {
5075
+ try {
5076
+ await execAsync5("command -v cliclick", { timeout: 2e3 });
5077
+ return true;
5078
+ } catch {
5079
+ return false;
5080
+ }
5081
+ }
5082
+ async function runJxa(script) {
5083
+ try {
5084
+ const escaped = script.replace(/'/g, `'\\''`);
5085
+ const { stdout } = await execAsync5(`osascript -l JavaScript -e '${escaped}'`, {
5086
+ timeout: 5e3
5087
+ });
5088
+ return JSON.parse(stdout.trim());
5089
+ } catch {
5090
+ return null;
5091
+ }
5092
+ }
5093
+ async function hasAccessibilityPermissions() {
5094
+ try {
5095
+ const { stderr } = await execAsync5("cliclick p:.", { timeout: 3e3 });
5096
+ if (/accessibility privileges not enabled/i.test(stderr)) {
5097
+ return { ok: false, error: stderr.trim().split("\n")[0] };
5098
+ }
5099
+ return { ok: true };
5100
+ } catch (err) {
5101
+ return { ok: false, error: err?.message || String(err) };
5102
+ }
5103
+ }
5104
+ async function hasScreenRecordingPermissions() {
5105
+ const result = await runJxa(
5106
+ `ObjC.import("Cocoa");
5107
+ ObjC.import("CoreGraphics");
5108
+ ObjC.bindFunction("CGPreflightScreenCaptureAccess", ["bool", []]);
5109
+ JSON.stringify({ hasAccess: !!$.CGPreflightScreenCaptureAccess() });`
5110
+ );
5111
+ return result?.hasAccess ?? false;
5112
+ }
5113
+ async function requestAccessibilityPrompt() {
5114
+ const result = await runJxa(
5115
+ `ObjC.import("ApplicationServices");
5116
+ var key = $.kAXTrustedCheckOptionPrompt;
5117
+ var dict = $.NSDictionary.dictionaryWithObjectForKey($.kCFBooleanTrue, key);
5118
+ var trusted = $.AXIsProcessTrustedWithOptions(dict);
5119
+ JSON.stringify({ trusted: !!trusted });`
5120
+ );
5121
+ return result?.trusted ?? false;
5122
+ }
5123
+ async function requestScreenRecordingPrompt() {
5124
+ const result = await runJxa(
5125
+ `ObjC.import("Cocoa");
5126
+ ObjC.import("CoreGraphics");
5127
+ ObjC.bindFunction("CGRequestScreenCaptureAccess", ["bool", []]);
5128
+ JSON.stringify({ granted: !!$.CGRequestScreenCaptureAccess() });`
5129
+ );
5130
+ return result?.granted ?? false;
5131
+ }
5132
+ async function openSystemSettings(pane) {
5133
+ const url = pane === "accessibility" ? "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" : "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
5134
+ try {
5135
+ await execAsync5(`open '${url}'`, { timeout: 3e3 });
5136
+ } catch {
5137
+ }
5138
+ }
5139
+ async function detectScreenSize() {
5140
+ try {
5141
+ const { stdout } = await execAsync5(
5142
+ `osascript -e 'tell application "Finder" to get bounds of window of desktop'`,
5143
+ { timeout: 3e3 }
5144
+ );
5145
+ const parts = stdout.trim().split(",").map((s) => parseInt(s.trim(), 10));
5146
+ if (parts.length >= 4 && parts.every((n) => Number.isFinite(n))) {
5147
+ const [x1, y1, x2, y2] = parts;
5148
+ return { width: x2 - x1, height: y2 - y1 };
5149
+ }
5150
+ } catch {
5151
+ }
5152
+ return null;
5153
+ }
5154
+ async function runCliclick(args) {
5155
+ const quoted = args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(" ");
5156
+ const { stdout, stderr } = await execAsync5(`cliclick ${quoted}`, {
5157
+ timeout: 15e3,
5158
+ maxBuffer: 1024 * 1024
5159
+ });
5160
+ if (/accessibility privileges not enabled/i.test(stderr)) {
5161
+ throw new Error(
5162
+ "Accessibility permissions not granted to cliclick. Open System Settings \u2192 Privacy & Security \u2192 Accessibility, add cliclick (or the agent runtime), and toggle it on."
5163
+ );
5164
+ }
5165
+ if (stderr && !stdout) throw new Error(stderr.trim());
5166
+ return (stdout || "").trim();
5167
+ }
5168
+ async function runScreencapture(path) {
5169
+ await execAsync5(`screencapture -x -t png '${path.replace(/'/g, `'\\''`)}'`, {
5170
+ timeout: 5e3
5171
+ });
5172
+ }
5173
+ async function resizeScreenshotToPoints(path, targetWidth, targetHeight) {
5174
+ const sharpModule = await import("sharp");
5175
+ const sharp2 = sharpModule.default || sharpModule;
5176
+ const meta = await sharp2(path).metadata();
5177
+ if (meta.width === targetWidth && meta.height === targetHeight) {
5178
+ return readFileSync7(path);
5179
+ }
5180
+ return await sharp2(path).resize(targetWidth, targetHeight, { fit: "fill" }).png().toBuffer();
5181
+ }
5182
+ async function runScroll(dx, dy) {
5183
+ const wheelY = -Math.round(dy);
5184
+ const wheelX = -Math.round(dx);
5185
+ const script = `ObjC.import('CoreGraphics');var ev = $.CGEventCreateScrollWheelEvent(null, 0, 2, ${wheelY}, ${wheelX});$.CGEventPost(0, ev);`;
5186
+ await execAsync5(
5187
+ `osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
5188
+ { timeout: 5e3 }
5189
+ );
5190
+ }
5191
+ function translateKeyForCliclick(key) {
5192
+ if (!key) return [];
5193
+ const parts = key.split("+").map((p) => p.trim()).filter(Boolean);
5194
+ if (parts.length === 0) return [];
5195
+ const modMap = {
5196
+ ctrl: "ctrl",
5197
+ control: "ctrl",
5198
+ alt: "alt",
5199
+ option: "alt",
5200
+ shift: "shift",
5201
+ cmd: "cmd",
5202
+ super: "cmd",
5203
+ meta: "cmd",
5204
+ win: "cmd",
5205
+ fn: "fn"
5206
+ };
5207
+ const keyMap = {
5208
+ return: "enter",
5209
+ enter: "enter",
5210
+ esc: "esc",
5211
+ escape: "esc",
5212
+ backspace: "delete",
5213
+ back_space: "delete",
5214
+ delete: "fwd-delete",
5215
+ fwd_delete: "fwd-delete",
5216
+ forward_delete: "fwd-delete",
5217
+ tab: "tab",
5218
+ space: "space",
5219
+ up: "arrow-up",
5220
+ arrow_up: "arrow-up",
5221
+ down: "arrow-down",
5222
+ arrow_down: "arrow-down",
5223
+ left: "arrow-left",
5224
+ arrow_left: "arrow-left",
5225
+ right: "arrow-right",
5226
+ arrow_right: "arrow-right",
5227
+ page_up: "page-up",
5228
+ pageup: "page-up",
5229
+ page_down: "page-down",
5230
+ pagedown: "page-down",
5231
+ home: "home",
5232
+ end: "end",
5233
+ f1: "f1",
5234
+ f2: "f2",
5235
+ f3: "f3",
5236
+ f4: "f4",
5237
+ f5: "f5",
5238
+ f6: "f6",
5239
+ f7: "f7",
5240
+ f8: "f8",
5241
+ f9: "f9",
5242
+ f10: "f10",
5243
+ f11: "f11",
5244
+ f12: "f12"
5245
+ };
5246
+ const modifiers = [];
5247
+ let mainKey = null;
5248
+ for (let i = 0; i < parts.length; i++) {
5249
+ const lower = parts[i].toLowerCase().replace(/-/g, "_");
5250
+ if (i < parts.length - 1 && modMap[lower]) {
5251
+ modifiers.push(modMap[lower]);
5252
+ } else {
5253
+ mainKey = keyMap[lower] || lower;
5254
+ }
5255
+ }
5256
+ const args = [];
5257
+ if (modifiers.length > 0) args.push(`kd:${modifiers.join(",")}`);
5258
+ if (mainKey) {
5259
+ const isNamedKey = Object.values(keyMap).includes(mainKey) || /^f([1-9]|1[0-9]|20)$/.test(mainKey) || /^num-/.test(mainKey);
5260
+ if (isNamedKey) {
5261
+ args.push(`kp:${mainKey}`);
5262
+ } else {
5263
+ args.push(`t:${mainKey}`);
5264
+ }
5265
+ }
5266
+ if (modifiers.length > 0) args.push(`ku:${modifiers.join(",")}`);
5267
+ return args;
5268
+ }
5269
+ function modifierStringToCliclick(text) {
5270
+ return text.split("+").map((p) => p.trim().toLowerCase()).map((p) => {
5271
+ if (p === "ctrl" || p === "control") return "ctrl";
5272
+ if (p === "alt" || p === "option") return "alt";
5273
+ if (p === "shift") return "shift";
5274
+ if (p === "super" || p === "meta" || p === "cmd") return "cmd";
5275
+ return "";
5276
+ }).filter(Boolean);
5277
+ }
5278
+ function createComputerUseTool(options) {
5279
+ const displayWidth = options.displayWidth ?? DEFAULT_WIDTH;
5280
+ const displayHeight = options.displayHeight ?? DEFAULT_HEIGHT;
5281
+ return anthropic.tools.computer_20251124({
5282
+ displayWidthPx: displayWidth,
5283
+ displayHeightPx: displayHeight,
5284
+ enableZoom: true,
5285
+ execute: async (input) => {
5286
+ try {
5287
+ switch (input.action) {
5288
+ case "screenshot": {
5289
+ const path = join8(tmpdir(), `cu-${nanoid3(8)}.png`);
5290
+ await runScreencapture(path);
5291
+ const resized = await resizeScreenshotToPoints(path, displayWidth, displayHeight);
5292
+ try {
5293
+ unlinkSync2(path);
5294
+ } catch {
5295
+ }
5296
+ return { type: "image", data: resized.toString("base64") };
5297
+ }
5298
+ case "left_click": {
5299
+ const [x, y] = input.coordinate ?? [0, 0];
5300
+ if (input.text) {
5301
+ const mods = modifierStringToCliclick(input.text);
5302
+ if (mods.length > 0) {
5303
+ await runCliclick([`kd:${mods.join(",")}`, `c:${x},${y}`, `ku:${mods.join(",")}`]);
5304
+ } else {
5305
+ await runCliclick([`c:${x},${y}`]);
5306
+ }
5307
+ } else {
5308
+ await runCliclick([`c:${x},${y}`]);
5309
+ }
5310
+ return `clicked at (${x}, ${y})${input.text ? ` with ${input.text}` : ""}`;
5311
+ }
5312
+ case "right_click": {
5313
+ const [x, y] = input.coordinate ?? [0, 0];
5314
+ await runCliclick([`rc:${x},${y}`]);
5315
+ return `right-clicked at (${x}, ${y})`;
5316
+ }
5317
+ case "middle_click": {
5318
+ const [x, y] = input.coordinate ?? [0, 0];
5319
+ 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);`;
5320
+ await execAsync5(
5321
+ `osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
5322
+ { timeout: 3e3 }
5323
+ );
5324
+ return `middle-clicked at (${x}, ${y})`;
5325
+ }
5326
+ case "double_click": {
5327
+ const [x, y] = input.coordinate ?? [0, 0];
5328
+ await runCliclick([`dc:${x},${y}`]);
5329
+ return `double-clicked at (${x}, ${y})`;
5330
+ }
5331
+ case "triple_click": {
5332
+ const [x, y] = input.coordinate ?? [0, 0];
5333
+ await runCliclick([`tc:${x},${y}`]);
5334
+ return `triple-clicked at (${x}, ${y})`;
5335
+ }
5336
+ case "mouse_move": {
5337
+ const [x, y] = input.coordinate ?? [0, 0];
5338
+ await runCliclick([`m:${x},${y}`]);
5339
+ return `moved cursor to (${x}, ${y})`;
5340
+ }
5341
+ case "left_mouse_down": {
5342
+ const [x, y] = input.coordinate ?? [0, 0];
5343
+ await runCliclick([`dd:${x},${y}`]);
5344
+ return `left mouse button pressed at (${x}, ${y})`;
5345
+ }
5346
+ case "left_mouse_up": {
5347
+ const [x, y] = input.coordinate ?? [0, 0];
5348
+ await runCliclick([`du:${x},${y}`]);
5349
+ return `left mouse button released at (${x}, ${y})`;
5350
+ }
5351
+ case "left_click_drag": {
5352
+ const [sx, sy] = input.start_coordinate ?? [0, 0];
5353
+ const [ex, ey] = input.coordinate ?? [0, 0];
5354
+ await runCliclick([`dd:${sx},${sy}`, `m:${ex},${ey}`, `du:${ex},${ey}`]);
5355
+ return `dragged from (${sx}, ${sy}) to (${ex}, ${ey})`;
5356
+ }
5357
+ case "type": {
5358
+ const text = input.text ?? "";
5359
+ await runCliclick([`t:${text}`]);
5360
+ return `typed ${text.length} character(s)`;
5361
+ }
5362
+ case "key": {
5363
+ const args = translateKeyForCliclick(input.text ?? "");
5364
+ if (args.length === 0) return "no key specified";
5365
+ await runCliclick(args);
5366
+ return `pressed ${input.text}`;
5367
+ }
5368
+ case "hold_key": {
5369
+ const text = (input.text ?? "").toLowerCase();
5370
+ const duration = input.duration ?? 1;
5371
+ const modMap = {
5372
+ ctrl: "ctrl",
5373
+ control: "ctrl",
5374
+ alt: "alt",
5375
+ option: "alt",
5376
+ shift: "shift",
5377
+ cmd: "cmd",
5378
+ super: "cmd",
5379
+ meta: "cmd",
5380
+ fn: "fn"
5381
+ };
5382
+ const cliName = modMap[text] || text;
5383
+ await runCliclick([`kd:${cliName}`]);
5384
+ await new Promise((r) => setTimeout(r, duration * 1e3));
5385
+ await runCliclick([`ku:${cliName}`]);
5386
+ return `held ${text} for ${duration}s`;
5387
+ }
5388
+ case "scroll": {
5389
+ const direction = input.scroll_direction ?? "down";
5390
+ const amount = input.scroll_amount ?? 3;
5391
+ const px = amount * 100;
5392
+ const dx = direction === "left" ? -px : direction === "right" ? px : 0;
5393
+ const dy = direction === "up" ? -px : direction === "down" ? px : 0;
5394
+ if (input.coordinate) {
5395
+ const [x, y] = input.coordinate;
5396
+ await runCliclick([`m:${x},${y}`]);
5397
+ }
5398
+ const mods = input.text ? modifierStringToCliclick(input.text) : [];
5399
+ if (mods.length > 0) {
5400
+ await runCliclick([`kd:${mods.join(",")}`]);
5401
+ }
5402
+ await runScroll(dx, dy);
5403
+ if (mods.length > 0) {
5404
+ await runCliclick([`ku:${mods.join(",")}`]);
5405
+ }
5406
+ return `scrolled ${direction} by ${amount}`;
5407
+ }
5408
+ case "wait": {
5409
+ const duration = input.duration ?? 1;
5410
+ await new Promise((r) => setTimeout(r, duration * 1e3));
5411
+ return `waited ${duration}s`;
5412
+ }
5413
+ case "cursor_position": {
5414
+ const out = await runCliclick(["p:."]);
5415
+ return `cursor at ${out}`;
5416
+ }
5417
+ case "zoom": {
5418
+ const region = input.region ?? [0, 0, displayWidth, displayHeight];
5419
+ const [x1, y1, x2, y2] = region;
5420
+ const tmpPath = join8(tmpdir(), `cu-zoom-${nanoid3(8)}.png`);
5421
+ await runScreencapture(tmpPath);
5422
+ const sharpModule = await import("sharp");
5423
+ const sharp2 = sharpModule.default || sharpModule;
5424
+ const meta = await sharp2(tmpPath).metadata();
5425
+ const scaleX = (meta.width || displayWidth) / displayWidth;
5426
+ const scaleY = (meta.height || displayHeight) / displayHeight;
5427
+ const px = {
5428
+ left: Math.max(0, Math.round(x1 * scaleX)),
5429
+ top: Math.max(0, Math.round(y1 * scaleY)),
5430
+ width: Math.max(1, Math.round((x2 - x1) * scaleX)),
5431
+ height: Math.max(1, Math.round((y2 - y1) * scaleY))
5432
+ };
5433
+ const buf = await sharp2(tmpPath).extract(px).png().toBuffer();
5434
+ try {
5435
+ unlinkSync2(tmpPath);
5436
+ } catch {
5437
+ }
5438
+ return { type: "image", data: buf.toString("base64") };
5439
+ }
5440
+ default: {
5441
+ const exhaustive = input.action;
5442
+ return `unsupported action: ${String(exhaustive)}`;
5443
+ }
5444
+ }
5445
+ } catch (err) {
5446
+ const msg = err?.message || String(err);
5447
+ let hint = "";
5448
+ if (/accessibility|not authorized|tcc|operation not permitted/i.test(msg)) {
5449
+ hint = " (Hint: call enable_computer_use to (re-)check permissions and open System Settings)";
5450
+ } else if (/command not found/i.test(msg)) {
5451
+ hint = " (Hint: install cliclick with `brew install cliclick`)";
5452
+ }
5453
+ return `Error: ${msg}${hint}`;
5454
+ }
5455
+ },
5456
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5457
+ toModelOutput({ output }) {
5458
+ if (typeof output === "string") {
5459
+ return { type: "content", value: [{ type: "text", text: output }] };
5460
+ }
5461
+ return {
5462
+ type: "content",
5463
+ value: [{ type: "media", data: output.data, mediaType: "image/png" }]
5464
+ };
5465
+ }
5466
+ });
5467
+ }
5468
+
5469
+ // src/tools/enable-computer-use.ts
5470
+ init_db();
5471
+ import { tool as tool13 } from "ai";
5472
+ import { z as z14 } from "zod";
5473
+ var inputSchema = z14.object({
5474
+ display_width: z14.number().int().positive().optional().describe("Display width in pixels (defaults to detected primary display, fallback 1280)"),
5475
+ display_height: z14.number().int().positive().optional().describe("Display height in pixels (defaults to detected primary display, fallback 800)"),
5476
+ request_permissions: z14.boolean().optional().default(true).describe(
5477
+ "When true (default), proactively trigger macOS permission prompts and open System Settings panes for any missing permissions."
5478
+ )
5479
+ });
5480
+ var ACCESSIBILITY_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
5481
+ var SCREEN_RECORDING_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
5482
+ function createEnableComputerUseTool(options) {
5483
+ return tool13({
5484
+ 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.",
5485
+ inputSchema,
5486
+ execute: async ({ display_width, display_height, request_permissions }) => {
5487
+ try {
5488
+ if (!isMacOs()) {
5489
+ return {
5490
+ success: false,
5491
+ error: "Computer use is currently only supported on macOS.",
5492
+ platform: process.platform
5493
+ };
5494
+ }
5495
+ if (!await isCliclickInstalled()) {
5496
+ return {
5497
+ success: false,
5498
+ error: "`cliclick` is not installed. It is required for mouse/keyboard control on macOS.",
5499
+ installCommand: "brew install cliclick",
5500
+ fixSteps: [
5501
+ "In a terminal on this Mac, run: brew install cliclick",
5502
+ "(If Homebrew is not installed, install it first from https://brew.sh)",
5503
+ "Then call enable_computer_use again"
5504
+ ]
5505
+ };
5506
+ }
5507
+ const acc = await hasAccessibilityPermissions();
5508
+ const screen = await hasScreenRecordingPermissions();
5509
+ const missing = [];
5510
+ if (!acc.ok) {
5511
+ let prompted = false;
5512
+ let panelOpened = false;
5513
+ if (request_permissions) {
5514
+ prompted = await requestAccessibilityPrompt().then(() => true).catch(() => false);
5515
+ await openSystemSettings("accessibility").then(() => {
5516
+ panelOpened = true;
5517
+ }).catch(() => void 0);
5518
+ }
5519
+ missing.push({
5520
+ name: "Accessibility",
5521
+ reason: "cliclick failed: " + (acc.error?.split("\n")[0] || "no permission"),
5522
+ pane: "accessibility",
5523
+ settingsUrl: ACCESSIBILITY_URL,
5524
+ fixSteps: [
5525
+ "In the System Settings \u2192 Privacy & Security \u2192 Accessibility pane that opened",
5526
+ "Click the + button",
5527
+ "Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
5528
+ "Toggle the switch ON",
5529
+ "Restart the agent process so the new permission takes effect",
5530
+ "Then call enable_computer_use again"
5531
+ ],
5532
+ prompted,
5533
+ panelOpened
5534
+ });
5535
+ }
5536
+ if (!screen) {
5537
+ let prompted = false;
5538
+ let panelOpened = false;
5539
+ if (request_permissions) {
5540
+ prompted = await requestScreenRecordingPrompt().then(() => true).catch(() => false);
5541
+ await openSystemSettings("screen-recording").then(() => {
5542
+ panelOpened = true;
5543
+ }).catch(() => void 0);
5544
+ }
5545
+ missing.push({
5546
+ name: "Screen Recording",
5547
+ reason: "CGPreflightScreenCaptureAccess returned false",
5548
+ pane: "screen-recording",
5549
+ settingsUrl: SCREEN_RECORDING_URL,
5550
+ fixSteps: [
5551
+ "In the System Settings \u2192 Privacy & Security \u2192 Screen Recording pane that opened",
5552
+ "Click the + button",
5553
+ "Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
5554
+ "Toggle the switch ON",
5555
+ "Restart the agent process so the new permission takes effect",
5556
+ "Then call enable_computer_use again"
5557
+ ],
5558
+ prompted,
5559
+ panelOpened
5560
+ });
5561
+ }
5562
+ if (missing.length > 0) {
5563
+ return {
5564
+ success: false,
5565
+ error: `Missing permission${missing.length > 1 ? "s" : ""}: ` + missing.map((m) => m.name).join(" and ") + ".",
5566
+ missingPermissions: missing,
5567
+ 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."
5568
+ };
5569
+ }
5570
+ let width = display_width;
5571
+ let height = display_height;
5572
+ let detected = null;
5573
+ if (width === void 0 || height === void 0) {
5574
+ detected = await detectScreenSize();
5575
+ width = width ?? detected?.width ?? 1280;
5576
+ height = height ?? detected?.height ?? 800;
5577
+ }
5578
+ const session = await sessionQueries.getById(options.sessionId);
5579
+ if (!session) {
5580
+ return { success: false, error: "Session not found" };
5581
+ }
5582
+ const config = session.config || {};
5583
+ if (config.computerUseEnabled === true && config.computerUseDisplayWidth === width && config.computerUseDisplayHeight === height) {
5584
+ return {
5585
+ success: true,
5586
+ alreadyEnabled: true,
5587
+ message: "Computer use was already enabled for this session.",
5588
+ displayWidth: width,
5589
+ displayHeight: height
5590
+ };
5591
+ }
5592
+ const updated = {
5593
+ ...config,
5594
+ computerUseEnabled: true,
5595
+ computerUseDisplayWidth: width,
5596
+ computerUseDisplayHeight: height
5597
+ };
5598
+ await sessionQueries.update(options.sessionId, { config: updated });
5599
+ return {
5600
+ success: true,
5601
+ enabled: true,
5602
+ platform: "darwin",
5603
+ displayWidth: width,
5604
+ displayHeight: height,
5605
+ detectedScreenSize: detected || void 0,
5606
+ permissions: {
5607
+ accessibility: "granted",
5608
+ screenRecording: "granted"
5609
+ },
5610
+ 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.`
5611
+ };
5612
+ } catch (err) {
5613
+ return {
5614
+ success: false,
5615
+ error: err?.message || String(err)
5616
+ };
5617
+ }
5618
+ }
5619
+ });
5620
+ }
5621
+
5044
5622
  // src/tools/index.ts
5045
5623
  init_semantic();
5046
5624
  init_remote();
@@ -5088,6 +5666,20 @@ async function createTools(options) {
5088
5666
  sessionId: options.sessionId
5089
5667
  });
5090
5668
  }
5669
+ if (process.platform === "darwin") {
5670
+ if (options.enableComputerUse) {
5671
+ tools.computer = createComputerUseTool({
5672
+ workingDirectory: options.workingDirectory,
5673
+ sessionId: options.sessionId,
5674
+ displayWidth: options.computerUseDisplayWidth,
5675
+ displayHeight: options.computerUseDisplayHeight
5676
+ });
5677
+ } else {
5678
+ tools.enable_computer_use = createEnableComputerUseTool({
5679
+ sessionId: options.sessionId
5680
+ });
5681
+ }
5682
+ }
5091
5683
  if (options.enableSemanticSearch !== false) {
5092
5684
  try {
5093
5685
  if (isVectorGatewayConfigured()) {
@@ -5111,6 +5703,8 @@ export {
5111
5703
  createBashTool,
5112
5704
  createCodeGraphTool,
5113
5705
  createCompleteTaskTool,
5706
+ createComputerUseTool,
5707
+ createEnableComputerUseTool,
5114
5708
  createLinterTool,
5115
5709
  createLoadSkillTool,
5116
5710
  createReadFileTool,