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.
- package/dist/agent/index.js +49 -10
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +154 -50
- package/dist/cli.js.map +1 -1
- package/dist/index.js +154 -50
- package/dist/index.js.map +1 -1
- package/dist/server/index.js +154 -50
- package/dist/server/index.js.map +1 -1
- package/dist/skills/default/qa.md +376 -106
- package/package.json +1 -1
- package/src/skills/default/qa.md +376 -106
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__c71f29f9._.js → [root-of-the-server]__a1877334._.js} +4 -4
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_2b3a5919._.js +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_38156da8._.js +1 -1
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/{static/chunks/4e673433173ad456.js → standalone/web/.next/static/chunks/2cafc7cb79454d33.js} +3 -3
- package/web/.next/standalone/web/.next/static/chunks/41a5c049931b2c77.css +1 -0
- package/web/.next/standalone/web/.next/static/chunks/{515f0c0bd6087843.js → f95d41079838994a.js} +3 -3
- package/web/.next/standalone/web/.next/static/chunks/{31208ade542a0fcb.js → fc39a194539da104.js} +3 -3
- package/web/.next/standalone/web/.next/static/{chunks/4e673433173ad456.js → static/chunks/2cafc7cb79454d33.js} +3 -3
- package/web/.next/standalone/web/.next/static/static/chunks/41a5c049931b2c77.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/{515f0c0bd6087843.js → f95d41079838994a.js} +3 -3
- package/web/.next/{static/chunks/31208ade542a0fcb.js → standalone/web/.next/static/static/chunks/fc39a194539da104.js} +3 -3
- package/web/.next/standalone/web/src/components/ai-elements/read-file-tool.tsx +19 -2
- package/web/.next/standalone/web/src/components/chat-interface.tsx +63 -4
- package/web/.next/standalone/web/src/lib/api.ts +89 -16
- package/web/.next/{standalone/web/.next/static/static/chunks/4e673433173ad456.js → static/chunks/2cafc7cb79454d33.js} +3 -3
- package/web/.next/static/chunks/41a5c049931b2c77.css +1 -0
- package/web/.next/static/chunks/{515f0c0bd6087843.js → f95d41079838994a.js} +3 -3
- package/web/.next/{standalone/web/.next/static/static/chunks/31208ade542a0fcb.js → static/chunks/fc39a194539da104.js} +3 -3
- package/web/.next/standalone/web/.next/static/chunks/fd39dd62879495e1.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/fd39dd62879495e1.css +0 -1
- package/web/.next/static/chunks/fd39dd62879495e1.css +0 -1
- /package/web/.next/standalone/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
- /package/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
- /package/web/.next/static/{6Dlxqhgk8Mki7q7L-gDbl → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
- /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)
|
|
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)
|
|
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
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
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
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
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
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
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
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
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 () => {
|