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
package/dist/index.js CHANGED
@@ -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);
@@ -8078,11 +8117,19 @@ var cleanupInterval = setInterval(() => {
8078
8117
  }
8079
8118
  }, 6e4);
8080
8119
  cleanupInterval.unref();
8120
+ var publishCount = 0;
8121
+ var lastPublishLog = 0;
8081
8122
  var publisher = {
8082
8123
  connect: async () => {
8083
8124
  },
8084
8125
  publish: async (channel, message) => {
8085
8126
  const subscribers = channels.get(channel);
8127
+ publishCount++;
8128
+ const now = Date.now();
8129
+ if (now - lastPublishLog > 1e4) {
8130
+ console.log(`[ResumableStream] Publish stats: total=${publishCount}, channels=${channels.size}, store=${store.size}`);
8131
+ lastPublishLog = now;
8132
+ }
8086
8133
  if (subscribers) {
8087
8134
  for (const callback of subscribers) {
8088
8135
  setImmediate(() => callback(message));
@@ -8121,9 +8168,12 @@ var subscriber = {
8121
8168
  channels.set(channel, /* @__PURE__ */ new Set());
8122
8169
  }
8123
8170
  channels.get(channel).add(callback);
8171
+ console.log(`[ResumableStream] Subscribe to channel "${channel}" (total subscribers: ${channels.get(channel).size})`);
8124
8172
  },
8125
8173
  unsubscribe: async (channel) => {
8174
+ const count = channels.get(channel)?.size ?? 0;
8126
8175
  channels.delete(channel);
8176
+ console.log(`[ResumableStream] Unsubscribe from channel "${channel}" (removed ${count} subscribers)`);
8127
8177
  }
8128
8178
  };
8129
8179
  var streamContext = createResumableStreamContext({
@@ -8309,19 +8359,28 @@ function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
8309
8359
  const toolCallStarts = /* @__PURE__ */ new Set();
8310
8360
  const abortController = new AbortController();
8311
8361
  streamAbortControllers.set(streamId, abortController);
8362
+ let sseEventCount = 0;
8363
+ let sseBrowserFrameCount = 0;
8364
+ let sseWriteErrors = 0;
8312
8365
  const writeSSE = async (data) => {
8313
8366
  if (writerClosed) return;
8314
8367
  try {
8368
+ sseEventCount++;
8315
8369
  await writer.write(`data: ${data}
8316
8370
 
8317
8371
  `);
8318
8372
  } catch (err) {
8373
+ sseWriteErrors++;
8374
+ if (sseWriteErrors === 1) {
8375
+ console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
8376
+ }
8319
8377
  writerClosed = true;
8320
8378
  }
8321
8379
  };
8322
8380
  const safeClose = async () => {
8323
8381
  if (writerClosed) return;
8324
8382
  try {
8383
+ console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
8325
8384
  writerClosed = true;
8326
8385
  await writer.close();
8327
8386
  } catch {
@@ -8444,36 +8503,51 @@ ${prompt}` });
8444
8503
  const browserPort = progress.data?.browserStreamPort;
8445
8504
  const browserClosed = progress.data?.browserClosed;
8446
8505
  if (progress.toolName === "bash" && browserClosed) {
8447
- console.log(`[BROWSER-STREAM] agent-browser close detected, destroying proxy`);
8506
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${sessionId}`);
8448
8507
  destroyProxy(sessionId);
8449
8508
  } else if (progress.toolName === "bash" && browserPort) {
8450
- console.log(`[BROWSER-STREAM] agent-browser command detected, ensuring proxy on port ${browserPort}`);
8509
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, ensuring proxy on port ${browserPort} for session ${sessionId}`);
8451
8510
  const proxy = getOrCreateProxy(sessionId, browserPort);
8511
+ console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
8452
8512
  if (!sessionRecorders.has(sessionId)) {
8453
8513
  const recorder = new FrameRecorder(sessionId);
8454
8514
  recorder.start();
8455
8515
  sessionRecorders.set(sessionId, recorder);
8456
8516
  }
8457
- if (proxy.listenerCount("frame") === 0) {
8458
- proxy.on("frame", (frame) => {
8459
- const rec = sessionRecorders.get(sessionId);
8460
- rec?.addFrame(frame);
8461
- writeSSE(JSON.stringify({
8462
- type: "browser-frame",
8463
- data: frame.data,
8464
- metadata: frame.metadata
8465
- })).catch(() => {
8466
- });
8517
+ const oldFrameListeners = proxy.listenerCount("frame");
8518
+ if (oldFrameListeners > 0) {
8519
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
8520
+ proxy.removeAllListeners("frame");
8521
+ proxy.removeAllListeners("status");
8522
+ }
8523
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
8524
+ proxy.on("frame", (frame) => {
8525
+ sseBrowserFrameCount++;
8526
+ if (sseBrowserFrameCount === 1) {
8527
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
8528
+ } else if (sseBrowserFrameCount % 50 === 0) {
8529
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
8530
+ }
8531
+ const rec = sessionRecorders.get(sessionId);
8532
+ rec?.addFrame(frame);
8533
+ writeSSE(JSON.stringify({
8534
+ type: "browser-frame",
8535
+ data: frame.data,
8536
+ metadata: frame.metadata
8537
+ })).catch((err) => {
8538
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
8467
8539
  });
8468
- proxy.on("status", (s) => {
8469
- console.log(`[BROWSER-STREAM] Status:`, s);
8470
- writeSSE(JSON.stringify({
8471
- type: "browser-status",
8472
- ...s
8473
- })).catch(() => {
8474
- });
8540
+ });
8541
+ proxy.on("status", (s) => {
8542
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
8543
+ writeSSE(JSON.stringify({
8544
+ type: "browser-status",
8545
+ ...s
8546
+ })).catch((err) => {
8547
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
8475
8548
  });
8476
- }
8549
+ });
8550
+ proxy.requestStatus();
8477
8551
  }
8478
8552
  },
8479
8553
  onStepFinish: async () => {
@@ -8662,14 +8736,17 @@ ${prompt}` });
8662
8736
  }
8663
8737
  await messageQueries.create(id, { role: "user", content: userMessageContent });
8664
8738
  const streamId = `stream_${id}_${nanoid6(10)}`;
8739
+ console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
8665
8740
  await activeStreamQueries.create(id, streamId);
8666
8741
  const stream = await streamContext.resumableStream(
8667
8742
  streamId,
8668
8743
  createAgentStreamProducer(id, prompt, streamId, streamAttachments)
8669
8744
  );
8670
8745
  if (!stream) {
8746
+ console.error(`[STREAM] Failed to create resumable stream ${streamId}`);
8671
8747
  return c.json({ error: "Failed to create stream" }, 500);
8672
8748
  }
8749
+ console.log(`[STREAM] Stream ${streamId} created successfully`);
8673
8750
  const encodedStream = stream.pipeThrough(new TextEncoderStream());
8674
8751
  return new Response(encodedStream, {
8675
8752
  headers: {
@@ -8698,17 +8775,20 @@ agents.get("/:id/watch", async (c) => {
8698
8775
  }
8699
8776
  streamId = activeStream.streamId;
8700
8777
  }
8778
+ console.log(`[STREAM] Watch request for session ${sessionId}, streamId=${streamId}, resumeAt=${resumeAt || "none"}`);
8701
8779
  const stream = await streamContext.resumeExistingStream(
8702
8780
  streamId,
8703
8781
  resumeAt ? parseInt(resumeAt, 10) : void 0
8704
8782
  );
8705
8783
  if (!stream) {
8784
+ console.log(`[STREAM] Watch failed \u2014 stream ${streamId} is no longer active`);
8706
8785
  return c.json({
8707
8786
  error: "Stream is no longer active",
8708
8787
  streamId,
8709
8788
  hint: "The stream may have finished. Check /agents/:id/approvals or start a new run."
8710
8789
  }, 422);
8711
8790
  }
8791
+ console.log(`[STREAM] Client watching stream ${streamId}`);
8712
8792
  const encodedStream = stream.pipeThrough(new TextEncoderStream());
8713
8793
  return new Response(encodedStream, {
8714
8794
  headers: {
@@ -8870,19 +8950,28 @@ agents.post(
8870
8950
  const toolCallStarts = /* @__PURE__ */ new Set();
8871
8951
  const abortController = new AbortController();
8872
8952
  streamAbortControllers.set(streamId, abortController);
8953
+ let sseEventCount = 0;
8954
+ let sseBrowserFrameCount = 0;
8955
+ let sseWriteErrors = 0;
8873
8956
  const writeSSE = async (data) => {
8874
8957
  if (writerClosed) return;
8875
8958
  try {
8959
+ sseEventCount++;
8876
8960
  await writer.write(`data: ${data}
8877
8961
 
8878
8962
  `);
8879
8963
  } catch (err) {
8964
+ sseWriteErrors++;
8965
+ if (sseWriteErrors === 1) {
8966
+ console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
8967
+ }
8880
8968
  writerClosed = true;
8881
8969
  }
8882
8970
  };
8883
8971
  const safeClose = async () => {
8884
8972
  if (writerClosed) return;
8885
8973
  try {
8974
+ console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
8886
8975
  writerClosed = true;
8887
8976
  await writer.close();
8888
8977
  } catch {
@@ -8942,36 +9031,51 @@ agents.post(
8942
9031
  const browserPort = progress.data?.browserStreamPort;
8943
9032
  const browserClosed = progress.data?.browserClosed;
8944
9033
  if (progress.toolName === "bash" && browserClosed) {
8945
- console.log(`[BROWSER-STREAM] agent-browser close detected`);
9034
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${session.id}`);
8946
9035
  destroyProxy(session.id);
8947
9036
  } else if (progress.toolName === "bash" && browserPort) {
8948
- console.log(`[BROWSER-STREAM] agent-browser command detected, port ${browserPort}`);
9037
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, port ${browserPort} for session ${session.id}`);
8949
9038
  const proxy = getOrCreateProxy(session.id, browserPort);
9039
+ console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
8950
9040
  if (!sessionRecorders.has(session.id)) {
8951
9041
  const recorder = new FrameRecorder(session.id);
8952
9042
  recorder.start();
8953
9043
  sessionRecorders.set(session.id, recorder);
8954
9044
  }
8955
- if (proxy.listenerCount("frame") === 0) {
8956
- proxy.on("frame", (frame) => {
8957
- const rec = sessionRecorders.get(session.id);
8958
- rec?.addFrame(frame);
8959
- writeSSE(JSON.stringify({
8960
- type: "browser-frame",
8961
- data: frame.data,
8962
- metadata: frame.metadata
8963
- })).catch(() => {
8964
- });
9045
+ const oldFrameListeners = proxy.listenerCount("frame");
9046
+ if (oldFrameListeners > 0) {
9047
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
9048
+ proxy.removeAllListeners("frame");
9049
+ proxy.removeAllListeners("status");
9050
+ }
9051
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
9052
+ proxy.on("frame", (frame) => {
9053
+ sseBrowserFrameCount++;
9054
+ if (sseBrowserFrameCount === 1) {
9055
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
9056
+ } else if (sseBrowserFrameCount % 50 === 0) {
9057
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
9058
+ }
9059
+ const rec = sessionRecorders.get(session.id);
9060
+ rec?.addFrame(frame);
9061
+ writeSSE(JSON.stringify({
9062
+ type: "browser-frame",
9063
+ data: frame.data,
9064
+ metadata: frame.metadata
9065
+ })).catch((err) => {
9066
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
8965
9067
  });
8966
- proxy.on("status", (s) => {
8967
- console.log(`[BROWSER-STREAM] Status:`, s);
8968
- writeSSE(JSON.stringify({
8969
- type: "browser-status",
8970
- ...s
8971
- })).catch(() => {
8972
- });
9068
+ });
9069
+ proxy.on("status", (s) => {
9070
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
9071
+ writeSSE(JSON.stringify({
9072
+ type: "browser-status",
9073
+ ...s
9074
+ })).catch((err) => {
9075
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
8973
9076
  });
8974
- }
9077
+ });
9078
+ proxy.requestStatus();
8975
9079
  }
8976
9080
  },
8977
9081
  onStepFinish: async () => {