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/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)
|
|
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)
|
|
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
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
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
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
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
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
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
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
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 () => {
|