sparkecoder 0.1.68 → 0.1.70

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 (117) hide show
  1. package/dist/agent/index.js +49 -10
  2. package/dist/agent/index.js.map +1 -1
  3. package/dist/cli.js +154 -50
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.js +154 -50
  6. package/dist/index.js.map +1 -1
  7. package/dist/server/index.js +154 -50
  8. package/dist/server/index.js.map +1 -1
  9. package/dist/skills/default/qa.md +376 -106
  10. package/package.json +1 -1
  11. package/src/skills/default/qa.md +376 -106
  12. package/web/.next/BUILD_ID +1 -1
  13. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  14. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  15. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  16. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  17. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  18. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  19. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  20. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  21. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  22. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  23. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  24. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  25. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  29. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  30. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  32. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  35. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  36. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  37. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  38. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  39. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  41. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  42. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  48. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  50. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  52. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  53. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  58. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  62. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  63. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  68. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  71. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  72. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
  75. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
  76. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  77. package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
  78. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  79. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  80. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  81. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  83. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  84. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__c71f29f9._.js → [root-of-the-server]__a1877334._.js} +4 -4
  85. package/web/.next/standalone/web/.next/server/chunks/ssr/web_2b3a5919._.js +1 -1
  86. package/web/.next/standalone/web/.next/server/chunks/ssr/web_38156da8._.js +1 -1
  87. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  88. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  89. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  90. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  91. package/web/.next/{static/chunks/4e673433173ad456.js → standalone/web/.next/static/chunks/2cafc7cb79454d33.js} +3 -3
  92. package/web/.next/standalone/web/.next/static/chunks/41a5c049931b2c77.css +1 -0
  93. package/web/.next/standalone/web/.next/static/chunks/{515f0c0bd6087843.js → f95d41079838994a.js} +3 -3
  94. package/web/.next/standalone/web/.next/static/chunks/{31208ade542a0fcb.js → fc39a194539da104.js} +3 -3
  95. package/web/.next/standalone/web/.next/static/{chunks/4e673433173ad456.js → static/chunks/2cafc7cb79454d33.js} +3 -3
  96. package/web/.next/standalone/web/.next/static/static/chunks/41a5c049931b2c77.css +1 -0
  97. package/web/.next/standalone/web/.next/static/static/chunks/{515f0c0bd6087843.js → f95d41079838994a.js} +3 -3
  98. package/web/.next/{static/chunks/31208ade542a0fcb.js → standalone/web/.next/static/static/chunks/fc39a194539da104.js} +3 -3
  99. package/web/.next/standalone/web/src/components/ai-elements/read-file-tool.tsx +19 -2
  100. package/web/.next/standalone/web/src/components/chat-interface.tsx +63 -4
  101. package/web/.next/standalone/web/src/lib/api.ts +89 -16
  102. package/web/.next/{standalone/web/.next/static/static/chunks/4e673433173ad456.js → static/chunks/2cafc7cb79454d33.js} +3 -3
  103. package/web/.next/static/chunks/41a5c049931b2c77.css +1 -0
  104. package/web/.next/static/chunks/{515f0c0bd6087843.js → f95d41079838994a.js} +3 -3
  105. package/web/.next/{standalone/web/.next/static/static/chunks/31208ade542a0fcb.js → static/chunks/fc39a194539da104.js} +3 -3
  106. package/web/.next/standalone/web/.next/static/chunks/fd39dd62879495e1.css +0 -1
  107. package/web/.next/standalone/web/.next/static/static/chunks/fd39dd62879495e1.css +0 -1
  108. package/web/.next/static/chunks/fd39dd62879495e1.css +0 -1
  109. /package/web/.next/standalone/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  110. /package/web/.next/standalone/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  111. /package/web/.next/standalone/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
  112. /package/web/.next/standalone/web/.next/static/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  113. /package/web/.next/standalone/web/.next/static/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  114. /package/web/.next/standalone/web/.next/static/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
  115. /package/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  116. /package/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  117. /package/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
@@ -1785,10 +1785,15 @@ import WebSocket from "ws";
1785
1785
  import { EventEmitter } from "events";
1786
1786
  function getOrCreateProxy(sessionId, port) {
1787
1787
  const existing = activeProxies.get(sessionId);
1788
- if (existing) return existing;
1788
+ if (existing) {
1789
+ console.log(`[BROWSER-WS] Reusing existing proxy for session ${sessionId} (connected=${existing.connected})`);
1790
+ return existing;
1791
+ }
1792
+ console.log(`[BROWSER-WS] Creating new proxy for session ${sessionId} on port ${port} (active proxies: ${activeProxies.size})`);
1789
1793
  const proxy = new BrowserStreamProxy(port);
1790
1794
  activeProxies.set(sessionId, proxy);
1791
1795
  proxy.on("close", () => {
1796
+ console.log(`[BROWSER-WS] Proxy closed for session ${sessionId}, removing from registry`);
1792
1797
  activeProxies.delete(sessionId);
1793
1798
  });
1794
1799
  proxy.connect();
@@ -1800,8 +1805,11 @@ function getProxy(sessionId) {
1800
1805
  function destroyProxy(sessionId) {
1801
1806
  const proxy = activeProxies.get(sessionId);
1802
1807
  if (proxy) {
1808
+ console.log(`[BROWSER-WS] destroyProxy() called for session ${sessionId}`);
1803
1809
  proxy.destroy();
1804
1810
  activeProxies.delete(sessionId);
1811
+ } else {
1812
+ console.log(`[BROWSER-WS] destroyProxy() called but no proxy exists for session ${sessionId}`);
1805
1813
  }
1806
1814
  }
1807
1815
  var RECONNECT_DELAY_MS, MAX_RECONNECT_ATTEMPTS, FRAME_THROTTLE_MS, BrowserStreamProxy, activeProxies;
@@ -1832,35 +1840,37 @@ var init_stream_proxy = __esm({
1832
1840
  }
1833
1841
  connect() {
1834
1842
  if (this.destroyed) return;
1843
+ console.log(`[BROWSER-WS] connect() called for port ${this.port}`);
1835
1844
  this.doConnect();
1836
1845
  }
1837
1846
  doConnect() {
1838
1847
  if (this.destroyed) return;
1839
1848
  const url = `ws://localhost:${this.port}`;
1849
+ console.log(`[BROWSER-WS] Attempting WebSocket connection to ${url} (attempt ${this.reconnectAttempts + 1}/${MAX_RECONNECT_ATTEMPTS})`);
1840
1850
  try {
1841
1851
  this.ws = new WebSocket(url);
1842
- } catch {
1852
+ } catch (err) {
1853
+ console.warn(`[BROWSER-WS] WebSocket constructor threw for ${url}:`, err);
1843
1854
  this.scheduleReconnect();
1844
1855
  return;
1845
1856
  }
1846
1857
  this.ws.on("open", () => {
1858
+ console.log(`[BROWSER-WS] Connected to ${url} (after ${this.reconnectAttempts} retries)`);
1847
1859
  this.reconnectAttempts = 0;
1848
1860
  this._connected = true;
1849
- this.emit("status", {
1850
- connected: true,
1851
- screencasting: true
1852
- });
1853
1861
  });
1854
1862
  this.ws.on("message", (raw) => {
1855
1863
  try {
1856
1864
  const msg = JSON.parse(typeof raw === "string" ? raw : raw.toString("utf8"));
1857
1865
  this.handleMessage(msg);
1858
- } catch {
1866
+ } catch (err) {
1867
+ console.warn(`[BROWSER-WS] Malformed message from ${url}:`, err);
1859
1868
  }
1860
1869
  });
1861
- this.ws.on("close", () => {
1870
+ this.ws.on("close", (code, reason) => {
1862
1871
  const wasConnected = this._connected;
1863
1872
  this._connected = false;
1873
+ console.log(`[BROWSER-WS] Connection closed: code=${code} reason="${reason?.toString() || ""}" wasConnected=${wasConnected} destroyed=${this.destroyed}`);
1864
1874
  if (wasConnected) {
1865
1875
  this.emit("status", { connected: false, screencasting: false });
1866
1876
  }
@@ -1868,14 +1878,26 @@ var init_stream_proxy = __esm({
1868
1878
  this.scheduleReconnect();
1869
1879
  }
1870
1880
  });
1871
- this.ws.on("error", () => {
1881
+ this.ws.on("error", (err) => {
1882
+ console.warn(`[BROWSER-WS] WebSocket error on port ${this.port}:`, err.message);
1872
1883
  });
1873
1884
  }
1885
+ frameCount = 0;
1886
+ throttledCount = 0;
1887
+ lastFrameLogTime = 0;
1874
1888
  handleMessage(msg) {
1875
1889
  if (msg.type === "frame") {
1876
1890
  const now = Date.now();
1877
- if (now - this.lastFrameTime < FRAME_THROTTLE_MS) return;
1891
+ if (now - this.lastFrameTime < FRAME_THROTTLE_MS) {
1892
+ this.throttledCount++;
1893
+ return;
1894
+ }
1878
1895
  this.lastFrameTime = now;
1896
+ this.frameCount++;
1897
+ if (now - this.lastFrameLogTime > 5e3) {
1898
+ console.log(`[BROWSER-WS] Frame stats: emitted=${this.frameCount} throttled=${this.throttledCount} listeners=${this.listenerCount("frame")} dataSize=${msg.data?.length ?? 0}`);
1899
+ this.lastFrameLogTime = now;
1900
+ }
1879
1901
  const frame = {
1880
1902
  data: msg.data,
1881
1903
  metadata: msg.metadata ?? {
@@ -1891,21 +1913,26 @@ var init_stream_proxy = __esm({
1891
1913
  this._latestFrame = frame;
1892
1914
  this.emit("frame", frame);
1893
1915
  } else if (msg.type === "status") {
1916
+ console.log(`[BROWSER-WS] Status message received:`, JSON.stringify(msg));
1894
1917
  this.emit("status", {
1895
1918
  connected: msg.connected ?? true,
1896
1919
  screencasting: msg.screencasting ?? true,
1897
1920
  viewportWidth: msg.viewportWidth,
1898
1921
  viewportHeight: msg.viewportHeight
1899
1922
  });
1923
+ } else {
1924
+ console.log(`[BROWSER-WS] Unknown message type: ${msg.type}`);
1900
1925
  }
1901
1926
  }
1902
1927
  scheduleReconnect() {
1903
1928
  if (this.destroyed || this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
1929
+ console.log(`[BROWSER-WS] Giving up reconnection: destroyed=${this.destroyed} attempts=${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}`);
1904
1930
  this.emit("close");
1905
1931
  return;
1906
1932
  }
1907
1933
  this.reconnectAttempts++;
1908
1934
  const delay = this.reconnectAttempts <= 5 ? RECONNECT_DELAY_MS : RECONNECT_DELAY_MS * (this.reconnectAttempts - 4);
1935
+ console.log(`[BROWSER-WS] Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
1909
1936
  this.reconnectTimer = setTimeout(() => this.doConnect(), delay);
1910
1937
  }
1911
1938
  /**
@@ -1916,7 +1943,19 @@ var init_stream_proxy = __esm({
1916
1943
  this.ws.send(JSON.stringify(event));
1917
1944
  }
1918
1945
  }
1946
+ /**
1947
+ * Ask the StreamServer to send its current status (triggers sendStatus and
1948
+ * re-evaluates screencasting). Useful when listeners are replaced on a new
1949
+ * stream and we want a fresh status event.
1950
+ */
1951
+ requestStatus() {
1952
+ if (this.ws?.readyState === WebSocket.OPEN) {
1953
+ console.log(`[BROWSER-WS] Requesting fresh status from StreamServer`);
1954
+ this.ws.send(JSON.stringify({ type: "status" }));
1955
+ }
1956
+ }
1919
1957
  destroy() {
1958
+ console.log(`[BROWSER-WS] Destroying proxy for port ${this.port} (emitted ${this.frameCount} frames, throttled ${this.throttledCount})`);
1920
1959
  this.destroyed = true;
1921
1960
  if (this.reconnectTimer) {
1922
1961
  clearTimeout(this.reconnectTimer);
@@ -8063,11 +8102,19 @@ var cleanupInterval = setInterval(() => {
8063
8102
  }
8064
8103
  }, 6e4);
8065
8104
  cleanupInterval.unref();
8105
+ var publishCount = 0;
8106
+ var lastPublishLog = 0;
8066
8107
  var publisher = {
8067
8108
  connect: async () => {
8068
8109
  },
8069
8110
  publish: async (channel, message) => {
8070
8111
  const subscribers = channels.get(channel);
8112
+ publishCount++;
8113
+ const now = Date.now();
8114
+ if (now - lastPublishLog > 1e4) {
8115
+ console.log(`[ResumableStream] Publish stats: total=${publishCount}, channels=${channels.size}, store=${store.size}`);
8116
+ lastPublishLog = now;
8117
+ }
8071
8118
  if (subscribers) {
8072
8119
  for (const callback of subscribers) {
8073
8120
  setImmediate(() => callback(message));
@@ -8106,9 +8153,12 @@ var subscriber = {
8106
8153
  channels.set(channel, /* @__PURE__ */ new Set());
8107
8154
  }
8108
8155
  channels.get(channel).add(callback);
8156
+ console.log(`[ResumableStream] Subscribe to channel "${channel}" (total subscribers: ${channels.get(channel).size})`);
8109
8157
  },
8110
8158
  unsubscribe: async (channel) => {
8159
+ const count = channels.get(channel)?.size ?? 0;
8111
8160
  channels.delete(channel);
8161
+ console.log(`[ResumableStream] Unsubscribe from channel "${channel}" (removed ${count} subscribers)`);
8112
8162
  }
8113
8163
  };
8114
8164
  var streamContext = createResumableStreamContext({
@@ -8294,19 +8344,28 @@ function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
8294
8344
  const toolCallStarts = /* @__PURE__ */ new Set();
8295
8345
  const abortController = new AbortController();
8296
8346
  streamAbortControllers.set(streamId, abortController);
8347
+ let sseEventCount = 0;
8348
+ let sseBrowserFrameCount = 0;
8349
+ let sseWriteErrors = 0;
8297
8350
  const writeSSE = async (data) => {
8298
8351
  if (writerClosed) return;
8299
8352
  try {
8353
+ sseEventCount++;
8300
8354
  await writer.write(`data: ${data}
8301
8355
 
8302
8356
  `);
8303
8357
  } catch (err) {
8358
+ sseWriteErrors++;
8359
+ if (sseWriteErrors === 1) {
8360
+ console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
8361
+ }
8304
8362
  writerClosed = true;
8305
8363
  }
8306
8364
  };
8307
8365
  const safeClose = async () => {
8308
8366
  if (writerClosed) return;
8309
8367
  try {
8368
+ console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
8310
8369
  writerClosed = true;
8311
8370
  await writer.close();
8312
8371
  } catch {
@@ -8429,36 +8488,51 @@ ${prompt}` });
8429
8488
  const browserPort = progress.data?.browserStreamPort;
8430
8489
  const browserClosed = progress.data?.browserClosed;
8431
8490
  if (progress.toolName === "bash" && browserClosed) {
8432
- console.log(`[BROWSER-STREAM] agent-browser close detected, destroying proxy`);
8491
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${sessionId}`);
8433
8492
  destroyProxy(sessionId);
8434
8493
  } else if (progress.toolName === "bash" && browserPort) {
8435
- console.log(`[BROWSER-STREAM] agent-browser command detected, ensuring proxy on port ${browserPort}`);
8494
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, ensuring proxy on port ${browserPort} for session ${sessionId}`);
8436
8495
  const proxy = getOrCreateProxy(sessionId, browserPort);
8496
+ console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
8437
8497
  if (!sessionRecorders.has(sessionId)) {
8438
8498
  const recorder = new FrameRecorder(sessionId);
8439
8499
  recorder.start();
8440
8500
  sessionRecorders.set(sessionId, recorder);
8441
8501
  }
8442
- if (proxy.listenerCount("frame") === 0) {
8443
- proxy.on("frame", (frame) => {
8444
- const rec = sessionRecorders.get(sessionId);
8445
- rec?.addFrame(frame);
8446
- writeSSE(JSON.stringify({
8447
- type: "browser-frame",
8448
- data: frame.data,
8449
- metadata: frame.metadata
8450
- })).catch(() => {
8451
- });
8502
+ const oldFrameListeners = proxy.listenerCount("frame");
8503
+ if (oldFrameListeners > 0) {
8504
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
8505
+ proxy.removeAllListeners("frame");
8506
+ proxy.removeAllListeners("status");
8507
+ }
8508
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
8509
+ proxy.on("frame", (frame) => {
8510
+ sseBrowserFrameCount++;
8511
+ if (sseBrowserFrameCount === 1) {
8512
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
8513
+ } else if (sseBrowserFrameCount % 50 === 0) {
8514
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
8515
+ }
8516
+ const rec = sessionRecorders.get(sessionId);
8517
+ rec?.addFrame(frame);
8518
+ writeSSE(JSON.stringify({
8519
+ type: "browser-frame",
8520
+ data: frame.data,
8521
+ metadata: frame.metadata
8522
+ })).catch((err) => {
8523
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
8452
8524
  });
8453
- proxy.on("status", (s) => {
8454
- console.log(`[BROWSER-STREAM] Status:`, s);
8455
- writeSSE(JSON.stringify({
8456
- type: "browser-status",
8457
- ...s
8458
- })).catch(() => {
8459
- });
8525
+ });
8526
+ proxy.on("status", (s) => {
8527
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
8528
+ writeSSE(JSON.stringify({
8529
+ type: "browser-status",
8530
+ ...s
8531
+ })).catch((err) => {
8532
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
8460
8533
  });
8461
- }
8534
+ });
8535
+ proxy.requestStatus();
8462
8536
  }
8463
8537
  },
8464
8538
  onStepFinish: async () => {
@@ -8647,14 +8721,17 @@ ${prompt}` });
8647
8721
  }
8648
8722
  await messageQueries.create(id, { role: "user", content: userMessageContent });
8649
8723
  const streamId = `stream_${id}_${nanoid6(10)}`;
8724
+ console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
8650
8725
  await activeStreamQueries.create(id, streamId);
8651
8726
  const stream = await streamContext.resumableStream(
8652
8727
  streamId,
8653
8728
  createAgentStreamProducer(id, prompt, streamId, streamAttachments)
8654
8729
  );
8655
8730
  if (!stream) {
8731
+ console.error(`[STREAM] Failed to create resumable stream ${streamId}`);
8656
8732
  return c.json({ error: "Failed to create stream" }, 500);
8657
8733
  }
8734
+ console.log(`[STREAM] Stream ${streamId} created successfully`);
8658
8735
  const encodedStream = stream.pipeThrough(new TextEncoderStream());
8659
8736
  return new Response(encodedStream, {
8660
8737
  headers: {
@@ -8683,17 +8760,20 @@ agents.get("/:id/watch", async (c) => {
8683
8760
  }
8684
8761
  streamId = activeStream.streamId;
8685
8762
  }
8763
+ console.log(`[STREAM] Watch request for session ${sessionId}, streamId=${streamId}, resumeAt=${resumeAt || "none"}`);
8686
8764
  const stream = await streamContext.resumeExistingStream(
8687
8765
  streamId,
8688
8766
  resumeAt ? parseInt(resumeAt, 10) : void 0
8689
8767
  );
8690
8768
  if (!stream) {
8769
+ console.log(`[STREAM] Watch failed \u2014 stream ${streamId} is no longer active`);
8691
8770
  return c.json({
8692
8771
  error: "Stream is no longer active",
8693
8772
  streamId,
8694
8773
  hint: "The stream may have finished. Check /agents/:id/approvals or start a new run."
8695
8774
  }, 422);
8696
8775
  }
8776
+ console.log(`[STREAM] Client watching stream ${streamId}`);
8697
8777
  const encodedStream = stream.pipeThrough(new TextEncoderStream());
8698
8778
  return new Response(encodedStream, {
8699
8779
  headers: {
@@ -8855,19 +8935,28 @@ agents.post(
8855
8935
  const toolCallStarts = /* @__PURE__ */ new Set();
8856
8936
  const abortController = new AbortController();
8857
8937
  streamAbortControllers.set(streamId, abortController);
8938
+ let sseEventCount = 0;
8939
+ let sseBrowserFrameCount = 0;
8940
+ let sseWriteErrors = 0;
8858
8941
  const writeSSE = async (data) => {
8859
8942
  if (writerClosed) return;
8860
8943
  try {
8944
+ sseEventCount++;
8861
8945
  await writer.write(`data: ${data}
8862
8946
 
8863
8947
  `);
8864
8948
  } catch (err) {
8949
+ sseWriteErrors++;
8950
+ if (sseWriteErrors === 1) {
8951
+ console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
8952
+ }
8865
8953
  writerClosed = true;
8866
8954
  }
8867
8955
  };
8868
8956
  const safeClose = async () => {
8869
8957
  if (writerClosed) return;
8870
8958
  try {
8959
+ console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
8871
8960
  writerClosed = true;
8872
8961
  await writer.close();
8873
8962
  } catch {
@@ -8927,36 +9016,51 @@ agents.post(
8927
9016
  const browserPort = progress.data?.browserStreamPort;
8928
9017
  const browserClosed = progress.data?.browserClosed;
8929
9018
  if (progress.toolName === "bash" && browserClosed) {
8930
- console.log(`[BROWSER-STREAM] agent-browser close detected`);
9019
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${session.id}`);
8931
9020
  destroyProxy(session.id);
8932
9021
  } else if (progress.toolName === "bash" && browserPort) {
8933
- console.log(`[BROWSER-STREAM] agent-browser command detected, port ${browserPort}`);
9022
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, port ${browserPort} for session ${session.id}`);
8934
9023
  const proxy = getOrCreateProxy(session.id, browserPort);
9024
+ console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
8935
9025
  if (!sessionRecorders.has(session.id)) {
8936
9026
  const recorder = new FrameRecorder(session.id);
8937
9027
  recorder.start();
8938
9028
  sessionRecorders.set(session.id, recorder);
8939
9029
  }
8940
- if (proxy.listenerCount("frame") === 0) {
8941
- proxy.on("frame", (frame) => {
8942
- const rec = sessionRecorders.get(session.id);
8943
- rec?.addFrame(frame);
8944
- writeSSE(JSON.stringify({
8945
- type: "browser-frame",
8946
- data: frame.data,
8947
- metadata: frame.metadata
8948
- })).catch(() => {
8949
- });
9030
+ const oldFrameListeners = proxy.listenerCount("frame");
9031
+ if (oldFrameListeners > 0) {
9032
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
9033
+ proxy.removeAllListeners("frame");
9034
+ proxy.removeAllListeners("status");
9035
+ }
9036
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
9037
+ proxy.on("frame", (frame) => {
9038
+ sseBrowserFrameCount++;
9039
+ if (sseBrowserFrameCount === 1) {
9040
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
9041
+ } else if (sseBrowserFrameCount % 50 === 0) {
9042
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
9043
+ }
9044
+ const rec = sessionRecorders.get(session.id);
9045
+ rec?.addFrame(frame);
9046
+ writeSSE(JSON.stringify({
9047
+ type: "browser-frame",
9048
+ data: frame.data,
9049
+ metadata: frame.metadata
9050
+ })).catch((err) => {
9051
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
8950
9052
  });
8951
- proxy.on("status", (s) => {
8952
- console.log(`[BROWSER-STREAM] Status:`, s);
8953
- writeSSE(JSON.stringify({
8954
- type: "browser-status",
8955
- ...s
8956
- })).catch(() => {
8957
- });
9053
+ });
9054
+ proxy.on("status", (s) => {
9055
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
9056
+ writeSSE(JSON.stringify({
9057
+ type: "browser-status",
9058
+ ...s
9059
+ })).catch((err) => {
9060
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
8958
9061
  });
8959
- }
9062
+ });
9063
+ proxy.requestStatus();
8960
9064
  }
8961
9065
  },
8962
9066
  onStepFinish: async () => {