@adecore/agents 0.33.3 → 0.34.1

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.
@@ -13,7 +13,7 @@ export interface ChatSpawnOptions {
13
13
  command: string[];
14
14
  cwd: string;
15
15
  env: Record<string, string>;
16
- onExit(exitCode: number | null): void;
16
+ onExit(exitCode: number | null, signal?: string | null): void;
17
17
  onError?(error: Error): void;
18
18
  }
19
19
  export type SpawnChatProcess = (options: ChatSpawnOptions) => ChatProcess;
@@ -48,7 +48,7 @@ export declare class LineSplitter {
48
48
  export declare function readStreamLines(stream: ReadableStream<Uint8Array>, onLine: (line: string) => void, onOverflow: () => void, max?: number): Promise<void>;
49
49
  export interface ChatChildOptions extends Omit<ChatSpawnOptions, 'onExit'> {
50
50
  spawn?: SpawnChatProcess;
51
- onExit(exitCode: number | null, stderr: string | null): void;
51
+ onExit(exitCode: number | null, stderr: string | null, signal?: string | null): void;
52
52
  onError?(error: Error): void;
53
53
  }
54
54
  export declare class ChatChild {
@@ -26,12 +26,12 @@ export function spawnChatProcess(options) {
26
26
  throw new Error(`Could not start ${command}: no such executable`);
27
27
  }
28
28
  child.stdin.on('error', (error) => options.onError?.(error));
29
- child.on('exit', (exitCode) => {
29
+ child.on('exit', (exitCode, signal) => {
30
30
  // The last lines may still be on their way through the pipe; they belong before the exit.
31
31
  const report = () => {
32
32
  clearTimeout(timer);
33
33
  child.stdout.off('end', report);
34
- setImmediate(() => options.onExit(exitCode));
34
+ setImmediate(() => options.onExit(exitCode, signal));
35
35
  };
36
36
  const timer = setTimeout(report, STDOUT_DRAIN_MS);
37
37
  if (child.stdout.readableEnded) {
@@ -225,11 +225,11 @@ export class ChatChild {
225
225
  cwd: options.cwd,
226
226
  env: options.env,
227
227
  ...(options.onError ? { onError: options.onError } : {}),
228
- onExit: (exitCode) => {
228
+ onExit: (exitCode, signal = null) => {
229
229
  this.hasExited = true;
230
230
  this.clearGrace();
231
231
  settle();
232
- void this.report(exitCode, options.onExit);
232
+ void this.report(exitCode, signal, options.onExit);
233
233
  }
234
234
  });
235
235
  this.tail = this.process.stderr ? new StreamTail(this.process.stderr) : null;
@@ -274,9 +274,9 @@ export class ChatChild {
274
274
  this.graceTimer = null;
275
275
  }
276
276
  }
277
- async report(exitCode, onExit) {
277
+ async report(exitCode, signal, onExit) {
278
278
  if (exitCode === null || exitCode === 0 || this.tail === null) {
279
- onExit(exitCode, null);
279
+ onExit(exitCode, null, signal);
280
280
  return;
281
281
  }
282
282
  let release = () => undefined;
@@ -288,6 +288,6 @@ export class ChatChild {
288
288
  })
289
289
  ]);
290
290
  clearTimeout(timer);
291
- onExit(exitCode, this.tail.lines());
291
+ onExit(exitCode, this.tail.lines(), signal);
292
292
  }
293
293
  }
@@ -82,7 +82,8 @@ export class RecordDirectory {
82
82
  /*
83
83
  * Holds the record in memory at once, so the next question in the same tick already sees it, and
84
84
  * answers whether it is still the one this directory holds by the time the file is written. A
85
- * record removed while its file was being written does not come back: the rename is undone.
85
+ * record removed while its file was being written does not come back: the rename is undone, unless
86
+ * a newer record took the id meanwhile, whose own write is queued behind this one and replaces the file.
86
87
  */
87
88
  write(record) {
88
89
  const id = this.idOf(record);
@@ -93,11 +94,13 @@ export class RecordDirectory {
93
94
  }
94
95
  await mkdir(this.dir, { recursive: true, mode: 0o700 });
95
96
  await this.writeFile(this.pathOf(id), JSON.stringify(record));
97
+ if (this.records.get(id) === record) {
98
+ return true;
99
+ }
96
100
  if (!this.records.has(id)) {
97
101
  await this.removeFile(this.pathOf(id));
98
- return false;
99
102
  }
100
- return true;
103
+ return false;
101
104
  });
102
105
  }
103
106
  /* Forgets a record here and now; its file follows, after what is queued for it. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agents",
3
- "version": "0.33.3",
3
+ "version": "0.34.1",
4
4
  "description": "Node-compatible agent chat hosts, providers, accounts, usage and durable task coordination.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -81,8 +81,8 @@
81
81
  "test": "bun --conditions=source test"
82
82
  },
83
83
  "dependencies": {
84
- "@adecore/agent-contracts": "0.33.3",
85
- "@adecore/intelligent-ui": "0.33.3",
84
+ "@adecore/agent-contracts": "0.34.1",
85
+ "@adecore/intelligent-ui": "0.34.1",
86
86
  "zod": "^4.6.5"
87
87
  },
88
88
  "devDependencies": {
@@ -24,7 +24,7 @@ export interface ChatSpawnOptions {
24
24
  command: string[];
25
25
  cwd: string;
26
26
  env: Record<string, string>;
27
- onExit(exitCode: number | null): void;
27
+ onExit(exitCode: number | null, signal?: string | null): void;
28
28
  onError?(error: Error): void;
29
29
  }
30
30
 
@@ -56,12 +56,12 @@ export function spawnChatProcess(options: ChatSpawnOptions): ChatProcess {
56
56
  throw new Error(`Could not start ${command}: no such executable`);
57
57
  }
58
58
  child.stdin.on('error', (error) => options.onError?.(error));
59
- child.on('exit', (exitCode) => {
59
+ child.on('exit', (exitCode, signal) => {
60
60
  // The last lines may still be on their way through the pipe; they belong before the exit.
61
61
  const report = (): void => {
62
62
  clearTimeout(timer);
63
63
  child.stdout.off('end', report);
64
- setImmediate(() => options.onExit(exitCode));
64
+ setImmediate(() => options.onExit(exitCode, signal));
65
65
  };
66
66
  const timer = setTimeout(report, STDOUT_DRAIN_MS);
67
67
  if (child.stdout.readableEnded) {
@@ -259,7 +259,7 @@ export async function readStreamLines(
259
259
  export interface ChatChildOptions extends Omit<ChatSpawnOptions, 'onExit'> {
260
260
  spawn?: SpawnChatProcess;
261
261
  // `stderr` is the tail of what the CLI wrote there, for an exit with an error code only.
262
- onExit(exitCode: number | null, stderr: string | null): void;
262
+ onExit(exitCode: number | null, stderr: string | null, signal?: string | null): void;
263
263
  onError?(error: Error): void;
264
264
  }
265
265
 
@@ -287,11 +287,11 @@ export class ChatChild {
287
287
  cwd: options.cwd,
288
288
  env: options.env,
289
289
  ...(options.onError ? { onError: options.onError } : {}),
290
- onExit: (exitCode) => {
290
+ onExit: (exitCode, signal = null) => {
291
291
  this.hasExited = true;
292
292
  this.clearGrace();
293
293
  settle();
294
- void this.report(exitCode, options.onExit);
294
+ void this.report(exitCode, signal, options.onExit);
295
295
  }
296
296
  });
297
297
  this.tail = this.process.stderr ? new StreamTail(this.process.stderr) : null;
@@ -341,9 +341,9 @@ export class ChatChild {
341
341
  }
342
342
  }
343
343
 
344
- private async report(exitCode: number | null, onExit: ChatChildOptions['onExit']): Promise<void> {
344
+ private async report(exitCode: number | null, signal: string | null, onExit: ChatChildOptions['onExit']): Promise<void> {
345
345
  if (exitCode === null || exitCode === 0 || this.tail === null) {
346
- onExit(exitCode, null);
346
+ onExit(exitCode, null, signal);
347
347
  return;
348
348
  }
349
349
  let release: () => void = () => undefined;
@@ -355,6 +355,6 @@ export class ChatChild {
355
355
  })
356
356
  ]);
357
357
  clearTimeout(timer);
358
- onExit(exitCode, this.tail.lines());
358
+ onExit(exitCode, this.tail.lines(), signal);
359
359
  }
360
360
  }
@@ -103,7 +103,8 @@ export class RecordDirectory<T> {
103
103
  /*
104
104
  * Holds the record in memory at once, so the next question in the same tick already sees it, and
105
105
  * answers whether it is still the one this directory holds by the time the file is written. A
106
- * record removed while its file was being written does not come back: the rename is undone.
106
+ * record removed while its file was being written does not come back: the rename is undone, unless
107
+ * a newer record took the id meanwhile, whose own write is queued behind this one and replaces the file.
107
108
  */
108
109
  write(record: T): Promise<boolean> {
109
110
  const id = this.idOf(record);
@@ -114,11 +115,13 @@ export class RecordDirectory<T> {
114
115
  }
115
116
  await mkdir(this.dir, { recursive: true, mode: 0o700 });
116
117
  await this.writeFile(this.pathOf(id), JSON.stringify(record));
118
+ if (this.records.get(id) === record) {
119
+ return true;
120
+ }
117
121
  if (!this.records.has(id)) {
118
122
  await this.removeFile(this.pathOf(id));
119
- return false;
120
123
  }
121
- return true;
124
+ return false;
122
125
  });
123
126
  }
124
127