@adecore/agents 0.29.2 → 0.29.3

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.
@@ -104,6 +104,7 @@ export declare class ChatSession {
104
104
  private staleResults;
105
105
  private spokenTurnId;
106
106
  private titleTimer;
107
+ private readonly checkpointWork;
107
108
  private naming;
108
109
  private frozen;
109
110
  private readonly subagentOwners;
@@ -232,6 +233,7 @@ export declare class ChatSession {
232
233
  private settleAgentTurn;
233
234
  private checkpoint;
234
235
  private settleCheckpoint;
236
+ private trackCheckpoint;
235
237
  private contextNote;
236
238
  private submit;
237
239
  private acceptPrompt;
@@ -71,6 +71,8 @@ export class ChatSession {
71
71
  // The last turn the main agent spoke in; a turn opened only for a subagent's request gets no `result`.
72
72
  spokenTurnId = null;
73
73
  titleTimer = null;
74
+ /* Checkpoints still asking git, which `dispose` waits for: the host may remove the folder next. */
75
+ checkpointWork = new Set();
74
76
  // A name is asked for once per chat this host holds; the turn count keeps it once across restarts.
75
77
  naming = false;
76
78
  // Set while the host goes down: a CLI dying with it must not end the turn a restart takes up again.
@@ -1126,7 +1128,7 @@ export class ChatSession {
1126
1128
  this.frozen = true;
1127
1129
  this.backend?.stop();
1128
1130
  }
1129
- /* Ends the CLI and everything it started; settles once it exited or was sent the SIGKILL. */
1131
+ /* Ends the CLI and everything it started; settles once it exited or was sent the SIGKILL and no checkpoint asks git any more. */
1130
1132
  dispose() {
1131
1133
  this.uiReplies.clear();
1132
1134
  this.clearStartWarning();
@@ -1138,7 +1140,7 @@ export class ChatSession {
1138
1140
  const ended = this.backend?.dispose() ?? Promise.resolve();
1139
1141
  this.backend = null;
1140
1142
  this.starting = null;
1141
- return ended;
1143
+ return Promise.all([ended, ...this.checkpointWork]).then(() => undefined);
1142
1144
  }
1143
1145
  /* What a turn changed against its checkpoint: the stored answer, or one taken now while it runs. */
1144
1146
  async turnDiff(turnId) {
@@ -1412,15 +1414,13 @@ export class ChatSession {
1412
1414
  return Promise.resolve();
1413
1415
  }
1414
1416
  const cwd = this.thread.info.cwd;
1415
- return Promise.all([checkpoints.take(cwd), checkpoints.head?.(cwd) ?? null])
1416
- .then(([tree, head]) => {
1417
+ return this.trackCheckpoint(Promise.all([checkpoints.take(cwd), checkpoints.head?.(cwd) ?? null]).then(([tree, head]) => {
1417
1418
  const turn = this.thread.get(turnId);
1418
1419
  if (tree === null || turn?.kind !== 'turn' || turn.state !== 'running' || this.info.activeTurnId !== turnId) {
1419
1420
  return;
1420
1421
  }
1421
1422
  this.emit([this.thread.upsert({ ...turn, checkpoint: tree, ...(head === null ? {} : { checkpointHead: head }) })]);
1422
- })
1423
- .catch(() => undefined);
1423
+ }));
1424
1424
  }
1425
1425
  /* The diff of a turn that just ended, so a reload shows it without asking git again, and the tree a fork after it starts from. */
1426
1426
  settleCheckpoint(turnId) {
@@ -1430,8 +1430,7 @@ export class ChatSession {
1430
1430
  }
1431
1431
  const { checkpoints } = this.options;
1432
1432
  const cwd = this.thread.info.cwd;
1433
- void Promise.all([checkpoints.settle(cwd, turn.checkpoint), checkpoints.head?.(cwd) ?? null])
1434
- .then(([answer, head]) => {
1433
+ void this.trackCheckpoint(Promise.all([checkpoints.settle(cwd, turn.checkpoint), checkpoints.head?.(cwd) ?? null]).then(([answer, head]) => {
1435
1434
  const settled = this.thread.get(turnId);
1436
1435
  if (answer === null || settled?.kind !== 'turn') {
1437
1436
  return;
@@ -1445,8 +1444,13 @@ export class ChatSession {
1445
1444
  })
1446
1445
  ]);
1447
1446
  this.options.persist();
1448
- })
1449
- .catch(() => undefined);
1447
+ }));
1448
+ }
1449
+ /* Keeps the work in `checkpointWork` until it settled; a failure leaves the turn without a checkpoint. */
1450
+ trackCheckpoint(work) {
1451
+ const tracked = work.catch(() => undefined).finally(() => this.checkpointWork.delete(tracked));
1452
+ this.checkpointWork.add(tracked);
1453
+ return tracked;
1450
1454
  }
1451
1455
  /*
1452
1456
  * What the agent has to hear before this prompt: what the chat kept for it and what the host says.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agents",
3
- "version": "0.29.2",
3
+ "version": "0.29.3",
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.29.2",
85
- "@adecore/intelligent-ui": "0.29.2",
84
+ "@adecore/agent-contracts": "0.29.3",
85
+ "@adecore/intelligent-ui": "0.29.3",
86
86
  "zod": "^4.6.5"
87
87
  },
88
88
  "devDependencies": {
@@ -198,6 +198,8 @@ export class ChatSession {
198
198
  // The last turn the main agent spoke in; a turn opened only for a subagent's request gets no `result`.
199
199
  private spokenTurnId: string | null = null;
200
200
  private titleTimer: ReturnType<typeof setTimeout> | null = null;
201
+ /* Checkpoints still asking git, which `dispose` waits for: the host may remove the folder next. */
202
+ private readonly checkpointWork = new Set<Promise<void>>();
201
203
  // A name is asked for once per chat this host holds; the turn count keeps it once across restarts.
202
204
  private naming = false;
203
205
  // Set while the host goes down: a CLI dying with it must not end the turn a restart takes up again.
@@ -1341,7 +1343,7 @@ export class ChatSession {
1341
1343
  this.backend?.stop();
1342
1344
  }
1343
1345
 
1344
- /* Ends the CLI and everything it started; settles once it exited or was sent the SIGKILL. */
1346
+ /* Ends the CLI and everything it started; settles once it exited or was sent the SIGKILL and no checkpoint asks git any more. */
1345
1347
  dispose(): Promise<void> {
1346
1348
  this.uiReplies.clear();
1347
1349
  this.clearStartWarning();
@@ -1353,7 +1355,7 @@ export class ChatSession {
1353
1355
  const ended = this.backend?.dispose() ?? Promise.resolve();
1354
1356
  this.backend = null;
1355
1357
  this.starting = null;
1356
- return ended;
1358
+ return Promise.all([ended, ...this.checkpointWork]).then(() => undefined);
1357
1359
  }
1358
1360
 
1359
1361
  /* What a turn changed against its checkpoint: the stored answer, or one taken now while it runs. */
@@ -1645,15 +1647,15 @@ export class ChatSession {
1645
1647
  return Promise.resolve();
1646
1648
  }
1647
1649
  const cwd = this.thread.info.cwd;
1648
- return Promise.all([checkpoints.take(cwd), checkpoints.head?.(cwd) ?? null])
1649
- .then(([tree, head]) => {
1650
+ return this.trackCheckpoint(
1651
+ Promise.all([checkpoints.take(cwd), checkpoints.head?.(cwd) ?? null]).then(([tree, head]) => {
1650
1652
  const turn = this.thread.get(turnId);
1651
1653
  if (tree === null || turn?.kind !== 'turn' || turn.state !== 'running' || this.info.activeTurnId !== turnId) {
1652
1654
  return;
1653
1655
  }
1654
1656
  this.emit([this.thread.upsert({ ...turn, checkpoint: tree, ...(head === null ? {} : { checkpointHead: head }) })]);
1655
1657
  })
1656
- .catch(() => undefined);
1658
+ );
1657
1659
  }
1658
1660
 
1659
1661
  /* The diff of a turn that just ended, so a reload shows it without asking git again, and the tree a fork after it starts from. */
@@ -1664,8 +1666,8 @@ export class ChatSession {
1664
1666
  }
1665
1667
  const { checkpoints } = this.options;
1666
1668
  const cwd = this.thread.info.cwd;
1667
- void Promise.all([checkpoints.settle(cwd, turn.checkpoint), checkpoints.head?.(cwd) ?? null])
1668
- .then(([answer, head]) => {
1669
+ void this.trackCheckpoint(
1670
+ Promise.all([checkpoints.settle(cwd, turn.checkpoint), checkpoints.head?.(cwd) ?? null]).then(([answer, head]) => {
1669
1671
  const settled = this.thread.get(turnId);
1670
1672
  if (answer === null || settled?.kind !== 'turn') {
1671
1673
  return;
@@ -1680,7 +1682,14 @@ export class ChatSession {
1680
1682
  ]);
1681
1683
  this.options.persist();
1682
1684
  })
1683
- .catch(() => undefined);
1685
+ );
1686
+ }
1687
+
1688
+ /* Keeps the work in `checkpointWork` until it settled; a failure leaves the turn without a checkpoint. */
1689
+ private trackCheckpoint(work: Promise<void>): Promise<void> {
1690
+ const tracked = work.catch(() => undefined).finally(() => this.checkpointWork.delete(tracked));
1691
+ this.checkpointWork.add(tracked);
1692
+ return tracked;
1684
1693
  }
1685
1694
 
1686
1695
  /*