@zgeoff/atc 2.8.1 → 2.8.2

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/README.md CHANGED
@@ -118,8 +118,18 @@ installs in detail.
118
118
  ## Beyond the keyboard
119
119
 
120
120
  - `atc mcp` exposes the fleet as MCP tools, so an agent can spawn, drive, and read other agents. A
121
- session spawned this way lists under the session that spawned it and is killed with it. Register
122
- it with `claude mcp add --scope user atc -- atc mcp`.
121
+ session spawned this way lists under the session that spawned it and is killed with it. Each agent
122
+ keeps its own MCP config, so register the server once per agent you run:
123
+
124
+ ```bash
125
+ claude mcp add --scope user atc -- atc mcp
126
+ codex mcp add atc -- atc mcp
127
+ grok mcp add atc -- atc mcp
128
+ ```
129
+
130
+ A Claude or Codex session that is already running loads the server only after it restarts. A
131
+ running Grok session loads it when you press `r` in its `/mcps` list.
132
+
123
133
  - A Claude session takes messages from other tools. `atc_session_message` queues one, and the
124
134
  session reads it in a new turn, or inside the turn it is running. `atc_message_get` returns the
125
135
  reply once that turn ends. The [protocol](./docs/architecture/protocol.md#messages) covers the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zgeoff/atc",
3
- "version": "2.8.1",
3
+ "version": "2.8.2",
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",
@@ -30,6 +30,10 @@ export class DaemonClient {
30
30
 
31
31
  private socket: { end: () => void } | null = null;
32
32
 
33
+ // Set once the connection ends, so a request sent afterwards rejects at
34
+ // once instead of waiting on a response that can never arrive.
35
+ private closedReason: string | null = null;
36
+
33
37
  static async open(socketPath: string): Promise<DaemonClient> {
34
38
  const client = new DaemonClient();
35
39
 
@@ -65,6 +69,10 @@ export class DaemonClient {
65
69
  m: string,
66
70
  p?: Readonly<Record<string, unknown>>,
67
71
  ): Promise<Readonly<Record<string, unknown>>> {
72
+ if (this.closedReason !== null) {
73
+ return Promise.reject(new DaemonError('internal', this.closedReason));
74
+ }
75
+
68
76
  const id = this.nextID++;
69
77
  const resolvers = Promise.withResolvers<Readonly<Record<string, unknown>>>();
70
78
 
@@ -135,6 +143,8 @@ export class DaemonClient {
135
143
  }
136
144
 
137
145
  private drainPending(reason: string): void {
146
+ this.closedReason ??= reason;
147
+
138
148
  for (const [id, waiter] of this.pending) {
139
149
  this.pending.delete(id);
140
150
  waiter.reject(new DaemonError('internal', reason));