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/cli.js CHANGED
@@ -2556,10 +2556,15 @@ import WebSocket from "ws";
2556
2556
  import { EventEmitter } from "events";
2557
2557
  function getOrCreateProxy(sessionId, port) {
2558
2558
  const existing = activeProxies.get(sessionId);
2559
- if (existing) return existing;
2559
+ if (existing) {
2560
+ console.log(`[BROWSER-WS] Reusing existing proxy for session ${sessionId} (connected=${existing.connected})`);
2561
+ return existing;
2562
+ }
2563
+ console.log(`[BROWSER-WS] Creating new proxy for session ${sessionId} on port ${port} (active proxies: ${activeProxies.size})`);
2560
2564
  const proxy = new BrowserStreamProxy(port);
2561
2565
  activeProxies.set(sessionId, proxy);
2562
2566
  proxy.on("close", () => {
2567
+ console.log(`[BROWSER-WS] Proxy closed for session ${sessionId}, removing from registry`);
2563
2568
  activeProxies.delete(sessionId);
2564
2569
  });
2565
2570
  proxy.connect();
@@ -2571,8 +2576,11 @@ function getProxy(sessionId) {
2571
2576
  function destroyProxy(sessionId) {
2572
2577
  const proxy = activeProxies.get(sessionId);
2573
2578
  if (proxy) {
2579
+ console.log(`[BROWSER-WS] destroyProxy() called for session ${sessionId}`);
2574
2580
  proxy.destroy();
2575
2581
  activeProxies.delete(sessionId);
2582
+ } else {
2583
+ console.log(`[BROWSER-WS] destroyProxy() called but no proxy exists for session ${sessionId}`);
2576
2584
  }
2577
2585
  }
2578
2586
  var RECONNECT_DELAY_MS, MAX_RECONNECT_ATTEMPTS, FRAME_THROTTLE_MS, BrowserStreamProxy, activeProxies;
@@ -2603,35 +2611,37 @@ var init_stream_proxy = __esm({
2603
2611
  }
2604
2612
  connect() {
2605
2613
  if (this.destroyed) return;
2614
+ console.log(`[BROWSER-WS] connect() called for port ${this.port}`);
2606
2615
  this.doConnect();
2607
2616
  }
2608
2617
  doConnect() {
2609
2618
  if (this.destroyed) return;
2610
2619
  const url = `ws://localhost:${this.port}`;
2620
+ console.log(`[BROWSER-WS] Attempting WebSocket connection to ${url} (attempt ${this.reconnectAttempts + 1}/${MAX_RECONNECT_ATTEMPTS})`);
2611
2621
  try {
2612
2622
  this.ws = new WebSocket(url);
2613
- } catch {
2623
+ } catch (err) {
2624
+ console.warn(`[BROWSER-WS] WebSocket constructor threw for ${url}:`, err);
2614
2625
  this.scheduleReconnect();
2615
2626
  return;
2616
2627
  }
2617
2628
  this.ws.on("open", () => {
2629
+ console.log(`[BROWSER-WS] Connected to ${url} (after ${this.reconnectAttempts} retries)`);
2618
2630
  this.reconnectAttempts = 0;
2619
2631
  this._connected = true;
2620
- this.emit("status", {
2621
- connected: true,
2622
- screencasting: true
2623
- });
2624
2632
  });
2625
2633
  this.ws.on("message", (raw) => {
2626
2634
  try {
2627
2635
  const msg = JSON.parse(typeof raw === "string" ? raw : raw.toString("utf8"));
2628
2636
  this.handleMessage(msg);
2629
- } catch {
2637
+ } catch (err) {
2638
+ console.warn(`[BROWSER-WS] Malformed message from ${url}:`, err);
2630
2639
  }
2631
2640
  });
2632
- this.ws.on("close", () => {
2641
+ this.ws.on("close", (code, reason) => {
2633
2642
  const wasConnected = this._connected;
2634
2643
  this._connected = false;
2644
+ console.log(`[BROWSER-WS] Connection closed: code=${code} reason="${reason?.toString() || ""}" wasConnected=${wasConnected} destroyed=${this.destroyed}`);
2635
2645
  if (wasConnected) {
2636
2646
  this.emit("status", { connected: false, screencasting: false });
2637
2647
  }
@@ -2639,14 +2649,26 @@ var init_stream_proxy = __esm({
2639
2649
  this.scheduleReconnect();
2640
2650
  }
2641
2651
  });
2642
- this.ws.on("error", () => {
2652
+ this.ws.on("error", (err) => {
2653
+ console.warn(`[BROWSER-WS] WebSocket error on port ${this.port}:`, err.message);
2643
2654
  });
2644
2655
  }
2656
+ frameCount = 0;
2657
+ throttledCount = 0;
2658
+ lastFrameLogTime = 0;
2645
2659
  handleMessage(msg) {
2646
2660
  if (msg.type === "frame") {
2647
2661
  const now = Date.now();
2648
- if (now - this.lastFrameTime < FRAME_THROTTLE_MS) return;
2662
+ if (now - this.lastFrameTime < FRAME_THROTTLE_MS) {
2663
+ this.throttledCount++;
2664
+ return;
2665
+ }
2649
2666
  this.lastFrameTime = now;
2667
+ this.frameCount++;
2668
+ if (now - this.lastFrameLogTime > 5e3) {
2669
+ console.log(`[BROWSER-WS] Frame stats: emitted=${this.frameCount} throttled=${this.throttledCount} listeners=${this.listenerCount("frame")} dataSize=${msg.data?.length ?? 0}`);
2670
+ this.lastFrameLogTime = now;
2671
+ }
2650
2672
  const frame = {
2651
2673
  data: msg.data,
2652
2674
  metadata: msg.metadata ?? {
@@ -2662,21 +2684,26 @@ var init_stream_proxy = __esm({
2662
2684
  this._latestFrame = frame;
2663
2685
  this.emit("frame", frame);
2664
2686
  } else if (msg.type === "status") {
2687
+ console.log(`[BROWSER-WS] Status message received:`, JSON.stringify(msg));
2665
2688
  this.emit("status", {
2666
2689
  connected: msg.connected ?? true,
2667
2690
  screencasting: msg.screencasting ?? true,
2668
2691
  viewportWidth: msg.viewportWidth,
2669
2692
  viewportHeight: msg.viewportHeight
2670
2693
  });
2694
+ } else {
2695
+ console.log(`[BROWSER-WS] Unknown message type: ${msg.type}`);
2671
2696
  }
2672
2697
  }
2673
2698
  scheduleReconnect() {
2674
2699
  if (this.destroyed || this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
2700
+ console.log(`[BROWSER-WS] Giving up reconnection: destroyed=${this.destroyed} attempts=${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}`);
2675
2701
  this.emit("close");
2676
2702
  return;
2677
2703
  }
2678
2704
  this.reconnectAttempts++;
2679
2705
  const delay = this.reconnectAttempts <= 5 ? RECONNECT_DELAY_MS : RECONNECT_DELAY_MS * (this.reconnectAttempts - 4);
2706
+ console.log(`[BROWSER-WS] Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
2680
2707
  this.reconnectTimer = setTimeout(() => this.doConnect(), delay);
2681
2708
  }
2682
2709
  /**
@@ -2687,7 +2714,19 @@ var init_stream_proxy = __esm({
2687
2714
  this.ws.send(JSON.stringify(event));
2688
2715
  }
2689
2716
  }
2717
+ /**
2718
+ * Ask the StreamServer to send its current status (triggers sendStatus and
2719
+ * re-evaluates screencasting). Useful when listeners are replaced on a new
2720
+ * stream and we want a fresh status event.
2721
+ */
2722
+ requestStatus() {
2723
+ if (this.ws?.readyState === WebSocket.OPEN) {
2724
+ console.log(`[BROWSER-WS] Requesting fresh status from StreamServer`);
2725
+ this.ws.send(JSON.stringify({ type: "status" }));
2726
+ }
2727
+ }
2690
2728
  destroy() {
2729
+ console.log(`[BROWSER-WS] Destroying proxy for port ${this.port} (emitted ${this.frameCount} frames, throttled ${this.throttledCount})`);
2691
2730
  this.destroyed = true;
2692
2731
  if (this.reconnectTimer) {
2693
2732
  clearTimeout(this.reconnectTimer);
@@ -8841,11 +8880,19 @@ var cleanupInterval = setInterval(() => {
8841
8880
  }
8842
8881
  }, 6e4);
8843
8882
  cleanupInterval.unref();
8883
+ var publishCount = 0;
8884
+ var lastPublishLog = 0;
8844
8885
  var publisher = {
8845
8886
  connect: async () => {
8846
8887
  },
8847
8888
  publish: async (channel, message) => {
8848
8889
  const subscribers = channels.get(channel);
8890
+ publishCount++;
8891
+ const now = Date.now();
8892
+ if (now - lastPublishLog > 1e4) {
8893
+ console.log(`[ResumableStream] Publish stats: total=${publishCount}, channels=${channels.size}, store=${store.size}`);
8894
+ lastPublishLog = now;
8895
+ }
8849
8896
  if (subscribers) {
8850
8897
  for (const callback of subscribers) {
8851
8898
  setImmediate(() => callback(message));
@@ -8884,9 +8931,12 @@ var subscriber = {
8884
8931
  channels.set(channel, /* @__PURE__ */ new Set());
8885
8932
  }
8886
8933
  channels.get(channel).add(callback);
8934
+ console.log(`[ResumableStream] Subscribe to channel "${channel}" (total subscribers: ${channels.get(channel).size})`);
8887
8935
  },
8888
8936
  unsubscribe: async (channel) => {
8937
+ const count = channels.get(channel)?.size ?? 0;
8889
8938
  channels.delete(channel);
8939
+ console.log(`[ResumableStream] Unsubscribe from channel "${channel}" (removed ${count} subscribers)`);
8890
8940
  }
8891
8941
  };
8892
8942
  var streamContext = createResumableStreamContext({
@@ -9072,19 +9122,28 @@ function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
9072
9122
  const toolCallStarts = /* @__PURE__ */ new Set();
9073
9123
  const abortController = new AbortController();
9074
9124
  streamAbortControllers.set(streamId, abortController);
9125
+ let sseEventCount = 0;
9126
+ let sseBrowserFrameCount = 0;
9127
+ let sseWriteErrors = 0;
9075
9128
  const writeSSE = async (data) => {
9076
9129
  if (writerClosed) return;
9077
9130
  try {
9131
+ sseEventCount++;
9078
9132
  await writer.write(`data: ${data}
9079
9133
 
9080
9134
  `);
9081
9135
  } catch (err) {
9136
+ sseWriteErrors++;
9137
+ if (sseWriteErrors === 1) {
9138
+ console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
9139
+ }
9082
9140
  writerClosed = true;
9083
9141
  }
9084
9142
  };
9085
9143
  const safeClose = async () => {
9086
9144
  if (writerClosed) return;
9087
9145
  try {
9146
+ console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
9088
9147
  writerClosed = true;
9089
9148
  await writer.close();
9090
9149
  } catch {
@@ -9207,36 +9266,51 @@ ${prompt}` });
9207
9266
  const browserPort = progress.data?.browserStreamPort;
9208
9267
  const browserClosed = progress.data?.browserClosed;
9209
9268
  if (progress.toolName === "bash" && browserClosed) {
9210
- console.log(`[BROWSER-STREAM] agent-browser close detected, destroying proxy`);
9269
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${sessionId}`);
9211
9270
  destroyProxy(sessionId);
9212
9271
  } else if (progress.toolName === "bash" && browserPort) {
9213
- console.log(`[BROWSER-STREAM] agent-browser command detected, ensuring proxy on port ${browserPort}`);
9272
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, ensuring proxy on port ${browserPort} for session ${sessionId}`);
9214
9273
  const proxy = getOrCreateProxy(sessionId, browserPort);
9274
+ console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
9215
9275
  if (!sessionRecorders.has(sessionId)) {
9216
9276
  const recorder = new FrameRecorder(sessionId);
9217
9277
  recorder.start();
9218
9278
  sessionRecorders.set(sessionId, recorder);
9219
9279
  }
9220
- if (proxy.listenerCount("frame") === 0) {
9221
- proxy.on("frame", (frame) => {
9222
- const rec = sessionRecorders.get(sessionId);
9223
- rec?.addFrame(frame);
9224
- writeSSE(JSON.stringify({
9225
- type: "browser-frame",
9226
- data: frame.data,
9227
- metadata: frame.metadata
9228
- })).catch(() => {
9229
- });
9280
+ const oldFrameListeners = proxy.listenerCount("frame");
9281
+ if (oldFrameListeners > 0) {
9282
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
9283
+ proxy.removeAllListeners("frame");
9284
+ proxy.removeAllListeners("status");
9285
+ }
9286
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
9287
+ proxy.on("frame", (frame) => {
9288
+ sseBrowserFrameCount++;
9289
+ if (sseBrowserFrameCount === 1) {
9290
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
9291
+ } else if (sseBrowserFrameCount % 50 === 0) {
9292
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
9293
+ }
9294
+ const rec = sessionRecorders.get(sessionId);
9295
+ rec?.addFrame(frame);
9296
+ writeSSE(JSON.stringify({
9297
+ type: "browser-frame",
9298
+ data: frame.data,
9299
+ metadata: frame.metadata
9300
+ })).catch((err) => {
9301
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
9230
9302
  });
9231
- proxy.on("status", (s) => {
9232
- console.log(`[BROWSER-STREAM] Status:`, s);
9233
- writeSSE(JSON.stringify({
9234
- type: "browser-status",
9235
- ...s
9236
- })).catch(() => {
9237
- });
9303
+ });
9304
+ proxy.on("status", (s) => {
9305
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
9306
+ writeSSE(JSON.stringify({
9307
+ type: "browser-status",
9308
+ ...s
9309
+ })).catch((err) => {
9310
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
9238
9311
  });
9239
- }
9312
+ });
9313
+ proxy.requestStatus();
9240
9314
  }
9241
9315
  },
9242
9316
  onStepFinish: async () => {
@@ -9425,14 +9499,17 @@ ${prompt}` });
9425
9499
  }
9426
9500
  await messageQueries.create(id, { role: "user", content: userMessageContent });
9427
9501
  const streamId = `stream_${id}_${nanoid6(10)}`;
9502
+ console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
9428
9503
  await activeStreamQueries.create(id, streamId);
9429
9504
  const stream = await streamContext.resumableStream(
9430
9505
  streamId,
9431
9506
  createAgentStreamProducer(id, prompt, streamId, streamAttachments)
9432
9507
  );
9433
9508
  if (!stream) {
9509
+ console.error(`[STREAM] Failed to create resumable stream ${streamId}`);
9434
9510
  return c.json({ error: "Failed to create stream" }, 500);
9435
9511
  }
9512
+ console.log(`[STREAM] Stream ${streamId} created successfully`);
9436
9513
  const encodedStream = stream.pipeThrough(new TextEncoderStream());
9437
9514
  return new Response(encodedStream, {
9438
9515
  headers: {
@@ -9461,17 +9538,20 @@ agents.get("/:id/watch", async (c) => {
9461
9538
  }
9462
9539
  streamId = activeStream.streamId;
9463
9540
  }
9541
+ console.log(`[STREAM] Watch request for session ${sessionId}, streamId=${streamId}, resumeAt=${resumeAt || "none"}`);
9464
9542
  const stream = await streamContext.resumeExistingStream(
9465
9543
  streamId,
9466
9544
  resumeAt ? parseInt(resumeAt, 10) : void 0
9467
9545
  );
9468
9546
  if (!stream) {
9547
+ console.log(`[STREAM] Watch failed \u2014 stream ${streamId} is no longer active`);
9469
9548
  return c.json({
9470
9549
  error: "Stream is no longer active",
9471
9550
  streamId,
9472
9551
  hint: "The stream may have finished. Check /agents/:id/approvals or start a new run."
9473
9552
  }, 422);
9474
9553
  }
9554
+ console.log(`[STREAM] Client watching stream ${streamId}`);
9475
9555
  const encodedStream = stream.pipeThrough(new TextEncoderStream());
9476
9556
  return new Response(encodedStream, {
9477
9557
  headers: {
@@ -9633,19 +9713,28 @@ agents.post(
9633
9713
  const toolCallStarts = /* @__PURE__ */ new Set();
9634
9714
  const abortController = new AbortController();
9635
9715
  streamAbortControllers.set(streamId, abortController);
9716
+ let sseEventCount = 0;
9717
+ let sseBrowserFrameCount = 0;
9718
+ let sseWriteErrors = 0;
9636
9719
  const writeSSE = async (data) => {
9637
9720
  if (writerClosed) return;
9638
9721
  try {
9722
+ sseEventCount++;
9639
9723
  await writer.write(`data: ${data}
9640
9724
 
9641
9725
  `);
9642
9726
  } catch (err) {
9727
+ sseWriteErrors++;
9728
+ if (sseWriteErrors === 1) {
9729
+ console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
9730
+ }
9643
9731
  writerClosed = true;
9644
9732
  }
9645
9733
  };
9646
9734
  const safeClose = async () => {
9647
9735
  if (writerClosed) return;
9648
9736
  try {
9737
+ console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
9649
9738
  writerClosed = true;
9650
9739
  await writer.close();
9651
9740
  } catch {
@@ -9705,36 +9794,51 @@ agents.post(
9705
9794
  const browserPort = progress.data?.browserStreamPort;
9706
9795
  const browserClosed = progress.data?.browserClosed;
9707
9796
  if (progress.toolName === "bash" && browserClosed) {
9708
- console.log(`[BROWSER-STREAM] agent-browser close detected`);
9797
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${session.id}`);
9709
9798
  destroyProxy(session.id);
9710
9799
  } else if (progress.toolName === "bash" && browserPort) {
9711
- console.log(`[BROWSER-STREAM] agent-browser command detected, port ${browserPort}`);
9800
+ console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, port ${browserPort} for session ${session.id}`);
9712
9801
  const proxy = getOrCreateProxy(session.id, browserPort);
9802
+ console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
9713
9803
  if (!sessionRecorders.has(session.id)) {
9714
9804
  const recorder = new FrameRecorder(session.id);
9715
9805
  recorder.start();
9716
9806
  sessionRecorders.set(session.id, recorder);
9717
9807
  }
9718
- if (proxy.listenerCount("frame") === 0) {
9719
- proxy.on("frame", (frame) => {
9720
- const rec = sessionRecorders.get(session.id);
9721
- rec?.addFrame(frame);
9722
- writeSSE(JSON.stringify({
9723
- type: "browser-frame",
9724
- data: frame.data,
9725
- metadata: frame.metadata
9726
- })).catch(() => {
9727
- });
9808
+ const oldFrameListeners = proxy.listenerCount("frame");
9809
+ if (oldFrameListeners > 0) {
9810
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
9811
+ proxy.removeAllListeners("frame");
9812
+ proxy.removeAllListeners("status");
9813
+ }
9814
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
9815
+ proxy.on("frame", (frame) => {
9816
+ sseBrowserFrameCount++;
9817
+ if (sseBrowserFrameCount === 1) {
9818
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
9819
+ } else if (sseBrowserFrameCount % 50 === 0) {
9820
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
9821
+ }
9822
+ const rec = sessionRecorders.get(session.id);
9823
+ rec?.addFrame(frame);
9824
+ writeSSE(JSON.stringify({
9825
+ type: "browser-frame",
9826
+ data: frame.data,
9827
+ metadata: frame.metadata
9828
+ })).catch((err) => {
9829
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
9728
9830
  });
9729
- proxy.on("status", (s) => {
9730
- console.log(`[BROWSER-STREAM] Status:`, s);
9731
- writeSSE(JSON.stringify({
9732
- type: "browser-status",
9733
- ...s
9734
- })).catch(() => {
9735
- });
9831
+ });
9832
+ proxy.on("status", (s) => {
9833
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
9834
+ writeSSE(JSON.stringify({
9835
+ type: "browser-status",
9836
+ ...s
9837
+ })).catch((err) => {
9838
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
9736
9839
  });
9737
- }
9840
+ });
9841
+ proxy.requestStatus();
9738
9842
  }
9739
9843
  },
9740
9844
  onStepFinish: async () => {