@bridge4dev/runner 0.33.0 → 0.34.0
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/adapters/claude.js +30 -2
- package/dist/supervisor.d.ts +24 -0
- package/dist/supervisor.js +60 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -9
package/dist/adapters/claude.js
CHANGED
|
@@ -1800,11 +1800,39 @@ class ClaudeSession {
|
|
|
1800
1800
|
break;
|
|
1801
1801
|
}
|
|
1802
1802
|
case 'assistant': {
|
|
1803
|
+
// Ticket #186. A subagent's own prose is NOT this session's answer.
|
|
1804
|
+
//
|
|
1805
|
+
// The SDK's own note — «by default, only tool_use/tool_result blocks
|
|
1806
|
+
// from subagents are emitted» — covers SYNCHRONOUS Task subagents. A
|
|
1807
|
+
// BACKGROUND agent's closing report is delivered regardless, as an
|
|
1808
|
+
// ordinary top-level assistant message that merely carries
|
|
1809
|
+
// `parent_tool_use_id` (probe against 0.3.226, 2026-08-08):
|
|
1810
|
+
//
|
|
1811
|
+
// {type:'assistant', parent_tool_use_id:'toolu_…',
|
|
1812
|
+
// subagent_type:'general-purpose', task_description:'…',
|
|
1813
|
+
// message:{content:[{type:'text', text:'…'}]}}
|
|
1814
|
+
//
|
|
1815
|
+
// Session 50eb77b9 put two of those — 46 655 and 16 368 characters of
|
|
1816
|
+
// internal survey — into the feed four seconds after an
|
|
1817
|
+
// AskUserQuestion card, while the main agent was blocked on the very
|
|
1818
|
+
// tool call that card belongs to and could not have written a word.
|
|
1819
|
+
//
|
|
1820
|
+
// Only text and thinking are dropped. `tool_use` stays, and so does
|
|
1821
|
+
// the paired `tool_result` in `case 'user'` below: the dashboard
|
|
1822
|
+
// resolves a result by toolUseId and falls back to the last unfinished
|
|
1823
|
+
// row, so filtering one arm without the other sticks a subagent's
|
|
1824
|
+
// result onto a stranger's row (that fallback has misfired before).
|
|
1825
|
+
//
|
|
1826
|
+
// The test is «is a string», not «is not null»: an emitter that
|
|
1827
|
+
// omits the field must read as the main loop. Treating a missing
|
|
1828
|
+
// field as a subagent would blank the transcript of every session on
|
|
1829
|
+
// that CLI, which is a far worse failure than one leaked report.
|
|
1830
|
+
const fromSubagent = typeof msg.parent_tool_use_id === 'string';
|
|
1803
1831
|
for (const block of msg.message.content) {
|
|
1804
|
-
if (block.type === 'text' && block.text.trim()) {
|
|
1832
|
+
if (block.type === 'text' && !fromSubagent && block.text.trim()) {
|
|
1805
1833
|
this.emit({ type: 'message', role: 'assistant', text: truncate(block.text) });
|
|
1806
1834
|
}
|
|
1807
|
-
else if (block.type === 'thinking' && block.thinking.trim()) {
|
|
1835
|
+
else if (block.type === 'thinking' && !fromSubagent && block.thinking.trim()) {
|
|
1808
1836
|
this.emit({ type: 'thinking', text: truncate(block.thinking, 8_000) });
|
|
1809
1837
|
}
|
|
1810
1838
|
else if (block.type === 'tool_use') {
|
package/dist/supervisor.d.ts
CHANGED
|
@@ -246,6 +246,30 @@ export declare class Supervisor {
|
|
|
246
246
|
* rewound (ticket #126).
|
|
247
247
|
*/
|
|
248
248
|
private park;
|
|
249
|
+
/**
|
|
250
|
+
* The agent is producing output while this runner still says a human is
|
|
251
|
+
* expected — put `lastReported` back to RUNNING (ticket #185).
|
|
252
|
+
*
|
|
253
|
+
* This is not cosmetics on top of the API's own repair. While `lastReported`
|
|
254
|
+
* lies, two things break here and only here:
|
|
255
|
+
*
|
|
256
|
+
* - `isParkable()` reads WAITING_INPUT with no open question as «idle», so
|
|
257
|
+
* `ensureCapacity` may KILL a live agent process to hand its slot to
|
|
258
|
+
* another session;
|
|
259
|
+
* - `isBillable()` counts only RUNNING/STARTING, so `agentActiveMs` stops
|
|
260
|
+
* growing and the time budget never fires for work that is really happening.
|
|
261
|
+
*
|
|
262
|
+
* It happens for real: a background subagent's report wakes a new turn inside
|
|
263
|
+
* the agent process (SDK 0.3.226 emits a second `system:init` and a `result`
|
|
264
|
+
* carrying `origin: {kind:'task-notification'}`), and nothing here reports a
|
|
265
|
+
* turn the runner did not start. Compaction does the same, and so does a
|
|
266
|
+
* question this runner withdraws by itself.
|
|
267
|
+
*
|
|
268
|
+
* An open question is the one thing that must survive: there, WAITING_INPUT
|
|
269
|
+
* means a tool call is parked on a person, and the agent narrating around its
|
|
270
|
+
* own question must not take the card off the screen.
|
|
271
|
+
*/
|
|
272
|
+
private noteAgentIsWorking;
|
|
249
273
|
private forwardEvent;
|
|
250
274
|
/**
|
|
251
275
|
* The dashboard's answer to a parked question (session 12).
|
package/dist/supervisor.js
CHANGED
|
@@ -22,6 +22,12 @@ import { availableModes, MODE_REFUSED_TEXT } from './adapters/types.js';
|
|
|
22
22
|
/** Refusals shared by every checkpoint command (ticket #126). */
|
|
23
23
|
const CHECKPOINTS_OFF = 'Restore points are switched off on this server ([checkpoints] enabled = false)';
|
|
24
24
|
const AGENT_BUSY = 'The agent is still working — stop the turn first';
|
|
25
|
+
/**
|
|
26
|
+
* Adapter events that exist only because the agent is producing output right
|
|
27
|
+
* now (ticket #185). `message` is deliberately absent — a user echo carries the
|
|
28
|
+
* same type and proves nothing about the agent — and is handled at the call site.
|
|
29
|
+
*/
|
|
30
|
+
const AGENT_OUTPUT_EVENTS = new Set(['tool', 'thinking']);
|
|
25
31
|
/**
|
|
26
32
|
* The project's git policy, lifted out of a session descriptor (session 18).
|
|
27
33
|
*
|
|
@@ -1046,8 +1052,47 @@ export class Supervisor {
|
|
|
1046
1052
|
}
|
|
1047
1053
|
running.session.stop('session_parked');
|
|
1048
1054
|
}
|
|
1055
|
+
/**
|
|
1056
|
+
* The agent is producing output while this runner still says a human is
|
|
1057
|
+
* expected — put `lastReported` back to RUNNING (ticket #185).
|
|
1058
|
+
*
|
|
1059
|
+
* This is not cosmetics on top of the API's own repair. While `lastReported`
|
|
1060
|
+
* lies, two things break here and only here:
|
|
1061
|
+
*
|
|
1062
|
+
* - `isParkable()` reads WAITING_INPUT with no open question as «idle», so
|
|
1063
|
+
* `ensureCapacity` may KILL a live agent process to hand its slot to
|
|
1064
|
+
* another session;
|
|
1065
|
+
* - `isBillable()` counts only RUNNING/STARTING, so `agentActiveMs` stops
|
|
1066
|
+
* growing and the time budget never fires for work that is really happening.
|
|
1067
|
+
*
|
|
1068
|
+
* It happens for real: a background subagent's report wakes a new turn inside
|
|
1069
|
+
* the agent process (SDK 0.3.226 emits a second `system:init` and a `result`
|
|
1070
|
+
* carrying `origin: {kind:'task-notification'}`), and nothing here reports a
|
|
1071
|
+
* turn the runner did not start. Compaction does the same, and so does a
|
|
1072
|
+
* question this runner withdraws by itself.
|
|
1073
|
+
*
|
|
1074
|
+
* An open question is the one thing that must survive: there, WAITING_INPUT
|
|
1075
|
+
* means a tool call is parked on a person, and the agent narrating around its
|
|
1076
|
+
* own question must not take the card off the screen.
|
|
1077
|
+
*/
|
|
1078
|
+
noteAgentIsWorking(running) {
|
|
1079
|
+
if (running.openQuestions.size > 0)
|
|
1080
|
+
return;
|
|
1081
|
+
if (running.stopRequested || running.parkRequested || running.budgetSpent)
|
|
1082
|
+
return;
|
|
1083
|
+
if (running.lastReported !== 'WAITING_INPUT' && running.lastReported !== 'REVIEW')
|
|
1084
|
+
return;
|
|
1085
|
+
running.lastReported = 'RUNNING';
|
|
1086
|
+
this.syncBudgetClock(running);
|
|
1087
|
+
this.reportStatus(running.descriptor.id, 'RUNNING', {});
|
|
1088
|
+
}
|
|
1049
1089
|
forwardEvent(running, event) {
|
|
1050
1090
|
const { descriptor } = running;
|
|
1091
|
+
// Anything below that is the agent talking means the agent is working. Read
|
|
1092
|
+
// before the switch so every such case gets it, including the ones added
|
|
1093
|
+
// after this line was written.
|
|
1094
|
+
if (event.type === 'message' ? event.role === 'assistant' : AGENT_OUTPUT_EVENTS.has(event.type))
|
|
1095
|
+
this.noteAgentIsWorking(running);
|
|
1051
1096
|
switch (event.type) {
|
|
1052
1097
|
case 'provider_session': {
|
|
1053
1098
|
running.descriptor = { ...descriptor, providerSessionId: event.providerSessionId };
|
|
@@ -2431,6 +2476,21 @@ export class Supervisor {
|
|
|
2431
2476
|
error: 'The agent could not compact right now — wait for the current turn to finish',
|
|
2432
2477
|
});
|
|
2433
2478
|
}
|
|
2479
|
+
// Ticket #185. Compaction is a whole agent turn — `/compact` goes in
|
|
2480
|
+
// as an ordinary user message — and until now nobody said so: the
|
|
2481
|
+
// session read «Ваш ход» for the entire squeeze, and the dashboard
|
|
2482
|
+
// papered over it with `compactingSince`, a variable that exists only
|
|
2483
|
+
// in the tab that pressed the button. A second tab, a teammate or a
|
|
2484
|
+
// reload saw a session waiting for a person who had nothing to do.
|
|
2485
|
+
//
|
|
2486
|
+
// It is also not only cosmetic here: a session reading WAITING_INPUT
|
|
2487
|
+
// with no open question is parkable, and parking it would kill the
|
|
2488
|
+
// compaction mid-flight.
|
|
2489
|
+
if (running.lastReported !== 'RUNNING') {
|
|
2490
|
+
running.lastReported = 'RUNNING';
|
|
2491
|
+
this.syncBudgetClock(running);
|
|
2492
|
+
this.reportStatus(running.descriptor.id, 'RUNNING', {});
|
|
2493
|
+
}
|
|
2434
2494
|
return void reply({ ok: true, result: { started: true } });
|
|
2435
2495
|
}
|
|
2436
2496
|
case 'git_status': {
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const RUNNER_VERSION = "0.
|
|
1
|
+
export declare const RUNNER_VERSION = "0.34.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bridge4dev/runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "DevBridge dev runner — connects a dev server to DevBridge and runs agent sessions (Claude Code / Codex)",
|
|
5
5
|
"homepage": "https://bridge4.dev",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,16 +37,16 @@
|
|
|
37
37
|
"prepublishOnly": "tsc"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
41
|
-
"smol-toml": "^1.7.
|
|
42
|
-
"ws": "^8.21.
|
|
43
|
-
"zod": "^3.
|
|
40
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.226",
|
|
41
|
+
"smol-toml": "^1.7.1",
|
|
42
|
+
"ws": "^8.21.3",
|
|
43
|
+
"zod": "^3.25.76"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^22.
|
|
46
|
+
"@types/node": "^22.20.1",
|
|
47
47
|
"@types/ws": "^8.18.1",
|
|
48
|
-
"tsx": "^4.
|
|
49
|
-
"typescript": "~5.7.
|
|
50
|
-
"vitest": "^2.
|
|
48
|
+
"tsx": "^4.23.11",
|
|
49
|
+
"typescript": "~5.7.3",
|
|
50
|
+
"vitest": "^3.2.7"
|
|
51
51
|
}
|
|
52
52
|
}
|