sparkecoder 0.1.114 → 0.1.116

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 (162) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +42 -0
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +477 -93
  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-Biy5JTop.d.ts} +104 -104
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +293 -64
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-ecQSnCMz.d.ts → schema-CYSKJZ3m.d.ts} +3 -3
  12. package/dist/{search-DOzC4ojH.d.ts → search-CVVfuBPZ.d.ts} +4 -4
  13. package/dist/server/index.js +293 -64
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/skills/default/computer-use.md +27 -13
  16. package/dist/skills/default/recording.md +71 -42
  17. package/dist/tools/index.d.ts +3 -3
  18. package/package.json +1 -1
  19. package/src/skills/default/computer-use.md +27 -13
  20. package/src/skills/default/recording.md +71 -42
  21. package/web/.next/BUILD_ID +1 -1
  22. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  24. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  25. package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +1 -1
  26. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  27. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  28. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  29. package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.nft.json +1 -1
  30. package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  32. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  42. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  44. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  47. package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
  48. package/web/.next/standalone/web/.next/server/app/agents.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +2 -2
  53. package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +2 -2
  55. package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
  56. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  58. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  62. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  63. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  71. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  73. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  74. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  78. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  80. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  81. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  83. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  84. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  88. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  89. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  90. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  91. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  92. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  93. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  94. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  95. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  96. package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
  97. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  98. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  99. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
  100. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  101. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  102. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  103. package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
  104. package/web/.next/standalone/web/.next/server/app/settings.rsc +3 -3
  105. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +2 -2
  106. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
  107. package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
  108. package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
  109. package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  110. package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  111. package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  112. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_7340c8b3._.js +3 -0
  113. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f3e6443f._.js +15 -0
  114. package/web/.next/standalone/web/.next/server/chunks/ssr/web_41927ef5._.js +3 -0
  115. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_settings_page_tsx_eb320e07._.js +1 -1
  116. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  117. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  118. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  119. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  120. package/web/.next/standalone/web/.next/static/chunks/2c3c1d478808e4e4.js +1 -0
  121. package/web/.next/standalone/web/.next/static/chunks/3b0501ec3249235f.js +7 -0
  122. package/web/.next/standalone/web/.next/static/chunks/44c575e006ddb992.js +13 -0
  123. package/web/.next/standalone/web/.next/static/chunks/9a3afb48c245fb2a.js +1 -0
  124. package/web/.next/standalone/web/.next/static/chunks/b3bc7244f3477729.css +1 -0
  125. package/web/.next/standalone/web/.next/static/static/chunks/2c3c1d478808e4e4.js +1 -0
  126. package/web/.next/standalone/web/.next/static/static/chunks/3b0501ec3249235f.js +7 -0
  127. package/web/.next/standalone/web/.next/static/static/chunks/44c575e006ddb992.js +13 -0
  128. package/web/.next/standalone/web/.next/static/static/chunks/9a3afb48c245fb2a.js +1 -0
  129. package/web/.next/standalone/web/.next/static/static/chunks/b3bc7244f3477729.css +1 -0
  130. package/web/.next/standalone/web/src/app/(main)/settings/page.tsx +255 -3
  131. package/web/.next/static/chunks/2c3c1d478808e4e4.js +1 -0
  132. package/web/.next/static/chunks/3b0501ec3249235f.js +7 -0
  133. package/web/.next/static/chunks/44c575e006ddb992.js +13 -0
  134. package/web/.next/static/chunks/9a3afb48c245fb2a.js +1 -0
  135. package/web/.next/static/chunks/b3bc7244f3477729.css +1 -0
  136. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_50c2f239._.js +0 -3
  137. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__6097da17._.js +0 -15
  138. package/web/.next/standalone/web/.next/server/chunks/ssr/web_3b9a2423._.js +0 -3
  139. package/web/.next/standalone/web/.next/static/chunks/60359bdd369c0c72.js +0 -1
  140. package/web/.next/standalone/web/.next/static/chunks/a189cacf6d83cf0b.js +0 -13
  141. package/web/.next/standalone/web/.next/static/chunks/bef6931fdd8428c8.js +0 -1
  142. package/web/.next/standalone/web/.next/static/chunks/c1f73b3fa4353c31.css +0 -1
  143. package/web/.next/standalone/web/.next/static/chunks/fbdcbd65f9a38f4b.js +0 -7
  144. package/web/.next/standalone/web/.next/static/static/chunks/60359bdd369c0c72.js +0 -1
  145. package/web/.next/standalone/web/.next/static/static/chunks/a189cacf6d83cf0b.js +0 -13
  146. package/web/.next/standalone/web/.next/static/static/chunks/bef6931fdd8428c8.js +0 -1
  147. package/web/.next/standalone/web/.next/static/static/chunks/c1f73b3fa4353c31.css +0 -1
  148. package/web/.next/standalone/web/.next/static/static/chunks/fbdcbd65f9a38f4b.js +0 -7
  149. package/web/.next/static/chunks/60359bdd369c0c72.js +0 -1
  150. package/web/.next/static/chunks/a189cacf6d83cf0b.js +0 -13
  151. package/web/.next/static/chunks/bef6931fdd8428c8.js +0 -1
  152. package/web/.next/static/chunks/c1f73b3fa4353c31.css +0 -1
  153. package/web/.next/static/chunks/fbdcbd65f9a38f4b.js +0 -7
  154. /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_buildManifest.js +0 -0
  155. /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_clientMiddlewareManifest.json +0 -0
  156. /package/web/.next/standalone/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_ssgManifest.js +0 -0
  157. /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_buildManifest.js +0 -0
  158. /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_clientMiddlewareManifest.json +0 -0
  159. /package/web/.next/standalone/web/.next/static/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_ssgManifest.js +0 -0
  160. /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_buildManifest.js +0 -0
  161. /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_clientMiddlewareManifest.json +0 -0
  162. /package/web/.next/static/{N09EiHl0JwEgsGMdCtwpB → 7tYFi20tEUZNGXmy2DJ1K}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -5386,7 +5386,7 @@ function isPathExcluded(relativePath, exclude) {
5386
5386
  }
5387
5387
  async function walkDirectory(dir, include, exclude, baseDir) {
5388
5388
  const { readdirSync: readdirSync4 } = await import("fs");
5389
- const { join: join17, relative: relative10 } = await import("path");
5389
+ const { join: join18, relative: relative10 } = await import("path");
5390
5390
  const files = [];
5391
5391
  function walk(currentDir) {
5392
5392
  let entries;
@@ -5396,7 +5396,7 @@ async function walkDirectory(dir, include, exclude, baseDir) {
5396
5396
  return;
5397
5397
  }
5398
5398
  for (const entry2 of entries) {
5399
- const fullPath = join17(currentDir, entry2.name);
5399
+ const fullPath = join18(currentDir, entry2.name);
5400
5400
  const relativePath = relative10(baseDir, fullPath);
5401
5401
  if (isPathExcluded(relativePath, exclude)) {
5402
5402
  continue;
@@ -8409,6 +8409,48 @@ Headless workers never interfere with desktop workers (they don't touch the scre
8409
8409
  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.` : ""}
8410
8410
 
8411
8411
  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.
8412
+
8413
+ ### How to write a worker goal (and what NOT to put in it)
8414
+
8415
+ You delegate; the worker executes. Stay at the **what** level, not the **how**.
8416
+
8417
+ **DO** put in the goal:
8418
+
8419
+ - The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
8420
+ - Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill computer-use\`).
8421
+ - Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
8422
+ - Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
8423
+
8424
+ **DO NOT** put in the goal (when you're inventing commands from memory):
8425
+
8426
+ - Literal shell commands (\`screencapture -v -V 45 -C /tmp/...\`, \`osascript -e ...\`, \`cliclick kp:cmd+f\`).
8427
+ - Pre-written step-by-step bash that you're drafting from training-data recall.
8428
+ - The names of tools you can't see in your current toolset.
8429
+
8430
+ If you have to reach for memory to write a command, **you're guessing** \u2014 your training data has lots of plausible-looking commands that are subtly wrong or stale. The worker will dutifully follow your wrong instructions instead of consulting the source of truth.
8431
+
8432
+ **Exceptions \u2014 concrete steps ARE appropriate ONLY when relayed verbatim:**
8433
+
8434
+ You CAN include literal step-by-step commands when one of these is true:
8435
+
8436
+ 1. **The user gave the steps to you.** Their message says "first run X, then Y, then commit" \u2192 forward those verbatim. That's the user's intent, not your guess.
8437
+ 2. **A previous turn already established them.** An earlier worker reported "the deploy script lives at \`./scripts/deploy.sh --env prod\`" \u2192 you can hand that command to the next worker.
8438
+ 3. **The conversation history contains them.** A documented \`bash\` invocation from earlier in the chat, an error message that revealed the right path, a CI log the user shared \u2014 anything already concrete in context.
8439
+
8440
+ In all three cases, **label the source** in the goal: *"Per your instructions earlier: 1. run X, 2. run Y..."* or *"Reusing the command worker-foo found last turn: \`./scripts/deploy.sh\`"*.
8441
+
8442
+ What you do NOT do:
8443
+
8444
+ - **Don't paraphrase or "improve" a skill into the goal.** If the recording skill describes \`sparkecoder record start/stop\`, the goal just says *"per the recording skill, bracket the work with start/stop"* \u2014 never quote the exact command. The worker loads the skill and reads the canonical version itself. You quoting it just adds a way for the quote to drift behind the source of truth.
8445
+ - **Don't reason your way to a command.** "I think macOS uses \`open -a Weather\` to launch apps" is exactly the kind of training-data recall that produces stale or wrong commands. Don't include it.
8446
+
8447
+ The rule is: **relay, never invent \u2014 and never quote a skill.** Your job is to (a) tell the worker which skills to load, (b) state the objective and acceptance criteria, (c) relay any concrete steps the user or prior context already established. Workers do the looking-up; you do the dispatching.
8448
+
8449
+ Bad goal (don't do this):
8450
+ > "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'..."
8451
+
8452
+ Good goal (do this):
8453
+ > "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."
8412
8454
  `;
8413
8455
  }
8414
8456
  function createSummaryPrompt(conversationHistory) {
@@ -11325,11 +11367,11 @@ ${p.text}` : p.text;
11325
11367
  const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
11326
11368
  if (!isRemoteConfigured2()) return [];
11327
11369
  const { readFile: readFile12 } = await import("fs/promises");
11328
- const { join: join17, basename: basename6 } = await import("path");
11370
+ const { join: join18, basename: basename6 } = await import("path");
11329
11371
  const urls = [];
11330
11372
  for (const filePath of filePaths) {
11331
11373
  try {
11332
- const fullPath = filePath.startsWith("/") ? filePath : join17(this.session.workingDirectory, filePath);
11374
+ const fullPath = filePath.startsWith("/") ? filePath : join18(this.session.workingDirectory, filePath);
11333
11375
  const fileName = basename6(fullPath);
11334
11376
  const ext = fileName.split(".").pop()?.toLowerCase() || "";
11335
11377
  const mimeMap = {
@@ -11533,6 +11575,121 @@ var init_session_lock = __esm({
11533
11575
  }
11534
11576
  });
11535
11577
 
11578
+ // src/orchestrator/webhook-events.ts
11579
+ import { existsSync as existsSync18, readFileSync as readFileSync9, appendFileSync as appendFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
11580
+ import { dirname as dirname6, join as join12 } from "path";
11581
+ import { nanoid as nanoid10 } from "nanoid";
11582
+ function logFilePath() {
11583
+ return join12(getAppDataDirectory(), "webhook-events.jsonl");
11584
+ }
11585
+ function ensureLoaded() {
11586
+ if (cache !== null) return cache;
11587
+ cache = [];
11588
+ try {
11589
+ const p = logFilePath();
11590
+ if (!existsSync18(p)) return cache;
11591
+ const lines = readFileSync9(p, "utf-8").split("\n").filter(Boolean);
11592
+ for (const line of lines) {
11593
+ try {
11594
+ cache.push(JSON.parse(line));
11595
+ } catch {
11596
+ }
11597
+ }
11598
+ if (cache.length > MAX_EVENTS) {
11599
+ cache = cache.slice(-MAX_EVENTS);
11600
+ try {
11601
+ writeFileSync3(p, cache.map((e) => JSON.stringify(e)).join("\n") + "\n");
11602
+ } catch {
11603
+ }
11604
+ }
11605
+ } catch {
11606
+ }
11607
+ return cache;
11608
+ }
11609
+ function appendEvent(ev) {
11610
+ const list = ensureLoaded();
11611
+ list.push(ev);
11612
+ if (list.length > MAX_EVENTS) list.shift();
11613
+ try {
11614
+ const p = logFilePath();
11615
+ mkdirSync7(dirname6(p), { recursive: true });
11616
+ appendFileSync3(p, JSON.stringify(ev) + "\n");
11617
+ } catch {
11618
+ }
11619
+ }
11620
+ function recordEvent(ev) {
11621
+ const full = {
11622
+ id: ev.id ?? nanoid10(),
11623
+ ts: ev.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
11624
+ source: ev.source,
11625
+ status: ev.status,
11626
+ subtype: ev.subtype,
11627
+ channel: ev.channel,
11628
+ user: ev.user,
11629
+ textSnippet: ev.textSnippet?.slice(0, 200),
11630
+ dropReason: ev.dropReason,
11631
+ error: ev.error,
11632
+ sessionId: ev.sessionId,
11633
+ durationMs: ev.durationMs,
11634
+ meta: ev.meta
11635
+ };
11636
+ appendEvent(full);
11637
+ return full.id;
11638
+ }
11639
+ function updateEvent(id, patch) {
11640
+ const list = ensureLoaded();
11641
+ const i = list.findIndex((e) => e.id === id);
11642
+ if (i < 0) return;
11643
+ list[i] = { ...list[i], ...patch };
11644
+ try {
11645
+ const p = logFilePath();
11646
+ mkdirSync7(dirname6(p), { recursive: true });
11647
+ writeFileSync3(p, list.map((e) => JSON.stringify(e)).join("\n") + "\n");
11648
+ } catch {
11649
+ }
11650
+ }
11651
+ function listEvents(filter = {}) {
11652
+ const list = ensureLoaded();
11653
+ const q = filter.q?.toLowerCase();
11654
+ const sinceTs = filter.since ? Date.parse(filter.since) : -Infinity;
11655
+ const beforeTs = filter.before ? Date.parse(filter.before) : Infinity;
11656
+ const matched = list.filter((e) => {
11657
+ if (filter.source && e.source !== filter.source) return false;
11658
+ if (filter.status && e.status !== filter.status) return false;
11659
+ const t = Date.parse(e.ts);
11660
+ if (t < sinceTs) return false;
11661
+ if (t >= beforeTs) return false;
11662
+ if (q) {
11663
+ const hay = `${e.channel ?? ""} ${e.user ?? ""} ${e.textSnippet ?? ""} ${e.dropReason ?? ""} ${e.error ?? ""} ${e.subtype ?? ""}`.toLowerCase();
11664
+ if (!hay.includes(q)) return false;
11665
+ }
11666
+ return true;
11667
+ });
11668
+ matched.reverse();
11669
+ const offset = Math.max(0, filter.offset ?? 0);
11670
+ const limit = Math.min(500, Math.max(1, filter.limit ?? 50));
11671
+ return {
11672
+ events: matched.slice(offset, offset + limit),
11673
+ total: matched.length
11674
+ };
11675
+ }
11676
+ function clearAllEvents() {
11677
+ cache = [];
11678
+ try {
11679
+ writeFileSync3(logFilePath(), "");
11680
+ } catch {
11681
+ }
11682
+ }
11683
+ var MAX_EVENTS, cache;
11684
+ var init_webhook_events = __esm({
11685
+ "src/orchestrator/webhook-events.ts"() {
11686
+ "use strict";
11687
+ init_config();
11688
+ MAX_EVENTS = 1e3;
11689
+ cache = null;
11690
+ }
11691
+ });
11692
+
11536
11693
  // src/orchestrator/daemon.ts
11537
11694
  var daemon_exports = {};
11538
11695
  __export(daemon_exports, {
@@ -11594,6 +11751,17 @@ async function runDaemonTurn(sessionId, events) {
11594
11751
  });
11595
11752
  const finishedAt = /* @__PURE__ */ new Date();
11596
11753
  const trimmed = text.trim();
11754
+ recordEvent({
11755
+ source: "daemon",
11756
+ status: error ? "failed" : "completed",
11757
+ channel: events[0]?.ref?.channel ?? void 0,
11758
+ user: events[0]?.ref?.user,
11759
+ textSnippet: trimmed.slice(0, 200),
11760
+ error,
11761
+ sessionId,
11762
+ durationMs: finishedAt.getTime() - startedAt.getTime(),
11763
+ meta: { triggeredBy: events.map((e) => e.content?.slice(0, 80)) }
11764
+ });
11597
11765
  broadcast({ sessionId, text: trimmed, triggeredBy: events, startedAt, finishedAt, error });
11598
11766
  }
11599
11767
  var listeners;
@@ -11604,6 +11772,7 @@ var init_daemon = __esm({
11604
11772
  init_session_lock();
11605
11773
  init_db();
11606
11774
  init_inbox();
11775
+ init_webhook_events();
11607
11776
  listeners = /* @__PURE__ */ new Map();
11608
11777
  }
11609
11778
  });
@@ -11855,7 +12024,7 @@ import chalk from "chalk";
11855
12024
  import ora from "ora";
11856
12025
  import "dotenv/config";
11857
12026
  import { createInterface } from "readline";
11858
- import { dirname as dirname8 } from "path";
12027
+ import { dirname as dirname9 } from "path";
11859
12028
  import { fileURLToPath as fileURLToPath5 } from "url";
11860
12029
 
11861
12030
  // src/server/index.ts
@@ -11864,8 +12033,8 @@ import { Hono as Hono9 } from "hono";
11864
12033
  import { serve } from "@hono/node-server";
11865
12034
  import { cors } from "hono/cors";
11866
12035
  import { logger } from "hono/logger";
11867
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5 } from "fs";
11868
- import { resolve as resolve11, dirname as dirname7, join as join15 } from "path";
12036
+ import { existsSync as existsSync21, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "fs";
12037
+ import { resolve as resolve11, dirname as dirname8, join as join16 } from "path";
11869
12038
  import { spawn as spawn2 } from "child_process";
11870
12039
  import { createServer as createNetServer } from "net";
11871
12040
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -11879,10 +12048,10 @@ init_checkpoints();
11879
12048
  import { Hono } from "hono";
11880
12049
  import { zValidator } from "@hono/zod-validator";
11881
12050
  import { z as z17 } from "zod";
11882
- import { existsSync as existsSync18, mkdirSync as mkdirSync7, writeFileSync as writeFileSync3, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
12051
+ import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync3 } from "fs";
11883
12052
  import { readdir as readdir6 } from "fs/promises";
11884
- import { join as join12, basename as basename5, extname as extname8, relative as relative9 } from "path";
11885
- import { nanoid as nanoid10 } from "nanoid";
12053
+ import { join as join13, basename as basename5, extname as extname8, relative as relative9 } from "path";
12054
+ import { nanoid as nanoid11 } from "nanoid";
11886
12055
 
11887
12056
  // src/tasks/agent-status.ts
11888
12057
  init_questions();
@@ -12523,12 +12692,12 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
12523
12692
  });
12524
12693
  function getAttachmentsDir(sessionId) {
12525
12694
  const appDataDir = getAppDataDirectory();
12526
- return join12(appDataDir, "attachments", sessionId);
12695
+ return join13(appDataDir, "attachments", sessionId);
12527
12696
  }
12528
12697
  function ensureAttachmentsDir(sessionId) {
12529
12698
  const dir = getAttachmentsDir(sessionId);
12530
- if (!existsSync18(dir)) {
12531
- mkdirSync7(dir, { recursive: true });
12699
+ if (!existsSync19(dir)) {
12700
+ mkdirSync8(dir, { recursive: true });
12532
12701
  }
12533
12702
  return dir;
12534
12703
  }
@@ -12539,12 +12708,12 @@ sessions2.get("/:id/attachments", async (c) => {
12539
12708
  return c.json({ error: "Session not found" }, 404);
12540
12709
  }
12541
12710
  const dir = getAttachmentsDir(sessionId);
12542
- if (!existsSync18(dir)) {
12711
+ if (!existsSync19(dir)) {
12543
12712
  return c.json({ sessionId, attachments: [], count: 0 });
12544
12713
  }
12545
12714
  const files = readdirSync3(dir);
12546
12715
  const attachments = files.map((filename) => {
12547
- const filePath = join12(dir, filename);
12716
+ const filePath = join13(dir, filename);
12548
12717
  const stats = statSync2(filePath);
12549
12718
  return {
12550
12719
  id: filename.split("_")[0],
@@ -12576,12 +12745,12 @@ sessions2.post("/:id/attachments", async (c) => {
12576
12745
  return c.json({ error: "No file provided" }, 400);
12577
12746
  }
12578
12747
  const dir = ensureAttachmentsDir(sessionId);
12579
- const id = nanoid10(10);
12748
+ const id = nanoid11(10);
12580
12749
  const ext = extname8(file.name) || "";
12581
12750
  const safeFilename = `${id}_${basename5(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
12582
- const filePath = join12(dir, safeFilename);
12751
+ const filePath = join13(dir, safeFilename);
12583
12752
  const arrayBuffer = await file.arrayBuffer();
12584
- writeFileSync3(filePath, Buffer.from(arrayBuffer));
12753
+ writeFileSync4(filePath, Buffer.from(arrayBuffer));
12585
12754
  return c.json({
12586
12755
  id,
12587
12756
  filename: file.name,
@@ -12602,16 +12771,16 @@ sessions2.post("/:id/attachments", async (c) => {
12602
12771
  return c.json({ error: "Missing filename or data" }, 400);
12603
12772
  }
12604
12773
  const dir = ensureAttachmentsDir(sessionId);
12605
- const id = nanoid10(10);
12774
+ const id = nanoid11(10);
12606
12775
  const ext = extname8(body.filename) || "";
12607
12776
  const safeFilename = `${id}_${basename5(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
12608
- const filePath = join12(dir, safeFilename);
12777
+ const filePath = join13(dir, safeFilename);
12609
12778
  let base64Data = body.data;
12610
12779
  if (base64Data.includes(",")) {
12611
12780
  base64Data = base64Data.split(",")[1];
12612
12781
  }
12613
12782
  const buffer = Buffer.from(base64Data, "base64");
12614
- writeFileSync3(filePath, buffer);
12783
+ writeFileSync4(filePath, buffer);
12615
12784
  return c.json({
12616
12785
  id,
12617
12786
  filename: body.filename,
@@ -12634,7 +12803,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
12634
12803
  return c.json({ error: "Session not found" }, 404);
12635
12804
  }
12636
12805
  const dir = getAttachmentsDir(sessionId);
12637
- if (!existsSync18(dir)) {
12806
+ if (!existsSync19(dir)) {
12638
12807
  return c.json({ error: "Attachment not found" }, 404);
12639
12808
  }
12640
12809
  const files = readdirSync3(dir);
@@ -12642,7 +12811,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
12642
12811
  if (!file) {
12643
12812
  return c.json({ error: "Attachment not found" }, 404);
12644
12813
  }
12645
- const filePath = join12(dir, file);
12814
+ const filePath = join13(dir, file);
12646
12815
  unlinkSync3(filePath);
12647
12816
  return c.json({ success: true, id: attachmentId });
12648
12817
  });
@@ -12725,7 +12894,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
12725
12894
  const entries = await readdir6(currentDir, { withFileTypes: true });
12726
12895
  for (const entry2 of entries) {
12727
12896
  if (results.length >= limit * 2) break;
12728
- const fullPath = join12(currentDir, entry2.name);
12897
+ const fullPath = join13(currentDir, entry2.name);
12729
12898
  const relativePath = relative9(baseDir, fullPath);
12730
12899
  if (entry2.isDirectory() && IGNORED_DIRECTORIES.has(entry2.name)) {
12731
12900
  continue;
@@ -12773,7 +12942,7 @@ sessions2.get(
12773
12942
  return c.json({ error: "Session not found" }, 404);
12774
12943
  }
12775
12944
  const workingDirectory = session.workingDirectory;
12776
- if (!existsSync18(workingDirectory)) {
12945
+ if (!existsSync19(workingDirectory)) {
12777
12946
  return c.json({
12778
12947
  sessionId,
12779
12948
  workingDirectory,
@@ -12887,8 +13056,8 @@ init_config();
12887
13056
  import { Hono as Hono2 } from "hono";
12888
13057
  import { zValidator as zValidator2 } from "@hono/zod-validator";
12889
13058
  import { z as z18 } from "zod";
12890
- import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync4 } from "fs";
12891
- import { join as join13 } from "path";
13059
+ import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5 } from "fs";
13060
+ import { join as join14 } from "path";
12892
13061
 
12893
13062
  // src/server/resumable-stream.ts
12894
13063
  import { createResumableStreamContext } from "resumable-stream/generic";
@@ -12975,7 +13144,7 @@ var streamContext = createResumableStreamContext({
12975
13144
 
12976
13145
  // src/server/routes/agents.ts
12977
13146
  init_checkpoints();
12978
- import { nanoid as nanoid11 } from "nanoid";
13147
+ import { nanoid as nanoid12 } from "nanoid";
12979
13148
  init_stream_proxy();
12980
13149
  init_recorder();
12981
13150
  init_remote();
@@ -13095,12 +13264,12 @@ var rejectSchema = z18.object({
13095
13264
  var streamAbortControllers = /* @__PURE__ */ new Map();
13096
13265
  function getAttachmentsDirectory(sessionId) {
13097
13266
  const appDataDir = getAppDataDirectory();
13098
- return join13(appDataDir, "attachments", sessionId);
13267
+ return join14(appDataDir, "attachments", sessionId);
13099
13268
  }
13100
13269
  async function saveAttachmentToDisk(sessionId, attachment, index) {
13101
13270
  const attachmentsDir = getAttachmentsDirectory(sessionId);
13102
- if (!existsSync19(attachmentsDir)) {
13103
- mkdirSync8(attachmentsDir, { recursive: true });
13271
+ if (!existsSync20(attachmentsDir)) {
13272
+ mkdirSync9(attachmentsDir, { recursive: true });
13104
13273
  }
13105
13274
  let filename = attachment.filename;
13106
13275
  if (!filename) {
@@ -13118,8 +13287,8 @@ async function saveAttachmentToDisk(sessionId, attachment, index) {
13118
13287
  attachment.mediaType = resized.mediaType;
13119
13288
  attachment.data = buffer.toString("base64");
13120
13289
  }
13121
- const filePath = join13(attachmentsDir, filename);
13122
- writeFileSync4(filePath, buffer);
13290
+ const filePath = join14(attachmentsDir, filename);
13291
+ writeFileSync5(filePath, buffer);
13123
13292
  return filePath;
13124
13293
  }
13125
13294
  function stripDataUrlPrefix2(data) {
@@ -13555,7 +13724,7 @@ ${prompt}` });
13555
13724
  });
13556
13725
  } catch {
13557
13726
  }
13558
- const streamId = `stream_${id}_${nanoid11(10)}`;
13727
+ const streamId = `stream_${id}_${nanoid12(10)}`;
13559
13728
  console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
13560
13729
  await activeStreamQueries.create(id, streamId);
13561
13730
  const stream = await streamContext.resumableStream(
@@ -13760,7 +13929,7 @@ agents.post(
13760
13929
  });
13761
13930
  const session = agent.getSession();
13762
13931
  const enrichedPrompt = enrichPromptWithDevtoolsContext(session.id, body.prompt);
13763
- const streamId = `stream_${session.id}_${nanoid11(10)}`;
13932
+ const streamId = `stream_${session.id}_${nanoid12(10)}`;
13764
13933
  await createCheckpoint(session.id, session.workingDirectory, 0);
13765
13934
  await activeStreamQueries.create(session.id, streamId);
13766
13935
  const createQuickStreamProducer = () => {
@@ -14079,26 +14248,26 @@ init_config();
14079
14248
  import { Hono as Hono3 } from "hono";
14080
14249
  import { zValidator as zValidator3 } from "@hono/zod-validator";
14081
14250
  import { z as z19 } from "zod";
14082
- import { readFileSync as readFileSync9 } from "fs";
14251
+ import { readFileSync as readFileSync10 } from "fs";
14083
14252
  import { fileURLToPath as fileURLToPath3 } from "url";
14084
- import { dirname as dirname6, join as join14 } from "path";
14253
+ import { dirname as dirname7, join as join15 } from "path";
14085
14254
  var __filename = fileURLToPath3(import.meta.url);
14086
- var __dirname = dirname6(__filename);
14255
+ var __dirname = dirname7(__filename);
14087
14256
  var possiblePaths = [
14088
- join14(__dirname, "../package.json"),
14257
+ join15(__dirname, "../package.json"),
14089
14258
  // From dist/server -> dist/../package.json
14090
- join14(__dirname, "../../package.json"),
14259
+ join15(__dirname, "../../package.json"),
14091
14260
  // From dist/server (if nested differently)
14092
- join14(__dirname, "../../../package.json"),
14261
+ join15(__dirname, "../../../package.json"),
14093
14262
  // From src/server/routes (development)
14094
- join14(process.cwd(), "package.json")
14263
+ join15(process.cwd(), "package.json")
14095
14264
  // From current working directory
14096
14265
  ];
14097
14266
  var currentVersion = "0.0.0";
14098
14267
  var packageName = "sparkecoder";
14099
14268
  for (const packageJsonPath of possiblePaths) {
14100
14269
  try {
14101
- const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
14270
+ const packageJson = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
14102
14271
  if (packageJson.name === "sparkecoder") {
14103
14272
  currentVersion = packageJson.version || "0.0.0";
14104
14273
  packageName = packageJson.name || "sparkecoder";
@@ -14548,7 +14717,7 @@ init_config();
14548
14717
  import { Hono as Hono5 } from "hono";
14549
14718
  import { zValidator as zValidator5 } from "@hono/zod-validator";
14550
14719
  import { z as z21 } from "zod";
14551
- import { nanoid as nanoid12 } from "nanoid";
14720
+ import { nanoid as nanoid13 } from "nanoid";
14552
14721
  init_questions();
14553
14722
  var tasks = new Hono5();
14554
14723
  var taskAbortControllers = /* @__PURE__ */ new Map();
@@ -14629,7 +14798,7 @@ tasks.post(
14629
14798
  const taskId = agent.sessionId;
14630
14799
  const abortController = new AbortController();
14631
14800
  taskAbortControllers.set(taskId, abortController);
14632
- const streamId = `stream_${taskId}_${nanoid12(10)}`;
14801
+ const streamId = `stream_${taskId}_${nanoid13(10)}`;
14633
14802
  await activeStreamQueries.create(taskId, streamId);
14634
14803
  const taskStreamProducer = () => {
14635
14804
  const { readable, writable } = new TransformStream();
@@ -14855,6 +15024,7 @@ function verifySlackSignature(opts) {
14855
15024
  // src/server/routes/slack.ts
14856
15025
  init_client3();
14857
15026
  init_slack();
15027
+ init_webhook_events();
14858
15028
  init_inbox();
14859
15029
  var recentlyHandled = /* @__PURE__ */ new Map();
14860
15030
  var MAX_RECENT = 1e3;
@@ -14895,7 +15065,17 @@ slack.post("/events", async (c) => {
14895
15065
  }
14896
15066
  if (payload?.type === "event_callback" && payload?.event) {
14897
15067
  const ev = payload.event;
15068
+ const auditId = recordEvent({
15069
+ source: "slack",
15070
+ status: "received",
15071
+ subtype: ev.type === "message" ? `message.${ev.channel_type ?? "channels"}` : ev.type,
15072
+ channel: ev.channel,
15073
+ user: ev.user,
15074
+ textSnippet: typeof ev.text === "string" ? ev.text : void 0,
15075
+ meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
15076
+ });
14898
15077
  if (alreadyHandled(ev.channel, ev.ts)) {
15078
+ updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
14899
15079
  return c.json({ ok: true });
14900
15080
  }
14901
15081
  const { event: inbound, dropReason } = slackEventToInboundResult(ev);
@@ -14905,6 +15085,7 @@ slack.post("/events", async (c) => {
14905
15085
  const ours = isThreadOwned(ev.channel, ev.thread_ts) || await threadBelongsToUs(ev.channel, ev.thread_ts);
14906
15086
  if (!ours) {
14907
15087
  console.log(`[slack] dropping thread reply in unknown thread: channel=${ev.channel} thread=${ev.thread_ts}`);
15088
+ updateEvent(auditId, { status: "dropped", dropReason: "thread_not_owned" });
14908
15089
  return c.json({ ok: true });
14909
15090
  }
14910
15091
  }
@@ -14917,8 +15098,12 @@ slack.post("/events", async (c) => {
14917
15098
  const orchestratorId = await findOrCreateOrchestratorId();
14918
15099
  if (orchestratorId) {
14919
15100
  pushToInbox(orchestratorId, inbound);
15101
+ updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
15102
+ } else {
15103
+ updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
14920
15104
  }
14921
15105
  } else if (dropReason) {
15106
+ updateEvent(auditId, { status: "dropped", dropReason });
14922
15107
  const userFacingDrops = ["user_not_allowed", "channel_not_allowed", "dm_blocked"];
14923
15108
  if (userFacingDrops.includes(dropReason)) {
14924
15109
  console.log(`[slack] dropped event from user=${payload.event.user} channel=${payload.event.channel}: ${dropReason}`);
@@ -14981,13 +15166,30 @@ async function sendDeniedReply(event) {
14981
15166
  init_webhooks_store();
14982
15167
  init_webhook();
14983
15168
  init_inbox();
15169
+ init_webhook_events();
14984
15170
  import { Hono as Hono7 } from "hono";
14985
15171
  var inbox = new Hono7();
14986
15172
  inbox.post("/:token", async (c) => {
14987
15173
  const token = c.req.param("token");
14988
- if (!token || token.length < 16) return c.json({ error: "invalid token" }, 401);
15174
+ if (!token || token.length < 16) {
15175
+ recordEvent({
15176
+ source: "inbox",
15177
+ status: "dropped",
15178
+ dropReason: "invalid_token_format",
15179
+ meta: { tokenLen: token?.length ?? 0 }
15180
+ });
15181
+ return c.json({ error: "invalid token" }, 401);
15182
+ }
14989
15183
  const lookup = await findByToken(token);
14990
- if (!lookup) return c.json({ error: "unknown token" }, 404);
15184
+ if (!lookup) {
15185
+ recordEvent({
15186
+ source: "inbox",
15187
+ status: "dropped",
15188
+ dropReason: "unknown_token",
15189
+ meta: { tokenPrefix: token.slice(0, 8) + "\u2026" }
15190
+ });
15191
+ return c.json({ error: "unknown token" }, 404);
15192
+ }
14991
15193
  let body;
14992
15194
  const contentType = c.req.header("content-type") || "";
14993
15195
  try {
@@ -15009,6 +15211,14 @@ inbox.post("/:token", async (c) => {
15009
15211
  pushToInbox(lookup.orchestratorSessionId, event);
15010
15212
  void recordHit(lookup.orchestratorSessionId, lookup.webhook.id).catch(() => {
15011
15213
  });
15214
+ recordEvent({
15215
+ source: "inbox",
15216
+ status: "routed",
15217
+ channel: lookup.webhook.name,
15218
+ textSnippet: typeof body === "string" ? body : JSON.stringify(body).slice(0, 200),
15219
+ sessionId: lookup.orchestratorSessionId,
15220
+ meta: { wake: lookup.webhook.wake, webhookId: lookup.webhook.id }
15221
+ });
15012
15222
  return c.json({ ok: true, queued: true, wake: lookup.webhook.wake });
15013
15223
  });
15014
15224
 
@@ -15020,6 +15230,7 @@ init_webhooks_store();
15020
15230
  init_messenger();
15021
15231
  init_store();
15022
15232
  init_pool();
15233
+ init_webhook_events();
15023
15234
  import { Hono as Hono8 } from "hono";
15024
15235
  import { zValidator as zValidator6 } from "@hono/zod-validator";
15025
15236
  import { z as z22 } from "zod";
@@ -15078,6 +15289,24 @@ function webhookPrefix() {
15078
15289
  const token = cfg?.webhooks?.token;
15079
15290
  return token ? `/w/${token}` : "/api";
15080
15291
  }
15292
+ integrations.get("/events", async (c) => {
15293
+ const q = c.req.query();
15294
+ const filter = {
15295
+ source: q.source,
15296
+ status: q.status,
15297
+ q: q.q,
15298
+ since: q.since,
15299
+ before: q.before,
15300
+ limit: q.limit ? Math.min(500, Math.max(1, parseInt(q.limit, 10))) : 50,
15301
+ offset: q.offset ? Math.max(0, parseInt(q.offset, 10)) : 0
15302
+ };
15303
+ const { events, total } = listEvents(filter);
15304
+ return c.json({ events, total, limit: filter.limit, offset: filter.offset });
15305
+ });
15306
+ integrations.delete("/events", async (c) => {
15307
+ clearAllEvents();
15308
+ return c.json({ ok: true });
15309
+ });
15081
15310
  integrations.get("/", async (c) => {
15082
15311
  const cfg = getConfig();
15083
15312
  return c.json({
@@ -15536,13 +15765,13 @@ var DEFAULT_WEB_PORT = 6969;
15536
15765
  var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
15537
15766
  function getWebDirectory() {
15538
15767
  try {
15539
- const currentDir = dirname7(fileURLToPath4(import.meta.url));
15768
+ const currentDir = dirname8(fileURLToPath4(import.meta.url));
15540
15769
  const webDir = resolve11(currentDir, "..", "web");
15541
- if (existsSync20(webDir) && existsSync20(join15(webDir, "package.json"))) {
15770
+ if (existsSync21(webDir) && existsSync21(join16(webDir, "package.json"))) {
15542
15771
  return webDir;
15543
15772
  }
15544
15773
  const altWebDir = resolve11(currentDir, "..", "..", "web");
15545
- if (existsSync20(altWebDir) && existsSync20(join15(altWebDir, "package.json"))) {
15774
+ if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
15546
15775
  return altWebDir;
15547
15776
  }
15548
15777
  return null;
@@ -15600,23 +15829,23 @@ async function findWebPort(preferredPort) {
15600
15829
  return { port: preferredPort, alreadyRunning: false };
15601
15830
  }
15602
15831
  function hasProductionBuild(webDir) {
15603
- const buildIdPath = join15(webDir, ".next", "BUILD_ID");
15604
- return existsSync20(buildIdPath);
15832
+ const buildIdPath = join16(webDir, ".next", "BUILD_ID");
15833
+ return existsSync21(buildIdPath);
15605
15834
  }
15606
15835
  function hasSourceFiles(webDir) {
15607
- const appDir = join15(webDir, "src", "app");
15608
- const pagesDir = join15(webDir, "src", "pages");
15609
- const rootAppDir = join15(webDir, "app");
15610
- const rootPagesDir = join15(webDir, "pages");
15611
- return existsSync20(appDir) || existsSync20(pagesDir) || existsSync20(rootAppDir) || existsSync20(rootPagesDir);
15836
+ const appDir = join16(webDir, "src", "app");
15837
+ const pagesDir = join16(webDir, "src", "pages");
15838
+ const rootAppDir = join16(webDir, "app");
15839
+ const rootPagesDir = join16(webDir, "pages");
15840
+ return existsSync21(appDir) || existsSync21(pagesDir) || existsSync21(rootAppDir) || existsSync21(rootPagesDir);
15612
15841
  }
15613
15842
  function getStandaloneServerPath(webDir) {
15614
15843
  const possiblePaths2 = [
15615
- join15(webDir, ".next", "standalone", "server.js"),
15616
- join15(webDir, ".next", "standalone", "web", "server.js")
15844
+ join16(webDir, ".next", "standalone", "server.js"),
15845
+ join16(webDir, ".next", "standalone", "web", "server.js")
15617
15846
  ];
15618
15847
  for (const serverPath of possiblePaths2) {
15619
- if (existsSync20(serverPath)) {
15848
+ if (existsSync21(serverPath)) {
15620
15849
  return serverPath;
15621
15850
  }
15622
15851
  }
@@ -15656,15 +15885,15 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15656
15885
  if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
15657
15886
  return { process: null, port: actualPort };
15658
15887
  }
15659
- const usePnpm = existsSync20(join15(webDir, "pnpm-lock.yaml"));
15660
- const useNpm = !usePnpm && existsSync20(join15(webDir, "package-lock.json"));
15888
+ const usePnpm = existsSync21(join16(webDir, "pnpm-lock.yaml"));
15889
+ const useNpm = !usePnpm && existsSync21(join16(webDir, "package-lock.json"));
15661
15890
  const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
15662
15891
  const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
15663
15892
  const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
15664
15893
  const runtimeConfig = { apiBaseUrl: apiUrl };
15665
- const runtimeConfigPath = join15(webDir, "runtime-config.json");
15894
+ const runtimeConfigPath = join16(webDir, "runtime-config.json");
15666
15895
  try {
15667
- writeFileSync5(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
15896
+ writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
15668
15897
  if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
15669
15898
  } catch (err) {
15670
15899
  if (!quiet) console.warn(` \u26A0 Could not write runtime config: ${err}`);
@@ -15684,7 +15913,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
15684
15913
  if (standaloneServerPath) {
15685
15914
  command = "node";
15686
15915
  args = ["server.js"];
15687
- cwd = dirname7(standaloneServerPath);
15916
+ cwd = dirname8(standaloneServerPath);
15688
15917
  webEnv.PORT = String(actualPort);
15689
15918
  webEnv.HOSTNAME = "0.0.0.0";
15690
15919
  if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
@@ -15877,8 +16106,8 @@ async function startServer(options = {}) {
15877
16106
  if (options.workingDirectory) {
15878
16107
  config.resolvedWorkingDirectory = options.workingDirectory;
15879
16108
  }
15880
- if (!existsSync20(config.resolvedWorkingDirectory)) {
15881
- mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
16109
+ if (!existsSync21(config.resolvedWorkingDirectory)) {
16110
+ mkdirSync10(config.resolvedWorkingDirectory, { recursive: true });
15882
16111
  if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
15883
16112
  }
15884
16113
  if (!config.resolvedRemoteServer.url) {
@@ -16422,18 +16651,18 @@ function generateOpenAPISpec() {
16422
16651
  init_config();
16423
16652
  init_semantic();
16424
16653
  init_db();
16425
- import { writeFileSync as writeFileSync6, readFileSync as readFileSync10, existsSync as existsSync21 } from "fs";
16426
- import { resolve as resolve12, join as join16 } from "path";
16654
+ import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync7, readFileSync as readFileSync11, existsSync as existsSync22, statSync as statSync3 } from "fs";
16655
+ import { resolve as resolve12, join as join17 } from "path";
16427
16656
  function getCliVersion() {
16428
- const here = dirname8(fileURLToPath5(import.meta.url));
16657
+ const here = dirname9(fileURLToPath5(import.meta.url));
16429
16658
  const candidates = [
16430
- join16(here, "..", "package.json"),
16431
- join16(here, "..", "..", "package.json"),
16432
- join16(process.cwd(), "package.json")
16659
+ join17(here, "..", "package.json"),
16660
+ join17(here, "..", "..", "package.json"),
16661
+ join17(process.cwd(), "package.json")
16433
16662
  ];
16434
16663
  for (const p of candidates) {
16435
16664
  try {
16436
- const pkg = JSON.parse(readFileSync10(p, "utf8"));
16665
+ const pkg = JSON.parse(readFileSync11(p, "utf8"));
16437
16666
  if (pkg.name === "sparkecoder" && pkg.version) return pkg.version;
16438
16667
  } catch {
16439
16668
  }
@@ -17096,8 +17325,8 @@ program.command("task").description("Run an autonomous task that completes witho
17096
17325
  let outputSchema;
17097
17326
  try {
17098
17327
  const schemaStr = options.schema;
17099
- if (existsSync21(schemaStr)) {
17100
- outputSchema = JSON.parse(readFileSync10(schemaStr, "utf-8"));
17328
+ if (existsSync22(schemaStr)) {
17329
+ outputSchema = JSON.parse(readFileSync11(schemaStr, "utf-8"));
17101
17330
  } else {
17102
17331
  outputSchema = JSON.parse(schemaStr);
17103
17332
  }
@@ -17164,19 +17393,19 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
17164
17393
  let configLocation;
17165
17394
  if (options.global) {
17166
17395
  const appDataDir = ensureAppDataDirectory();
17167
- configPath = join16(appDataDir, "sparkecoder.config.json");
17396
+ configPath = join17(appDataDir, "sparkecoder.config.json");
17168
17397
  configLocation = "global";
17169
17398
  } else {
17170
17399
  configPath = resolve12(process.cwd(), "sparkecoder.config.json");
17171
17400
  configLocation = "local";
17172
17401
  }
17173
- if (existsSync21(configPath) && !options.force) {
17402
+ if (existsSync22(configPath) && !options.force) {
17174
17403
  console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
17175
17404
  console.log(chalk.dim(` ${configPath}`));
17176
17405
  return;
17177
17406
  }
17178
17407
  const config = createDefaultConfig();
17179
- writeFileSync6(configPath, JSON.stringify(config, null, 2));
17408
+ writeFileSync7(configPath, JSON.stringify(config, null, 2));
17180
17409
  console.log(chalk.green(`\u2713 Created ${configLocation} config`));
17181
17410
  console.log(chalk.dim(` ${configPath}`));
17182
17411
  console.log(chalk.dim("Set AI_GATEWAY_API_KEY and run sparkecoder to start"));
@@ -17195,11 +17424,11 @@ program.command("slack-setup").description("Interactively configure Slack integr
17195
17424
  console.error(chalk.red("Both bot token and signing secret are required."));
17196
17425
  process.exit(1);
17197
17426
  }
17198
- const configPath = options.global ? join16(ensureAppDataDirectory(), "sparkecoder.config.json") : resolve12(process.cwd(), "sparkecoder.config.json");
17427
+ const configPath = options.global ? join17(ensureAppDataDirectory(), "sparkecoder.config.json") : resolve12(process.cwd(), "sparkecoder.config.json");
17199
17428
  let existing = {};
17200
- if (existsSync21(configPath)) {
17429
+ if (existsSync22(configPath)) {
17201
17430
  try {
17202
- existing = JSON.parse(readFileSync10(configPath, "utf-8"));
17431
+ existing = JSON.parse(readFileSync11(configPath, "utf-8"));
17203
17432
  } catch {
17204
17433
  }
17205
17434
  } else {
@@ -17211,7 +17440,7 @@ program.command("slack-setup").description("Interactively configure Slack integr
17211
17440
  signingSecret,
17212
17441
  defaultOrchestratorName: existing.slack?.defaultOrchestratorName ?? "orchestrator"
17213
17442
  };
17214
- writeFileSync6(configPath, JSON.stringify(existing, null, 2));
17443
+ writeFileSync7(configPath, JSON.stringify(existing, null, 2));
17215
17444
  console.log(chalk.green(`
17216
17445
  \u2713 Slack configured`));
17217
17446
  console.log(chalk.dim(` ${configPath}`));
@@ -17463,9 +17692,9 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17463
17692
  }
17464
17693
  const verOut = run("cloudflared", ["--version"]).stdout?.toString().split("\n")[0] || "installed";
17465
17694
  console.log(chalk.green("\u2713"), "cloudflared:", chalk.dim(verOut));
17466
- const cfDir = join16(homedir2(), ".cloudflared");
17467
- const certPath = join16(cfDir, "cert.pem");
17468
- if (!existsSync21(certPath)) {
17695
+ const cfDir = join17(homedir2(), ".cloudflared");
17696
+ const certPath = join17(cfDir, "cert.pem");
17697
+ if (!existsSync22(certPath)) {
17469
17698
  console.log(chalk.yellow("No Cloudflare login cert found."));
17470
17699
  if (await confirm("Run `cloudflared tunnel login` now (opens a browser)?", true)) {
17471
17700
  run("cloudflared", ["tunnel", "login"], { inheritIO: true, check: true });
@@ -17509,8 +17738,8 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17509
17738
  return;
17510
17739
  }
17511
17740
  }
17512
- const credsFile = tunnel.credentials_file || join16(cfDir, `${tunnel.id}.json`);
17513
- if (!existsSync21(credsFile)) {
17741
+ const credsFile = tunnel.credentials_file || join17(cfDir, `${tunnel.id}.json`);
17742
+ if (!existsSync22(credsFile)) {
17514
17743
  console.log(chalk.yellow(`Credentials file not found at ${credsFile}. The tunnel may still work via cert.pem.`));
17515
17744
  }
17516
17745
  let hostname = options.hostname;
@@ -17533,7 +17762,7 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
17533
17762
  console.log(chalk.yellow("DNS route warning:"), err.trim().split("\n").slice(-2).join(" "));
17534
17763
  }
17535
17764
  }
17536
- const configPath = join16(cfDir, "config.yml");
17765
+ const configPath = join17(cfDir, "config.yml");
17537
17766
  const configBody = `tunnel: ${tunnel.id}
17538
17767
  credentials-file: ${credsFile}
17539
17768
  ingress:
@@ -17544,14 +17773,14 @@ ingress:
17544
17773
  - service: http_status:404
17545
17774
  `;
17546
17775
  let wroteConfig = false;
17547
- if (existsSync21(configPath)) {
17548
- const existing = readFileSync10(configPath, "utf8");
17776
+ if (existsSync22(configPath)) {
17777
+ const existing = readFileSync11(configPath, "utf8");
17549
17778
  if (existing.trim() === configBody.trim()) {
17550
17779
  console.log(chalk.green("\u2713"), `config.yml already up to date: ${configPath}`);
17551
17780
  wroteConfig = true;
17552
17781
  } else if (await confirm(`A different ${configPath} exists. Overwrite (a backup will be saved)?`, false)) {
17553
17782
  copyFileSync(configPath, `${configPath}.bak.${Date.now()}`);
17554
- writeFileSync6(configPath, configBody);
17783
+ writeFileSync7(configPath, configBody);
17555
17784
  console.log(chalk.green("\u2713"), `wrote ${configPath} (previous saved as .bak.*)`);
17556
17785
  wroteConfig = true;
17557
17786
  } else {
@@ -17559,7 +17788,7 @@ ingress:
17559
17788
  console.log(chalk.cyan(configBody));
17560
17789
  }
17561
17790
  } else {
17562
- writeFileSync6(configPath, configBody);
17791
+ writeFileSync7(configPath, configBody);
17563
17792
  console.log(chalk.green("\u2713"), `wrote ${configPath}`);
17564
17793
  wroteConfig = true;
17565
17794
  }
@@ -18026,5 +18255,160 @@ program.command("request-permissions").description("Request macOS permissions fo
18026
18255
  }
18027
18256
  console.log();
18028
18257
  });
18258
+ {
18259
+ let stateFilePath = function() {
18260
+ return join17(ensureAppDataDirectory(), "recordings.json");
18261
+ }, readState = function() {
18262
+ const p = stateFilePath();
18263
+ if (!existsSync22(p)) return [];
18264
+ try {
18265
+ return JSON.parse(readFileSync11(p, "utf-8"));
18266
+ } catch {
18267
+ return [];
18268
+ }
18269
+ }, writeState = function(rows) {
18270
+ writeFileSync7(stateFilePath(), JSON.stringify(rows, null, 2), { mode: 384 });
18271
+ }, isAlive = function(pid) {
18272
+ try {
18273
+ process.kill(pid, 0);
18274
+ return true;
18275
+ } catch {
18276
+ return false;
18277
+ }
18278
+ }, pruneDead = function(rows) {
18279
+ return rows.filter((r) => isAlive(r.pid));
18280
+ };
18281
+ stateFilePath2 = stateFilePath, readState2 = readState, writeState2 = writeState, isAlive2 = isAlive, pruneDead2 = pruneDead;
18282
+ const record = program.command("record").description("Start/stop screen recordings");
18283
+ record.command("start").description("Start a screen recording (returns id, path, pid as JSON)").option("--name <slug>", "Optional human-readable label for the recording").option("--dir <path>", "Output directory (default: ~/recordings)").action(async (opts) => {
18284
+ const { spawn: spawn3 } = await import("child_process");
18285
+ const { homedir: homedir2, platform: osPlatform } = await import("os");
18286
+ const outDir = opts.dir ? resolve12(opts.dir.replace(/^~/, homedir2())) : join17(homedir2(), "recordings");
18287
+ mkdirSync11(outDir, { recursive: true });
18288
+ const id = `rec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
18289
+ const ext = osPlatform() === "darwin" ? "mov" : "mp4";
18290
+ const filename = `${id}${opts.name ? `-${String(opts.name).replace(/[^a-z0-9-]+/gi, "-").toLowerCase()}` : ""}.${ext}`;
18291
+ const path = join17(outDir, filename);
18292
+ let cmd;
18293
+ let args;
18294
+ if (osPlatform() === "darwin") {
18295
+ cmd = "screencapture";
18296
+ args = ["-v", "-C", "-k", path];
18297
+ } else if (osPlatform() === "linux") {
18298
+ const display = process.env.DISPLAY || ":0.0";
18299
+ const size = process.env.SPARKECODER_RECORD_SIZE || "1920x1080";
18300
+ cmd = "ffmpeg";
18301
+ args = [
18302
+ "-y",
18303
+ "-f",
18304
+ "x11grab",
18305
+ "-framerate",
18306
+ "25",
18307
+ "-video_size",
18308
+ size,
18309
+ "-i",
18310
+ display,
18311
+ "-c:v",
18312
+ "libx264",
18313
+ "-preset",
18314
+ "veryfast",
18315
+ "-pix_fmt",
18316
+ "yuv420p",
18317
+ path
18318
+ ];
18319
+ } else {
18320
+ console.error(JSON.stringify({ error: `Unsupported platform: ${osPlatform()}` }));
18321
+ process.exit(1);
18322
+ }
18323
+ const child = spawn3(cmd, args, { detached: true, stdio: "ignore" });
18324
+ child.unref();
18325
+ if (!child.pid) {
18326
+ console.error(JSON.stringify({ error: `Failed to spawn ${cmd}` }));
18327
+ process.exit(1);
18328
+ }
18329
+ const row = {
18330
+ id,
18331
+ name: opts.name,
18332
+ path,
18333
+ pid: child.pid,
18334
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
18335
+ platform: osPlatform()
18336
+ };
18337
+ const rows = pruneDead(readState());
18338
+ rows.push(row);
18339
+ writeState(rows);
18340
+ console.log(JSON.stringify({ id, path, pid: child.pid, platform: osPlatform() }));
18341
+ });
18342
+ record.command("stop <id>").description("Stop a recording started by `sparkecoder record start`").action(async (id) => {
18343
+ const rows = readState();
18344
+ const row = rows.find((r) => r.id === id);
18345
+ if (!row) {
18346
+ console.error(JSON.stringify({ error: `No recording found with id ${id}` }));
18347
+ process.exit(1);
18348
+ }
18349
+ const startedAt = new Date(row.startedAt).getTime();
18350
+ if (isAlive(row.pid)) {
18351
+ try {
18352
+ process.kill(row.pid, "SIGINT");
18353
+ } catch {
18354
+ }
18355
+ for (let i = 0; i < 40; i++) {
18356
+ if (!isAlive(row.pid)) break;
18357
+ await new Promise((r) => setTimeout(r, 200));
18358
+ }
18359
+ if (isAlive(row.pid)) {
18360
+ try {
18361
+ process.kill(row.pid, "SIGTERM");
18362
+ } catch {
18363
+ }
18364
+ }
18365
+ }
18366
+ writeState(rows.filter((r) => r.id !== id));
18367
+ const fileExists = existsSync22(row.path);
18368
+ const sizeMb = fileExists ? Math.round(statSync3(row.path).size / (1024 * 1024) * 10) / 10 : 0;
18369
+ console.log(JSON.stringify({
18370
+ id,
18371
+ path: row.path,
18372
+ durationSec: Math.round((Date.now() - startedAt) / 1e3),
18373
+ sizeMb,
18374
+ ok: fileExists
18375
+ }));
18376
+ });
18377
+ record.command("list").description("List active recordings").action(() => {
18378
+ const rows = pruneDead(readState());
18379
+ writeState(rows);
18380
+ console.log(JSON.stringify(rows, null, 2));
18381
+ });
18382
+ record.command("stop-all").description("Stop every active recording").action(async () => {
18383
+ const rows = readState();
18384
+ const stopped = [];
18385
+ for (const r of rows) {
18386
+ if (isAlive(r.pid)) {
18387
+ try {
18388
+ process.kill(r.pid, "SIGINT");
18389
+ } catch {
18390
+ }
18391
+ for (let i = 0; i < 30; i++) {
18392
+ if (!isAlive(r.pid)) break;
18393
+ await new Promise((res) => setTimeout(res, 200));
18394
+ }
18395
+ if (isAlive(r.pid)) {
18396
+ try {
18397
+ process.kill(r.pid, "SIGTERM");
18398
+ } catch {
18399
+ }
18400
+ }
18401
+ }
18402
+ stopped.push({ id: r.id, path: r.path, ok: existsSync22(r.path) });
18403
+ }
18404
+ writeState([]);
18405
+ console.log(JSON.stringify({ stopped }));
18406
+ });
18407
+ }
18408
+ var stateFilePath2;
18409
+ var readState2;
18410
+ var writeState2;
18411
+ var isAlive2;
18412
+ var pruneDead2;
18029
18413
  program.parse();
18030
18414
  //# sourceMappingURL=cli.js.map