sparkecoder 0.1.67 → 0.1.68

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 (87) hide show
  1. package/dist/agent/index.d.ts +1 -1
  2. package/dist/agent/index.js +135 -13
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +200 -48
  5. package/dist/cli.js.map +1 -1
  6. package/dist/{index-DHyVVhJY.d.ts → index-Dm6wGcYv.d.ts} +1 -0
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.js +200 -48
  9. package/dist/index.js.map +1 -1
  10. package/dist/server/index.js +200 -48
  11. package/dist/server/index.js.map +1 -1
  12. package/package.json +1 -1
  13. package/web/.next/BUILD_ID +1 -1
  14. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  15. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  16. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  17. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  18. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  19. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  20. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  21. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  22. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  23. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  24. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  25. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  33. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  42. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  51. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  68. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  76. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  77. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  78. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  79. /package/web/.next/standalone/web/.next/static/{static/tZkod5afiOX7T9AkN1yPO → 6Dlxqhgk8Mki7q7L-gDbl}/_buildManifest.js +0 -0
  80. /package/web/.next/standalone/web/.next/static/{static/tZkod5afiOX7T9AkN1yPO → 6Dlxqhgk8Mki7q7L-gDbl}/_clientMiddlewareManifest.json +0 -0
  81. /package/web/.next/standalone/web/.next/static/{static/tZkod5afiOX7T9AkN1yPO → 6Dlxqhgk8Mki7q7L-gDbl}/_ssgManifest.js +0 -0
  82. /package/web/.next/standalone/web/.next/static/{tZkod5afiOX7T9AkN1yPO → static/6Dlxqhgk8Mki7q7L-gDbl}/_buildManifest.js +0 -0
  83. /package/web/.next/standalone/web/.next/static/{tZkod5afiOX7T9AkN1yPO → static/6Dlxqhgk8Mki7q7L-gDbl}/_clientMiddlewareManifest.json +0 -0
  84. /package/web/.next/standalone/web/.next/static/{tZkod5afiOX7T9AkN1yPO → static/6Dlxqhgk8Mki7q7L-gDbl}/_ssgManifest.js +0 -0
  85. /package/web/.next/static/{tZkod5afiOX7T9AkN1yPO → 6Dlxqhgk8Mki7q7L-gDbl}/_buildManifest.js +0 -0
  86. /package/web/.next/static/{tZkod5afiOX7T9AkN1yPO → 6Dlxqhgk8Mki7q7L-gDbl}/_clientMiddlewareManifest.json +0 -0
  87. /package/web/.next/static/{tZkod5afiOX7T9AkN1yPO → 6Dlxqhgk8Mki7q7L-gDbl}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -7256,6 +7256,21 @@ ${this.summary}`
7256
7256
 
7257
7257
  // src/agent/index.ts
7258
7258
  init_webhook();
7259
+ var MAX_SSE_FIELD_LENGTH = 8 * 1024;
7260
+ var SSE_PREVIEW_LENGTH = 2 * 1024;
7261
+ function truncateWriteFileInput(input) {
7262
+ const out = { ...input };
7263
+ for (const key of ["content", "old_string", "new_string"]) {
7264
+ const val = out[key];
7265
+ if (typeof val === "string" && val.length > MAX_SSE_FIELD_LENGTH) {
7266
+ out[key] = `${val.slice(0, SSE_PREVIEW_LENGTH)}
7267
+ ... (truncated)`;
7268
+ out[`${key}Truncated`] = true;
7269
+ out[`${key}Length`] = val.length;
7270
+ }
7271
+ }
7272
+ return out;
7273
+ }
7259
7274
  var approvalResolvers = /* @__PURE__ */ new Map();
7260
7275
  var Agent = class _Agent {
7261
7276
  session;
@@ -7499,8 +7514,11 @@ ${prompt}` });
7499
7514
  };
7500
7515
  let taskRecorder = null;
7501
7516
  const sessionId = this.session.id;
7517
+ const emit = options.writeSSE;
7502
7518
  const bashProgressHandler = (progress) => {
7503
7519
  options.onToolProgress?.({ toolName: "bash", data: progress });
7520
+ if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "bash", data: progress })).catch(() => {
7521
+ });
7504
7522
  const port = progress.browserStreamPort;
7505
7523
  if (port && progress.status === "started") {
7506
7524
  Promise.resolve().then(() => (init_stream_proxy(), stream_proxy_exports)).then(({ getOrCreateProxy: getOrCreateProxy2 }) => {
@@ -7509,7 +7527,17 @@ ${prompt}` });
7509
7527
  Promise.resolve().then(() => (init_recorder(), recorder_exports)).then(({ FrameRecorder: FrameRecorder2 }) => {
7510
7528
  taskRecorder = new FrameRecorder2(sessionId);
7511
7529
  taskRecorder.start();
7512
- proxy.on("frame", (frame) => taskRecorder?.addFrame(frame));
7530
+ });
7531
+ }
7532
+ if (proxy.listenerCount("frame") === 0) {
7533
+ proxy.on("frame", (frame) => {
7534
+ taskRecorder?.addFrame(frame);
7535
+ if (emit) emit(JSON.stringify({ type: "browser-frame", data: frame.data, metadata: frame.metadata })).catch(() => {
7536
+ });
7537
+ });
7538
+ proxy.on("status", (s) => {
7539
+ if (emit) emit(JSON.stringify({ type: "browser-status", ...s })).catch(() => {
7540
+ });
7513
7541
  });
7514
7542
  }
7515
7543
  });
@@ -7520,8 +7548,16 @@ ${prompt}` });
7520
7548
  workingDirectory: this.session.workingDirectory,
7521
7549
  skillsDirectories: config.resolvedSkillsDirectories,
7522
7550
  onBashProgress: bashProgressHandler,
7523
- onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
7524
- onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0,
7551
+ onWriteFileProgress: (progress) => {
7552
+ options.onToolProgress?.({ toolName: "write_file", data: progress });
7553
+ if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "write_file", data: progress })).catch(() => {
7554
+ });
7555
+ },
7556
+ onSearchProgress: (progress) => {
7557
+ options.onToolProgress?.({ toolName: "explore_agent", data: progress });
7558
+ if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "explore_agent", data: progress })).catch(() => {
7559
+ });
7560
+ },
7525
7561
  taskTools: {
7526
7562
  outputSchema: options.taskConfig.outputSchema,
7527
7563
  onComplete
@@ -7539,6 +7575,9 @@ ${prompt}` });
7539
7575
 
7540
7576
  ${taskAddendum}`;
7541
7577
  fireWebhook("task.started", { prompt: options.prompt });
7578
+ if (emit) {
7579
+ await emit(JSON.stringify({ type: "data-user-message", data: { id: `user_${Date.now()}`, content: options.prompt } }));
7580
+ }
7542
7581
  await this.context.addUserMessage(options.prompt);
7543
7582
  let iteration = 0;
7544
7583
  while (iteration < maxIterations) {
@@ -7550,7 +7589,15 @@ ${taskAddendum}`;
7550
7589
  }
7551
7590
  const messages = await this.context.getMessages();
7552
7591
  const useAnthropic = isAnthropicModel(this.session.model);
7553
- const result = await generateText3({
7592
+ if (emit) {
7593
+ await emit(JSON.stringify({ type: "start", messageId: `msg_${Date.now()}` }));
7594
+ }
7595
+ let textStarted = false;
7596
+ let textId = `text_${Date.now()}`;
7597
+ let reasoningId = `reasoning_${Date.now()}`;
7598
+ let reasoningStarted = false;
7599
+ const toolCallStarts = /* @__PURE__ */ new Set();
7600
+ const iterStream = streamText2({
7554
7601
  model: resolveModel(this.session.model),
7555
7602
  system: systemPrompt,
7556
7603
  messages,
@@ -7559,21 +7606,94 @@ ${taskAddendum}`;
7559
7606
  abortSignal: options.abortSignal,
7560
7607
  providerOptions: useAnthropic ? {
7561
7608
  anthropic: {
7609
+ toolStreaming: true,
7562
7610
  thinking: { type: "enabled", budgetTokens: 1e4 }
7563
7611
  }
7564
7612
  } : void 0,
7565
- onStepFinish: (step) => {
7613
+ onStepFinish: async (step) => {
7566
7614
  options.onStepFinish?.(step);
7567
7615
  fireWebhook("task.step_finished", { iteration, text: step.text });
7616
+ if (emit) {
7617
+ if (textStarted) {
7618
+ await emit(JSON.stringify({ type: "text-end", id: textId }));
7619
+ textStarted = false;
7620
+ textId = `text_${Date.now()}`;
7621
+ }
7622
+ await emit(JSON.stringify({ type: "finish-step" }));
7623
+ }
7568
7624
  }
7569
7625
  });
7570
- const responseMessages = result.response.messages;
7626
+ for await (const part of iterStream.fullStream) {
7627
+ if (part.type === "text-delta") {
7628
+ if (emit) {
7629
+ if (!textStarted) {
7630
+ await emit(JSON.stringify({ type: "text-start", id: textId }));
7631
+ textStarted = true;
7632
+ }
7633
+ await emit(JSON.stringify({ type: "text-delta", id: textId, delta: part.text }));
7634
+ }
7635
+ } else if (part.type === "reasoning-start") {
7636
+ if (emit) {
7637
+ await emit(JSON.stringify({ type: "reasoning-start", id: reasoningId }));
7638
+ reasoningStarted = true;
7639
+ }
7640
+ } else if (part.type === "reasoning-delta") {
7641
+ if (emit) {
7642
+ await emit(JSON.stringify({ type: "reasoning-delta", id: reasoningId, delta: part.text }));
7643
+ }
7644
+ } else if (part.type === "reasoning-end") {
7645
+ if (emit && reasoningStarted) {
7646
+ await emit(JSON.stringify({ type: "reasoning-end", id: reasoningId }));
7647
+ reasoningStarted = false;
7648
+ reasoningId = `reasoning_${Date.now()}`;
7649
+ }
7650
+ } else if (part.type === "tool-call-streaming-start") {
7651
+ if (emit) {
7652
+ const p = part;
7653
+ await emit(JSON.stringify({ type: "tool-input-start", toolCallId: p.toolCallId, toolName: p.toolName }));
7654
+ toolCallStarts.add(p.toolCallId);
7655
+ }
7656
+ } else if (part.type === "tool-call-delta") {
7657
+ if (emit) {
7658
+ const p = part;
7659
+ await emit(JSON.stringify({ type: "tool-input-delta", toolCallId: p.toolCallId, argsTextDelta: p.argsTextDelta }));
7660
+ }
7661
+ } else if (part.type === "tool-call") {
7662
+ if (emit) {
7663
+ if (!toolCallStarts.has(part.toolCallId)) {
7664
+ await emit(JSON.stringify({ type: "tool-input-start", toolCallId: part.toolCallId, toolName: part.toolName }));
7665
+ toolCallStarts.add(part.toolCallId);
7666
+ }
7667
+ const safeInput = part.toolName === "write_file" && part.input && typeof part.input === "object" ? truncateWriteFileInput(part.input) : part.input;
7668
+ await emit(JSON.stringify({ type: "tool-input-available", toolCallId: part.toolCallId, toolName: part.toolName, input: safeInput }));
7669
+ }
7670
+ } else if (part.type === "tool-result") {
7671
+ if (emit) {
7672
+ await emit(JSON.stringify({ type: "tool-output-available", toolCallId: part.toolCallId, output: part.output }));
7673
+ }
7674
+ } else if (part.type === "error") {
7675
+ console.error("Task stream error:", part.error);
7676
+ if (emit) {
7677
+ await emit(JSON.stringify({ type: "error", errorText: String(part.error) }));
7678
+ }
7679
+ }
7680
+ }
7681
+ if (emit && textStarted) {
7682
+ await emit(JSON.stringify({ type: "text-end", id: textId }));
7683
+ }
7684
+ if (emit && reasoningStarted) {
7685
+ await emit(JSON.stringify({ type: "reasoning-end", id: reasoningId }));
7686
+ }
7687
+ const iterResponse = await iterStream.response;
7688
+ const responseMessages = iterResponse.messages;
7571
7689
  await this.context.addResponseMessages(responseMessages);
7572
- if (result.text) {
7573
- options.onText?.(result.text);
7574
- fireWebhook("task.message", { iteration, text: result.text });
7690
+ const resultText = await iterStream.text;
7691
+ const resultSteps = await iterStream.steps;
7692
+ if (resultText) {
7693
+ options.onText?.(resultText);
7694
+ fireWebhook("task.message", { iteration, text: resultText });
7575
7695
  }
7576
- for (const step of result.steps) {
7696
+ for (const step of resultSteps) {
7577
7697
  if (step.toolCalls) {
7578
7698
  for (const tc of step.toolCalls) {
7579
7699
  options.onToolCall?.({ toolCallId: tc.toolCallId, toolName: tc.toolName, input: tc.args });
@@ -7626,9 +7746,11 @@ ${taskAddendum}`;
7626
7746
  iterations: iteration
7627
7747
  };
7628
7748
  }
7629
- await this.context.addUserMessage(
7630
- "Continue working on the task. Before calling `complete_task`, VERIFY your work is correct \u2014 re-read edited files, run the linter, run tests if applicable, and check the browser/server if you made UI or API changes. Make sure you searched the right directories and found everything relevant. When fully verified, call `complete_task` with the result. If you cannot complete it, call `task_failed` with a reason."
7631
- );
7749
+ const continuationPrompt = "Continue working on the task. Before calling `complete_task`, VERIFY your work is correct \u2014 re-read edited files, run the linter, run tests if applicable, and check the browser/server if you made UI or API changes. Make sure you searched the right directories and found everything relevant. When fully verified, call `complete_task` with the result. If you cannot complete it, call `task_failed` with a reason.";
7750
+ if (emit) {
7751
+ await emit(JSON.stringify({ type: "data-user-message", data: { id: `user_${Date.now()}`, content: continuationPrompt } }));
7752
+ }
7753
+ await this.context.addUserMessage(continuationPrompt);
7632
7754
  }
7633
7755
  const timeoutError = `Task did not complete within ${maxIterations} iterations`;
7634
7756
  const timeoutRecordingUrls = await this.finishTaskRecording(taskRecorder);
@@ -10260,6 +10382,7 @@ init_db();
10260
10382
  import { Hono as Hono5 } from "hono";
10261
10383
  import { zValidator as zValidator5 } from "@hono/zod-validator";
10262
10384
  import { z as z19 } from "zod";
10385
+ import { nanoid as nanoid7 } from "nanoid";
10263
10386
  init_config();
10264
10387
  var tasks = new Hono5();
10265
10388
  var taskAbortControllers = /* @__PURE__ */ new Map();
@@ -10297,45 +10420,74 @@ tasks.post(
10297
10420
  const taskId = agent.sessionId;
10298
10421
  const abortController = new AbortController();
10299
10422
  taskAbortControllers.set(taskId, abortController);
10300
- (async () => {
10301
- try {
10302
- await agent.runTask({
10303
- prompt: body.prompt,
10304
- taskConfig,
10305
- abortSignal: abortController.signal
10306
- });
10307
- } catch (err) {
10308
- if (err.name === "AbortError" || abortController.signal.aborted) {
10309
- console.log(`[TASK] Task ${taskId} was cancelled`);
10310
- } else {
10311
- console.error(`[TASK] Error in task ${taskId}:`, err.message);
10312
- const errorMsg = err.message || "Unknown error";
10313
- const failedTask = {
10314
- ...taskConfig,
10315
- status: "failed",
10316
- error: errorMsg
10317
- };
10318
- await sessionQueries.update(taskId, {
10319
- config: {
10320
- toolApprovals: { bash: false, write_file: false, read_file: false },
10321
- task: failedTask
10322
- }
10423
+ const streamId = `stream_${taskId}_${nanoid7(10)}`;
10424
+ await activeStreamQueries.create(taskId, streamId);
10425
+ const taskStreamProducer = () => {
10426
+ const { readable, writable } = new TransformStream();
10427
+ const writer = writable.getWriter();
10428
+ let writerClosed = false;
10429
+ const writeSSE = async (data) => {
10430
+ if (writerClosed) return;
10431
+ try {
10432
+ await writer.write(`data: ${data}
10433
+
10434
+ `);
10435
+ } catch {
10436
+ writerClosed = true;
10437
+ }
10438
+ };
10439
+ (async () => {
10440
+ await writeSSE(JSON.stringify({ type: "data-stream-id", streamId }));
10441
+ try {
10442
+ await agent.runTask({
10443
+ prompt: body.prompt,
10444
+ taskConfig,
10445
+ abortSignal: abortController.signal,
10446
+ writeSSE
10323
10447
  });
10324
- if (taskConfig.webhookUrl) {
10325
- const { sendWebhook: sendWebhook2 } = await Promise.resolve().then(() => (init_webhook(), webhook_exports));
10326
- sendWebhook2(taskConfig.webhookUrl, {
10327
- type: "task.failed",
10328
- taskId,
10329
- sessionId: taskId,
10330
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
10331
- data: { status: "failed", error: errorMsg }
10448
+ await writeSSE(JSON.stringify({ type: "finish" }));
10449
+ } catch (err) {
10450
+ if (err.name === "AbortError" || abortController.signal.aborted) {
10451
+ console.log(`[TASK] Task ${taskId} was cancelled`);
10452
+ await writeSSE(JSON.stringify({ type: "abort" }));
10453
+ } else {
10454
+ console.error(`[TASK] Error in task ${taskId}:`, err.message);
10455
+ const errorMsg = err.message || "Unknown error";
10456
+ await writeSSE(JSON.stringify({ type: "error", errorText: errorMsg }));
10457
+ const failedTask = {
10458
+ ...taskConfig,
10459
+ status: "failed",
10460
+ error: errorMsg
10461
+ };
10462
+ await sessionQueries.update(taskId, {
10463
+ config: {
10464
+ toolApprovals: { bash: false, write_file: false, read_file: false },
10465
+ task: failedTask
10466
+ }
10332
10467
  });
10468
+ if (taskConfig.webhookUrl) {
10469
+ const { sendWebhook: sendWebhook2 } = await Promise.resolve().then(() => (init_webhook(), webhook_exports));
10470
+ sendWebhook2(taskConfig.webhookUrl, {
10471
+ type: "task.failed",
10472
+ taskId,
10473
+ sessionId: taskId,
10474
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
10475
+ data: { status: "failed", error: errorMsg }
10476
+ });
10477
+ }
10333
10478
  }
10479
+ } finally {
10480
+ await writeSSE("[DONE]");
10481
+ writer.close().catch(() => {
10482
+ });
10483
+ await activeStreamQueries.finish(streamId).catch(() => {
10484
+ });
10485
+ taskAbortControllers.delete(taskId);
10334
10486
  }
10335
- } finally {
10336
- taskAbortControllers.delete(taskId);
10337
- }
10338
- })();
10487
+ })();
10488
+ return readable;
10489
+ };
10490
+ await streamContext.resumableStream(streamId, taskStreamProducer);
10339
10491
  return c.json({ taskId, status: "running" }, 201);
10340
10492
  }
10341
10493
  );