@cortexkit/aft-bridge 0.36.0 → 0.37.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/bash-hints.d.ts +15 -0
- package/dist/bash-hints.d.ts.map +1 -1
- package/dist/bash-hints.js +169 -4
- package/dist/bash-hints.js.map +1 -1
- package/dist/bridge.d.ts +6 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +102 -15
- package/dist/bridge.js.map +1 -1
- package/dist/command-timeouts.d.ts +18 -0
- package/dist/command-timeouts.d.ts.map +1 -0
- package/dist/command-timeouts.js +29 -0
- package/dist/command-timeouts.js.map +1 -0
- package/dist/edit-summary.d.ts +5 -1
- package/dist/edit-summary.d.ts.map +1 -1
- package/dist/edit-summary.js +11 -3
- package/dist/edit-summary.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/pipe-strip.d.ts.map +1 -1
- package/dist/pipe-strip.js +383 -20
- package/dist/pipe-strip.js.map +1 -1
- package/dist/pool.d.ts.map +1 -1
- package/dist/pool.js +4 -4
- package/dist/pool.js.map +1 -1
- package/dist/protocol.d.ts +4 -1
- package/dist/protocol.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/bash-hints.d.ts
CHANGED
|
@@ -13,4 +13,19 @@
|
|
|
13
13
|
* agents into a confusing error.
|
|
14
14
|
*/
|
|
15
15
|
export declare function maybeAppendConflictsHint(output: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Return true when the command itself leads with a code-search command.
|
|
18
|
+
*
|
|
19
|
+
* This deliberately ignores grep/rg used as downstream filters (for example,
|
|
20
|
+
* `bun test | grep fail`). It only inspects the first pipeline stage's first
|
|
21
|
+
* quote/escape-aware token, after peeling a leading `cd <dir> &&` prefix.
|
|
22
|
+
* Ambiguous shell syntax (notably unmatched quotes) returns false so the nudge
|
|
23
|
+
* never fires spuriously.
|
|
24
|
+
*/
|
|
25
|
+
export declare function commandLeadsWithCodeSearch(command: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Append the grep/rg code-search nudge for native bash output that did not go
|
|
28
|
+
* through the Rust grep rewrite footer path.
|
|
29
|
+
*/
|
|
30
|
+
export declare function maybeAppendGrepSearchHint(output: string, command: string, aftSearchRegistered: boolean): string;
|
|
16
31
|
//# sourceMappingURL=bash-hints.d.ts.map
|
package/dist/bash-hints.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash-hints.d.ts","sourceRoot":"","sources":["../src/bash-hints.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bash-hints.d.ts","sourceRoot":"","sources":["../src/bash-hints.ts"],"names":[],"mappings":"AAyBA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAM/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAanE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,mBAAmB,EAAE,OAAO,GAC3B,MAAM,CAOR"}
|
package/dist/bash-hints.js
CHANGED
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
// applies it inside its hoisted bash tool). Returns the new output string (or
|
|
5
5
|
// the original when no hint should fire). The appended "[Hint] ..." line is
|
|
6
6
|
// agent-visible and persists in the tool result.
|
|
7
|
-
//
|
|
8
|
-
// Note: the grep/rg code-search redirect lives in the Rust bash rewriter
|
|
9
|
-
// (`bash_rewrite::footer::add_grep_footer`), which owns the rewrite and reads
|
|
10
|
-
// `aft_search_registered` from config. It is not duplicated here.
|
|
11
7
|
const CONFLICT_HINT = "\n\n[Hint] Use aft_conflicts to see all conflict regions across files in a single call.";
|
|
8
|
+
const GREP_SEARCH_AFT_SEARCH_HINT = "DO NOT search code by running grep/rg in bash — it is unindexed, unranked, and serial. Use the `aft_search` tool instead (it auto-routes concepts, identifiers, regex, and literals).";
|
|
9
|
+
const GREP_SEARCH_GREP_HINT = "DO NOT search code by running grep/rg in bash — it is unindexed, unranked, and serial. Use the `grep` tool instead (indexed and ranked).";
|
|
10
|
+
const GREP_SEARCH_HINT_PREFIX = "DO NOT search code by running grep/rg in bash —";
|
|
12
11
|
/**
|
|
13
12
|
* Append the `aft_conflicts` hint when the output indicates a real git merge
|
|
14
13
|
* or rebase produced conflicts.
|
|
@@ -32,4 +31,170 @@ export function maybeAppendConflictsHint(output) {
|
|
|
32
31
|
return output;
|
|
33
32
|
return output + CONFLICT_HINT;
|
|
34
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Return true when the command itself leads with a code-search command.
|
|
36
|
+
*
|
|
37
|
+
* This deliberately ignores grep/rg used as downstream filters (for example,
|
|
38
|
+
* `bun test | grep fail`). It only inspects the first pipeline stage's first
|
|
39
|
+
* quote/escape-aware token, after peeling a leading `cd <dir> &&` prefix.
|
|
40
|
+
* Ambiguous shell syntax (notably unmatched quotes) returns false so the nudge
|
|
41
|
+
* never fires spuriously.
|
|
42
|
+
*/
|
|
43
|
+
export function commandLeadsWithCodeSearch(command) {
|
|
44
|
+
const trimmed = command.trim();
|
|
45
|
+
if (!trimmed)
|
|
46
|
+
return false;
|
|
47
|
+
const afterCd = peelLeadingCdAnd(trimmed);
|
|
48
|
+
if (afterCd === null)
|
|
49
|
+
return false;
|
|
50
|
+
const firstStage = firstPipelineStage(afterCd);
|
|
51
|
+
if (firstStage === null)
|
|
52
|
+
return false;
|
|
53
|
+
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
|
|
54
|
+
if (firstToken === null)
|
|
55
|
+
return false;
|
|
56
|
+
return firstToken.token === "grep" || firstToken.token === "rg";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Append the grep/rg code-search nudge for native bash output that did not go
|
|
60
|
+
* through the Rust grep rewrite footer path.
|
|
61
|
+
*/
|
|
62
|
+
export function maybeAppendGrepSearchHint(output, command, aftSearchRegistered) {
|
|
63
|
+
if (output === "")
|
|
64
|
+
return output;
|
|
65
|
+
if (!commandLeadsWithCodeSearch(command))
|
|
66
|
+
return output;
|
|
67
|
+
if (output.includes(GREP_SEARCH_HINT_PREFIX))
|
|
68
|
+
return output;
|
|
69
|
+
const hint = aftSearchRegistered ? GREP_SEARCH_AFT_SEARCH_HINT : GREP_SEARCH_GREP_HINT;
|
|
70
|
+
return `${output}\n\n${hint}`;
|
|
71
|
+
}
|
|
72
|
+
function peelLeadingCdAnd(command) {
|
|
73
|
+
const first = readShellToken(command, skipSpaces(command, 0));
|
|
74
|
+
if (first === null)
|
|
75
|
+
return null;
|
|
76
|
+
if (first.token !== "cd")
|
|
77
|
+
return command;
|
|
78
|
+
const dir = readShellToken(command, skipSpaces(command, first.end));
|
|
79
|
+
if (dir === null)
|
|
80
|
+
return null;
|
|
81
|
+
if (!dir.token)
|
|
82
|
+
return command;
|
|
83
|
+
const afterDir = skipSpaces(command, dir.end);
|
|
84
|
+
if (!command.startsWith("&&", afterDir))
|
|
85
|
+
return command;
|
|
86
|
+
return command.slice(afterDir + 2).trim();
|
|
87
|
+
}
|
|
88
|
+
function firstPipelineStage(command) {
|
|
89
|
+
let quote = "none";
|
|
90
|
+
let firstPipeIndex;
|
|
91
|
+
for (let index = 0; index < command.length; index++) {
|
|
92
|
+
const ch = command[index];
|
|
93
|
+
if (quote === "single") {
|
|
94
|
+
if (ch === "'")
|
|
95
|
+
quote = "none";
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (quote === "double") {
|
|
99
|
+
if (ch === '"') {
|
|
100
|
+
quote = "none";
|
|
101
|
+
}
|
|
102
|
+
else if (ch === "\\") {
|
|
103
|
+
index++;
|
|
104
|
+
}
|
|
105
|
+
else if (ch === "`") {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (ch === "'") {
|
|
111
|
+
quote = "single";
|
|
112
|
+
}
|
|
113
|
+
else if (ch === '"') {
|
|
114
|
+
quote = "double";
|
|
115
|
+
}
|
|
116
|
+
else if (ch === "\\") {
|
|
117
|
+
index++;
|
|
118
|
+
}
|
|
119
|
+
else if (ch === "`") {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
else if (ch === "|") {
|
|
123
|
+
if (command[index + 1] === "|") {
|
|
124
|
+
index++;
|
|
125
|
+
}
|
|
126
|
+
else if (firstPipeIndex === undefined) {
|
|
127
|
+
firstPipeIndex = index;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (quote !== "none")
|
|
132
|
+
return null;
|
|
133
|
+
return command.slice(0, firstPipeIndex ?? command.length).trim();
|
|
134
|
+
}
|
|
135
|
+
function readShellToken(command, start) {
|
|
136
|
+
let quote = "none";
|
|
137
|
+
let token = "";
|
|
138
|
+
let index = start;
|
|
139
|
+
for (; index < command.length; index++) {
|
|
140
|
+
const ch = command[index];
|
|
141
|
+
if (quote === "single") {
|
|
142
|
+
if (ch === "'") {
|
|
143
|
+
quote = "none";
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
token += ch;
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (quote === "double") {
|
|
151
|
+
if (ch === '"') {
|
|
152
|
+
quote = "none";
|
|
153
|
+
}
|
|
154
|
+
else if (ch === "\\") {
|
|
155
|
+
index++;
|
|
156
|
+
token += command[index] ?? "\\";
|
|
157
|
+
}
|
|
158
|
+
else if (ch === "`") {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
token += ch;
|
|
163
|
+
}
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (/\s/.test(ch))
|
|
167
|
+
break;
|
|
168
|
+
if (isTokenBoundary(ch))
|
|
169
|
+
break;
|
|
170
|
+
if (ch === "'") {
|
|
171
|
+
quote = "single";
|
|
172
|
+
}
|
|
173
|
+
else if (ch === '"') {
|
|
174
|
+
quote = "double";
|
|
175
|
+
}
|
|
176
|
+
else if (ch === "\\") {
|
|
177
|
+
index++;
|
|
178
|
+
token += command[index] ?? "\\";
|
|
179
|
+
}
|
|
180
|
+
else if (ch === "`") {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
token += ch;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (quote !== "none")
|
|
188
|
+
return null;
|
|
189
|
+
return { token, end: index };
|
|
190
|
+
}
|
|
191
|
+
function isTokenBoundary(ch) {
|
|
192
|
+
return ch === "|" || ch === ";" || ch === "&" || ch === "<" || ch === ">";
|
|
193
|
+
}
|
|
194
|
+
function skipSpaces(input, start) {
|
|
195
|
+
let index = start;
|
|
196
|
+
while (index < input.length && /\s/.test(input[index]))
|
|
197
|
+
index++;
|
|
198
|
+
return index;
|
|
199
|
+
}
|
|
35
200
|
//# sourceMappingURL=bash-hints.js.map
|
package/dist/bash-hints.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash-hints.js","sourceRoot":"","sources":["../src/bash-hints.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,2EAA2E;AAC3E,8EAA8E;AAC9E,4EAA4E;AAC5E,iDAAiD;
|
|
1
|
+
{"version":3,"file":"bash-hints.js","sourceRoot":"","sources":["../src/bash-hints.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { type StatusBarCounts } from "./status-bar.js";
|
|
|
23
23
|
*/
|
|
24
24
|
export declare function tagStderrLine(line: string): string;
|
|
25
25
|
export declare function compareSemver(a: string, b: string): number;
|
|
26
|
+
export declare function clampSemanticTimeout(configOverrides: Record<string, unknown>, bridgeTimeoutMs: number): Record<string, unknown>;
|
|
26
27
|
/** Single configure-time warning produced by the Rust side. */
|
|
27
28
|
export interface ConfigureWarning {
|
|
28
29
|
code?: string;
|
|
@@ -163,8 +164,10 @@ export declare class BinaryBridge {
|
|
|
163
164
|
private cwd;
|
|
164
165
|
private process;
|
|
165
166
|
private pending;
|
|
167
|
+
private outstandingBackgroundTaskIds;
|
|
166
168
|
private nextId;
|
|
167
169
|
private stdoutBuffer;
|
|
170
|
+
private stdoutReadOffset;
|
|
168
171
|
private stderrBuffer;
|
|
169
172
|
/** Ring buffer of the last N stderr lines, cleared on every spawn. */
|
|
170
173
|
private stderrTail;
|
|
@@ -213,6 +216,7 @@ export declare class BinaryBridge {
|
|
|
213
216
|
/** Whether the child process is currently alive. */
|
|
214
217
|
isAlive(): boolean;
|
|
215
218
|
hasPendingRequests(): boolean;
|
|
219
|
+
hasOutstandingBackgroundTasks(): boolean;
|
|
216
220
|
/** Project root this bridge was spawned/configured for. */
|
|
217
221
|
getCwd(): string;
|
|
218
222
|
/** Returns the latest pushed or primed status snapshot, or null before the cold path completes. */
|
|
@@ -259,8 +263,10 @@ export declare class BinaryBridge {
|
|
|
259
263
|
*/
|
|
260
264
|
private formatStderrTail;
|
|
261
265
|
private onStdoutData;
|
|
266
|
+
private compactStdoutBuffer;
|
|
262
267
|
private flushStdoutBuffer;
|
|
263
268
|
private processStdoutLine;
|
|
269
|
+
private accountForBashTaskResponse;
|
|
264
270
|
/**
|
|
265
271
|
* Cache the agent status-bar counts from a response. The Rust `Response.data`
|
|
266
272
|
* is `#[serde(flatten)]`, so the attached `status_bar` object lands at the
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.js
CHANGED
|
@@ -3,11 +3,34 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { StringDecoder } from "node:string_decoder";
|
|
5
5
|
import { error, getActiveLogger, getLogFilePath, log, warn } from "./active-logger.js";
|
|
6
|
+
import { LONG_RUNNING_COMMAND_TIMEOUT_MS } from "./command-timeouts.js";
|
|
6
7
|
import { parseStatusBarCounts } from "./status-bar.js";
|
|
7
8
|
const DEFAULT_BRIDGE_TIMEOUT_MS = 30_000;
|
|
8
9
|
const BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
9
10
|
const SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS = 5_000;
|
|
10
11
|
const MAX_STDOUT_BUFFER = 64 * 1024 * 1024; // 64MB
|
|
12
|
+
const STDOUT_BUFFER_COMPACT_THRESHOLD = 64 * 1024;
|
|
13
|
+
const TERMINAL_BASH_STATUSES = new Set([
|
|
14
|
+
"completed",
|
|
15
|
+
"failed",
|
|
16
|
+
"killed",
|
|
17
|
+
"timed_out",
|
|
18
|
+
// Historical/defensive aliases seen in plugin-side compatibility code.
|
|
19
|
+
"cancelled",
|
|
20
|
+
"timeout",
|
|
21
|
+
]);
|
|
22
|
+
function isTerminalBashStatus(status) {
|
|
23
|
+
return typeof status === "string" && TERMINAL_BASH_STATUSES.has(status);
|
|
24
|
+
}
|
|
25
|
+
function bashTaskIdFrom(response) {
|
|
26
|
+
const snakeCase = response.task_id;
|
|
27
|
+
if (typeof snakeCase === "string" && snakeCase.length > 0)
|
|
28
|
+
return snakeCase;
|
|
29
|
+
const camelCase = response.taskId;
|
|
30
|
+
if (typeof camelCase === "string" && camelCase.length > 0)
|
|
31
|
+
return camelCase;
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
11
34
|
// ## Note on TypeScript `as` type assertions
|
|
12
35
|
//
|
|
13
36
|
// Bridge responses use `as string`, `as string[]` etc. in several places.
|
|
@@ -85,7 +108,7 @@ export function compareSemver(a, b) {
|
|
|
85
108
|
}
|
|
86
109
|
return 0;
|
|
87
110
|
}
|
|
88
|
-
function clampSemanticTimeout(configOverrides, bridgeTimeoutMs) {
|
|
111
|
+
export function clampSemanticTimeout(configOverrides, bridgeTimeoutMs) {
|
|
89
112
|
const semantic = configOverrides.semantic;
|
|
90
113
|
if (!semantic || typeof semantic !== "object" || Array.isArray(semantic)) {
|
|
91
114
|
return configOverrides;
|
|
@@ -94,13 +117,21 @@ function clampSemanticTimeout(configOverrides, bridgeTimeoutMs) {
|
|
|
94
117
|
if (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs)) {
|
|
95
118
|
return configOverrides;
|
|
96
119
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
120
|
+
// The clamp exists so a Rust-side embed request fails BEFORE the transport
|
|
121
|
+
// gives up on the request that carries it. But `semantic_search` doesn't
|
|
122
|
+
// run on the bridge default budget — plugins send it with the per-command
|
|
123
|
+
// override from LONG_RUNNING_COMMAND_TIMEOUT_MS. Clamping against the bare
|
|
124
|
+
// default (30s) silently cut user-configured cold-load headroom (e.g.
|
|
125
|
+
// LMStudio 8B: 60s → 25s) and aborted background refresh batches that have
|
|
126
|
+
// no transport constraint at all. Clamp against the real budget.
|
|
127
|
+
const semanticTransportBudgetMs = Math.max(bridgeTimeoutMs, LONG_RUNNING_COMMAND_TIMEOUT_MS.semantic_search ?? 0);
|
|
128
|
+
const maxSemanticTimeoutMs = semanticTransportBudgetMs > SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS
|
|
129
|
+
? semanticTransportBudgetMs - SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS
|
|
130
|
+
: Math.max(1, semanticTransportBudgetMs - 1);
|
|
100
131
|
if (timeoutMs <= maxSemanticTimeoutMs) {
|
|
101
132
|
return configOverrides;
|
|
102
133
|
}
|
|
103
|
-
warn(`semantic.timeout_ms=${timeoutMs} exceeds
|
|
134
|
+
warn(`semantic.timeout_ms=${timeoutMs} exceeds the semantic transport budget; clamping to ${maxSemanticTimeoutMs}ms (budget: ${semanticTransportBudgetMs}ms)`);
|
|
104
135
|
return {
|
|
105
136
|
...configOverrides,
|
|
106
137
|
semantic: {
|
|
@@ -130,8 +161,10 @@ export class BinaryBridge {
|
|
|
130
161
|
cwd;
|
|
131
162
|
process = null;
|
|
132
163
|
pending = new Map();
|
|
164
|
+
outstandingBackgroundTaskIds = new Set();
|
|
133
165
|
nextId = 1;
|
|
134
166
|
stdoutBuffer = "";
|
|
167
|
+
stdoutReadOffset = 0;
|
|
135
168
|
stderrBuffer = "";
|
|
136
169
|
/** Ring buffer of the last N stderr lines, cleared on every spawn. */
|
|
137
170
|
stderrTail = [];
|
|
@@ -260,6 +293,9 @@ export class BinaryBridge {
|
|
|
260
293
|
hasPendingRequests() {
|
|
261
294
|
return this.pending.size > 0;
|
|
262
295
|
}
|
|
296
|
+
hasOutstandingBackgroundTasks() {
|
|
297
|
+
return this.outstandingBackgroundTaskIds.size > 0;
|
|
298
|
+
}
|
|
263
299
|
/** Project root this bridge was spawned/configured for. */
|
|
264
300
|
getCwd() {
|
|
265
301
|
return this.cwd;
|
|
@@ -437,7 +473,7 @@ export class BinaryBridge {
|
|
|
437
473
|
entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
438
474
|
this.handleTimeout(requestSessionId);
|
|
439
475
|
}, effectiveTimeoutMs);
|
|
440
|
-
this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress });
|
|
476
|
+
this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress, command });
|
|
441
477
|
if (!this.process?.stdin?.writable) {
|
|
442
478
|
this.pending.delete(id);
|
|
443
479
|
clearTimeout(timer);
|
|
@@ -776,6 +812,7 @@ export class BinaryBridge {
|
|
|
776
812
|
});
|
|
777
813
|
this.process = child;
|
|
778
814
|
this.stdoutBuffer = "";
|
|
815
|
+
this.stdoutReadOffset = 0;
|
|
779
816
|
this.stderrBuffer = "";
|
|
780
817
|
this.lastChildActivityAt = 0;
|
|
781
818
|
this.consecutiveRequestTimeouts = 0;
|
|
@@ -823,24 +860,42 @@ export class BinaryBridge {
|
|
|
823
860
|
return `\n --- last ${this.stderrTail.length} stderr lines ---\n ${tail}`;
|
|
824
861
|
}
|
|
825
862
|
onStdoutData(data) {
|
|
863
|
+
if (this.stdoutReadOffset > STDOUT_BUFFER_COMPACT_THRESHOLD) {
|
|
864
|
+
this.compactStdoutBuffer();
|
|
865
|
+
}
|
|
826
866
|
this.stdoutBuffer += data;
|
|
827
|
-
if (this.stdoutBuffer.length > MAX_STDOUT_BUFFER) {
|
|
867
|
+
if (this.stdoutBuffer.length - this.stdoutReadOffset > MAX_STDOUT_BUFFER) {
|
|
828
868
|
this.handleCrash(new Error(`aft bridge stdout buffer exceeded ${MAX_STDOUT_BUFFER} bytes — killing bridge`));
|
|
829
869
|
return;
|
|
830
870
|
}
|
|
831
|
-
// Process complete lines
|
|
871
|
+
// Process complete lines without repeatedly slicing the remaining buffer.
|
|
832
872
|
let newlineIdx;
|
|
833
|
-
while ((newlineIdx = this.stdoutBuffer.indexOf("\n")) !== -1) {
|
|
834
|
-
const line = this.stdoutBuffer.slice(
|
|
835
|
-
this.
|
|
836
|
-
if (
|
|
837
|
-
|
|
838
|
-
|
|
873
|
+
while ((newlineIdx = this.stdoutBuffer.indexOf("\n", this.stdoutReadOffset)) !== -1) {
|
|
874
|
+
const line = this.stdoutBuffer.slice(this.stdoutReadOffset, newlineIdx).trim();
|
|
875
|
+
this.stdoutReadOffset = newlineIdx + 1;
|
|
876
|
+
if (line) {
|
|
877
|
+
this.processStdoutLine(line);
|
|
878
|
+
}
|
|
879
|
+
if (this.stdoutReadOffset > STDOUT_BUFFER_COMPACT_THRESHOLD &&
|
|
880
|
+
this.stdoutReadOffset > this.stdoutBuffer.length / 2) {
|
|
881
|
+
this.compactStdoutBuffer();
|
|
882
|
+
}
|
|
839
883
|
}
|
|
884
|
+
if (this.stdoutReadOffset === this.stdoutBuffer.length) {
|
|
885
|
+
this.stdoutBuffer = "";
|
|
886
|
+
this.stdoutReadOffset = 0;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
compactStdoutBuffer() {
|
|
890
|
+
if (this.stdoutReadOffset === 0)
|
|
891
|
+
return;
|
|
892
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(this.stdoutReadOffset);
|
|
893
|
+
this.stdoutReadOffset = 0;
|
|
840
894
|
}
|
|
841
895
|
flushStdoutBuffer() {
|
|
842
|
-
const line = this.stdoutBuffer.trim();
|
|
896
|
+
const line = this.stdoutBuffer.slice(this.stdoutReadOffset).trim();
|
|
843
897
|
this.stdoutBuffer = "";
|
|
898
|
+
this.stdoutReadOffset = 0;
|
|
844
899
|
if (!line)
|
|
845
900
|
return;
|
|
846
901
|
this.processStdoutLine(line);
|
|
@@ -873,6 +928,9 @@ export class BinaryBridge {
|
|
|
873
928
|
return;
|
|
874
929
|
}
|
|
875
930
|
if (response.type === "bash_completed") {
|
|
931
|
+
const taskId = bashTaskIdFrom(response);
|
|
932
|
+
if (taskId)
|
|
933
|
+
this.outstandingBackgroundTaskIds.delete(taskId);
|
|
876
934
|
this.onBashCompletion?.(response, this);
|
|
877
935
|
return;
|
|
878
936
|
}
|
|
@@ -903,6 +961,7 @@ export class BinaryBridge {
|
|
|
903
961
|
clearTimeout(entry.timer);
|
|
904
962
|
this.consecutiveRequestTimeouts = 0;
|
|
905
963
|
this.scheduleRestartCountReset();
|
|
964
|
+
this.accountForBashTaskResponse(entry.command, response);
|
|
906
965
|
this.captureStatusBar(response);
|
|
907
966
|
entry.resolve(response);
|
|
908
967
|
}
|
|
@@ -914,6 +973,22 @@ export class BinaryBridge {
|
|
|
914
973
|
this.warnVia(`Failed to parse stdout line: ${line}`);
|
|
915
974
|
}
|
|
916
975
|
}
|
|
976
|
+
accountForBashTaskResponse(command, response) {
|
|
977
|
+
const taskId = bashTaskIdFrom(response);
|
|
978
|
+
if (!taskId)
|
|
979
|
+
return;
|
|
980
|
+
if (isTerminalBashStatus(response.status)) {
|
|
981
|
+
this.outstandingBackgroundTaskIds.delete(taskId);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
// A successful bash spawn returns { task_id, status: "running", ... }.
|
|
985
|
+
// Bias toward wake safety: if a bash response has a task id but an unknown
|
|
986
|
+
// non-terminal/missing status, keep the bridge alive until a terminal
|
|
987
|
+
// bash_completed frame or terminal bash_status/bash_kill response removes it.
|
|
988
|
+
if (command === "bash" && response.success !== false) {
|
|
989
|
+
this.outstandingBackgroundTaskIds.add(taskId);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
917
992
|
/**
|
|
918
993
|
* Cache the agent status-bar counts from a response. The Rust `Response.data`
|
|
919
994
|
* is `#[serde(flatten)]`, so the attached `status_bar` object lands at the
|
|
@@ -939,6 +1014,13 @@ export class BinaryBridge {
|
|
|
939
1014
|
// sibling in-flight requests now instead of leaving them parked until their
|
|
940
1015
|
// own independent timers fire.
|
|
941
1016
|
this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
|
|
1017
|
+
// Forget outstanding background task ids: their removal hooks died with
|
|
1018
|
+
// the child (foreground polls were just rejected and won't resume, and a
|
|
1019
|
+
// completion frame can't arrive from a killed process), so keeping them
|
|
1020
|
+
// would pin this bridge against idle eviction forever. Safe to forget —
|
|
1021
|
+
// detached tasks persist undelivered completions on disk, and the next
|
|
1022
|
+
// spawn rehydrates and delivers them.
|
|
1023
|
+
this.outstandingBackgroundTaskIds.clear();
|
|
942
1024
|
if (this.process) {
|
|
943
1025
|
this.process.kill("SIGKILL");
|
|
944
1026
|
this.process = null;
|
|
@@ -977,6 +1059,11 @@ export class BinaryBridge {
|
|
|
977
1059
|
}
|
|
978
1060
|
this.clearRestartResetTimer();
|
|
979
1061
|
this.configured = false; // Force reconfigure on next command after restart
|
|
1062
|
+
// Forget outstanding background task ids — same rationale as
|
|
1063
|
+
// handleTimeout: abandoned removal hooks would otherwise pin this bridge
|
|
1064
|
+
// against idle eviction permanently (phantom ids). Disk-persisted
|
|
1065
|
+
// completions are re-delivered after the next spawn rehydrates.
|
|
1066
|
+
this.outstandingBackgroundTaskIds.clear();
|
|
980
1067
|
// Capture the tail BEFORE spawning the replacement, because the next spawn
|
|
981
1068
|
// clears the ring. The tail goes to the plugin log only — it's operator
|
|
982
1069
|
// diagnostic output that the agent can't act on. The pending-request
|