sparkecoder 0.1.65 → 0.1.66

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 (144) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +31 -28
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +96 -118
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-BhKQYdOx.d.ts → index-Db23cukG.d.ts} +25 -25
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +96 -118
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-XcP0dedO.d.ts → schema-C7Mm4Ykn.d.ts} +3 -3
  12. package/dist/{search-DkNNtC5A.d.ts → search-CVVfuBPZ.d.ts} +5 -4
  13. package/dist/server/index.js +96 -118
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/tools/index.d.ts +3 -3
  16. package/dist/tools/index.js +16 -11
  17. package/dist/tools/index.js.map +1 -1
  18. package/package.json +1 -1
  19. package/web/.next/BUILD_ID +1 -1
  20. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  21. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  22. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  23. package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
  24. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  25. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  26. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  28. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  43. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  57. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  58. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  66. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  75. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  76. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  77. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  78. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
  83. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
  84. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  85. package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
  86. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  87. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  88. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  89. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  91. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  92. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_708e1a2e._.js → 2374f_02a118f9._.js} +1 -1
  93. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_3db08933._.js → 2374f_0ed477f8._.js} +1 -1
  94. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_7d957e38._.js → 2374f_12bad06e._.js} +1 -1
  95. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_1225fb9d._.js → 2374f_2526ca80._.js} +1 -1
  96. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_95e53328._.js → 2374f_3b51a934._.js} +1 -1
  97. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f1f229dd._.js → 2374f_3e519469._.js} +1 -1
  98. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_a19628a3._.js → 2374f_5ebfcf1a._.js} +1 -1
  99. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_a90b70ee._.js → 2374f_a0f483d1._.js} +1 -1
  100. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_2775224f._.js → 2374f_acf3dfe4._.js} +1 -1
  101. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_32fd5733._.js → 2374f_ad08e83a._.js} +1 -1
  102. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_2eac907f._.js → 2374f_c1d54c16._.js} +1 -1
  103. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_51500fd2._.js → 2374f_db3e363b._.js} +1 -1
  104. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_8385a57b._.js → 2374f_f0d7e130._.js} +1 -1
  105. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_6f1ea3a7._.js → 2374f_fc992d90._.js} +1 -1
  106. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__5bde4c40._.js → [root-of-the-server]__06818a54._.js} +2 -2
  107. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__4085bf35._.js → [root-of-the-server]__c71f29f9._.js} +4 -4
  108. package/web/.next/standalone/web/.next/server/chunks/ssr/web_2b3a5919._.js +1 -1
  109. package/web/.next/standalone/web/.next/server/chunks/ssr/web_38156da8._.js +1 -1
  110. package/web/.next/standalone/web/.next/server/chunks/ssr/web_5cca707f._.js +7 -0
  111. package/web/.next/standalone/web/.next/server/chunks/ssr/web_935e81f5._.js +7 -0
  112. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_14735895._.js → web_cc5f7515._.js} +2 -2
  113. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  114. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  115. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  116. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  117. package/web/.next/{static/chunks/4b6a9454bc6460c5.js → standalone/web/.next/static/chunks/31208ade542a0fcb.js} +3 -3
  118. package/web/.next/{static/chunks/4a490bc538aa0940.js → standalone/web/.next/static/chunks/4e673433173ad456.js} +3 -3
  119. package/web/.next/standalone/web/.next/static/chunks/{c2dccd8f56d8eb5a.js → 515f0c0bd6087843.js} +5 -5
  120. package/web/.next/standalone/web/.next/static/chunks/fd39dd62879495e1.css +1 -0
  121. package/web/.next/standalone/web/.next/static/static/chunks/{4b6a9454bc6460c5.js → 31208ade542a0fcb.js} +3 -3
  122. package/web/.next/standalone/web/.next/static/static/chunks/{4a490bc538aa0940.js → 4e673433173ad456.js} +3 -3
  123. package/web/.next/standalone/web/.next/static/static/chunks/{c2dccd8f56d8eb5a.js → 515f0c0bd6087843.js} +5 -5
  124. package/web/.next/standalone/web/.next/static/static/chunks/fd39dd62879495e1.css +1 -0
  125. package/web/.next/standalone/web/src/components/chat-interface.tsx +96 -1
  126. package/web/.next/standalone/web/src/lib/api.ts +24 -0
  127. package/web/.next/{standalone/web/.next/static/chunks/4b6a9454bc6460c5.js → static/chunks/31208ade542a0fcb.js} +3 -3
  128. package/web/.next/{standalone/web/.next/static/chunks/4a490bc538aa0940.js → static/chunks/4e673433173ad456.js} +3 -3
  129. package/web/.next/static/chunks/{c2dccd8f56d8eb5a.js → 515f0c0bd6087843.js} +5 -5
  130. package/web/.next/static/chunks/fd39dd62879495e1.css +1 -0
  131. package/web/.next/standalone/web/.next/server/chunks/ssr/web_08bbd8c8._.js +0 -7
  132. package/web/.next/standalone/web/.next/server/chunks/ssr/web_c729ad51._.js +0 -7
  133. package/web/.next/standalone/web/.next/static/chunks/8b53c110c5a6f9a0.css +0 -1
  134. package/web/.next/standalone/web/.next/static/static/chunks/8b53c110c5a6f9a0.css +0 -1
  135. package/web/.next/static/chunks/8b53c110c5a6f9a0.css +0 -1
  136. /package/web/.next/standalone/web/.next/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_buildManifest.js +0 -0
  137. /package/web/.next/standalone/web/.next/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_clientMiddlewareManifest.json +0 -0
  138. /package/web/.next/standalone/web/.next/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_ssgManifest.js +0 -0
  139. /package/web/.next/standalone/web/.next/static/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_buildManifest.js +0 -0
  140. /package/web/.next/standalone/web/.next/static/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_clientMiddlewareManifest.json +0 -0
  141. /package/web/.next/standalone/web/.next/static/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_ssgManifest.js +0 -0
  142. /package/web/.next/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_buildManifest.js +0 -0
  143. /package/web/.next/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_clientMiddlewareManifest.json +0 -0
  144. /package/web/.next/static/{NuySHGSR0CchKU9zo42LK → UMGGmtMDTCI6fL-AIFkiM}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -2555,19 +2555,14 @@ __export(stream_proxy_exports, {
2555
2555
  import WebSocket from "ws";
2556
2556
  import { EventEmitter } from "events";
2557
2557
  function getOrCreateProxy(sessionId, port) {
2558
- let proxy = activeProxies.get(sessionId);
2559
- if (proxy && !proxy.connected) {
2560
- proxy.destroy();
2561
- proxy = void 0;
2562
- }
2563
- if (!proxy) {
2564
- proxy = new BrowserStreamProxy(port);
2565
- activeProxies.set(sessionId, proxy);
2566
- proxy.on("close", () => {
2567
- activeProxies.delete(sessionId);
2568
- });
2569
- proxy.connect();
2570
- }
2558
+ const existing = activeProxies.get(sessionId);
2559
+ if (existing) return existing;
2560
+ const proxy = new BrowserStreamProxy(port);
2561
+ activeProxies.set(sessionId, proxy);
2562
+ proxy.on("close", () => {
2563
+ activeProxies.delete(sessionId);
2564
+ });
2565
+ proxy.connect();
2571
2566
  return proxy;
2572
2567
  }
2573
2568
  function getProxy(sessionId) {
@@ -2584,8 +2579,8 @@ var RECONNECT_DELAY_MS, MAX_RECONNECT_ATTEMPTS, FRAME_THROTTLE_MS, BrowserStream
2584
2579
  var init_stream_proxy = __esm({
2585
2580
  "src/browser/stream-proxy.ts"() {
2586
2581
  "use strict";
2587
- RECONNECT_DELAY_MS = 500;
2588
- MAX_RECONNECT_ATTEMPTS = 10;
2582
+ RECONNECT_DELAY_MS = 1e3;
2583
+ MAX_RECONNECT_ATTEMPTS = 20;
2589
2584
  FRAME_THROTTLE_MS = 100;
2590
2585
  BrowserStreamProxy = class extends EventEmitter {
2591
2586
  ws = null;
@@ -2635,8 +2630,11 @@ var init_stream_proxy = __esm({
2635
2630
  }
2636
2631
  });
2637
2632
  this.ws.on("close", () => {
2633
+ const wasConnected = this._connected;
2638
2634
  this._connected = false;
2639
- this.emit("status", { connected: false, screencasting: false });
2635
+ if (wasConnected) {
2636
+ this.emit("status", { connected: false, screencasting: false });
2637
+ }
2640
2638
  if (!this.destroyed) {
2641
2639
  this.scheduleReconnect();
2642
2640
  }
@@ -2678,7 +2676,7 @@ var init_stream_proxy = __esm({
2678
2676
  return;
2679
2677
  }
2680
2678
  this.reconnectAttempts++;
2681
- const delay = RECONNECT_DELAY_MS * this.reconnectAttempts;
2679
+ const delay = this.reconnectAttempts <= 5 ? RECONNECT_DELAY_MS : RECONNECT_DELAY_MS * (this.reconnectAttempts - 4);
2682
2680
  this.reconnectTimer = setTimeout(() => this.doConnect(), delay);
2683
2681
  }
2684
2682
  /**
@@ -3395,15 +3393,15 @@ function getBrowserStreamPort(sessionId) {
3395
3393
  }
3396
3394
  return port;
3397
3395
  }
3398
- function isAgentBrowserOpenCommand(command) {
3399
- return /\bagent-browser\s+open\b/.test(command);
3396
+ function hasAgentBrowserCommand(command) {
3397
+ return /\bagent-browser\b/.test(command);
3400
3398
  }
3401
3399
  function isAgentBrowserCloseCommand(command) {
3402
- return /\bagent-browser\s+close\b/.test(command);
3400
+ return /\bagent-browser\s+(close|close\s+--all)\b/.test(command);
3403
3401
  }
3404
3402
  function injectBrowserStreamPort(command, port) {
3405
3403
  return command.replace(
3406
- /\bagent-browser\b/,
3404
+ /\bagent-browser\b/g,
3407
3405
  `AGENT_BROWSER_STREAM_PORT=${port} agent-browser`
3408
3406
  );
3409
3407
  }
@@ -3573,12 +3571,14 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
3573
3571
  };
3574
3572
  }
3575
3573
  let actualCommand = command;
3576
- const browserOpen = isAgentBrowserOpenCommand(command);
3574
+ const hasAgentBrowser = hasAgentBrowserCommand(command);
3577
3575
  const browserClose = isAgentBrowserCloseCommand(command);
3578
3576
  let browserPort;
3579
- if (browserOpen) {
3577
+ if (hasAgentBrowser) {
3580
3578
  browserPort = getBrowserStreamPort(options.sessionId);
3581
- actualCommand = injectBrowserStreamPort(command, browserPort);
3579
+ if (!browserClose) {
3580
+ actualCommand = injectBrowserStreamPort(command, browserPort);
3581
+ }
3582
3582
  }
3583
3583
  const canUseTmux = await shouldUseTmux();
3584
3584
  if (background) {
@@ -3594,9 +3594,6 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
3594
3594
  sessionId: options.sessionId,
3595
3595
  terminalId
3596
3596
  });
3597
- if (browserOpen && browserPort) {
3598
- options.onProgress?.({ terminalId, status: "running", command, browserStreamPort: browserPort });
3599
- }
3600
3597
  return {
3601
3598
  success: true,
3602
3599
  id: result.id,
@@ -3615,7 +3612,13 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
3615
3612
  });
3616
3613
  const truncatedOutput = truncateOutput(result.output, MAX_OUTPUT_CHARS2);
3617
3614
  options.onOutput?.(truncatedOutput);
3618
- options.onProgress?.({ terminalId, status: "completed", command, browserStreamPort: browserClose ? browserPort : void 0 });
3615
+ options.onProgress?.({
3616
+ terminalId,
3617
+ status: "completed",
3618
+ command,
3619
+ browserStreamPort: browserPort,
3620
+ browserClosed: browserClose || void 0
3621
+ });
3619
3622
  return {
3620
3623
  success: result.exitCode === 0,
3621
3624
  id: result.id,
@@ -8860,42 +8863,6 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
8860
8863
 
8861
8864
  ${prompt}`;
8862
8865
  }
8863
- async function encodAndUploadRecording(sessionId, recorder) {
8864
- if (!isRemoteConfigured()) {
8865
- console.log("[RECORDING] Remote server not configured, skipping upload");
8866
- recorder.clear();
8867
- return;
8868
- }
8869
- console.log(`[RECORDING] Encoding ${recorder.frameCount} frames for session ${sessionId}...`);
8870
- const result = await recorder.encode();
8871
- recorder.clear();
8872
- if (!result) {
8873
- console.log("[RECORDING] Encoding failed or produced no output");
8874
- return;
8875
- }
8876
- try {
8877
- const { storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
8878
- const { readFile: readFile11, unlink: unlink3 } = await import("fs/promises");
8879
- const uploadInfo = await storageQueries2.getUploadUrl(
8880
- sessionId,
8881
- `browser-recording-${Date.now()}.mp4`,
8882
- "video/mp4",
8883
- "browser-recording"
8884
- );
8885
- const fileData = await readFile11(result.path);
8886
- await fetch(uploadInfo.uploadUrl, {
8887
- method: "PUT",
8888
- headers: { "Content-Type": "video/mp4" },
8889
- body: fileData
8890
- });
8891
- await storageQueries2.updateFile(uploadInfo.fileId, { sizeBytes: result.sizeBytes });
8892
- console.log(`[RECORDING] Uploaded recording for session ${sessionId} (${result.sizeBytes} bytes)`);
8893
- await unlink3(result.path).catch(() => {
8894
- });
8895
- } catch (err) {
8896
- console.error("[RECORDING] Upload failed:", err.message);
8897
- }
8898
- }
8899
8866
  var agents = new Hono2();
8900
8867
  var attachmentSchema = z16.object({
8901
8868
  type: z16.enum(["image", "file"]),
@@ -9110,30 +9077,38 @@ ${prompt}` });
9110
9077
  await new Promise((resolve12) => setTimeout(resolve12, 0));
9111
9078
  }
9112
9079
  const browserPort = progress.data?.browserStreamPort;
9113
- if (progress.toolName === "bash" && browserPort && status === "started") {
9080
+ const browserClosed = progress.data?.browserClosed;
9081
+ if (progress.toolName === "bash" && browserClosed) {
9082
+ console.log(`[BROWSER-STREAM] agent-browser close detected, destroying proxy`);
9083
+ destroyProxy(sessionId);
9084
+ } else if (progress.toolName === "bash" && browserPort) {
9085
+ console.log(`[BROWSER-STREAM] agent-browser command detected, ensuring proxy on port ${browserPort}`);
9114
9086
  const proxy = getOrCreateProxy(sessionId, browserPort);
9115
- let recorder = sessionRecorders.get(sessionId);
9116
- if (!recorder) {
9117
- recorder = new FrameRecorder(sessionId);
9087
+ if (!sessionRecorders.has(sessionId)) {
9088
+ const recorder = new FrameRecorder(sessionId);
9118
9089
  recorder.start();
9119
9090
  sessionRecorders.set(sessionId, recorder);
9120
9091
  }
9121
- proxy.on("frame", (frame) => {
9122
- recorder.addFrame(frame);
9123
- writeSSE(JSON.stringify({
9124
- type: "browser-frame",
9125
- data: frame.data,
9126
- metadata: frame.metadata
9127
- })).catch(() => {
9092
+ if (proxy.listenerCount("frame") === 0) {
9093
+ proxy.on("frame", (frame) => {
9094
+ const rec = sessionRecorders.get(sessionId);
9095
+ rec?.addFrame(frame);
9096
+ writeSSE(JSON.stringify({
9097
+ type: "browser-frame",
9098
+ data: frame.data,
9099
+ metadata: frame.metadata
9100
+ })).catch(() => {
9101
+ });
9128
9102
  });
9129
- });
9130
- proxy.on("status", (s) => {
9131
- writeSSE(JSON.stringify({
9132
- type: "browser-status",
9133
- ...s
9134
- })).catch(() => {
9103
+ proxy.on("status", (s) => {
9104
+ console.log(`[BROWSER-STREAM] Status:`, s);
9105
+ writeSSE(JSON.stringify({
9106
+ type: "browser-status",
9107
+ ...s
9108
+ })).catch(() => {
9109
+ });
9135
9110
  });
9136
- });
9111
+ }
9137
9112
  }
9138
9113
  },
9139
9114
  onStepFinish: async () => {
@@ -9245,15 +9220,6 @@ ${prompt}` });
9245
9220
  }
9246
9221
  } finally {
9247
9222
  cleanupAbortController();
9248
- destroyProxy(sessionId);
9249
- const recorder = sessionRecorders.get(sessionId);
9250
- if (recorder && recorder.frameCount > 0) {
9251
- sessionRecorders.delete(sessionId);
9252
- recorder.stop();
9253
- encodAndUploadRecording(sessionId, recorder).catch((err) => {
9254
- console.error("[RECORDING] Failed to encode/upload:", err.message);
9255
- });
9256
- }
9257
9223
  await writeSSE("[DONE]");
9258
9224
  await safeClose();
9259
9225
  }
@@ -9609,30 +9575,38 @@ agents.post(
9609
9575
  await new Promise((resolve12) => setTimeout(resolve12, 0));
9610
9576
  }
9611
9577
  const browserPort = progress.data?.browserStreamPort;
9612
- if (progress.toolName === "bash" && browserPort && status === "started") {
9578
+ const browserClosed = progress.data?.browserClosed;
9579
+ if (progress.toolName === "bash" && browserClosed) {
9580
+ console.log(`[BROWSER-STREAM] agent-browser close detected`);
9581
+ destroyProxy(session.id);
9582
+ } else if (progress.toolName === "bash" && browserPort) {
9583
+ console.log(`[BROWSER-STREAM] agent-browser command detected, port ${browserPort}`);
9613
9584
  const proxy = getOrCreateProxy(session.id, browserPort);
9614
- let recorder = sessionRecorders.get(session.id);
9615
- if (!recorder) {
9616
- recorder = new FrameRecorder(session.id);
9585
+ if (!sessionRecorders.has(session.id)) {
9586
+ const recorder = new FrameRecorder(session.id);
9617
9587
  recorder.start();
9618
9588
  sessionRecorders.set(session.id, recorder);
9619
9589
  }
9620
- proxy.on("frame", (frame) => {
9621
- recorder.addFrame(frame);
9622
- writeSSE(JSON.stringify({
9623
- type: "browser-frame",
9624
- data: frame.data,
9625
- metadata: frame.metadata
9626
- })).catch(() => {
9590
+ if (proxy.listenerCount("frame") === 0) {
9591
+ proxy.on("frame", (frame) => {
9592
+ const rec = sessionRecorders.get(session.id);
9593
+ rec?.addFrame(frame);
9594
+ writeSSE(JSON.stringify({
9595
+ type: "browser-frame",
9596
+ data: frame.data,
9597
+ metadata: frame.metadata
9598
+ })).catch(() => {
9599
+ });
9627
9600
  });
9628
- });
9629
- proxy.on("status", (s) => {
9630
- writeSSE(JSON.stringify({
9631
- type: "browser-status",
9632
- ...s
9633
- })).catch(() => {
9601
+ proxy.on("status", (s) => {
9602
+ console.log(`[BROWSER-STREAM] Status:`, s);
9603
+ writeSSE(JSON.stringify({
9604
+ type: "browser-status",
9605
+ ...s
9606
+ })).catch(() => {
9607
+ });
9634
9608
  });
9635
- });
9609
+ }
9636
9610
  }
9637
9611
  },
9638
9612
  onStepFinish: async () => {
@@ -9738,15 +9712,6 @@ agents.post(
9738
9712
  }
9739
9713
  } finally {
9740
9714
  cleanupAbortController();
9741
- destroyProxy(session.id);
9742
- const recorder = sessionRecorders.get(session.id);
9743
- if (recorder && recorder.frameCount > 0) {
9744
- sessionRecorders.delete(session.id);
9745
- recorder.stop();
9746
- encodAndUploadRecording(session.id, recorder).catch((err) => {
9747
- console.error("[RECORDING] Failed to encode/upload:", err.message);
9748
- });
9749
- }
9750
9715
  await writeSSE("[DONE]");
9751
9716
  await safeClose();
9752
9717
  }
@@ -9806,6 +9771,19 @@ agents.post(
9806
9771
  return c.json({ success: true });
9807
9772
  }
9808
9773
  );
9774
+ agents.get("/:id/browser-stream", async (c) => {
9775
+ const sessionId = c.req.param("id");
9776
+ const proxy = getProxy(sessionId);
9777
+ return c.json({
9778
+ sessionId,
9779
+ active: !!proxy?.connected,
9780
+ hasProxy: !!proxy,
9781
+ latestFrame: proxy?.latestFrame ? {
9782
+ metadata: proxy.latestFrame.metadata,
9783
+ timestamp: proxy.latestFrame.timestamp
9784
+ } : null
9785
+ });
9786
+ });
9809
9787
 
9810
9788
  // src/server/routes/health.ts
9811
9789
  init_config();