sparkecoder 0.1.59 → 0.1.61

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 (179) hide show
  1. package/dist/agent/index.d.ts +4 -3
  2. package/dist/agent/index.js +662 -203
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +906 -129
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +3 -2
  7. package/dist/db/index.js.map +1 -1
  8. package/dist/{index-Csad1Nx4.d.ts → index-DuGtMaAJ.d.ts} +85 -4
  9. package/dist/index.d.ts +6 -5
  10. package/dist/index.js +1302 -711
  11. package/dist/index.js.map +1 -1
  12. package/dist/schema-C7Mm4Ykn.d.ts +1410 -0
  13. package/dist/{search-BETuS1vh.d.ts → search-C_IFImt1.d.ts} +3 -3
  14. package/dist/server/index.js +692 -101
  15. package/dist/server/index.js.map +1 -1
  16. package/dist/skills/default/browser.md +143 -0
  17. package/dist/skills/default/code-review.md +122 -0
  18. package/dist/skills/default/debugging.md +105 -0
  19. package/dist/skills/default/refactoring.md +197 -0
  20. package/dist/tools/index.d.ts +54 -4
  21. package/dist/tools/index.js +304 -29
  22. package/dist/tools/index.js.map +1 -1
  23. package/package.json +6 -1
  24. package/src/skills/default/browser.md +143 -0
  25. package/src/skills/default/code-review.md +122 -0
  26. package/src/skills/default/debugging.md +105 -0
  27. package/src/skills/default/refactoring.md +197 -0
  28. package/web/.next/BUILD_ID +1 -1
  29. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  30. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  31. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  32. package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
  33. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  34. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  35. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  37. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  44. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  45. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  46. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  47. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  52. package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
  53. package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  56. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  57. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  58. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  61. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  68. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  71. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  72. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  77. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  81. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  82. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  86. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  87. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  88. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  90. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  91. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  92. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  93. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
  94. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +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 +4 -4
  97. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  98. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  99. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  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/chunks/ssr/{2374f_19289e11._.js → 2374f_03d22c16._.js} +1 -1
  104. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b4b86c1f._.js → 2374f_0d0b1fb9._.js} +1 -1
  105. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_4858a1ea._.js → 2374f_3f7d6d28._.js} +1 -1
  106. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_51385fed._.js → 2374f_6166d14d._.js} +1 -1
  107. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_40e35a02._.js → 2374f_67b9525f._.js} +1 -1
  108. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_fb95e3c9._.js → 2374f_6b436efc._.js} +1 -1
  109. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_2f0d9f6f._.js → 2374f_784d851c._.js} +1 -1
  110. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_35475cbe._.js → 2374f_976b2ded._.js} +1 -1
  111. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_7db22cde._.js → 2374f_98eba491._.js} +1 -1
  112. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_90b8e4fb._.js → 2374f_99b7b533._.js} +1 -1
  113. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_4666c827._.js → 2374f_a1a36483._.js} +1 -1
  114. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_076f03ec._.js → 2374f_b98835eb._.js} +1 -1
  115. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b17fce11._.js → 2374f_e19eaecc._.js} +1 -1
  116. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d8b9ce38._.js → 2374f_e3aec189._.js} +1 -1
  117. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__7775f784._.js → [root-of-the-server]__4f176a16._.js} +2 -2
  118. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__d59f831d._.js +15 -0
  119. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_645f4b90._.js → web_693f514e._.js} +2 -2
  120. package/web/.next/standalone/web/.next/server/chunks/ssr/web_a42a2651._.js +7 -0
  121. package/web/.next/standalone/web/.next/server/chunks/ssr/web_dc6ce793._.js +7 -0
  122. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_components_sessions-sidebar_tsx_92510070._.js +1 -1
  123. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  124. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  125. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  126. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  127. package/web/.next/standalone/web/.next/static/chunks/0358a0e7a40cfb93.css +1 -0
  128. package/web/.next/standalone/web/.next/static/chunks/1ecde4c0d426a635.js +1 -0
  129. package/web/.next/standalone/web/.next/static/chunks/20e0fa99c7a1c1fc.js +13 -0
  130. package/web/.next/standalone/web/.next/static/chunks/36688a049d72e8ab.js +5 -0
  131. package/web/.next/standalone/web/.next/static/chunks/95436454a7559b0d.js +7 -0
  132. package/web/.next/standalone/web/.next/static/chunks/a751ca474cc46212.js +5 -0
  133. package/web/.next/standalone/web/.next/static/static/chunks/0358a0e7a40cfb93.css +1 -0
  134. package/web/.next/standalone/web/.next/static/static/chunks/1ecde4c0d426a635.js +1 -0
  135. package/web/.next/standalone/web/.next/static/static/chunks/20e0fa99c7a1c1fc.js +13 -0
  136. package/web/.next/standalone/web/.next/static/static/chunks/36688a049d72e8ab.js +5 -0
  137. package/web/.next/standalone/web/.next/static/static/chunks/95436454a7559b0d.js +7 -0
  138. package/web/.next/standalone/web/.next/static/static/chunks/a751ca474cc46212.js +5 -0
  139. package/web/.next/standalone/web/src/components/ai-elements/complete-task-tool.tsx +126 -0
  140. package/web/.next/standalone/web/src/components/chat-interface.tsx +68 -3
  141. package/web/.next/standalone/web/src/components/sessions-sidebar.tsx +18 -1
  142. package/web/.next/standalone/web/src/lib/api.ts +12 -0
  143. package/web/.next/static/chunks/0358a0e7a40cfb93.css +1 -0
  144. package/web/.next/static/chunks/1ecde4c0d426a635.js +1 -0
  145. package/web/.next/static/chunks/20e0fa99c7a1c1fc.js +13 -0
  146. package/web/.next/static/chunks/36688a049d72e8ab.js +5 -0
  147. package/web/.next/static/chunks/95436454a7559b0d.js +7 -0
  148. package/web/.next/static/chunks/a751ca474cc46212.js +5 -0
  149. package/dist/schema-NcQknWCg.d.ts +0 -295
  150. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__bd396152._.js +0 -15
  151. package/web/.next/standalone/web/.next/server/chunks/ssr/web_9c9f0e3b._.js +0 -7
  152. package/web/.next/standalone/web/.next/server/chunks/ssr/web_d08270f7._.js +0 -7
  153. package/web/.next/standalone/web/.next/static/chunks/2868b007ce5163fc.css +0 -1
  154. package/web/.next/standalone/web/.next/static/chunks/3f295b6960943c38.js +0 -1
  155. package/web/.next/standalone/web/.next/static/chunks/631b023d37a08635.js +0 -13
  156. package/web/.next/standalone/web/.next/static/chunks/a2b4737b190d1b54.js +0 -5
  157. package/web/.next/standalone/web/.next/static/chunks/e97212fcc8221479.js +0 -5
  158. package/web/.next/standalone/web/.next/static/chunks/f6e47c8a9766ce91.js +0 -7
  159. package/web/.next/standalone/web/.next/static/static/chunks/2868b007ce5163fc.css +0 -1
  160. package/web/.next/standalone/web/.next/static/static/chunks/3f295b6960943c38.js +0 -1
  161. package/web/.next/standalone/web/.next/static/static/chunks/631b023d37a08635.js +0 -13
  162. package/web/.next/standalone/web/.next/static/static/chunks/a2b4737b190d1b54.js +0 -5
  163. package/web/.next/standalone/web/.next/static/static/chunks/e97212fcc8221479.js +0 -5
  164. package/web/.next/standalone/web/.next/static/static/chunks/f6e47c8a9766ce91.js +0 -7
  165. package/web/.next/static/chunks/2868b007ce5163fc.css +0 -1
  166. package/web/.next/static/chunks/3f295b6960943c38.js +0 -1
  167. package/web/.next/static/chunks/631b023d37a08635.js +0 -13
  168. package/web/.next/static/chunks/a2b4737b190d1b54.js +0 -5
  169. package/web/.next/static/chunks/e97212fcc8221479.js +0 -5
  170. package/web/.next/static/chunks/f6e47c8a9766ce91.js +0 -7
  171. /package/web/.next/standalone/web/.next/static/{static/u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
  172. /package/web/.next/standalone/web/.next/static/{static/u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
  173. /package/web/.next/standalone/web/.next/static/{static/u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
  174. /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
  175. /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
  176. /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
  177. /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
  178. /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
  179. /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
@@ -367,7 +367,7 @@ var init_db = __esm({
367
367
 
368
368
  // src/config/types.ts
369
369
  import { z } from "zod";
370
- var ToolApprovalConfigSchema, SkillMetadataSchema, SessionConfigSchema, VectorGatewayConfigSchema, RemoteServerConfigSchema, SparkcoderConfigSchema;
370
+ var ToolApprovalConfigSchema, SkillMetadataSchema, TaskConfigSchema, SessionConfigSchema, VectorGatewayConfigSchema, RemoteServerConfigSchema, SparkcoderConfigSchema;
371
371
  var init_types = __esm({
372
372
  "src/config/types.ts"() {
373
373
  "use strict";
@@ -386,11 +386,22 @@ var init_types = __esm({
386
386
  // Glob patterns - auto-inject when working with matching files
387
387
  globs: z.array(z.string()).optional().default([])
388
388
  });
389
+ TaskConfigSchema = z.object({
390
+ enabled: z.boolean(),
391
+ outputSchema: z.record(z.string(), z.unknown()),
392
+ webhookUrl: z.string().url().optional(),
393
+ maxIterations: z.number().optional(),
394
+ status: z.enum(["running", "completed", "failed"]),
395
+ result: z.unknown().optional(),
396
+ error: z.string().optional(),
397
+ iterations: z.number().optional()
398
+ });
389
399
  SessionConfigSchema = z.object({
390
400
  toolApprovals: z.record(z.string(), z.boolean()).optional(),
391
401
  approvalWebhook: z.string().url().optional(),
392
402
  skillsDirectory: z.string().optional(),
393
- maxContextChars: z.number().optional().default(2e5)
403
+ maxContextChars: z.number().optional().default(2e5),
404
+ task: TaskConfigSchema.optional()
394
405
  });
395
406
  VectorGatewayConfigSchema = z.object({
396
407
  // Redis cluster nodes URL for Vector Gateway (or use REDIS_CLUSTER_NODES env var)
@@ -521,8 +532,15 @@ function discoverSkillDirectories(workingDir) {
521
532
  if (existsSync(agentsMd)) {
522
533
  agentsMdPath = agentsMd;
523
534
  }
524
- const builtInSkillsDir = resolve(dirname(import.meta.url.replace("file://", "")), "../skills/default");
525
- if (existsSync(builtInSkillsDir)) {
535
+ const baseDir = dirname(import.meta.url.replace("file://", ""));
536
+ const builtInCandidates = [
537
+ resolve(baseDir, "../skills/default"),
538
+ // dev: src/config → src/skills/default
539
+ resolve(baseDir, "./skills/default")
540
+ // prod: dist/ → dist/skills/default
541
+ ];
542
+ const builtInSkillsDir = builtInCandidates.find((p) => existsSync(p));
543
+ if (builtInSkillsDir) {
526
544
  onDemandDirs.push({ path: builtInSkillsDir, priority: 100 });
527
545
  allDirectories.push(builtInSkillsDir);
528
546
  }
@@ -691,6 +709,9 @@ function getConfig() {
691
709
  }
692
710
  function requiresApproval(toolName, sessionConfig) {
693
711
  const config = getConfig();
712
+ if (sessionConfig?.toolApprovals?.["*"] !== void 0) {
713
+ return sessionConfig.toolApprovals["*"];
714
+ }
694
715
  if (sessionConfig?.toolApprovals?.[toolName] !== void 0) {
695
716
  return sessionConfig.toolApprovals[toolName];
696
717
  }
@@ -874,7 +895,7 @@ __export(skills_exports, {
874
895
  loadSkillsFromDirectory: () => loadSkillsFromDirectory
875
896
  });
876
897
  import { readFile as readFile6, readdir } from "fs/promises";
877
- import { resolve as resolve6, basename, extname as extname3, relative as relative4 } from "path";
898
+ import { resolve as resolve6, basename, extname as extname4, relative as relative4 } from "path";
878
899
  import { existsSync as existsSync8 } from "fs";
879
900
  import { minimatch } from "minimatch";
880
901
  function parseSkillFrontmatter(content) {
@@ -945,7 +966,7 @@ function parseSkillFrontmatter(content) {
945
966
  }
946
967
  }
947
968
  function getSkillNameFromPath(filePath) {
948
- return basename(filePath, extname3(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
969
+ return basename(filePath, extname4(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
949
970
  }
950
971
  async function loadSkillsFromDirectory(directory, options = {}) {
951
972
  const {
@@ -1250,7 +1271,7 @@ var init_hasher = __esm({
1250
1271
  });
1251
1272
 
1252
1273
  // src/semantic/chunker.ts
1253
- import { extname as extname5, basename as basename2 } from "path";
1274
+ import { extname as extname6, basename as basename2 } from "path";
1254
1275
  var init_chunker = __esm({
1255
1276
  "src/semantic/chunker.ts"() {
1256
1277
  "use strict";
@@ -1598,7 +1619,7 @@ var init_semantic_search = __esm({
1598
1619
 
1599
1620
  // src/server/index.ts
1600
1621
  import "dotenv/config";
1601
- import { Hono as Hono5 } from "hono";
1622
+ import { Hono as Hono6 } from "hono";
1602
1623
  import { serve } from "@hono/node-server";
1603
1624
  import { cors } from "hono/cors";
1604
1625
  import { logger } from "hono/logger";
@@ -1612,28 +1633,186 @@ import { fileURLToPath as fileURLToPath4 } from "url";
1612
1633
  init_db();
1613
1634
  import { Hono } from "hono";
1614
1635
  import { zValidator } from "@hono/zod-validator";
1615
- import { z as z13 } from "zod";
1636
+ import { z as z14 } from "zod";
1616
1637
  import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync as statSync2, unlinkSync } from "fs";
1617
1638
  import { readdir as readdir5 } from "fs/promises";
1618
- import { join as join5, basename as basename4, extname as extname6, relative as relative9 } from "path";
1639
+ import { join as join5, basename as basename4, extname as extname7, relative as relative9 } from "path";
1619
1640
  import { nanoid as nanoid4 } from "nanoid";
1620
1641
 
1621
1642
  // src/agent/index.ts
1622
1643
  import {
1623
1644
  streamText as streamText2,
1624
1645
  generateText as generateText3,
1625
- tool as tool11,
1646
+ tool as tool12,
1626
1647
  stepCountIs as stepCountIs2
1627
1648
  } from "ai";
1628
1649
 
1629
1650
  // src/agent/model.ts
1630
1651
  import { gateway } from "@ai-sdk/gateway";
1652
+
1653
+ // src/agent/remote-model.ts
1654
+ function serializePrompt(prompt) {
1655
+ return prompt.map((msg) => {
1656
+ if (!Array.isArray(msg.content)) return msg;
1657
+ return {
1658
+ ...msg,
1659
+ content: msg.content.map((part) => {
1660
+ if (part.type === "file" && part.data instanceof Uint8Array) {
1661
+ return {
1662
+ ...part,
1663
+ data: Buffer.from(part.data).toString("base64"),
1664
+ _base64: true
1665
+ };
1666
+ }
1667
+ return part;
1668
+ })
1669
+ };
1670
+ });
1671
+ }
1672
+ function deserializeValue(value) {
1673
+ if (value && typeof value === "object") {
1674
+ if (value.__uint8array && typeof value.data === "string") {
1675
+ return Buffer.from(value.data, "base64");
1676
+ }
1677
+ if (Array.isArray(value)) {
1678
+ return value.map(deserializeValue);
1679
+ }
1680
+ const result = {};
1681
+ for (const [k, v] of Object.entries(value)) {
1682
+ result[k] = deserializeValue(v);
1683
+ }
1684
+ return result;
1685
+ }
1686
+ return value;
1687
+ }
1688
+ function prepareOptions(options) {
1689
+ const { abortSignal, ...rest } = options;
1690
+ return {
1691
+ ...rest,
1692
+ prompt: serializePrompt(options.prompt)
1693
+ };
1694
+ }
1695
+ function createRemoteModel(modelId, config) {
1696
+ const baseUrl = config.url.replace(/\/$/, "");
1697
+ const headers = {
1698
+ "Content-Type": "application/json",
1699
+ "Authorization": `Bearer ${config.authKey}`
1700
+ };
1701
+ return {
1702
+ specificationVersion: "v3",
1703
+ provider: "remote-proxy",
1704
+ modelId,
1705
+ supportedUrls: {},
1706
+ async doGenerate(options) {
1707
+ const res = await fetch(`${baseUrl}/inference/generate`, {
1708
+ method: "POST",
1709
+ headers,
1710
+ body: JSON.stringify({
1711
+ modelId,
1712
+ options: prepareOptions(options)
1713
+ }),
1714
+ signal: options.abortSignal
1715
+ });
1716
+ if (!res.ok) {
1717
+ const err = await res.json().catch(() => ({}));
1718
+ throw new Error(
1719
+ `Remote inference failed (${res.status}): ${err.error || res.statusText}`
1720
+ );
1721
+ }
1722
+ const result = await res.json();
1723
+ return deserializeValue(result);
1724
+ },
1725
+ async doStream(options) {
1726
+ const res = await fetch(`${baseUrl}/inference/stream`, {
1727
+ method: "POST",
1728
+ headers,
1729
+ body: JSON.stringify({
1730
+ modelId,
1731
+ options: prepareOptions(options)
1732
+ }),
1733
+ signal: options.abortSignal
1734
+ });
1735
+ if (!res.ok) {
1736
+ const err = await res.json().catch(() => ({}));
1737
+ throw new Error(
1738
+ `Remote inference failed (${res.status}): ${err.error || res.statusText}`
1739
+ );
1740
+ }
1741
+ const reader = res.body.getReader();
1742
+ const decoder = new TextDecoder();
1743
+ let buffer = "";
1744
+ const stream = new ReadableStream({
1745
+ async pull(controller) {
1746
+ while (true) {
1747
+ const { done, value } = await reader.read();
1748
+ if (done) {
1749
+ if (buffer.trim()) {
1750
+ try {
1751
+ const parsed = deserializeValue(JSON.parse(buffer.trim()));
1752
+ if (parsed.type === "error") {
1753
+ controller.error(new Error(parsed.error));
1754
+ } else {
1755
+ controller.enqueue(parsed);
1756
+ }
1757
+ } catch {
1758
+ }
1759
+ }
1760
+ controller.close();
1761
+ return;
1762
+ }
1763
+ buffer += decoder.decode(value, { stream: true });
1764
+ const lines = buffer.split("\n");
1765
+ buffer = lines.pop() || "";
1766
+ for (const line of lines) {
1767
+ if (!line.trim()) continue;
1768
+ try {
1769
+ const parsed = deserializeValue(JSON.parse(line));
1770
+ if (parsed.type === "error") {
1771
+ controller.error(new Error(parsed.error));
1772
+ return;
1773
+ }
1774
+ controller.enqueue(parsed);
1775
+ } catch {
1776
+ }
1777
+ }
1778
+ }
1779
+ },
1780
+ cancel() {
1781
+ reader.cancel();
1782
+ }
1783
+ });
1784
+ const responseHeaders = {};
1785
+ res.headers.forEach((v, k) => {
1786
+ if (k.startsWith("x-upstream-")) {
1787
+ responseHeaders[k.replace("x-upstream-", "")] = v;
1788
+ }
1789
+ });
1790
+ return {
1791
+ stream,
1792
+ response: Object.keys(responseHeaders).length > 0 ? { headers: responseHeaders } : void 0
1793
+ };
1794
+ }
1795
+ };
1796
+ }
1797
+
1798
+ // src/agent/model.ts
1799
+ init_config();
1631
1800
  var ANTHROPIC_PREFIX = "anthropic/";
1632
1801
  function isAnthropicModel(modelId) {
1633
1802
  const normalized = modelId.trim().toLowerCase();
1634
1803
  return normalized.startsWith(ANTHROPIC_PREFIX) || normalized.startsWith("claude-");
1635
1804
  }
1636
1805
  function resolveModel(modelId) {
1806
+ try {
1807
+ const config = getConfig();
1808
+ if (config.resolvedRemoteServer.isConfigured) {
1809
+ return createRemoteModel(modelId.trim(), {
1810
+ url: config.resolvedRemoteServer.url,
1811
+ authKey: config.resolvedRemoteServer.authKey
1812
+ });
1813
+ }
1814
+ } catch {
1815
+ }
1637
1816
  return gateway(modelId.trim());
1638
1817
  }
1639
1818
  var SUBAGENT_MODELS = {
@@ -1645,7 +1824,7 @@ var SUBAGENT_MODELS = {
1645
1824
  // src/agent/index.ts
1646
1825
  init_db();
1647
1826
  init_config();
1648
- import { z as z12 } from "zod";
1827
+ import { z as z13 } from "zod";
1649
1828
  import { nanoid as nanoid3 } from "nanoid";
1650
1829
 
1651
1830
  // src/tools/bash.ts
@@ -2226,26 +2405,41 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
2226
2405
  import { tool as tool2 } from "ai";
2227
2406
  import { z as z3 } from "zod";
2228
2407
  import { readFile as readFile2, stat } from "fs/promises";
2229
- import { resolve as resolve2, relative, isAbsolute } from "path";
2408
+ import { resolve as resolve2, relative, isAbsolute, extname } from "path";
2230
2409
  import { existsSync as existsSync3 } from "fs";
2231
2410
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
2411
+ var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
2232
2412
  var MAX_OUTPUT_CHARS3 = 5e4;
2413
+ var IMAGE_EXTENSIONS = {
2414
+ ".png": "image/png",
2415
+ ".jpg": "image/jpeg",
2416
+ ".jpeg": "image/jpeg",
2417
+ ".gif": "image/gif",
2418
+ ".webp": "image/webp"
2419
+ };
2420
+ function isImageFile(filePath) {
2421
+ return extname(filePath).toLowerCase() in IMAGE_EXTENSIONS;
2422
+ }
2423
+ function getImageMediaType(filePath) {
2424
+ return IMAGE_EXTENSIONS[extname(filePath).toLowerCase()] || "image/png";
2425
+ }
2233
2426
  var readFileInputSchema = z3.object({
2234
- path: z3.string().describe("The path to the file to read. Can be relative to working directory or absolute."),
2235
- startLine: z3.number().optional().describe("Optional: Start reading from this line number (1-indexed)"),
2236
- endLine: z3.number().optional().describe("Optional: Stop reading at this line number (1-indexed, inclusive)")
2427
+ path: z3.string().describe("The path to the file to read. Can be relative to working directory or absolute. Supports text files and images (png, jpg, jpeg, gif, webp)."),
2428
+ startLine: z3.number().optional().describe("Optional: Start reading from this line number (1-indexed). Only for text files."),
2429
+ endLine: z3.number().optional().describe("Optional: Stop reading at this line number (1-indexed, inclusive). Only for text files.")
2237
2430
  });
2238
2431
  function createReadFileTool(options) {
2239
2432
  return tool2({
2240
2433
  description: `Read the contents of a file. Provide a path relative to the working directory (${options.workingDirectory}) or an absolute path.
2241
- Large files will be automatically truncated. Binary files are not supported.
2242
- Use this to understand existing code, check file contents, or gather context.`,
2434
+ Supports text files (automatically truncated if large) and image files (png, jpg, jpeg, gif, webp).
2435
+ For images, the file contents are returned as visual data you can see and analyze.
2436
+ Use this to understand existing code, check file contents, view screenshots, or gather context.`,
2243
2437
  inputSchema: readFileInputSchema,
2244
- execute: async ({ path, startLine, endLine }) => {
2438
+ execute: async ({ path: filePath, startLine, endLine }) => {
2245
2439
  try {
2246
- const absolutePath = isAbsolute(path) ? path : resolve2(options.workingDirectory, path);
2440
+ const absolutePath = isAbsolute(filePath) ? filePath : resolve2(options.workingDirectory, filePath);
2247
2441
  const relativePath = relative(options.workingDirectory, absolutePath);
2248
- if (relativePath.startsWith("..") && !isAbsolute(path)) {
2442
+ if (relativePath.startsWith("..") && !isAbsolute(filePath)) {
2249
2443
  return {
2250
2444
  success: false,
2251
2445
  error: "Path escapes the working directory. Use an absolute path if intentional.",
@@ -2255,22 +2449,43 @@ Use this to understand existing code, check file contents, or gather context.`,
2255
2449
  if (!existsSync3(absolutePath)) {
2256
2450
  return {
2257
2451
  success: false,
2258
- error: `File not found: ${path}`,
2452
+ error: `File not found: ${filePath}`,
2259
2453
  content: null
2260
2454
  };
2261
2455
  }
2262
2456
  const stats = await stat(absolutePath);
2263
- if (stats.size > MAX_FILE_SIZE) {
2457
+ if (stats.isDirectory()) {
2264
2458
  return {
2265
2459
  success: false,
2266
- error: `File is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_FILE_SIZE / 1024 / 1024}MB.`,
2460
+ error: 'Path is a directory, not a file. Use bash with "ls" to list directory contents.',
2267
2461
  content: null
2268
2462
  };
2269
2463
  }
2270
- if (stats.isDirectory()) {
2464
+ if (isImageFile(absolutePath)) {
2465
+ if (stats.size > MAX_IMAGE_SIZE) {
2466
+ return {
2467
+ success: false,
2468
+ error: `Image is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_IMAGE_SIZE / 1024 / 1024}MB.`,
2469
+ content: null
2470
+ };
2471
+ }
2472
+ const buffer = await readFile2(absolutePath);
2473
+ const base64 = buffer.toString("base64");
2474
+ const mediaType = getImageMediaType(absolutePath);
2475
+ return {
2476
+ success: true,
2477
+ path: absolutePath,
2478
+ relativePath: relative(options.workingDirectory, absolutePath),
2479
+ content: `[Image: ${relativePath} (${mediaType}, ${(stats.size / 1024).toFixed(1)}KB)]`,
2480
+ mediaType,
2481
+ imageData: base64,
2482
+ sizeBytes: stats.size
2483
+ };
2484
+ }
2485
+ if (stats.size > MAX_FILE_SIZE) {
2271
2486
  return {
2272
2487
  success: false,
2273
- error: 'Path is a directory, not a file. Use bash with "ls" to list directory contents.',
2488
+ error: `File is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_FILE_SIZE / 1024 / 1024}MB.`,
2274
2489
  content: null
2275
2490
  };
2276
2491
  }
@@ -2286,9 +2501,7 @@ Use this to understand existing code, check file contents, or gather context.`,
2286
2501
  content: null
2287
2502
  };
2288
2503
  }
2289
- content = lines.slice(start, end).join("\n");
2290
- const lineNumbers = lines.slice(start, end).map((line, idx) => `${(start + idx + 1).toString().padStart(4)}: ${line}`).join("\n");
2291
- content = lineNumbers;
2504
+ content = lines.slice(start, end).map((line, idx) => `${(start + idx + 1).toString().padStart(4)}: ${line}`).join("\n");
2292
2505
  }
2293
2506
  const truncatedContent = truncateOutput(content, MAX_OUTPUT_CHARS3);
2294
2507
  const wasTruncated = truncatedContent.length < content.length;
@@ -2315,6 +2528,19 @@ Use this to understand existing code, check file contents, or gather context.`,
2315
2528
  content: null
2316
2529
  };
2317
2530
  }
2531
+ },
2532
+ toModelOutput: ({ output }) => {
2533
+ if (output && typeof output === "object" && "imageData" in output && output.imageData) {
2534
+ const result = output;
2535
+ return {
2536
+ type: "content",
2537
+ value: [
2538
+ { type: "text", text: result.content },
2539
+ { type: "image-data", data: result.imageData, mediaType: result.mediaType }
2540
+ ]
2541
+ };
2542
+ }
2543
+ return typeof output === "string" ? { type: "text", value: output } : { type: "json", value: output };
2318
2544
  }
2319
2545
  });
2320
2546
  }
@@ -2515,7 +2741,7 @@ function clearCheckpointManager(sessionId) {
2515
2741
  }
2516
2742
 
2517
2743
  // src/lsp/index.ts
2518
- import { extname as extname2, dirname as dirname4 } from "path";
2744
+ import { extname as extname3, dirname as dirname4 } from "path";
2519
2745
 
2520
2746
  // src/lsp/servers.ts
2521
2747
  import { spawn } from "child_process";
@@ -2638,9 +2864,9 @@ import {
2638
2864
  import { pathToFileURL, fileURLToPath } from "url";
2639
2865
  import { readFile as readFile4 } from "fs/promises";
2640
2866
  import { existsSync as existsSync6 } from "fs";
2641
- import { extname, normalize } from "path";
2867
+ import { extname as extname2, normalize } from "path";
2642
2868
  function getLanguageId(filePath) {
2643
- const ext = extname(filePath).toLowerCase();
2869
+ const ext = extname2(filePath).toLowerCase();
2644
2870
  const map = {
2645
2871
  ".ts": "typescript",
2646
2872
  ".tsx": "typescriptreact",
@@ -3028,7 +3254,7 @@ var state = {
3028
3254
  };
3029
3255
  async function getClientForFile(filePath) {
3030
3256
  const normalized = normalizePath(filePath);
3031
- const ext = extname2(normalized);
3257
+ const ext = extname3(normalized);
3032
3258
  const serverDef = getServerForExtension(ext);
3033
3259
  if (!serverDef) {
3034
3260
  return null;
@@ -3121,7 +3347,7 @@ async function formatDiagnosticsOutput(filePath, options = {}) {
3121
3347
  return formatDiagnosticsForAgent(filePath, diagnostics, options);
3122
3348
  }
3123
3349
  function isSupported(filePath) {
3124
- const ext = extname2(filePath);
3350
+ const ext = extname3(filePath);
3125
3351
  return getServerForExtension(ext) !== null;
3126
3352
  }
3127
3353
 
@@ -3548,7 +3774,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
3548
3774
  // src/tools/linter.ts
3549
3775
  import { tool as tool6 } from "ai";
3550
3776
  import { z as z7 } from "zod";
3551
- import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as extname4 } from "path";
3777
+ import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as extname5 } from "path";
3552
3778
  import { existsSync as existsSync9 } from "fs";
3553
3779
  import { readdir as readdir2, stat as stat2 } from "fs/promises";
3554
3780
  var linterInputSchema = z7.object({
@@ -3571,7 +3797,7 @@ async function findSupportedFiles(dir, workingDirectory, maxFiles = 50) {
3571
3797
  }
3572
3798
  await walk(fullPath);
3573
3799
  } else if (entry.isFile()) {
3574
- const ext = extname4(entry.name);
3800
+ const ext = extname5(entry.name);
3575
3801
  if (supportedExtensions.includes(ext)) {
3576
3802
  files.push(fullPath);
3577
3803
  }
@@ -4908,6 +5134,59 @@ Context: ${context}` : query;
4908
5134
 
4909
5135
  // src/tools/index.ts
4910
5136
  init_semantic_search();
5137
+
5138
+ // src/tools/task.ts
5139
+ import { tool as tool11 } from "ai";
5140
+ import { z as z12 } from "zod";
5141
+ import Ajv from "ajv";
5142
+ var ajv = new Ajv({ allErrors: true });
5143
+ function createCompleteTaskTool(options) {
5144
+ const validate = ajv.compile(options.outputSchema);
5145
+ return tool11({
5146
+ description: "Call this tool when you have completed the task. Pass the result as a JSON object matching the required output schema. If the result does not match the schema, you will receive validation errors and should fix and retry.",
5147
+ inputSchema: z12.object({
5148
+ result: z12.record(z12.string(), z12.unknown()).describe("The task result as a JSON object matching the output schema")
5149
+ }),
5150
+ execute: async (input) => {
5151
+ const valid = validate(input.result);
5152
+ if (!valid) {
5153
+ const errors = validate.errors?.map((e) => ({
5154
+ path: e.instancePath || "/",
5155
+ message: e.message,
5156
+ params: e.params
5157
+ }));
5158
+ return {
5159
+ status: "validation_error",
5160
+ message: "The result does not match the required output schema. Fix the errors and call complete_task again.",
5161
+ errors,
5162
+ expectedSchema: options.outputSchema
5163
+ };
5164
+ }
5165
+ options.onComplete({ status: "completed", result: input.result });
5166
+ return {
5167
+ status: "completed",
5168
+ message: "Task completed successfully."
5169
+ };
5170
+ }
5171
+ });
5172
+ }
5173
+ function createTaskFailedTool(options) {
5174
+ return tool11({
5175
+ description: "Call this tool if you are unable to complete the task. Provide a clear reason explaining why the task cannot be completed.",
5176
+ inputSchema: z12.object({
5177
+ reason: z12.string().describe("Explanation of why the task cannot be completed")
5178
+ }),
5179
+ execute: async (input) => {
5180
+ options.onComplete({ status: "failed", error: input.reason });
5181
+ return {
5182
+ status: "failed",
5183
+ message: `Task marked as failed: ${input.reason}`
5184
+ };
5185
+ }
5186
+ });
5187
+ }
5188
+
5189
+ // src/tools/index.ts
4911
5190
  init_semantic();
4912
5191
  init_semantic_search();
4913
5192
  async function createTools(options) {
@@ -4959,6 +5238,10 @@ async function createTools(options) {
4959
5238
  } catch {
4960
5239
  }
4961
5240
  }
5241
+ if (options.taskTools) {
5242
+ tools.complete_task = createCompleteTaskTool(options.taskTools);
5243
+ tools.task_failed = createTaskFailedTool(options.taskTools);
5244
+ }
4962
5245
  return tools;
4963
5246
  }
4964
5247
 
@@ -5276,6 +5559,30 @@ function formatTodosForContext(todos) {
5276
5559
  }
5277
5560
  return lines.join("\n");
5278
5561
  }
5562
+ function buildTaskPromptAddendum(outputSchema) {
5563
+ return `
5564
+ ## Task Mode
5565
+
5566
+ You are running in **task mode**. You have been given a specific task to complete autonomously.
5567
+
5568
+ ### Rules
5569
+ 1. Work independently \u2014 no human will approve tool calls. All tools run without approval.
5570
+ 2. Keep working until the task is fully complete.
5571
+ 3. When done, call the \`complete_task\` tool with a JSON result matching the output schema below.
5572
+ 4. If you determine the task is impossible or encounter an unrecoverable error, call the \`task_failed\` tool with a clear reason.
5573
+ 5. Do NOT stop without calling one of these two tools.
5574
+
5575
+ ### Output Schema
5576
+ The \`complete_task\` tool expects a \`result\` object matching this JSON Schema:
5577
+ \`\`\`json
5578
+ ${JSON.stringify(outputSchema, null, 2)}
5579
+ \`\`\`
5580
+
5581
+ ### Completion Tools
5582
+ - **\`complete_task({ result: ... })\`** \u2014 Call when the task is done. The result is validated against the schema above. If validation fails you will get errors back \u2014 fix and retry.
5583
+ - **\`task_failed({ reason: "..." })\`** \u2014 Call only if the task truly cannot be completed.
5584
+ `;
5585
+ }
5279
5586
  function createSummaryPrompt(conversationHistory) {
5280
5587
  return `Please provide a concise summary of the following conversation history. Focus on:
5281
5588
  1. The main task or goal being worked on
@@ -5537,6 +5844,25 @@ ${this.summary}`
5537
5844
  }
5538
5845
  };
5539
5846
 
5847
+ // src/utils/webhook.ts
5848
+ async function sendWebhook(url, event) {
5849
+ try {
5850
+ const controller = new AbortController();
5851
+ const timeout = setTimeout(() => controller.abort(), 5e3);
5852
+ await fetch(url, {
5853
+ method: "POST",
5854
+ headers: {
5855
+ "Content-Type": "application/json",
5856
+ "X-SparkECoder-Event": event.type
5857
+ },
5858
+ body: JSON.stringify(event),
5859
+ signal: controller.signal
5860
+ });
5861
+ clearTimeout(timeout);
5862
+ } catch {
5863
+ }
5864
+ }
5865
+
5540
5866
  // src/agent/index.ts
5541
5867
  var approvalResolvers = /* @__PURE__ */ new Map();
5542
5868
  var Agent = class _Agent {
@@ -5756,6 +6082,145 @@ ${prompt}` });
5756
6082
  steps: result.steps
5757
6083
  };
5758
6084
  }
6085
+ /**
6086
+ * Run the agent in task mode — loops autonomously until the agent calls
6087
+ * complete_task or task_failed (or hits maxIterations).
6088
+ * All tools run without approval. Webhook events are fired throughout.
6089
+ */
6090
+ async runTask(options) {
6091
+ const config = getConfig();
6092
+ const maxIterations = options.taskConfig.maxIterations ?? 50;
6093
+ const webhookUrl = options.taskConfig.webhookUrl;
6094
+ const fireWebhook = (type, data) => {
6095
+ if (!webhookUrl) return;
6096
+ sendWebhook(webhookUrl, {
6097
+ type,
6098
+ taskId: this.session.id,
6099
+ sessionId: this.session.id,
6100
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6101
+ data
6102
+ });
6103
+ };
6104
+ const completion = { signal: null };
6105
+ const onComplete = (signal) => {
6106
+ completion.signal = signal;
6107
+ };
6108
+ const taskTools = await createTools({
6109
+ sessionId: this.session.id,
6110
+ workingDirectory: this.session.workingDirectory,
6111
+ skillsDirectories: config.resolvedSkillsDirectories,
6112
+ onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
6113
+ onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
6114
+ onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0,
6115
+ taskTools: {
6116
+ outputSchema: options.taskConfig.outputSchema,
6117
+ onComplete
6118
+ }
6119
+ });
6120
+ const baseSystemPrompt = await buildSystemPrompt({
6121
+ workingDirectory: this.session.workingDirectory,
6122
+ skillsDirectories: config.resolvedSkillsDirectories,
6123
+ sessionId: this.session.id,
6124
+ discoveredSkills: config.discoveredSkills,
6125
+ activeFiles: []
6126
+ });
6127
+ const taskAddendum = buildTaskPromptAddendum(options.taskConfig.outputSchema);
6128
+ const systemPrompt = `${baseSystemPrompt}
6129
+
6130
+ ${taskAddendum}`;
6131
+ fireWebhook("task.started", { prompt: options.prompt });
6132
+ this.context.addUserMessage(options.prompt);
6133
+ let iteration = 0;
6134
+ while (iteration < maxIterations) {
6135
+ iteration++;
6136
+ if (options.abortSignal?.aborted) {
6137
+ const cancelError = "Task was cancelled";
6138
+ fireWebhook("task.failed", { status: "failed", error: cancelError, iterations: iteration });
6139
+ return { status: "failed", error: cancelError, iterations: iteration };
6140
+ }
6141
+ const messages = await this.context.getMessages();
6142
+ const useAnthropic = isAnthropicModel(this.session.model);
6143
+ const result = await generateText3({
6144
+ model: resolveModel(this.session.model),
6145
+ system: systemPrompt,
6146
+ messages,
6147
+ tools: taskTools,
6148
+ stopWhen: stepCountIs2(500),
6149
+ abortSignal: options.abortSignal,
6150
+ providerOptions: useAnthropic ? {
6151
+ anthropic: {
6152
+ thinking: { type: "enabled", budgetTokens: 1e4 }
6153
+ }
6154
+ } : void 0,
6155
+ onStepFinish: (step) => {
6156
+ options.onStepFinish?.(step);
6157
+ fireWebhook("task.step_finished", { iteration, text: step.text });
6158
+ }
6159
+ });
6160
+ const responseMessages = result.response.messages;
6161
+ this.context.addResponseMessages(responseMessages);
6162
+ if (result.text) {
6163
+ options.onText?.(result.text);
6164
+ fireWebhook("task.message", { iteration, text: result.text });
6165
+ }
6166
+ for (const step of result.steps) {
6167
+ if (step.toolCalls) {
6168
+ for (const tc of step.toolCalls) {
6169
+ options.onToolCall?.({ toolCallId: tc.toolCallId, toolName: tc.toolName, input: tc.args });
6170
+ fireWebhook("task.tool_call", { iteration, toolName: tc.toolName, toolCallId: tc.toolCallId, input: tc.args });
6171
+ }
6172
+ }
6173
+ if (step.toolResults) {
6174
+ for (const tr of step.toolResults) {
6175
+ options.onToolResult?.({ toolCallId: tr.toolCallId, toolName: tr.toolName, output: tr.result });
6176
+ fireWebhook("task.tool_result", { iteration, toolName: tr.toolName, toolCallId: tr.toolCallId, output: tr.result });
6177
+ }
6178
+ }
6179
+ }
6180
+ if (completion.signal) {
6181
+ const sig = completion.signal;
6182
+ const finalStatus = sig.status;
6183
+ const eventType = finalStatus === "completed" ? "task.completed" : "task.failed";
6184
+ fireWebhook(eventType, {
6185
+ status: finalStatus,
6186
+ result: sig.result,
6187
+ error: sig.error,
6188
+ iterations: iteration
6189
+ });
6190
+ const updatedTask2 = {
6191
+ ...options.taskConfig,
6192
+ status: finalStatus,
6193
+ result: sig.result,
6194
+ error: sig.error,
6195
+ iterations: iteration
6196
+ };
6197
+ await sessionQueries.update(this.session.id, {
6198
+ config: { ...this.session.config, task: updatedTask2 }
6199
+ });
6200
+ return {
6201
+ status: finalStatus,
6202
+ result: sig.result,
6203
+ error: sig.error,
6204
+ iterations: iteration
6205
+ };
6206
+ }
6207
+ this.context.addUserMessage(
6208
+ "Continue working on the task. When done, call `complete_task` with the result. If you cannot complete it, call `task_failed` with a reason."
6209
+ );
6210
+ }
6211
+ const timeoutError = `Task did not complete within ${maxIterations} iterations`;
6212
+ fireWebhook("task.failed", { status: "failed", error: timeoutError, iterations: iteration });
6213
+ const updatedTask = {
6214
+ ...options.taskConfig,
6215
+ status: "failed",
6216
+ error: timeoutError,
6217
+ iterations: iteration
6218
+ };
6219
+ await sessionQueries.update(this.session.id, {
6220
+ config: { ...this.session.config, task: updatedTask }
6221
+ });
6222
+ return { status: "failed", error: timeoutError, iterations: iteration };
6223
+ }
5759
6224
  /**
5760
6225
  * Wrap tools to add approval checking
5761
6226
  */
@@ -5769,9 +6234,9 @@ ${prompt}` });
5769
6234
  wrappedTools[name] = originalTool;
5770
6235
  continue;
5771
6236
  }
5772
- wrappedTools[name] = tool11({
6237
+ wrappedTools[name] = tool12({
5773
6238
  description: originalTool.description || "",
5774
- inputSchema: originalTool.inputSchema || z12.object({}),
6239
+ inputSchema: originalTool.inputSchema || z13.object({}),
5775
6240
  execute: async (input, toolOptions) => {
5776
6241
  const toolCallId = toolOptions.toolCallId || nanoid3();
5777
6242
  const execution = toolExecutionQueries.create({
@@ -5912,18 +6377,18 @@ function cleanupPendingInputs() {
5912
6377
  }
5913
6378
  }
5914
6379
  }
5915
- var createSessionSchema = z13.object({
5916
- name: z13.string().optional(),
5917
- workingDirectory: z13.string().optional(),
5918
- model: z13.string().optional(),
5919
- toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
6380
+ var createSessionSchema = z14.object({
6381
+ name: z14.string().optional(),
6382
+ workingDirectory: z14.string().optional(),
6383
+ model: z14.string().optional(),
6384
+ toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
5920
6385
  });
5921
- var paginationQuerySchema = z13.object({
5922
- limit: z13.string().optional(),
5923
- offset: z13.string().optional()
6386
+ var paginationQuerySchema = z14.object({
6387
+ limit: z14.string().optional(),
6388
+ offset: z14.string().optional()
5924
6389
  });
5925
- var messagesQuerySchema = z13.object({
5926
- limit: z13.string().optional()
6390
+ var messagesQuerySchema = z14.object({
6391
+ limit: z14.string().optional()
5927
6392
  });
5928
6393
  sessions.get(
5929
6394
  "/",
@@ -6062,10 +6527,10 @@ sessions.get("/:id/tools", async (c) => {
6062
6527
  count: executions.length
6063
6528
  });
6064
6529
  });
6065
- var updateSessionSchema = z13.object({
6066
- model: z13.string().optional(),
6067
- name: z13.string().optional(),
6068
- toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
6530
+ var updateSessionSchema = z14.object({
6531
+ model: z14.string().optional(),
6532
+ name: z14.string().optional(),
6533
+ toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
6069
6534
  });
6070
6535
  sessions.patch(
6071
6536
  "/:id",
@@ -6135,8 +6600,8 @@ sessions.post("/:id/clear", async (c) => {
6135
6600
  await agent.clearContext();
6136
6601
  return c.json({ success: true, sessionId: id });
6137
6602
  });
6138
- var pendingInputSchema = z13.object({
6139
- text: z13.string()
6603
+ var pendingInputSchema = z14.object({
6604
+ text: z14.string()
6140
6605
  });
6141
6606
  sessions.post(
6142
6607
  "/:id/pending-input",
@@ -6167,13 +6632,13 @@ sessions.get("/:id/pending-input", async (c) => {
6167
6632
  createdAt: pending.createdAt.toISOString()
6168
6633
  });
6169
6634
  });
6170
- var devtoolsContextSchema = z13.object({
6171
- url: z13.string(),
6172
- path: z13.string(),
6173
- pageName: z13.string().optional(),
6174
- screenWidth: z13.number().optional(),
6175
- screenHeight: z13.number().optional(),
6176
- devicePixelRatio: z13.number().optional()
6635
+ var devtoolsContextSchema = z14.object({
6636
+ url: z14.string(),
6637
+ path: z14.string(),
6638
+ pageName: z14.string().optional(),
6639
+ screenWidth: z14.number().optional(),
6640
+ screenHeight: z14.number().optional(),
6641
+ devicePixelRatio: z14.number().optional()
6177
6642
  });
6178
6643
  sessions.post(
6179
6644
  "/:id/devtools-context",
@@ -6395,7 +6860,7 @@ sessions.post("/:id/attachments", async (c) => {
6395
6860
  }
6396
6861
  const dir = ensureAttachmentsDir(sessionId);
6397
6862
  const id = nanoid4(10);
6398
- const ext = extname6(file.name) || "";
6863
+ const ext = extname7(file.name) || "";
6399
6864
  const safeFilename = `${id}_${basename4(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
6400
6865
  const filePath = join5(dir, safeFilename);
6401
6866
  const arrayBuffer = await file.arrayBuffer();
@@ -6421,7 +6886,7 @@ sessions.post("/:id/attachments", async (c) => {
6421
6886
  }
6422
6887
  const dir = ensureAttachmentsDir(sessionId);
6423
6888
  const id = nanoid4(10);
6424
- const ext = extname6(body.filename) || "";
6889
+ const ext = extname7(body.filename) || "";
6425
6890
  const safeFilename = `${id}_${basename4(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
6426
6891
  const filePath = join5(dir, safeFilename);
6427
6892
  let base64Data = body.data;
@@ -6464,10 +6929,10 @@ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
6464
6929
  unlinkSync(filePath);
6465
6930
  return c.json({ success: true, id: attachmentId });
6466
6931
  });
6467
- var filesQuerySchema = z13.object({
6468
- query: z13.string().optional(),
6932
+ var filesQuerySchema = z14.object({
6933
+ query: z14.string().optional(),
6469
6934
  // Filter query (e.g., "src/com" to match "src/components")
6470
- limit: z13.string().optional()
6935
+ limit: z14.string().optional()
6471
6936
  // Max results (default 50)
6472
6937
  });
6473
6938
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -6551,7 +7016,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
6551
7016
  if (entry.name.startsWith(".")) {
6552
7017
  continue;
6553
7018
  }
6554
- const ext = extname6(entry.name).toLowerCase();
7019
+ const ext = extname7(entry.name).toLowerCase();
6555
7020
  if (IGNORED_EXTENSIONS.has(ext)) {
6556
7021
  continue;
6557
7022
  }
@@ -6645,7 +7110,7 @@ sessions.get(
6645
7110
  init_db();
6646
7111
  import { Hono as Hono2 } from "hono";
6647
7112
  import { zValidator as zValidator2 } from "@hono/zod-validator";
6648
- import { z as z14 } from "zod";
7113
+ import { z as z15 } from "zod";
6649
7114
  import { existsSync as existsSync14, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
6650
7115
  import { join as join6 } from "path";
6651
7116
  init_config();
@@ -6810,30 +7275,30 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
6810
7275
  ${prompt}`;
6811
7276
  }
6812
7277
  var agents = new Hono2();
6813
- var attachmentSchema = z14.object({
6814
- type: z14.enum(["image", "file"]),
6815
- data: z14.string(),
7278
+ var attachmentSchema = z15.object({
7279
+ type: z15.enum(["image", "file"]),
7280
+ data: z15.string(),
6816
7281
  // base64 data URL or raw base64
6817
- mediaType: z14.string().optional(),
6818
- filename: z14.string().optional()
7282
+ mediaType: z15.string().optional(),
7283
+ filename: z15.string().optional()
6819
7284
  });
6820
- var runPromptSchema = z14.object({
6821
- prompt: z14.string(),
7285
+ var runPromptSchema = z15.object({
7286
+ prompt: z15.string(),
6822
7287
  // Can be empty if attachments are provided
6823
- attachments: z14.array(attachmentSchema).optional()
7288
+ attachments: z15.array(attachmentSchema).optional()
6824
7289
  }).refine(
6825
7290
  (data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
6826
7291
  { message: "Either prompt or attachments must be provided" }
6827
7292
  );
6828
- var quickStartSchema = z14.object({
6829
- prompt: z14.string().min(1),
6830
- name: z14.string().optional(),
6831
- workingDirectory: z14.string().optional(),
6832
- model: z14.string().optional(),
6833
- toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
7293
+ var quickStartSchema = z15.object({
7294
+ prompt: z15.string().min(1),
7295
+ name: z15.string().optional(),
7296
+ workingDirectory: z15.string().optional(),
7297
+ model: z15.string().optional(),
7298
+ toolApprovals: z15.record(z15.string(), z15.boolean()).optional()
6834
7299
  });
6835
- var rejectSchema = z14.object({
6836
- reason: z14.string().optional()
7300
+ var rejectSchema = z15.object({
7301
+ reason: z15.string().optional()
6837
7302
  }).optional();
6838
7303
  var streamAbortControllers = /* @__PURE__ */ new Map();
6839
7304
  function getAttachmentsDirectory(sessionId) {
@@ -7263,7 +7728,7 @@ agents.get("/:id/watch", async (c) => {
7263
7728
  "Cache-Control": "no-cache",
7264
7729
  "Connection": "keep-alive",
7265
7730
  "x-vercel-ai-ui-message-stream": "v1",
7266
- "x-stream-id": streamId
7731
+ "x-stream-id": streamId ?? ""
7267
7732
  }
7268
7733
  });
7269
7734
  });
@@ -7621,7 +8086,7 @@ agents.post(
7621
8086
  init_config();
7622
8087
  import { Hono as Hono3 } from "hono";
7623
8088
  import { zValidator as zValidator3 } from "@hono/zod-validator";
7624
- import { z as z15 } from "zod";
8089
+ import { z as z16 } from "zod";
7625
8090
  import { readFileSync as readFileSync5 } from "fs";
7626
8091
  import { fileURLToPath as fileURLToPath3 } from "url";
7627
8092
  import { dirname as dirname6, join as join7 } from "path";
@@ -7731,9 +8196,9 @@ health.get("/api-keys", async (c) => {
7731
8196
  supportedProviders: SUPPORTED_PROVIDERS
7732
8197
  });
7733
8198
  });
7734
- var setApiKeySchema = z15.object({
7735
- provider: z15.string(),
7736
- apiKey: z15.string().min(1)
8199
+ var setApiKeySchema = z16.object({
8200
+ provider: z16.string(),
8201
+ apiKey: z16.string().min(1)
7737
8202
  });
7738
8203
  health.post(
7739
8204
  "/api-keys",
@@ -7772,13 +8237,13 @@ health.delete("/api-keys/:provider", async (c) => {
7772
8237
  // src/server/routes/terminals.ts
7773
8238
  import { Hono as Hono4 } from "hono";
7774
8239
  import { zValidator as zValidator4 } from "@hono/zod-validator";
7775
- import { z as z16 } from "zod";
8240
+ import { z as z17 } from "zod";
7776
8241
  init_db();
7777
8242
  var terminals = new Hono4();
7778
- var spawnSchema = z16.object({
7779
- command: z16.string(),
7780
- cwd: z16.string().optional(),
7781
- name: z16.string().optional()
8243
+ var spawnSchema = z17.object({
8244
+ command: z17.string(),
8245
+ cwd: z17.string().optional(),
8246
+ name: z17.string().optional()
7782
8247
  });
7783
8248
  terminals.post(
7784
8249
  "/:sessionId/terminals",
@@ -7859,8 +8324,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
7859
8324
  // We don't track exit codes in tmux mode
7860
8325
  });
7861
8326
  });
7862
- var logsQuerySchema = z16.object({
7863
- tail: z16.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
8327
+ var logsQuerySchema = z17.object({
8328
+ tail: z17.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
7864
8329
  });
7865
8330
  terminals.get(
7866
8331
  "/:sessionId/terminals/:terminalId/logs",
@@ -7884,8 +8349,8 @@ terminals.get(
7884
8349
  });
7885
8350
  }
7886
8351
  );
7887
- var killSchema = z16.object({
7888
- signal: z16.enum(["SIGTERM", "SIGKILL"]).optional()
8352
+ var killSchema = z17.object({
8353
+ signal: z17.enum(["SIGTERM", "SIGKILL"]).optional()
7889
8354
  });
7890
8355
  terminals.post(
7891
8356
  "/:sessionId/terminals/:terminalId/kill",
@@ -7899,8 +8364,8 @@ terminals.post(
7899
8364
  return c.json({ success: true, message: "Terminal killed" });
7900
8365
  }
7901
8366
  );
7902
- var writeSchema = z16.object({
7903
- input: z16.string()
8367
+ var writeSchema = z17.object({
8368
+ input: z17.string()
7904
8369
  });
7905
8370
  terminals.post(
7906
8371
  "/:sessionId/terminals/:terminalId/write",
@@ -8081,6 +8546,131 @@ data: ${JSON.stringify({ status: "stopped" })}
8081
8546
  );
8082
8547
  });
8083
8548
 
8549
+ // src/server/routes/tasks.ts
8550
+ init_db();
8551
+ import { Hono as Hono5 } from "hono";
8552
+ import { zValidator as zValidator5 } from "@hono/zod-validator";
8553
+ import { z as z18 } from "zod";
8554
+ init_config();
8555
+ var tasks = new Hono5();
8556
+ var taskAbortControllers = /* @__PURE__ */ new Map();
8557
+ var createTaskSchema = z18.object({
8558
+ prompt: z18.string().min(1),
8559
+ outputSchema: z18.record(z18.string(), z18.unknown()),
8560
+ webhookUrl: z18.string().url().optional(),
8561
+ model: z18.string().optional(),
8562
+ workingDirectory: z18.string().optional(),
8563
+ name: z18.string().optional(),
8564
+ maxIterations: z18.number().int().min(1).max(500).optional()
8565
+ });
8566
+ tasks.post(
8567
+ "/",
8568
+ zValidator5("json", createTaskSchema),
8569
+ async (c) => {
8570
+ const body = c.req.valid("json");
8571
+ const config = getConfig();
8572
+ const taskConfig = {
8573
+ enabled: true,
8574
+ outputSchema: body.outputSchema,
8575
+ webhookUrl: body.webhookUrl,
8576
+ maxIterations: body.maxIterations ?? 50,
8577
+ status: "running"
8578
+ };
8579
+ const agent = await Agent.create({
8580
+ name: body.name || "Task",
8581
+ workingDirectory: body.workingDirectory || config.resolvedWorkingDirectory,
8582
+ model: body.model || config.defaultModel,
8583
+ sessionConfig: {
8584
+ toolApprovals: { bash: false, write_file: false, read_file: false },
8585
+ task: taskConfig
8586
+ }
8587
+ });
8588
+ const taskId = agent.sessionId;
8589
+ const abortController = new AbortController();
8590
+ taskAbortControllers.set(taskId, abortController);
8591
+ (async () => {
8592
+ try {
8593
+ await agent.runTask({
8594
+ prompt: body.prompt,
8595
+ taskConfig,
8596
+ abortSignal: abortController.signal
8597
+ });
8598
+ } catch (err) {
8599
+ if (err.name === "AbortError" || abortController.signal.aborted) {
8600
+ console.log(`[TASK] Task ${taskId} was cancelled`);
8601
+ } else {
8602
+ console.error(`[TASK] Error in task ${taskId}:`, err.message);
8603
+ const failedTask = {
8604
+ ...taskConfig,
8605
+ status: "failed",
8606
+ error: err.message || "Unknown error"
8607
+ };
8608
+ await sessionQueries.update(taskId, {
8609
+ config: {
8610
+ toolApprovals: { bash: false, write_file: false, read_file: false },
8611
+ task: failedTask
8612
+ }
8613
+ });
8614
+ }
8615
+ } finally {
8616
+ taskAbortControllers.delete(taskId);
8617
+ }
8618
+ })();
8619
+ return c.json({ taskId, status: "running" }, 201);
8620
+ }
8621
+ );
8622
+ tasks.get("/:id", async (c) => {
8623
+ const id = c.req.param("id");
8624
+ const session = await sessionQueries.getById(id);
8625
+ if (!session) {
8626
+ return c.json({ error: "Task not found" }, 404);
8627
+ }
8628
+ const task = session.config?.task;
8629
+ if (!task?.enabled) {
8630
+ return c.json({ error: "Session is not a task" }, 400);
8631
+ }
8632
+ return c.json({
8633
+ taskId: id,
8634
+ status: task.status,
8635
+ result: task.result,
8636
+ error: task.error,
8637
+ iterations: task.iterations,
8638
+ model: session.model,
8639
+ name: session.name,
8640
+ createdAt: session.createdAt.toISOString(),
8641
+ updatedAt: session.updatedAt.toISOString()
8642
+ });
8643
+ });
8644
+ tasks.post("/:id/cancel", async (c) => {
8645
+ const id = c.req.param("id");
8646
+ const session = await sessionQueries.getById(id);
8647
+ if (!session) {
8648
+ return c.json({ error: "Task not found" }, 404);
8649
+ }
8650
+ const task = session.config?.task;
8651
+ if (!task?.enabled) {
8652
+ return c.json({ error: "Session is not a task" }, 400);
8653
+ }
8654
+ if (task.status !== "running") {
8655
+ return c.json({ error: `Task is already ${task.status}` }, 400);
8656
+ }
8657
+ const abortController = taskAbortControllers.get(id);
8658
+ if (abortController) {
8659
+ abortController.abort();
8660
+ taskAbortControllers.delete(id);
8661
+ }
8662
+ const cancelledTask = {
8663
+ ...task,
8664
+ status: "failed",
8665
+ error: "Task cancelled by user"
8666
+ };
8667
+ await sessionQueries.update(id, {
8668
+ config: { ...session.config, task: cancelledTask }
8669
+ });
8670
+ return c.json({ taskId: id, status: "failed", error: "Task cancelled by user" });
8671
+ });
8672
+ var tasks_default = tasks;
8673
+
8084
8674
  // src/server/index.ts
8085
8675
  init_config();
8086
8676
  init_db();
@@ -8417,7 +9007,7 @@ function stopWebUI() {
8417
9007
  }
8418
9008
  }
8419
9009
  async function createApp(options = {}) {
8420
- const app = new Hono5();
9010
+ const app = new Hono6();
8421
9011
  app.use("*", cors({
8422
9012
  origin: "*",
8423
9013
  // Allow all origins
@@ -8435,6 +9025,7 @@ async function createApp(options = {}) {
8435
9025
  app.route("/agents", agents);
8436
9026
  app.route("/sessions", terminals);
8437
9027
  app.route("/terminals", terminals);
9028
+ app.route("/tasks", tasks_default);
8438
9029
  app.get("/openapi.json", async (c) => {
8439
9030
  return c.json(generateOpenAPISpec());
8440
9031
  });