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/cli.js
CHANGED
|
@@ -2556,10 +2556,15 @@ import WebSocket from "ws";
|
|
|
2556
2556
|
import { EventEmitter } from "events";
|
|
2557
2557
|
function getOrCreateProxy(sessionId, port) {
|
|
2558
2558
|
const existing = activeProxies.get(sessionId);
|
|
2559
|
-
if (existing)
|
|
2559
|
+
if (existing) {
|
|
2560
|
+
console.log(`[BROWSER-WS] Reusing existing proxy for session ${sessionId} (connected=${existing.connected})`);
|
|
2561
|
+
return existing;
|
|
2562
|
+
}
|
|
2563
|
+
console.log(`[BROWSER-WS] Creating new proxy for session ${sessionId} on port ${port} (active proxies: ${activeProxies.size})`);
|
|
2560
2564
|
const proxy = new BrowserStreamProxy(port);
|
|
2561
2565
|
activeProxies.set(sessionId, proxy);
|
|
2562
2566
|
proxy.on("close", () => {
|
|
2567
|
+
console.log(`[BROWSER-WS] Proxy closed for session ${sessionId}, removing from registry`);
|
|
2563
2568
|
activeProxies.delete(sessionId);
|
|
2564
2569
|
});
|
|
2565
2570
|
proxy.connect();
|
|
@@ -2571,8 +2576,11 @@ function getProxy(sessionId) {
|
|
|
2571
2576
|
function destroyProxy(sessionId) {
|
|
2572
2577
|
const proxy = activeProxies.get(sessionId);
|
|
2573
2578
|
if (proxy) {
|
|
2579
|
+
console.log(`[BROWSER-WS] destroyProxy() called for session ${sessionId}`);
|
|
2574
2580
|
proxy.destroy();
|
|
2575
2581
|
activeProxies.delete(sessionId);
|
|
2582
|
+
} else {
|
|
2583
|
+
console.log(`[BROWSER-WS] destroyProxy() called but no proxy exists for session ${sessionId}`);
|
|
2576
2584
|
}
|
|
2577
2585
|
}
|
|
2578
2586
|
var RECONNECT_DELAY_MS, MAX_RECONNECT_ATTEMPTS, FRAME_THROTTLE_MS, BrowserStreamProxy, activeProxies;
|
|
@@ -2603,18 +2611,22 @@ var init_stream_proxy = __esm({
|
|
|
2603
2611
|
}
|
|
2604
2612
|
connect() {
|
|
2605
2613
|
if (this.destroyed) return;
|
|
2614
|
+
console.log(`[BROWSER-WS] connect() called for port ${this.port}`);
|
|
2606
2615
|
this.doConnect();
|
|
2607
2616
|
}
|
|
2608
2617
|
doConnect() {
|
|
2609
2618
|
if (this.destroyed) return;
|
|
2610
2619
|
const url = `ws://localhost:${this.port}`;
|
|
2620
|
+
console.log(`[BROWSER-WS] Attempting WebSocket connection to ${url} (attempt ${this.reconnectAttempts + 1}/${MAX_RECONNECT_ATTEMPTS})`);
|
|
2611
2621
|
try {
|
|
2612
2622
|
this.ws = new WebSocket(url);
|
|
2613
|
-
} catch {
|
|
2623
|
+
} catch (err) {
|
|
2624
|
+
console.warn(`[BROWSER-WS] WebSocket constructor threw for ${url}:`, err);
|
|
2614
2625
|
this.scheduleReconnect();
|
|
2615
2626
|
return;
|
|
2616
2627
|
}
|
|
2617
2628
|
this.ws.on("open", () => {
|
|
2629
|
+
console.log(`[BROWSER-WS] Connected to ${url} (after ${this.reconnectAttempts} retries)`);
|
|
2618
2630
|
this.reconnectAttempts = 0;
|
|
2619
2631
|
this._connected = true;
|
|
2620
2632
|
this.emit("status", {
|
|
@@ -2626,12 +2638,14 @@ var init_stream_proxy = __esm({
|
|
|
2626
2638
|
try {
|
|
2627
2639
|
const msg = JSON.parse(typeof raw === "string" ? raw : raw.toString("utf8"));
|
|
2628
2640
|
this.handleMessage(msg);
|
|
2629
|
-
} catch {
|
|
2641
|
+
} catch (err) {
|
|
2642
|
+
console.warn(`[BROWSER-WS] Malformed message from ${url}:`, err);
|
|
2630
2643
|
}
|
|
2631
2644
|
});
|
|
2632
|
-
this.ws.on("close", () => {
|
|
2645
|
+
this.ws.on("close", (code, reason) => {
|
|
2633
2646
|
const wasConnected = this._connected;
|
|
2634
2647
|
this._connected = false;
|
|
2648
|
+
console.log(`[BROWSER-WS] Connection closed: code=${code} reason="${reason?.toString() || ""}" wasConnected=${wasConnected} destroyed=${this.destroyed}`);
|
|
2635
2649
|
if (wasConnected) {
|
|
2636
2650
|
this.emit("status", { connected: false, screencasting: false });
|
|
2637
2651
|
}
|
|
@@ -2639,14 +2653,26 @@ var init_stream_proxy = __esm({
|
|
|
2639
2653
|
this.scheduleReconnect();
|
|
2640
2654
|
}
|
|
2641
2655
|
});
|
|
2642
|
-
this.ws.on("error", () => {
|
|
2656
|
+
this.ws.on("error", (err) => {
|
|
2657
|
+
console.warn(`[BROWSER-WS] WebSocket error on port ${this.port}:`, err.message);
|
|
2643
2658
|
});
|
|
2644
2659
|
}
|
|
2660
|
+
frameCount = 0;
|
|
2661
|
+
throttledCount = 0;
|
|
2662
|
+
lastFrameLogTime = 0;
|
|
2645
2663
|
handleMessage(msg) {
|
|
2646
2664
|
if (msg.type === "frame") {
|
|
2647
2665
|
const now = Date.now();
|
|
2648
|
-
if (now - this.lastFrameTime < FRAME_THROTTLE_MS)
|
|
2666
|
+
if (now - this.lastFrameTime < FRAME_THROTTLE_MS) {
|
|
2667
|
+
this.throttledCount++;
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2649
2670
|
this.lastFrameTime = now;
|
|
2671
|
+
this.frameCount++;
|
|
2672
|
+
if (now - this.lastFrameLogTime > 5e3) {
|
|
2673
|
+
console.log(`[BROWSER-WS] Frame stats: emitted=${this.frameCount} throttled=${this.throttledCount} listeners=${this.listenerCount("frame")} dataSize=${msg.data?.length ?? 0}`);
|
|
2674
|
+
this.lastFrameLogTime = now;
|
|
2675
|
+
}
|
|
2650
2676
|
const frame = {
|
|
2651
2677
|
data: msg.data,
|
|
2652
2678
|
metadata: msg.metadata ?? {
|
|
@@ -2662,21 +2688,26 @@ var init_stream_proxy = __esm({
|
|
|
2662
2688
|
this._latestFrame = frame;
|
|
2663
2689
|
this.emit("frame", frame);
|
|
2664
2690
|
} else if (msg.type === "status") {
|
|
2691
|
+
console.log(`[BROWSER-WS] Status message received:`, JSON.stringify(msg));
|
|
2665
2692
|
this.emit("status", {
|
|
2666
2693
|
connected: msg.connected ?? true,
|
|
2667
2694
|
screencasting: msg.screencasting ?? true,
|
|
2668
2695
|
viewportWidth: msg.viewportWidth,
|
|
2669
2696
|
viewportHeight: msg.viewportHeight
|
|
2670
2697
|
});
|
|
2698
|
+
} else {
|
|
2699
|
+
console.log(`[BROWSER-WS] Unknown message type: ${msg.type}`);
|
|
2671
2700
|
}
|
|
2672
2701
|
}
|
|
2673
2702
|
scheduleReconnect() {
|
|
2674
2703
|
if (this.destroyed || this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
2704
|
+
console.log(`[BROWSER-WS] Giving up reconnection: destroyed=${this.destroyed} attempts=${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}`);
|
|
2675
2705
|
this.emit("close");
|
|
2676
2706
|
return;
|
|
2677
2707
|
}
|
|
2678
2708
|
this.reconnectAttempts++;
|
|
2679
2709
|
const delay = this.reconnectAttempts <= 5 ? RECONNECT_DELAY_MS : RECONNECT_DELAY_MS * (this.reconnectAttempts - 4);
|
|
2710
|
+
console.log(`[BROWSER-WS] Scheduling reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
|
|
2680
2711
|
this.reconnectTimer = setTimeout(() => this.doConnect(), delay);
|
|
2681
2712
|
}
|
|
2682
2713
|
/**
|
|
@@ -2688,6 +2719,7 @@ var init_stream_proxy = __esm({
|
|
|
2688
2719
|
}
|
|
2689
2720
|
}
|
|
2690
2721
|
destroy() {
|
|
2722
|
+
console.log(`[BROWSER-WS] Destroying proxy for port ${this.port} (emitted ${this.frameCount} frames, throttled ${this.throttledCount})`);
|
|
2691
2723
|
this.destroyed = true;
|
|
2692
2724
|
if (this.reconnectTimer) {
|
|
2693
2725
|
clearTimeout(this.reconnectTimer);
|
|
@@ -7256,6 +7288,21 @@ ${this.summary}`
|
|
|
7256
7288
|
|
|
7257
7289
|
// src/agent/index.ts
|
|
7258
7290
|
init_webhook();
|
|
7291
|
+
var MAX_SSE_FIELD_LENGTH = 8 * 1024;
|
|
7292
|
+
var SSE_PREVIEW_LENGTH = 2 * 1024;
|
|
7293
|
+
function truncateWriteFileInput(input) {
|
|
7294
|
+
const out = { ...input };
|
|
7295
|
+
for (const key of ["content", "old_string", "new_string"]) {
|
|
7296
|
+
const val = out[key];
|
|
7297
|
+
if (typeof val === "string" && val.length > MAX_SSE_FIELD_LENGTH) {
|
|
7298
|
+
out[key] = `${val.slice(0, SSE_PREVIEW_LENGTH)}
|
|
7299
|
+
... (truncated)`;
|
|
7300
|
+
out[`${key}Truncated`] = true;
|
|
7301
|
+
out[`${key}Length`] = val.length;
|
|
7302
|
+
}
|
|
7303
|
+
}
|
|
7304
|
+
return out;
|
|
7305
|
+
}
|
|
7259
7306
|
var approvalResolvers = /* @__PURE__ */ new Map();
|
|
7260
7307
|
var Agent = class _Agent {
|
|
7261
7308
|
session;
|
|
@@ -7499,8 +7546,11 @@ ${prompt}` });
|
|
|
7499
7546
|
};
|
|
7500
7547
|
let taskRecorder = null;
|
|
7501
7548
|
const sessionId = this.session.id;
|
|
7549
|
+
const emit = options.writeSSE;
|
|
7502
7550
|
const bashProgressHandler = (progress) => {
|
|
7503
7551
|
options.onToolProgress?.({ toolName: "bash", data: progress });
|
|
7552
|
+
if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "bash", data: progress })).catch(() => {
|
|
7553
|
+
});
|
|
7504
7554
|
const port = progress.browserStreamPort;
|
|
7505
7555
|
if (port && progress.status === "started") {
|
|
7506
7556
|
Promise.resolve().then(() => (init_stream_proxy(), stream_proxy_exports)).then(({ getOrCreateProxy: getOrCreateProxy2 }) => {
|
|
@@ -7509,7 +7559,17 @@ ${prompt}` });
|
|
|
7509
7559
|
Promise.resolve().then(() => (init_recorder(), recorder_exports)).then(({ FrameRecorder: FrameRecorder2 }) => {
|
|
7510
7560
|
taskRecorder = new FrameRecorder2(sessionId);
|
|
7511
7561
|
taskRecorder.start();
|
|
7512
|
-
|
|
7562
|
+
});
|
|
7563
|
+
}
|
|
7564
|
+
if (proxy.listenerCount("frame") === 0) {
|
|
7565
|
+
proxy.on("frame", (frame) => {
|
|
7566
|
+
taskRecorder?.addFrame(frame);
|
|
7567
|
+
if (emit) emit(JSON.stringify({ type: "browser-frame", data: frame.data, metadata: frame.metadata })).catch(() => {
|
|
7568
|
+
});
|
|
7569
|
+
});
|
|
7570
|
+
proxy.on("status", (s) => {
|
|
7571
|
+
if (emit) emit(JSON.stringify({ type: "browser-status", ...s })).catch(() => {
|
|
7572
|
+
});
|
|
7513
7573
|
});
|
|
7514
7574
|
}
|
|
7515
7575
|
});
|
|
@@ -7520,8 +7580,16 @@ ${prompt}` });
|
|
|
7520
7580
|
workingDirectory: this.session.workingDirectory,
|
|
7521
7581
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
7522
7582
|
onBashProgress: bashProgressHandler,
|
|
7523
|
-
onWriteFileProgress:
|
|
7524
|
-
|
|
7583
|
+
onWriteFileProgress: (progress) => {
|
|
7584
|
+
options.onToolProgress?.({ toolName: "write_file", data: progress });
|
|
7585
|
+
if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "write_file", data: progress })).catch(() => {
|
|
7586
|
+
});
|
|
7587
|
+
},
|
|
7588
|
+
onSearchProgress: (progress) => {
|
|
7589
|
+
options.onToolProgress?.({ toolName: "explore_agent", data: progress });
|
|
7590
|
+
if (emit) emit(JSON.stringify({ type: "tool-progress", toolName: "explore_agent", data: progress })).catch(() => {
|
|
7591
|
+
});
|
|
7592
|
+
},
|
|
7525
7593
|
taskTools: {
|
|
7526
7594
|
outputSchema: options.taskConfig.outputSchema,
|
|
7527
7595
|
onComplete
|
|
@@ -7539,6 +7607,9 @@ ${prompt}` });
|
|
|
7539
7607
|
|
|
7540
7608
|
${taskAddendum}`;
|
|
7541
7609
|
fireWebhook("task.started", { prompt: options.prompt });
|
|
7610
|
+
if (emit) {
|
|
7611
|
+
await emit(JSON.stringify({ type: "data-user-message", data: { id: `user_${Date.now()}`, content: options.prompt } }));
|
|
7612
|
+
}
|
|
7542
7613
|
await this.context.addUserMessage(options.prompt);
|
|
7543
7614
|
let iteration = 0;
|
|
7544
7615
|
while (iteration < maxIterations) {
|
|
@@ -7550,7 +7621,15 @@ ${taskAddendum}`;
|
|
|
7550
7621
|
}
|
|
7551
7622
|
const messages = await this.context.getMessages();
|
|
7552
7623
|
const useAnthropic = isAnthropicModel(this.session.model);
|
|
7553
|
-
|
|
7624
|
+
if (emit) {
|
|
7625
|
+
await emit(JSON.stringify({ type: "start", messageId: `msg_${Date.now()}` }));
|
|
7626
|
+
}
|
|
7627
|
+
let textStarted = false;
|
|
7628
|
+
let textId = `text_${Date.now()}`;
|
|
7629
|
+
let reasoningId = `reasoning_${Date.now()}`;
|
|
7630
|
+
let reasoningStarted = false;
|
|
7631
|
+
const toolCallStarts = /* @__PURE__ */ new Set();
|
|
7632
|
+
const iterStream = streamText2({
|
|
7554
7633
|
model: resolveModel(this.session.model),
|
|
7555
7634
|
system: systemPrompt,
|
|
7556
7635
|
messages,
|
|
@@ -7559,21 +7638,94 @@ ${taskAddendum}`;
|
|
|
7559
7638
|
abortSignal: options.abortSignal,
|
|
7560
7639
|
providerOptions: useAnthropic ? {
|
|
7561
7640
|
anthropic: {
|
|
7641
|
+
toolStreaming: true,
|
|
7562
7642
|
thinking: { type: "enabled", budgetTokens: 1e4 }
|
|
7563
7643
|
}
|
|
7564
7644
|
} : void 0,
|
|
7565
|
-
onStepFinish: (step) => {
|
|
7645
|
+
onStepFinish: async (step) => {
|
|
7566
7646
|
options.onStepFinish?.(step);
|
|
7567
7647
|
fireWebhook("task.step_finished", { iteration, text: step.text });
|
|
7648
|
+
if (emit) {
|
|
7649
|
+
if (textStarted) {
|
|
7650
|
+
await emit(JSON.stringify({ type: "text-end", id: textId }));
|
|
7651
|
+
textStarted = false;
|
|
7652
|
+
textId = `text_${Date.now()}`;
|
|
7653
|
+
}
|
|
7654
|
+
await emit(JSON.stringify({ type: "finish-step" }));
|
|
7655
|
+
}
|
|
7568
7656
|
}
|
|
7569
7657
|
});
|
|
7570
|
-
const
|
|
7658
|
+
for await (const part of iterStream.fullStream) {
|
|
7659
|
+
if (part.type === "text-delta") {
|
|
7660
|
+
if (emit) {
|
|
7661
|
+
if (!textStarted) {
|
|
7662
|
+
await emit(JSON.stringify({ type: "text-start", id: textId }));
|
|
7663
|
+
textStarted = true;
|
|
7664
|
+
}
|
|
7665
|
+
await emit(JSON.stringify({ type: "text-delta", id: textId, delta: part.text }));
|
|
7666
|
+
}
|
|
7667
|
+
} else if (part.type === "reasoning-start") {
|
|
7668
|
+
if (emit) {
|
|
7669
|
+
await emit(JSON.stringify({ type: "reasoning-start", id: reasoningId }));
|
|
7670
|
+
reasoningStarted = true;
|
|
7671
|
+
}
|
|
7672
|
+
} else if (part.type === "reasoning-delta") {
|
|
7673
|
+
if (emit) {
|
|
7674
|
+
await emit(JSON.stringify({ type: "reasoning-delta", id: reasoningId, delta: part.text }));
|
|
7675
|
+
}
|
|
7676
|
+
} else if (part.type === "reasoning-end") {
|
|
7677
|
+
if (emit && reasoningStarted) {
|
|
7678
|
+
await emit(JSON.stringify({ type: "reasoning-end", id: reasoningId }));
|
|
7679
|
+
reasoningStarted = false;
|
|
7680
|
+
reasoningId = `reasoning_${Date.now()}`;
|
|
7681
|
+
}
|
|
7682
|
+
} else if (part.type === "tool-call-streaming-start") {
|
|
7683
|
+
if (emit) {
|
|
7684
|
+
const p = part;
|
|
7685
|
+
await emit(JSON.stringify({ type: "tool-input-start", toolCallId: p.toolCallId, toolName: p.toolName }));
|
|
7686
|
+
toolCallStarts.add(p.toolCallId);
|
|
7687
|
+
}
|
|
7688
|
+
} else if (part.type === "tool-call-delta") {
|
|
7689
|
+
if (emit) {
|
|
7690
|
+
const p = part;
|
|
7691
|
+
await emit(JSON.stringify({ type: "tool-input-delta", toolCallId: p.toolCallId, argsTextDelta: p.argsTextDelta }));
|
|
7692
|
+
}
|
|
7693
|
+
} else if (part.type === "tool-call") {
|
|
7694
|
+
if (emit) {
|
|
7695
|
+
if (!toolCallStarts.has(part.toolCallId)) {
|
|
7696
|
+
await emit(JSON.stringify({ type: "tool-input-start", toolCallId: part.toolCallId, toolName: part.toolName }));
|
|
7697
|
+
toolCallStarts.add(part.toolCallId);
|
|
7698
|
+
}
|
|
7699
|
+
const safeInput = part.toolName === "write_file" && part.input && typeof part.input === "object" ? truncateWriteFileInput(part.input) : part.input;
|
|
7700
|
+
await emit(JSON.stringify({ type: "tool-input-available", toolCallId: part.toolCallId, toolName: part.toolName, input: safeInput }));
|
|
7701
|
+
}
|
|
7702
|
+
} else if (part.type === "tool-result") {
|
|
7703
|
+
if (emit) {
|
|
7704
|
+
await emit(JSON.stringify({ type: "tool-output-available", toolCallId: part.toolCallId, output: part.output }));
|
|
7705
|
+
}
|
|
7706
|
+
} else if (part.type === "error") {
|
|
7707
|
+
console.error("Task stream error:", part.error);
|
|
7708
|
+
if (emit) {
|
|
7709
|
+
await emit(JSON.stringify({ type: "error", errorText: String(part.error) }));
|
|
7710
|
+
}
|
|
7711
|
+
}
|
|
7712
|
+
}
|
|
7713
|
+
if (emit && textStarted) {
|
|
7714
|
+
await emit(JSON.stringify({ type: "text-end", id: textId }));
|
|
7715
|
+
}
|
|
7716
|
+
if (emit && reasoningStarted) {
|
|
7717
|
+
await emit(JSON.stringify({ type: "reasoning-end", id: reasoningId }));
|
|
7718
|
+
}
|
|
7719
|
+
const iterResponse = await iterStream.response;
|
|
7720
|
+
const responseMessages = iterResponse.messages;
|
|
7571
7721
|
await this.context.addResponseMessages(responseMessages);
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7722
|
+
const resultText = await iterStream.text;
|
|
7723
|
+
const resultSteps = await iterStream.steps;
|
|
7724
|
+
if (resultText) {
|
|
7725
|
+
options.onText?.(resultText);
|
|
7726
|
+
fireWebhook("task.message", { iteration, text: resultText });
|
|
7575
7727
|
}
|
|
7576
|
-
for (const step of
|
|
7728
|
+
for (const step of resultSteps) {
|
|
7577
7729
|
if (step.toolCalls) {
|
|
7578
7730
|
for (const tc of step.toolCalls) {
|
|
7579
7731
|
options.onToolCall?.({ toolCallId: tc.toolCallId, toolName: tc.toolName, input: tc.args });
|
|
@@ -7626,9 +7778,11 @@ ${taskAddendum}`;
|
|
|
7626
7778
|
iterations: iteration
|
|
7627
7779
|
};
|
|
7628
7780
|
}
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7781
|
+
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.";
|
|
7782
|
+
if (emit) {
|
|
7783
|
+
await emit(JSON.stringify({ type: "data-user-message", data: { id: `user_${Date.now()}`, content: continuationPrompt } }));
|
|
7784
|
+
}
|
|
7785
|
+
await this.context.addUserMessage(continuationPrompt);
|
|
7632
7786
|
}
|
|
7633
7787
|
const timeoutError = `Task did not complete within ${maxIterations} iterations`;
|
|
7634
7788
|
const timeoutRecordingUrls = await this.finishTaskRecording(taskRecorder);
|
|
@@ -8719,11 +8873,19 @@ var cleanupInterval = setInterval(() => {
|
|
|
8719
8873
|
}
|
|
8720
8874
|
}, 6e4);
|
|
8721
8875
|
cleanupInterval.unref();
|
|
8876
|
+
var publishCount = 0;
|
|
8877
|
+
var lastPublishLog = 0;
|
|
8722
8878
|
var publisher = {
|
|
8723
8879
|
connect: async () => {
|
|
8724
8880
|
},
|
|
8725
8881
|
publish: async (channel, message) => {
|
|
8726
8882
|
const subscribers = channels.get(channel);
|
|
8883
|
+
publishCount++;
|
|
8884
|
+
const now = Date.now();
|
|
8885
|
+
if (now - lastPublishLog > 1e4) {
|
|
8886
|
+
console.log(`[ResumableStream] Publish stats: total=${publishCount}, channels=${channels.size}, store=${store.size}`);
|
|
8887
|
+
lastPublishLog = now;
|
|
8888
|
+
}
|
|
8727
8889
|
if (subscribers) {
|
|
8728
8890
|
for (const callback of subscribers) {
|
|
8729
8891
|
setImmediate(() => callback(message));
|
|
@@ -8762,9 +8924,12 @@ var subscriber = {
|
|
|
8762
8924
|
channels.set(channel, /* @__PURE__ */ new Set());
|
|
8763
8925
|
}
|
|
8764
8926
|
channels.get(channel).add(callback);
|
|
8927
|
+
console.log(`[ResumableStream] Subscribe to channel "${channel}" (total subscribers: ${channels.get(channel).size})`);
|
|
8765
8928
|
},
|
|
8766
8929
|
unsubscribe: async (channel) => {
|
|
8930
|
+
const count = channels.get(channel)?.size ?? 0;
|
|
8767
8931
|
channels.delete(channel);
|
|
8932
|
+
console.log(`[ResumableStream] Unsubscribe from channel "${channel}" (removed ${count} subscribers)`);
|
|
8768
8933
|
}
|
|
8769
8934
|
};
|
|
8770
8935
|
var streamContext = createResumableStreamContext({
|
|
@@ -8950,19 +9115,28 @@ function createAgentStreamProducer(sessionId, prompt, streamId, attachments) {
|
|
|
8950
9115
|
const toolCallStarts = /* @__PURE__ */ new Set();
|
|
8951
9116
|
const abortController = new AbortController();
|
|
8952
9117
|
streamAbortControllers.set(streamId, abortController);
|
|
9118
|
+
let sseEventCount = 0;
|
|
9119
|
+
let sseBrowserFrameCount = 0;
|
|
9120
|
+
let sseWriteErrors = 0;
|
|
8953
9121
|
const writeSSE = async (data) => {
|
|
8954
9122
|
if (writerClosed) return;
|
|
8955
9123
|
try {
|
|
9124
|
+
sseEventCount++;
|
|
8956
9125
|
await writer.write(`data: ${data}
|
|
8957
9126
|
|
|
8958
9127
|
`);
|
|
8959
9128
|
} catch (err) {
|
|
9129
|
+
sseWriteErrors++;
|
|
9130
|
+
if (sseWriteErrors === 1) {
|
|
9131
|
+
console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
|
|
9132
|
+
}
|
|
8960
9133
|
writerClosed = true;
|
|
8961
9134
|
}
|
|
8962
9135
|
};
|
|
8963
9136
|
const safeClose = async () => {
|
|
8964
9137
|
if (writerClosed) return;
|
|
8965
9138
|
try {
|
|
9139
|
+
console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
|
|
8966
9140
|
writerClosed = true;
|
|
8967
9141
|
await writer.close();
|
|
8968
9142
|
} catch {
|
|
@@ -9085,35 +9259,47 @@ ${prompt}` });
|
|
|
9085
9259
|
const browserPort = progress.data?.browserStreamPort;
|
|
9086
9260
|
const browserClosed = progress.data?.browserClosed;
|
|
9087
9261
|
if (progress.toolName === "bash" && browserClosed) {
|
|
9088
|
-
console.log(`[BROWSER-STREAM] agent-browser close detected, destroying proxy`);
|
|
9262
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${sessionId}`);
|
|
9089
9263
|
destroyProxy(sessionId);
|
|
9090
9264
|
} else if (progress.toolName === "bash" && browserPort) {
|
|
9091
|
-
console.log(`[BROWSER-STREAM] agent-browser command detected, ensuring proxy on port ${browserPort}`);
|
|
9265
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, ensuring proxy on port ${browserPort} for session ${sessionId}`);
|
|
9092
9266
|
const proxy = getOrCreateProxy(sessionId, browserPort);
|
|
9267
|
+
console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
|
|
9093
9268
|
if (!sessionRecorders.has(sessionId)) {
|
|
9094
9269
|
const recorder = new FrameRecorder(sessionId);
|
|
9095
9270
|
recorder.start();
|
|
9096
9271
|
sessionRecorders.set(sessionId, recorder);
|
|
9097
9272
|
}
|
|
9098
9273
|
if (proxy.listenerCount("frame") === 0) {
|
|
9274
|
+
console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
|
|
9099
9275
|
proxy.on("frame", (frame) => {
|
|
9276
|
+
sseBrowserFrameCount++;
|
|
9277
|
+
if (sseBrowserFrameCount === 1) {
|
|
9278
|
+
console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
|
|
9279
|
+
} else if (sseBrowserFrameCount % 50 === 0) {
|
|
9280
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
|
|
9281
|
+
}
|
|
9100
9282
|
const rec = sessionRecorders.get(sessionId);
|
|
9101
9283
|
rec?.addFrame(frame);
|
|
9102
9284
|
writeSSE(JSON.stringify({
|
|
9103
9285
|
type: "browser-frame",
|
|
9104
9286
|
data: frame.data,
|
|
9105
9287
|
metadata: frame.metadata
|
|
9106
|
-
})).catch(() => {
|
|
9288
|
+
})).catch((err) => {
|
|
9289
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
|
|
9107
9290
|
});
|
|
9108
9291
|
});
|
|
9109
9292
|
proxy.on("status", (s) => {
|
|
9110
|
-
console.log(`[BROWSER-STREAM]
|
|
9293
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
|
|
9111
9294
|
writeSSE(JSON.stringify({
|
|
9112
9295
|
type: "browser-status",
|
|
9113
9296
|
...s
|
|
9114
|
-
})).catch(() => {
|
|
9297
|
+
})).catch((err) => {
|
|
9298
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
|
|
9115
9299
|
});
|
|
9116
9300
|
});
|
|
9301
|
+
} else {
|
|
9302
|
+
console.log(`[BROWSER-STREAM:${streamId}] Frame listeners already attached (count=${proxy.listenerCount("frame")}), skipping`);
|
|
9117
9303
|
}
|
|
9118
9304
|
}
|
|
9119
9305
|
},
|
|
@@ -9303,14 +9489,17 @@ ${prompt}` });
|
|
|
9303
9489
|
}
|
|
9304
9490
|
await messageQueries.create(id, { role: "user", content: userMessageContent });
|
|
9305
9491
|
const streamId = `stream_${id}_${nanoid6(10)}`;
|
|
9492
|
+
console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
|
|
9306
9493
|
await activeStreamQueries.create(id, streamId);
|
|
9307
9494
|
const stream = await streamContext.resumableStream(
|
|
9308
9495
|
streamId,
|
|
9309
9496
|
createAgentStreamProducer(id, prompt, streamId, streamAttachments)
|
|
9310
9497
|
);
|
|
9311
9498
|
if (!stream) {
|
|
9499
|
+
console.error(`[STREAM] Failed to create resumable stream ${streamId}`);
|
|
9312
9500
|
return c.json({ error: "Failed to create stream" }, 500);
|
|
9313
9501
|
}
|
|
9502
|
+
console.log(`[STREAM] Stream ${streamId} created successfully`);
|
|
9314
9503
|
const encodedStream = stream.pipeThrough(new TextEncoderStream());
|
|
9315
9504
|
return new Response(encodedStream, {
|
|
9316
9505
|
headers: {
|
|
@@ -9339,17 +9528,20 @@ agents.get("/:id/watch", async (c) => {
|
|
|
9339
9528
|
}
|
|
9340
9529
|
streamId = activeStream.streamId;
|
|
9341
9530
|
}
|
|
9531
|
+
console.log(`[STREAM] Watch request for session ${sessionId}, streamId=${streamId}, resumeAt=${resumeAt || "none"}`);
|
|
9342
9532
|
const stream = await streamContext.resumeExistingStream(
|
|
9343
9533
|
streamId,
|
|
9344
9534
|
resumeAt ? parseInt(resumeAt, 10) : void 0
|
|
9345
9535
|
);
|
|
9346
9536
|
if (!stream) {
|
|
9537
|
+
console.log(`[STREAM] Watch failed \u2014 stream ${streamId} is no longer active`);
|
|
9347
9538
|
return c.json({
|
|
9348
9539
|
error: "Stream is no longer active",
|
|
9349
9540
|
streamId,
|
|
9350
9541
|
hint: "The stream may have finished. Check /agents/:id/approvals or start a new run."
|
|
9351
9542
|
}, 422);
|
|
9352
9543
|
}
|
|
9544
|
+
console.log(`[STREAM] Client watching stream ${streamId}`);
|
|
9353
9545
|
const encodedStream = stream.pipeThrough(new TextEncoderStream());
|
|
9354
9546
|
return new Response(encodedStream, {
|
|
9355
9547
|
headers: {
|
|
@@ -9511,19 +9703,28 @@ agents.post(
|
|
|
9511
9703
|
const toolCallStarts = /* @__PURE__ */ new Set();
|
|
9512
9704
|
const abortController = new AbortController();
|
|
9513
9705
|
streamAbortControllers.set(streamId, abortController);
|
|
9706
|
+
let sseEventCount = 0;
|
|
9707
|
+
let sseBrowserFrameCount = 0;
|
|
9708
|
+
let sseWriteErrors = 0;
|
|
9514
9709
|
const writeSSE = async (data) => {
|
|
9515
9710
|
if (writerClosed) return;
|
|
9516
9711
|
try {
|
|
9712
|
+
sseEventCount++;
|
|
9517
9713
|
await writer.write(`data: ${data}
|
|
9518
9714
|
|
|
9519
9715
|
`);
|
|
9520
9716
|
} catch (err) {
|
|
9717
|
+
sseWriteErrors++;
|
|
9718
|
+
if (sseWriteErrors === 1) {
|
|
9719
|
+
console.log(`[SSE:${streamId}] Writer closed (client disconnected). Total events sent: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}`);
|
|
9720
|
+
}
|
|
9521
9721
|
writerClosed = true;
|
|
9522
9722
|
}
|
|
9523
9723
|
};
|
|
9524
9724
|
const safeClose = async () => {
|
|
9525
9725
|
if (writerClosed) return;
|
|
9526
9726
|
try {
|
|
9727
|
+
console.log(`[SSE:${streamId}] Stream closing. Total events: ${sseEventCount}, browser frames: ${sseBrowserFrameCount}, write errors: ${sseWriteErrors}`);
|
|
9527
9728
|
writerClosed = true;
|
|
9528
9729
|
await writer.close();
|
|
9529
9730
|
} catch {
|
|
@@ -9583,35 +9784,47 @@ agents.post(
|
|
|
9583
9784
|
const browserPort = progress.data?.browserStreamPort;
|
|
9584
9785
|
const browserClosed = progress.data?.browserClosed;
|
|
9585
9786
|
if (progress.toolName === "bash" && browserClosed) {
|
|
9586
|
-
console.log(`[BROWSER-STREAM] agent-browser close detected`);
|
|
9787
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser close detected, destroying proxy for session ${session.id}`);
|
|
9587
9788
|
destroyProxy(session.id);
|
|
9588
9789
|
} else if (progress.toolName === "bash" && browserPort) {
|
|
9589
|
-
console.log(`[BROWSER-STREAM] agent-browser command detected, port ${browserPort}`);
|
|
9790
|
+
console.log(`[BROWSER-STREAM:${streamId}] agent-browser command detected, port ${browserPort} for session ${session.id}`);
|
|
9590
9791
|
const proxy = getOrCreateProxy(session.id, browserPort);
|
|
9792
|
+
console.log(`[BROWSER-STREAM:${streamId}] Proxy state: connected=${proxy.connected}, frameListeners=${proxy.listenerCount("frame")}, statusListeners=${proxy.listenerCount("status")}`);
|
|
9591
9793
|
if (!sessionRecorders.has(session.id)) {
|
|
9592
9794
|
const recorder = new FrameRecorder(session.id);
|
|
9593
9795
|
recorder.start();
|
|
9594
9796
|
sessionRecorders.set(session.id, recorder);
|
|
9595
9797
|
}
|
|
9596
9798
|
if (proxy.listenerCount("frame") === 0) {
|
|
9799
|
+
console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
|
|
9597
9800
|
proxy.on("frame", (frame) => {
|
|
9801
|
+
sseBrowserFrameCount++;
|
|
9802
|
+
if (sseBrowserFrameCount === 1) {
|
|
9803
|
+
console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
|
|
9804
|
+
} else if (sseBrowserFrameCount % 50 === 0) {
|
|
9805
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
|
|
9806
|
+
}
|
|
9598
9807
|
const rec = sessionRecorders.get(session.id);
|
|
9599
9808
|
rec?.addFrame(frame);
|
|
9600
9809
|
writeSSE(JSON.stringify({
|
|
9601
9810
|
type: "browser-frame",
|
|
9602
9811
|
data: frame.data,
|
|
9603
9812
|
metadata: frame.metadata
|
|
9604
|
-
})).catch(() => {
|
|
9813
|
+
})).catch((err) => {
|
|
9814
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
|
|
9605
9815
|
});
|
|
9606
9816
|
});
|
|
9607
9817
|
proxy.on("status", (s) => {
|
|
9608
|
-
console.log(`[BROWSER-STREAM]
|
|
9818
|
+
console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
|
|
9609
9819
|
writeSSE(JSON.stringify({
|
|
9610
9820
|
type: "browser-status",
|
|
9611
9821
|
...s
|
|
9612
|
-
})).catch(() => {
|
|
9822
|
+
})).catch((err) => {
|
|
9823
|
+
console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
|
|
9613
9824
|
});
|
|
9614
9825
|
});
|
|
9826
|
+
} else {
|
|
9827
|
+
console.log(`[BROWSER-STREAM:${streamId}] Frame listeners already attached (count=${proxy.listenerCount("frame")}), skipping`);
|
|
9615
9828
|
}
|
|
9616
9829
|
}
|
|
9617
9830
|
},
|
|
@@ -10260,6 +10473,7 @@ init_db();
|
|
|
10260
10473
|
import { Hono as Hono5 } from "hono";
|
|
10261
10474
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
10262
10475
|
import { z as z19 } from "zod";
|
|
10476
|
+
import { nanoid as nanoid7 } from "nanoid";
|
|
10263
10477
|
init_config();
|
|
10264
10478
|
var tasks = new Hono5();
|
|
10265
10479
|
var taskAbortControllers = /* @__PURE__ */ new Map();
|
|
@@ -10297,45 +10511,74 @@ tasks.post(
|
|
|
10297
10511
|
const taskId = agent.sessionId;
|
|
10298
10512
|
const abortController = new AbortController();
|
|
10299
10513
|
taskAbortControllers.set(taskId, abortController);
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10514
|
+
const streamId = `stream_${taskId}_${nanoid7(10)}`;
|
|
10515
|
+
await activeStreamQueries.create(taskId, streamId);
|
|
10516
|
+
const taskStreamProducer = () => {
|
|
10517
|
+
const { readable, writable } = new TransformStream();
|
|
10518
|
+
const writer = writable.getWriter();
|
|
10519
|
+
let writerClosed = false;
|
|
10520
|
+
const writeSSE = async (data) => {
|
|
10521
|
+
if (writerClosed) return;
|
|
10522
|
+
try {
|
|
10523
|
+
await writer.write(`data: ${data}
|
|
10524
|
+
|
|
10525
|
+
`);
|
|
10526
|
+
} catch {
|
|
10527
|
+
writerClosed = true;
|
|
10528
|
+
}
|
|
10529
|
+
};
|
|
10530
|
+
(async () => {
|
|
10531
|
+
await writeSSE(JSON.stringify({ type: "data-stream-id", streamId }));
|
|
10532
|
+
try {
|
|
10533
|
+
await agent.runTask({
|
|
10534
|
+
prompt: body.prompt,
|
|
10535
|
+
taskConfig,
|
|
10536
|
+
abortSignal: abortController.signal,
|
|
10537
|
+
writeSSE
|
|
10323
10538
|
});
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10539
|
+
await writeSSE(JSON.stringify({ type: "finish" }));
|
|
10540
|
+
} catch (err) {
|
|
10541
|
+
if (err.name === "AbortError" || abortController.signal.aborted) {
|
|
10542
|
+
console.log(`[TASK] Task ${taskId} was cancelled`);
|
|
10543
|
+
await writeSSE(JSON.stringify({ type: "abort" }));
|
|
10544
|
+
} else {
|
|
10545
|
+
console.error(`[TASK] Error in task ${taskId}:`, err.message);
|
|
10546
|
+
const errorMsg = err.message || "Unknown error";
|
|
10547
|
+
await writeSSE(JSON.stringify({ type: "error", errorText: errorMsg }));
|
|
10548
|
+
const failedTask = {
|
|
10549
|
+
...taskConfig,
|
|
10550
|
+
status: "failed",
|
|
10551
|
+
error: errorMsg
|
|
10552
|
+
};
|
|
10553
|
+
await sessionQueries.update(taskId, {
|
|
10554
|
+
config: {
|
|
10555
|
+
toolApprovals: { bash: false, write_file: false, read_file: false },
|
|
10556
|
+
task: failedTask
|
|
10557
|
+
}
|
|
10332
10558
|
});
|
|
10559
|
+
if (taskConfig.webhookUrl) {
|
|
10560
|
+
const { sendWebhook: sendWebhook2 } = await Promise.resolve().then(() => (init_webhook(), webhook_exports));
|
|
10561
|
+
sendWebhook2(taskConfig.webhookUrl, {
|
|
10562
|
+
type: "task.failed",
|
|
10563
|
+
taskId,
|
|
10564
|
+
sessionId: taskId,
|
|
10565
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10566
|
+
data: { status: "failed", error: errorMsg }
|
|
10567
|
+
});
|
|
10568
|
+
}
|
|
10333
10569
|
}
|
|
10570
|
+
} finally {
|
|
10571
|
+
await writeSSE("[DONE]");
|
|
10572
|
+
writer.close().catch(() => {
|
|
10573
|
+
});
|
|
10574
|
+
await activeStreamQueries.finish(streamId).catch(() => {
|
|
10575
|
+
});
|
|
10576
|
+
taskAbortControllers.delete(taskId);
|
|
10334
10577
|
}
|
|
10335
|
-
}
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10578
|
+
})();
|
|
10579
|
+
return readable;
|
|
10580
|
+
};
|
|
10581
|
+
await streamContext.resumableStream(streamId, taskStreamProducer);
|
|
10339
10582
|
return c.json({ taskId, status: "running" }, 201);
|
|
10340
10583
|
}
|
|
10341
10584
|
);
|