sparkecoder 0.1.67 → 0.1.69
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.d.ts +3 -3
- package/dist/agent/index.js +173 -19
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +307 -64
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-DHyVVhJY.d.ts → index-DqaHLgSC.d.ts} +20 -19
- package/dist/index.d.ts +5 -5
- package/dist/index.js +307 -64
- package/dist/index.js.map +1 -1
- package/dist/{schema-XcP0dedO.d.ts → schema-Bq4tID-f.d.ts} +3 -3
- package/dist/{search-CCffrVJE.d.ts → search-BRnGaIl-.d.ts} +7 -7
- package/dist/server/index.js +307 -64
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/package.json +1 -1
- 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_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.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- 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 +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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
- 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/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
- 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.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
- 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_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 +3 -3
- 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 +3 -3
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__c71f29f9._.js +3 -3
- 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/standalone/web/.next/static/chunks/{4e673433173ad456.js → 2cafc7cb79454d33.js} +3 -3
- package/web/.next/standalone/web/.next/static/chunks/{515f0c0bd6087843.js → b6ec74cad9ffd3ee.js} +3 -3
- package/web/.next/{static/chunks/31208ade542a0fcb.js → standalone/web/.next/static/chunks/fc39a194539da104.js} +3 -3
- package/web/.next/{static/chunks/4e673433173ad456.js → standalone/web/.next/static/static/chunks/2cafc7cb79454d33.js} +3 -3
- package/web/.next/standalone/web/.next/static/static/chunks/{515f0c0bd6087843.js → b6ec74cad9ffd3ee.js} +3 -3
- package/web/.next/standalone/web/.next/static/static/chunks/{31208ade542a0fcb.js → fc39a194539da104.js} +3 -3
- package/web/.next/standalone/web/src/components/chat-interface.tsx +14 -0
- 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/{515f0c0bd6087843.js → b6ec74cad9ffd3ee.js} +3 -3
- package/web/.next/{standalone/web/.next/static/chunks/31208ade542a0fcb.js → static/chunks/fc39a194539da104.js} +3 -3
- /package/web/.next/standalone/web/.next/static/{static/tZkod5afiOX7T9AkN1yPO → XB638PEDChQhwk6wSMrSh}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/tZkod5afiOX7T9AkN1yPO → XB638PEDChQhwk6wSMrSh}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/tZkod5afiOX7T9AkN1yPO → XB638PEDChQhwk6wSMrSh}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{tZkod5afiOX7T9AkN1yPO → static/XB638PEDChQhwk6wSMrSh}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{tZkod5afiOX7T9AkN1yPO → static/XB638PEDChQhwk6wSMrSh}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{tZkod5afiOX7T9AkN1yPO → static/XB638PEDChQhwk6wSMrSh}/_ssgManifest.js +0 -0
- /package/web/.next/static/{tZkod5afiOX7T9AkN1yPO → XB638PEDChQhwk6wSMrSh}/_buildManifest.js +0 -0
- /package/web/.next/static/{tZkod5afiOX7T9AkN1yPO → XB638PEDChQhwk6wSMrSh}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{tZkod5afiOX7T9AkN1yPO → XB638PEDChQhwk6wSMrSh}/_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,18 +1840,22 @@ 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
1861
|
this.emit("status", {
|
|
@@ -1855,12 +1867,14 @@ var init_stream_proxy = __esm({
|
|
|
1855
1867
|
try {
|
|
1856
1868
|
const msg = JSON.parse(typeof raw === "string" ? raw : raw.toString("utf8"));
|
|
1857
1869
|
this.handleMessage(msg);
|
|
1858
|
-
} catch {
|
|
1870
|
+
} catch (err) {
|
|
1871
|
+
console.warn(`[BROWSER-WS] Malformed message from ${url}:`, err);
|
|
1859
1872
|
}
|
|
1860
1873
|
});
|
|
1861
|
-
this.ws.on("close", () => {
|
|
1874
|
+
this.ws.on("close", (code, reason) => {
|
|
1862
1875
|
const wasConnected = this._connected;
|
|
1863
1876
|
this._connected = false;
|
|
1877
|
+
console.log(`[BROWSER-WS] Connection closed: code=${code} reason="${reason?.toString() || ""}" wasConnected=${wasConnected} destroyed=${this.destroyed}`);
|
|
1864
1878
|
if (wasConnected) {
|
|
1865
1879
|
this.emit("status", { connected: false, screencasting: false });
|
|
1866
1880
|
}
|
|
@@ -1868,14 +1882,26 @@ var init_stream_proxy = __esm({
|
|
|
1868
1882
|
this.scheduleReconnect();
|
|
1869
1883
|
}
|
|
1870
1884
|
});
|
|
1871
|
-
this.ws.on("error", () => {
|
|
1885
|
+
this.ws.on("error", (err) => {
|
|
1886
|
+
console.warn(`[BROWSER-WS] WebSocket error on port ${this.port}:`, err.message);
|
|
1872
1887
|
});
|
|
1873
1888
|
}
|
|
1889
|
+
frameCount = 0;
|
|
1890
|
+
throttledCount = 0;
|
|
1891
|
+
lastFrameLogTime = 0;
|
|
1874
1892
|
handleMessage(msg) {
|
|
1875
1893
|
if (msg.type === "frame") {
|
|
1876
1894
|
const now = Date.now();
|
|
1877
|
-
if (now - this.lastFrameTime < FRAME_THROTTLE_MS)
|
|
1895
|
+
if (now - this.lastFrameTime < FRAME_THROTTLE_MS) {
|
|
1896
|
+
this.throttledCount++;
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1878
1899
|
this.lastFrameTime = now;
|
|
1900
|
+
this.frameCount++;
|
|
1901
|
+
if (now - this.lastFrameLogTime > 5e3) {
|
|
1902
|
+
console.log(`[BROWSER-WS] Frame stats: emitted=${this.frameCount} throttled=${this.throttledCount} listeners=${this.listenerCount("frame")} dataSize=${msg.data?.length ?? 0}`);
|
|
1903
|
+
this.lastFrameLogTime = now;
|
|
1904
|
+
}
|
|
1879
1905
|
const frame = {
|
|
1880
1906
|
data: msg.data,
|
|
1881
1907
|
metadata: msg.metadata ?? {
|
|
@@ -1891,21 +1917,26 @@ var init_stream_proxy = __esm({
|
|
|
1891
1917
|
this._latestFrame = frame;
|
|
1892
1918
|
this.emit("frame", frame);
|
|
1893
1919
|
} else if (msg.type === "status") {
|
|
1920
|
+
console.log(`[BROWSER-WS] Status message received:`, JSON.stringify(msg));
|
|
1894
1921
|
this.emit("status", {
|
|
1895
1922
|
connected: msg.connected ?? true,
|
|
1896
1923
|
screencasting: msg.screencasting ?? true,
|
|
1897
1924
|
viewportWidth: msg.viewportWidth,
|
|
1898
1925
|
viewportHeight: msg.viewportHeight
|
|
1899
1926
|
});
|
|
1927
|
+
} else {
|
|
1928
|
+
console.log(`[BROWSER-WS] Unknown message type: ${msg.type}`);
|
|
1900
1929
|
}
|
|
1901
1930
|
}
|
|
1902
1931
|
scheduleReconnect() {
|
|
1903
1932
|
if (this.destroyed || this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
1933
|
+
console.log(`[BROWSER-WS] Giving up reconnection: destroyed=${this.destroyed} attempts=${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}`);
|
|
1904
1934
|
this.emit("close");
|
|
1905
1935
|
return;
|
|
1906
1936
|
}
|
|
1907
1937
|
this.reconnectAttempts++;
|
|
1908
1938
|
const delay = this.reconnectAttempts <= 5 ? RECONNECT_DELAY_MS : RECONNECT_DELAY_MS * (this.reconnectAttempts - 4);
|
|
1939
|
+
console.log(`[BROWSER-WS] Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
|
|
1909
1940
|
this.reconnectTimer = setTimeout(() => this.doConnect(), delay);
|
|
1910
1941
|
}
|
|
1911
1942
|
/**
|
|
@@ -1917,6 +1948,7 @@ var init_stream_proxy = __esm({
|
|
|
1917
1948
|
}
|
|
1918
1949
|
}
|
|
1919
1950
|
destroy() {
|
|
1951
|
+
console.log(`[BROWSER-WS] Destroying proxy for port ${this.port} (emitted ${this.frameCount} frames, throttled ${this.throttledCount})`);
|
|
1920
1952
|
this.destroyed = true;
|
|
1921
1953
|
if (this.reconnectTimer) {
|
|
1922
1954
|
clearTimeout(this.reconnectTimer);
|
|
@@ -6478,6 +6510,21 @@ ${this.summary}`
|
|
|
6478
6510
|
|
|
6479
6511
|
// src/agent/index.ts
|
|
6480
6512
|
init_webhook();
|
|
6513
|
+
var MAX_SSE_FIELD_LENGTH = 8 * 1024;
|
|
6514
|
+
var SSE_PREVIEW_LENGTH = 2 * 1024;
|
|
6515
|
+
function truncateWriteFileInput(input) {
|
|
6516
|
+
const out = { ...input };
|
|
6517
|
+
for (const key of ["content", "old_string", "new_string"]) {
|
|
6518
|
+
const val = out[key];
|
|
6519
|
+
if (typeof val === "string" && val.length > MAX_SSE_FIELD_LENGTH) {
|
|
6520
|
+
out[key] = `${val.slice(0, SSE_PREVIEW_LENGTH)}
|
|
6521
|
+
... (truncated)`;
|
|
6522
|
+
out[`${key}Truncated`] = true;
|
|
6523
|
+
out[`${key}Length`] = val.length;
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
return out;
|
|
6527
|
+
}
|
|
6481
6528
|
var approvalResolvers = /* @__PURE__ */ new Map();
|
|
6482
6529
|
var Agent = class _Agent {
|
|
6483
6530
|
session;
|
|
@@ -6721,8 +6768,11 @@ ${prompt}` });
|
|
|
6721
6768
|
};
|
|
6722
6769
|
let taskRecorder = null;
|
|
6723
6770
|
const sessionId = this.session.id;
|
|
6771
|
+
const emit = options.writeSSE;
|
|
6724
6772
|
const bashProgressHandler = (progress) => {
|
|
6725
6773
|
options.onToolProgress?.({ toolName: "bash", data: progress });
|
|
6774
|
+
if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "bash", data: progress })).catch(() => {
|
|
6775
|
+
});
|
|
6726
6776
|
const port = progress.browserStreamPort;
|
|
6727
6777
|
if (port && progress.status === "started") {
|
|
6728
6778
|
Promise.resolve().then(() => (init_stream_proxy(), stream_proxy_exports)).then(({ getOrCreateProxy: getOrCreateProxy2 }) => {
|
|
@@ -6731,7 +6781,17 @@ ${prompt}` });
|
|
|
6731
6781
|
Promise.resolve().then(() => (init_recorder(), recorder_exports)).then(({ FrameRecorder: FrameRecorder2 }) => {
|
|
6732
6782
|
taskRecorder = new FrameRecorder2(sessionId);
|
|
6733
6783
|
taskRecorder.start();
|
|
6734
|
-
|
|
6784
|
+
});
|
|
6785
|
+
}
|
|
6786
|
+
if (proxy.listenerCount("frame") === 0) {
|
|
6787
|
+
proxy.on("frame", (frame) => {
|
|
6788
|
+
taskRecorder?.addFrame(frame);
|
|
6789
|
+
if (emit) emit(JSON.stringify({ type: "browser-frame", data: frame.data, metadata: frame.metadata })).catch(() => {
|
|
6790
|
+
});
|
|
6791
|
+
});
|
|
6792
|
+
proxy.on("status", (s) => {
|
|
6793
|
+
if (emit) emit(JSON.stringify({ type: "browser-status", ...s })).catch(() => {
|
|
6794
|
+
});
|
|
6735
6795
|
});
|
|
6736
6796
|
}
|
|
6737
6797
|
});
|
|
@@ -6742,8 +6802,16 @@ ${prompt}` });
|
|
|
6742
6802
|
workingDirectory: this.session.workingDirectory,
|
|
6743
6803
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
6744
6804
|
onBashProgress: bashProgressHandler,
|
|
6745
|
-
onWriteFileProgress:
|
|
6746
|
-
|
|
6805
|
+
onWriteFileProgress: (progress) => {
|
|
6806
|
+
options.onToolProgress?.({ toolName: "write_file", data: progress });
|
|
6807
|
+
if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "write_file", data: progress })).catch(() => {
|
|
6808
|
+
});
|
|
6809
|
+
},
|
|
6810
|
+
onSearchProgress: (progress) => {
|
|
6811
|
+
options.onToolProgress?.({ toolName: "explore_agent", data: progress });
|
|
6812
|
+
if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "explore_agent", data: progress })).catch(() => {
|
|
6813
|
+
});
|
|
6814
|
+
},
|
|
6747
6815
|
taskTools: {
|
|
6748
6816
|
outputSchema: options.taskConfig.outputSchema,
|
|
6749
6817
|
onComplete
|
|
@@ -6761,6 +6829,9 @@ ${prompt}` });
|
|
|
6761
6829
|
|
|
6762
6830
|
${taskAddendum}`;
|
|
6763
6831
|
fireWebhook("task.started", { prompt: options.prompt });
|
|
6832
|
+
if (emit) {
|
|
6833
|
+
await emit(JSON.stringify({ type: "data-user-message", data: { id: `user_${Date.now()}`, content: options.prompt } }));
|
|
6834
|
+
}
|
|
6764
6835
|
await this.context.addUserMessage(options.prompt);
|
|
6765
6836
|
let iteration = 0;
|
|
6766
6837
|
while (iteration < maxIterations) {
|
|
@@ -6772,7 +6843,15 @@ ${taskAddendum}`;
|
|
|
6772
6843
|
}
|
|
6773
6844
|
const messages = await this.context.getMessages();
|
|
6774
6845
|
const useAnthropic = isAnthropicModel(this.session.model);
|
|
6775
|
-
|
|
6846
|
+
if (emit) {
|
|
6847
|
+
await emit(JSON.stringify({ type: "start", messageId: `msg_${Date.now()}` }));
|
|
6848
|
+
}
|
|
6849
|
+
let textStarted = false;
|
|
6850
|
+
let textId = `text_${Date.now()}`;
|
|
6851
|
+
let reasoningId = `reasoning_${Date.now()}`;
|
|
6852
|
+
let reasoningStarted = false;
|
|
6853
|
+
const toolCallStarts = /* @__PURE__ */ new Set();
|
|
6854
|
+
const iterStream = streamText2({
|
|
6776
6855
|
model: resolveModel(this.session.model),
|
|
6777
6856
|
system: systemPrompt,
|
|
6778
6857
|
messages,
|
|
@@ -6781,21 +6860,94 @@ ${taskAddendum}`;
|
|
|
6781
6860
|
abortSignal: options.abortSignal,
|
|
6782
6861
|
providerOptions: useAnthropic ? {
|
|
6783
6862
|
anthropic: {
|
|
6863
|
+
toolStreaming: true,
|
|
6784
6864
|
thinking: { type: "enabled", budgetTokens: 1e4 }
|
|
6785
6865
|
}
|
|
6786
6866
|
} : void 0,
|
|
6787
|
-
onStepFinish: (step) => {
|
|
6867
|
+
onStepFinish: async (step) => {
|
|
6788
6868
|
options.onStepFinish?.(step);
|
|
6789
6869
|
fireWebhook("task.step_finished", { iteration, text: step.text });
|
|
6870
|
+
if (emit) {
|
|
6871
|
+
if (textStarted) {
|
|
6872
|
+
await emit(JSON.stringify({ type: "text-end", id: textId }));
|
|
6873
|
+
textStarted = false;
|
|
6874
|
+
textId = `text_${Date.now()}`;
|
|
6875
|
+
}
|
|
6876
|
+
await emit(JSON.stringify({ type: "finish-step" }));
|
|
6877
|
+
}
|
|
6790
6878
|
}
|
|
6791
6879
|
});
|
|
6792
|
-
const
|
|
6880
|
+
for await (const part of iterStream.fullStream) {
|
|
6881
|
+
if (part.type === "text-delta") {
|
|
6882
|
+
if (emit) {
|
|
6883
|
+
if (!textStarted) {
|
|
6884
|
+
await emit(JSON.stringify({ type: "text-start", id: textId }));
|
|
6885
|
+
textStarted = true;
|
|
6886
|
+
}
|
|
6887
|
+
await emit(JSON.stringify({ type: "text-delta", id: textId, delta: part.text }));
|
|
6888
|
+
}
|
|
6889
|
+
} else if (part.type === "reasoning-start") {
|
|
6890
|
+
if (emit) {
|
|
6891
|
+
await emit(JSON.stringify({ type: "reasoning-start", id: reasoningId }));
|
|
6892
|
+
reasoningStarted = true;
|
|
6893
|
+
}
|
|
6894
|
+
} else if (part.type === "reasoning-delta") {
|
|
6895
|
+
if (emit) {
|
|
6896
|
+
await emit(JSON.stringify({ type: "reasoning-delta", id: reasoningId, delta: part.text }));
|
|
6897
|
+
}
|
|
6898
|
+
} else if (part.type === "reasoning-end") {
|
|
6899
|
+
if (emit && reasoningStarted) {
|
|
6900
|
+
await emit(JSON.stringify({ type: "reasoning-end", id: reasoningId }));
|
|
6901
|
+
reasoningStarted = false;
|
|
6902
|
+
reasoningId = `reasoning_${Date.now()}`;
|
|
6903
|
+
}
|
|
6904
|
+
} else if (part.type === "tool-call-streaming-start") {
|
|
6905
|
+
if (emit) {
|
|
6906
|
+
const p = part;
|
|
6907
|
+
await emit(JSON.stringify({ type: "tool-input-start", toolCallId: p.toolCallId, toolName: p.toolName }));
|
|
6908
|
+
toolCallStarts.add(p.toolCallId);
|
|
6909
|
+
}
|
|
6910
|
+
} else if (part.type === "tool-call-delta") {
|
|
6911
|
+
if (emit) {
|
|
6912
|
+
const p = part;
|
|
6913
|
+
await emit(JSON.stringify({ type: "tool-input-delta", toolCallId: p.toolCallId, argsTextDelta: p.argsTextDelta }));
|
|
6914
|
+
}
|
|
6915
|
+
} else if (part.type === "tool-call") {
|
|
6916
|
+
if (emit) {
|
|
6917
|
+
if (!toolCallStarts.has(part.toolCallId)) {
|
|
6918
|
+
await emit(JSON.stringify({ type: "tool-input-start", toolCallId: part.toolCallId, toolName: part.toolName }));
|
|
6919
|
+
toolCallStarts.add(part.toolCallId);
|
|
6920
|
+
}
|
|
6921
|
+
const safeInput = part.toolName === "write_file" && part.input && typeof part.input === "object" ? truncateWriteFileInput(part.input) : part.input;
|
|
6922
|
+
await emit(JSON.stringify({ type: "tool-input-available", toolCallId: part.toolCallId, toolName: part.toolName, input: safeInput }));
|
|
6923
|
+
}
|
|
6924
|
+
} else if (part.type === "tool-result") {
|
|
6925
|
+
if (emit) {
|
|
6926
|
+
await emit(JSON.stringify({ type: "tool-output-available", toolCallId: part.toolCallId, output: part.output }));
|
|
6927
|
+
}
|
|
6928
|
+
} else if (part.type === "error") {
|
|
6929
|
+
console.error("Task stream error:", part.error);
|
|
6930
|
+
if (emit) {
|
|
6931
|
+
await emit(JSON.stringify({ type: "error", errorText: String(part.error) }));
|
|
6932
|
+
}
|
|
6933
|
+
}
|
|
6934
|
+
}
|
|
6935
|
+
if (emit && textStarted) {
|
|
6936
|
+
await emit(JSON.stringify({ type: "text-end", id: textId }));
|
|
6937
|
+
}
|
|
6938
|
+
if (emit && reasoningStarted) {
|
|
6939
|
+
await emit(JSON.stringify({ type: "reasoning-end", id: reasoningId }));
|
|
6940
|
+
}
|
|
6941
|
+
const iterResponse = await iterStream.response;
|
|
6942
|
+
const responseMessages = iterResponse.messages;
|
|
6793
6943
|
await this.context.addResponseMessages(responseMessages);
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6944
|
+
const resultText = await iterStream.text;
|
|
6945
|
+
const resultSteps = await iterStream.steps;
|
|
6946
|
+
if (resultText) {
|
|
6947
|
+
options.onText?.(resultText);
|
|
6948
|
+
fireWebhook("task.message", { iteration, text: resultText });
|
|
6797
6949
|
}
|
|
6798
|
-
for (const step of
|
|
6950
|
+
for (const step of resultSteps) {
|
|
6799
6951
|
if (step.toolCalls) {
|
|
6800
6952
|
for (const tc of step.toolCalls) {
|
|
6801
6953
|
options.onToolCall?.({ toolCallId: tc.toolCallId, toolName: tc.toolName, input: tc.args });
|
|
@@ -6848,9 +7000,11 @@ ${taskAddendum}`;
|
|
|
6848
7000
|
iterations: iteration
|
|
6849
7001
|
};
|
|
6850
7002
|
}
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
7003
|
+
const continuationPrompt = "Continue working on the task. Before calling `complete_task`, VERIFY your work is correct \u2014 re-read edited files, run the linter, run tests if applicable, and check the browser/server if you made UI or API changes. Make sure you searched the right directories and found everything relevant. When fully verified, call `complete_task` with the result. If you cannot complete it, call `task_failed` with a reason.";
|
|
7004
|
+
if (emit) {
|
|
7005
|
+
await emit(JSON.stringify({ type: "data-user-message", data: { id: `user_${Date.now()}`, content: continuationPrompt } }));
|
|
7006
|
+
}
|
|
7007
|
+
await this.context.addUserMessage(continuationPrompt);
|
|
6854
7008
|
}
|
|
6855
7009
|
const timeoutError = `Task did not complete within ${maxIterations} iterations`;
|
|
6856
7010
|
const timeoutRecordingUrls = await this.finishTaskRecording(taskRecorder);
|
|
@@ -7941,11 +8095,19 @@ var cleanupInterval = setInterval(() => {
|
|
|
7941
8095
|
}
|
|
7942
8096
|
}, 6e4);
|
|
7943
8097
|
cleanupInterval.unref();
|
|
8098
|
+
var publishCount = 0;
|
|
8099
|
+
var lastPublishLog = 0;
|
|
7944
8100
|
var publisher = {
|
|
7945
8101
|
connect: async () => {
|
|
7946
8102
|
},
|
|
7947
8103
|
publish: async (channel, message) => {
|
|
7948
8104
|
const subscribers = channels.get(channel);
|
|
8105
|
+
publishCount++;
|
|
8106
|
+
const now = Date.now();
|
|
8107
|
+
if (now - lastPublishLog > 1e4) {
|
|
8108
|
+
console.log(`[ResumableStream] Publish stats: total=${publishCount}, channels=${channels.size}, store=${store.size}`);
|
|
8109
|
+
lastPublishLog = now;
|
|
8110
|
+
}
|
|
7949
8111
|
if (subscribers) {
|
|
7950
8112
|
for (const callback of subscribers) {
|
|
7951
8113
|
setImmediate(() => callback(message));
|
|
@@ -7984,9 +8146,12 @@ var subscriber = {
|
|
|
7984
8146
|
channels.set(channel, /* @__PURE__ */ new Set());
|
|
7985
8147
|
}
|
|
7986
8148
|
channels.get(channel).add(callback);
|
|
8149
|
+
console.log(`[ResumableStream] Subscribe to channel "${channel}" (total subscribers: ${channels.get(channel).size})`);
|
|
7987
8150
|
},
|
|
7988
8151
|
unsubscribe: async (channel) => {
|
|
8152
|
+
const count = channels.get(channel)?.size ?? 0;
|
|
7989
8153
|
channels.delete(channel);
|
|
8154
|
+
console.log(`[ResumableStream] Unsubscribe from channel "${channel}" (removed ${count} subscribers)`);
|
|
7990
8155
|
}
|
|
7991
8156
|
};
|
|
7992
8157
|
var streamContext = createResumableStreamContext({
|
|
@@ -8172,19 +8337,28 @@ function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
|
|
|
8172
8337
|
const toolCallStarts = /* @__PURE__ */ new Set();
|
|
8173
8338
|
const abortController = new AbortController();
|
|
8174
8339
|
streamAbortControllers.set(streamId, abortController);
|
|
8340
|
+
let sseEventCount = 0;
|
|
8341
|
+
let sseBrowserFrameCount = 0;
|
|
8342
|
+
let sseWriteErrors = 0;
|
|
8175
8343
|
const writeSSE = async (data) => {
|
|
8176
8344
|
if (writerClosed) return;
|
|
8177
8345
|
try {
|
|
8346
|
+
sseEventCount++;
|
|
8178
8347
|
await writer.write(`data: ${data}
|
|
8179
8348
|
|
|
8180
8349
|
`);
|
|
8181
8350
|
} catch (err) {
|
|
8351
|
+
sseWriteErrors++;
|
|
8352
|
+
if (sseWriteErrors === 1) {
|
|
8353
|
+
console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
|
|
8354
|
+
}
|
|
8182
8355
|
writerClosed = true;
|
|
8183
8356
|
}
|
|
8184
8357
|
};
|
|
8185
8358
|
const safeClose = async () => {
|
|
8186
8359
|
if (writerClosed) return;
|
|
8187
8360
|
try {
|
|
8361
|
+
console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
|
|
8188
8362
|
writerClosed = true;
|
|
8189
8363
|
await writer.close();
|
|
8190
8364
|
} catch {
|
|
@@ -8307,35 +8481,47 @@ ${prompt}` });
|
|
|
8307
8481
|
const browserPort = progress.data?.browserStreamPort;
|
|
8308
8482
|
const browserClosed = progress.data?.browserClosed;
|
|
8309
8483
|
if (progress.toolName === "bash" && browserClosed) {
|
|
8310
|
-
console.log(`[BROWSER-STREAM] agent-browser close detected, destroying proxy`);
|
|
8484
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${sessionId}`);
|
|
8311
8485
|
destroyProxy(sessionId);
|
|
8312
8486
|
} else if (progress.toolName === "bash" && browserPort) {
|
|
8313
|
-
console.log(`[BROWSER-STREAM] agent-browser command detected, ensuring proxy on port ${browserPort}`);
|
|
8487
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, ensuring proxy on port ${browserPort} for session ${sessionId}`);
|
|
8314
8488
|
const proxy = getOrCreateProxy(sessionId, browserPort);
|
|
8489
|
+
console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
|
|
8315
8490
|
if (!sessionRecorders.has(sessionId)) {
|
|
8316
8491
|
const recorder = new FrameRecorder(sessionId);
|
|
8317
8492
|
recorder.start();
|
|
8318
8493
|
sessionRecorders.set(sessionId, recorder);
|
|
8319
8494
|
}
|
|
8320
8495
|
if (proxy.listenerCount("frame") === 0) {
|
|
8496
|
+
console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
|
|
8321
8497
|
proxy.on("frame", (frame) => {
|
|
8498
|
+
sseBrowserFrameCount++;
|
|
8499
|
+
if (sseBrowserFrameCount === 1) {
|
|
8500
|
+
console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
|
|
8501
|
+
} else if (sseBrowserFrameCount % 50 === 0) {
|
|
8502
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
|
|
8503
|
+
}
|
|
8322
8504
|
const rec = sessionRecorders.get(sessionId);
|
|
8323
8505
|
rec?.addFrame(frame);
|
|
8324
8506
|
writeSSE(JSON.stringify({
|
|
8325
8507
|
type: "browser-frame",
|
|
8326
8508
|
data: frame.data,
|
|
8327
8509
|
metadata: frame.metadata
|
|
8328
|
-
})).catch(() => {
|
|
8510
|
+
})).catch((err) => {
|
|
8511
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
|
|
8329
8512
|
});
|
|
8330
8513
|
});
|
|
8331
8514
|
proxy.on("status", (s) => {
|
|
8332
|
-
console.log(`[BROWSER-STREAM]
|
|
8515
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
|
|
8333
8516
|
writeSSE(JSON.stringify({
|
|
8334
8517
|
type: "browser-status",
|
|
8335
8518
|
...s
|
|
8336
|
-
})).catch(() => {
|
|
8519
|
+
})).catch((err) => {
|
|
8520
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
|
|
8337
8521
|
});
|
|
8338
8522
|
});
|
|
8523
|
+
} else {
|
|
8524
|
+
console.log(`[BROWSER-STREAM:${streamId}] Frame listeners already attached (count=${proxy.listenerCount("frame")}), skipping`);
|
|
8339
8525
|
}
|
|
8340
8526
|
}
|
|
8341
8527
|
},
|
|
@@ -8525,14 +8711,17 @@ ${prompt}` });
|
|
|
8525
8711
|
}
|
|
8526
8712
|
await messageQueries.create(id, { role: "user", content: userMessageContent });
|
|
8527
8713
|
const streamId = `stream_${id}_${nanoid6(10)}`;
|
|
8714
|
+
console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
|
|
8528
8715
|
await activeStreamQueries.create(id, streamId);
|
|
8529
8716
|
const stream = await streamContext.resumableStream(
|
|
8530
8717
|
streamId,
|
|
8531
8718
|
createAgentStreamProducer(id, prompt, streamId, streamAttachments)
|
|
8532
8719
|
);
|
|
8533
8720
|
if (!stream) {
|
|
8721
|
+
console.error(`[STREAM] Failed to create resumable stream ${streamId}`);
|
|
8534
8722
|
return c.json({ error: "Failed to create stream" }, 500);
|
|
8535
8723
|
}
|
|
8724
|
+
console.log(`[STREAM] Stream ${streamId} created successfully`);
|
|
8536
8725
|
const encodedStream = stream.pipeThrough(new TextEncoderStream());
|
|
8537
8726
|
return new Response(encodedStream, {
|
|
8538
8727
|
headers: {
|
|
@@ -8561,17 +8750,20 @@ agents.get("/:id/watch", async (c) => {
|
|
|
8561
8750
|
}
|
|
8562
8751
|
streamId = activeStream.streamId;
|
|
8563
8752
|
}
|
|
8753
|
+
console.log(`[STREAM] Watch request for session ${sessionId}, streamId=${streamId}, resumeAt=${resumeAt || "none"}`);
|
|
8564
8754
|
const stream = await streamContext.resumeExistingStream(
|
|
8565
8755
|
streamId,
|
|
8566
8756
|
resumeAt ? parseInt(resumeAt, 10) : void 0
|
|
8567
8757
|
);
|
|
8568
8758
|
if (!stream) {
|
|
8759
|
+
console.log(`[STREAM] Watch failed \u2014 stream ${streamId} is no longer active`);
|
|
8569
8760
|
return c.json({
|
|
8570
8761
|
error: "Stream is no longer active",
|
|
8571
8762
|
streamId,
|
|
8572
8763
|
hint: "The stream may have finished. Check /agents/:id/approvals or start a new run."
|
|
8573
8764
|
}, 422);
|
|
8574
8765
|
}
|
|
8766
|
+
console.log(`[STREAM] Client watching stream ${streamId}`);
|
|
8575
8767
|
const encodedStream = stream.pipeThrough(new TextEncoderStream());
|
|
8576
8768
|
return new Response(encodedStream, {
|
|
8577
8769
|
headers: {
|
|
@@ -8733,19 +8925,28 @@ agents.post(
|
|
|
8733
8925
|
const toolCallStarts = /* @__PURE__ */ new Set();
|
|
8734
8926
|
const abortController = new AbortController();
|
|
8735
8927
|
streamAbortControllers.set(streamId, abortController);
|
|
8928
|
+
let sseEventCount = 0;
|
|
8929
|
+
let sseBrowserFrameCount = 0;
|
|
8930
|
+
let sseWriteErrors = 0;
|
|
8736
8931
|
const writeSSE = async (data) => {
|
|
8737
8932
|
if (writerClosed) return;
|
|
8738
8933
|
try {
|
|
8934
|
+
sseEventCount++;
|
|
8739
8935
|
await writer.write(`data: ${data}
|
|
8740
8936
|
|
|
8741
8937
|
`);
|
|
8742
8938
|
} catch (err) {
|
|
8939
|
+
sseWriteErrors++;
|
|
8940
|
+
if (sseWriteErrors === 1) {
|
|
8941
|
+
console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
|
|
8942
|
+
}
|
|
8743
8943
|
writerClosed = true;
|
|
8744
8944
|
}
|
|
8745
8945
|
};
|
|
8746
8946
|
const safeClose = async () => {
|
|
8747
8947
|
if (writerClosed) return;
|
|
8748
8948
|
try {
|
|
8949
|
+
console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
|
|
8749
8950
|
writerClosed = true;
|
|
8750
8951
|
await writer.close();
|
|
8751
8952
|
} catch {
|
|
@@ -8805,35 +9006,47 @@ agents.post(
|
|
|
8805
9006
|
const browserPort = progress.data?.browserStreamPort;
|
|
8806
9007
|
const browserClosed = progress.data?.browserClosed;
|
|
8807
9008
|
if (progress.toolName === "bash" && browserClosed) {
|
|
8808
|
-
console.log(`[BROWSER-STREAM] agent-browser close detected`);
|
|
9009
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${session.id}`);
|
|
8809
9010
|
destroyProxy(session.id);
|
|
8810
9011
|
} else if (progress.toolName === "bash" && browserPort) {
|
|
8811
|
-
console.log(`[BROWSER-STREAM] agent-browser command detected, port ${browserPort}`);
|
|
9012
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, port ${browserPort} for session ${session.id}`);
|
|
8812
9013
|
const proxy = getOrCreateProxy(session.id, browserPort);
|
|
9014
|
+
console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
|
|
8813
9015
|
if (!sessionRecorders.has(session.id)) {
|
|
8814
9016
|
const recorder = new FrameRecorder(session.id);
|
|
8815
9017
|
recorder.start();
|
|
8816
9018
|
sessionRecorders.set(session.id, recorder);
|
|
8817
9019
|
}
|
|
8818
9020
|
if (proxy.listenerCount("frame") === 0) {
|
|
9021
|
+
console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
|
|
8819
9022
|
proxy.on("frame", (frame) => {
|
|
9023
|
+
sseBrowserFrameCount++;
|
|
9024
|
+
if (sseBrowserFrameCount === 1) {
|
|
9025
|
+
console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
|
|
9026
|
+
} else if (sseBrowserFrameCount % 50 === 0) {
|
|
9027
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
|
|
9028
|
+
}
|
|
8820
9029
|
const rec = sessionRecorders.get(session.id);
|
|
8821
9030
|
rec?.addFrame(frame);
|
|
8822
9031
|
writeSSE(JSON.stringify({
|
|
8823
9032
|
type: "browser-frame",
|
|
8824
9033
|
data: frame.data,
|
|
8825
9034
|
metadata: frame.metadata
|
|
8826
|
-
})).catch(() => {
|
|
9035
|
+
})).catch((err) => {
|
|
9036
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
|
|
8827
9037
|
});
|
|
8828
9038
|
});
|
|
8829
9039
|
proxy.on("status", (s) => {
|
|
8830
|
-
console.log(`[BROWSER-STREAM]
|
|
9040
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
|
|
8831
9041
|
writeSSE(JSON.stringify({
|
|
8832
9042
|
type: "browser-status",
|
|
8833
9043
|
...s
|
|
8834
|
-
})).catch(() => {
|
|
9044
|
+
})).catch((err) => {
|
|
9045
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
|
|
8835
9046
|
});
|
|
8836
9047
|
});
|
|
9048
|
+
} else {
|
|
9049
|
+
console.log(`[BROWSER-STREAM:${streamId}] Frame listeners already attached (count=${proxy.listenerCount("frame")}), skipping`);
|
|
8837
9050
|
}
|
|
8838
9051
|
}
|
|
8839
9052
|
},
|
|
@@ -9482,6 +9695,7 @@ init_db();
|
|
|
9482
9695
|
import { Hono as Hono5 } from "hono";
|
|
9483
9696
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
9484
9697
|
import { z as z19 } from "zod";
|
|
9698
|
+
import { nanoid as nanoid7 } from "nanoid";
|
|
9485
9699
|
init_config();
|
|
9486
9700
|
var tasks = new Hono5();
|
|
9487
9701
|
var taskAbortControllers = /* @__PURE__ */ new Map();
|
|
@@ -9519,45 +9733,74 @@ tasks.post(
|
|
|
9519
9733
|
const taskId = agent.sessionId;
|
|
9520
9734
|
const abortController = new AbortController();
|
|
9521
9735
|
taskAbortControllers.set(taskId, abortController);
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9736
|
+
const streamId = `stream_${taskId}_${nanoid7(10)}`;
|
|
9737
|
+
await activeStreamQueries.create(taskId, streamId);
|
|
9738
|
+
const taskStreamProducer = () => {
|
|
9739
|
+
const { readable, writable } = new TransformStream();
|
|
9740
|
+
const writer = writable.getWriter();
|
|
9741
|
+
let writerClosed = false;
|
|
9742
|
+
const writeSSE = async (data) => {
|
|
9743
|
+
if (writerClosed) return;
|
|
9744
|
+
try {
|
|
9745
|
+
await writer.write(`data: ${data}
|
|
9746
|
+
|
|
9747
|
+
`);
|
|
9748
|
+
} catch {
|
|
9749
|
+
writerClosed = true;
|
|
9750
|
+
}
|
|
9751
|
+
};
|
|
9752
|
+
(async () => {
|
|
9753
|
+
await writeSSE(JSON.stringify({ type: "data-stream-id", streamId }));
|
|
9754
|
+
try {
|
|
9755
|
+
await agent.runTask({
|
|
9756
|
+
prompt: body.prompt,
|
|
9757
|
+
taskConfig,
|
|
9758
|
+
abortSignal: abortController.signal,
|
|
9759
|
+
writeSSE
|
|
9545
9760
|
});
|
|
9546
|
-
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9761
|
+
await writeSSE(JSON.stringify({ type: "finish" }));
|
|
9762
|
+
} catch (err) {
|
|
9763
|
+
if (err.name === "AbortError" || abortController.signal.aborted) {
|
|
9764
|
+
console.log(`[TASK] Task ${taskId} was cancelled`);
|
|
9765
|
+
await writeSSE(JSON.stringify({ type: "abort" }));
|
|
9766
|
+
} else {
|
|
9767
|
+
console.error(`[TASK] Error in task ${taskId}:`, err.message);
|
|
9768
|
+
const errorMsg = err.message || "Unknown error";
|
|
9769
|
+
await writeSSE(JSON.stringify({ type: "error", errorText: errorMsg }));
|
|
9770
|
+
const failedTask = {
|
|
9771
|
+
...taskConfig,
|
|
9772
|
+
status: "failed",
|
|
9773
|
+
error: errorMsg
|
|
9774
|
+
};
|
|
9775
|
+
await sessionQueries.update(taskId, {
|
|
9776
|
+
config: {
|
|
9777
|
+
toolApprovals: { bash: false, write_file: false, read_file: false },
|
|
9778
|
+
task: failedTask
|
|
9779
|
+
}
|
|
9554
9780
|
});
|
|
9781
|
+
if (taskConfig.webhookUrl) {
|
|
9782
|
+
const { sendWebhook: sendWebhook2 } = await Promise.resolve().then(() => (init_webhook(), webhook_exports));
|
|
9783
|
+
sendWebhook2(taskConfig.webhookUrl, {
|
|
9784
|
+
type: "task.failed",
|
|
9785
|
+
taskId,
|
|
9786
|
+
sessionId: taskId,
|
|
9787
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9788
|
+
data: { status: "failed", error: errorMsg }
|
|
9789
|
+
});
|
|
9790
|
+
}
|
|
9555
9791
|
}
|
|
9792
|
+
} finally {
|
|
9793
|
+
await writeSSE("[DONE]");
|
|
9794
|
+
writer.close().catch(() => {
|
|
9795
|
+
});
|
|
9796
|
+
await activeStreamQueries.finish(streamId).catch(() => {
|
|
9797
|
+
});
|
|
9798
|
+
taskAbortControllers.delete(taskId);
|
|
9556
9799
|
}
|
|
9557
|
-
}
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9800
|
+
})();
|
|
9801
|
+
return readable;
|
|
9802
|
+
};
|
|
9803
|
+
await streamContext.resumableStream(streamId, taskStreamProducer);
|
|
9561
9804
|
return c.json({ taskId, status: "running" }, 201);
|
|
9562
9805
|
}
|
|
9563
9806
|
);
|