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)
@@ -802,7 +813,8 @@ async function loadSkillsFromDirectory(directory, options = {}) {
802
813
  globs: parsed.metadata.globs,
803
814
  loadType,
804
815
  priority,
805
- sourceDir: directory
816
+ sourceDir: directory,
817
+ platforms: parsed.metadata.platforms
806
818
  });
807
819
  } else {
808
820
  const name = getSkillNameFromPath(filePath);
@@ -815,11 +827,14 @@ async function loadSkillsFromDirectory(directory, options = {}) {
815
827
  globs: [],
816
828
  loadType: forceAlwaysApply ? "always" : defaultLoadType,
817
829
  priority,
818
- sourceDir: directory
830
+ sourceDir: directory,
831
+ platforms: []
819
832
  });
820
833
  }
821
834
  }
822
- return skills;
835
+ return skills.filter(
836
+ (s) => s.platforms.length === 0 || s.platforms.includes(process.platform)
837
+ );
823
838
  }
824
839
  async function loadAllSkills(directories) {
825
840
  const allSkills = [];
@@ -1616,15 +1631,15 @@ var recorder_exports = {};
1616
1631
  __export(recorder_exports, {
1617
1632
  FrameRecorder: () => FrameRecorder
1618
1633
  });
1619
- import { exec as exec5 } from "child_process";
1620
- import { promisify as promisify5 } from "util";
1634
+ import { exec as exec6 } from "child_process";
1635
+ import { promisify as promisify6 } from "util";
1621
1636
  import { writeFile as writeFile5, mkdir as mkdir4, readFile as readFile11, unlink as unlink2, readdir as readdir5, rm } from "fs/promises";
1622
- import { join as join8 } from "path";
1623
- import { tmpdir } from "os";
1624
- import { nanoid as nanoid3 } from "nanoid";
1637
+ import { join as join9 } from "path";
1638
+ import { tmpdir as tmpdir2 } from "os";
1639
+ import { nanoid as nanoid4 } from "nanoid";
1625
1640
  async function checkFfmpeg() {
1626
1641
  try {
1627
- await execAsync5("ffmpeg -version", { timeout: 5e3 });
1642
+ await execAsync6("ffmpeg -version", { timeout: 5e3 });
1628
1643
  return true;
1629
1644
  } catch {
1630
1645
  return false;
@@ -1636,11 +1651,11 @@ async function cleanup(dir) {
1636
1651
  } catch {
1637
1652
  }
1638
1653
  }
1639
- var execAsync5, FrameRecorder;
1654
+ var execAsync6, FrameRecorder;
1640
1655
  var init_recorder = __esm({
1641
1656
  "src/browser/recorder.ts"() {
1642
1657
  "use strict";
1643
- execAsync5 = promisify5(exec5);
1658
+ execAsync6 = promisify6(exec6);
1644
1659
  FrameRecorder = class {
1645
1660
  frames = [];
1646
1661
  startTime = null;
@@ -1676,21 +1691,21 @@ var init_recorder = __esm({
1676
1691
  */
1677
1692
  async encode() {
1678
1693
  if (this.frames.length === 0) return null;
1679
- const workDir = join8(tmpdir(), `sparkecoder-recording-${nanoid3(8)}`);
1694
+ const workDir = join9(tmpdir2(), `sparkecoder-recording-${nanoid4(8)}`);
1680
1695
  await mkdir4(workDir, { recursive: true });
1681
1696
  try {
1682
1697
  for (let i = 0; i < this.frames.length; i++) {
1683
- const framePath = join8(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
1698
+ const framePath = join9(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
1684
1699
  await writeFile5(framePath, this.frames[i].data);
1685
1700
  }
1686
1701
  const duration = (this.frames[this.frames.length - 1].timestamp - this.frames[0].timestamp) / 1e3;
1687
1702
  const fps = duration > 0 ? Math.round(this.frames.length / duration) : 10;
1688
1703
  const clampedFps = Math.max(1, Math.min(fps, 30));
1689
- const outputPath = join8(workDir, `recording_${this.sessionId}.mp4`);
1704
+ const outputPath = join9(workDir, `recording_${this.sessionId}.mp4`);
1690
1705
  const hasFfmpeg = await checkFfmpeg();
1691
1706
  if (hasFfmpeg) {
1692
- await execAsync5(
1693
- `ffmpeg -y -framerate ${clampedFps} -i "${join8(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
1707
+ await execAsync6(
1708
+ `ffmpeg -y -framerate ${clampedFps} -i "${join9(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
1694
1709
  { timeout: 12e4 }
1695
1710
  );
1696
1711
  } else {
@@ -1702,7 +1717,7 @@ var init_recorder = __esm({
1702
1717
  const files = await readdir5(workDir);
1703
1718
  for (const f of files) {
1704
1719
  if (f.startsWith("frame_")) {
1705
- await unlink2(join8(workDir, f)).catch(() => {
1720
+ await unlink2(join9(workDir, f)).catch(() => {
1706
1721
  });
1707
1722
  }
1708
1723
  }
@@ -1727,7 +1742,7 @@ var init_recorder = __esm({
1727
1742
  import {
1728
1743
  streamText as streamText2,
1729
1744
  generateText as generateText3,
1730
- tool as tool13,
1745
+ tool as tool14,
1731
1746
  stepCountIs as stepCountIs2
1732
1747
  } from "ai";
1733
1748
 
@@ -1918,8 +1933,8 @@ var SUBAGENT_MODELS = {
1918
1933
  // src/agent/index.ts
1919
1934
  init_db();
1920
1935
  init_config();
1921
- import { z as z14 } from "zod";
1922
- import { nanoid as nanoid4 } from "nanoid";
1936
+ import { z as z15 } from "zod";
1937
+ import { nanoid as nanoid5 } from "nanoid";
1923
1938
 
1924
1939
  // src/tools/bash.ts
1925
1940
  import { tool } from "ai";
@@ -2802,12 +2817,12 @@ function findNearestRoot(startDir, markers) {
2802
2817
  }
2803
2818
  async function commandExists(cmd) {
2804
2819
  try {
2805
- const { exec: exec6 } = await import("child_process");
2806
- const { promisify: promisify6 } = await import("util");
2807
- const execAsync6 = promisify6(exec6);
2820
+ const { exec: exec7 } = await import("child_process");
2821
+ const { promisify: promisify7 } = await import("util");
2822
+ const execAsync7 = promisify7(exec7);
2808
2823
  const isWindows = process.platform === "win32";
2809
2824
  const checkCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
2810
- await execAsync6(checkCmd);
2825
+ await execAsync7(checkCmd);
2811
2826
  return true;
2812
2827
  } catch {
2813
2828
  return false;
@@ -5566,6 +5581,568 @@ function createUploadFileTool(options) {
5566
5581
  });
5567
5582
  }
5568
5583
 
5584
+ // src/tools/computer-use.ts
5585
+ import { anthropic } from "@ai-sdk/anthropic";
5586
+ import { exec as exec5 } from "child_process";
5587
+ import { promisify as promisify5 } from "util";
5588
+ import { mkdirSync as mkdirSync5, existsSync as existsSync15, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
5589
+ import { join as join8 } from "path";
5590
+ import { tmpdir } from "os";
5591
+ import { nanoid as nanoid3 } from "nanoid";
5592
+ var execAsync5 = promisify5(exec5);
5593
+ var DEFAULT_WIDTH = 1280;
5594
+ var DEFAULT_HEIGHT = 800;
5595
+ function isMacOs() {
5596
+ return process.platform === "darwin";
5597
+ }
5598
+ async function isCliclickInstalled() {
5599
+ try {
5600
+ await execAsync5("command -v cliclick", { timeout: 2e3 });
5601
+ return true;
5602
+ } catch {
5603
+ return false;
5604
+ }
5605
+ }
5606
+ async function runJxa(script) {
5607
+ try {
5608
+ const escaped = script.replace(/'/g, `'\\''`);
5609
+ const { stdout } = await execAsync5(`osascript -l JavaScript -e '${escaped}'`, {
5610
+ timeout: 5e3
5611
+ });
5612
+ return JSON.parse(stdout.trim());
5613
+ } catch {
5614
+ return null;
5615
+ }
5616
+ }
5617
+ async function hasAccessibilityPermissions() {
5618
+ try {
5619
+ const { stderr } = await execAsync5("cliclick p:.", { timeout: 3e3 });
5620
+ if (/accessibility privileges not enabled/i.test(stderr)) {
5621
+ return { ok: false, error: stderr.trim().split("\n")[0] };
5622
+ }
5623
+ return { ok: true };
5624
+ } catch (err) {
5625
+ return { ok: false, error: err?.message || String(err) };
5626
+ }
5627
+ }
5628
+ async function hasScreenRecordingPermissions() {
5629
+ const result = await runJxa(
5630
+ `ObjC.import("Cocoa");
5631
+ ObjC.import("CoreGraphics");
5632
+ ObjC.bindFunction("CGPreflightScreenCaptureAccess", ["bool", []]);
5633
+ JSON.stringify({ hasAccess: !!$.CGPreflightScreenCaptureAccess() });`
5634
+ );
5635
+ return result?.hasAccess ?? false;
5636
+ }
5637
+ async function requestAccessibilityPrompt() {
5638
+ const result = await runJxa(
5639
+ `ObjC.import("ApplicationServices");
5640
+ var key = $.kAXTrustedCheckOptionPrompt;
5641
+ var dict = $.NSDictionary.dictionaryWithObjectForKey($.kCFBooleanTrue, key);
5642
+ var trusted = $.AXIsProcessTrustedWithOptions(dict);
5643
+ JSON.stringify({ trusted: !!trusted });`
5644
+ );
5645
+ return result?.trusted ?? false;
5646
+ }
5647
+ async function requestScreenRecordingPrompt() {
5648
+ const result = await runJxa(
5649
+ `ObjC.import("Cocoa");
5650
+ ObjC.import("CoreGraphics");
5651
+ ObjC.bindFunction("CGRequestScreenCaptureAccess", ["bool", []]);
5652
+ JSON.stringify({ granted: !!$.CGRequestScreenCaptureAccess() });`
5653
+ );
5654
+ return result?.granted ?? false;
5655
+ }
5656
+ async function openSystemSettings(pane) {
5657
+ const url = pane === "accessibility" ? "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" : "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
5658
+ try {
5659
+ await execAsync5(`open '${url}'`, { timeout: 3e3 });
5660
+ } catch {
5661
+ }
5662
+ }
5663
+ async function detectScreenSize() {
5664
+ try {
5665
+ const { stdout } = await execAsync5(
5666
+ `osascript -e 'tell application "Finder" to get bounds of window of desktop'`,
5667
+ { timeout: 3e3 }
5668
+ );
5669
+ const parts = stdout.trim().split(",").map((s) => parseInt(s.trim(), 10));
5670
+ if (parts.length >= 4 && parts.every((n) => Number.isFinite(n))) {
5671
+ const [x1, y1, x2, y2] = parts;
5672
+ return { width: x2 - x1, height: y2 - y1 };
5673
+ }
5674
+ } catch {
5675
+ }
5676
+ return null;
5677
+ }
5678
+ async function runCliclick(args) {
5679
+ const quoted = args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(" ");
5680
+ const { stdout, stderr } = await execAsync5(`cliclick ${quoted}`, {
5681
+ timeout: 15e3,
5682
+ maxBuffer: 1024 * 1024
5683
+ });
5684
+ if (/accessibility privileges not enabled/i.test(stderr)) {
5685
+ throw new Error(
5686
+ "Accessibility permissions not granted to cliclick. Open System Settings \u2192 Privacy & Security \u2192 Accessibility, add cliclick (or the agent runtime), and toggle it on."
5687
+ );
5688
+ }
5689
+ if (stderr && !stdout) throw new Error(stderr.trim());
5690
+ return (stdout || "").trim();
5691
+ }
5692
+ async function runScreencapture(path) {
5693
+ await execAsync5(`screencapture -x -t png '${path.replace(/'/g, `'\\''`)}'`, {
5694
+ timeout: 5e3
5695
+ });
5696
+ }
5697
+ async function resizeScreenshotToPoints(path, targetWidth, targetHeight) {
5698
+ const sharpModule = await import("sharp");
5699
+ const sharp2 = sharpModule.default || sharpModule;
5700
+ const meta = await sharp2(path).metadata();
5701
+ if (meta.width === targetWidth && meta.height === targetHeight) {
5702
+ return readFileSync7(path);
5703
+ }
5704
+ return await sharp2(path).resize(targetWidth, targetHeight, { fit: "fill" }).png().toBuffer();
5705
+ }
5706
+ async function runScroll(dx, dy) {
5707
+ const wheelY = -Math.round(dy);
5708
+ const wheelX = -Math.round(dx);
5709
+ const script = `ObjC.import('CoreGraphics');var ev = $.CGEventCreateScrollWheelEvent(null, 0, 2, ${wheelY}, ${wheelX});$.CGEventPost(0, ev);`;
5710
+ await execAsync5(
5711
+ `osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
5712
+ { timeout: 5e3 }
5713
+ );
5714
+ }
5715
+ function translateKeyForCliclick(key) {
5716
+ if (!key) return [];
5717
+ const parts = key.split("+").map((p) => p.trim()).filter(Boolean);
5718
+ if (parts.length === 0) return [];
5719
+ const modMap = {
5720
+ ctrl: "ctrl",
5721
+ control: "ctrl",
5722
+ alt: "alt",
5723
+ option: "alt",
5724
+ shift: "shift",
5725
+ cmd: "cmd",
5726
+ super: "cmd",
5727
+ meta: "cmd",
5728
+ win: "cmd",
5729
+ fn: "fn"
5730
+ };
5731
+ const keyMap = {
5732
+ return: "enter",
5733
+ enter: "enter",
5734
+ esc: "esc",
5735
+ escape: "esc",
5736
+ backspace: "delete",
5737
+ back_space: "delete",
5738
+ delete: "fwd-delete",
5739
+ fwd_delete: "fwd-delete",
5740
+ forward_delete: "fwd-delete",
5741
+ tab: "tab",
5742
+ space: "space",
5743
+ up: "arrow-up",
5744
+ arrow_up: "arrow-up",
5745
+ down: "arrow-down",
5746
+ arrow_down: "arrow-down",
5747
+ left: "arrow-left",
5748
+ arrow_left: "arrow-left",
5749
+ right: "arrow-right",
5750
+ arrow_right: "arrow-right",
5751
+ page_up: "page-up",
5752
+ pageup: "page-up",
5753
+ page_down: "page-down",
5754
+ pagedown: "page-down",
5755
+ home: "home",
5756
+ end: "end",
5757
+ f1: "f1",
5758
+ f2: "f2",
5759
+ f3: "f3",
5760
+ f4: "f4",
5761
+ f5: "f5",
5762
+ f6: "f6",
5763
+ f7: "f7",
5764
+ f8: "f8",
5765
+ f9: "f9",
5766
+ f10: "f10",
5767
+ f11: "f11",
5768
+ f12: "f12"
5769
+ };
5770
+ const modifiers = [];
5771
+ let mainKey = null;
5772
+ for (let i = 0; i < parts.length; i++) {
5773
+ const lower = parts[i].toLowerCase().replace(/-/g, "_");
5774
+ if (i < parts.length - 1 && modMap[lower]) {
5775
+ modifiers.push(modMap[lower]);
5776
+ } else {
5777
+ mainKey = keyMap[lower] || lower;
5778
+ }
5779
+ }
5780
+ const args = [];
5781
+ if (modifiers.length > 0) args.push(`kd:${modifiers.join(",")}`);
5782
+ if (mainKey) {
5783
+ const isNamedKey = Object.values(keyMap).includes(mainKey) || /^f([1-9]|1[0-9]|20)$/.test(mainKey) || /^num-/.test(mainKey);
5784
+ if (isNamedKey) {
5785
+ args.push(`kp:${mainKey}`);
5786
+ } else {
5787
+ args.push(`t:${mainKey}`);
5788
+ }
5789
+ }
5790
+ if (modifiers.length > 0) args.push(`ku:${modifiers.join(",")}`);
5791
+ return args;
5792
+ }
5793
+ function modifierStringToCliclick(text) {
5794
+ return text.split("+").map((p) => p.trim().toLowerCase()).map((p) => {
5795
+ if (p === "ctrl" || p === "control") return "ctrl";
5796
+ if (p === "alt" || p === "option") return "alt";
5797
+ if (p === "shift") return "shift";
5798
+ if (p === "super" || p === "meta" || p === "cmd") return "cmd";
5799
+ return "";
5800
+ }).filter(Boolean);
5801
+ }
5802
+ function createComputerUseTool(options) {
5803
+ const displayWidth = options.displayWidth ?? DEFAULT_WIDTH;
5804
+ const displayHeight = options.displayHeight ?? DEFAULT_HEIGHT;
5805
+ return anthropic.tools.computer_20251124({
5806
+ displayWidthPx: displayWidth,
5807
+ displayHeightPx: displayHeight,
5808
+ enableZoom: true,
5809
+ execute: async (input) => {
5810
+ try {
5811
+ switch (input.action) {
5812
+ case "screenshot": {
5813
+ const path = join8(tmpdir(), `cu-${nanoid3(8)}.png`);
5814
+ await runScreencapture(path);
5815
+ const resized = await resizeScreenshotToPoints(path, displayWidth, displayHeight);
5816
+ try {
5817
+ unlinkSync2(path);
5818
+ } catch {
5819
+ }
5820
+ return { type: "image", data: resized.toString("base64") };
5821
+ }
5822
+ case "left_click": {
5823
+ const [x, y] = input.coordinate ?? [0, 0];
5824
+ if (input.text) {
5825
+ const mods = modifierStringToCliclick(input.text);
5826
+ if (mods.length > 0) {
5827
+ await runCliclick([`kd:${mods.join(",")}`, `c:${x},${y}`, `ku:${mods.join(",")}`]);
5828
+ } else {
5829
+ await runCliclick([`c:${x},${y}`]);
5830
+ }
5831
+ } else {
5832
+ await runCliclick([`c:${x},${y}`]);
5833
+ }
5834
+ return `clicked at (${x}, ${y})${input.text ? ` with ${input.text}` : ""}`;
5835
+ }
5836
+ case "right_click": {
5837
+ const [x, y] = input.coordinate ?? [0, 0];
5838
+ await runCliclick([`rc:${x},${y}`]);
5839
+ return `right-clicked at (${x}, ${y})`;
5840
+ }
5841
+ case "middle_click": {
5842
+ const [x, y] = input.coordinate ?? [0, 0];
5843
+ 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);`;
5844
+ await execAsync5(
5845
+ `osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
5846
+ { timeout: 3e3 }
5847
+ );
5848
+ return `middle-clicked at (${x}, ${y})`;
5849
+ }
5850
+ case "double_click": {
5851
+ const [x, y] = input.coordinate ?? [0, 0];
5852
+ await runCliclick([`dc:${x},${y}`]);
5853
+ return `double-clicked at (${x}, ${y})`;
5854
+ }
5855
+ case "triple_click": {
5856
+ const [x, y] = input.coordinate ?? [0, 0];
5857
+ await runCliclick([`tc:${x},${y}`]);
5858
+ return `triple-clicked at (${x}, ${y})`;
5859
+ }
5860
+ case "mouse_move": {
5861
+ const [x, y] = input.coordinate ?? [0, 0];
5862
+ await runCliclick([`m:${x},${y}`]);
5863
+ return `moved cursor to (${x}, ${y})`;
5864
+ }
5865
+ case "left_mouse_down": {
5866
+ const [x, y] = input.coordinate ?? [0, 0];
5867
+ await runCliclick([`dd:${x},${y}`]);
5868
+ return `left mouse button pressed at (${x}, ${y})`;
5869
+ }
5870
+ case "left_mouse_up": {
5871
+ const [x, y] = input.coordinate ?? [0, 0];
5872
+ await runCliclick([`du:${x},${y}`]);
5873
+ return `left mouse button released at (${x}, ${y})`;
5874
+ }
5875
+ case "left_click_drag": {
5876
+ const [sx, sy] = input.start_coordinate ?? [0, 0];
5877
+ const [ex, ey] = input.coordinate ?? [0, 0];
5878
+ await runCliclick([`dd:${sx},${sy}`, `m:${ex},${ey}`, `du:${ex},${ey}`]);
5879
+ return `dragged from (${sx}, ${sy}) to (${ex}, ${ey})`;
5880
+ }
5881
+ case "type": {
5882
+ const text = input.text ?? "";
5883
+ await runCliclick([`t:${text}`]);
5884
+ return `typed ${text.length} character(s)`;
5885
+ }
5886
+ case "key": {
5887
+ const args = translateKeyForCliclick(input.text ?? "");
5888
+ if (args.length === 0) return "no key specified";
5889
+ await runCliclick(args);
5890
+ return `pressed ${input.text}`;
5891
+ }
5892
+ case "hold_key": {
5893
+ const text = (input.text ?? "").toLowerCase();
5894
+ const duration = input.duration ?? 1;
5895
+ const modMap = {
5896
+ ctrl: "ctrl",
5897
+ control: "ctrl",
5898
+ alt: "alt",
5899
+ option: "alt",
5900
+ shift: "shift",
5901
+ cmd: "cmd",
5902
+ super: "cmd",
5903
+ meta: "cmd",
5904
+ fn: "fn"
5905
+ };
5906
+ const cliName = modMap[text] || text;
5907
+ await runCliclick([`kd:${cliName}`]);
5908
+ await new Promise((r) => setTimeout(r, duration * 1e3));
5909
+ await runCliclick([`ku:${cliName}`]);
5910
+ return `held ${text} for ${duration}s`;
5911
+ }
5912
+ case "scroll": {
5913
+ const direction = input.scroll_direction ?? "down";
5914
+ const amount = input.scroll_amount ?? 3;
5915
+ const px = amount * 100;
5916
+ const dx = direction === "left" ? -px : direction === "right" ? px : 0;
5917
+ const dy = direction === "up" ? -px : direction === "down" ? px : 0;
5918
+ if (input.coordinate) {
5919
+ const [x, y] = input.coordinate;
5920
+ await runCliclick([`m:${x},${y}`]);
5921
+ }
5922
+ const mods = input.text ? modifierStringToCliclick(input.text) : [];
5923
+ if (mods.length > 0) {
5924
+ await runCliclick([`kd:${mods.join(",")}`]);
5925
+ }
5926
+ await runScroll(dx, dy);
5927
+ if (mods.length > 0) {
5928
+ await runCliclick([`ku:${mods.join(",")}`]);
5929
+ }
5930
+ return `scrolled ${direction} by ${amount}`;
5931
+ }
5932
+ case "wait": {
5933
+ const duration = input.duration ?? 1;
5934
+ await new Promise((r) => setTimeout(r, duration * 1e3));
5935
+ return `waited ${duration}s`;
5936
+ }
5937
+ case "cursor_position": {
5938
+ const out = await runCliclick(["p:."]);
5939
+ return `cursor at ${out}`;
5940
+ }
5941
+ case "zoom": {
5942
+ const region = input.region ?? [0, 0, displayWidth, displayHeight];
5943
+ const [x1, y1, x2, y2] = region;
5944
+ const tmpPath = join8(tmpdir(), `cu-zoom-${nanoid3(8)}.png`);
5945
+ await runScreencapture(tmpPath);
5946
+ const sharpModule = await import("sharp");
5947
+ const sharp2 = sharpModule.default || sharpModule;
5948
+ const meta = await sharp2(tmpPath).metadata();
5949
+ const scaleX = (meta.width || displayWidth) / displayWidth;
5950
+ const scaleY = (meta.height || displayHeight) / displayHeight;
5951
+ const px = {
5952
+ left: Math.max(0, Math.round(x1 * scaleX)),
5953
+ top: Math.max(0, Math.round(y1 * scaleY)),
5954
+ width: Math.max(1, Math.round((x2 - x1) * scaleX)),
5955
+ height: Math.max(1, Math.round((y2 - y1) * scaleY))
5956
+ };
5957
+ const buf = await sharp2(tmpPath).extract(px).png().toBuffer();
5958
+ try {
5959
+ unlinkSync2(tmpPath);
5960
+ } catch {
5961
+ }
5962
+ return { type: "image", data: buf.toString("base64") };
5963
+ }
5964
+ default: {
5965
+ const exhaustive = input.action;
5966
+ return `unsupported action: ${String(exhaustive)}`;
5967
+ }
5968
+ }
5969
+ } catch (err) {
5970
+ const msg = err?.message || String(err);
5971
+ let hint = "";
5972
+ if (/accessibility|not authorized|tcc|operation not permitted/i.test(msg)) {
5973
+ hint = " (Hint: call enable_computer_use to (re-)check permissions and open System Settings)";
5974
+ } else if (/command not found/i.test(msg)) {
5975
+ hint = " (Hint: install cliclick with `brew install cliclick`)";
5976
+ }
5977
+ return `Error: ${msg}${hint}`;
5978
+ }
5979
+ },
5980
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5981
+ toModelOutput({ output }) {
5982
+ if (typeof output === "string") {
5983
+ return { type: "content", value: [{ type: "text", text: output }] };
5984
+ }
5985
+ return {
5986
+ type: "content",
5987
+ value: [{ type: "media", data: output.data, mediaType: "image/png" }]
5988
+ };
5989
+ }
5990
+ });
5991
+ }
5992
+
5993
+ // src/tools/enable-computer-use.ts
5994
+ init_db();
5995
+ import { tool as tool13 } from "ai";
5996
+ import { z as z14 } from "zod";
5997
+ var inputSchema = z14.object({
5998
+ display_width: z14.number().int().positive().optional().describe("Display width in pixels (defaults to detected primary display, fallback 1280)"),
5999
+ display_height: z14.number().int().positive().optional().describe("Display height in pixels (defaults to detected primary display, fallback 800)"),
6000
+ request_permissions: z14.boolean().optional().default(true).describe(
6001
+ "When true (default), proactively trigger macOS permission prompts and open System Settings panes for any missing permissions."
6002
+ )
6003
+ });
6004
+ var ACCESSIBILITY_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
6005
+ var SCREEN_RECORDING_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
6006
+ function createEnableComputerUseTool(options) {
6007
+ return tool13({
6008
+ 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.",
6009
+ inputSchema,
6010
+ execute: async ({ display_width, display_height, request_permissions }) => {
6011
+ try {
6012
+ if (!isMacOs()) {
6013
+ return {
6014
+ success: false,
6015
+ error: "Computer use is currently only supported on macOS.",
6016
+ platform: process.platform
6017
+ };
6018
+ }
6019
+ if (!await isCliclickInstalled()) {
6020
+ return {
6021
+ success: false,
6022
+ error: "`cliclick` is not installed. It is required for mouse/keyboard control on macOS.",
6023
+ installCommand: "brew install cliclick",
6024
+ fixSteps: [
6025
+ "In a terminal on this Mac, run: brew install cliclick",
6026
+ "(If Homebrew is not installed, install it first from https://brew.sh)",
6027
+ "Then call enable_computer_use again"
6028
+ ]
6029
+ };
6030
+ }
6031
+ const acc = await hasAccessibilityPermissions();
6032
+ const screen = await hasScreenRecordingPermissions();
6033
+ const missing = [];
6034
+ if (!acc.ok) {
6035
+ let prompted = false;
6036
+ let panelOpened = false;
6037
+ if (request_permissions) {
6038
+ prompted = await requestAccessibilityPrompt().then(() => true).catch(() => false);
6039
+ await openSystemSettings("accessibility").then(() => {
6040
+ panelOpened = true;
6041
+ }).catch(() => void 0);
6042
+ }
6043
+ missing.push({
6044
+ name: "Accessibility",
6045
+ reason: "cliclick failed: " + (acc.error?.split("\n")[0] || "no permission"),
6046
+ pane: "accessibility",
6047
+ settingsUrl: ACCESSIBILITY_URL,
6048
+ fixSteps: [
6049
+ "In the System Settings \u2192 Privacy & Security \u2192 Accessibility pane that opened",
6050
+ "Click the + button",
6051
+ "Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
6052
+ "Toggle the switch ON",
6053
+ "Restart the agent process so the new permission takes effect",
6054
+ "Then call enable_computer_use again"
6055
+ ],
6056
+ prompted,
6057
+ panelOpened
6058
+ });
6059
+ }
6060
+ if (!screen) {
6061
+ let prompted = false;
6062
+ let panelOpened = false;
6063
+ if (request_permissions) {
6064
+ prompted = await requestScreenRecordingPrompt().then(() => true).catch(() => false);
6065
+ await openSystemSettings("screen-recording").then(() => {
6066
+ panelOpened = true;
6067
+ }).catch(() => void 0);
6068
+ }
6069
+ missing.push({
6070
+ name: "Screen Recording",
6071
+ reason: "CGPreflightScreenCaptureAccess returned false",
6072
+ pane: "screen-recording",
6073
+ settingsUrl: SCREEN_RECORDING_URL,
6074
+ fixSteps: [
6075
+ "In the System Settings \u2192 Privacy & Security \u2192 Screen Recording pane that opened",
6076
+ "Click the + button",
6077
+ "Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
6078
+ "Toggle the switch ON",
6079
+ "Restart the agent process so the new permission takes effect",
6080
+ "Then call enable_computer_use again"
6081
+ ],
6082
+ prompted,
6083
+ panelOpened
6084
+ });
6085
+ }
6086
+ if (missing.length > 0) {
6087
+ return {
6088
+ success: false,
6089
+ error: `Missing permission${missing.length > 1 ? "s" : ""}: ` + missing.map((m) => m.name).join(" and ") + ".",
6090
+ missingPermissions: missing,
6091
+ 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."
6092
+ };
6093
+ }
6094
+ let width = display_width;
6095
+ let height = display_height;
6096
+ let detected = null;
6097
+ if (width === void 0 || height === void 0) {
6098
+ detected = await detectScreenSize();
6099
+ width = width ?? detected?.width ?? 1280;
6100
+ height = height ?? detected?.height ?? 800;
6101
+ }
6102
+ const session = await sessionQueries.getById(options.sessionId);
6103
+ if (!session) {
6104
+ return { success: false, error: "Session not found" };
6105
+ }
6106
+ const config = session.config || {};
6107
+ if (config.computerUseEnabled === true && config.computerUseDisplayWidth === width && config.computerUseDisplayHeight === height) {
6108
+ return {
6109
+ success: true,
6110
+ alreadyEnabled: true,
6111
+ message: "Computer use was already enabled for this session.",
6112
+ displayWidth: width,
6113
+ displayHeight: height
6114
+ };
6115
+ }
6116
+ const updated = {
6117
+ ...config,
6118
+ computerUseEnabled: true,
6119
+ computerUseDisplayWidth: width,
6120
+ computerUseDisplayHeight: height
6121
+ };
6122
+ await sessionQueries.update(options.sessionId, { config: updated });
6123
+ return {
6124
+ success: true,
6125
+ enabled: true,
6126
+ platform: "darwin",
6127
+ displayWidth: width,
6128
+ displayHeight: height,
6129
+ detectedScreenSize: detected || void 0,
6130
+ permissions: {
6131
+ accessibility: "granted",
6132
+ screenRecording: "granted"
6133
+ },
6134
+ 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.`
6135
+ };
6136
+ } catch (err) {
6137
+ return {
6138
+ success: false,
6139
+ error: err?.message || String(err)
6140
+ };
6141
+ }
6142
+ }
6143
+ });
6144
+ }
6145
+
5569
6146
  // src/tools/index.ts
5570
6147
  init_semantic();
5571
6148
  init_remote();
@@ -5613,6 +6190,20 @@ async function createTools(options) {
5613
6190
  sessionId: options.sessionId
5614
6191
  });
5615
6192
  }
6193
+ if (process.platform === "darwin") {
6194
+ if (options.enableComputerUse) {
6195
+ tools.computer = createComputerUseTool({
6196
+ workingDirectory: options.workingDirectory,
6197
+ sessionId: options.sessionId,
6198
+ displayWidth: options.computerUseDisplayWidth,
6199
+ displayHeight: options.computerUseDisplayHeight
6200
+ });
6201
+ } else {
6202
+ tools.enable_computer_use = createEnableComputerUseTool({
6203
+ sessionId: options.sessionId
6204
+ });
6205
+ }
6206
+ }
5616
6207
  if (options.enableSemanticSearch !== false) {
5617
6208
  try {
5618
6209
  if (isVectorGatewayConfigured()) {
@@ -6598,7 +7189,14 @@ function repairToolPairing(messages) {
6598
7189
  }
6599
7190
 
6600
7191
  // src/utils/webhook.ts
7192
+ var TERMINAL_EVENTS = /* @__PURE__ */ new Set([
7193
+ "task.started",
7194
+ "task.completed",
7195
+ "task.failed"
7196
+ ]);
7197
+ var QUIET = process.env.SPARKECODER_QUIET_WEBHOOKS === "1";
6601
7198
  async function sendWebhook(url, event) {
7199
+ const t0 = Date.now();
6602
7200
  try {
6603
7201
  const controller = new AbortController();
6604
7202
  const timeout = setTimeout(() => controller.abort(), 5e3);
@@ -6612,12 +7210,24 @@ async function sendWebhook(url, event) {
6612
7210
  signal: controller.signal
6613
7211
  });
6614
7212
  clearTimeout(timeout);
7213
+ const ms = Date.now() - t0;
6615
7214
  if (!response.ok) {
6616
- console.warn(`[WEBHOOK] ${event.type} to ${url} returned HTTP ${response.status}`);
7215
+ const body = await response.text().catch(() => "");
7216
+ console.warn(
7217
+ `[WEBHOOK] ${event.type} task=${event.taskId} -> HTTP ${response.status} in ${ms}ms${body ? ` (${body.slice(0, 200)})` : ""}`
7218
+ );
7219
+ return;
7220
+ }
7221
+ if (!QUIET || TERMINAL_EVENTS.has(event.type)) {
7222
+ console.log(
7223
+ `[WEBHOOK] ${event.type} task=${event.taskId} -> 200 in ${ms}ms`
7224
+ );
6617
7225
  }
6618
7226
  } catch (err) {
6619
7227
  const reason = err.name === "AbortError" ? "timeout (5s)" : err.message;
6620
- console.warn(`[WEBHOOK] ${event.type} to ${url} failed: ${reason}`);
7228
+ console.warn(
7229
+ `[WEBHOOK] ${event.type} task=${event.taskId} -> failed: ${reason}`
7230
+ );
6621
7231
  }
6622
7232
  }
6623
7233
 
@@ -6660,10 +7270,14 @@ var Agent = class _Agent {
6660
7270
  */
6661
7271
  async createToolsWithCallbacks(options) {
6662
7272
  const config = getConfig();
7273
+ const sessionConfig = this.session.config || {};
6663
7274
  return createTools({
6664
7275
  sessionId: this.session.id,
6665
7276
  workingDirectory: this.session.workingDirectory,
6666
7277
  skillsDirectories: config.resolvedSkillsDirectories,
7278
+ enableComputerUse: sessionConfig.computerUseEnabled === true,
7279
+ computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
7280
+ computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight,
6667
7281
  onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
6668
7282
  onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
6669
7283
  onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0
@@ -6696,10 +7310,14 @@ var Agent = class _Agent {
6696
7310
  keepRecentMessages: config.context?.keepRecentMessages || 10,
6697
7311
  autoSummarize: config.context?.autoSummarize ?? true
6698
7312
  });
7313
+ const sessionConfig = session.config || {};
6699
7314
  const tools = await createTools({
6700
7315
  sessionId: session.id,
6701
7316
  workingDirectory: session.workingDirectory,
6702
- skillsDirectories: config.resolvedSkillsDirectories
7317
+ skillsDirectories: config.resolvedSkillsDirectories,
7318
+ enableComputerUse: sessionConfig.computerUseEnabled === true,
7319
+ computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
7320
+ computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight
6703
7321
  });
6704
7322
  return new _Agent(session, context, tools);
6705
7323
  }
@@ -6918,10 +7536,14 @@ ${prompt}` });
6918
7536
  });
6919
7537
  }
6920
7538
  };
7539
+ const taskSessionConfig = this.session.config || {};
6921
7540
  const taskTools = await createTools({
6922
7541
  sessionId: this.session.id,
6923
7542
  workingDirectory: this.session.workingDirectory,
6924
7543
  skillsDirectories: config.resolvedSkillsDirectories,
7544
+ enableComputerUse: taskSessionConfig.computerUseEnabled === true,
7545
+ computerUseDisplayWidth: taskSessionConfig.computerUseDisplayWidth,
7546
+ computerUseDisplayHeight: taskSessionConfig.computerUseDisplayHeight,
6925
7547
  onBashProgress: bashProgressHandler,
6926
7548
  onWriteFileProgress: (progress) => {
6927
7549
  options.onToolProgress?.({ toolName: "write_file", data: progress });
@@ -7204,11 +7826,11 @@ ${taskAddendum}`;
7204
7826
  const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
7205
7827
  if (!isRemoteConfigured2()) return [];
7206
7828
  const { readFile: readFile12 } = await import("fs/promises");
7207
- const { join: join9, basename: basename5 } = await import("path");
7829
+ const { join: join10, basename: basename5 } = await import("path");
7208
7830
  const urls = [];
7209
7831
  for (const filePath of filePaths) {
7210
7832
  try {
7211
- const fullPath = filePath.startsWith("/") ? filePath : join9(this.session.workingDirectory, filePath);
7833
+ const fullPath = filePath.startsWith("/") ? filePath : join10(this.session.workingDirectory, filePath);
7212
7834
  const fileName = basename5(fullPath);
7213
7835
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
7214
7836
  const mimeMap = {
@@ -7266,11 +7888,11 @@ ${taskAddendum}`;
7266
7888
  wrappedTools[name] = originalTool;
7267
7889
  continue;
7268
7890
  }
7269
- wrappedTools[name] = tool13({
7891
+ wrappedTools[name] = tool14({
7270
7892
  description: originalTool.description || "",
7271
- inputSchema: originalTool.inputSchema || z14.object({}),
7893
+ inputSchema: originalTool.inputSchema || z15.object({}),
7272
7894
  execute: async (input, toolOptions) => {
7273
- const toolCallId = toolOptions.toolCallId || nanoid4();
7895
+ const toolCallId = toolOptions.toolCallId || nanoid5();
7274
7896
  const execution = toolExecutionQueries.create({
7275
7897
  sessionId: this.session.id,
7276
7898
  toolName: name,
@@ -7288,10 +7910,10 @@ ${taskAddendum}`;
7288
7910
  const resolverData = approvalResolvers.get(toolCallId);
7289
7911
  approvalResolvers.delete(toolCallId);
7290
7912
  this.pendingApprovals.delete(toolCallId);
7291
- const exec6 = await execution;
7913
+ const exec7 = await execution;
7292
7914
  if (!approved) {
7293
7915
  const reason = resolverData?.reason || "User rejected the tool execution";
7294
- await toolExecutionQueries.reject(exec6.id);
7916
+ await toolExecutionQueries.reject(exec7.id);
7295
7917
  await sessionQueries.updateStatus(this.session.id, "active");
7296
7918
  return {
7297
7919
  status: "rejected",
@@ -7301,14 +7923,14 @@ ${taskAddendum}`;
7301
7923
  message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
7302
7924
  };
7303
7925
  }
7304
- await toolExecutionQueries.approve(exec6.id);
7926
+ await toolExecutionQueries.approve(exec7.id);
7305
7927
  await sessionQueries.updateStatus(this.session.id, "active");
7306
7928
  try {
7307
7929
  const result = await originalTool.execute(input, toolOptions);
7308
- await toolExecutionQueries.complete(exec6.id, result);
7930
+ await toolExecutionQueries.complete(exec7.id, result);
7309
7931
  return result;
7310
7932
  } catch (error) {
7311
- await toolExecutionQueries.complete(exec6.id, null, error.message);
7933
+ await toolExecutionQueries.complete(exec7.id, null, error.message);
7312
7934
  throw error;
7313
7935
  }
7314
7936
  }