@zgeoff/atc 2.26.0 → 2.26.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zgeoff/atc",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "Terminal control tower for coding-agent sessions",
5
5
  "homepage": "https://github.com/zgeoff/atc#readme",
6
6
  "bugs": "https://github.com/zgeoff/atc/issues",
@@ -10,6 +10,7 @@ import type {
10
10
  ImpExecRequirement,
11
11
  ImpPort,
12
12
  ImpSessionConnection,
13
+ ImpSessionHandlers,
13
14
  ImpSessionOutcome,
14
15
  ImpSessionRequest,
15
16
  ImpSessionStarted,
@@ -22,7 +23,8 @@ interface ImpHarnessHost {
22
23
  // connection the sleep ends is the sleep's and not the harness's end.
23
24
  readonly isSuspending: () => boolean;
24
25
 
25
- // Called once, when the harness ends or the daemon lets go of it.
26
+ // Called once, when the harness ends or the daemon lets go of it; for an
27
+ // end, after every exit listener has run.
26
28
  readonly onDone: () => void;
27
29
 
28
30
  // Whether impd carries output offsets, so a reconnect can resume.
@@ -273,6 +275,7 @@ export class ImpHarness implements HarnessHandle {
273
275
 
274
276
  this.stopFollowing();
275
277
  connection?.close();
278
+ this.host.onDone();
276
279
  };
277
280
 
278
281
  // oxlint-disable-next-line prefer-readonly-parameter-types -- a promise is a live handle
@@ -423,22 +426,29 @@ export class ImpHarness implements HarnessHandle {
423
426
  private openConnection(request: ImpSessionRequest, ticket?: LaunchTicket): void {
424
427
  const gate = ticket === undefined ? undefined : () => this.checkTicket(ticket, request.kind);
425
428
 
426
- const connection = this.port.openSession(
427
- request,
428
- {
429
- onStarted: (started) => {
430
- if (this.connection === connection) {
431
- this.applyStarted(connection, started);
432
- }
433
- },
434
- onOutput: (data) => {
435
- if (this.connection === connection) {
436
- this.applyOutput(data);
437
- }
438
- },
429
+ const handlers: ImpSessionHandlers = {
430
+ onStarted: (started) => {
431
+ if (this.connection === connection) {
432
+ this.applyStarted(connection, started);
433
+ }
439
434
  },
440
- gate,
441
- );
435
+ onOutput: (data) => {
436
+ if (this.connection === connection) {
437
+ this.applyOutput(data);
438
+ }
439
+ },
440
+ };
441
+
442
+ let connection: ImpSessionConnection;
443
+
444
+ // The ticket goes back however the connection ends, a throw from
445
+ // opening it included.
446
+ try {
447
+ connection = this.port.openSession(request, handlers, gate);
448
+ } catch (error) {
449
+ ticket?.release();
450
+ throw error;
451
+ }
442
452
 
443
453
  this.connection = connection;
444
454
  this.connectionTicket = ticket ?? null;
@@ -794,8 +804,14 @@ export class ImpHarness implements HarnessHandle {
794
804
 
795
805
  this.stopFollowing();
796
806
 
797
- for (const listener of listeners) {
798
- listener(exit);
807
+ // The host hears the harness is done only after every exit listener
808
+ // has run, so its owner no longer counts the harness as running then.
809
+ try {
810
+ for (const listener of listeners) {
811
+ listener(exit);
812
+ }
813
+ } finally {
814
+ this.host.onDone();
799
815
  }
800
816
  }
801
817
 
@@ -852,7 +868,6 @@ export class ImpHarness implements HarnessHandle {
852
868
  this.dataListeners.clear();
853
869
  this.exitListeners.clear();
854
870
  this.attachmentListeners.clear();
855
- this.host.onDone();
856
871
  }
857
872
  }
858
873
 
@@ -535,16 +535,17 @@ export class ImpProvider implements ExecutionProvider {
535
535
  }
536
536
 
537
537
  // Runs a readying, a sleep, or a lease return of an imp after the one
538
- // before it there. A turn never starts in its caller's tick, so a harness
539
- // end the daemon still applies counts before the turn checks the host.
538
+ // before it there.
540
539
  private async withHostTurn<T>(name: string, run: () => Promise<T>): Promise<T> {
541
- const before = this.turns.get(name) ?? Promise.resolve();
540
+ const before = this.turns.get(name);
542
541
  const turn = Promise.withResolvers<void>();
543
542
 
544
543
  this.turns.set(name, turn.promise);
545
544
 
546
545
  try {
547
- await before;
546
+ if (before !== undefined) {
547
+ await before;
548
+ }
548
549
 
549
550
  return await run();
550
551
  } finally {