sparkecoder 0.1.60 → 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 (171) hide show
  1. package/dist/agent/index.d.ts +4 -3
  2. package/dist/agent/index.js +588 -176
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +818 -95
  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 +1202 -665
  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 +605 -68
  15. package/dist/server/index.js.map +1 -1
  16. package/dist/tools/index.d.ts +38 -3
  17. package/dist/tools/index.js +230 -2
  18. package/dist/tools/index.js.map +1 -1
  19. package/package.json +5 -3
  20. package/web/.next/BUILD_ID +1 -1
  21. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  22. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  23. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  24. package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
  25. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  26. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  27. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  29. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  44. package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
  45. package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  52. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  53. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  63. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  64. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  73. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  74. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  76. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  82. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  83. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  84. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  85. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
  86. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  88. package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
  89. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  90. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  91. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  92. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  93. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  94. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  95. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_19289e11._.js → 2374f_03d22c16._.js} +1 -1
  96. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b4b86c1f._.js → 2374f_0d0b1fb9._.js} +1 -1
  97. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_4858a1ea._.js → 2374f_3f7d6d28._.js} +1 -1
  98. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_51385fed._.js → 2374f_6166d14d._.js} +1 -1
  99. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_40e35a02._.js → 2374f_67b9525f._.js} +1 -1
  100. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_fb95e3c9._.js → 2374f_6b436efc._.js} +1 -1
  101. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_2f0d9f6f._.js → 2374f_784d851c._.js} +1 -1
  102. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_35475cbe._.js → 2374f_976b2ded._.js} +1 -1
  103. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_7db22cde._.js → 2374f_98eba491._.js} +1 -1
  104. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_90b8e4fb._.js → 2374f_99b7b533._.js} +1 -1
  105. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_4666c827._.js → 2374f_a1a36483._.js} +1 -1
  106. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_076f03ec._.js → 2374f_b98835eb._.js} +1 -1
  107. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_b17fce11._.js → 2374f_e19eaecc._.js} +1 -1
  108. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d8b9ce38._.js → 2374f_e3aec189._.js} +1 -1
  109. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__7775f784._.js → [root-of-the-server]__4f176a16._.js} +2 -2
  110. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__d59f831d._.js +15 -0
  111. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_645f4b90._.js → web_693f514e._.js} +2 -2
  112. package/web/.next/standalone/web/.next/server/chunks/ssr/web_a42a2651._.js +7 -0
  113. package/web/.next/standalone/web/.next/server/chunks/ssr/web_dc6ce793._.js +7 -0
  114. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_components_sessions-sidebar_tsx_92510070._.js +1 -1
  115. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  116. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  117. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  118. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  119. package/web/.next/standalone/web/.next/static/chunks/0358a0e7a40cfb93.css +1 -0
  120. package/web/.next/standalone/web/.next/static/chunks/1ecde4c0d426a635.js +1 -0
  121. package/web/.next/standalone/web/.next/static/chunks/20e0fa99c7a1c1fc.js +13 -0
  122. package/web/.next/standalone/web/.next/static/chunks/36688a049d72e8ab.js +5 -0
  123. package/web/.next/standalone/web/.next/static/chunks/95436454a7559b0d.js +7 -0
  124. package/web/.next/standalone/web/.next/static/chunks/a751ca474cc46212.js +5 -0
  125. package/web/.next/standalone/web/.next/static/static/chunks/0358a0e7a40cfb93.css +1 -0
  126. package/web/.next/standalone/web/.next/static/static/chunks/1ecde4c0d426a635.js +1 -0
  127. package/web/.next/standalone/web/.next/static/static/chunks/20e0fa99c7a1c1fc.js +13 -0
  128. package/web/.next/standalone/web/.next/static/static/chunks/36688a049d72e8ab.js +5 -0
  129. package/web/.next/standalone/web/.next/static/static/chunks/95436454a7559b0d.js +7 -0
  130. package/web/.next/standalone/web/.next/static/static/chunks/a751ca474cc46212.js +5 -0
  131. package/web/.next/standalone/web/src/components/ai-elements/complete-task-tool.tsx +126 -0
  132. package/web/.next/standalone/web/src/components/chat-interface.tsx +68 -3
  133. package/web/.next/standalone/web/src/components/sessions-sidebar.tsx +18 -1
  134. package/web/.next/standalone/web/src/lib/api.ts +12 -0
  135. package/web/.next/static/chunks/0358a0e7a40cfb93.css +1 -0
  136. package/web/.next/static/chunks/1ecde4c0d426a635.js +1 -0
  137. package/web/.next/static/chunks/20e0fa99c7a1c1fc.js +13 -0
  138. package/web/.next/static/chunks/36688a049d72e8ab.js +5 -0
  139. package/web/.next/static/chunks/95436454a7559b0d.js +7 -0
  140. package/web/.next/static/chunks/a751ca474cc46212.js +5 -0
  141. package/dist/schema-NcQknWCg.d.ts +0 -295
  142. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__bd396152._.js +0 -15
  143. package/web/.next/standalone/web/.next/server/chunks/ssr/web_9c9f0e3b._.js +0 -7
  144. package/web/.next/standalone/web/.next/server/chunks/ssr/web_d08270f7._.js +0 -7
  145. package/web/.next/standalone/web/.next/static/chunks/2868b007ce5163fc.css +0 -1
  146. package/web/.next/standalone/web/.next/static/chunks/3f295b6960943c38.js +0 -1
  147. package/web/.next/standalone/web/.next/static/chunks/631b023d37a08635.js +0 -13
  148. package/web/.next/standalone/web/.next/static/chunks/a2b4737b190d1b54.js +0 -5
  149. package/web/.next/standalone/web/.next/static/chunks/e97212fcc8221479.js +0 -5
  150. package/web/.next/standalone/web/.next/static/chunks/f6e47c8a9766ce91.js +0 -7
  151. package/web/.next/standalone/web/.next/static/static/chunks/2868b007ce5163fc.css +0 -1
  152. package/web/.next/standalone/web/.next/static/static/chunks/3f295b6960943c38.js +0 -1
  153. package/web/.next/standalone/web/.next/static/static/chunks/631b023d37a08635.js +0 -13
  154. package/web/.next/standalone/web/.next/static/static/chunks/a2b4737b190d1b54.js +0 -5
  155. package/web/.next/standalone/web/.next/static/static/chunks/e97212fcc8221479.js +0 -5
  156. package/web/.next/standalone/web/.next/static/static/chunks/f6e47c8a9766ce91.js +0 -7
  157. package/web/.next/static/chunks/2868b007ce5163fc.css +0 -1
  158. package/web/.next/static/chunks/3f295b6960943c38.js +0 -1
  159. package/web/.next/static/chunks/631b023d37a08635.js +0 -13
  160. package/web/.next/static/chunks/a2b4737b190d1b54.js +0 -5
  161. package/web/.next/static/chunks/e97212fcc8221479.js +0 -5
  162. package/web/.next/static/chunks/f6e47c8a9766ce91.js +0 -7
  163. /package/web/.next/standalone/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
  164. /package/web/.next/standalone/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
  165. /package/web/.next/standalone/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
  166. /package/web/.next/standalone/web/.next/static/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
  167. /package/web/.next/standalone/web/.next/static/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
  168. /package/web/.next/standalone/web/.next/static/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
  169. /package/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_buildManifest.js +0 -0
  170. /package/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_clientMiddlewareManifest.json +0 -0
  171. /package/web/.next/static/{R5xiWSOp_Nqqe_js-LROo → 8zJH-RqrUQ3scBGbdaCmn}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -404,7 +404,7 @@ var init_db = __esm({
404
404
 
405
405
  // src/config/types.ts
406
406
  import { z } from "zod";
407
- var ToolApprovalConfigSchema, SkillMetadataSchema, SessionConfigSchema, VectorGatewayConfigSchema, RemoteServerConfigSchema, SparkcoderConfigSchema;
407
+ var ToolApprovalConfigSchema, SkillMetadataSchema, TaskConfigSchema, SessionConfigSchema, VectorGatewayConfigSchema, RemoteServerConfigSchema, SparkcoderConfigSchema;
408
408
  var init_types = __esm({
409
409
  "src/config/types.ts"() {
410
410
  "use strict";
@@ -423,11 +423,22 @@ var init_types = __esm({
423
423
  // Glob patterns - auto-inject when working with matching files
424
424
  globs: z.array(z.string()).optional().default([])
425
425
  });
426
+ TaskConfigSchema = z.object({
427
+ enabled: z.boolean(),
428
+ outputSchema: z.record(z.string(), z.unknown()),
429
+ webhookUrl: z.string().url().optional(),
430
+ maxIterations: z.number().optional(),
431
+ status: z.enum(["running", "completed", "failed"]),
432
+ result: z.unknown().optional(),
433
+ error: z.string().optional(),
434
+ iterations: z.number().optional()
435
+ });
426
436
  SessionConfigSchema = z.object({
427
437
  toolApprovals: z.record(z.string(), z.boolean()).optional(),
428
438
  approvalWebhook: z.string().url().optional(),
429
439
  skillsDirectory: z.string().optional(),
430
- maxContextChars: z.number().optional().default(2e5)
440
+ maxContextChars: z.number().optional().default(2e5),
441
+ task: TaskConfigSchema.optional()
431
442
  });
432
443
  VectorGatewayConfigSchema = z.object({
433
444
  // Redis cluster nodes URL for Vector Gateway (or use REDIS_CLUSTER_NODES env var)
@@ -735,6 +746,9 @@ function getConfig() {
735
746
  }
736
747
  function requiresApproval(toolName, sessionConfig) {
737
748
  const config = getConfig();
749
+ if (sessionConfig?.toolApprovals?.["*"] !== void 0) {
750
+ return sessionConfig.toolApprovals["*"];
751
+ }
738
752
  if (sessionConfig?.toolApprovals?.[toolName] !== void 0) {
739
753
  return sessionConfig.toolApprovals[toolName];
740
754
  }
@@ -2418,7 +2432,7 @@ import { createInterface } from "readline";
2418
2432
 
2419
2433
  // src/server/index.ts
2420
2434
  import "dotenv/config";
2421
- import { Hono as Hono5 } from "hono";
2435
+ import { Hono as Hono6 } from "hono";
2422
2436
  import { serve } from "@hono/node-server";
2423
2437
  import { cors } from "hono/cors";
2424
2438
  import { logger } from "hono/logger";
@@ -2432,7 +2446,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
2432
2446
  init_db();
2433
2447
  import { Hono } from "hono";
2434
2448
  import { zValidator } from "@hono/zod-validator";
2435
- import { z as z13 } from "zod";
2449
+ import { z as z14 } from "zod";
2436
2450
  import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync as statSync2, unlinkSync } from "fs";
2437
2451
  import { readdir as readdir5 } from "fs/promises";
2438
2452
  import { join as join5, basename as basename4, extname as extname7, relative as relative9 } from "path";
@@ -2442,18 +2456,176 @@ import { nanoid as nanoid4 } from "nanoid";
2442
2456
  import {
2443
2457
  streamText as streamText2,
2444
2458
  generateText as generateText3,
2445
- tool as tool11,
2459
+ tool as tool12,
2446
2460
  stepCountIs as stepCountIs2
2447
2461
  } from "ai";
2448
2462
 
2449
2463
  // src/agent/model.ts
2450
2464
  import { gateway } from "@ai-sdk/gateway";
2465
+
2466
+ // src/agent/remote-model.ts
2467
+ function serializePrompt(prompt) {
2468
+ return prompt.map((msg) => {
2469
+ if (!Array.isArray(msg.content)) return msg;
2470
+ return {
2471
+ ...msg,
2472
+ content: msg.content.map((part) => {
2473
+ if (part.type === "file" && part.data instanceof Uint8Array) {
2474
+ return {
2475
+ ...part,
2476
+ data: Buffer.from(part.data).toString("base64"),
2477
+ _base64: true
2478
+ };
2479
+ }
2480
+ return part;
2481
+ })
2482
+ };
2483
+ });
2484
+ }
2485
+ function deserializeValue(value) {
2486
+ if (value && typeof value === "object") {
2487
+ if (value.__uint8array && typeof value.data === "string") {
2488
+ return Buffer.from(value.data, "base64");
2489
+ }
2490
+ if (Array.isArray(value)) {
2491
+ return value.map(deserializeValue);
2492
+ }
2493
+ const result = {};
2494
+ for (const [k, v] of Object.entries(value)) {
2495
+ result[k] = deserializeValue(v);
2496
+ }
2497
+ return result;
2498
+ }
2499
+ return value;
2500
+ }
2501
+ function prepareOptions(options) {
2502
+ const { abortSignal, ...rest } = options;
2503
+ return {
2504
+ ...rest,
2505
+ prompt: serializePrompt(options.prompt)
2506
+ };
2507
+ }
2508
+ function createRemoteModel(modelId, config) {
2509
+ const baseUrl = config.url.replace(/\/$/, "");
2510
+ const headers = {
2511
+ "Content-Type": "application/json",
2512
+ "Authorization": `Bearer ${config.authKey}`
2513
+ };
2514
+ return {
2515
+ specificationVersion: "v3",
2516
+ provider: "remote-proxy",
2517
+ modelId,
2518
+ supportedUrls: {},
2519
+ async doGenerate(options) {
2520
+ const res = await fetch(`${baseUrl}/inference/generate`, {
2521
+ method: "POST",
2522
+ headers,
2523
+ body: JSON.stringify({
2524
+ modelId,
2525
+ options: prepareOptions(options)
2526
+ }),
2527
+ signal: options.abortSignal
2528
+ });
2529
+ if (!res.ok) {
2530
+ const err = await res.json().catch(() => ({}));
2531
+ throw new Error(
2532
+ `Remote inference failed (${res.status}): ${err.error || res.statusText}`
2533
+ );
2534
+ }
2535
+ const result = await res.json();
2536
+ return deserializeValue(result);
2537
+ },
2538
+ async doStream(options) {
2539
+ const res = await fetch(`${baseUrl}/inference/stream`, {
2540
+ method: "POST",
2541
+ headers,
2542
+ body: JSON.stringify({
2543
+ modelId,
2544
+ options: prepareOptions(options)
2545
+ }),
2546
+ signal: options.abortSignal
2547
+ });
2548
+ if (!res.ok) {
2549
+ const err = await res.json().catch(() => ({}));
2550
+ throw new Error(
2551
+ `Remote inference failed (${res.status}): ${err.error || res.statusText}`
2552
+ );
2553
+ }
2554
+ const reader = res.body.getReader();
2555
+ const decoder = new TextDecoder();
2556
+ let buffer = "";
2557
+ const stream = new ReadableStream({
2558
+ async pull(controller) {
2559
+ while (true) {
2560
+ const { done, value } = await reader.read();
2561
+ if (done) {
2562
+ if (buffer.trim()) {
2563
+ try {
2564
+ const parsed = deserializeValue(JSON.parse(buffer.trim()));
2565
+ if (parsed.type === "error") {
2566
+ controller.error(new Error(parsed.error));
2567
+ } else {
2568
+ controller.enqueue(parsed);
2569
+ }
2570
+ } catch {
2571
+ }
2572
+ }
2573
+ controller.close();
2574
+ return;
2575
+ }
2576
+ buffer += decoder.decode(value, { stream: true });
2577
+ const lines = buffer.split("\n");
2578
+ buffer = lines.pop() || "";
2579
+ for (const line of lines) {
2580
+ if (!line.trim()) continue;
2581
+ try {
2582
+ const parsed = deserializeValue(JSON.parse(line));
2583
+ if (parsed.type === "error") {
2584
+ controller.error(new Error(parsed.error));
2585
+ return;
2586
+ }
2587
+ controller.enqueue(parsed);
2588
+ } catch {
2589
+ }
2590
+ }
2591
+ }
2592
+ },
2593
+ cancel() {
2594
+ reader.cancel();
2595
+ }
2596
+ });
2597
+ const responseHeaders = {};
2598
+ res.headers.forEach((v, k) => {
2599
+ if (k.startsWith("x-upstream-")) {
2600
+ responseHeaders[k.replace("x-upstream-", "")] = v;
2601
+ }
2602
+ });
2603
+ return {
2604
+ stream,
2605
+ response: Object.keys(responseHeaders).length > 0 ? { headers: responseHeaders } : void 0
2606
+ };
2607
+ }
2608
+ };
2609
+ }
2610
+
2611
+ // src/agent/model.ts
2612
+ init_config();
2451
2613
  var ANTHROPIC_PREFIX = "anthropic/";
2452
2614
  function isAnthropicModel(modelId) {
2453
2615
  const normalized = modelId.trim().toLowerCase();
2454
2616
  return normalized.startsWith(ANTHROPIC_PREFIX) || normalized.startsWith("claude-");
2455
2617
  }
2456
2618
  function resolveModel(modelId) {
2619
+ try {
2620
+ const config = getConfig();
2621
+ if (config.resolvedRemoteServer.isConfigured) {
2622
+ return createRemoteModel(modelId.trim(), {
2623
+ url: config.resolvedRemoteServer.url,
2624
+ authKey: config.resolvedRemoteServer.authKey
2625
+ });
2626
+ }
2627
+ } catch {
2628
+ }
2457
2629
  return gateway(modelId.trim());
2458
2630
  }
2459
2631
  var SUBAGENT_MODELS = {
@@ -2465,7 +2637,7 @@ var SUBAGENT_MODELS = {
2465
2637
  // src/agent/index.ts
2466
2638
  init_db();
2467
2639
  init_config();
2468
- import { z as z12 } from "zod";
2640
+ import { z as z13 } from "zod";
2469
2641
  import { nanoid as nanoid3 } from "nanoid";
2470
2642
 
2471
2643
  // src/tools/bash.ts
@@ -5775,6 +5947,59 @@ Context: ${context}` : query;
5775
5947
 
5776
5948
  // src/tools/index.ts
5777
5949
  init_semantic_search();
5950
+
5951
+ // src/tools/task.ts
5952
+ import { tool as tool11 } from "ai";
5953
+ import { z as z12 } from "zod";
5954
+ import Ajv from "ajv";
5955
+ var ajv = new Ajv({ allErrors: true });
5956
+ function createCompleteTaskTool(options) {
5957
+ const validate = ajv.compile(options.outputSchema);
5958
+ return tool11({
5959
+ 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.",
5960
+ inputSchema: z12.object({
5961
+ result: z12.record(z12.string(), z12.unknown()).describe("The task result as a JSON object matching the output schema")
5962
+ }),
5963
+ execute: async (input) => {
5964
+ const valid = validate(input.result);
5965
+ if (!valid) {
5966
+ const errors = validate.errors?.map((e) => ({
5967
+ path: e.instancePath || "/",
5968
+ message: e.message,
5969
+ params: e.params
5970
+ }));
5971
+ return {
5972
+ status: "validation_error",
5973
+ message: "The result does not match the required output schema. Fix the errors and call complete_task again.",
5974
+ errors,
5975
+ expectedSchema: options.outputSchema
5976
+ };
5977
+ }
5978
+ options.onComplete({ status: "completed", result: input.result });
5979
+ return {
5980
+ status: "completed",
5981
+ message: "Task completed successfully."
5982
+ };
5983
+ }
5984
+ });
5985
+ }
5986
+ function createTaskFailedTool(options) {
5987
+ return tool11({
5988
+ description: "Call this tool if you are unable to complete the task. Provide a clear reason explaining why the task cannot be completed.",
5989
+ inputSchema: z12.object({
5990
+ reason: z12.string().describe("Explanation of why the task cannot be completed")
5991
+ }),
5992
+ execute: async (input) => {
5993
+ options.onComplete({ status: "failed", error: input.reason });
5994
+ return {
5995
+ status: "failed",
5996
+ message: `Task marked as failed: ${input.reason}`
5997
+ };
5998
+ }
5999
+ });
6000
+ }
6001
+
6002
+ // src/tools/index.ts
5778
6003
  init_semantic();
5779
6004
  init_semantic_search();
5780
6005
  async function createTools(options) {
@@ -5826,6 +6051,10 @@ async function createTools(options) {
5826
6051
  } catch {
5827
6052
  }
5828
6053
  }
6054
+ if (options.taskTools) {
6055
+ tools.complete_task = createCompleteTaskTool(options.taskTools);
6056
+ tools.task_failed = createTaskFailedTool(options.taskTools);
6057
+ }
5829
6058
  return tools;
5830
6059
  }
5831
6060
 
@@ -6143,6 +6372,30 @@ function formatTodosForContext(todos) {
6143
6372
  }
6144
6373
  return lines.join("\n");
6145
6374
  }
6375
+ function buildTaskPromptAddendum(outputSchema) {
6376
+ return `
6377
+ ## Task Mode
6378
+
6379
+ You are running in **task mode**. You have been given a specific task to complete autonomously.
6380
+
6381
+ ### Rules
6382
+ 1. Work independently \u2014 no human will approve tool calls. All tools run without approval.
6383
+ 2. Keep working until the task is fully complete.
6384
+ 3. When done, call the \`complete_task\` tool with a JSON result matching the output schema below.
6385
+ 4. If you determine the task is impossible or encounter an unrecoverable error, call the \`task_failed\` tool with a clear reason.
6386
+ 5. Do NOT stop without calling one of these two tools.
6387
+
6388
+ ### Output Schema
6389
+ The \`complete_task\` tool expects a \`result\` object matching this JSON Schema:
6390
+ \`\`\`json
6391
+ ${JSON.stringify(outputSchema, null, 2)}
6392
+ \`\`\`
6393
+
6394
+ ### Completion Tools
6395
+ - **\`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.
6396
+ - **\`task_failed({ reason: "..." })\`** \u2014 Call only if the task truly cannot be completed.
6397
+ `;
6398
+ }
6146
6399
  function createSummaryPrompt(conversationHistory) {
6147
6400
  return `Please provide a concise summary of the following conversation history. Focus on:
6148
6401
  1. The main task or goal being worked on
@@ -6404,6 +6657,25 @@ ${this.summary}`
6404
6657
  }
6405
6658
  };
6406
6659
 
6660
+ // src/utils/webhook.ts
6661
+ async function sendWebhook(url, event) {
6662
+ try {
6663
+ const controller = new AbortController();
6664
+ const timeout = setTimeout(() => controller.abort(), 5e3);
6665
+ await fetch(url, {
6666
+ method: "POST",
6667
+ headers: {
6668
+ "Content-Type": "application/json",
6669
+ "X-SparkECoder-Event": event.type
6670
+ },
6671
+ body: JSON.stringify(event),
6672
+ signal: controller.signal
6673
+ });
6674
+ clearTimeout(timeout);
6675
+ } catch {
6676
+ }
6677
+ }
6678
+
6407
6679
  // src/agent/index.ts
6408
6680
  var approvalResolvers = /* @__PURE__ */ new Map();
6409
6681
  var Agent = class _Agent {
@@ -6623,6 +6895,145 @@ ${prompt}` });
6623
6895
  steps: result.steps
6624
6896
  };
6625
6897
  }
6898
+ /**
6899
+ * Run the agent in task mode — loops autonomously until the agent calls
6900
+ * complete_task or task_failed (or hits maxIterations).
6901
+ * All tools run without approval. Webhook events are fired throughout.
6902
+ */
6903
+ async runTask(options) {
6904
+ const config = getConfig();
6905
+ const maxIterations = options.taskConfig.maxIterations ?? 50;
6906
+ const webhookUrl = options.taskConfig.webhookUrl;
6907
+ const fireWebhook = (type, data) => {
6908
+ if (!webhookUrl) return;
6909
+ sendWebhook(webhookUrl, {
6910
+ type,
6911
+ taskId: this.session.id,
6912
+ sessionId: this.session.id,
6913
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6914
+ data
6915
+ });
6916
+ };
6917
+ const completion = { signal: null };
6918
+ const onComplete = (signal) => {
6919
+ completion.signal = signal;
6920
+ };
6921
+ const taskTools = await createTools({
6922
+ sessionId: this.session.id,
6923
+ workingDirectory: this.session.workingDirectory,
6924
+ skillsDirectories: config.resolvedSkillsDirectories,
6925
+ onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
6926
+ onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
6927
+ onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0,
6928
+ taskTools: {
6929
+ outputSchema: options.taskConfig.outputSchema,
6930
+ onComplete
6931
+ }
6932
+ });
6933
+ const baseSystemPrompt = await buildSystemPrompt({
6934
+ workingDirectory: this.session.workingDirectory,
6935
+ skillsDirectories: config.resolvedSkillsDirectories,
6936
+ sessionId: this.session.id,
6937
+ discoveredSkills: config.discoveredSkills,
6938
+ activeFiles: []
6939
+ });
6940
+ const taskAddendum = buildTaskPromptAddendum(options.taskConfig.outputSchema);
6941
+ const systemPrompt = `${baseSystemPrompt}
6942
+
6943
+ ${taskAddendum}`;
6944
+ fireWebhook("task.started", { prompt: options.prompt });
6945
+ this.context.addUserMessage(options.prompt);
6946
+ let iteration = 0;
6947
+ while (iteration < maxIterations) {
6948
+ iteration++;
6949
+ if (options.abortSignal?.aborted) {
6950
+ const cancelError = "Task was cancelled";
6951
+ fireWebhook("task.failed", { status: "failed", error: cancelError, iterations: iteration });
6952
+ return { status: "failed", error: cancelError, iterations: iteration };
6953
+ }
6954
+ const messages = await this.context.getMessages();
6955
+ const useAnthropic = isAnthropicModel(this.session.model);
6956
+ const result = await generateText3({
6957
+ model: resolveModel(this.session.model),
6958
+ system: systemPrompt,
6959
+ messages,
6960
+ tools: taskTools,
6961
+ stopWhen: stepCountIs2(500),
6962
+ abortSignal: options.abortSignal,
6963
+ providerOptions: useAnthropic ? {
6964
+ anthropic: {
6965
+ thinking: { type: "enabled", budgetTokens: 1e4 }
6966
+ }
6967
+ } : void 0,
6968
+ onStepFinish: (step) => {
6969
+ options.onStepFinish?.(step);
6970
+ fireWebhook("task.step_finished", { iteration, text: step.text });
6971
+ }
6972
+ });
6973
+ const responseMessages = result.response.messages;
6974
+ this.context.addResponseMessages(responseMessages);
6975
+ if (result.text) {
6976
+ options.onText?.(result.text);
6977
+ fireWebhook("task.message", { iteration, text: result.text });
6978
+ }
6979
+ for (const step of result.steps) {
6980
+ if (step.toolCalls) {
6981
+ for (const tc of step.toolCalls) {
6982
+ options.onToolCall?.({ toolCallId: tc.toolCallId, toolName: tc.toolName, input: tc.args });
6983
+ fireWebhook("task.tool_call", { iteration, toolName: tc.toolName, toolCallId: tc.toolCallId, input: tc.args });
6984
+ }
6985
+ }
6986
+ if (step.toolResults) {
6987
+ for (const tr of step.toolResults) {
6988
+ options.onToolResult?.({ toolCallId: tr.toolCallId, toolName: tr.toolName, output: tr.result });
6989
+ fireWebhook("task.tool_result", { iteration, toolName: tr.toolName, toolCallId: tr.toolCallId, output: tr.result });
6990
+ }
6991
+ }
6992
+ }
6993
+ if (completion.signal) {
6994
+ const sig = completion.signal;
6995
+ const finalStatus = sig.status;
6996
+ const eventType = finalStatus === "completed" ? "task.completed" : "task.failed";
6997
+ fireWebhook(eventType, {
6998
+ status: finalStatus,
6999
+ result: sig.result,
7000
+ error: sig.error,
7001
+ iterations: iteration
7002
+ });
7003
+ const updatedTask2 = {
7004
+ ...options.taskConfig,
7005
+ status: finalStatus,
7006
+ result: sig.result,
7007
+ error: sig.error,
7008
+ iterations: iteration
7009
+ };
7010
+ await sessionQueries.update(this.session.id, {
7011
+ config: { ...this.session.config, task: updatedTask2 }
7012
+ });
7013
+ return {
7014
+ status: finalStatus,
7015
+ result: sig.result,
7016
+ error: sig.error,
7017
+ iterations: iteration
7018
+ };
7019
+ }
7020
+ this.context.addUserMessage(
7021
+ "Continue working on the task. When done, call `complete_task` with the result. If you cannot complete it, call `task_failed` with a reason."
7022
+ );
7023
+ }
7024
+ const timeoutError = `Task did not complete within ${maxIterations} iterations`;
7025
+ fireWebhook("task.failed", { status: "failed", error: timeoutError, iterations: iteration });
7026
+ const updatedTask = {
7027
+ ...options.taskConfig,
7028
+ status: "failed",
7029
+ error: timeoutError,
7030
+ iterations: iteration
7031
+ };
7032
+ await sessionQueries.update(this.session.id, {
7033
+ config: { ...this.session.config, task: updatedTask }
7034
+ });
7035
+ return { status: "failed", error: timeoutError, iterations: iteration };
7036
+ }
6626
7037
  /**
6627
7038
  * Wrap tools to add approval checking
6628
7039
  */
@@ -6636,9 +7047,9 @@ ${prompt}` });
6636
7047
  wrappedTools[name] = originalTool;
6637
7048
  continue;
6638
7049
  }
6639
- wrappedTools[name] = tool11({
7050
+ wrappedTools[name] = tool12({
6640
7051
  description: originalTool.description || "",
6641
- inputSchema: originalTool.inputSchema || z12.object({}),
7052
+ inputSchema: originalTool.inputSchema || z13.object({}),
6642
7053
  execute: async (input, toolOptions) => {
6643
7054
  const toolCallId = toolOptions.toolCallId || nanoid3();
6644
7055
  const execution = toolExecutionQueries.create({
@@ -6779,18 +7190,18 @@ function cleanupPendingInputs() {
6779
7190
  }
6780
7191
  }
6781
7192
  }
6782
- var createSessionSchema = z13.object({
6783
- name: z13.string().optional(),
6784
- workingDirectory: z13.string().optional(),
6785
- model: z13.string().optional(),
6786
- toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
7193
+ var createSessionSchema = z14.object({
7194
+ name: z14.string().optional(),
7195
+ workingDirectory: z14.string().optional(),
7196
+ model: z14.string().optional(),
7197
+ toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
6787
7198
  });
6788
- var paginationQuerySchema = z13.object({
6789
- limit: z13.string().optional(),
6790
- offset: z13.string().optional()
7199
+ var paginationQuerySchema = z14.object({
7200
+ limit: z14.string().optional(),
7201
+ offset: z14.string().optional()
6791
7202
  });
6792
- var messagesQuerySchema = z13.object({
6793
- limit: z13.string().optional()
7203
+ var messagesQuerySchema = z14.object({
7204
+ limit: z14.string().optional()
6794
7205
  });
6795
7206
  sessions.get(
6796
7207
  "/",
@@ -6929,10 +7340,10 @@ sessions.get("/:id/tools", async (c) => {
6929
7340
  count: executions.length
6930
7341
  });
6931
7342
  });
6932
- var updateSessionSchema = z13.object({
6933
- model: z13.string().optional(),
6934
- name: z13.string().optional(),
6935
- toolApprovals: z13.record(z13.string(), z13.boolean()).optional()
7343
+ var updateSessionSchema = z14.object({
7344
+ model: z14.string().optional(),
7345
+ name: z14.string().optional(),
7346
+ toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
6936
7347
  });
6937
7348
  sessions.patch(
6938
7349
  "/:id",
@@ -7002,8 +7413,8 @@ sessions.post("/:id/clear", async (c) => {
7002
7413
  await agent.clearContext();
7003
7414
  return c.json({ success: true, sessionId: id });
7004
7415
  });
7005
- var pendingInputSchema = z13.object({
7006
- text: z13.string()
7416
+ var pendingInputSchema = z14.object({
7417
+ text: z14.string()
7007
7418
  });
7008
7419
  sessions.post(
7009
7420
  "/:id/pending-input",
@@ -7034,13 +7445,13 @@ sessions.get("/:id/pending-input", async (c) => {
7034
7445
  createdAt: pending.createdAt.toISOString()
7035
7446
  });
7036
7447
  });
7037
- var devtoolsContextSchema = z13.object({
7038
- url: z13.string(),
7039
- path: z13.string(),
7040
- pageName: z13.string().optional(),
7041
- screenWidth: z13.number().optional(),
7042
- screenHeight: z13.number().optional(),
7043
- devicePixelRatio: z13.number().optional()
7448
+ var devtoolsContextSchema = z14.object({
7449
+ url: z14.string(),
7450
+ path: z14.string(),
7451
+ pageName: z14.string().optional(),
7452
+ screenWidth: z14.number().optional(),
7453
+ screenHeight: z14.number().optional(),
7454
+ devicePixelRatio: z14.number().optional()
7044
7455
  });
7045
7456
  sessions.post(
7046
7457
  "/:id/devtools-context",
@@ -7331,10 +7742,10 @@ sessions.delete("/:id/attachments/:attachmentId", async (c) => {
7331
7742
  unlinkSync(filePath);
7332
7743
  return c.json({ success: true, id: attachmentId });
7333
7744
  });
7334
- var filesQuerySchema = z13.object({
7335
- query: z13.string().optional(),
7745
+ var filesQuerySchema = z14.object({
7746
+ query: z14.string().optional(),
7336
7747
  // Filter query (e.g., "src/com" to match "src/components")
7337
- limit: z13.string().optional()
7748
+ limit: z14.string().optional()
7338
7749
  // Max results (default 50)
7339
7750
  });
7340
7751
  var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
@@ -7512,7 +7923,7 @@ sessions.get(
7512
7923
  init_db();
7513
7924
  import { Hono as Hono2 } from "hono";
7514
7925
  import { zValidator as zValidator2 } from "@hono/zod-validator";
7515
- import { z as z14 } from "zod";
7926
+ import { z as z15 } from "zod";
7516
7927
  import { existsSync as existsSync14, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
7517
7928
  import { join as join6 } from "path";
7518
7929
  init_config();
@@ -7677,30 +8088,30 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
7677
8088
  ${prompt}`;
7678
8089
  }
7679
8090
  var agents = new Hono2();
7680
- var attachmentSchema = z14.object({
7681
- type: z14.enum(["image", "file"]),
7682
- data: z14.string(),
8091
+ var attachmentSchema = z15.object({
8092
+ type: z15.enum(["image", "file"]),
8093
+ data: z15.string(),
7683
8094
  // base64 data URL or raw base64
7684
- mediaType: z14.string().optional(),
7685
- filename: z14.string().optional()
8095
+ mediaType: z15.string().optional(),
8096
+ filename: z15.string().optional()
7686
8097
  });
7687
- var runPromptSchema = z14.object({
7688
- prompt: z14.string(),
8098
+ var runPromptSchema = z15.object({
8099
+ prompt: z15.string(),
7689
8100
  // Can be empty if attachments are provided
7690
- attachments: z14.array(attachmentSchema).optional()
8101
+ attachments: z15.array(attachmentSchema).optional()
7691
8102
  }).refine(
7692
8103
  (data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
7693
8104
  { message: "Either prompt or attachments must be provided" }
7694
8105
  );
7695
- var quickStartSchema = z14.object({
7696
- prompt: z14.string().min(1),
7697
- name: z14.string().optional(),
7698
- workingDirectory: z14.string().optional(),
7699
- model: z14.string().optional(),
7700
- toolApprovals: z14.record(z14.string(), z14.boolean()).optional()
8106
+ var quickStartSchema = z15.object({
8107
+ prompt: z15.string().min(1),
8108
+ name: z15.string().optional(),
8109
+ workingDirectory: z15.string().optional(),
8110
+ model: z15.string().optional(),
8111
+ toolApprovals: z15.record(z15.string(), z15.boolean()).optional()
7701
8112
  });
7702
- var rejectSchema = z14.object({
7703
- reason: z14.string().optional()
8113
+ var rejectSchema = z15.object({
8114
+ reason: z15.string().optional()
7704
8115
  }).optional();
7705
8116
  var streamAbortControllers = /* @__PURE__ */ new Map();
7706
8117
  function getAttachmentsDirectory(sessionId) {
@@ -8130,7 +8541,7 @@ agents.get("/:id/watch", async (c) => {
8130
8541
  "Cache-Control": "no-cache",
8131
8542
  "Connection": "keep-alive",
8132
8543
  "x-vercel-ai-ui-message-stream": "v1",
8133
- "x-stream-id": streamId
8544
+ "x-stream-id": streamId ?? ""
8134
8545
  }
8135
8546
  });
8136
8547
  });
@@ -8488,7 +8899,7 @@ agents.post(
8488
8899
  init_config();
8489
8900
  import { Hono as Hono3 } from "hono";
8490
8901
  import { zValidator as zValidator3 } from "@hono/zod-validator";
8491
- import { z as z15 } from "zod";
8902
+ import { z as z16 } from "zod";
8492
8903
  import { readFileSync as readFileSync5 } from "fs";
8493
8904
  import { fileURLToPath as fileURLToPath3 } from "url";
8494
8905
  import { dirname as dirname6, join as join7 } from "path";
@@ -8598,9 +9009,9 @@ health.get("/api-keys", async (c) => {
8598
9009
  supportedProviders: SUPPORTED_PROVIDERS
8599
9010
  });
8600
9011
  });
8601
- var setApiKeySchema = z15.object({
8602
- provider: z15.string(),
8603
- apiKey: z15.string().min(1)
9012
+ var setApiKeySchema = z16.object({
9013
+ provider: z16.string(),
9014
+ apiKey: z16.string().min(1)
8604
9015
  });
8605
9016
  health.post(
8606
9017
  "/api-keys",
@@ -8639,13 +9050,13 @@ health.delete("/api-keys/:provider", async (c) => {
8639
9050
  // src/server/routes/terminals.ts
8640
9051
  import { Hono as Hono4 } from "hono";
8641
9052
  import { zValidator as zValidator4 } from "@hono/zod-validator";
8642
- import { z as z16 } from "zod";
9053
+ import { z as z17 } from "zod";
8643
9054
  init_db();
8644
9055
  var terminals = new Hono4();
8645
- var spawnSchema = z16.object({
8646
- command: z16.string(),
8647
- cwd: z16.string().optional(),
8648
- name: z16.string().optional()
9056
+ var spawnSchema = z17.object({
9057
+ command: z17.string(),
9058
+ cwd: z17.string().optional(),
9059
+ name: z17.string().optional()
8649
9060
  });
8650
9061
  terminals.post(
8651
9062
  "/:sessionId/terminals",
@@ -8726,8 +9137,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
8726
9137
  // We don't track exit codes in tmux mode
8727
9138
  });
8728
9139
  });
8729
- var logsQuerySchema = z16.object({
8730
- tail: z16.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
9140
+ var logsQuerySchema = z17.object({
9141
+ tail: z17.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
8731
9142
  });
8732
9143
  terminals.get(
8733
9144
  "/:sessionId/terminals/:terminalId/logs",
@@ -8751,8 +9162,8 @@ terminals.get(
8751
9162
  });
8752
9163
  }
8753
9164
  );
8754
- var killSchema = z16.object({
8755
- signal: z16.enum(["SIGTERM", "SIGKILL"]).optional()
9165
+ var killSchema = z17.object({
9166
+ signal: z17.enum(["SIGTERM", "SIGKILL"]).optional()
8756
9167
  });
8757
9168
  terminals.post(
8758
9169
  "/:sessionId/terminals/:terminalId/kill",
@@ -8766,8 +9177,8 @@ terminals.post(
8766
9177
  return c.json({ success: true, message: "Terminal killed" });
8767
9178
  }
8768
9179
  );
8769
- var writeSchema = z16.object({
8770
- input: z16.string()
9180
+ var writeSchema = z17.object({
9181
+ input: z17.string()
8771
9182
  });
8772
9183
  terminals.post(
8773
9184
  "/:sessionId/terminals/:terminalId/write",
@@ -8948,6 +9359,131 @@ data: ${JSON.stringify({ status: "stopped" })}
8948
9359
  );
8949
9360
  });
8950
9361
 
9362
+ // src/server/routes/tasks.ts
9363
+ init_db();
9364
+ import { Hono as Hono5 } from "hono";
9365
+ import { zValidator as zValidator5 } from "@hono/zod-validator";
9366
+ import { z as z18 } from "zod";
9367
+ init_config();
9368
+ var tasks = new Hono5();
9369
+ var taskAbortControllers = /* @__PURE__ */ new Map();
9370
+ var createTaskSchema = z18.object({
9371
+ prompt: z18.string().min(1),
9372
+ outputSchema: z18.record(z18.string(), z18.unknown()),
9373
+ webhookUrl: z18.string().url().optional(),
9374
+ model: z18.string().optional(),
9375
+ workingDirectory: z18.string().optional(),
9376
+ name: z18.string().optional(),
9377
+ maxIterations: z18.number().int().min(1).max(500).optional()
9378
+ });
9379
+ tasks.post(
9380
+ "/",
9381
+ zValidator5("json", createTaskSchema),
9382
+ async (c) => {
9383
+ const body = c.req.valid("json");
9384
+ const config = getConfig();
9385
+ const taskConfig = {
9386
+ enabled: true,
9387
+ outputSchema: body.outputSchema,
9388
+ webhookUrl: body.webhookUrl,
9389
+ maxIterations: body.maxIterations ?? 50,
9390
+ status: "running"
9391
+ };
9392
+ const agent = await Agent.create({
9393
+ name: body.name || "Task",
9394
+ workingDirectory: body.workingDirectory || config.resolvedWorkingDirectory,
9395
+ model: body.model || config.defaultModel,
9396
+ sessionConfig: {
9397
+ toolApprovals: { bash: false, write_file: false, read_file: false },
9398
+ task: taskConfig
9399
+ }
9400
+ });
9401
+ const taskId = agent.sessionId;
9402
+ const abortController = new AbortController();
9403
+ taskAbortControllers.set(taskId, abortController);
9404
+ (async () => {
9405
+ try {
9406
+ await agent.runTask({
9407
+ prompt: body.prompt,
9408
+ taskConfig,
9409
+ abortSignal: abortController.signal
9410
+ });
9411
+ } catch (err) {
9412
+ if (err.name === "AbortError" || abortController.signal.aborted) {
9413
+ console.log(`[TASK] Task ${taskId} was cancelled`);
9414
+ } else {
9415
+ console.error(`[TASK] Error in task ${taskId}:`, err.message);
9416
+ const failedTask = {
9417
+ ...taskConfig,
9418
+ status: "failed",
9419
+ error: err.message || "Unknown error"
9420
+ };
9421
+ await sessionQueries.update(taskId, {
9422
+ config: {
9423
+ toolApprovals: { bash: false, write_file: false, read_file: false },
9424
+ task: failedTask
9425
+ }
9426
+ });
9427
+ }
9428
+ } finally {
9429
+ taskAbortControllers.delete(taskId);
9430
+ }
9431
+ })();
9432
+ return c.json({ taskId, status: "running" }, 201);
9433
+ }
9434
+ );
9435
+ tasks.get("/:id", async (c) => {
9436
+ const id = c.req.param("id");
9437
+ const session = await sessionQueries.getById(id);
9438
+ if (!session) {
9439
+ return c.json({ error: "Task not found" }, 404);
9440
+ }
9441
+ const task = session.config?.task;
9442
+ if (!task?.enabled) {
9443
+ return c.json({ error: "Session is not a task" }, 400);
9444
+ }
9445
+ return c.json({
9446
+ taskId: id,
9447
+ status: task.status,
9448
+ result: task.result,
9449
+ error: task.error,
9450
+ iterations: task.iterations,
9451
+ model: session.model,
9452
+ name: session.name,
9453
+ createdAt: session.createdAt.toISOString(),
9454
+ updatedAt: session.updatedAt.toISOString()
9455
+ });
9456
+ });
9457
+ tasks.post("/:id/cancel", async (c) => {
9458
+ const id = c.req.param("id");
9459
+ const session = await sessionQueries.getById(id);
9460
+ if (!session) {
9461
+ return c.json({ error: "Task not found" }, 404);
9462
+ }
9463
+ const task = session.config?.task;
9464
+ if (!task?.enabled) {
9465
+ return c.json({ error: "Session is not a task" }, 400);
9466
+ }
9467
+ if (task.status !== "running") {
9468
+ return c.json({ error: `Task is already ${task.status}` }, 400);
9469
+ }
9470
+ const abortController = taskAbortControllers.get(id);
9471
+ if (abortController) {
9472
+ abortController.abort();
9473
+ taskAbortControllers.delete(id);
9474
+ }
9475
+ const cancelledTask = {
9476
+ ...task,
9477
+ status: "failed",
9478
+ error: "Task cancelled by user"
9479
+ };
9480
+ await sessionQueries.update(id, {
9481
+ config: { ...session.config, task: cancelledTask }
9482
+ });
9483
+ return c.json({ taskId: id, status: "failed", error: "Task cancelled by user" });
9484
+ });
9485
+ var tasks_default = tasks;
9486
+
8951
9487
  // src/server/index.ts
8952
9488
  init_config();
8953
9489
  init_db();
@@ -9028,6 +9564,43 @@ async function checkDependencies(options = {}) {
9028
9564
  }
9029
9565
  return true;
9030
9566
  }
9567
+ async function checkAgentBrowser() {
9568
+ try {
9569
+ const { stdout } = await execAsync5("agent-browser --version", { timeout: 1e4 });
9570
+ const version = stdout.trim();
9571
+ return { available: true, version };
9572
+ } catch {
9573
+ return {
9574
+ available: false,
9575
+ error: "agent-browser is not installed globally",
9576
+ installInstructions: "Install agent-browser globally:\n npm install -g agent-browser\n agent-browser install"
9577
+ };
9578
+ }
9579
+ }
9580
+ async function tryInstallAgentBrowser(options = {}) {
9581
+ try {
9582
+ if (!options.quiet) {
9583
+ console.log("\u{1F4E6} Installing agent-browser globally...");
9584
+ }
9585
+ await execAsync5("npm install -g agent-browser", { timeout: 12e4 });
9586
+ try {
9587
+ if (!options.quiet) {
9588
+ console.log("\u{1F4E6} Installing Chromium for browser automation...");
9589
+ }
9590
+ await execAsync5("agent-browser install", { timeout: 12e4 });
9591
+ } catch {
9592
+ }
9593
+ if (!options.quiet) {
9594
+ console.log("\u2705 agent-browser installed successfully");
9595
+ }
9596
+ return true;
9597
+ } catch (error) {
9598
+ if (!options.quiet) {
9599
+ console.error(`Failed to install agent-browser: ${error.message}`);
9600
+ }
9601
+ return false;
9602
+ }
9603
+ }
9031
9604
  async function tryAutoInstallTmux() {
9032
9605
  const os2 = platform2();
9033
9606
  try {
@@ -9072,16 +9645,20 @@ async function tryAutoInstallTmux() {
9072
9645
  async function ensureDependencies(options = {}) {
9073
9646
  const { autoInstall = false, quiet = false } = options;
9074
9647
  const tmuxCheck = await checkTmux();
9075
- if (tmuxCheck.available) {
9076
- return;
9077
- }
9078
- if (autoInstall) {
9079
- const installed = await tryAutoInstallTmux();
9080
- if (installed) {
9081
- return;
9648
+ if (!tmuxCheck.available) {
9649
+ if (autoInstall) {
9650
+ const installed = await tryAutoInstallTmux();
9651
+ if (!installed) {
9652
+ await checkDependencies({ quiet, exitOnFailure: true });
9653
+ }
9654
+ } else {
9655
+ await checkDependencies({ quiet, exitOnFailure: true });
9082
9656
  }
9083
9657
  }
9084
- await checkDependencies({ quiet, exitOnFailure: true });
9658
+ const browserCheck = await checkAgentBrowser();
9659
+ if (!browserCheck.available) {
9660
+ await tryInstallAgentBrowser({ quiet });
9661
+ }
9085
9662
  }
9086
9663
 
9087
9664
  // src/server/index.ts
@@ -9339,7 +9916,7 @@ function stopWebUI() {
9339
9916
  }
9340
9917
  }
9341
9918
  async function createApp(options = {}) {
9342
- const app = new Hono5();
9919
+ const app = new Hono6();
9343
9920
  app.use("*", cors({
9344
9921
  origin: "*",
9345
9922
  // Allow all origins
@@ -9357,6 +9934,7 @@ async function createApp(options = {}) {
9357
9934
  app.route("/agents", agents);
9358
9935
  app.route("/sessions", terminals);
9359
9936
  app.route("/terminals", terminals);
9937
+ app.route("/tasks", tasks_default);
9360
9938
  app.get("/openapi.json", async (c) => {
9361
9939
  return c.json(generateOpenAPISpec());
9362
9940
  });
@@ -9926,7 +10504,7 @@ function generateOpenAPISpec() {
9926
10504
  init_config();
9927
10505
  init_semantic();
9928
10506
  init_db();
9929
- import { writeFileSync as writeFileSync5, existsSync as existsSync16 } from "fs";
10507
+ import { writeFileSync as writeFileSync5, readFileSync as readFileSync6, existsSync as existsSync16 } from "fs";
9930
10508
  import { resolve as resolve11, join as join9 } from "path";
9931
10509
  async function apiRequest(baseUrl, path, options = {}) {
9932
10510
  const url = `${baseUrl}${path}`;
@@ -9966,9 +10544,15 @@ function promptApproval(rl, toolName, input) {
9966
10544
  const inputStr = JSON.stringify(input);
9967
10545
  const truncatedInput = inputStr.length > 100 ? inputStr.slice(0, 100) + "..." : inputStr;
9968
10546
  console.log(chalk.dim(` Command: ${truncatedInput}`));
9969
- rl.question(chalk.yellow(` Approve? [y/n]: `), (answer) => {
9970
- const approved = answer.toLowerCase().startsWith("y");
9971
- resolve12(approved);
10547
+ rl.question(chalk.yellow(` Approve? [y/n/a(lways)]: `), (answer) => {
10548
+ const lower = answer.toLowerCase().trim();
10549
+ if (lower === "a" || lower === "always") {
10550
+ resolve12("always");
10551
+ } else if (lower.startsWith("y")) {
10552
+ resolve12("approve");
10553
+ } else {
10554
+ resolve12("reject");
10555
+ }
9972
10556
  });
9973
10557
  });
9974
10558
  }
@@ -10030,14 +10614,27 @@ async function consumeSSEStream(response, options = {}) {
10030
10614
  }
10031
10615
  if (event.type === "data-approval-required") {
10032
10616
  const approval = event.data;
10033
- console.log(chalk.yellow(`
10617
+ if (options.skipApprovals && options.baseUrl && options.sessionId) {
10618
+ console.log(chalk.dim(` \u2713 Auto-approved: ${approval.toolName}`));
10619
+ await apiRequest(options.baseUrl, `/agents/${options.sessionId}/approve/${approval.toolCallId}`, { method: "POST" });
10620
+ } else if (options.interactive && options.baseUrl && options.sessionId && options.readline) {
10621
+ console.log(chalk.yellow(`
10034
10622
  \u26A0\uFE0F Approval required for: ${approval.toolName}`));
10035
- if (options.interactive && options.baseUrl && options.sessionId && options.readline) {
10036
- const approved = await promptApproval(options.readline, approval.toolName, approval.input);
10037
- const endpoint = approved ? "approve" : "reject";
10623
+ const result = await promptApproval(options.readline, approval.toolName, approval.input);
10624
+ const endpoint = result === "reject" ? "reject" : "approve";
10038
10625
  const apiResponse = await apiRequest(options.baseUrl, `/agents/${options.sessionId}/${endpoint}/${approval.toolCallId}`, { method: "POST" });
10039
10626
  if (apiResponse.ok) {
10040
- console.log(approved ? chalk.green(" \u2713 Approved") : chalk.red(" \u2717 Rejected"));
10627
+ if (result === "always") {
10628
+ await apiRequest(options.baseUrl, `/sessions/${options.sessionId}`, {
10629
+ method: "PATCH",
10630
+ body: { toolApprovals: { [approval.toolName]: false } }
10631
+ });
10632
+ console.log(chalk.green(` \u2713 Always allowed \u2014 ${approval.toolName} won't ask again this session`));
10633
+ } else if (result === "approve") {
10634
+ console.log(chalk.green(" \u2713 Approved"));
10635
+ } else {
10636
+ console.log(chalk.red(" \u2717 Rejected"));
10637
+ }
10041
10638
  } else {
10042
10639
  console.log(chalk.red(` Failed to ${endpoint}: ${apiResponse.statusText}`));
10043
10640
  }
@@ -10176,6 +10773,7 @@ async function runChat(options) {
10176
10773
  output: process.stdout
10177
10774
  });
10178
10775
  let sessionId;
10776
+ const skipApprovals = !!options.dangerouslySkipApprovals;
10179
10777
  if (options.session) {
10180
10778
  const response = await apiRequest(baseUrl, `/sessions/${options.session}`);
10181
10779
  if (!response.ok) {
@@ -10185,6 +10783,12 @@ async function runChat(options) {
10185
10783
  const session = await response.json();
10186
10784
  sessionId = session.id;
10187
10785
  console.log(chalk.dim(`Resuming session: ${session.name || sessionId}`));
10786
+ if (skipApprovals) {
10787
+ await apiRequest(baseUrl, `/sessions/${sessionId}`, {
10788
+ method: "PATCH",
10789
+ body: { toolApprovals: { "*": false } }
10790
+ });
10791
+ }
10188
10792
  const streamInfo = await getActiveStream(baseUrl, sessionId);
10189
10793
  if (streamInfo.hasActiveStream && streamInfo.streamId) {
10190
10794
  console.log(chalk.cyan(`
@@ -10195,7 +10799,7 @@ async function runChat(options) {
10195
10799
  try {
10196
10800
  const watchResponse = await fetch(`${baseUrl}/agents/${sessionId}/watch?streamId=${streamInfo.streamId}`);
10197
10801
  if (watchResponse.ok) {
10198
- await consumeSSEStream(watchResponse, { interactive: true, baseUrl, sessionId, readline: rl });
10802
+ await consumeSSEStream(watchResponse, { interactive: true, skipApprovals, baseUrl, sessionId, readline: rl });
10199
10803
  }
10200
10804
  } catch (err) {
10201
10805
  console.log(chalk.dim("Stream ended or connection lost."));
@@ -10204,13 +10808,17 @@ async function runChat(options) {
10204
10808
  }
10205
10809
  } else {
10206
10810
  const config = loadConfig(options.config, options.workingDir);
10811
+ const sessionBody = {
10812
+ name: options.name || "CLI Chat",
10813
+ workingDirectory: options.workingDir || config.resolvedWorkingDirectory,
10814
+ model: options.model || config.defaultModel
10815
+ };
10816
+ if (skipApprovals) {
10817
+ sessionBody.toolApprovals = { "*": false };
10818
+ }
10207
10819
  const response = await apiRequest(baseUrl, "/sessions", {
10208
10820
  method: "POST",
10209
- body: {
10210
- name: options.name || "CLI Chat",
10211
- workingDirectory: options.workingDir || config.resolvedWorkingDirectory,
10212
- model: options.model || config.defaultModel
10213
- }
10821
+ body: sessionBody
10214
10822
  });
10215
10823
  if (!response.ok) {
10216
10824
  const error = await response.json();
@@ -10226,6 +10834,9 @@ async function runChat(options) {
10226
10834
  console.log("");
10227
10835
  console.log(chalk.bold.cyan("\u{1F436} SparkECoder"));
10228
10836
  console.log(chalk.dim(`Working directory: ${workingDir}`));
10837
+ if (skipApprovals) {
10838
+ console.log(chalk.yellow("\u26A1 All tool approvals disabled (--dangerously-skip-approvals)"));
10839
+ }
10229
10840
  console.log(chalk.dim("Commands: /quit, /clear, /session, /tools, /help"));
10230
10841
  console.log("");
10231
10842
  const prompt = () => {
@@ -10254,6 +10865,7 @@ async function runChat(options) {
10254
10865
  console.log(chalk.dim(" /approve <id> - Approve pending tool call"));
10255
10866
  console.log(chalk.dim(" /reject <id> - Reject pending tool call"));
10256
10867
  console.log(chalk.dim(" /approvals - List pending approvals"));
10868
+ console.log(chalk.dim(" /allow <tool> - Always allow a tool (skip approval)"));
10257
10869
  console.log(chalk.dim(" /help, /? - Show this help"));
10258
10870
  prompt();
10259
10871
  return;
@@ -10265,7 +10877,7 @@ async function runChat(options) {
10265
10877
  try {
10266
10878
  const watchResponse = await fetch(`${baseUrl}/agents/${sessionId}/watch?streamId=${streamInfo.streamId}`);
10267
10879
  if (watchResponse.ok) {
10268
- await consumeSSEStream(watchResponse, { interactive: true, baseUrl, sessionId, readline: rl });
10880
+ await consumeSSEStream(watchResponse, { interactive: true, skipApprovals, baseUrl, sessionId, readline: rl });
10269
10881
  } else {
10270
10882
  console.log(chalk.dim("Failed to connect to stream."));
10271
10883
  }
@@ -10331,6 +10943,26 @@ async function runChat(options) {
10331
10943
  prompt();
10332
10944
  return;
10333
10945
  }
10946
+ if (cmd.startsWith("/allow ")) {
10947
+ const toolName = trimmed.slice(7).trim();
10948
+ if (!toolName) {
10949
+ console.log(chalk.yellow("Usage: /allow <toolName> (e.g., /allow bash)"));
10950
+ prompt();
10951
+ return;
10952
+ }
10953
+ const response = await apiRequest(baseUrl, `/sessions/${sessionId}`, {
10954
+ method: "PATCH",
10955
+ body: { toolApprovals: { [toolName]: false } }
10956
+ });
10957
+ if (response.ok) {
10958
+ console.log(chalk.green(`\u2713 ${toolName} will no longer require approval this session`));
10959
+ } else {
10960
+ const error = await response.json();
10961
+ console.log(chalk.red(`Failed: ${error.error || "Unknown error"}`));
10962
+ }
10963
+ prompt();
10964
+ return;
10965
+ }
10334
10966
  if (cmd === "/clear") {
10335
10967
  const response = await apiRequest(baseUrl, `/sessions/${sessionId}/clear`, { method: "POST" });
10336
10968
  if (response.ok) {
@@ -10407,7 +11039,7 @@ async function runChat(options) {
10407
11039
  const error = await response.json();
10408
11040
  throw new Error(error.error || `HTTP ${response.status}`);
10409
11041
  }
10410
- await consumeSSEStream(response, { interactive: true, baseUrl, sessionId, readline: rl });
11042
+ await consumeSSEStream(response, { interactive: true, skipApprovals, baseUrl, sessionId, readline: rl });
10411
11043
  } catch (error) {
10412
11044
  process.stdout.write("\r" + " ".repeat(20) + "\r");
10413
11045
  console.log(chalk.red(`
@@ -10436,7 +11068,7 @@ Unexpected error: ${outerError.message}`));
10436
11068
  }
10437
11069
  }
10438
11070
  var program = new Command();
10439
- program.name("sparkecoder").description("AI coding agent - just type sparkecoder to start chatting").version("0.1.0").option("-s, --session <id>", "Resume an existing session").option("-n, --name <name>", "Name for the new session").option("-m, --model <model>", "Model to use").option("-w, --working-dir <path>", "Working directory").option("-c, --config <path>", "Path to config file").option("-p, --port <port>", "Server port", "3141").option("-H, --host <host>", "Server host", "127.0.0.1").option("--no-auto-start", "Do not auto-start server if not running").option("--web-port <port>", "Web UI port", "6969").option("--no-web", "Do not start web UI when auto-starting server").option("--public-url <url>", "Public URL for web UI to connect to API (for Docker/remote access)").option("-v, --verbose", "Enable verbose logging for web server").action(async (options) => {
11071
+ program.name("sparkecoder").description("AI coding agent - just type sparkecoder to start chatting").version("0.1.0").option("-s, --session <id>", "Resume an existing session").option("-n, --name <name>", "Name for the new session").option("-m, --model <model>", "Model to use").option("-w, --working-dir <path>", "Working directory").option("-c, --config <path>", "Path to config file").option("-p, --port <port>", "Server port", "3141").option("-H, --host <host>", "Server host", "127.0.0.1").option("--no-auto-start", "Do not auto-start server if not running").option("--web-port <port>", "Web UI port", "6969").option("--no-web", "Do not start web UI when auto-starting server").option("--public-url <url>", "Public URL for web UI to connect to API (for Docker/remote access)").option("-v, --verbose", "Enable verbose logging for web server").option("--dangerously-skip-approvals", "Auto-approve all tool calls (no confirmation prompts)").action(async (options) => {
10440
11072
  await runChat(options);
10441
11073
  });
10442
11074
  program.command("server").description("Start the SparkECoder server (API + Web UI)").option("-p, --port <port>", "API server port", "3141").option("-h, --host <host>", "Server host", "127.0.0.1").option("-c, --config <path>", "Path to config file").option("-w, --working-dir <path>", "Working directory").option("--web-port <port>", "Web UI port", "6969").option("--no-web", "Do not start web UI").option("--public-url <url>", "Public URL for web UI to connect to API (for Docker/remote access)").option("-v, --verbose", "Enable verbose logging for web server").action(async (options) => {
@@ -10476,9 +11108,100 @@ program.command("server").description("Start the SparkECoder server (API + Web U
10476
11108
  process.exit(1);
10477
11109
  }
10478
11110
  });
10479
- program.command("chat").description("Start an interactive chat session").option("-s, --session <id>", "Resume an existing session").option("-n, --name <name>", "Name for the new session").option("-m, --model <model>", "Model to use").option("-w, --working-dir <path>", "Working directory").option("-c, --config <path>", "Path to config file").option("-p, --port <port>", "Server port", "3141").option("-H, --host <host>", "Server host", "127.0.0.1").option("--no-auto-start", "Do not auto-start server if not running").option("--web-port <port>", "Web UI port", "6969").option("--no-web", "Do not start web UI when auto-starting server").option("--public-url <url>", "Public URL for web UI to connect to API (for Docker/remote access)").option("-v, --verbose", "Enable verbose logging for web server").action(async (options) => {
11111
+ program.command("chat").description("Start an interactive chat session").option("-s, --session <id>", "Resume an existing session").option("-n, --name <name>", "Name for the new session").option("-m, --model <model>", "Model to use").option("-w, --working-dir <path>", "Working directory").option("-c, --config <path>", "Path to config file").option("-p, --port <port>", "Server port", "3141").option("-H, --host <host>", "Server host", "127.0.0.1").option("--no-auto-start", "Do not auto-start server if not running").option("--web-port <port>", "Web UI port", "6969").option("--no-web", "Do not start web UI when auto-starting server").option("--public-url <url>", "Public URL for web UI to connect to API (for Docker/remote access)").option("-v, --verbose", "Enable verbose logging for web server").option("--dangerously-skip-approvals", "Auto-approve all tool calls (no confirmation prompts)").action(async (options) => {
10480
11112
  await runChat(options);
10481
11113
  });
11114
+ program.command("task").description("Run an autonomous task that completes without human interaction").requiredOption("--prompt <prompt>", "Task prompt describing what to do").requiredOption("--schema <schema>", "JSON Schema for the output (file path or inline JSON string)").option("--webhook <url>", "Webhook URL to receive task events").option("-m, --model <model>", "Model to use").option("-w, --working-dir <path>", "Working directory").option("-n, --name <name>", "Name for the task").option("--max-iterations <n>", "Maximum agent loop iterations", "50").option("-p, --port <port>", "Server port", "3141").option("-H, --host <host>", "Server host", "127.0.0.1").option("-c, --config <path>", "Path to config file").option("--no-auto-start", "Do not auto-start server if not running").option("--wait", "Block and poll until task completes").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
11115
+ await ensureDependencies({ quiet: true });
11116
+ loadApiKeysIntoEnv();
11117
+ const baseUrl = `http://${options.host}:${options.port}`;
11118
+ const running = await isServerRunning(baseUrl);
11119
+ if (!running) {
11120
+ if (options.autoStart === false) {
11121
+ console.error(chalk.red(`Server not running at ${baseUrl}`));
11122
+ process.exit(1);
11123
+ }
11124
+ const spinner2 = ora("Starting server...").start();
11125
+ try {
11126
+ await startServer({
11127
+ port: parseInt(options.port),
11128
+ host: options.host,
11129
+ configPath: options.config,
11130
+ workingDirectory: options.workingDir,
11131
+ quiet: true,
11132
+ webUI: false
11133
+ });
11134
+ spinner2.succeed("Server started");
11135
+ } catch (err) {
11136
+ spinner2.fail(chalk.red(`Failed to start server: ${err.message}`));
11137
+ process.exit(1);
11138
+ }
11139
+ }
11140
+ let outputSchema;
11141
+ try {
11142
+ const schemaStr = options.schema;
11143
+ if (existsSync16(schemaStr)) {
11144
+ outputSchema = JSON.parse(readFileSync6(schemaStr, "utf-8"));
11145
+ } else {
11146
+ outputSchema = JSON.parse(schemaStr);
11147
+ }
11148
+ } catch (err) {
11149
+ console.error(chalk.red(`Invalid schema: ${err.message}`));
11150
+ console.error(chalk.dim("Provide a path to a JSON Schema file or an inline JSON string"));
11151
+ process.exit(1);
11152
+ }
11153
+ const body = {
11154
+ prompt: options.prompt,
11155
+ outputSchema,
11156
+ maxIterations: parseInt(options.maxIterations)
11157
+ };
11158
+ if (options.webhook) body.webhookUrl = options.webhook;
11159
+ if (options.model) body.model = options.model;
11160
+ if (options.workingDir) body.workingDirectory = options.workingDir;
11161
+ if (options.name) body.name = options.name;
11162
+ const spinner = ora("Creating task...").start();
11163
+ const response = await apiRequest(baseUrl, "/tasks", {
11164
+ method: "POST",
11165
+ body
11166
+ });
11167
+ if (!response.ok) {
11168
+ const err = await response.json();
11169
+ spinner.fail(chalk.red(`Failed to create task: ${err.error || "Unknown error"}`));
11170
+ process.exit(1);
11171
+ }
11172
+ const { taskId } = await response.json();
11173
+ spinner.succeed(`Task created: ${chalk.cyan(taskId)}`);
11174
+ if (!options.wait) {
11175
+ console.log("");
11176
+ console.log(chalk.dim("Task is running in the background."));
11177
+ console.log(chalk.dim(`Check status: sparkecoder task-status ${taskId} --port ${options.port}`));
11178
+ console.log(chalk.dim(`Cancel: curl -X POST ${baseUrl}/tasks/${taskId}/cancel`));
11179
+ process.exit(0);
11180
+ }
11181
+ console.log(chalk.dim("\nWaiting for task to complete..."));
11182
+ const pollInterval = 3e3;
11183
+ while (true) {
11184
+ await new Promise((r) => setTimeout(r, pollInterval));
11185
+ const statusRes = await apiRequest(baseUrl, `/tasks/${taskId}`);
11186
+ if (!statusRes.ok) {
11187
+ console.error(chalk.red("Failed to check task status"));
11188
+ process.exit(1);
11189
+ }
11190
+ const status = await statusRes.json();
11191
+ if (status.status === "completed") {
11192
+ console.log(chalk.green(`
11193
+ Task completed in ${status.iterations} iteration(s).`));
11194
+ console.log(chalk.bold("Result:"));
11195
+ console.log(JSON.stringify(status.result, null, 2));
11196
+ process.exit(0);
11197
+ } else if (status.status === "failed") {
11198
+ console.error(chalk.red(`
11199
+ Task failed: ${status.error}`));
11200
+ process.exit(1);
11201
+ }
11202
+ process.stdout.write(chalk.dim("."));
11203
+ }
11204
+ });
10482
11205
  program.command("init").description("Create a sparkecoder.config.json file").option("-f, --force", "Overwrite existing config").option("-g, --global", "Create global config in app data directory").action((options) => {
10483
11206
  let configPath;
10484
11207
  let configLocation;