sparkecoder 0.1.117 → 0.1.119

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/agent/index.d.ts +2 -2
  2. package/dist/agent/index.js +117 -698
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +639 -1042
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-Bi8Ek02A.d.ts → index-Bcz0aCAR.d.ts} +1 -10
  8. package/dist/index.d.ts +4 -4
  9. package/dist/index.js +406 -944
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-ecQSnCMz.d.ts → schema-BWbWmfDQ.d.ts} +0 -2
  12. package/dist/server/index.js +406 -944
  13. package/dist/server/index.js.map +1 -1
  14. package/dist/skills/default/desktop-automation.md +290 -0
  15. package/dist/skills/default/recording.md +3 -3
  16. package/dist/tools/index.d.ts +1 -167
  17. package/dist/tools/index.js +5 -590
  18. package/dist/tools/index.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/skills/default/desktop-automation.md +290 -0
  21. package/src/skills/default/recording.md +3 -3
  22. package/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  24. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  25. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  26. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  27. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  42. package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  86. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  88. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  92. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  93. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  94. package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
  95. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
  96. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  97. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  98. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
  99. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  100. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  101. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  102. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  103. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  104. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  105. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  106. package/dist/skills/default/computer-use.md +0 -225
  107. package/src/skills/default/computer-use.md +0 -225
  108. /package/web/.next/standalone/web/.next/static/{static/vLqK4jK7EKdLCpQ-D6-qL → Bt00m8W4k5F79ALhN700F}/_buildManifest.js +0 -0
  109. /package/web/.next/standalone/web/.next/static/{static/vLqK4jK7EKdLCpQ-D6-qL → Bt00m8W4k5F79ALhN700F}/_clientMiddlewareManifest.json +0 -0
  110. /package/web/.next/standalone/web/.next/static/{static/vLqK4jK7EKdLCpQ-D6-qL → Bt00m8W4k5F79ALhN700F}/_ssgManifest.js +0 -0
  111. /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/Bt00m8W4k5F79ALhN700F}/_buildManifest.js +0 -0
  112. /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/Bt00m8W4k5F79ALhN700F}/_clientMiddlewareManifest.json +0 -0
  113. /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/Bt00m8W4k5F79ALhN700F}/_ssgManifest.js +0 -0
  114. /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → Bt00m8W4k5F79ALhN700F}/_buildManifest.js +0 -0
  115. /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → Bt00m8W4k5F79ALhN700F}/_clientMiddlewareManifest.json +0 -0
  116. /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → Bt00m8W4k5F79ALhN700F}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -211,12 +211,6 @@ var init_types = __esm({
211
211
  skillsDirectory: z.string().optional(),
212
212
  maxContextChars: z.number().optional().default(2e5),
213
213
  task: TaskConfigSchema.optional(),
214
- // Anthropic computer use tool — opt-in. When true, the `computer` tool is
215
- // included in the toolset for Anthropic models. Default false.
216
- computerUseEnabled: z.boolean().optional(),
217
- // Display dimensions for the computer use tool (defaults: 1280x800).
218
- computerUseDisplayWidth: z.number().int().positive().optional(),
219
- computerUseDisplayHeight: z.number().int().positive().optional(),
220
214
  // 'orchestrator' = supervisor session; 'worker' = task spawned by an orchestrator.
221
215
  role: z.enum(["orchestrator", "worker", "chat"]).optional(),
222
216
  // Optional persona / extra system-prompt text appended to the orchestrator's
@@ -652,7 +646,7 @@ function loadConfig(configPath, workingDirectory) {
652
646
  ...config,
653
647
  server: {
654
648
  port: config.server.port,
655
- host: config.server.host ?? "127.0.0.1",
649
+ host: config.server.host ?? "0.0.0.0",
656
650
  publicUrl: config.server.publicUrl
657
651
  },
658
652
  resolvedWorkingDirectory,
@@ -819,7 +813,7 @@ function createDefaultConfig() {
819
813
  },
820
814
  server: {
821
815
  port: 3141,
822
- host: "127.0.0.1"
816
+ host: "0.0.0.0"
823
817
  },
824
818
  databasePath: "./sparkecoder.db"
825
819
  };
@@ -2731,12 +2725,12 @@ function findNearestRoot(startDir, markers) {
2731
2725
  }
2732
2726
  async function commandExists(cmd) {
2733
2727
  try {
2734
- const { exec: exec8 } = await import("child_process");
2735
- const { promisify: promisify8 } = await import("util");
2736
- const execAsync8 = promisify8(exec8);
2728
+ const { exec: exec7 } = await import("child_process");
2729
+ const { promisify: promisify7 } = await import("util");
2730
+ const execAsync7 = promisify7(exec7);
2737
2731
  const isWindows = process.platform === "win32";
2738
2732
  const checkCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
2739
- await execAsync8(checkCmd);
2733
+ await execAsync7(checkCmd);
2740
2734
  return true;
2741
2735
  } catch {
2742
2736
  return false;
@@ -6381,581 +6375,6 @@ var init_upload_file = __esm({
6381
6375
  }
6382
6376
  });
6383
6377
 
6384
- // src/tools/computer-use.ts
6385
- import { anthropic } from "@ai-sdk/anthropic";
6386
- import { exec as exec5 } from "child_process";
6387
- import { promisify as promisify5 } from "util";
6388
- import { mkdirSync as mkdirSync5, existsSync as existsSync15, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
6389
- import { join as join8 } from "path";
6390
- import { tmpdir } from "os";
6391
- import { nanoid as nanoid4 } from "nanoid";
6392
- function isMacOs() {
6393
- return process.platform === "darwin";
6394
- }
6395
- async function isCliclickInstalled() {
6396
- try {
6397
- await execAsync5("command -v cliclick", { timeout: 2e3 });
6398
- return true;
6399
- } catch {
6400
- return false;
6401
- }
6402
- }
6403
- async function runJxa(script) {
6404
- try {
6405
- const escaped = script.replace(/'/g, `'\\''`);
6406
- const { stdout } = await execAsync5(`osascript -l JavaScript -e '${escaped}'`, {
6407
- timeout: 5e3
6408
- });
6409
- return JSON.parse(stdout.trim());
6410
- } catch {
6411
- return null;
6412
- }
6413
- }
6414
- async function hasAccessibilityPermissions() {
6415
- try {
6416
- const { stderr } = await execAsync5("cliclick p:.", { timeout: 3e3 });
6417
- if (/accessibility privileges not enabled/i.test(stderr)) {
6418
- return { ok: false, error: stderr.trim().split("\n")[0] };
6419
- }
6420
- return { ok: true };
6421
- } catch (err) {
6422
- return { ok: false, error: err?.message || String(err) };
6423
- }
6424
- }
6425
- async function hasScreenRecordingPermissions() {
6426
- const result = await runJxa(
6427
- `ObjC.import("Cocoa");
6428
- ObjC.import("CoreGraphics");
6429
- ObjC.bindFunction("CGPreflightScreenCaptureAccess", ["bool", []]);
6430
- JSON.stringify({ hasAccess: !!$.CGPreflightScreenCaptureAccess() });`
6431
- );
6432
- return result?.hasAccess ?? false;
6433
- }
6434
- async function requestAccessibilityPrompt() {
6435
- const result = await runJxa(
6436
- `ObjC.import("ApplicationServices");
6437
- var key = $.kAXTrustedCheckOptionPrompt;
6438
- var dict = $.NSDictionary.dictionaryWithObjectForKey($.kCFBooleanTrue, key);
6439
- var trusted = $.AXIsProcessTrustedWithOptions(dict);
6440
- JSON.stringify({ trusted: !!trusted });`
6441
- );
6442
- return result?.trusted ?? false;
6443
- }
6444
- async function requestScreenRecordingPrompt() {
6445
- const result = await runJxa(
6446
- `ObjC.import("Cocoa");
6447
- ObjC.import("CoreGraphics");
6448
- ObjC.bindFunction("CGRequestScreenCaptureAccess", ["bool", []]);
6449
- JSON.stringify({ granted: !!$.CGRequestScreenCaptureAccess() });`
6450
- );
6451
- return result?.granted ?? false;
6452
- }
6453
- async function openSystemSettings(pane) {
6454
- const url = pane === "accessibility" ? "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" : "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
6455
- try {
6456
- await execAsync5(`open '${url}'`, { timeout: 3e3 });
6457
- } catch {
6458
- }
6459
- }
6460
- async function detectScreenSize() {
6461
- try {
6462
- const { stdout } = await execAsync5(
6463
- `osascript -e 'tell application "Finder" to get bounds of window of desktop'`,
6464
- { timeout: 3e3 }
6465
- );
6466
- const parts = stdout.trim().split(",").map((s) => parseInt(s.trim(), 10));
6467
- if (parts.length >= 4 && parts.every((n) => Number.isFinite(n))) {
6468
- const [x1, y1, x2, y2] = parts;
6469
- return { width: x2 - x1, height: y2 - y1 };
6470
- }
6471
- } catch {
6472
- }
6473
- return null;
6474
- }
6475
- async function runCliclick(args) {
6476
- const quoted = args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(" ");
6477
- const { stdout, stderr } = await execAsync5(`cliclick ${quoted}`, {
6478
- timeout: 15e3,
6479
- maxBuffer: 1024 * 1024
6480
- });
6481
- if (/accessibility privileges not enabled/i.test(stderr)) {
6482
- throw new Error(
6483
- "Accessibility permissions not granted to cliclick. Open System Settings \u2192 Privacy & Security \u2192 Accessibility, add cliclick (or the agent runtime), and toggle it on."
6484
- );
6485
- }
6486
- if (stderr && !stdout) throw new Error(stderr.trim());
6487
- return (stdout || "").trim();
6488
- }
6489
- async function runScreencapture(path) {
6490
- await execAsync5(`screencapture -x -t png '${path.replace(/'/g, `'\\''`)}'`, {
6491
- timeout: 5e3
6492
- });
6493
- }
6494
- async function resizeScreenshotToPoints(path, targetWidth, targetHeight) {
6495
- const sharpModule = await import("sharp");
6496
- const sharp2 = sharpModule.default || sharpModule;
6497
- const meta = await sharp2(path).metadata();
6498
- if (meta.width === targetWidth && meta.height === targetHeight) {
6499
- return readFileSync7(path);
6500
- }
6501
- return await sharp2(path).resize(targetWidth, targetHeight, { fit: "fill" }).png().toBuffer();
6502
- }
6503
- async function runScroll(dx, dy) {
6504
- const wheelY = -Math.round(dy);
6505
- const wheelX = -Math.round(dx);
6506
- const script = `ObjC.import('CoreGraphics');var ev = $.CGEventCreateScrollWheelEvent(null, 0, 2, ${wheelY}, ${wheelX});$.CGEventPost(0, ev);`;
6507
- await execAsync5(
6508
- `osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
6509
- { timeout: 5e3 }
6510
- );
6511
- }
6512
- function translateKeyForCliclick(key2) {
6513
- if (!key2) return [];
6514
- const parts = key2.split("+").map((p) => p.trim()).filter(Boolean);
6515
- if (parts.length === 0) return [];
6516
- const modMap = {
6517
- ctrl: "ctrl",
6518
- control: "ctrl",
6519
- alt: "alt",
6520
- option: "alt",
6521
- shift: "shift",
6522
- cmd: "cmd",
6523
- super: "cmd",
6524
- meta: "cmd",
6525
- win: "cmd",
6526
- fn: "fn"
6527
- };
6528
- const keyMap = {
6529
- return: "enter",
6530
- enter: "enter",
6531
- esc: "esc",
6532
- escape: "esc",
6533
- backspace: "delete",
6534
- back_space: "delete",
6535
- delete: "fwd-delete",
6536
- fwd_delete: "fwd-delete",
6537
- forward_delete: "fwd-delete",
6538
- tab: "tab",
6539
- space: "space",
6540
- up: "arrow-up",
6541
- arrow_up: "arrow-up",
6542
- down: "arrow-down",
6543
- arrow_down: "arrow-down",
6544
- left: "arrow-left",
6545
- arrow_left: "arrow-left",
6546
- right: "arrow-right",
6547
- arrow_right: "arrow-right",
6548
- page_up: "page-up",
6549
- pageup: "page-up",
6550
- page_down: "page-down",
6551
- pagedown: "page-down",
6552
- home: "home",
6553
- end: "end",
6554
- f1: "f1",
6555
- f2: "f2",
6556
- f3: "f3",
6557
- f4: "f4",
6558
- f5: "f5",
6559
- f6: "f6",
6560
- f7: "f7",
6561
- f8: "f8",
6562
- f9: "f9",
6563
- f10: "f10",
6564
- f11: "f11",
6565
- f12: "f12"
6566
- };
6567
- const modifiers = [];
6568
- let mainKey = null;
6569
- for (let i = 0; i < parts.length; i++) {
6570
- const lower = parts[i].toLowerCase().replace(/-/g, "_");
6571
- if (i < parts.length - 1 && modMap[lower]) {
6572
- modifiers.push(modMap[lower]);
6573
- } else {
6574
- mainKey = keyMap[lower] || lower;
6575
- }
6576
- }
6577
- const args = [];
6578
- if (modifiers.length > 0) args.push(`kd:${modifiers.join(",")}`);
6579
- if (mainKey) {
6580
- const isNamedKey = Object.values(keyMap).includes(mainKey) || /^f([1-9]|1[0-9]|20)$/.test(mainKey) || /^num-/.test(mainKey);
6581
- if (isNamedKey) {
6582
- args.push(`kp:${mainKey}`);
6583
- } else {
6584
- args.push(`t:${mainKey}`);
6585
- }
6586
- }
6587
- if (modifiers.length > 0) args.push(`ku:${modifiers.join(",")}`);
6588
- return args;
6589
- }
6590
- function modifierStringToCliclick(text) {
6591
- return text.split("+").map((p) => p.trim().toLowerCase()).map((p) => {
6592
- if (p === "ctrl" || p === "control") return "ctrl";
6593
- if (p === "alt" || p === "option") return "alt";
6594
- if (p === "shift") return "shift";
6595
- if (p === "super" || p === "meta" || p === "cmd") return "cmd";
6596
- return "";
6597
- }).filter(Boolean);
6598
- }
6599
- function createComputerUseTool(options) {
6600
- const displayWidth = options.displayWidth ?? DEFAULT_WIDTH;
6601
- const displayHeight = options.displayHeight ?? DEFAULT_HEIGHT;
6602
- return anthropic.tools.computer_20251124({
6603
- displayWidthPx: displayWidth,
6604
- displayHeightPx: displayHeight,
6605
- enableZoom: true,
6606
- execute: async (input) => {
6607
- try {
6608
- switch (input.action) {
6609
- case "screenshot": {
6610
- const path = join8(tmpdir(), `cu-${nanoid4(8)}.png`);
6611
- await runScreencapture(path);
6612
- const resized = await resizeScreenshotToPoints(path, displayWidth, displayHeight);
6613
- try {
6614
- unlinkSync2(path);
6615
- } catch {
6616
- }
6617
- return { type: "image", data: resized.toString("base64") };
6618
- }
6619
- case "left_click": {
6620
- const [x, y] = input.coordinate ?? [0, 0];
6621
- if (input.text) {
6622
- const mods = modifierStringToCliclick(input.text);
6623
- if (mods.length > 0) {
6624
- await runCliclick([`kd:${mods.join(",")}`, `c:${x},${y}`, `ku:${mods.join(",")}`]);
6625
- } else {
6626
- await runCliclick([`c:${x},${y}`]);
6627
- }
6628
- } else {
6629
- await runCliclick([`c:${x},${y}`]);
6630
- }
6631
- return `clicked at (${x}, ${y})${input.text ? ` with ${input.text}` : ""}`;
6632
- }
6633
- case "right_click": {
6634
- const [x, y] = input.coordinate ?? [0, 0];
6635
- await runCliclick([`rc:${x},${y}`]);
6636
- return `right-clicked at (${x}, ${y})`;
6637
- }
6638
- case "middle_click": {
6639
- const [x, y] = input.coordinate ?? [0, 0];
6640
- 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);`;
6641
- await execAsync5(
6642
- `osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
6643
- { timeout: 3e3 }
6644
- );
6645
- return `middle-clicked at (${x}, ${y})`;
6646
- }
6647
- case "double_click": {
6648
- const [x, y] = input.coordinate ?? [0, 0];
6649
- await runCliclick([`dc:${x},${y}`]);
6650
- return `double-clicked at (${x}, ${y})`;
6651
- }
6652
- case "triple_click": {
6653
- const [x, y] = input.coordinate ?? [0, 0];
6654
- await runCliclick([`tc:${x},${y}`]);
6655
- return `triple-clicked at (${x}, ${y})`;
6656
- }
6657
- case "mouse_move": {
6658
- const [x, y] = input.coordinate ?? [0, 0];
6659
- await runCliclick([`m:${x},${y}`]);
6660
- return `moved cursor to (${x}, ${y})`;
6661
- }
6662
- case "left_mouse_down": {
6663
- const [x, y] = input.coordinate ?? [0, 0];
6664
- await runCliclick([`dd:${x},${y}`]);
6665
- return `left mouse button pressed at (${x}, ${y})`;
6666
- }
6667
- case "left_mouse_up": {
6668
- const [x, y] = input.coordinate ?? [0, 0];
6669
- await runCliclick([`du:${x},${y}`]);
6670
- return `left mouse button released at (${x}, ${y})`;
6671
- }
6672
- case "left_click_drag": {
6673
- const [sx, sy] = input.start_coordinate ?? [0, 0];
6674
- const [ex, ey] = input.coordinate ?? [0, 0];
6675
- await runCliclick([`dd:${sx},${sy}`, `m:${ex},${ey}`, `du:${ex},${ey}`]);
6676
- return `dragged from (${sx}, ${sy}) to (${ex}, ${ey})`;
6677
- }
6678
- case "type": {
6679
- const text = input.text ?? "";
6680
- await runCliclick([`t:${text}`]);
6681
- return `typed ${text.length} character(s)`;
6682
- }
6683
- case "key": {
6684
- const args = translateKeyForCliclick(input.text ?? "");
6685
- if (args.length === 0) return "no key specified";
6686
- await runCliclick(args);
6687
- return `pressed ${input.text}`;
6688
- }
6689
- case "hold_key": {
6690
- const text = (input.text ?? "").toLowerCase();
6691
- const duration = input.duration ?? 1;
6692
- const modMap = {
6693
- ctrl: "ctrl",
6694
- control: "ctrl",
6695
- alt: "alt",
6696
- option: "alt",
6697
- shift: "shift",
6698
- cmd: "cmd",
6699
- super: "cmd",
6700
- meta: "cmd",
6701
- fn: "fn"
6702
- };
6703
- const cliName = modMap[text] || text;
6704
- await runCliclick([`kd:${cliName}`]);
6705
- await new Promise((r) => setTimeout(r, duration * 1e3));
6706
- await runCliclick([`ku:${cliName}`]);
6707
- return `held ${text} for ${duration}s`;
6708
- }
6709
- case "scroll": {
6710
- const direction = input.scroll_direction ?? "down";
6711
- const amount = input.scroll_amount ?? 3;
6712
- const px = amount * 100;
6713
- const dx = direction === "left" ? -px : direction === "right" ? px : 0;
6714
- const dy = direction === "up" ? -px : direction === "down" ? px : 0;
6715
- if (input.coordinate) {
6716
- const [x, y] = input.coordinate;
6717
- await runCliclick([`m:${x},${y}`]);
6718
- }
6719
- const mods = input.text ? modifierStringToCliclick(input.text) : [];
6720
- if (mods.length > 0) {
6721
- await runCliclick([`kd:${mods.join(",")}`]);
6722
- }
6723
- await runScroll(dx, dy);
6724
- if (mods.length > 0) {
6725
- await runCliclick([`ku:${mods.join(",")}`]);
6726
- }
6727
- return `scrolled ${direction} by ${amount}`;
6728
- }
6729
- case "wait": {
6730
- const duration = input.duration ?? 1;
6731
- await new Promise((r) => setTimeout(r, duration * 1e3));
6732
- return `waited ${duration}s`;
6733
- }
6734
- case "cursor_position": {
6735
- const out = await runCliclick(["p:."]);
6736
- return `cursor at ${out}`;
6737
- }
6738
- case "zoom": {
6739
- const region = input.region ?? [0, 0, displayWidth, displayHeight];
6740
- const [x1, y1, x2, y2] = region;
6741
- const tmpPath = join8(tmpdir(), `cu-zoom-${nanoid4(8)}.png`);
6742
- await runScreencapture(tmpPath);
6743
- const sharpModule = await import("sharp");
6744
- const sharp2 = sharpModule.default || sharpModule;
6745
- const meta = await sharp2(tmpPath).metadata();
6746
- const scaleX = (meta.width || displayWidth) / displayWidth;
6747
- const scaleY = (meta.height || displayHeight) / displayHeight;
6748
- const px = {
6749
- left: Math.max(0, Math.round(x1 * scaleX)),
6750
- top: Math.max(0, Math.round(y1 * scaleY)),
6751
- width: Math.max(1, Math.round((x2 - x1) * scaleX)),
6752
- height: Math.max(1, Math.round((y2 - y1) * scaleY))
6753
- };
6754
- const buf = await sharp2(tmpPath).extract(px).png().toBuffer();
6755
- try {
6756
- unlinkSync2(tmpPath);
6757
- } catch {
6758
- }
6759
- return { type: "image", data: buf.toString("base64") };
6760
- }
6761
- default: {
6762
- const exhaustive = input.action;
6763
- return `unsupported action: ${String(exhaustive)}`;
6764
- }
6765
- }
6766
- } catch (err) {
6767
- const msg = err?.message || String(err);
6768
- let hint = "";
6769
- if (/accessibility|not authorized|tcc|operation not permitted/i.test(msg)) {
6770
- hint = " (Hint: call enable_computer_use to (re-)check permissions and open System Settings)";
6771
- } else if (/command not found/i.test(msg)) {
6772
- hint = " (Hint: install cliclick with `brew install cliclick`)";
6773
- }
6774
- return `Error: ${msg}${hint}`;
6775
- }
6776
- },
6777
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
6778
- toModelOutput({ output }) {
6779
- if (typeof output === "string") {
6780
- return { type: "content", value: [{ type: "text", text: output }] };
6781
- }
6782
- return {
6783
- type: "content",
6784
- value: [{ type: "media", data: output.data, mediaType: "image/png" }]
6785
- };
6786
- }
6787
- });
6788
- }
6789
- var execAsync5, DEFAULT_WIDTH, DEFAULT_HEIGHT;
6790
- var init_computer_use = __esm({
6791
- "src/tools/computer-use.ts"() {
6792
- "use strict";
6793
- execAsync5 = promisify5(exec5);
6794
- DEFAULT_WIDTH = 1280;
6795
- DEFAULT_HEIGHT = 800;
6796
- }
6797
- });
6798
-
6799
- // src/tools/enable-computer-use.ts
6800
- import { tool as tool13 } from "ai";
6801
- import { z as z14 } from "zod";
6802
- function createEnableComputerUseTool(options) {
6803
- return tool13({
6804
- 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.",
6805
- inputSchema,
6806
- execute: async ({ display_width, display_height, request_permissions }) => {
6807
- try {
6808
- if (!isMacOs()) {
6809
- return {
6810
- success: false,
6811
- error: "Computer use is currently only supported on macOS.",
6812
- platform: process.platform
6813
- };
6814
- }
6815
- if (!await isCliclickInstalled()) {
6816
- return {
6817
- success: false,
6818
- error: "`cliclick` is not installed. It is required for mouse/keyboard control on macOS.",
6819
- installCommand: "brew install cliclick",
6820
- fixSteps: [
6821
- "In a terminal on this Mac, run: brew install cliclick",
6822
- "(If Homebrew is not installed, install it first from https://brew.sh)",
6823
- "Then call enable_computer_use again"
6824
- ]
6825
- };
6826
- }
6827
- const acc = await hasAccessibilityPermissions();
6828
- const screen = await hasScreenRecordingPermissions();
6829
- const missing = [];
6830
- if (!acc.ok) {
6831
- let prompted = false;
6832
- let panelOpened = false;
6833
- if (request_permissions) {
6834
- prompted = await requestAccessibilityPrompt().then(() => true).catch(() => false);
6835
- await openSystemSettings("accessibility").then(() => {
6836
- panelOpened = true;
6837
- }).catch(() => void 0);
6838
- }
6839
- missing.push({
6840
- name: "Accessibility",
6841
- reason: "cliclick failed: " + (acc.error?.split("\n")[0] || "no permission"),
6842
- pane: "accessibility",
6843
- settingsUrl: ACCESSIBILITY_URL,
6844
- fixSteps: [
6845
- "In the System Settings \u2192 Privacy & Security \u2192 Accessibility pane that opened",
6846
- "Click the + button",
6847
- "Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
6848
- "Toggle the switch ON",
6849
- "Restart the agent process so the new permission takes effect",
6850
- "Then call enable_computer_use again"
6851
- ],
6852
- prompted,
6853
- panelOpened
6854
- });
6855
- }
6856
- if (!screen) {
6857
- let prompted = false;
6858
- let panelOpened = false;
6859
- if (request_permissions) {
6860
- prompted = await requestScreenRecordingPrompt().then(() => true).catch(() => false);
6861
- await openSystemSettings("screen-recording").then(() => {
6862
- panelOpened = true;
6863
- }).catch(() => void 0);
6864
- }
6865
- missing.push({
6866
- name: "Screen Recording",
6867
- reason: "CGPreflightScreenCaptureAccess returned false",
6868
- pane: "screen-recording",
6869
- settingsUrl: SCREEN_RECORDING_URL,
6870
- fixSteps: [
6871
- "In the System Settings \u2192 Privacy & Security \u2192 Screen Recording pane that opened",
6872
- "Click the + button",
6873
- "Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
6874
- "Toggle the switch ON",
6875
- "Restart the agent process so the new permission takes effect",
6876
- "Then call enable_computer_use again"
6877
- ],
6878
- prompted,
6879
- panelOpened
6880
- });
6881
- }
6882
- if (missing.length > 0) {
6883
- return {
6884
- success: false,
6885
- error: `Missing permission${missing.length > 1 ? "s" : ""}: ` + missing.map((m) => m.name).join(" and ") + ".",
6886
- missingPermissions: missing,
6887
- 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."
6888
- };
6889
- }
6890
- let width = display_width;
6891
- let height = display_height;
6892
- let detected = null;
6893
- if (width === void 0 || height === void 0) {
6894
- detected = await detectScreenSize();
6895
- width = width ?? detected?.width ?? 1280;
6896
- height = height ?? detected?.height ?? 800;
6897
- }
6898
- const session = await sessionQueries.getById(options.sessionId);
6899
- if (!session) {
6900
- return { success: false, error: "Session not found" };
6901
- }
6902
- const config = session.config || {};
6903
- if (config.computerUseEnabled === true && config.computerUseDisplayWidth === width && config.computerUseDisplayHeight === height) {
6904
- return {
6905
- success: true,
6906
- alreadyEnabled: true,
6907
- message: "Computer use was already enabled for this session.",
6908
- displayWidth: width,
6909
- displayHeight: height
6910
- };
6911
- }
6912
- const updated = {
6913
- ...config,
6914
- computerUseEnabled: true,
6915
- computerUseDisplayWidth: width,
6916
- computerUseDisplayHeight: height
6917
- };
6918
- await sessionQueries.update(options.sessionId, { config: updated });
6919
- return {
6920
- success: true,
6921
- enabled: true,
6922
- platform: "darwin",
6923
- displayWidth: width,
6924
- displayHeight: height,
6925
- detectedScreenSize: detected || void 0,
6926
- permissions: {
6927
- accessibility: "granted",
6928
- screenRecording: "granted"
6929
- },
6930
- 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.`
6931
- };
6932
- } catch (err) {
6933
- return {
6934
- success: false,
6935
- error: err?.message || String(err)
6936
- };
6937
- }
6938
- }
6939
- });
6940
- }
6941
- var inputSchema, ACCESSIBILITY_URL, SCREEN_RECORDING_URL;
6942
- var init_enable_computer_use = __esm({
6943
- "src/tools/enable-computer-use.ts"() {
6944
- "use strict";
6945
- init_db();
6946
- init_computer_use();
6947
- inputSchema = z14.object({
6948
- display_width: z14.number().int().positive().optional().describe("Display width in pixels (defaults to detected primary display, fallback 1280)"),
6949
- display_height: z14.number().int().positive().optional().describe("Display height in pixels (defaults to detected primary display, fallback 800)"),
6950
- request_permissions: z14.boolean().optional().default(true).describe(
6951
- "When true (default), proactively trigger macOS permission prompts and open System Settings panes for any missing permissions."
6952
- )
6953
- });
6954
- ACCESSIBILITY_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
6955
- SCREEN_RECORDING_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
6956
- }
6957
- });
6958
-
6959
6378
  // src/tools/index.ts
6960
6379
  async function createTools(options) {
6961
6380
  const tools = {
@@ -7000,20 +6419,6 @@ async function createTools(options) {
7000
6419
  sessionId: options.sessionId
7001
6420
  });
7002
6421
  }
7003
- if (process.platform === "darwin") {
7004
- if (options.enableComputerUse) {
7005
- tools.computer = createComputerUseTool({
7006
- workingDirectory: options.workingDirectory,
7007
- sessionId: options.sessionId,
7008
- displayWidth: options.computerUseDisplayWidth,
7009
- displayHeight: options.computerUseDisplayHeight
7010
- });
7011
- } else {
7012
- tools.enable_computer_use = createEnableComputerUseTool({
7013
- sessionId: options.sessionId
7014
- });
7015
- }
7016
- }
7017
6422
  if (options.enableSemanticSearch !== false) {
7018
6423
  try {
7019
6424
  if (isVectorGatewayConfigured()) {
@@ -7048,8 +6453,6 @@ var init_tools = __esm({
7048
6453
  init_code_graph();
7049
6454
  init_task();
7050
6455
  init_upload_file();
7051
- init_computer_use();
7052
- init_enable_computer_use();
7053
6456
  init_semantic();
7054
6457
  init_remote();
7055
6458
  init_bash();
@@ -7063,8 +6466,6 @@ var init_tools = __esm({
7063
6466
  init_code_graph();
7064
6467
  init_task();
7065
6468
  init_upload_file();
7066
- init_computer_use();
7067
- init_enable_computer_use();
7068
6469
  }
7069
6470
  });
7070
6471
 
@@ -7540,8 +6941,7 @@ ${JSON.stringify(outputSchema, null, 2)}
7540
6941
  `;
7541
6942
  }
7542
6943
  function buildOrchestratorPromptAddendum() {
7543
- const platform3 = process.platform === "darwin" ? "darwin" : "other";
7544
- const computerUseAvailable = platform3 === "darwin";
6944
+ const desktopAvailable = process.platform === "darwin";
7545
6945
  return `
7546
6946
  ## Orchestrator Mode
7547
6947
 
@@ -7640,14 +7040,14 @@ When NOT to split (keep as one worker):
7640
7040
  When spawning a worker, push it toward the *cheapest tool that gets the job done*:
7641
7041
 
7642
7042
  1. **Bash / file tools** for anything with a CLI (git, npm, brew, builds, tests, file editing, HTTP via curl, scripting).
7643
- 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 ? `
7644
- 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).
7043
+ 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 ? `
7044
+ 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.
7645
7045
 
7646
- 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."*
7046
+ 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."*
7647
7047
 
7648
- ### Serialize desktop / computer-use tasks
7048
+ ### Serialize desktop-automation tasks
7649
7049
 
7650
- 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.
7050
+ 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.
7651
7051
 
7652
7052
  **Rule**: when spawning workers, look at each one's goal:
7653
7053
 
@@ -7668,7 +7068,7 @@ Example: *"Take a screenshot of Calculator AND run the test suite AND open Syste
7668
7068
 
7669
7069
  Headless workers never interfere with desktop workers (they don't touch the screen), so they always run in parallel.
7670
7070
 
7671
- 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.` : ""}
7071
+ 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.` : ""}
7672
7072
 
7673
7073
  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.
7674
7074
 
@@ -7699,7 +7099,7 @@ You delegate; the worker executes. Stay at the **what** level, not the **how**.
7699
7099
  **DO** put in the goal:
7700
7100
 
7701
7101
  - The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
7702
- - Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill computer-use\`).
7102
+ - Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill desktop-automation\`).
7703
7103
  - Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
7704
7104
  - Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
7705
7105
 
@@ -7732,7 +7132,7 @@ Bad goal (don't do this):
7732
7132
  > "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'..."
7733
7133
 
7734
7134
  Good goal (do this):
7735
- > "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."
7135
+ > "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."
7736
7136
  `;
7737
7137
  }
7738
7138
  function createSummaryPrompt(conversationHistory) {
@@ -7951,17 +7351,17 @@ __export(conversation_archive_exports, {
7951
7351
  getHistoryDir: () => getHistoryDir,
7952
7352
  listSessionArchives: () => listSessionArchives
7953
7353
  });
7954
- import { existsSync as existsSync16, mkdirSync as mkdirSync6, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
7955
- import { join as join9 } from "path";
7354
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
7355
+ import { join as join8 } from "path";
7956
7356
  function getHistoryDir() {
7957
- const dir = join9(ensureAppDataDirectory(), "history");
7958
- if (!existsSync16(dir)) mkdirSync6(dir, { recursive: true });
7357
+ const dir = join8(ensureAppDataDirectory(), "history");
7358
+ if (!existsSync15(dir)) mkdirSync5(dir, { recursive: true });
7959
7359
  return dir;
7960
7360
  }
7961
7361
  function appendTurn(turn) {
7962
7362
  try {
7963
7363
  const dir = getHistoryDir();
7964
- const path = join9(dir, `${turn.sessionId}.jsonl`);
7364
+ const path = join8(dir, `${turn.sessionId}.jsonl`);
7965
7365
  const line = JSON.stringify({
7966
7366
  ts: turn.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
7967
7367
  sessionId: turn.sessionId,
@@ -7990,7 +7390,7 @@ function flattenContent(content) {
7990
7390
  }
7991
7391
  function listSessionArchives() {
7992
7392
  const dir = getHistoryDir();
7993
- if (!existsSync16(dir)) return [];
7393
+ if (!existsSync15(dir)) return [];
7994
7394
  return readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
7995
7395
  }
7996
7396
  var init_conversation_archive = __esm({
@@ -8062,6 +7462,18 @@ function repairToolPairing(messages) {
8062
7462
  }
8063
7463
  return repaired;
8064
7464
  }
7465
+ function ensureEndsWithUserOrTool(messages) {
7466
+ if (!Array.isArray(messages) || messages.length === 0) return messages;
7467
+ const last = messages[messages.length - 1];
7468
+ if (last?.role !== "assistant") return messages;
7469
+ console.warn(
7470
+ "[context] Trailing assistant message detected \u2014 appending synthetic user turn to satisfy prefill restrictions"
7471
+ );
7472
+ return [
7473
+ ...messages,
7474
+ { role: "user", content: [{ type: "text", text: "Please continue." }] }
7475
+ ];
7476
+ }
8065
7477
  var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager;
8066
7478
  var init_context = __esm({
8067
7479
  "src/agent/context.ts"() {
@@ -8119,6 +7531,7 @@ ${summaryContent}`
8119
7531
  ];
8120
7532
  }
8121
7533
  messages = repairToolPairing(messages);
7534
+ messages = ensureEndsWithUserOrTool(messages);
8122
7535
  return messages;
8123
7536
  }
8124
7537
  // ---------------------------------------------------------------------------
@@ -8312,7 +7725,8 @@ ${summaryContent}`
8312
7725
  }
8313
7726
  }
8314
7727
  async addResponseMessages(messages) {
8315
- await messageQueries.addMany(this.sessionId, messages);
7728
+ const safe = repairToolPairing(messages);
7729
+ await messageQueries.addMany(this.sessionId, safe);
8316
7730
  try {
8317
7731
  const { appendTurn: appendTurn2, flattenContent: flattenContent2 } = await Promise.resolve().then(() => (init_conversation_archive(), conversation_archive_exports));
8318
7732
  const { sessionQueries: sessionQueries2 } = await Promise.resolve().then(() => (init_db(), db_exports));
@@ -8408,6 +7822,44 @@ function getSlackSigningSecret() {
8408
7822
  function getDefaultOrchestratorName() {
8409
7823
  return readSlackConfig()?.defaultOrchestratorName ?? null;
8410
7824
  }
7825
+ function getCachedSlackSelfIdentity() {
7826
+ const cfg = readSlackConfig();
7827
+ if (!cfg) return null;
7828
+ if (cachedSelf && cachedSelf.token === cfg.botToken) return cachedSelf.identity;
7829
+ return null;
7830
+ }
7831
+ async function ensureSlackSelfIdentity() {
7832
+ const cfg = readSlackConfig();
7833
+ if (!cfg) return null;
7834
+ if (cachedSelf && cachedSelf.token === cfg.botToken) return cachedSelf.identity;
7835
+ if (selfInflight) return selfInflight;
7836
+ selfInflight = (async () => {
7837
+ try {
7838
+ const res = await fetch("https://slack.com/api/auth.test", {
7839
+ method: "POST",
7840
+ headers: { Authorization: `Bearer ${cfg.botToken}` }
7841
+ });
7842
+ const data = await res.json().catch(() => ({}));
7843
+ if (!data?.ok) {
7844
+ console.warn(`[slack] auth.test failed: ${data?.error || `HTTP ${res.status}`}`);
7845
+ return null;
7846
+ }
7847
+ const identity = {
7848
+ botUserId: String(data.user_id || ""),
7849
+ botId: String(data.bot_id || ""),
7850
+ teamId: data.team_id ? String(data.team_id) : void 0
7851
+ };
7852
+ cachedSelf = { token: cfg.botToken, identity };
7853
+ return identity;
7854
+ } catch (err) {
7855
+ console.warn("[slack] auth.test error:", err?.message || err);
7856
+ return null;
7857
+ } finally {
7858
+ selfInflight = null;
7859
+ }
7860
+ })();
7861
+ return selfInflight;
7862
+ }
8411
7863
  function getSlackAllowlistPolicy() {
8412
7864
  try {
8413
7865
  const cfg = getConfig();
@@ -8433,11 +7885,13 @@ function getSlackDeniedReplyPolicy() {
8433
7885
  return { enabled: true, template: DEFAULT_DENIED_TEMPLATE };
8434
7886
  }
8435
7887
  }
8436
- var DEFAULT_DENIED_TEMPLATE;
7888
+ var cachedSelf, selfInflight, DEFAULT_DENIED_TEMPLATE;
8437
7889
  var init_client3 = __esm({
8438
7890
  "src/integrations/slack/client.ts"() {
8439
7891
  "use strict";
8440
7892
  init_config();
7893
+ cachedSelf = null;
7894
+ selfInflight = null;
8441
7895
  DEFAULT_DENIED_TEMPLATE = "Sorry, you don't have permission to use this bot. (Contact the bot owner if you think this is a mistake.)";
8442
7896
  }
8443
7897
  });
@@ -8455,9 +7909,19 @@ function isThreadOwned(channel, threadTs) {
8455
7909
  function stripMention(text) {
8456
7910
  return String(text || "").replace(/<@[^>]+>/g, "").trim();
8457
7911
  }
8458
- function slackEventToInboundResult(event) {
7912
+ function isSelfAuthored(event, self) {
7913
+ if (!self) return true;
7914
+ if (self.botId && event.bot_id && event.bot_id === self.botId) return true;
7915
+ if (self.botUserId && event.user && event.user === self.botUserId) return true;
7916
+ return false;
7917
+ }
7918
+ function slackEventToInboundResult(event, opts = {}) {
8459
7919
  if (!event) return { event: null, dropReason: "empty_text" };
8460
- if (event.bot_id) return { event: null, dropReason: "bot_message" };
7920
+ const self = opts.self ?? getCachedSlackSelfIdentity();
7921
+ const isBotAuthored = !!event.bot_id || event.type === "message" && event.subtype === "bot_message";
7922
+ if (isBotAuthored && isSelfAuthored(event, self)) {
7923
+ return { event: null, dropReason: "bot_message" };
7924
+ }
8461
7925
  if (event.type === "message" && event.subtype && IGNORED_MESSAGE_SUBTYPES.has(event.subtype)) {
8462
7926
  return { event: null, dropReason: "bot_message" };
8463
7927
  }
@@ -8534,7 +7998,6 @@ var init_slack = __esm({
8534
7998
  }
8535
7999
  };
8536
8000
  IGNORED_MESSAGE_SUBTYPES = /* @__PURE__ */ new Set([
8537
- "bot_message",
8538
8001
  "message_changed",
8539
8002
  "message_deleted",
8540
8003
  "channel_join",
@@ -8753,7 +8216,7 @@ var init_messenger = __esm({
8753
8216
  });
8754
8217
 
8755
8218
  // src/orchestrator/schedules-store.ts
8756
- import { nanoid as nanoid5 } from "nanoid";
8219
+ import { nanoid as nanoid4 } from "nanoid";
8757
8220
  async function readOrch(orchestratorSessionId) {
8758
8221
  const s = await sessionQueries.getById(orchestratorSessionId);
8759
8222
  if (!s) return null;
@@ -8768,7 +8231,7 @@ async function createSchedule(orchestratorSessionId, input) {
8768
8231
  const data = await readOrch(orchestratorSessionId);
8769
8232
  if (!data) throw new Error("orchestrator session not found");
8770
8233
  const row = {
8771
- id: `sch_${nanoid5(10)}`,
8234
+ id: `sch_${nanoid4(10)}`,
8772
8235
  name: input.name,
8773
8236
  cron: input.cron,
8774
8237
  prompt: input.prompt,
@@ -8805,7 +8268,7 @@ var init_schedules_store = __esm({
8805
8268
 
8806
8269
  // src/orchestrator/webhooks-store.ts
8807
8270
  import { randomBytes } from "crypto";
8808
- import { nanoid as nanoid6 } from "nanoid";
8271
+ import { nanoid as nanoid5 } from "nanoid";
8809
8272
  function newToken() {
8810
8273
  return randomBytes(24).toString("base64url");
8811
8274
  }
@@ -8822,7 +8285,7 @@ async function createWebhook(orchestratorSessionId, input) {
8822
8285
  const data = await readOrch2(orchestratorSessionId);
8823
8286
  if (!data) throw new Error("orchestrator session not found");
8824
8287
  const row = {
8825
- id: `whk_${nanoid6(10)}`,
8288
+ id: `whk_${nanoid5(10)}`,
8826
8289
  name: input.name,
8827
8290
  token: newToken(),
8828
8291
  wake: input.wake ?? "now",
@@ -8878,8 +8341,8 @@ var init_webhooks_store = __esm({
8878
8341
  });
8879
8342
 
8880
8343
  // src/tools/orchestrator-actions.ts
8881
- import { tool as tool14 } from "ai";
8882
- import { z as z15 } from "zod";
8344
+ import { tool as tool13 } from "ai";
8345
+ import { z as z14 } from "zod";
8883
8346
  async function api2(baseUrl, path, init = {}) {
8884
8347
  const res = await fetch(`${baseUrl}${path}`, {
8885
8348
  method: init.method || "GET",
@@ -8905,7 +8368,7 @@ function previewMessageContent(content) {
8905
8368
  return "";
8906
8369
  }
8907
8370
  function buildAgentTool(opts) {
8908
- return tool14({
8371
+ return tool13({
8909
8372
  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).",
8910
8373
  inputSchema: agentInputSchema,
8911
8374
  execute: async (input) => {
@@ -9008,7 +8471,7 @@ function buildAgentTool(opts) {
9008
8471
  });
9009
8472
  }
9010
8473
  function buildMessengerTool() {
9011
- return tool14({
8474
+ return tool13({
9012
8475
  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.",
9013
8476
  inputSchema: messengerInputSchema,
9014
8477
  execute: async (input) => {
@@ -9030,7 +8493,7 @@ function buildMessengerTool() {
9030
8493
  });
9031
8494
  }
9032
8495
  function buildScheduleTool(opts) {
9033
- return tool14({
8496
+ return tool13({
9034
8497
  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.",
9035
8498
  inputSchema: scheduleInputSchema,
9036
8499
  execute: async (input) => {
@@ -9073,7 +8536,7 @@ function buildWebhookUrl(opts, token) {
9073
8536
  return `${base}${webhookPrefix2}/inbox/${token}`;
9074
8537
  }
9075
8538
  function buildWebhookTool(opts) {
9076
- return tool14({
8539
+ return tool13({
9077
8540
  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.",
9078
8541
  inputSchema: webhookInputSchema,
9079
8542
  execute: async (input) => {
@@ -9122,66 +8585,66 @@ var init_orchestrator_actions = __esm({
9122
8585
  init_schedules_store();
9123
8586
  init_config();
9124
8587
  init_webhooks_store();
9125
- AGENT_STATUS_ENUM = z15.enum(["running", "needs_attention", "completed", "failed", "idle"]);
9126
- agentInputSchema = z15.object({
9127
- action: z15.enum(["list", "get", "spawn", "message", "answer_question", "stop"]).describe("Which agent operation to perform."),
8588
+ AGENT_STATUS_ENUM = z14.enum(["running", "needs_attention", "completed", "failed", "idle"]);
8589
+ agentInputSchema = z14.object({
8590
+ action: z14.enum(["list", "get", "spawn", "message", "answer_question", "stop"]).describe("Which agent operation to perform."),
9128
8591
  // list
9129
8592
  status: AGENT_STATUS_ENUM.optional().describe("list only: filter to one status."),
9130
- limit: z15.number().int().min(1).max(100).optional().describe("list only: max rows."),
8593
+ limit: z14.number().int().min(1).max(100).optional().describe("list only: max rows."),
9131
8594
  // get / message / answer_question / stop
9132
- id: z15.string().optional().describe("get | message | answer_question | stop: the agent (session) id."),
9133
- recentMessages: z15.number().int().min(0).max(50).optional().describe("get only: how many recent messages to include."),
8595
+ id: z14.string().optional().describe("get | message | answer_question | stop: the agent (session) id."),
8596
+ recentMessages: z14.number().int().min(0).max(50).optional().describe("get only: how many recent messages to include."),
9134
8597
  // spawn
9135
- name: z15.string().optional().describe("spawn only: short human-readable label."),
9136
- goal: z15.string().optional().describe("spawn only: the worker's self-contained instruction."),
9137
- outputSchema: z15.record(z15.string(), z15.unknown()).optional().describe(
8598
+ name: z14.string().optional().describe("spawn only: short human-readable label."),
8599
+ goal: z14.string().optional().describe("spawn only: the worker's self-contained instruction."),
8600
+ outputSchema: z14.record(z14.string(), z14.unknown()).optional().describe(
9138
8601
  'spawn only: JSON Schema for the worker result. Defaults to {type:"object", properties:{summary:{type:"string"}}, required:["summary"]}.'
9139
8602
  ),
9140
- model: z15.string().optional().describe("spawn only: model override."),
9141
- workingDirectory: z15.string().optional().describe("spawn only: working directory override."),
9142
- maxIterations: z15.number().int().min(1).max(500).optional().describe("spawn only."),
8603
+ model: z14.string().optional().describe("spawn only: model override."),
8604
+ workingDirectory: z14.string().optional().describe("spawn only: working directory override."),
8605
+ maxIterations: z14.number().int().min(1).max(500).optional().describe("spawn only."),
9143
8606
  // message
9144
- text: z15.string().optional().describe("message only: the text to deliver to the worker."),
9145
- force: z15.boolean().optional().describe("message only: soft-interrupt the current step."),
8607
+ text: z14.string().optional().describe("message only: the text to deliver to the worker."),
8608
+ force: z14.boolean().optional().describe("message only: soft-interrupt the current step."),
9146
8609
  // answer_question
9147
- questionId: z15.string().optional().describe("answer_question only: pending question id (e.g. q_abc123)."),
9148
- answer: z15.string().optional().describe("answer_question only: your answer.")
8610
+ questionId: z14.string().optional().describe("answer_question only: pending question id (e.g. q_abc123)."),
8611
+ answer: z14.string().optional().describe("answer_question only: your answer.")
9149
8612
  });
9150
- messengerInputSchema = z15.object({
9151
- action: z15.enum(["list_channels", "post"]),
8613
+ messengerInputSchema = z14.object({
8614
+ action: z14.enum(["list_channels", "post"]),
9152
8615
  // post
9153
- channel: z15.string().optional().describe('post only: channel id (e.g. "slack").'),
9154
- to: z15.string().optional().describe('post only: destination. Slack: channel id (C0123), user id (U0123), or "#channel-name".'),
9155
- text: z15.string().optional().describe("post only: message body."),
9156
- threadTs: z15.string().optional().describe("post + slack: reply in this thread."),
9157
- subject: z15.string().optional().describe("post + email: subject (future).")
8616
+ channel: z14.string().optional().describe('post only: channel id (e.g. "slack").'),
8617
+ to: z14.string().optional().describe('post only: destination. Slack: channel id (C0123), user id (U0123), or "#channel-name".'),
8618
+ text: z14.string().optional().describe("post only: message body."),
8619
+ threadTs: z14.string().optional().describe("post + slack: reply in this thread."),
8620
+ subject: z14.string().optional().describe("post + email: subject (future).")
9158
8621
  });
9159
- scheduleInputSchema = z15.object({
9160
- action: z15.enum(["create", "list", "update", "delete", "pause", "resume"]),
8622
+ scheduleInputSchema = z14.object({
8623
+ action: z14.enum(["create", "list", "update", "delete", "pause", "resume"]),
9161
8624
  // create / update
9162
- name: z15.string().optional().describe("create | update"),
9163
- cron: z15.string().optional().describe('create | update: 5-field cron (e.g. "0 9 * * 1-5" = weekdays at 9am).'),
9164
- prompt: z15.string().optional().describe("create | update: the prompt injected when the schedule fires."),
9165
- replyChannel: z15.string().optional().describe("create | update: default channel id for orchestrator replies."),
8625
+ name: z14.string().optional().describe("create | update"),
8626
+ cron: z14.string().optional().describe('create | update: 5-field cron (e.g. "0 9 * * 1-5" = weekdays at 9am).'),
8627
+ prompt: z14.string().optional().describe("create | update: the prompt injected when the schedule fires."),
8628
+ replyChannel: z14.string().optional().describe("create | update: default channel id for orchestrator replies."),
9166
8629
  // update / delete / pause / resume
9167
- id: z15.string().optional().describe("update | delete | pause | resume: schedule id."),
9168
- enabled: z15.boolean().optional().describe("update only.")
8630
+ id: z14.string().optional().describe("update | delete | pause | resume: schedule id."),
8631
+ enabled: z14.boolean().optional().describe("update only.")
9169
8632
  });
9170
- webhookInputSchema = z15.object({
9171
- action: z15.enum(["create", "list", "update", "delete"]),
9172
- name: z15.string().optional().describe("create | update."),
9173
- wake: z15.enum(["now", "next"]).optional().describe("create | update: now = wake orchestrator immediately; next = add as context."),
9174
- template: z15.string().optional().describe("create | update: mustache-style template ({{path.to.field}}). Defaults to pretty-printed JSON."),
9175
- id: z15.string().optional().describe("update | delete: webhook id."),
9176
- rotateToken: z15.boolean().optional().describe("update only: regenerate the URL token.")
8633
+ webhookInputSchema = z14.object({
8634
+ action: z14.enum(["create", "list", "update", "delete"]),
8635
+ name: z14.string().optional().describe("create | update."),
8636
+ wake: z14.enum(["now", "next"]).optional().describe("create | update: now = wake orchestrator immediately; next = add as context."),
8637
+ template: z14.string().optional().describe("create | update: mustache-style template ({{path.to.field}}). Defaults to pretty-printed JSON."),
8638
+ id: z14.string().optional().describe("update | delete: webhook id."),
8639
+ rotateToken: z14.boolean().optional().describe("update only: regenerate the URL token.")
9177
8640
  });
9178
8641
  }
9179
8642
  });
9180
8643
 
9181
8644
  // src/integrations/mcp/store.ts
9182
- import { nanoid as nanoid7 } from "nanoid";
9183
- import { existsSync as existsSync17, readFileSync as readFileSync8 } from "fs";
9184
- import { resolve as resolve10, join as join10 } from "path";
8645
+ import { nanoid as nanoid6 } from "nanoid";
8646
+ import { existsSync as existsSync16, readFileSync as readFileSync7 } from "fs";
8647
+ import { resolve as resolve10, join as join9 } from "path";
9185
8648
  function readServers() {
9186
8649
  try {
9187
8650
  const cfg = getConfig();
@@ -9193,12 +8656,12 @@ function readServers() {
9193
8656
  function refreshMcpServersFromDisk() {
9194
8657
  const candidates = [
9195
8658
  resolve10(process.cwd(), "sparkecoder.config.json"),
9196
- join10(ensureAppDataDirectory(), "sparkecoder.config.json")
8659
+ join9(ensureAppDataDirectory(), "sparkecoder.config.json")
9197
8660
  ];
9198
8661
  for (const path of candidates) {
9199
- if (!existsSync17(path)) continue;
8662
+ if (!existsSync16(path)) continue;
9200
8663
  try {
9201
- const raw = JSON.parse(readFileSync8(path, "utf-8"));
8664
+ const raw = JSON.parse(readFileSync7(path, "utf-8"));
9202
8665
  const servers2 = Array.isArray(raw?.mcp?.servers) ? raw.mcp.servers : [];
9203
8666
  setMcpServers(servers2);
9204
8667
  return servers2;
@@ -9217,7 +8680,7 @@ function createMcpServer(input) {
9217
8680
  const all = readServers();
9218
8681
  validateInput(input);
9219
8682
  const row = {
9220
- id: `mcp_${nanoid7(10)}`,
8683
+ id: `mcp_${nanoid6(10)}`,
9221
8684
  name: sanitizeName(input.name),
9222
8685
  transport: input.transport,
9223
8686
  url: input.url,
@@ -9808,15 +9271,15 @@ var recorder_exports = {};
9808
9271
  __export(recorder_exports, {
9809
9272
  FrameRecorder: () => FrameRecorder
9810
9273
  });
9811
- import { exec as exec6 } from "child_process";
9812
- import { promisify as promisify6 } from "util";
9274
+ import { exec as exec5 } from "child_process";
9275
+ import { promisify as promisify5 } from "util";
9813
9276
  import { writeFile as writeFile5, mkdir as mkdir4, readFile as readFile11, unlink as unlink2, readdir as readdir5, rm } from "fs/promises";
9814
- import { join as join11 } from "path";
9815
- import { tmpdir as tmpdir2 } from "os";
9816
- import { nanoid as nanoid8 } from "nanoid";
9277
+ import { join as join10 } from "path";
9278
+ import { tmpdir } from "os";
9279
+ import { nanoid as nanoid7 } from "nanoid";
9817
9280
  async function checkFfmpeg() {
9818
9281
  try {
9819
- await execAsync6("ffmpeg -version", { timeout: 5e3 });
9282
+ await execAsync5("ffmpeg -version", { timeout: 5e3 });
9820
9283
  return true;
9821
9284
  } catch {
9822
9285
  return false;
@@ -9828,11 +9291,11 @@ async function cleanup(dir) {
9828
9291
  } catch {
9829
9292
  }
9830
9293
  }
9831
- var execAsync6, FrameRecorder;
9294
+ var execAsync5, FrameRecorder;
9832
9295
  var init_recorder = __esm({
9833
9296
  "src/browser/recorder.ts"() {
9834
9297
  "use strict";
9835
- execAsync6 = promisify6(exec6);
9298
+ execAsync5 = promisify5(exec5);
9836
9299
  FrameRecorder = class {
9837
9300
  frames = [];
9838
9301
  startTime = null;
@@ -9868,21 +9331,21 @@ var init_recorder = __esm({
9868
9331
  */
9869
9332
  async encode() {
9870
9333
  if (this.frames.length === 0) return null;
9871
- const workDir = join11(tmpdir2(), `sparkecoder-recording-${nanoid8(8)}`);
9334
+ const workDir = join10(tmpdir(), `sparkecoder-recording-${nanoid7(8)}`);
9872
9335
  await mkdir4(workDir, { recursive: true });
9873
9336
  try {
9874
9337
  for (let i = 0; i < this.frames.length; i++) {
9875
- const framePath = join11(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
9338
+ const framePath = join10(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
9876
9339
  await writeFile5(framePath, this.frames[i].data);
9877
9340
  }
9878
9341
  const duration = (this.frames[this.frames.length - 1].timestamp - this.frames[0].timestamp) / 1e3;
9879
9342
  const fps = duration > 0 ? Math.round(this.frames.length / duration) : 10;
9880
9343
  const clampedFps = Math.max(1, Math.min(fps, 30));
9881
- const outputPath = join11(workDir, `recording_${this.sessionId}.mp4`);
9344
+ const outputPath = join10(workDir, `recording_${this.sessionId}.mp4`);
9882
9345
  const hasFfmpeg = await checkFfmpeg();
9883
9346
  if (hasFfmpeg) {
9884
- await execAsync6(
9885
- `ffmpeg -y -framerate ${clampedFps} -i "${join11(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
9347
+ await execAsync5(
9348
+ `ffmpeg -y -framerate ${clampedFps} -i "${join10(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
9886
9349
  { timeout: 12e4 }
9887
9350
  );
9888
9351
  } else {
@@ -9894,7 +9357,7 @@ var init_recorder = __esm({
9894
9357
  const files = await readdir5(workDir);
9895
9358
  for (const f of files) {
9896
9359
  if (f.startsWith("frame_")) {
9897
- await unlink2(join11(workDir, f)).catch(() => {
9360
+ await unlink2(join10(workDir, f)).catch(() => {
9898
9361
  });
9899
9362
  }
9900
9363
  }
@@ -9919,11 +9382,11 @@ var init_recorder = __esm({
9919
9382
  import {
9920
9383
  streamText as streamText2,
9921
9384
  generateText as generateText3,
9922
- tool as tool15,
9385
+ tool as tool14,
9923
9386
  stepCountIs as stepCountIs2
9924
9387
  } from "ai";
9925
- import { z as z16 } from "zod";
9926
- import { nanoid as nanoid9 } from "nanoid";
9388
+ import { z as z15 } from "zod";
9389
+ import { nanoid as nanoid8 } from "nanoid";
9927
9390
  function anySignal(signals) {
9928
9391
  const ctrl = new AbortController();
9929
9392
  for (const s of signals) {
@@ -9992,14 +9455,10 @@ var init_agent = __esm({
9992
9455
  */
9993
9456
  async createToolsWithCallbacks(options) {
9994
9457
  const config = getConfig();
9995
- const sessionConfig = this.session.config || {};
9996
9458
  const tools = await createTools({
9997
9459
  sessionId: this.session.id,
9998
9460
  workingDirectory: this.session.workingDirectory,
9999
9461
  skillsDirectories: config.resolvedSkillsDirectories,
10000
- enableComputerUse: sessionConfig.computerUseEnabled === true,
10001
- computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
10002
- computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight,
10003
9462
  onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
10004
9463
  onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
10005
9464
  onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0
@@ -10049,14 +9508,10 @@ var init_agent = __esm({
10049
9508
  keepRecentMessages: config.context?.keepRecentMessages || 10,
10050
9509
  autoSummarize: config.context?.autoSummarize ?? true
10051
9510
  });
10052
- const sessionConfig = session.config || {};
10053
9511
  const tools = await createTools({
10054
9512
  sessionId: session.id,
10055
9513
  workingDirectory: session.workingDirectory,
10056
- skillsDirectories: config.resolvedSkillsDirectories,
10057
- enableComputerUse: sessionConfig.computerUseEnabled === true,
10058
- computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
10059
- computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight
9514
+ skillsDirectories: config.resolvedSkillsDirectories
10060
9515
  });
10061
9516
  if (session.config?.role === "orchestrator") {
10062
9517
  const baseUrl = `http://127.0.0.1:${config.server?.port ?? 3141}`;
@@ -10294,14 +9749,10 @@ ${personality.trim()}`;
10294
9749
  });
10295
9750
  }
10296
9751
  };
10297
- const taskSessionConfig = this.session.config || {};
10298
9752
  const taskTools = await createTools({
10299
9753
  sessionId: this.session.id,
10300
9754
  workingDirectory: this.session.workingDirectory,
10301
9755
  skillsDirectories: config.resolvedSkillsDirectories,
10302
- enableComputerUse: taskSessionConfig.computerUseEnabled === true,
10303
- computerUseDisplayWidth: taskSessionConfig.computerUseDisplayWidth,
10304
- computerUseDisplayHeight: taskSessionConfig.computerUseDisplayHeight,
10305
9756
  onBashProgress: bashProgressHandler,
10306
9757
  onWriteFileProgress: (progress) => {
10307
9758
  options.onToolProgress?.({ toolName: "write_file", data: progress });
@@ -10649,11 +10100,11 @@ ${p.text}` : p.text;
10649
10100
  const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
10650
10101
  if (!isRemoteConfigured2()) return [];
10651
10102
  const { readFile: readFile12 } = await import("fs/promises");
10652
- const { join: join17, basename: basename6 } = await import("path");
10103
+ const { join: join16, basename: basename6 } = await import("path");
10653
10104
  const urls = [];
10654
10105
  for (const filePath of filePaths) {
10655
10106
  try {
10656
- const fullPath = filePath.startsWith("/") ? filePath : join17(this.session.workingDirectory, filePath);
10107
+ const fullPath = filePath.startsWith("/") ? filePath : join16(this.session.workingDirectory, filePath);
10657
10108
  const fileName = basename6(fullPath);
10658
10109
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
10659
10110
  const mimeMap = {
@@ -10711,11 +10162,11 @@ ${p.text}` : p.text;
10711
10162
  wrappedTools[name] = originalTool;
10712
10163
  continue;
10713
10164
  }
10714
- wrappedTools[name] = tool15({
10165
+ wrappedTools[name] = tool14({
10715
10166
  description: originalTool.description || "",
10716
- inputSchema: originalTool.inputSchema || z16.object({}),
10167
+ inputSchema: originalTool.inputSchema || z15.object({}),
10717
10168
  execute: async (input, toolOptions) => {
10718
- const toolCallId = toolOptions.toolCallId || nanoid9();
10169
+ const toolCallId = toolOptions.toolCallId || nanoid8();
10719
10170
  const execution = toolExecutionQueries.create({
10720
10171
  sessionId: this.session.id,
10721
10172
  toolName: name,
@@ -10733,10 +10184,10 @@ ${p.text}` : p.text;
10733
10184
  const resolverData = approvalResolvers.get(toolCallId);
10734
10185
  approvalResolvers.delete(toolCallId);
10735
10186
  this.pendingApprovals.delete(toolCallId);
10736
- const exec8 = await execution;
10187
+ const exec7 = await execution;
10737
10188
  if (!approved) {
10738
10189
  const reason = resolverData?.reason || "User rejected the tool execution";
10739
- await toolExecutionQueries.reject(exec8.id);
10190
+ await toolExecutionQueries.reject(exec7.id);
10740
10191
  await sessionQueries.updateStatus(this.session.id, "active");
10741
10192
  return {
10742
10193
  status: "rejected",
@@ -10746,14 +10197,14 @@ ${p.text}` : p.text;
10746
10197
  message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
10747
10198
  };
10748
10199
  }
10749
- await toolExecutionQueries.approve(exec8.id);
10200
+ await toolExecutionQueries.approve(exec7.id);
10750
10201
  await sessionQueries.updateStatus(this.session.id, "active");
10751
10202
  try {
10752
10203
  const result = await originalTool.execute(input, toolOptions);
10753
- await toolExecutionQueries.complete(exec8.id, result);
10204
+ await toolExecutionQueries.complete(exec7.id, result);
10754
10205
  return result;
10755
10206
  } catch (error) {
10756
- await toolExecutionQueries.complete(exec8.id, null, error.message);
10207
+ await toolExecutionQueries.complete(exec7.id, null, error.message);
10757
10208
  throw error;
10758
10209
  }
10759
10210
  }
@@ -10858,19 +10309,19 @@ var init_session_lock = __esm({
10858
10309
  });
10859
10310
 
10860
10311
  // src/orchestrator/webhook-events.ts
10861
- import { existsSync as existsSync18, readFileSync as readFileSync9, appendFileSync as appendFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
10862
- import { dirname as dirname6, join as join12 } from "path";
10863
- import { nanoid as nanoid10 } from "nanoid";
10312
+ import { existsSync as existsSync17, readFileSync as readFileSync8, appendFileSync as appendFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync6 } from "fs";
10313
+ import { dirname as dirname6, join as join11 } from "path";
10314
+ import { nanoid as nanoid9 } from "nanoid";
10864
10315
  function logFilePath() {
10865
- return join12(getAppDataDirectory(), "webhook-events.jsonl");
10316
+ return join11(getAppDataDirectory(), "webhook-events.jsonl");
10866
10317
  }
10867
10318
  function ensureLoaded() {
10868
10319
  if (cache !== null) return cache;
10869
10320
  cache = [];
10870
10321
  try {
10871
10322
  const p = logFilePath();
10872
- if (!existsSync18(p)) return cache;
10873
- const lines = readFileSync9(p, "utf-8").split("\n").filter(Boolean);
10323
+ if (!existsSync17(p)) return cache;
10324
+ const lines = readFileSync8(p, "utf-8").split("\n").filter(Boolean);
10874
10325
  for (const line of lines) {
10875
10326
  try {
10876
10327
  cache.push(JSON.parse(line));
@@ -10894,14 +10345,14 @@ function appendEvent(ev) {
10894
10345
  if (list.length > MAX_EVENTS) list.shift();
10895
10346
  try {
10896
10347
  const p = logFilePath();
10897
- mkdirSync7(dirname6(p), { recursive: true });
10348
+ mkdirSync6(dirname6(p), { recursive: true });
10898
10349
  appendFileSync3(p, JSON.stringify(ev) + "\n");
10899
10350
  } catch {
10900
10351
  }
10901
10352
  }
10902
10353
  function recordEvent(ev) {
10903
10354
  const full = {
10904
- id: ev.id ?? nanoid10(),
10355
+ id: ev.id ?? nanoid9(),
10905
10356
  ts: ev.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
10906
10357
  source: ev.source,
10907
10358
  status: ev.status,
@@ -10925,7 +10376,7 @@ function updateEvent(id, patch) {
10925
10376
  list[i] = { ...list[i], ...patch };
10926
10377
  try {
10927
10378
  const p = logFilePath();
10928
- mkdirSync7(dirname6(p), { recursive: true });
10379
+ mkdirSync6(dirname6(p), { recursive: true });
10929
10380
  writeFileSync3(p, list.map((e) => JSON.stringify(e)).join("\n") + "\n");
10930
10381
  } catch {
10931
10382
  }
@@ -11232,8 +10683,8 @@ import { Hono as Hono9 } from "hono";
11232
10683
  import { serve } from "@hono/node-server";
11233
10684
  import { cors } from "hono/cors";
11234
10685
  import { logger } from "hono/logger";
11235
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "fs";
11236
- import { resolve as resolve11, dirname as dirname8, join as join16 } from "path";
10686
+ import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
10687
+ import { resolve as resolve11, dirname as dirname8, join as join15 } from "path";
11237
10688
  import { spawn as spawn2 } from "child_process";
11238
10689
  import { createServer as createNetServer } from "net";
11239
10690
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -11246,11 +10697,11 @@ init_tmux();
11246
10697
  init_checkpoints();
11247
10698
  import { Hono } from "hono";
11248
10699
  import { zValidator } from "@hono/zod-validator";
11249
- import { z as z17 } from "zod";
11250
- import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
10700
+ import { z as z16 } from "zod";
10701
+ import { existsSync as existsSync18, mkdirSync as mkdirSync7, writeFileSync as writeFileSync4, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync2 } from "fs";
11251
10702
  import { readdir as readdir6 } from "fs/promises";
11252
- import { join as join13, basename as basename5, extname as extname8, relative as relative9 } from "path";
11253
- import { nanoid as nanoid11 } from "nanoid";
10703
+ import { join as join12, basename as basename5, extname as extname8, relative as relative9 } from "path";
10704
+ import { nanoid as nanoid10 } from "nanoid";
11254
10705
 
11255
10706
  // src/tasks/agent-status.ts
11256
10707
  init_questions();
@@ -11308,22 +10759,22 @@ function cleanupPendingInputs() {
11308
10759
  }
11309
10760
  }
11310
10761
  }
11311
- var createSessionSchema = z17.object({
11312
- name: z17.string().optional(),
11313
- workingDirectory: z17.string().optional(),
11314
- model: z17.string().optional(),
11315
- toolApprovals: z17.record(z17.string(), z17.boolean()).optional(),
11316
- // Optional full session-config passthrough (computerUseEnabled, etc.)
11317
- config: z17.record(z17.string(), z17.unknown()).optional(),
11318
- role: z17.enum(["orchestrator", "worker", "chat"]).optional()
10762
+ var createSessionSchema = z16.object({
10763
+ name: z16.string().optional(),
10764
+ workingDirectory: z16.string().optional(),
10765
+ model: z16.string().optional(),
10766
+ toolApprovals: z16.record(z16.string(), z16.boolean()).optional(),
10767
+ // Optional full session-config passthrough (role, persona, etc.)
10768
+ config: z16.record(z16.string(), z16.unknown()).optional(),
10769
+ role: z16.enum(["orchestrator", "worker", "chat"]).optional()
11319
10770
  });
11320
- var paginationQuerySchema = z17.object({
11321
- limit: z17.string().optional(),
11322
- offset: z17.string().optional(),
11323
- role: z17.enum(["orchestrator", "worker", "chat", "all"]).optional()
10771
+ var paginationQuerySchema = z16.object({
10772
+ limit: z16.string().optional(),
10773
+ offset: z16.string().optional(),
10774
+ role: z16.enum(["orchestrator", "worker", "chat", "all"]).optional()
11324
10775
  });
11325
- var messagesQuerySchema = z17.object({
11326
- limit: z17.string().optional()
10776
+ var messagesQuerySchema = z16.object({
10777
+ limit: z16.string().optional()
11327
10778
  });
11328
10779
  sessions2.get(
11329
10780
  "/",
@@ -11392,15 +10843,11 @@ sessions2.post(
11392
10843
  async (c) => {
11393
10844
  const body = c.req.valid("json");
11394
10845
  const config = getConfig();
11395
- const cuDefault = process.env.SPARKECODER_COMPUTER_USE === "1";
11396
10846
  const baseConfig = body.config || {};
11397
10847
  const mergedConfig = {
11398
10848
  ...baseConfig,
11399
10849
  ...body.toolApprovals ? { toolApprovals: body.toolApprovals } : {},
11400
- ...body.role ? { role: body.role } : {},
11401
- // Turn on computer use by default if the server was launched with --enable-computer-use,
11402
- // unless the client explicitly provided a value.
11403
- ...cuDefault && baseConfig.computerUseEnabled === void 0 ? { computerUseEnabled: true } : {}
10850
+ ...body.role ? { role: body.role } : {}
11404
10851
  };
11405
10852
  const agent = await Agent.create({
11406
10853
  name: body.name,
@@ -11577,10 +11024,10 @@ sessions2.get("/:id/tools", async (c) => {
11577
11024
  count: executions.length
11578
11025
  });
11579
11026
  });
11580
- var updateSessionSchema = z17.object({
11581
- model: z17.string().optional(),
11582
- name: z17.string().optional(),
11583
- toolApprovals: z17.record(z17.string(), z17.boolean()).optional()
11027
+ var updateSessionSchema = z16.object({
11028
+ model: z16.string().optional(),
11029
+ name: z16.string().optional(),
11030
+ toolApprovals: z16.record(z16.string(), z16.boolean()).optional()
11584
11031
  });
11585
11032
  sessions2.patch(
11586
11033
  "/:id",
@@ -11650,10 +11097,10 @@ sessions2.post("/:id/clear", async (c) => {
11650
11097
  await agent.clearContext();
11651
11098
  return c.json({ success: true, sessionId: id });
11652
11099
  });
11653
- var injectMessageSchema = z17.object({
11654
- text: z17.string().min(1),
11655
- source: z17.enum(["orchestrator", "user", "system"]).optional(),
11656
- force: z17.boolean().optional()
11100
+ var injectMessageSchema = z16.object({
11101
+ text: z16.string().min(1),
11102
+ source: z16.enum(["orchestrator", "user", "system"]).optional(),
11103
+ force: z16.boolean().optional()
11657
11104
  });
11658
11105
  sessions2.post(
11659
11106
  "/:id/messages",
@@ -11667,8 +11114,8 @@ sessions2.post(
11667
11114
  return c.json({ success: true, sessionId: id, queued: true, force });
11668
11115
  }
11669
11116
  );
11670
- var pendingInputSchema = z17.object({
11671
- text: z17.string()
11117
+ var pendingInputSchema = z16.object({
11118
+ text: z16.string()
11672
11119
  });
11673
11120
  sessions2.post(
11674
11121
  "/:id/pending-input",
@@ -11699,13 +11146,13 @@ sessions2.get("/:id/pending-input", async (c) => {
11699
11146
  createdAt: pending.createdAt.toISOString()
11700
11147
  });
11701
11148
  });
11702
- var devtoolsContextSchema = z17.object({
11703
- url: z17.string(),
11704
- path: z17.string(),
11705
- pageName: z17.string().optional(),
11706
- screenWidth: z17.number().optional(),
11707
- screenHeight: z17.number().optional(),
11708
- devicePixelRatio: z17.number().optional()
11149
+ var devtoolsContextSchema = z16.object({
11150
+ url: z16.string(),
11151
+ path: z16.string(),
11152
+ pageName: z16.string().optional(),
11153
+ screenWidth: z16.number().optional(),
11154
+ screenHeight: z16.number().optional(),
11155
+ devicePixelRatio: z16.number().optional()
11709
11156
  });
11710
11157
  sessions2.post(
11711
11158
  "/:id/devtools-context",
@@ -11891,12 +11338,12 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
11891
11338
  });
11892
11339
  function getAttachmentsDir(sessionId) {
11893
11340
  const appDataDir = getAppDataDirectory();
11894
- return join13(appDataDir, "attachments", sessionId);
11341
+ return join12(appDataDir, "attachments", sessionId);
11895
11342
  }
11896
11343
  function ensureAttachmentsDir(sessionId) {
11897
11344
  const dir = getAttachmentsDir(sessionId);
11898
- if (!existsSync19(dir)) {
11899
- mkdirSync8(dir, { recursive: true });
11345
+ if (!existsSync18(dir)) {
11346
+ mkdirSync7(dir, { recursive: true });
11900
11347
  }
11901
11348
  return dir;
11902
11349
  }
@@ -11907,12 +11354,12 @@ sessions2.get("/:id/attachments", async (c) => {
11907
11354
  return c.json({ error: "Session not found" }, 404);
11908
11355
  }
11909
11356
  const dir = getAttachmentsDir(sessionId);
11910
- if (!existsSync19(dir)) {
11357
+ if (!existsSync18(dir)) {
11911
11358
  return c.json({ sessionId, attachments: [], count: 0 });
11912
11359
  }
11913
11360
  const files = readdirSync3(dir);
11914
11361
  const attachments = files.map((filename) => {
11915
- const filePath = join13(dir, filename);
11362
+ const filePath = join12(dir, filename);
11916
11363
  const stats = statSync2(filePath);
11917
11364
  return {
11918
11365
  id: filename.split("_")[0],
@@ -11944,10 +11391,10 @@ sessions2.post("/:id/attachments", async (c) => {
11944
11391
  return c.json({ error: "No file provided" }, 400);
11945
11392
  }
11946
11393
  const dir = ensureAttachmentsDir(sessionId);
11947
- const id = nanoid11(10);
11394
+ const id = nanoid10(10);
11948
11395
  const ext = extname8(file.name) || "";
11949
11396
  const safeFilename = `${id}_${basename5(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
11950
- const filePath = join13(dir, safeFilename);
11397
+ const filePath = join12(dir, safeFilename);
11951
11398
  const arrayBuffer = await file.arrayBuffer();
11952
11399
  writeFileSync4(filePath, Buffer.from(arrayBuffer));
11953
11400
  return c.json({
@@ -11970,10 +11417,10 @@ sessions2.post("/:id/attachments", async (c) => {
11970
11417
  return c.json({ error: "Missing filename or data" }, 400);
11971
11418
  }
11972
11419
  const dir = ensureAttachmentsDir(sessionId);
11973
- const id = nanoid11(10);
11420
+ const id = nanoid10(10);
11974
11421
  const ext = extname8(body.filename) || "";
11975
11422
  const safeFilename = `${id}_${basename5(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
11976
- const filePath = join13(dir, safeFilename);
11423
+ const filePath = join12(dir, safeFilename);
11977
11424
  let base64Data = body.data;
11978
11425
  if (base64Data.includes(",")) {
11979
11426
  base64Data = base64Data.split(",")[1];
@@ -12002,7 +11449,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
12002
11449
  return c.json({ error: "Session not found" }, 404);
12003
11450
  }
12004
11451
  const dir = getAttachmentsDir(sessionId);
12005
- if (!existsSync19(dir)) {
11452
+ if (!existsSync18(dir)) {
12006
11453
  return c.json({ error: "Attachment not found" }, 404);
12007
11454
  }
12008
11455
  const files = readdirSync3(dir);
@@ -12010,14 +11457,14 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
12010
11457
  if (!file) {
12011
11458
  return c.json({ error: "Attachment not found" }, 404);
12012
11459
  }
12013
- const filePath = join13(dir, file);
12014
- unlinkSync3(filePath);
11460
+ const filePath = join12(dir, file);
11461
+ unlinkSync2(filePath);
12015
11462
  return c.json({ success: true, id: attachmentId });
12016
11463
  });
12017
- var filesQuerySchema = z17.object({
12018
- query: z17.string().optional(),
11464
+ var filesQuerySchema = z16.object({
11465
+ query: z16.string().optional(),
12019
11466
  // Filter query (e.g., "src/com" to match "src/components")
12020
- limit: z17.string().optional()
11467
+ limit: z16.string().optional()
12021
11468
  // Max results (default 50)
12022
11469
  });
12023
11470
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -12093,7 +11540,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
12093
11540
  const entries = await readdir6(currentDir, { withFileTypes: true });
12094
11541
  for (const entry2 of entries) {
12095
11542
  if (results.length >= limit * 2) break;
12096
- const fullPath = join13(currentDir, entry2.name);
11543
+ const fullPath = join12(currentDir, entry2.name);
12097
11544
  const relativePath = relative9(baseDir, fullPath);
12098
11545
  if (entry2.isDirectory() && IGNORED_DIRECTORIES.has(entry2.name)) {
12099
11546
  continue;
@@ -12141,7 +11588,7 @@ sessions2.get(
12141
11588
  return c.json({ error: "Session not found" }, 404);
12142
11589
  }
12143
11590
  const workingDirectory = session.workingDirectory;
12144
- if (!existsSync19(workingDirectory)) {
11591
+ if (!existsSync18(workingDirectory)) {
12145
11592
  return c.json({
12146
11593
  sessionId,
12147
11594
  workingDirectory,
@@ -12254,9 +11701,9 @@ init_session_lock();
12254
11701
  init_config();
12255
11702
  import { Hono as Hono2 } from "hono";
12256
11703
  import { zValidator as zValidator2 } from "@hono/zod-validator";
12257
- import { z as z18 } from "zod";
12258
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5 } from "fs";
12259
- import { join as join14 } from "path";
11704
+ import { z as z17 } from "zod";
11705
+ import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync5 } from "fs";
11706
+ import { join as join13 } from "path";
12260
11707
 
12261
11708
  // src/server/resumable-stream.ts
12262
11709
  import { createResumableStreamContext } from "resumable-stream/generic";
@@ -12343,7 +11790,7 @@ var streamContext = createResumableStreamContext({
12343
11790
 
12344
11791
  // src/server/routes/agents.ts
12345
11792
  init_checkpoints();
12346
- import { nanoid as nanoid12 } from "nanoid";
11793
+ import { nanoid as nanoid11 } from "nanoid";
12347
11794
  init_stream_proxy();
12348
11795
  init_recorder();
12349
11796
  init_remote();
@@ -12435,40 +11882,40 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
12435
11882
  ${prompt}`;
12436
11883
  }
12437
11884
  var agents = new Hono2();
12438
- var attachmentSchema = z18.object({
12439
- type: z18.enum(["image", "file"]),
12440
- data: z18.string(),
11885
+ var attachmentSchema = z17.object({
11886
+ type: z17.enum(["image", "file"]),
11887
+ data: z17.string(),
12441
11888
  // base64 data URL or raw base64
12442
- mediaType: z18.string().optional(),
12443
- filename: z18.string().optional()
11889
+ mediaType: z17.string().optional(),
11890
+ filename: z17.string().optional()
12444
11891
  });
12445
- var runPromptSchema = z18.object({
12446
- prompt: z18.string(),
11892
+ var runPromptSchema = z17.object({
11893
+ prompt: z17.string(),
12447
11894
  // Can be empty if attachments are provided
12448
- attachments: z18.array(attachmentSchema).optional()
11895
+ attachments: z17.array(attachmentSchema).optional()
12449
11896
  }).refine(
12450
11897
  (data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
12451
11898
  { message: "Either prompt or attachments must be provided" }
12452
11899
  );
12453
- var quickStartSchema = z18.object({
12454
- prompt: z18.string().min(1),
12455
- name: z18.string().optional(),
12456
- workingDirectory: z18.string().optional(),
12457
- model: z18.string().optional(),
12458
- toolApprovals: z18.record(z18.string(), z18.boolean()).optional()
12459
- });
12460
- var rejectSchema = z18.object({
12461
- reason: z18.string().optional()
11900
+ var quickStartSchema = z17.object({
11901
+ prompt: z17.string().min(1),
11902
+ name: z17.string().optional(),
11903
+ workingDirectory: z17.string().optional(),
11904
+ model: z17.string().optional(),
11905
+ toolApprovals: z17.record(z17.string(), z17.boolean()).optional()
11906
+ });
11907
+ var rejectSchema = z17.object({
11908
+ reason: z17.string().optional()
12462
11909
  }).optional();
12463
11910
  var streamAbortControllers = /* @__PURE__ */ new Map();
12464
11911
  function getAttachmentsDirectory(sessionId) {
12465
11912
  const appDataDir = getAppDataDirectory();
12466
- return join14(appDataDir, "attachments", sessionId);
11913
+ return join13(appDataDir, "attachments", sessionId);
12467
11914
  }
12468
11915
  async function saveAttachmentToDisk(sessionId, attachment, index) {
12469
11916
  const attachmentsDir = getAttachmentsDirectory(sessionId);
12470
- if (!existsSync20(attachmentsDir)) {
12471
- mkdirSync9(attachmentsDir, { recursive: true });
11917
+ if (!existsSync19(attachmentsDir)) {
11918
+ mkdirSync8(attachmentsDir, { recursive: true });
12472
11919
  }
12473
11920
  let filename = attachment.filename;
12474
11921
  if (!filename) {
@@ -12486,7 +11933,7 @@ async function saveAttachmentToDisk(sessionId, attachment, index) {
12486
11933
  attachment.mediaType = resized.mediaType;
12487
11934
  attachment.data = buffer.toString("base64");
12488
11935
  }
12489
- const filePath = join14(attachmentsDir, filename);
11936
+ const filePath = join13(attachmentsDir, filename);
12490
11937
  writeFileSync5(filePath, buffer);
12491
11938
  return filePath;
12492
11939
  }
@@ -12923,7 +12370,7 @@ ${prompt}` });
12923
12370
  });
12924
12371
  } catch {
12925
12372
  }
12926
- const streamId = `stream_${id}_${nanoid12(10)}`;
12373
+ const streamId = `stream_${id}_${nanoid11(10)}`;
12927
12374
  console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
12928
12375
  await activeStreamQueries.create(id, streamId);
12929
12376
  const stream = await streamContext.resumableStream(
@@ -13128,7 +12575,7 @@ agents.post(
13128
12575
  });
13129
12576
  const session = agent.getSession();
13130
12577
  const enrichedPrompt = enrichPromptWithDevtoolsContext(session.id, body.prompt);
13131
- const streamId = `stream_${session.id}_${nanoid12(10)}`;
12578
+ const streamId = `stream_${session.id}_${nanoid11(10)}`;
13132
12579
  await createCheckpoint(session.id, session.workingDirectory, 0);
13133
12580
  await activeStreamQueries.create(session.id, streamId);
13134
12581
  const createQuickStreamProducer = () => {
@@ -13395,23 +12842,23 @@ agents.post(
13395
12842
  });
13396
12843
  }
13397
12844
  );
13398
- var browserInputSchema = z18.object({
13399
- type: z18.enum(["input_mouse", "input_keyboard", "input_touch"]),
13400
- eventType: z18.string(),
13401
- x: z18.number().optional(),
13402
- y: z18.number().optional(),
13403
- button: z18.string().optional(),
13404
- clickCount: z18.number().optional(),
13405
- deltaX: z18.number().optional(),
13406
- deltaY: z18.number().optional(),
13407
- key: z18.string().optional(),
13408
- code: z18.string().optional(),
13409
- text: z18.string().optional(),
13410
- modifiers: z18.number().optional(),
13411
- touchPoints: z18.array(z18.object({
13412
- x: z18.number(),
13413
- y: z18.number(),
13414
- id: z18.number().optional()
12845
+ var browserInputSchema = z17.object({
12846
+ type: z17.enum(["input_mouse", "input_keyboard", "input_touch"]),
12847
+ eventType: z17.string(),
12848
+ x: z17.number().optional(),
12849
+ y: z17.number().optional(),
12850
+ button: z17.string().optional(),
12851
+ clickCount: z17.number().optional(),
12852
+ deltaX: z17.number().optional(),
12853
+ deltaY: z17.number().optional(),
12854
+ key: z17.string().optional(),
12855
+ code: z17.string().optional(),
12856
+ text: z17.string().optional(),
12857
+ modifiers: z17.number().optional(),
12858
+ touchPoints: z17.array(z17.object({
12859
+ x: z17.number(),
12860
+ y: z17.number(),
12861
+ id: z17.number().optional()
13415
12862
  })).optional()
13416
12863
  });
13417
12864
  agents.post(
@@ -13446,27 +12893,27 @@ agents.get("/:id/browser-stream", async (c) => {
13446
12893
  init_config();
13447
12894
  import { Hono as Hono3 } from "hono";
13448
12895
  import { zValidator as zValidator3 } from "@hono/zod-validator";
13449
- import { z as z19 } from "zod";
13450
- import { readFileSync as readFileSync10 } from "fs";
12896
+ import { z as z18 } from "zod";
12897
+ import { readFileSync as readFileSync9 } from "fs";
13451
12898
  import { fileURLToPath as fileURLToPath3 } from "url";
13452
- import { dirname as dirname7, join as join15 } from "path";
12899
+ import { dirname as dirname7, join as join14 } from "path";
13453
12900
  var __filename = fileURLToPath3(import.meta.url);
13454
12901
  var __dirname = dirname7(__filename);
13455
12902
  var possiblePaths = [
13456
- join15(__dirname, "../package.json"),
12903
+ join14(__dirname, "../package.json"),
13457
12904
  // From dist/server -> dist/../package.json
13458
- join15(__dirname, "../../package.json"),
12905
+ join14(__dirname, "../../package.json"),
13459
12906
  // From dist/server (if nested differently)
13460
- join15(__dirname, "../../../package.json"),
12907
+ join14(__dirname, "../../../package.json"),
13461
12908
  // From src/server/routes (development)
13462
- join15(process.cwd(), "package.json")
12909
+ join14(process.cwd(), "package.json")
13463
12910
  // From current working directory
13464
12911
  ];
13465
12912
  var currentVersion = "0.0.0";
13466
12913
  var packageName = "sparkecoder";
13467
12914
  for (const packageJsonPath of possiblePaths) {
13468
12915
  try {
13469
- const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
12916
+ const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
13470
12917
  if (packageJson.name === "sparkecoder") {
13471
12918
  currentVersion = packageJson.version || "0.0.0";
13472
12919
  packageName = packageJson.name || "sparkecoder";
@@ -13558,9 +13005,9 @@ health.get("/api-keys", async (c) => {
13558
13005
  supportedProviders: SUPPORTED_PROVIDERS
13559
13006
  });
13560
13007
  });
13561
- var setApiKeySchema = z19.object({
13562
- provider: z19.string(),
13563
- apiKey: z19.string().min(1)
13008
+ var setApiKeySchema = z18.object({
13009
+ provider: z18.string(),
13010
+ apiKey: z18.string().min(1)
13564
13011
  });
13565
13012
  health.post(
13566
13013
  "/api-keys",
@@ -13601,12 +13048,12 @@ init_tmux();
13601
13048
  init_db();
13602
13049
  import { Hono as Hono4 } from "hono";
13603
13050
  import { zValidator as zValidator4 } from "@hono/zod-validator";
13604
- import { z as z20 } from "zod";
13051
+ import { z as z19 } from "zod";
13605
13052
  var terminals = new Hono4();
13606
- var spawnSchema = z20.object({
13607
- command: z20.string(),
13608
- cwd: z20.string().optional(),
13609
- name: z20.string().optional()
13053
+ var spawnSchema = z19.object({
13054
+ command: z19.string(),
13055
+ cwd: z19.string().optional(),
13056
+ name: z19.string().optional()
13610
13057
  });
13611
13058
  terminals.post(
13612
13059
  "/:sessionId/terminals",
@@ -13687,8 +13134,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
13687
13134
  // We don't track exit codes in tmux mode
13688
13135
  });
13689
13136
  });
13690
- var logsQuerySchema = z20.object({
13691
- tail: z20.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
13137
+ var logsQuerySchema = z19.object({
13138
+ tail: z19.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
13692
13139
  });
13693
13140
  terminals.get(
13694
13141
  "/:sessionId/terminals/:terminalId/logs",
@@ -13712,8 +13159,8 @@ terminals.get(
13712
13159
  });
13713
13160
  }
13714
13161
  );
13715
- var killSchema = z20.object({
13716
- signal: z20.enum(["SIGTERM", "SIGKILL"]).optional()
13162
+ var killSchema = z19.object({
13163
+ signal: z19.enum(["SIGTERM", "SIGKILL"]).optional()
13717
13164
  });
13718
13165
  terminals.post(
13719
13166
  "/:sessionId/terminals/:terminalId/kill",
@@ -13727,8 +13174,8 @@ terminals.post(
13727
13174
  return c.json({ success: true, message: "Terminal killed" });
13728
13175
  }
13729
13176
  );
13730
- var writeSchema = z20.object({
13731
- input: z20.string()
13177
+ var writeSchema = z19.object({
13178
+ input: z19.string()
13732
13179
  });
13733
13180
  terminals.post(
13734
13181
  "/:sessionId/terminals/:terminalId/write",
@@ -13915,23 +13362,23 @@ init_agent();
13915
13362
  init_config();
13916
13363
  import { Hono as Hono5 } from "hono";
13917
13364
  import { zValidator as zValidator5 } from "@hono/zod-validator";
13918
- import { z as z21 } from "zod";
13919
- import { nanoid as nanoid13 } from "nanoid";
13365
+ import { z as z20 } from "zod";
13366
+ import { nanoid as nanoid12 } from "nanoid";
13920
13367
  init_questions();
13921
13368
  var tasks = new Hono5();
13922
13369
  var taskAbortControllers = /* @__PURE__ */ new Map();
13923
- var createTaskSchema = z21.object({
13924
- prompt: z21.string().min(1),
13925
- outputSchema: z21.record(z21.string(), z21.unknown()),
13926
- webhookUrl: z21.string().url().optional(),
13927
- model: z21.string().optional(),
13928
- workingDirectory: z21.string().optional(),
13929
- name: z21.string().optional(),
13930
- maxIterations: z21.number().int().min(1).max(500).optional(),
13931
- parentTaskId: z21.string().optional(),
13370
+ var createTaskSchema = z20.object({
13371
+ prompt: z20.string().min(1),
13372
+ outputSchema: z20.record(z20.string(), z20.unknown()),
13373
+ webhookUrl: z20.string().url().optional(),
13374
+ model: z20.string().optional(),
13375
+ workingDirectory: z20.string().optional(),
13376
+ name: z20.string().optional(),
13377
+ maxIterations: z20.number().int().min(1).max(500).optional(),
13378
+ parentTaskId: z20.string().optional(),
13932
13379
  /** When set, the spawning orchestrator's session id. Stamped on the
13933
13380
  * worker's config so terminal events can wake the orchestrator. */
13934
- orchestratorSessionId: z21.string().optional()
13381
+ orchestratorSessionId: z20.string().optional()
13935
13382
  });
13936
13383
  tasks.post(
13937
13384
  "/",
@@ -13997,7 +13444,7 @@ tasks.post(
13997
13444
  const taskId = agent.sessionId;
13998
13445
  const abortController = new AbortController();
13999
13446
  taskAbortControllers.set(taskId, abortController);
14000
- const streamId = `stream_${taskId}_${nanoid13(10)}`;
13447
+ const streamId = `stream_${taskId}_${nanoid12(10)}`;
14001
13448
  await activeStreamQueries.create(taskId, streamId);
14002
13449
  const taskStreamProducer = () => {
14003
13450
  const { readable, writable } = new TransformStream();
@@ -14146,9 +13593,9 @@ tasks.post("/:id/cancel", async (c) => {
14146
13593
  }
14147
13594
  return c.json({ taskId: id, status: "failed", error: "Task cancelled by user" });
14148
13595
  });
14149
- var answerQuestionSchema = z21.object({
14150
- answer: z21.string().min(1),
14151
- answeredBy: z21.enum(["orchestrator", "user", "system"]).optional()
13596
+ var answerQuestionSchema = z20.object({
13597
+ answer: z20.string().min(1),
13598
+ answeredBy: z20.enum(["orchestrator", "user", "system"]).optional()
14152
13599
  });
14153
13600
  tasks.post(
14154
13601
  "/:id/questions/:questionId/answer",
@@ -14277,7 +13724,8 @@ slack.post("/events", async (c) => {
14277
13724
  updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
14278
13725
  return c.json({ ok: true });
14279
13726
  }
14280
- const { event: inbound, dropReason } = slackEventToInboundResult(ev);
13727
+ const self = await ensureSlackSelfIdentity();
13728
+ const { event: inbound, dropReason } = slackEventToInboundResult(ev, { self });
14281
13729
  if (inbound) {
14282
13730
  const isThreadReply = ev.type === "message" && ev.channel_type !== "im" && typeof ev.thread_ts === "string" && ev.thread_ts !== ev.ts;
14283
13731
  if (isThreadReply) {
@@ -14432,7 +13880,7 @@ init_pool();
14432
13880
  init_webhook_events();
14433
13881
  import { Hono as Hono8 } from "hono";
14434
13882
  import { zValidator as zValidator6 } from "@hono/zod-validator";
14435
- import { z as z22 } from "zod";
13883
+ import { z as z21 } from "zod";
14436
13884
  var integrations = new Hono8();
14437
13885
  var orchestratorRouter = new Hono8();
14438
13886
  async function getOrchestratorId() {
@@ -14455,9 +13903,9 @@ orchestratorRouter.get("/", async (c) => {
14455
13903
  });
14456
13904
  orchestratorRouter.patch(
14457
13905
  "/",
14458
- zValidator6("json", z22.object({
14459
- name: z22.string().min(1).optional(),
14460
- personality: z22.string().optional()
13906
+ zValidator6("json", z21.object({
13907
+ name: z21.string().min(1).optional(),
13908
+ personality: z21.string().optional()
14461
13909
  })),
14462
13910
  async (c) => {
14463
13911
  const id = await getOrchestratorId();
@@ -14533,15 +13981,15 @@ integrations.get("/", async (c) => {
14533
13981
  }
14534
13982
  });
14535
13983
  });
14536
- var slackConfigSchema = z22.object({
14537
- botToken: z22.string().optional(),
14538
- signingSecret: z22.string().optional(),
14539
- defaultOrchestratorName: z22.string().optional(),
14540
- allowedUsers: z22.array(z22.string()).optional(),
14541
- allowedChannels: z22.array(z22.string()).optional(),
14542
- allowDmsFromAnyone: z22.boolean().optional(),
14543
- deniedReplyEnabled: z22.boolean().optional(),
14544
- deniedReplyTemplate: z22.string().optional()
13984
+ var slackConfigSchema = z21.object({
13985
+ botToken: z21.string().optional(),
13986
+ signingSecret: z21.string().optional(),
13987
+ defaultOrchestratorName: z21.string().optional(),
13988
+ allowedUsers: z21.array(z21.string()).optional(),
13989
+ allowedChannels: z21.array(z21.string()).optional(),
13990
+ allowDmsFromAnyone: z21.boolean().optional(),
13991
+ deniedReplyEnabled: z21.boolean().optional(),
13992
+ deniedReplyTemplate: z21.string().optional()
14545
13993
  });
14546
13994
  integrations.post("/slack", zValidator6("json", slackConfigSchema), async (c) => {
14547
13995
  const body = c.req.valid("json");
@@ -14570,11 +14018,11 @@ schedulesRouter.get("/", async (c) => {
14570
14018
  });
14571
14019
  schedulesRouter.post(
14572
14020
  "/",
14573
- zValidator6("json", z22.object({
14574
- name: z22.string().min(1),
14575
- cron: z22.string().min(1),
14576
- prompt: z22.string().min(1),
14577
- replyChannel: z22.string().optional()
14021
+ zValidator6("json", z21.object({
14022
+ name: z21.string().min(1),
14023
+ cron: z21.string().min(1),
14024
+ prompt: z21.string().min(1),
14025
+ replyChannel: z21.string().optional()
14578
14026
  })),
14579
14027
  async (c) => {
14580
14028
  const orcId = await getOrchestratorId();
@@ -14585,12 +14033,12 @@ schedulesRouter.post(
14585
14033
  );
14586
14034
  schedulesRouter.patch(
14587
14035
  "/:id",
14588
- zValidator6("json", z22.object({
14589
- name: z22.string().optional(),
14590
- cron: z22.string().optional(),
14591
- prompt: z22.string().optional(),
14592
- enabled: z22.boolean().optional(),
14593
- replyChannel: z22.string().optional()
14036
+ zValidator6("json", z21.object({
14037
+ name: z21.string().optional(),
14038
+ cron: z21.string().optional(),
14039
+ prompt: z21.string().optional(),
14040
+ enabled: z21.boolean().optional(),
14041
+ replyChannel: z21.string().optional()
14594
14042
  })),
14595
14043
  async (c) => {
14596
14044
  const orcId = await getOrchestratorId();
@@ -14618,10 +14066,10 @@ webhooksRouter.get("/", async (c) => {
14618
14066
  });
14619
14067
  webhooksRouter.post(
14620
14068
  "/",
14621
- zValidator6("json", z22.object({
14622
- name: z22.string().min(1),
14623
- wake: z22.enum(["now", "next"]).optional(),
14624
- template: z22.string().optional()
14069
+ zValidator6("json", z21.object({
14070
+ name: z21.string().min(1),
14071
+ wake: z21.enum(["now", "next"]).optional(),
14072
+ template: z21.string().optional()
14625
14073
  })),
14626
14074
  async (c) => {
14627
14075
  const orcId = await getOrchestratorId();
@@ -14632,11 +14080,11 @@ webhooksRouter.post(
14632
14080
  );
14633
14081
  webhooksRouter.patch(
14634
14082
  "/:id",
14635
- zValidator6("json", z22.object({
14636
- name: z22.string().optional(),
14637
- wake: z22.enum(["now", "next"]).optional(),
14638
- template: z22.string().optional(),
14639
- rotateToken: z22.boolean().optional()
14083
+ zValidator6("json", z21.object({
14084
+ name: z21.string().optional(),
14085
+ wake: z21.enum(["now", "next"]).optional(),
14086
+ template: z21.string().optional(),
14087
+ rotateToken: z21.boolean().optional()
14640
14088
  })),
14641
14089
  async (c) => {
14642
14090
  const orcId = await getOrchestratorId();
@@ -14653,22 +14101,22 @@ webhooksRouter.delete("/:id", async (c) => {
14653
14101
  return c.json({ deleted: ok });
14654
14102
  });
14655
14103
  var mcpRouter = new Hono8();
14656
- var mcpServerSchema = z22.object({
14657
- name: z22.string().min(1),
14658
- transport: z22.enum(["http", "sse", "stdio"]),
14659
- url: z22.string().optional(),
14660
- headers: z22.record(z22.string(), z22.string()).optional(),
14661
- command: z22.string().optional(),
14662
- args: z22.array(z22.string()).optional(),
14663
- enabled: z22.boolean().optional()
14664
- });
14665
- var mcpPatchSchema = z22.object({
14666
- name: z22.string().optional(),
14667
- url: z22.string().optional(),
14668
- headers: z22.record(z22.string(), z22.string()).optional(),
14669
- command: z22.string().optional(),
14670
- args: z22.array(z22.string()).optional(),
14671
- enabled: z22.boolean().optional()
14104
+ var mcpServerSchema = z21.object({
14105
+ name: z21.string().min(1),
14106
+ transport: z21.enum(["http", "sse", "stdio"]),
14107
+ url: z21.string().optional(),
14108
+ headers: z21.record(z21.string(), z21.string()).optional(),
14109
+ command: z21.string().optional(),
14110
+ args: z21.array(z21.string()).optional(),
14111
+ enabled: z21.boolean().optional()
14112
+ });
14113
+ var mcpPatchSchema = z21.object({
14114
+ name: z21.string().optional(),
14115
+ url: z21.string().optional(),
14116
+ headers: z21.record(z21.string(), z21.string()).optional(),
14117
+ command: z21.string().optional(),
14118
+ args: z21.array(z21.string()).optional(),
14119
+ enabled: z21.boolean().optional()
14672
14120
  });
14673
14121
  mcpRouter.get("/", async (c) => {
14674
14122
  const rows = listMcpServers().map((s) => ({
@@ -14785,10 +14233,10 @@ init_config();
14785
14233
  init_db();
14786
14234
 
14787
14235
  // src/utils/dependencies.ts
14788
- import { exec as exec7 } from "child_process";
14789
- import { promisify as promisify7 } from "util";
14236
+ import { exec as exec6 } from "child_process";
14237
+ import { promisify as promisify6 } from "util";
14790
14238
  import { platform as platform2 } from "os";
14791
- var execAsync7 = promisify7(exec7);
14239
+ var execAsync6 = promisify6(exec6);
14792
14240
  function getInstallInstructions() {
14793
14241
  const os2 = platform2();
14794
14242
  if (os2 === "darwin") {
@@ -14821,7 +14269,7 @@ Install tmux:
14821
14269
  }
14822
14270
  async function checkTmux() {
14823
14271
  try {
14824
- const { stdout } = await execAsync7("tmux -V", { timeout: 5e3 });
14272
+ const { stdout } = await execAsync6("tmux -V", { timeout: 5e3 });
14825
14273
  const version = stdout.trim();
14826
14274
  return {
14827
14275
  available: true,
@@ -14870,11 +14318,11 @@ function getWebDirectory() {
14870
14318
  try {
14871
14319
  const currentDir = dirname8(fileURLToPath4(import.meta.url));
14872
14320
  const webDir = resolve11(currentDir, "..", "web");
14873
- if (existsSync21(webDir) && existsSync21(join16(webDir, "package.json"))) {
14321
+ if (existsSync20(webDir) && existsSync20(join15(webDir, "package.json"))) {
14874
14322
  return webDir;
14875
14323
  }
14876
14324
  const altWebDir = resolve11(currentDir, "..", "..", "web");
14877
- if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
14325
+ if (existsSync20(altWebDir) && existsSync20(join15(altWebDir, "package.json"))) {
14878
14326
  return altWebDir;
14879
14327
  }
14880
14328
  return null;
@@ -14932,23 +14380,23 @@ async function findWebPort(preferredPort) {
14932
14380
  return { port: preferredPort, alreadyRunning: false };
14933
14381
  }
14934
14382
  function hasProductionBuild(webDir) {
14935
- const buildIdPath = join16(webDir, ".next", "BUILD_ID");
14936
- return existsSync21(buildIdPath);
14383
+ const buildIdPath = join15(webDir, ".next", "BUILD_ID");
14384
+ return existsSync20(buildIdPath);
14937
14385
  }
14938
14386
  function hasSourceFiles(webDir) {
14939
- const appDir = join16(webDir, "src", "app");
14940
- const pagesDir = join16(webDir, "src", "pages");
14941
- const rootAppDir = join16(webDir, "app");
14942
- const rootPagesDir = join16(webDir, "pages");
14943
- return existsSync21(appDir) || existsSync21(pagesDir) || existsSync21(rootAppDir) || existsSync21(rootPagesDir);
14387
+ const appDir = join15(webDir, "src", "app");
14388
+ const pagesDir = join15(webDir, "src", "pages");
14389
+ const rootAppDir = join15(webDir, "app");
14390
+ const rootPagesDir = join15(webDir, "pages");
14391
+ return existsSync20(appDir) || existsSync20(pagesDir) || existsSync20(rootAppDir) || existsSync20(rootPagesDir);
14944
14392
  }
14945
14393
  function getStandaloneServerPath(webDir) {
14946
14394
  const possiblePaths2 = [
14947
- join16(webDir, ".next", "standalone", "server.js"),
14948
- join16(webDir, ".next", "standalone", "web", "server.js")
14395
+ join15(webDir, ".next", "standalone", "server.js"),
14396
+ join15(webDir, ".next", "standalone", "web", "server.js")
14949
14397
  ];
14950
14398
  for (const serverPath of possiblePaths2) {
14951
- if (existsSync21(serverPath)) {
14399
+ if (existsSync20(serverPath)) {
14952
14400
  return serverPath;
14953
14401
  }
14954
14402
  }
@@ -14988,13 +14436,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
14988
14436
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
14989
14437
  return { process: null, port: actualPort };
14990
14438
  }
14991
- const usePnpm = existsSync21(join16(webDir, "pnpm-lock.yaml"));
14992
- const useNpm = !usePnpm && existsSync21(join16(webDir, "package-lock.json"));
14439
+ const usePnpm = existsSync20(join15(webDir, "pnpm-lock.yaml"));
14440
+ const useNpm = !usePnpm && existsSync20(join15(webDir, "package-lock.json"));
14993
14441
  const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
14994
14442
  const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
14995
14443
  const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
14996
14444
  const runtimeConfig = { apiBaseUrl: apiUrl };
14997
- const runtimeConfigPath = join16(webDir, "runtime-config.json");
14445
+ const runtimeConfigPath = join15(webDir, "runtime-config.json");
14998
14446
  try {
14999
14447
  writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
15000
14448
  if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
@@ -15209,8 +14657,8 @@ async function startServer(options = {}) {
15209
14657
  if (options.workingDirectory) {
15210
14658
  config.resolvedWorkingDirectory = options.workingDirectory;
15211
14659
  }
15212
- if (!existsSync21(config.resolvedWorkingDirectory)) {
15213
- mkdirSync10(config.resolvedWorkingDirectory, { recursive: true });
14660
+ if (!existsSync20(config.resolvedWorkingDirectory)) {
14661
+ mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
15214
14662
  if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
15215
14663
  }
15216
14664
  if (!config.resolvedRemoteServer.url) {
@@ -15251,13 +14699,15 @@ async function startServer(options = {}) {
15251
14699
  if (!options.quiet) console.warn(`[scheduler] start skipped: ${err.message}`);
15252
14700
  }
15253
14701
  const port = options.port || config.server.port;
15254
- const host = options.host || config.server.host || "0.0.0.0";
14702
+ const envHost = process.env.SPARKECODER_API_HOST || process.env.SPARKECODER_HOST;
14703
+ const host = envHost || options.host || config.server.host || "0.0.0.0";
14704
+ const hostSource = envHost ? process.env.SPARKECODER_API_HOST ? "env SPARKECODER_API_HOST" : "env SPARKECODER_HOST" : options.host ? "--host flag" : config.server.host ? "config.server.host" : "default";
15255
14705
  const publicUrl = options.publicUrl || config.server.publicUrl;
15256
14706
  const app = await createApp({ quiet: options.quiet });
15257
14707
  if (!options.quiet) {
15258
14708
  console.log(`
15259
14709
  \u{1F680} SparkECoder API Server`);
15260
- console.log(` \u2192 Running at http://${host}:${port}`);
14710
+ console.log(` \u2192 Binding to ${host}:${port} (source: ${hostSource})`);
15261
14711
  if (publicUrl) {
15262
14712
  console.log(` \u2192 Public URL: ${publicUrl}`);
15263
14713
  }
@@ -15266,10 +14716,22 @@ async function startServer(options = {}) {
15266
14716
  console.log(` \u2192 OpenAPI spec: http://${host}:${port}/openapi.json
15267
14717
  `);
15268
14718
  }
14719
+ if (host === "127.0.0.1" || host === "localhost") {
14720
+ console.log(`[sparkecoder] \u26A0 API bound to ${host} only \u2014 not reachable from outside the machine.`);
14721
+ console.log(`[sparkecoder] For tunnels/reverse proxies (Modal/Fly/Docker), set --host 0.0.0.0 or SPARKECODER_API_HOST=0.0.0.0.`);
14722
+ }
15269
14723
  serverInstance = serve({
15270
14724
  fetch: app.fetch,
15271
14725
  port,
15272
14726
  hostname: host
14727
+ }, (info) => {
14728
+ const actual = `${info.address}:${info.port}`;
14729
+ const requested = `${host}:${port}`;
14730
+ if (info.address === "127.0.0.1" && host !== "127.0.0.1" && host !== "localhost") {
14731
+ console.warn(`[sparkecoder] \u2717 API listener bound to ${actual} but ${requested} was requested. External requests will be refused.`);
14732
+ } else {
14733
+ console.log(`[sparkecoder] \u2713 API listening on ${actual}`);
14734
+ }
15273
14735
  });
15274
14736
  let webPort;
15275
14737
  let webStarted;