@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.
Files changed (168) hide show
  1. package/dist/async.d.ts +5 -0
  2. package/dist/async.js +13 -5
  3. package/dist/chat/acp-protocol.js +3 -5
  4. package/dist/chat/chat-core.js +0 -2
  5. package/dist/chat/chat-process.d.ts +1 -0
  6. package/dist/chat/chat-process.js +1 -1
  7. package/dist/chat/chat-session.js +6 -8
  8. package/dist/chat/claude-protocol.js +1 -6
  9. package/dist/chat/claude-transcript.js +1 -3
  10. package/dist/chat/codex-protocol.js +1 -6
  11. package/dist/chat/projector.js +1 -3
  12. package/dist/chat/request-summary.js +1 -3
  13. package/dist/coded-error.js +1 -5
  14. package/dist/context/argv.js +1 -2
  15. package/dist/context/verb.js +1 -3
  16. package/dist/error-text.d.ts +1 -0
  17. package/dist/error-text.js +4 -0
  18. package/dist/fields.d.ts +3 -0
  19. package/dist/fields.js +12 -0
  20. package/dist/fs.d.ts +1 -0
  21. package/dist/fs.js +8 -4
  22. package/dist/lineage.js +11 -5
  23. package/dist/messages/notice-store.js +3 -4
  24. package/dist/providers/accounts/acp-account.js +3 -20
  25. package/dist/providers/accounts/service.d.ts +1 -2
  26. package/dist/providers/accounts/service.js +5 -9
  27. package/dist/providers/accounts/status.js +1 -3
  28. package/dist/providers/acp-agent-state.js +1 -3
  29. package/dist/providers/claude.js +0 -1
  30. package/dist/providers/codex.js +0 -1
  31. package/dist/providers/detect.js +1 -1
  32. package/dist/providers/shell-commands.js +1 -2
  33. package/dist/run-process.js +1 -3
  34. package/dist/skills.d.ts +1 -1
  35. package/dist/skills.js +5 -8
  36. package/dist/tasks/background-limit.js +3 -2
  37. package/dist/tasks/task-coordinator.js +11 -17
  38. package/dist/testing/watch.js +0 -1
  39. package/dist/usage/limits/monitor.js +1 -2
  40. package/dist/usage/readers/claude.js +3 -10
  41. package/dist/usage/readers/codex.js +2 -9
  42. package/dist/usage/readers/grok.js +3 -10
  43. package/dist/usage/readers/host-log.js +3 -10
  44. package/dist/usage/record.d.ts +2 -0
  45. package/dist/usage/record.js +13 -0
  46. package/package.json +6 -6
  47. package/src/async.ts +23 -5
  48. package/src/chat/acp-backend.ts +121 -0
  49. package/src/chat/acp-protocol.ts +172 -6
  50. package/src/chat/attachment-store.ts +21 -0
  51. package/src/chat/backend.ts +0 -2
  52. package/src/chat/bookmark-store.ts +23 -0
  53. package/src/chat/chat-core.ts +208 -7
  54. package/src/chat/chat-log.ts +19 -0
  55. package/src/chat/chat-process.ts +52 -1
  56. package/src/chat/chat-session.ts +401 -10
  57. package/src/chat/chat-store.ts +50 -0
  58. package/src/chat/chat-title.ts +9 -0
  59. package/src/chat/claude-backend.ts +50 -0
  60. package/src/chat/claude-protocol.ts +155 -8
  61. package/src/chat/claude-title.ts +10 -0
  62. package/src/chat/claude-transcript.ts +47 -4
  63. package/src/chat/codex-backend.ts +131 -0
  64. package/src/chat/codex-protocol.ts +180 -8
  65. package/src/chat/codex-thread.ts +23 -0
  66. package/src/chat/composer-preferences.ts +6 -0
  67. package/src/chat/context-breakdown.ts +11 -0
  68. package/src/chat/delta-coalescer.ts +8 -0
  69. package/src/chat/external-sessions.ts +28 -0
  70. package/src/chat/generated-image.ts +25 -0
  71. package/src/chat/index.ts +5 -0
  72. package/src/chat/input.ts +13 -0
  73. package/src/chat/json-rpc-transport.ts +28 -0
  74. package/src/chat/limit-resume.ts +13 -0
  75. package/src/chat/projector.ts +160 -4
  76. package/src/chat/readable-text.ts +3 -0
  77. package/src/chat/request-summary.ts +18 -4
  78. package/src/chat/subagent-projection.ts +3 -0
  79. package/src/chat/subagent-reader.ts +76 -0
  80. package/src/chat/subagent-settlement.ts +24 -0
  81. package/src/chat/thread-search.ts +13 -0
  82. package/src/chat/thread.ts +31 -0
  83. package/src/chat/ui-queries.ts +122 -0
  84. package/src/chat/ui-replies.ts +34 -0
  85. package/src/chat/visual-store.ts +49 -0
  86. package/src/chat/wake-chat.ts +3 -0
  87. package/src/client-sinks.ts +2 -0
  88. package/src/coded-error.ts +1 -5
  89. package/src/context/argv.ts +18 -2
  90. package/src/context/index.ts +1 -0
  91. package/src/context/refusal.ts +3 -0
  92. package/src/context/verb.ts +32 -4
  93. package/src/coordination/index.ts +10 -0
  94. package/src/error-text.ts +7 -0
  95. package/src/fields.ts +14 -0
  96. package/src/fs.ts +20 -4
  97. package/src/host/agent-host.ts +20 -0
  98. package/src/host/environment.ts +6 -0
  99. package/src/host/handlers.ts +18 -0
  100. package/src/host/memory-port.ts +4 -0
  101. package/src/host/wiring.ts +10 -0
  102. package/src/index.ts +1 -0
  103. package/src/lineage.ts +24 -5
  104. package/src/messages/deliver-message.ts +6 -0
  105. package/src/messages/deliver-notice.ts +13 -0
  106. package/src/messages/notice-store.ts +16 -4
  107. package/src/modes.ts +1 -0
  108. package/src/outbox/outbox-worker.ts +31 -0
  109. package/src/outbox/outbox.ts +5 -0
  110. package/src/providers/accounts/accounts.ts +36 -0
  111. package/src/providers/accounts/acp-account.ts +22 -27
  112. package/src/providers/accounts/launch.ts +4 -0
  113. package/src/providers/accounts/service.ts +110 -12
  114. package/src/providers/accounts/shadow-home.ts +10 -0
  115. package/src/providers/accounts/status.ts +13 -4
  116. package/src/providers/accounts/store.ts +25 -0
  117. package/src/providers/accounts/variables.ts +11 -1
  118. package/src/providers/acp-agent-state.ts +24 -3
  119. package/src/providers/acp-provider.ts +5 -0
  120. package/src/providers/acp-usage-log.ts +2 -0
  121. package/src/providers/catalog.ts +17 -0
  122. package/src/providers/claude.ts +10 -1
  123. package/src/providers/codex-rules.ts +3 -0
  124. package/src/providers/codex.ts +2 -1
  125. package/src/providers/detect.ts +6 -1
  126. package/src/providers/provider.ts +1 -1
  127. package/src/providers/registry.ts +18 -2
  128. package/src/providers/shell-commands.ts +37 -2
  129. package/src/record-directory.ts +19 -0
  130. package/src/run-process.ts +24 -3
  131. package/src/serializer.ts +3 -0
  132. package/src/skills.ts +73 -10
  133. package/src/tasks/background-limit.ts +9 -2
  134. package/src/tasks/end-children.ts +9 -0
  135. package/src/tasks/give-task.ts +8 -0
  136. package/src/tasks/task-coordinator.ts +81 -17
  137. package/src/tasks/task-store.ts +28 -0
  138. package/src/tasks/waiting-child.ts +21 -0
  139. package/src/tasks/wake-parent.ts +26 -0
  140. package/src/tasks/wiring.ts +22 -0
  141. package/src/testing/accounts.ts +2 -0
  142. package/src/testing/fake-acp-capture.ts +16 -0
  143. package/src/testing/fake-acp.ts +66 -0
  144. package/src/testing/fake-claude.ts +177 -0
  145. package/src/testing/fake-cli.ts +29 -0
  146. package/src/testing/fake-codex.ts +122 -0
  147. package/src/testing/manual-clock.ts +2 -0
  148. package/src/testing/watch.ts +7 -1
  149. package/src/title-file.ts +12 -0
  150. package/src/usage/accounts.ts +6 -0
  151. package/src/usage/aggregate.ts +19 -0
  152. package/src/usage/exchange.ts +12 -0
  153. package/src/usage/index-file.ts +27 -0
  154. package/src/usage/limits/monitor.ts +31 -3
  155. package/src/usage/limits/normalize.ts +30 -0
  156. package/src/usage/limits/probe.ts +17 -0
  157. package/src/usage/pricing.ts +35 -0
  158. package/src/usage/projects.ts +8 -0
  159. package/src/usage/readers/claude.ts +12 -9
  160. package/src/usage/readers/codex.ts +40 -8
  161. package/src/usage/readers/grok.ts +17 -9
  162. package/src/usage/readers/host-log.ts +9 -9
  163. package/src/usage/record.ts +22 -0
  164. package/src/usage/roots.ts +6 -0
  165. package/src/usage/scanner.ts +61 -0
  166. package/src/usage/usage-service.ts +14 -0
  167. package/src/watch-seam.ts +8 -0
  168. package/src/windows-command.ts +12 -0
@@ -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
  }
@@ -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
  }