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/server/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);
|
|
@@ -8063,11 +8102,19 @@ var cleanupInterval = setInterval(() => {
|
|
|
8063
8102
|
}
|
|
8064
8103
|
}, 6e4);
|
|
8065
8104
|
cleanupInterval.unref();
|
|
8105
|
+
var publishCount = 0;
|
|
8106
|
+
var lastPublishLog = 0;
|
|
8066
8107
|
var publisher = {
|
|
8067
8108
|
connect: async () => {
|
|
8068
8109
|
},
|
|
8069
8110
|
publish: async (channel, message) => {
|
|
8070
8111
|
const subscribers = channels.get(channel);
|
|
8112
|
+
publishCount++;
|
|
8113
|
+
const now = Date.now();
|
|
8114
|
+
if (now - lastPublishLog > 1e4) {
|
|
8115
|
+
console.log(`[ResumableStream] Publish stats: total=${publishCount}, channels=${channels.size}, store=${store.size}`);
|
|
8116
|
+
lastPublishLog = now;
|
|
8117
|
+
}
|
|
8071
8118
|
if (subscribers) {
|
|
8072
8119
|
for (const callback of subscribers) {
|
|
8073
8120
|
setImmediate(() => callback(message));
|
|
@@ -8106,9 +8153,12 @@ var subscriber = {
|
|
|
8106
8153
|
channels.set(channel, /* @__PURE__ */ new Set());
|
|
8107
8154
|
}
|
|
8108
8155
|
channels.get(channel).add(callback);
|
|
8156
|
+
console.log(`[ResumableStream] Subscribe to channel "${channel}" (total subscribers: ${channels.get(channel).size})`);
|
|
8109
8157
|
},
|
|
8110
8158
|
unsubscribe: async (channel) => {
|
|
8159
|
+
const count = channels.get(channel)?.size ?? 0;
|
|
8111
8160
|
channels.delete(channel);
|
|
8161
|
+
console.log(`[ResumableStream] Unsubscribe from channel "${channel}" (removed ${count} subscribers)`);
|
|
8112
8162
|
}
|
|
8113
8163
|
};
|
|
8114
8164
|
var streamContext = createResumableStreamContext({
|
|
@@ -8294,19 +8344,28 @@ function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
|
|
|
8294
8344
|
const toolCallStarts = /* @__PURE__ */ new Set();
|
|
8295
8345
|
const abortController = new AbortController();
|
|
8296
8346
|
streamAbortControllers.set(streamId, abortController);
|
|
8347
|
+
let sseEventCount = 0;
|
|
8348
|
+
let sseBrowserFrameCount = 0;
|
|
8349
|
+
let sseWriteErrors = 0;
|
|
8297
8350
|
const writeSSE = async (data) => {
|
|
8298
8351
|
if (writerClosed) return;
|
|
8299
8352
|
try {
|
|
8353
|
+
sseEventCount++;
|
|
8300
8354
|
await writer.write(`data: ${data}
|
|
8301
8355
|
|
|
8302
8356
|
`);
|
|
8303
8357
|
} catch (err) {
|
|
8358
|
+
sseWriteErrors++;
|
|
8359
|
+
if (sseWriteErrors === 1) {
|
|
8360
|
+
console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
|
|
8361
|
+
}
|
|
8304
8362
|
writerClosed = true;
|
|
8305
8363
|
}
|
|
8306
8364
|
};
|
|
8307
8365
|
const safeClose = async () => {
|
|
8308
8366
|
if (writerClosed) return;
|
|
8309
8367
|
try {
|
|
8368
|
+
console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
|
|
8310
8369
|
writerClosed = true;
|
|
8311
8370
|
await writer.close();
|
|
8312
8371
|
} catch {
|
|
@@ -8429,36 +8488,51 @@ ${prompt}` });
|
|
|
8429
8488
|
const browserPort = progress.data?.browserStreamPort;
|
|
8430
8489
|
const browserClosed = progress.data?.browserClosed;
|
|
8431
8490
|
if (progress.toolName === "bash" && browserClosed) {
|
|
8432
|
-
console.log(`[BROWSER-STREAM] agent-browser close detected, destroying proxy`);
|
|
8491
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${sessionId}`);
|
|
8433
8492
|
destroyProxy(sessionId);
|
|
8434
8493
|
} else if (progress.toolName === "bash" && browserPort) {
|
|
8435
|
-
console.log(`[BROWSER-STREAM] agent-browser command detected, ensuring proxy on port ${browserPort}`);
|
|
8494
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, ensuring proxy on port ${browserPort} for session ${sessionId}`);
|
|
8436
8495
|
const proxy = getOrCreateProxy(sessionId, browserPort);
|
|
8496
|
+
console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
|
|
8437
8497
|
if (!sessionRecorders.has(sessionId)) {
|
|
8438
8498
|
const recorder = new FrameRecorder(sessionId);
|
|
8439
8499
|
recorder.start();
|
|
8440
8500
|
sessionRecorders.set(sessionId, recorder);
|
|
8441
8501
|
}
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8502
|
+
const oldFrameListeners = proxy.listenerCount("frame");
|
|
8503
|
+
if (oldFrameListeners > 0) {
|
|
8504
|
+
console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
|
|
8505
|
+
proxy.removeAllListeners("frame");
|
|
8506
|
+
proxy.removeAllListeners("status");
|
|
8507
|
+
}
|
|
8508
|
+
console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
|
|
8509
|
+
proxy.on("frame", (frame) => {
|
|
8510
|
+
sseBrowserFrameCount++;
|
|
8511
|
+
if (sseBrowserFrameCount === 1) {
|
|
8512
|
+
console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
|
|
8513
|
+
} else if (sseBrowserFrameCount % 50 === 0) {
|
|
8514
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
|
|
8515
|
+
}
|
|
8516
|
+
const rec = sessionRecorders.get(sessionId);
|
|
8517
|
+
rec?.addFrame(frame);
|
|
8518
|
+
writeSSE(JSON.stringify({
|
|
8519
|
+
type: "browser-frame",
|
|
8520
|
+
data: frame.data,
|
|
8521
|
+
metadata: frame.metadata
|
|
8522
|
+
})).catch((err) => {
|
|
8523
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
|
|
8452
8524
|
});
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8525
|
+
});
|
|
8526
|
+
proxy.on("status", (s) => {
|
|
8527
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
|
|
8528
|
+
writeSSE(JSON.stringify({
|
|
8529
|
+
type: "browser-status",
|
|
8530
|
+
...s
|
|
8531
|
+
})).catch((err) => {
|
|
8532
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
|
|
8460
8533
|
});
|
|
8461
|
-
}
|
|
8534
|
+
});
|
|
8535
|
+
proxy.requestStatus();
|
|
8462
8536
|
}
|
|
8463
8537
|
},
|
|
8464
8538
|
onStepFinish: async () => {
|
|
@@ -8647,14 +8721,17 @@ ${prompt}` });
|
|
|
8647
8721
|
}
|
|
8648
8722
|
await messageQueries.create(id, { role: "user", content: userMessageContent });
|
|
8649
8723
|
const streamId = `stream_${id}_${nanoid6(10)}`;
|
|
8724
|
+
console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
|
|
8650
8725
|
await activeStreamQueries.create(id, streamId);
|
|
8651
8726
|
const stream = await streamContext.resumableStream(
|
|
8652
8727
|
streamId,
|
|
8653
8728
|
createAgentStreamProducer(id, prompt, streamId, streamAttachments)
|
|
8654
8729
|
);
|
|
8655
8730
|
if (!stream) {
|
|
8731
|
+
console.error(`[STREAM] Failed to create resumable stream ${streamId}`);
|
|
8656
8732
|
return c.json({ error: "Failed to create stream" }, 500);
|
|
8657
8733
|
}
|
|
8734
|
+
console.log(`[STREAM] Stream ${streamId} created successfully`);
|
|
8658
8735
|
const encodedStream = stream.pipeThrough(new TextEncoderStream());
|
|
8659
8736
|
return new Response(encodedStream, {
|
|
8660
8737
|
headers: {
|
|
@@ -8683,17 +8760,20 @@ agents.get("/:id/watch", async (c) => {
|
|
|
8683
8760
|
}
|
|
8684
8761
|
streamId = activeStream.streamId;
|
|
8685
8762
|
}
|
|
8763
|
+
console.log(`[STREAM] Watch request for session ${sessionId}, streamId=${streamId}, resumeAt=${resumeAt || "none"}`);
|
|
8686
8764
|
const stream = await streamContext.resumeExistingStream(
|
|
8687
8765
|
streamId,
|
|
8688
8766
|
resumeAt ? parseInt(resumeAt, 10) : void 0
|
|
8689
8767
|
);
|
|
8690
8768
|
if (!stream) {
|
|
8769
|
+
console.log(`[STREAM] Watch failed \u2014 stream ${streamId} is no longer active`);
|
|
8691
8770
|
return c.json({
|
|
8692
8771
|
error: "Stream is no longer active",
|
|
8693
8772
|
streamId,
|
|
8694
8773
|
hint: "The stream may have finished. Check /agents/:id/approvals or start a new run."
|
|
8695
8774
|
}, 422);
|
|
8696
8775
|
}
|
|
8776
|
+
console.log(`[STREAM] Client watching stream ${streamId}`);
|
|
8697
8777
|
const encodedStream = stream.pipeThrough(new TextEncoderStream());
|
|
8698
8778
|
return new Response(encodedStream, {
|
|
8699
8779
|
headers: {
|
|
@@ -8855,19 +8935,28 @@ agents.post(
|
|
|
8855
8935
|
const toolCallStarts = /* @__PURE__ */ new Set();
|
|
8856
8936
|
const abortController = new AbortController();
|
|
8857
8937
|
streamAbortControllers.set(streamId, abortController);
|
|
8938
|
+
let sseEventCount = 0;
|
|
8939
|
+
let sseBrowserFrameCount = 0;
|
|
8940
|
+
let sseWriteErrors = 0;
|
|
8858
8941
|
const writeSSE = async (data) => {
|
|
8859
8942
|
if (writerClosed) return;
|
|
8860
8943
|
try {
|
|
8944
|
+
sseEventCount++;
|
|
8861
8945
|
await writer.write(`data: ${data}
|
|
8862
8946
|
|
|
8863
8947
|
`);
|
|
8864
8948
|
} catch (err) {
|
|
8949
|
+
sseWriteErrors++;
|
|
8950
|
+
if (sseWriteErrors === 1) {
|
|
8951
|
+
console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
|
|
8952
|
+
}
|
|
8865
8953
|
writerClosed = true;
|
|
8866
8954
|
}
|
|
8867
8955
|
};
|
|
8868
8956
|
const safeClose = async () => {
|
|
8869
8957
|
if (writerClosed) return;
|
|
8870
8958
|
try {
|
|
8959
|
+
console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
|
|
8871
8960
|
writerClosed = true;
|
|
8872
8961
|
await writer.close();
|
|
8873
8962
|
} catch {
|
|
@@ -8927,36 +9016,51 @@ agents.post(
|
|
|
8927
9016
|
const browserPort = progress.data?.browserStreamPort;
|
|
8928
9017
|
const browserClosed = progress.data?.browserClosed;
|
|
8929
9018
|
if (progress.toolName === "bash" && browserClosed) {
|
|
8930
|
-
console.log(`[BROWSER-STREAM] agent-browser close detected`);
|
|
9019
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${session.id}`);
|
|
8931
9020
|
destroyProxy(session.id);
|
|
8932
9021
|
} else if (progress.toolName === "bash" && browserPort) {
|
|
8933
|
-
console.log(`[BROWSER-STREAM] agent-browser command detected, port ${browserPort}`);
|
|
9022
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, port ${browserPort} for session ${session.id}`);
|
|
8934
9023
|
const proxy = getOrCreateProxy(session.id, browserPort);
|
|
9024
|
+
console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
|
|
8935
9025
|
if (!sessionRecorders.has(session.id)) {
|
|
8936
9026
|
const recorder = new FrameRecorder(session.id);
|
|
8937
9027
|
recorder.start();
|
|
8938
9028
|
sessionRecorders.set(session.id, recorder);
|
|
8939
9029
|
}
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
9030
|
+
const oldFrameListeners = proxy.listenerCount("frame");
|
|
9031
|
+
if (oldFrameListeners > 0) {
|
|
9032
|
+
console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
|
|
9033
|
+
proxy.removeAllListeners("frame");
|
|
9034
|
+
proxy.removeAllListeners("status");
|
|
9035
|
+
}
|
|
9036
|
+
console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
|
|
9037
|
+
proxy.on("frame", (frame) => {
|
|
9038
|
+
sseBrowserFrameCount++;
|
|
9039
|
+
if (sseBrowserFrameCount === 1) {
|
|
9040
|
+
console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
|
|
9041
|
+
} else if (sseBrowserFrameCount % 50 === 0) {
|
|
9042
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
|
|
9043
|
+
}
|
|
9044
|
+
const rec = sessionRecorders.get(session.id);
|
|
9045
|
+
rec?.addFrame(frame);
|
|
9046
|
+
writeSSE(JSON.stringify({
|
|
9047
|
+
type: "browser-frame",
|
|
9048
|
+
data: frame.data,
|
|
9049
|
+
metadata: frame.metadata
|
|
9050
|
+
})).catch((err) => {
|
|
9051
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
|
|
8950
9052
|
});
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
9053
|
+
});
|
|
9054
|
+
proxy.on("status", (s) => {
|
|
9055
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
|
|
9056
|
+
writeSSE(JSON.stringify({
|
|
9057
|
+
type: "browser-status",
|
|
9058
|
+
...s
|
|
9059
|
+
})).catch((err) => {
|
|
9060
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
|
|
8958
9061
|
});
|
|
8959
|
-
}
|
|
9062
|
+
});
|
|
9063
|
+
proxy.requestStatus();
|
|
8960
9064
|
}
|
|
8961
9065
|
},
|
|
8962
9066
|
onStepFinish: async () => {
|