@zgeoff/atc 3.4.2 → 3.5.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 (78) hide show
  1. package/package.json +1 -1
  2. package/src/agents/agent-adapter.ts +4 -0
  3. package/src/agents/build-claude-query-options.ts +1 -0
  4. package/src/agents/build-headless-env.ts +4 -0
  5. package/src/client/daemon-client.ts +6 -1
  6. package/src/collect-restart-env.ts +6 -3
  7. package/src/daemon/build-scoped-context.ts +2 -0
  8. package/src/daemon/check-session-scope.ts +235 -0
  9. package/src/daemon/daemon-connection.ts +74 -0
  10. package/src/daemon/daemon-context.ts +21 -0
  11. package/src/daemon/daemon.ts +21 -0
  12. package/src/daemon/find-host-branch.ts +28 -0
  13. package/src/daemon/materialize-workspace.ts +6 -1
  14. package/src/daemon/merge-recorded-scope.ts +50 -0
  15. package/src/daemon/published-records.ts +269 -0
  16. package/src/daemon/request-access-classes.ts +1 -0
  17. package/src/daemon/run-host-git.ts +52 -0
  18. package/src/daemon/sessions.ts +185 -53
  19. package/src/daemon/start-headless-turn.ts +3 -0
  20. package/src/federation/id-rules.ts +11 -2
  21. package/src/mcp/mcp-tools.ts +50 -0
  22. package/src/mcp/require-daemon-features.ts +1 -0
  23. package/src/mcp/run-tool.ts +14 -0
  24. package/src/protocol/daemon-features.ts +5 -0
  25. package/src/protocol/parse-declared-scope.ts +158 -0
  26. package/src/protocol/protocol.ts +1 -0
  27. package/src/protocol/published-record.ts +62 -0
  28. package/src/protocol/request-param-schemas.ts +16 -0
  29. package/src/store/parse-published-record.ts +59 -0
  30. package/src/store/run-migrations.ts +23 -0
  31. package/src/store/state-store.ts +32 -0
  32. package/src/test-utils/build-mock-published-record.ts +39 -0
  33. package/src/test-utils/build-stub-brokered-agent-adapter.ts +3 -1
  34. package/src/test-utils/build-stub-daemon-context.ts +2 -0
  35. package/src/test-utils/build-stub-daemon-requests.ts +3 -3
  36. package/src/test-utils/build-stub-harness-relay.ts +2 -2
  37. package/src/test-utils/build-stub-held-inbox-source.ts +2 -2
  38. package/src/test-utils/build-stub-host-hold.ts +7 -6
  39. package/src/test-utils/build-stub-launch-admission.ts +20 -0
  40. package/src/test-utils/build-stub-pregranted-broker-host.ts +22 -0
  41. package/src/test-utils/build-stub-terminal.ts +3 -3
  42. package/src/test-utils/create-git-fixture.ts +2 -2
  43. package/src/test-utils/create-stub-imp-port.ts +37 -37
  44. package/src/test-utils/create-stub-systemd.ts +6 -6
  45. package/src/test-utils/isolate-home.ts +8 -5
  46. package/src/test-utils/read-query-plan.ts +10 -6
  47. package/src/test-utils/register-test-cleanup.ts +10 -10
  48. package/src/test-utils/setup-mcp-home.ts +7 -6
  49. package/src/test-utils/setup-mcp-http.ts +6 -6
  50. package/src/test-utils/setup-temp-dir.ts +4 -10
  51. package/src/test-utils/start-cut-proxy.ts +2 -4
  52. package/src/test-utils/start-daemon-process.ts +13 -13
  53. package/src/test-utils/start-git-http-server.ts +2 -4
  54. package/src/test-utils/start-mcp-stdio.ts +5 -5
  55. package/src/test-utils/start-stub-answering-listener.ts +3 -3
  56. package/src/test-utils/start-stub-closing-listener.ts +3 -3
  57. package/src/test-utils/start-stub-dropping-daemon.ts +2 -3
  58. package/src/test-utils/start-stub-impd-info.ts +3 -7
  59. package/src/test-utils/start-stub-impd.ts +3 -5
  60. package/src/test-utils/start-stub-legacy-daemon.ts +2 -3
  61. package/src/test-utils/start-stub-mcp-server.ts +3 -5
  62. package/src/test-utils/start-stub-recording-listener.ts +3 -3
  63. package/src/test-utils/start-stub-refusing-listener.ts +3 -3
  64. package/src/test-utils/start-stub-reporter-socket.ts +3 -9
  65. package/src/test-utils/start-stub-restore-daemon.ts +2 -7
  66. package/src/test-utils/start-stub-session-bridge.ts +3 -9
  67. package/src/test-utils/start-stub-silent-server.ts +3 -6
  68. package/src/test-utils/start-stub-source-daemon.ts +2 -2
  69. package/src/test-utils/start-stub-stalled-client.ts +3 -3
  70. package/src/test-utils/start-stub-stalled-listener.ts +3 -3
  71. package/src/test-utils/start-stub-tcp-daemon.ts +3 -3
  72. package/src/test-utils/start-stub-telemetry-collector.ts +2 -2
  73. package/src/test-utils/start-stub-unanswering-listener.ts +3 -3
  74. package/src/test-utils/start-stub-unanswering-unix-listener.ts +3 -3
  75. package/src/test-utils/start-test-daemon.ts +62 -18
  76. package/src/test-utils/start-tui-harness.ts +17 -10
  77. package/src/test-utils/subscribe-to-socket-lines.ts +5 -9
  78. package/src/test-utils/{can-bind-addresses.ts → try-bind-addresses.ts} +4 -3
@@ -32,16 +32,17 @@ interface TUIHarnessOptions {
32
32
  * starts a new client on the same home and stops capturing the old one.
33
33
  * `read` returns every byte the client drew since the last `reset`, and
34
34
  * `waitFor` polls that capture for a needle. The client appends what it
35
- * decides without drawing to a log: `markClientLog` returns a cursor into
36
- * it, and `waitForClientLog` polls for a line written after that cursor.
35
+ * decides without drawing to a log: `countClientLogLines` returns how many
36
+ * lines it holds, and `waitForClientLog` polls for a line written after
37
+ * that many.
37
38
  * `writeConfig` writes the home's config: the stand-in binaries and the git
38
39
  * transports the fixture repositories need, with the fields given laid over
39
40
  * them. `env` is the environment the client runs with, so a daemon started
40
41
  * with it serves the client. A wait made before the client draws anything
41
- * gets `bootMs` for that first byte. Disposal stops the client and the
42
- * daemon in the home and removes it. That disposal runs once the current
43
- * test finishes, so it must run inside a test; disposing sooner runs it
44
- * then, and a second disposal does nothing.
42
+ * gets `bootMs` for that first byte. `stop` kills every client a boot
43
+ * started, stops the daemon in the home, and removes the home. That stop runs once the current test
44
+ * finishes, so it must run inside a test; calling `stop` sooner runs it
45
+ * then, and a second stop does nothing.
45
46
  */
46
47
  export function startTUIHarness(options: TUIHarnessOptions = {}) {
47
48
  const tmp = setupTempDir('atc-tui-');
@@ -50,9 +51,9 @@ export function startTUIHarness(options: TUIHarnessOptions = {}) {
50
51
  // daemon stop before their home is removed.
51
52
  const owned = new AsyncDisposableStack();
52
53
 
53
- const dispose = registerTestCleanup(() => owned.disposeAsync());
54
+ const stop = registerTestCleanup(() => owned.disposeAsync());
54
55
 
55
- owned.use(tmp);
56
+ owned.defer(tmp.teardown);
56
57
 
57
58
  // The client boots with this home as its cwd and lists it first in the
58
59
  // picker, so the path is resolved the way the client reports it.
@@ -129,6 +130,12 @@ export function startTUIHarness(options: TUIHarnessOptions = {}) {
129
130
  env,
130
131
  });
131
132
 
133
+ // Every client a boot starts is killed before the daemon stops, so a
134
+ // client a later boot replaced never outlives the test.
135
+ owned.defer(() => {
136
+ booted.kill();
137
+ });
138
+
132
139
  const exit = Promise.withResolvers<number>();
133
140
 
134
141
  booted.onExit((event) => {
@@ -206,7 +213,7 @@ export function startTUIHarness(options: TUIHarnessOptions = {}) {
206
213
  return exited;
207
214
  },
208
215
 
209
- markClientLog(): number {
216
+ countClientLogLines(): number {
210
217
  return readClientLog(clientLogPath).length;
211
218
  },
212
219
 
@@ -225,7 +232,7 @@ export function startTUIHarness(options: TUIHarnessOptions = {}) {
225
232
  );
226
233
  },
227
234
 
228
- [Symbol.asyncDispose]: dispose,
235
+ stop,
229
236
  };
230
237
  }
231
238
 
@@ -11,7 +11,7 @@ interface SocketLines {
11
11
 
12
12
  readonly write: (data: string) => void;
13
13
  readonly waitForLine: (count?: number, timeoutMs?: number) => Promise<string[]>;
14
- readonly [Symbol.asyncDispose]: () => Promise<void>;
14
+ readonly unsubscribe: () => void;
15
15
  }
16
16
 
17
17
  interface SocketLinesOptions {
@@ -35,8 +35,8 @@ interface SocketLinesOptions {
35
35
  * the lines, throwing when `timeoutMs` passes first or the connection
36
36
  * closes short of the count; a stub clock passed as `now` and `wait` steps
37
37
  * that timeout without waiting it out. The connection ends once the current
38
- * test finishes, so it must run inside a test; disposal ends it sooner, and
39
- * a second end does nothing.
38
+ * test finishes, so it must run inside a test; `unsubscribe` ends it
39
+ * sooner, and a second end does nothing.
40
40
  */
41
41
  export async function subscribeToSocketLines(
42
42
  path: string,
@@ -72,7 +72,7 @@ export async function subscribeToSocketLines(
72
72
  },
73
73
  });
74
74
 
75
- const stop = registerTestCleanup(() => {
75
+ const unsubscribe = registerTestCleanup(() => {
76
76
  socket.end();
77
77
  });
78
78
 
@@ -118,10 +118,6 @@ export async function subscribeToSocketLines(
118
118
 
119
119
  return requireLines(count);
120
120
  },
121
- [Symbol.asyncDispose]: () => {
122
- stop();
123
-
124
- return Promise.resolve();
125
- },
121
+ unsubscribe,
126
122
  };
127
123
  }
@@ -1,10 +1,11 @@
1
1
  /**
2
- * Whether this host can bind a socket to every given address, tried by
3
- * listening on a kernel-chosen port of each and closing it at once. A host
2
+ * Tries to bind a socket to every given address on this host, by listening
3
+ * on a kernel-chosen port of each and closing it at once, and returns whether
4
+ * every bind succeeded. A host
4
5
  * without an address, such as stock macOS for any loopback address past
5
6
  * 127.0.0.1, refuses the bind with EADDRNOTAVAIL.
6
7
  */
7
- export function canBindAddresses(hostnames: readonly string[]): boolean {
8
+ export function tryBindAddresses(hostnames: readonly string[]): boolean {
8
9
  for (const hostname of hostnames) {
9
10
  try {
10
11
  const listener = Bun.listen({ hostname, port: 0, socket: { data() {} } });