@sideboard-ai/core 0.1.88 → 0.1.89
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/agents/cursor-runner.cjs +47 -1
- package/dist/agents/cursor-runner.js +7 -1
- package/dist/chunk-MMI4RJ5I.js +46 -0
- package/dist/index.cjs +57 -8
- package/dist/index.js +15 -8
- package/dist/mcp/run-stdio.cjs +57 -8
- package/dist/mcp/run-stdio.js +55 -8
- package/package.json +1 -1
|
@@ -286,6 +286,49 @@ function cursorSdkMessageToEvents(msg) {
|
|
|
286
286
|
return [];
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
// src/agents/cursor-stream-coalesce.ts
|
|
290
|
+
function isTextEvent(event) {
|
|
291
|
+
return event.type === "stdout" || event.type === "thinking";
|
|
292
|
+
}
|
|
293
|
+
function createAgentStreamCoalescer(emit2, opts) {
|
|
294
|
+
const intervalMs = opts?.intervalMs ?? 32;
|
|
295
|
+
let pending = null;
|
|
296
|
+
let timer = null;
|
|
297
|
+
const flush = () => {
|
|
298
|
+
if (timer) {
|
|
299
|
+
clearTimeout(timer);
|
|
300
|
+
timer = null;
|
|
301
|
+
}
|
|
302
|
+
if (!pending) return;
|
|
303
|
+
const event = pending;
|
|
304
|
+
pending = null;
|
|
305
|
+
emit2({ type: event.type, data: event.data });
|
|
306
|
+
};
|
|
307
|
+
const schedule = () => {
|
|
308
|
+
if (timer) return;
|
|
309
|
+
timer = setTimeout(flush, intervalMs);
|
|
310
|
+
timer.unref?.();
|
|
311
|
+
};
|
|
312
|
+
return {
|
|
313
|
+
push(event) {
|
|
314
|
+
if (!isTextEvent(event)) {
|
|
315
|
+
flush();
|
|
316
|
+
emit2(event);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (!event.data) return;
|
|
320
|
+
if (pending && pending.type === event.type) {
|
|
321
|
+
pending.data += event.data;
|
|
322
|
+
} else {
|
|
323
|
+
flush();
|
|
324
|
+
pending = { type: event.type, data: event.data };
|
|
325
|
+
}
|
|
326
|
+
schedule();
|
|
327
|
+
},
|
|
328
|
+
flush
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
289
332
|
// src/agents/cursor-runner.ts
|
|
290
333
|
dropNestedElectronEnvFromProcess();
|
|
291
334
|
function emit(event) {
|
|
@@ -442,12 +485,14 @@ async function main() {
|
|
|
442
485
|
emit({ type: "session_id", data: agent.agentId });
|
|
443
486
|
const run = await sendPrompt(agent);
|
|
444
487
|
liveRun = run;
|
|
488
|
+
const stream = createAgentStreamCoalescer(emit);
|
|
445
489
|
try {
|
|
446
490
|
for await (const msg of run.stream()) {
|
|
447
491
|
for (const event of cursorSdkMessageToEvents(msg)) {
|
|
448
|
-
|
|
492
|
+
stream.push(event);
|
|
449
493
|
}
|
|
450
494
|
}
|
|
495
|
+
stream.flush();
|
|
451
496
|
const result = await run.wait();
|
|
452
497
|
if (result.status === "error") {
|
|
453
498
|
const detail = formatUnknownDetail(result.error);
|
|
@@ -460,6 +505,7 @@ async function main() {
|
|
|
460
505
|
if (result.status === "cancelled") return 0;
|
|
461
506
|
return 0;
|
|
462
507
|
} finally {
|
|
508
|
+
stream.flush();
|
|
463
509
|
liveRun = null;
|
|
464
510
|
}
|
|
465
511
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createAgentStreamCoalescer
|
|
4
|
+
} from "../chunk-MMI4RJ5I.js";
|
|
2
5
|
import {
|
|
3
6
|
cursorSdkMessageToEvents,
|
|
4
7
|
formatUnknownDetail
|
|
@@ -208,12 +211,14 @@ async function main() {
|
|
|
208
211
|
emit({ type: "session_id", data: agent.agentId });
|
|
209
212
|
const run = await sendPrompt(agent);
|
|
210
213
|
liveRun = run;
|
|
214
|
+
const stream = createAgentStreamCoalescer(emit);
|
|
211
215
|
try {
|
|
212
216
|
for await (const msg of run.stream()) {
|
|
213
217
|
for (const event of cursorSdkMessageToEvents(msg)) {
|
|
214
|
-
|
|
218
|
+
stream.push(event);
|
|
215
219
|
}
|
|
216
220
|
}
|
|
221
|
+
stream.flush();
|
|
217
222
|
const result = await run.wait();
|
|
218
223
|
if (result.status === "error") {
|
|
219
224
|
const detail = formatUnknownDetail(result.error);
|
|
@@ -226,6 +231,7 @@ async function main() {
|
|
|
226
231
|
if (result.status === "cancelled") return 0;
|
|
227
232
|
return 0;
|
|
228
233
|
} finally {
|
|
234
|
+
stream.flush();
|
|
229
235
|
liveRun = null;
|
|
230
236
|
}
|
|
231
237
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/agents/cursor-stream-coalesce.ts
|
|
2
|
+
function isTextEvent(event) {
|
|
3
|
+
return event.type === "stdout" || event.type === "thinking";
|
|
4
|
+
}
|
|
5
|
+
function createAgentStreamCoalescer(emit, opts) {
|
|
6
|
+
const intervalMs = opts?.intervalMs ?? 32;
|
|
7
|
+
let pending = null;
|
|
8
|
+
let timer = null;
|
|
9
|
+
const flush = () => {
|
|
10
|
+
if (timer) {
|
|
11
|
+
clearTimeout(timer);
|
|
12
|
+
timer = null;
|
|
13
|
+
}
|
|
14
|
+
if (!pending) return;
|
|
15
|
+
const event = pending;
|
|
16
|
+
pending = null;
|
|
17
|
+
emit({ type: event.type, data: event.data });
|
|
18
|
+
};
|
|
19
|
+
const schedule = () => {
|
|
20
|
+
if (timer) return;
|
|
21
|
+
timer = setTimeout(flush, intervalMs);
|
|
22
|
+
timer.unref?.();
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
push(event) {
|
|
26
|
+
if (!isTextEvent(event)) {
|
|
27
|
+
flush();
|
|
28
|
+
emit(event);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!event.data) return;
|
|
32
|
+
if (pending && pending.type === event.type) {
|
|
33
|
+
pending.data += event.data;
|
|
34
|
+
} else {
|
|
35
|
+
flush();
|
|
36
|
+
pending = { type: event.type, data: event.data };
|
|
37
|
+
}
|
|
38
|
+
schedule();
|
|
39
|
+
},
|
|
40
|
+
flush
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
createAgentStreamCoalescer
|
|
46
|
+
};
|
package/dist/index.cjs
CHANGED
|
@@ -10441,6 +10441,51 @@ function normalizeParseResult(parsed) {
|
|
|
10441
10441
|
// src/agents/spawn.ts
|
|
10442
10442
|
init_path();
|
|
10443
10443
|
init_usage();
|
|
10444
|
+
|
|
10445
|
+
// src/agents/cursor-stream-coalesce.ts
|
|
10446
|
+
function isTextEvent(event) {
|
|
10447
|
+
return event.type === "stdout" || event.type === "thinking";
|
|
10448
|
+
}
|
|
10449
|
+
function createAgentStreamCoalescer(emit, opts) {
|
|
10450
|
+
const intervalMs = opts?.intervalMs ?? 32;
|
|
10451
|
+
let pending = null;
|
|
10452
|
+
let timer = null;
|
|
10453
|
+
const flush = () => {
|
|
10454
|
+
if (timer) {
|
|
10455
|
+
clearTimeout(timer);
|
|
10456
|
+
timer = null;
|
|
10457
|
+
}
|
|
10458
|
+
if (!pending) return;
|
|
10459
|
+
const event = pending;
|
|
10460
|
+
pending = null;
|
|
10461
|
+
emit({ type: event.type, data: event.data });
|
|
10462
|
+
};
|
|
10463
|
+
const schedule = () => {
|
|
10464
|
+
if (timer) return;
|
|
10465
|
+
timer = setTimeout(flush, intervalMs);
|
|
10466
|
+
timer.unref?.();
|
|
10467
|
+
};
|
|
10468
|
+
return {
|
|
10469
|
+
push(event) {
|
|
10470
|
+
if (!isTextEvent(event)) {
|
|
10471
|
+
flush();
|
|
10472
|
+
emit(event);
|
|
10473
|
+
return;
|
|
10474
|
+
}
|
|
10475
|
+
if (!event.data) return;
|
|
10476
|
+
if (pending && pending.type === event.type) {
|
|
10477
|
+
pending.data += event.data;
|
|
10478
|
+
} else {
|
|
10479
|
+
flush();
|
|
10480
|
+
pending = { type: event.type, data: event.data };
|
|
10481
|
+
}
|
|
10482
|
+
schedule();
|
|
10483
|
+
},
|
|
10484
|
+
flush
|
|
10485
|
+
};
|
|
10486
|
+
}
|
|
10487
|
+
|
|
10488
|
+
// src/agents/spawn.ts
|
|
10444
10489
|
async function spawnAgentTurn(thread, input, onEvent) {
|
|
10445
10490
|
ensureAgentPath();
|
|
10446
10491
|
if (!thread.worktreePath?.trim()) {
|
|
@@ -10495,12 +10540,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
10495
10540
|
let assistantText = "";
|
|
10496
10541
|
let parts = [];
|
|
10497
10542
|
let usage = null;
|
|
10543
|
+
const outbound = createAgentStreamCoalescer(onEvent);
|
|
10498
10544
|
const consume = (stream, kind) => {
|
|
10499
10545
|
if (!stream) return;
|
|
10500
10546
|
const rl = (0, import_node_readline2.createInterface)({ input: stream, crlfDelay: Infinity });
|
|
10501
10547
|
rl.on("line", (line) => {
|
|
10502
10548
|
if (kind === "stderr") {
|
|
10503
|
-
|
|
10549
|
+
outbound.push({ type: "stderr", data: line });
|
|
10504
10550
|
return;
|
|
10505
10551
|
}
|
|
10506
10552
|
let events = normalizeParseResult(adapter.parseEvent(line));
|
|
@@ -10515,12 +10561,12 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
10515
10561
|
for (const parsed of events) {
|
|
10516
10562
|
if (parsed.type === "session_id") {
|
|
10517
10563
|
sessionId = parsed.data;
|
|
10518
|
-
|
|
10564
|
+
outbound.push(parsed);
|
|
10519
10565
|
continue;
|
|
10520
10566
|
}
|
|
10521
10567
|
if (parsed.type === "usage") {
|
|
10522
10568
|
usage = applyTurnUsage(usage, parsed.data, parsed.scope ?? "request");
|
|
10523
|
-
|
|
10569
|
+
outbound.push(parsed);
|
|
10524
10570
|
continue;
|
|
10525
10571
|
}
|
|
10526
10572
|
if (parsed.type === "stdout") {
|
|
@@ -10533,13 +10579,14 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
10533
10579
|
assistantText += parsed.data;
|
|
10534
10580
|
}
|
|
10535
10581
|
parts = applyAgentEvent(parts, parsed);
|
|
10536
|
-
|
|
10582
|
+
outbound.push(parsed);
|
|
10537
10583
|
}
|
|
10538
10584
|
});
|
|
10539
10585
|
};
|
|
10540
10586
|
consume(child.stdout, "stdout");
|
|
10541
10587
|
consume(child.stderr, "stderr");
|
|
10542
10588
|
const done = child.then((result) => {
|
|
10589
|
+
outbound.flush();
|
|
10543
10590
|
const exitCode = result.exitCode ?? null;
|
|
10544
10591
|
onEvent({ type: "exit", data: exitCode });
|
|
10545
10592
|
const finalized = finalizeParts(parts);
|
|
@@ -15560,10 +15607,12 @@ var Orchestrator = class {
|
|
|
15560
15607
|
if (event.type === "stderr" && typeof event.data === "string") {
|
|
15561
15608
|
pushTurnStderr(stderrTail, event.data);
|
|
15562
15609
|
}
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
|
|
15566
|
-
|
|
15610
|
+
if (event.type !== "stdout" && event.type !== "thinking") {
|
|
15611
|
+
const live = readThread(threadId);
|
|
15612
|
+
if (live?.lastError?.includes("reconciled on startup") && (this.activeTurns.has(threadId) || this.startingTurns.has(threadId))) {
|
|
15613
|
+
setStatus(threadId, "running");
|
|
15614
|
+
this.emit({ type: "status_changed", threadId, status: "running" });
|
|
15615
|
+
}
|
|
15567
15616
|
}
|
|
15568
15617
|
}
|
|
15569
15618
|
);
|
package/dist/index.js
CHANGED
|
@@ -124,6 +124,9 @@ import {
|
|
|
124
124
|
resolvePlanMarkdown,
|
|
125
125
|
writePlanFile
|
|
126
126
|
} from "./chunk-QTUESPAW.js";
|
|
127
|
+
import {
|
|
128
|
+
createAgentStreamCoalescer
|
|
129
|
+
} from "./chunk-MMI4RJ5I.js";
|
|
127
130
|
import {
|
|
128
131
|
cursorSdkMessageToEvents,
|
|
129
132
|
fallbackTurnFailDetail,
|
|
@@ -1372,12 +1375,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
1372
1375
|
let assistantText = "";
|
|
1373
1376
|
let parts = [];
|
|
1374
1377
|
let usage = null;
|
|
1378
|
+
const outbound = createAgentStreamCoalescer(onEvent);
|
|
1375
1379
|
const consume = (stream, kind) => {
|
|
1376
1380
|
if (!stream) return;
|
|
1377
1381
|
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
1378
1382
|
rl.on("line", (line) => {
|
|
1379
1383
|
if (kind === "stderr") {
|
|
1380
|
-
|
|
1384
|
+
outbound.push({ type: "stderr", data: line });
|
|
1381
1385
|
return;
|
|
1382
1386
|
}
|
|
1383
1387
|
let events = normalizeParseResult(adapter.parseEvent(line));
|
|
@@ -1392,12 +1396,12 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
1392
1396
|
for (const parsed of events) {
|
|
1393
1397
|
if (parsed.type === "session_id") {
|
|
1394
1398
|
sessionId = parsed.data;
|
|
1395
|
-
|
|
1399
|
+
outbound.push(parsed);
|
|
1396
1400
|
continue;
|
|
1397
1401
|
}
|
|
1398
1402
|
if (parsed.type === "usage") {
|
|
1399
1403
|
usage = applyTurnUsage(usage, parsed.data, parsed.scope ?? "request");
|
|
1400
|
-
|
|
1404
|
+
outbound.push(parsed);
|
|
1401
1405
|
continue;
|
|
1402
1406
|
}
|
|
1403
1407
|
if (parsed.type === "stdout") {
|
|
@@ -1410,13 +1414,14 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
1410
1414
|
assistantText += parsed.data;
|
|
1411
1415
|
}
|
|
1412
1416
|
parts = applyAgentEvent(parts, parsed);
|
|
1413
|
-
|
|
1417
|
+
outbound.push(parsed);
|
|
1414
1418
|
}
|
|
1415
1419
|
});
|
|
1416
1420
|
};
|
|
1417
1421
|
consume(child.stdout, "stdout");
|
|
1418
1422
|
consume(child.stderr, "stderr");
|
|
1419
1423
|
const done = child.then((result) => {
|
|
1424
|
+
outbound.flush();
|
|
1420
1425
|
const exitCode = result.exitCode ?? null;
|
|
1421
1426
|
onEvent({ type: "exit", data: exitCode });
|
|
1422
1427
|
const finalized = finalizeParts(parts);
|
|
@@ -6354,10 +6359,12 @@ var Orchestrator = class {
|
|
|
6354
6359
|
if (event.type === "stderr" && typeof event.data === "string") {
|
|
6355
6360
|
pushTurnStderr(stderrTail, event.data);
|
|
6356
6361
|
}
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6362
|
+
if (event.type !== "stdout" && event.type !== "thinking") {
|
|
6363
|
+
const live = readThread(threadId);
|
|
6364
|
+
if (live?.lastError?.includes("reconciled on startup") && (this.activeTurns.has(threadId) || this.startingTurns.has(threadId))) {
|
|
6365
|
+
setStatus(threadId, "running");
|
|
6366
|
+
this.emit({ type: "status_changed", threadId, status: "running" });
|
|
6367
|
+
}
|
|
6361
6368
|
}
|
|
6362
6369
|
}
|
|
6363
6370
|
);
|
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -9312,6 +9312,51 @@ function normalizeParseResult(parsed) {
|
|
|
9312
9312
|
// src/agents/spawn.ts
|
|
9313
9313
|
init_path();
|
|
9314
9314
|
init_usage();
|
|
9315
|
+
|
|
9316
|
+
// src/agents/cursor-stream-coalesce.ts
|
|
9317
|
+
function isTextEvent(event) {
|
|
9318
|
+
return event.type === "stdout" || event.type === "thinking";
|
|
9319
|
+
}
|
|
9320
|
+
function createAgentStreamCoalescer(emit, opts) {
|
|
9321
|
+
const intervalMs = opts?.intervalMs ?? 32;
|
|
9322
|
+
let pending = null;
|
|
9323
|
+
let timer = null;
|
|
9324
|
+
const flush = () => {
|
|
9325
|
+
if (timer) {
|
|
9326
|
+
clearTimeout(timer);
|
|
9327
|
+
timer = null;
|
|
9328
|
+
}
|
|
9329
|
+
if (!pending) return;
|
|
9330
|
+
const event = pending;
|
|
9331
|
+
pending = null;
|
|
9332
|
+
emit({ type: event.type, data: event.data });
|
|
9333
|
+
};
|
|
9334
|
+
const schedule = () => {
|
|
9335
|
+
if (timer) return;
|
|
9336
|
+
timer = setTimeout(flush, intervalMs);
|
|
9337
|
+
timer.unref?.();
|
|
9338
|
+
};
|
|
9339
|
+
return {
|
|
9340
|
+
push(event) {
|
|
9341
|
+
if (!isTextEvent(event)) {
|
|
9342
|
+
flush();
|
|
9343
|
+
emit(event);
|
|
9344
|
+
return;
|
|
9345
|
+
}
|
|
9346
|
+
if (!event.data) return;
|
|
9347
|
+
if (pending && pending.type === event.type) {
|
|
9348
|
+
pending.data += event.data;
|
|
9349
|
+
} else {
|
|
9350
|
+
flush();
|
|
9351
|
+
pending = { type: event.type, data: event.data };
|
|
9352
|
+
}
|
|
9353
|
+
schedule();
|
|
9354
|
+
},
|
|
9355
|
+
flush
|
|
9356
|
+
};
|
|
9357
|
+
}
|
|
9358
|
+
|
|
9359
|
+
// src/agents/spawn.ts
|
|
9315
9360
|
async function spawnAgentTurn(thread, input, onEvent) {
|
|
9316
9361
|
ensureAgentPath();
|
|
9317
9362
|
if (!thread.worktreePath?.trim()) {
|
|
@@ -9366,12 +9411,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
9366
9411
|
let assistantText = "";
|
|
9367
9412
|
let parts = [];
|
|
9368
9413
|
let usage = null;
|
|
9414
|
+
const outbound = createAgentStreamCoalescer(onEvent);
|
|
9369
9415
|
const consume = (stream, kind) => {
|
|
9370
9416
|
if (!stream) return;
|
|
9371
9417
|
const rl = (0, import_node_readline2.createInterface)({ input: stream, crlfDelay: Infinity });
|
|
9372
9418
|
rl.on("line", (line) => {
|
|
9373
9419
|
if (kind === "stderr") {
|
|
9374
|
-
|
|
9420
|
+
outbound.push({ type: "stderr", data: line });
|
|
9375
9421
|
return;
|
|
9376
9422
|
}
|
|
9377
9423
|
let events = normalizeParseResult(adapter.parseEvent(line));
|
|
@@ -9386,12 +9432,12 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
9386
9432
|
for (const parsed of events) {
|
|
9387
9433
|
if (parsed.type === "session_id") {
|
|
9388
9434
|
sessionId = parsed.data;
|
|
9389
|
-
|
|
9435
|
+
outbound.push(parsed);
|
|
9390
9436
|
continue;
|
|
9391
9437
|
}
|
|
9392
9438
|
if (parsed.type === "usage") {
|
|
9393
9439
|
usage = applyTurnUsage(usage, parsed.data, parsed.scope ?? "request");
|
|
9394
|
-
|
|
9440
|
+
outbound.push(parsed);
|
|
9395
9441
|
continue;
|
|
9396
9442
|
}
|
|
9397
9443
|
if (parsed.type === "stdout") {
|
|
@@ -9404,13 +9450,14 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
9404
9450
|
assistantText += parsed.data;
|
|
9405
9451
|
}
|
|
9406
9452
|
parts = applyAgentEvent(parts, parsed);
|
|
9407
|
-
|
|
9453
|
+
outbound.push(parsed);
|
|
9408
9454
|
}
|
|
9409
9455
|
});
|
|
9410
9456
|
};
|
|
9411
9457
|
consume(child.stdout, "stdout");
|
|
9412
9458
|
consume(child.stderr, "stderr");
|
|
9413
9459
|
const done = child.then((result) => {
|
|
9460
|
+
outbound.flush();
|
|
9414
9461
|
const exitCode = result.exitCode ?? null;
|
|
9415
9462
|
onEvent({ type: "exit", data: exitCode });
|
|
9416
9463
|
const finalized = finalizeParts(parts);
|
|
@@ -13624,10 +13671,12 @@ var Orchestrator = class {
|
|
|
13624
13671
|
if (event.type === "stderr" && typeof event.data === "string") {
|
|
13625
13672
|
pushTurnStderr(stderrTail, event.data);
|
|
13626
13673
|
}
|
|
13627
|
-
|
|
13628
|
-
|
|
13629
|
-
|
|
13630
|
-
|
|
13674
|
+
if (event.type !== "stdout" && event.type !== "thinking") {
|
|
13675
|
+
const live = readThread(threadId);
|
|
13676
|
+
if (live?.lastError?.includes("reconciled on startup") && (this.activeTurns.has(threadId) || this.startingTurns.has(threadId))) {
|
|
13677
|
+
setStatus(threadId, "running");
|
|
13678
|
+
this.emit({ type: "status_changed", threadId, status: "running" });
|
|
13679
|
+
}
|
|
13631
13680
|
}
|
|
13632
13681
|
}
|
|
13633
13682
|
);
|
package/dist/mcp/run-stdio.js
CHANGED
|
@@ -888,6 +888,49 @@ function normalizeParseResult(parsed) {
|
|
|
888
888
|
return Array.isArray(parsed) ? parsed : [parsed];
|
|
889
889
|
}
|
|
890
890
|
|
|
891
|
+
// src/agents/cursor-stream-coalesce.ts
|
|
892
|
+
function isTextEvent(event) {
|
|
893
|
+
return event.type === "stdout" || event.type === "thinking";
|
|
894
|
+
}
|
|
895
|
+
function createAgentStreamCoalescer(emit, opts) {
|
|
896
|
+
const intervalMs = opts?.intervalMs ?? 32;
|
|
897
|
+
let pending = null;
|
|
898
|
+
let timer = null;
|
|
899
|
+
const flush = () => {
|
|
900
|
+
if (timer) {
|
|
901
|
+
clearTimeout(timer);
|
|
902
|
+
timer = null;
|
|
903
|
+
}
|
|
904
|
+
if (!pending) return;
|
|
905
|
+
const event = pending;
|
|
906
|
+
pending = null;
|
|
907
|
+
emit({ type: event.type, data: event.data });
|
|
908
|
+
};
|
|
909
|
+
const schedule = () => {
|
|
910
|
+
if (timer) return;
|
|
911
|
+
timer = setTimeout(flush, intervalMs);
|
|
912
|
+
timer.unref?.();
|
|
913
|
+
};
|
|
914
|
+
return {
|
|
915
|
+
push(event) {
|
|
916
|
+
if (!isTextEvent(event)) {
|
|
917
|
+
flush();
|
|
918
|
+
emit(event);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (!event.data) return;
|
|
922
|
+
if (pending && pending.type === event.type) {
|
|
923
|
+
pending.data += event.data;
|
|
924
|
+
} else {
|
|
925
|
+
flush();
|
|
926
|
+
pending = { type: event.type, data: event.data };
|
|
927
|
+
}
|
|
928
|
+
schedule();
|
|
929
|
+
},
|
|
930
|
+
flush
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
891
934
|
// src/agents/spawn.ts
|
|
892
935
|
async function spawnAgentTurn(thread, input, onEvent) {
|
|
893
936
|
ensureAgentPath();
|
|
@@ -943,12 +986,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
943
986
|
let assistantText = "";
|
|
944
987
|
let parts = [];
|
|
945
988
|
let usage = null;
|
|
989
|
+
const outbound = createAgentStreamCoalescer(onEvent);
|
|
946
990
|
const consume = (stream, kind) => {
|
|
947
991
|
if (!stream) return;
|
|
948
992
|
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
949
993
|
rl.on("line", (line) => {
|
|
950
994
|
if (kind === "stderr") {
|
|
951
|
-
|
|
995
|
+
outbound.push({ type: "stderr", data: line });
|
|
952
996
|
return;
|
|
953
997
|
}
|
|
954
998
|
let events = normalizeParseResult(adapter.parseEvent(line));
|
|
@@ -963,12 +1007,12 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
963
1007
|
for (const parsed of events) {
|
|
964
1008
|
if (parsed.type === "session_id") {
|
|
965
1009
|
sessionId = parsed.data;
|
|
966
|
-
|
|
1010
|
+
outbound.push(parsed);
|
|
967
1011
|
continue;
|
|
968
1012
|
}
|
|
969
1013
|
if (parsed.type === "usage") {
|
|
970
1014
|
usage = applyTurnUsage(usage, parsed.data, parsed.scope ?? "request");
|
|
971
|
-
|
|
1015
|
+
outbound.push(parsed);
|
|
972
1016
|
continue;
|
|
973
1017
|
}
|
|
974
1018
|
if (parsed.type === "stdout") {
|
|
@@ -981,13 +1025,14 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
981
1025
|
assistantText += parsed.data;
|
|
982
1026
|
}
|
|
983
1027
|
parts = applyAgentEvent(parts, parsed);
|
|
984
|
-
|
|
1028
|
+
outbound.push(parsed);
|
|
985
1029
|
}
|
|
986
1030
|
});
|
|
987
1031
|
};
|
|
988
1032
|
consume(child.stdout, "stdout");
|
|
989
1033
|
consume(child.stderr, "stderr");
|
|
990
1034
|
const done = child.then((result) => {
|
|
1035
|
+
outbound.flush();
|
|
991
1036
|
const exitCode = result.exitCode ?? null;
|
|
992
1037
|
onEvent({ type: "exit", data: exitCode });
|
|
993
1038
|
const finalized = finalizeParts(parts);
|
|
@@ -5136,10 +5181,12 @@ var Orchestrator = class {
|
|
|
5136
5181
|
if (event.type === "stderr" && typeof event.data === "string") {
|
|
5137
5182
|
pushTurnStderr(stderrTail, event.data);
|
|
5138
5183
|
}
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5184
|
+
if (event.type !== "stdout" && event.type !== "thinking") {
|
|
5185
|
+
const live = readThread(threadId);
|
|
5186
|
+
if (live?.lastError?.includes("reconciled on startup") && (this.activeTurns.has(threadId) || this.startingTurns.has(threadId))) {
|
|
5187
|
+
setStatus(threadId, "running");
|
|
5188
|
+
this.emit({ type: "status_changed", threadId, status: "running" });
|
|
5189
|
+
}
|
|
5143
5190
|
}
|
|
5144
5191
|
}
|
|
5145
5192
|
);
|