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
@@ -85,7 +85,7 @@ declare const sessions: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
85
85
  tableName: "sessions";
86
86
  dataType: "string";
87
87
  columnType: "SQLiteText";
88
- data: "error" | "completed" | "active" | "waiting";
88
+ data: "completed" | "error" | "active" | "waiting";
89
89
  driverParam: string;
90
90
  notNull: true;
91
91
  hasDefault: true;
@@ -391,7 +391,7 @@ declare const toolExecutions: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
391
391
  tableName: "tool_executions";
392
392
  dataType: "string";
393
393
  columnType: "SQLiteText";
394
- data: "error" | "completed" | "pending" | "approved" | "rejected";
394
+ data: "completed" | "error" | "pending" | "approved" | "rejected";
395
395
  driverParam: string;
396
396
  notNull: true;
397
397
  hasDefault: true;
@@ -814,7 +814,7 @@ declare const terminals: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
814
814
  tableName: "terminals";
815
815
  dataType: "string";
816
816
  columnType: "SQLiteText";
817
- data: "error" | "running" | "stopped";
817
+ data: "running" | "error" | "stopped";
818
818
  driverParam: string;
819
819
  notNull: true;
820
820
  hasDefault: true;
@@ -5,6 +5,7 @@ interface BashToolProgress {
5
5
  status: 'started' | 'running' | 'completed';
6
6
  command?: string;
7
7
  browserStreamPort?: number;
8
+ browserClosed?: boolean;
8
9
  }
9
10
  interface BashToolOptions {
10
11
  workingDirectory: string;
@@ -14,12 +15,12 @@ interface BashToolOptions {
14
15
  }
15
16
  declare function createBashTool(options: BashToolOptions): ai.Tool<{
16
17
  background: boolean;
17
- input?: string | undefined;
18
18
  id?: string | undefined;
19
+ input?: string | undefined;
19
20
  command?: string | undefined;
20
21
  kill?: boolean | undefined;
21
22
  tail?: number | undefined;
22
- key?: "y" | "Enter" | "Escape" | "Up" | "Down" | "Left" | "Right" | "Tab" | "C-c" | "C-d" | "n" | undefined;
23
+ key?: "Enter" | "Escape" | "Up" | "Down" | "Left" | "Right" | "Tab" | "C-c" | "C-d" | "y" | "n" | undefined;
23
24
  }, {
24
25
  success: boolean;
25
26
  id: string;
@@ -65,7 +66,7 @@ declare function createBashTool(options: BashToolOptions): ai.Tool<{
65
66
  id: string;
66
67
  output: string;
67
68
  exitCode: number;
68
- status: "error" | "completed" | "running" | "stopped";
69
+ status: "running" | "completed" | "error" | "stopped";
69
70
  message?: undefined;
70
71
  error?: undefined;
71
72
  } | {
@@ -217,8 +218,8 @@ interface SearchToolOptions {
217
218
  * Progress is streamed back to the UI so users can see exploration happening.
218
219
  */
219
220
  declare function createSearchTool(options: SearchToolOptions): ai.Tool<{
220
- context: string;
221
221
  query: string;
222
+ context: string;
222
223
  }, {
223
224
  success: boolean;
224
225
  error: string;
@@ -1784,19 +1784,14 @@ __export(stream_proxy_exports, {
1784
1784
  import WebSocket from "ws";
1785
1785
  import { EventEmitter } from "events";
1786
1786
  function getOrCreateProxy(sessionId, port) {
1787
- let proxy = activeProxies.get(sessionId);
1788
- if (proxy && !proxy.connected) {
1789
- proxy.destroy();
1790
- proxy = void 0;
1791
- }
1792
- if (!proxy) {
1793
- proxy = new BrowserStreamProxy(port);
1794
- activeProxies.set(sessionId, proxy);
1795
- proxy.on("close", () => {
1796
- activeProxies.delete(sessionId);
1797
- });
1798
- proxy.connect();
1799
- }
1787
+ const existing = activeProxies.get(sessionId);
1788
+ if (existing) return existing;
1789
+ const proxy = new BrowserStreamProxy(port);
1790
+ activeProxies.set(sessionId, proxy);
1791
+ proxy.on("close", () => {
1792
+ activeProxies.delete(sessionId);
1793
+ });
1794
+ proxy.connect();
1800
1795
  return proxy;
1801
1796
  }
1802
1797
  function getProxy(sessionId) {
@@ -1813,8 +1808,8 @@ var RECONNECT_DELAY_MS, MAX_RECONNECT_ATTEMPTS, FRAME_THROTTLE_MS, BrowserStream
1813
1808
  var init_stream_proxy = __esm({
1814
1809
  "src/browser/stream-proxy.ts"() {
1815
1810
  "use strict";
1816
- RECONNECT_DELAY_MS = 500;
1817
- MAX_RECONNECT_ATTEMPTS = 10;
1811
+ RECONNECT_DELAY_MS = 1e3;
1812
+ MAX_RECONNECT_ATTEMPTS = 20;
1818
1813
  FRAME_THROTTLE_MS = 100;
1819
1814
  BrowserStreamProxy = class extends EventEmitter {
1820
1815
  ws = null;
@@ -1864,8 +1859,11 @@ var init_stream_proxy = __esm({
1864
1859
  }
1865
1860
  });
1866
1861
  this.ws.on("close", () => {
1862
+ const wasConnected = this._connected;
1867
1863
  this._connected = false;
1868
- this.emit("status", { connected: false, screencasting: false });
1864
+ if (wasConnected) {
1865
+ this.emit("status", { connected: false, screencasting: false });
1866
+ }
1869
1867
  if (!this.destroyed) {
1870
1868
  this.scheduleReconnect();
1871
1869
  }
@@ -1907,7 +1905,7 @@ var init_stream_proxy = __esm({
1907
1905
  return;
1908
1906
  }
1909
1907
  this.reconnectAttempts++;
1910
- const delay = RECONNECT_DELAY_MS * this.reconnectAttempts;
1908
+ const delay = this.reconnectAttempts <= 5 ? RECONNECT_DELAY_MS : RECONNECT_DELAY_MS * (this.reconnectAttempts - 4);
1911
1909
  this.reconnectTimer = setTimeout(() => this.doConnect(), delay);
1912
1910
  }
1913
1911
  /**
@@ -2617,15 +2615,15 @@ function getBrowserStreamPort(sessionId) {
2617
2615
  }
2618
2616
  return port;
2619
2617
  }
2620
- function isAgentBrowserOpenCommand(command) {
2621
- return /\bagent-browser\s+open\b/.test(command);
2618
+ function hasAgentBrowserCommand(command) {
2619
+ return /\bagent-browser\b/.test(command);
2622
2620
  }
2623
2621
  function isAgentBrowserCloseCommand(command) {
2624
- return /\bagent-browser\s+close\b/.test(command);
2622
+ return /\bagent-browser\s+(close|close\s+--all)\b/.test(command);
2625
2623
  }
2626
2624
  function injectBrowserStreamPort(command, port) {
2627
2625
  return command.replace(
2628
- /\bagent-browser\b/,
2626
+ /\bagent-browser\b/g,
2629
2627
  `AGENT_BROWSER_STREAM_PORT=${port} agent-browser`
2630
2628
  );
2631
2629
  }
@@ -2795,12 +2793,14 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
2795
2793
  };
2796
2794
  }
2797
2795
  let actualCommand = command;
2798
- const browserOpen = isAgentBrowserOpenCommand(command);
2796
+ const hasAgentBrowser = hasAgentBrowserCommand(command);
2799
2797
  const browserClose = isAgentBrowserCloseCommand(command);
2800
2798
  let browserPort;
2801
- if (browserOpen) {
2799
+ if (hasAgentBrowser) {
2802
2800
  browserPort = getBrowserStreamPort(options.sessionId);
2803
- actualCommand = injectBrowserStreamPort(command, browserPort);
2801
+ if (!browserClose) {
2802
+ actualCommand = injectBrowserStreamPort(command, browserPort);
2803
+ }
2804
2804
  }
2805
2805
  const canUseTmux = await shouldUseTmux();
2806
2806
  if (background) {
@@ -2816,9 +2816,6 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
2816
2816
  sessionId: options.sessionId,
2817
2817
  terminalId
2818
2818
  });
2819
- if (browserOpen && browserPort) {
2820
- options.onProgress?.({ terminalId, status: "running", command, browserStreamPort: browserPort });
2821
- }
2822
2819
  return {
2823
2820
  success: true,
2824
2821
  id: result.id,
@@ -2837,7 +2834,13 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
2837
2834
  });
2838
2835
  const truncatedOutput = truncateOutput(result.output, MAX_OUTPUT_CHARS2);
2839
2836
  options.onOutput?.(truncatedOutput);
2840
- options.onProgress?.({ terminalId, status: "completed", command, browserStreamPort: browserClose ? browserPort : void 0 });
2837
+ options.onProgress?.({
2838
+ terminalId,
2839
+ status: "completed",
2840
+ command,
2841
+ browserStreamPort: browserPort,
2842
+ browserClosed: browserClose || void 0
2843
+ });
2841
2844
  return {
2842
2845
  success: result.exitCode === 0,
2843
2846
  id: result.id,
@@ -8082,42 +8085,6 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
8082
8085
 
8083
8086
  ${prompt}`;
8084
8087
  }
8085
- async function encodAndUploadRecording(sessionId, recorder) {
8086
- if (!isRemoteConfigured()) {
8087
- console.log("[RECORDING] Remote server not configured, skipping upload");
8088
- recorder.clear();
8089
- return;
8090
- }
8091
- console.log(`[RECORDING] Encoding ${recorder.frameCount} frames for session ${sessionId}...`);
8092
- const result = await recorder.encode();
8093
- recorder.clear();
8094
- if (!result) {
8095
- console.log("[RECORDING] Encoding failed or produced no output");
8096
- return;
8097
- }
8098
- try {
8099
- const { storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
8100
- const { readFile: readFile11, unlink: unlink3 } = await import("fs/promises");
8101
- const uploadInfo = await storageQueries2.getUploadUrl(
8102
- sessionId,
8103
- `browser-recording-${Date.now()}.mp4`,
8104
- "video/mp4",
8105
- "browser-recording"
8106
- );
8107
- const fileData = await readFile11(result.path);
8108
- await fetch(uploadInfo.uploadUrl, {
8109
- method: "PUT",
8110
- headers: { "Content-Type": "video/mp4" },
8111
- body: fileData
8112
- });
8113
- await storageQueries2.updateFile(uploadInfo.fileId, { sizeBytes: result.sizeBytes });
8114
- console.log(`[RECORDING] Uploaded recording for session ${sessionId} (${result.sizeBytes} bytes)`);
8115
- await unlink3(result.path).catch(() => {
8116
- });
8117
- } catch (err) {
8118
- console.error("[RECORDING] Upload failed:", err.message);
8119
- }
8120
- }
8121
8088
  var agents = new Hono2();
8122
8089
  var attachmentSchema = z16.object({
8123
8090
  type: z16.enum(["image", "file"]),
@@ -8332,30 +8299,38 @@ ${prompt}` });
8332
8299
  await new Promise((resolve11) => setTimeout(resolve11, 0));
8333
8300
  }
8334
8301
  const browserPort = progress.data?.browserStreamPort;
8335
- if (progress.toolName === "bash" && browserPort && status === "started") {
8302
+ const browserClosed = progress.data?.browserClosed;
8303
+ if (progress.toolName === "bash" && browserClosed) {
8304
+ console.log(`[BROWSER-STREAM] agent-browser close detected, destroying proxy`);
8305
+ destroyProxy(sessionId);
8306
+ } else if (progress.toolName === "bash" && browserPort) {
8307
+ console.log(`[BROWSER-STREAM] agent-browser command detected, ensuring proxy on port ${browserPort}`);
8336
8308
  const proxy = getOrCreateProxy(sessionId, browserPort);
8337
- let recorder = sessionRecorders.get(sessionId);
8338
- if (!recorder) {
8339
- recorder = new FrameRecorder(sessionId);
8309
+ if (!sessionRecorders.has(sessionId)) {
8310
+ const recorder = new FrameRecorder(sessionId);
8340
8311
  recorder.start();
8341
8312
  sessionRecorders.set(sessionId, recorder);
8342
8313
  }
8343
- proxy.on("frame", (frame) => {
8344
- recorder.addFrame(frame);
8345
- writeSSE(JSON.stringify({
8346
- type: "browser-frame",
8347
- data: frame.data,
8348
- metadata: frame.metadata
8349
- })).catch(() => {
8314
+ if (proxy.listenerCount("frame") === 0) {
8315
+ proxy.on("frame", (frame) => {
8316
+ const rec = sessionRecorders.get(sessionId);
8317
+ rec?.addFrame(frame);
8318
+ writeSSE(JSON.stringify({
8319
+ type: "browser-frame",
8320
+ data: frame.data,
8321
+ metadata: frame.metadata
8322
+ })).catch(() => {
8323
+ });
8350
8324
  });
8351
- });
8352
- proxy.on("status", (s) => {
8353
- writeSSE(JSON.stringify({
8354
- type: "browser-status",
8355
- ...s
8356
- })).catch(() => {
8325
+ proxy.on("status", (s) => {
8326
+ console.log(`[BROWSER-STREAM] Status:`, s);
8327
+ writeSSE(JSON.stringify({
8328
+ type: "browser-status",
8329
+ ...s
8330
+ })).catch(() => {
8331
+ });
8357
8332
  });
8358
- });
8333
+ }
8359
8334
  }
8360
8335
  },
8361
8336
  onStepFinish: async () => {
@@ -8467,15 +8442,6 @@ ${prompt}` });
8467
8442
  }
8468
8443
  } finally {
8469
8444
  cleanupAbortController();
8470
- destroyProxy(sessionId);
8471
- const recorder = sessionRecorders.get(sessionId);
8472
- if (recorder && recorder.frameCount > 0) {
8473
- sessionRecorders.delete(sessionId);
8474
- recorder.stop();
8475
- encodAndUploadRecording(sessionId, recorder).catch((err) => {
8476
- console.error("[RECORDING] Failed to encode/upload:", err.message);
8477
- });
8478
- }
8479
8445
  await writeSSE("[DONE]");
8480
8446
  await safeClose();
8481
8447
  }
@@ -8831,30 +8797,38 @@ agents.post(
8831
8797
  await new Promise((resolve11) => setTimeout(resolve11, 0));
8832
8798
  }
8833
8799
  const browserPort = progress.data?.browserStreamPort;
8834
- if (progress.toolName === "bash" && browserPort && status === "started") {
8800
+ const browserClosed = progress.data?.browserClosed;
8801
+ if (progress.toolName === "bash" && browserClosed) {
8802
+ console.log(`[BROWSER-STREAM] agent-browser close detected`);
8803
+ destroyProxy(session.id);
8804
+ } else if (progress.toolName === "bash" && browserPort) {
8805
+ console.log(`[BROWSER-STREAM] agent-browser command detected, port ${browserPort}`);
8835
8806
  const proxy = getOrCreateProxy(session.id, browserPort);
8836
- let recorder = sessionRecorders.get(session.id);
8837
- if (!recorder) {
8838
- recorder = new FrameRecorder(session.id);
8807
+ if (!sessionRecorders.has(session.id)) {
8808
+ const recorder = new FrameRecorder(session.id);
8839
8809
  recorder.start();
8840
8810
  sessionRecorders.set(session.id, recorder);
8841
8811
  }
8842
- proxy.on("frame", (frame) => {
8843
- recorder.addFrame(frame);
8844
- writeSSE(JSON.stringify({
8845
- type: "browser-frame",
8846
- data: frame.data,
8847
- metadata: frame.metadata
8848
- })).catch(() => {
8812
+ if (proxy.listenerCount("frame") === 0) {
8813
+ proxy.on("frame", (frame) => {
8814
+ const rec = sessionRecorders.get(session.id);
8815
+ rec?.addFrame(frame);
8816
+ writeSSE(JSON.stringify({
8817
+ type: "browser-frame",
8818
+ data: frame.data,
8819
+ metadata: frame.metadata
8820
+ })).catch(() => {
8821
+ });
8849
8822
  });
8850
- });
8851
- proxy.on("status", (s) => {
8852
- writeSSE(JSON.stringify({
8853
- type: "browser-status",
8854
- ...s
8855
- })).catch(() => {
8823
+ proxy.on("status", (s) => {
8824
+ console.log(`[BROWSER-STREAM] Status:`, s);
8825
+ writeSSE(JSON.stringify({
8826
+ type: "browser-status",
8827
+ ...s
8828
+ })).catch(() => {
8829
+ });
8856
8830
  });
8857
- });
8831
+ }
8858
8832
  }
8859
8833
  },
8860
8834
  onStepFinish: async () => {
@@ -8960,15 +8934,6 @@ agents.post(
8960
8934
  }
8961
8935
  } finally {
8962
8936
  cleanupAbortController();
8963
- destroyProxy(session.id);
8964
- const recorder = sessionRecorders.get(session.id);
8965
- if (recorder && recorder.frameCount > 0) {
8966
- sessionRecorders.delete(session.id);
8967
- recorder.stop();
8968
- encodAndUploadRecording(session.id, recorder).catch((err) => {
8969
- console.error("[RECORDING] Failed to encode/upload:", err.message);
8970
- });
8971
- }
8972
8937
  await writeSSE("[DONE]");
8973
8938
  await safeClose();
8974
8939
  }
@@ -9028,6 +8993,19 @@ agents.post(
9028
8993
  return c.json({ success: true });
9029
8994
  }
9030
8995
  );
8996
+ agents.get("/:id/browser-stream", async (c) => {
8997
+ const sessionId = c.req.param("id");
8998
+ const proxy = getProxy(sessionId);
8999
+ return c.json({
9000
+ sessionId,
9001
+ active: !!proxy?.connected,
9002
+ hasProxy: !!proxy,
9003
+ latestFrame: proxy?.latestFrame ? {
9004
+ metadata: proxy.latestFrame.metadata,
9005
+ timestamp: proxy.latestFrame.timestamp
9006
+ } : null
9007
+ });
9008
+ });
9031
9009
 
9032
9010
  // src/server/routes/health.ts
9033
9011
  init_config();