@adecore/agents 0.39.0 → 0.40.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/async.d.ts +5 -0
- package/dist/async.js +13 -5
- package/dist/chat/acp-protocol.js +3 -5
- package/dist/chat/chat-core.js +0 -2
- package/dist/chat/chat-process.d.ts +1 -0
- package/dist/chat/chat-process.js +1 -1
- package/dist/chat/chat-session.js +6 -8
- package/dist/chat/claude-protocol.js +1 -6
- package/dist/chat/claude-transcript.js +1 -3
- package/dist/chat/codex-protocol.js +1 -6
- package/dist/chat/projector.js +1 -3
- package/dist/chat/request-summary.js +1 -3
- package/dist/coded-error.js +1 -5
- package/dist/context/argv.js +1 -2
- package/dist/context/verb.js +1 -3
- package/dist/error-text.d.ts +1 -0
- package/dist/error-text.js +4 -0
- package/dist/fields.d.ts +3 -0
- package/dist/fields.js +12 -0
- package/dist/fs.d.ts +1 -0
- package/dist/fs.js +8 -4
- package/dist/lineage.js +11 -5
- package/dist/messages/notice-store.js +3 -4
- package/dist/providers/accounts/acp-account.js +3 -20
- package/dist/providers/accounts/service.d.ts +1 -2
- package/dist/providers/accounts/service.js +5 -9
- package/dist/providers/accounts/status.js +1 -3
- package/dist/providers/acp-agent-state.js +1 -3
- package/dist/providers/claude.js +0 -1
- package/dist/providers/codex.js +0 -1
- package/dist/providers/detect.js +1 -1
- package/dist/providers/shell-commands.js +1 -2
- package/dist/run-process.js +1 -3
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +5 -8
- package/dist/tasks/background-limit.js +3 -2
- package/dist/tasks/task-coordinator.js +11 -17
- package/dist/testing/watch.js +0 -1
- package/dist/usage/limits/monitor.js +1 -2
- package/dist/usage/readers/claude.js +3 -10
- package/dist/usage/readers/codex.js +2 -9
- package/dist/usage/readers/grok.js +3 -10
- package/dist/usage/readers/host-log.js +3 -10
- package/dist/usage/record.d.ts +2 -0
- package/dist/usage/record.js +13 -0
- package/package.json +6 -6
- package/src/async.ts +23 -5
- package/src/chat/acp-backend.ts +121 -0
- package/src/chat/acp-protocol.ts +172 -6
- package/src/chat/attachment-store.ts +21 -0
- package/src/chat/backend.ts +0 -2
- package/src/chat/bookmark-store.ts +23 -0
- package/src/chat/chat-core.ts +208 -7
- package/src/chat/chat-log.ts +19 -0
- package/src/chat/chat-process.ts +52 -1
- package/src/chat/chat-session.ts +401 -10
- package/src/chat/chat-store.ts +50 -0
- package/src/chat/chat-title.ts +9 -0
- package/src/chat/claude-backend.ts +50 -0
- package/src/chat/claude-protocol.ts +155 -8
- package/src/chat/claude-title.ts +10 -0
- package/src/chat/claude-transcript.ts +47 -4
- package/src/chat/codex-backend.ts +131 -0
- package/src/chat/codex-protocol.ts +180 -8
- package/src/chat/codex-thread.ts +23 -0
- package/src/chat/composer-preferences.ts +6 -0
- package/src/chat/context-breakdown.ts +11 -0
- package/src/chat/delta-coalescer.ts +8 -0
- package/src/chat/external-sessions.ts +28 -0
- package/src/chat/generated-image.ts +25 -0
- package/src/chat/index.ts +5 -0
- package/src/chat/input.ts +13 -0
- package/src/chat/json-rpc-transport.ts +28 -0
- package/src/chat/limit-resume.ts +13 -0
- package/src/chat/projector.ts +160 -4
- package/src/chat/readable-text.ts +3 -0
- package/src/chat/request-summary.ts +18 -4
- package/src/chat/subagent-projection.ts +3 -0
- package/src/chat/subagent-reader.ts +76 -0
- package/src/chat/subagent-settlement.ts +24 -0
- package/src/chat/thread-search.ts +13 -0
- package/src/chat/thread.ts +31 -0
- package/src/chat/ui-queries.ts +122 -0
- package/src/chat/ui-replies.ts +34 -0
- package/src/chat/visual-store.ts +49 -0
- package/src/chat/wake-chat.ts +3 -0
- package/src/client-sinks.ts +2 -0
- package/src/coded-error.ts +1 -5
- package/src/context/argv.ts +18 -2
- package/src/context/index.ts +1 -0
- package/src/context/refusal.ts +3 -0
- package/src/context/verb.ts +32 -4
- package/src/coordination/index.ts +10 -0
- package/src/error-text.ts +7 -0
- package/src/fields.ts +14 -0
- package/src/fs.ts +20 -4
- package/src/host/agent-host.ts +20 -0
- package/src/host/environment.ts +6 -0
- package/src/host/handlers.ts +18 -0
- package/src/host/memory-port.ts +4 -0
- package/src/host/wiring.ts +10 -0
- package/src/index.ts +1 -0
- package/src/lineage.ts +24 -5
- package/src/messages/deliver-message.ts +6 -0
- package/src/messages/deliver-notice.ts +13 -0
- package/src/messages/notice-store.ts +16 -4
- package/src/modes.ts +1 -0
- package/src/outbox/outbox-worker.ts +31 -0
- package/src/outbox/outbox.ts +5 -0
- package/src/providers/accounts/accounts.ts +36 -0
- package/src/providers/accounts/acp-account.ts +22 -27
- package/src/providers/accounts/launch.ts +4 -0
- package/src/providers/accounts/service.ts +110 -12
- package/src/providers/accounts/shadow-home.ts +10 -0
- package/src/providers/accounts/status.ts +13 -4
- package/src/providers/accounts/store.ts +25 -0
- package/src/providers/accounts/variables.ts +11 -1
- package/src/providers/acp-agent-state.ts +24 -3
- package/src/providers/acp-provider.ts +5 -0
- package/src/providers/acp-usage-log.ts +2 -0
- package/src/providers/catalog.ts +17 -0
- package/src/providers/claude.ts +10 -1
- package/src/providers/codex-rules.ts +3 -0
- package/src/providers/codex.ts +2 -1
- package/src/providers/detect.ts +6 -1
- package/src/providers/provider.ts +1 -1
- package/src/providers/registry.ts +18 -2
- package/src/providers/shell-commands.ts +37 -2
- package/src/record-directory.ts +19 -0
- package/src/run-process.ts +24 -3
- package/src/serializer.ts +3 -0
- package/src/skills.ts +73 -10
- package/src/tasks/background-limit.ts +9 -2
- package/src/tasks/end-children.ts +9 -0
- package/src/tasks/give-task.ts +8 -0
- package/src/tasks/task-coordinator.ts +81 -17
- package/src/tasks/task-store.ts +28 -0
- package/src/tasks/waiting-child.ts +21 -0
- package/src/tasks/wake-parent.ts +26 -0
- package/src/tasks/wiring.ts +22 -0
- package/src/testing/accounts.ts +2 -0
- package/src/testing/fake-acp-capture.ts +16 -0
- package/src/testing/fake-acp.ts +66 -0
- package/src/testing/fake-claude.ts +177 -0
- package/src/testing/fake-cli.ts +29 -0
- package/src/testing/fake-codex.ts +122 -0
- package/src/testing/manual-clock.ts +2 -0
- package/src/testing/watch.ts +7 -1
- package/src/title-file.ts +12 -0
- package/src/usage/accounts.ts +6 -0
- package/src/usage/aggregate.ts +19 -0
- package/src/usage/exchange.ts +12 -0
- package/src/usage/index-file.ts +27 -0
- package/src/usage/limits/monitor.ts +31 -3
- package/src/usage/limits/normalize.ts +30 -0
- package/src/usage/limits/probe.ts +17 -0
- package/src/usage/pricing.ts +35 -0
- package/src/usage/projects.ts +8 -0
- package/src/usage/readers/claude.ts +12 -9
- package/src/usage/readers/codex.ts +40 -8
- package/src/usage/readers/grok.ts +17 -9
- package/src/usage/readers/host-log.ts +9 -9
- package/src/usage/record.ts +22 -0
- package/src/usage/roots.ts +6 -0
- package/src/usage/scanner.ts +61 -0
- package/src/usage/usage-service.ts +14 -0
- package/src/watch-seam.ts +8 -0
- package/src/windows-command.ts +12 -0
package/src/chat/chat-log.ts
CHANGED
|
@@ -22,20 +22,25 @@ const LineSchema = z.object({ seq: z.number().int().positive(), at: z.number(),
|
|
|
22
22
|
*/
|
|
23
23
|
export function parseLog(raw: string): ChatLogLine[] {
|
|
24
24
|
const lines: ChatLogLine[] = [];
|
|
25
|
+
|
|
25
26
|
for (const text of raw.split('\n')) {
|
|
26
27
|
if (text === '') {
|
|
27
28
|
continue;
|
|
28
29
|
}
|
|
30
|
+
|
|
29
31
|
let parsed: ReturnType<typeof LineSchema.safeParse>;
|
|
32
|
+
|
|
30
33
|
try {
|
|
31
34
|
parsed = LineSchema.safeParse(JSON.parse(text));
|
|
32
35
|
} catch {
|
|
33
36
|
continue;
|
|
34
37
|
}
|
|
38
|
+
|
|
35
39
|
if (parsed.success && parsed.data.seq > (lines.at(-1)?.seq ?? 0)) {
|
|
36
40
|
lines.push(parsed.data);
|
|
37
41
|
}
|
|
38
42
|
}
|
|
43
|
+
|
|
39
44
|
return lines;
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -72,6 +77,7 @@ export class ChatLog {
|
|
|
72
77
|
const first = state.lines[0];
|
|
73
78
|
// A log with a hole in front of it only answers from after the hole.
|
|
74
79
|
this.base = first === undefined ? this.seqValue : first.seq - 1;
|
|
80
|
+
|
|
75
81
|
for (let i = 1; i < state.lines.length; i++) {
|
|
76
82
|
if (state.lines[i]!.seq !== state.lines[i - 1]!.seq + 1) {
|
|
77
83
|
this.base = state.lines[i - 1]!.seq;
|
|
@@ -102,12 +108,15 @@ export class ChatLog {
|
|
|
102
108
|
append(event: ChatEvent, at: number): number {
|
|
103
109
|
this.seqValue += 1;
|
|
104
110
|
const line: ChatLogLine = { seq: this.seqValue, at, event };
|
|
111
|
+
|
|
105
112
|
if (event.type === 'reset') {
|
|
106
113
|
this.resetSeqValue = line.seq;
|
|
107
114
|
}
|
|
115
|
+
|
|
108
116
|
const text = `${JSON.stringify(line)}\n`;
|
|
109
117
|
this.lines.push(line);
|
|
110
118
|
this.bytes += Buffer.byteLength(text);
|
|
119
|
+
|
|
111
120
|
if (this.path !== null) {
|
|
112
121
|
try {
|
|
113
122
|
this.fd ??= this.open(this.path);
|
|
@@ -118,6 +127,7 @@ export class ChatLog {
|
|
|
118
127
|
console.error(`Appending to ${this.path} failed:`, errorText(e));
|
|
119
128
|
}
|
|
120
129
|
}
|
|
130
|
+
|
|
121
131
|
return line.seq;
|
|
122
132
|
}
|
|
123
133
|
|
|
@@ -130,6 +140,7 @@ export class ChatLog {
|
|
|
130
140
|
if (since < this.resetSeqValue || since < this.base || since > this.seqValue) {
|
|
131
141
|
return null;
|
|
132
142
|
}
|
|
143
|
+
|
|
133
144
|
return this.lines.filter((line) => line.seq > since).map((line) => line.event);
|
|
134
145
|
}
|
|
135
146
|
|
|
@@ -143,20 +154,25 @@ export class ChatLog {
|
|
|
143
154
|
if (seq <= this.base) {
|
|
144
155
|
return;
|
|
145
156
|
}
|
|
157
|
+
|
|
146
158
|
this.lines = this.lines.filter((line) => line.seq > seq);
|
|
147
159
|
this.base = seq;
|
|
148
160
|
const text = this.lines.map((line) => `${JSON.stringify(line)}\n`).join('');
|
|
149
161
|
this.bytes = Buffer.byteLength(text);
|
|
162
|
+
|
|
150
163
|
if (this.path === null) {
|
|
151
164
|
return;
|
|
152
165
|
}
|
|
166
|
+
|
|
153
167
|
this.close();
|
|
168
|
+
|
|
154
169
|
try {
|
|
155
170
|
if (text === '') {
|
|
156
171
|
rmSync(this.path, { force: true });
|
|
157
172
|
} else {
|
|
158
173
|
writeAtomicSync(this.path, text);
|
|
159
174
|
}
|
|
175
|
+
|
|
160
176
|
this.written = true;
|
|
161
177
|
} catch (e) {
|
|
162
178
|
console.error(`Compacting ${this.path} failed:`, errorText(e));
|
|
@@ -175,13 +191,16 @@ export class ChatLog {
|
|
|
175
191
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
176
192
|
const fd = openSync(path, 'a+', 0o600);
|
|
177
193
|
const { size } = fstatSync(fd);
|
|
194
|
+
|
|
178
195
|
if (size > 0) {
|
|
179
196
|
const last = Buffer.alloc(1);
|
|
180
197
|
readSync(fd, last, 0, 1, size - 1);
|
|
198
|
+
|
|
181
199
|
if (last[0] !== 0x0a) {
|
|
182
200
|
writeSync(fd, '\n');
|
|
183
201
|
}
|
|
184
202
|
}
|
|
203
|
+
|
|
185
204
|
return fd;
|
|
186
205
|
}
|
|
187
206
|
}
|
package/src/chat/chat-process.ts
CHANGED
|
@@ -35,10 +35,13 @@ const STDOUT_DRAIN_MS = 250;
|
|
|
35
35
|
|
|
36
36
|
export function spawnChatProcess(options: ChatSpawnOptions): ChatProcess {
|
|
37
37
|
const [command, ...args] = options.command;
|
|
38
|
+
|
|
38
39
|
if (command === undefined) {
|
|
39
40
|
throw new Error('No command to start');
|
|
40
41
|
}
|
|
42
|
+
|
|
41
43
|
const target = spawnTarget(command, args, { env: options.env });
|
|
44
|
+
|
|
42
45
|
const child = spawn(target.file, target.args, {
|
|
43
46
|
cwd: options.cwd,
|
|
44
47
|
env: options.env,
|
|
@@ -49,13 +52,17 @@ export function spawnChatProcess(options: ChatSpawnOptions): ChatProcess {
|
|
|
49
52
|
detached: process.platform !== 'win32',
|
|
50
53
|
windowsHide: true
|
|
51
54
|
});
|
|
55
|
+
|
|
52
56
|
// Node reports a missing executable as an event after the call; a start that failed says so on the spot.
|
|
53
57
|
child.on('error', () => undefined);
|
|
54
58
|
const pid = child.pid;
|
|
59
|
+
|
|
55
60
|
if (pid === undefined) {
|
|
56
61
|
throw new Error(`Could not start ${command}: no such executable`);
|
|
57
62
|
}
|
|
63
|
+
|
|
58
64
|
child.stdin.on('error', (error) => options.onError?.(error));
|
|
65
|
+
|
|
59
66
|
child.on('exit', (exitCode, signal) => {
|
|
60
67
|
// The last lines may still be on their way through the pipe; they belong before the exit.
|
|
61
68
|
const report = (): void => {
|
|
@@ -63,13 +70,18 @@ export function spawnChatProcess(options: ChatSpawnOptions): ChatProcess {
|
|
|
63
70
|
child.stdout.off('end', report);
|
|
64
71
|
setImmediate(() => options.onExit(exitCode, signal));
|
|
65
72
|
};
|
|
73
|
+
|
|
66
74
|
const timer = setTimeout(report, STDOUT_DRAIN_MS);
|
|
75
|
+
|
|
67
76
|
if (child.stdout.readableEnded) {
|
|
68
77
|
report();
|
|
78
|
+
|
|
69
79
|
return;
|
|
70
80
|
}
|
|
81
|
+
|
|
71
82
|
child.stdout.once('end', report);
|
|
72
83
|
});
|
|
84
|
+
|
|
73
85
|
return {
|
|
74
86
|
pid,
|
|
75
87
|
stdin: {
|
|
@@ -92,10 +104,13 @@ export interface ProcessTreeKillOptions {
|
|
|
92
104
|
/* Signals a process and everything it started. Windows has no process groups and no SIGTERM, so there the tree is ended at once. */
|
|
93
105
|
export function killProcessTree(pid: number, signal: 'SIGTERM' | 'SIGKILL', options: ProcessTreeKillOptions = {}): void {
|
|
94
106
|
const { platform = process.platform, kill = process.kill, run = runQuietly } = options;
|
|
107
|
+
|
|
95
108
|
if (platform === 'win32') {
|
|
96
109
|
run(['taskkill', '/pid', String(pid), '/t', '/f']);
|
|
110
|
+
|
|
97
111
|
return;
|
|
98
112
|
}
|
|
113
|
+
|
|
99
114
|
try {
|
|
100
115
|
kill(-pid, signal);
|
|
101
116
|
} catch {
|
|
@@ -107,6 +122,7 @@ function runQuietly([command, ...args]: string[]): void {
|
|
|
107
122
|
if (command === undefined) {
|
|
108
123
|
return;
|
|
109
124
|
}
|
|
125
|
+
|
|
110
126
|
const child = spawn(command, args, { stdio: 'ignore', windowsHide: true });
|
|
111
127
|
// A tree that already ended makes taskkill fail, which is what the caller wanted anyway.
|
|
112
128
|
child.on('error', () => undefined);
|
|
@@ -115,7 +131,7 @@ function runQuietly([command, ...args]: string[]): void {
|
|
|
115
131
|
// After stdin closed, a CLI that is still around is not going to say more.
|
|
116
132
|
const EXIT_GRACE_MS = 3000;
|
|
117
133
|
// What a CLI and everything it started get between the SIGTERM and the SIGKILL.
|
|
118
|
-
const KILL_GRACE_MS = 2000;
|
|
134
|
+
export const KILL_GRACE_MS = 2000;
|
|
119
135
|
// How long a crash note waits for the last of stderr; a process the CLI started may hold the pipe open.
|
|
120
136
|
const STDERR_DRAIN_MS = 250;
|
|
121
137
|
// A pipe nobody reads is buffered in memory for as long as the CLI lives, so stderr is read into this much and no more.
|
|
@@ -140,19 +156,24 @@ export class StreamTail {
|
|
|
140
156
|
.map((line) => line.trimEnd())
|
|
141
157
|
.filter((line) => line.trim() !== '')
|
|
142
158
|
.slice(-NOTE_TAIL_LINES);
|
|
159
|
+
|
|
143
160
|
const tail = lines.join('\n').slice(-NOTE_TAIL_CHARS);
|
|
161
|
+
|
|
144
162
|
return tail === '' ? null : tail;
|
|
145
163
|
}
|
|
146
164
|
|
|
147
165
|
private async read(stream: ReadableStream<Uint8Array>): Promise<void> {
|
|
148
166
|
const reader = stream.getReader();
|
|
149
167
|
const decoder = new TextDecoder();
|
|
168
|
+
|
|
150
169
|
try {
|
|
151
170
|
while (true) {
|
|
152
171
|
const { value, done } = await reader.read();
|
|
172
|
+
|
|
153
173
|
if (done) {
|
|
154
174
|
break;
|
|
155
175
|
}
|
|
176
|
+
|
|
156
177
|
this.text = (this.text + decoder.decode(value, { stream: true })).slice(-STDERR_TAIL_CHARS);
|
|
157
178
|
}
|
|
158
179
|
} catch {
|
|
@@ -186,21 +207,26 @@ export class LineSplitter {
|
|
|
186
207
|
push(text: string): void {
|
|
187
208
|
let start = 0;
|
|
188
209
|
let newline = text.indexOf('\n');
|
|
210
|
+
|
|
189
211
|
while (newline >= 0) {
|
|
190
212
|
this.add(text.slice(start, newline));
|
|
191
213
|
const line = this.take();
|
|
214
|
+
|
|
192
215
|
if (line !== null) {
|
|
193
216
|
this.onLine(line);
|
|
194
217
|
}
|
|
218
|
+
|
|
195
219
|
start = newline + 1;
|
|
196
220
|
newline = text.indexOf('\n', start);
|
|
197
221
|
}
|
|
222
|
+
|
|
198
223
|
this.add(text.slice(start));
|
|
199
224
|
}
|
|
200
225
|
|
|
201
226
|
/* Hands on what came after the last line break, for a stream that ended without one. */
|
|
202
227
|
end(): void {
|
|
203
228
|
const line = this.take();
|
|
229
|
+
|
|
204
230
|
if (line !== null && line !== '') {
|
|
205
231
|
this.onLine(line);
|
|
206
232
|
}
|
|
@@ -210,13 +236,17 @@ export class LineSplitter {
|
|
|
210
236
|
if (this.dropping || piece === '') {
|
|
211
237
|
return;
|
|
212
238
|
}
|
|
239
|
+
|
|
213
240
|
this.length += piece.length;
|
|
241
|
+
|
|
214
242
|
if (this.length > this.max) {
|
|
215
243
|
this.parts = [];
|
|
216
244
|
this.dropping = true;
|
|
217
245
|
this.onOverflow();
|
|
246
|
+
|
|
218
247
|
return;
|
|
219
248
|
}
|
|
249
|
+
|
|
220
250
|
this.parts.push(piece);
|
|
221
251
|
}
|
|
222
252
|
|
|
@@ -225,6 +255,7 @@ export class LineSplitter {
|
|
|
225
255
|
this.parts = [];
|
|
226
256
|
this.length = 0;
|
|
227
257
|
this.dropping = false;
|
|
258
|
+
|
|
228
259
|
return line;
|
|
229
260
|
}
|
|
230
261
|
}
|
|
@@ -242,14 +273,18 @@ export async function readStreamLines(
|
|
|
242
273
|
const reader = stream.getReader();
|
|
243
274
|
const decoder = new TextDecoder();
|
|
244
275
|
const lines = new LineSplitter(onLine, onOverflow, max);
|
|
276
|
+
|
|
245
277
|
try {
|
|
246
278
|
while (true) {
|
|
247
279
|
const { value, done } = await reader.read();
|
|
280
|
+
|
|
248
281
|
if (done) {
|
|
249
282
|
break;
|
|
250
283
|
}
|
|
284
|
+
|
|
251
285
|
lines.push(decoder.decode(value, { stream: true }));
|
|
252
286
|
}
|
|
287
|
+
|
|
253
288
|
lines.end();
|
|
254
289
|
} finally {
|
|
255
290
|
reader.releaseLock();
|
|
@@ -278,10 +313,13 @@ export class ChatChild {
|
|
|
278
313
|
|
|
279
314
|
constructor(options: ChatChildOptions) {
|
|
280
315
|
let settle: () => void = () => undefined;
|
|
316
|
+
|
|
281
317
|
this.exited = new Promise((resolve) => {
|
|
282
318
|
settle = resolve;
|
|
283
319
|
});
|
|
320
|
+
|
|
284
321
|
const spawn = options.spawn ?? spawnChatProcess;
|
|
322
|
+
|
|
285
323
|
this.process = spawn({
|
|
286
324
|
command: options.command,
|
|
287
325
|
cwd: options.cwd,
|
|
@@ -294,6 +332,7 @@ export class ChatChild {
|
|
|
294
332
|
void this.report(exitCode, signal, options.onExit);
|
|
295
333
|
}
|
|
296
334
|
});
|
|
335
|
+
|
|
297
336
|
this.tail = this.process.stderr ? new StreamTail(this.process.stderr) : null;
|
|
298
337
|
}
|
|
299
338
|
|
|
@@ -306,31 +345,38 @@ export class ChatChild {
|
|
|
306
345
|
if (this.hasExited || this.graceTimer !== null || this.ending !== null) {
|
|
307
346
|
return;
|
|
308
347
|
}
|
|
348
|
+
|
|
309
349
|
this.graceTimer = setTimeout(() => {
|
|
310
350
|
this.graceTimer = null;
|
|
311
351
|
void this.end();
|
|
312
352
|
}, EXIT_GRACE_MS);
|
|
353
|
+
|
|
313
354
|
this.graceTimer.unref?.();
|
|
314
355
|
}
|
|
315
356
|
|
|
316
357
|
/* SIGTERM to the group now and SIGKILL after the grace; settles once the CLI exited or was sent the SIGKILL. */
|
|
317
358
|
end(): Promise<void> {
|
|
318
359
|
this.clearGrace();
|
|
360
|
+
|
|
319
361
|
if (this.hasExited) {
|
|
320
362
|
return Promise.resolve();
|
|
321
363
|
}
|
|
364
|
+
|
|
322
365
|
if (this.ending === null) {
|
|
323
366
|
this.process.kill('SIGTERM');
|
|
367
|
+
|
|
324
368
|
this.ending = new Promise((resolve) => {
|
|
325
369
|
// Sent even when the CLI left in time: what it started may still be shutting down, or ignoring the SIGTERM.
|
|
326
370
|
const timer = setTimeout(() => {
|
|
327
371
|
this.process.kill('SIGKILL');
|
|
328
372
|
resolve();
|
|
329
373
|
}, KILL_GRACE_MS);
|
|
374
|
+
|
|
330
375
|
timer.unref?.();
|
|
331
376
|
void this.exited.then(resolve);
|
|
332
377
|
});
|
|
333
378
|
}
|
|
379
|
+
|
|
334
380
|
return this.ending;
|
|
335
381
|
}
|
|
336
382
|
|
|
@@ -344,16 +390,21 @@ export class ChatChild {
|
|
|
344
390
|
private async report(exitCode: number | null, signal: string | null, onExit: ChatChildOptions['onExit']): Promise<void> {
|
|
345
391
|
if (exitCode === null || exitCode === 0 || this.tail === null) {
|
|
346
392
|
onExit(exitCode, null, signal);
|
|
393
|
+
|
|
347
394
|
return;
|
|
348
395
|
}
|
|
396
|
+
|
|
349
397
|
let release: () => void = () => undefined;
|
|
398
|
+
|
|
350
399
|
const timer = setTimeout(() => release(), STDERR_DRAIN_MS);
|
|
400
|
+
|
|
351
401
|
await Promise.race([
|
|
352
402
|
this.tail.done,
|
|
353
403
|
new Promise<void>((resolve) => {
|
|
354
404
|
release = resolve;
|
|
355
405
|
})
|
|
356
406
|
]);
|
|
407
|
+
|
|
357
408
|
clearTimeout(timer);
|
|
358
409
|
onExit(exitCode, this.tail.lines(), signal);
|
|
359
410
|
}
|