@zgeoff/atc 2.12.0 → 2.14.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 (75) hide show
  1. package/README.md +12 -11
  2. package/package.json +2 -1
  3. package/src/agents/agent-adapter.ts +48 -11
  4. package/src/agents/build-atc-bridge-files.ts +13 -0
  5. package/src/agents/build-claude-query-options.ts +72 -0
  6. package/src/agents/build-cli-command.ts +7 -5
  7. package/src/{daemon → agents}/build-headless-env.ts +12 -6
  8. package/src/agents/build-hook-settings.ts +8 -2
  9. package/src/agents/build-restore-mode-args.ts +23 -0
  10. package/src/agents/claude-adapter.ts +64 -17
  11. package/src/agents/codex-adapter.ts +5 -0
  12. package/src/agents/find-claude-permission-mode.ts +24 -0
  13. package/src/agents/gateway-adapter.ts +38 -11
  14. package/src/agents/grok-adapter.ts +5 -0
  15. package/src/agents/make-claude-headless-runner.ts +46 -0
  16. package/src/agents/plan-pasted-line-input.ts +22 -0
  17. package/src/agents/plan-typed-line-input.ts +8 -0
  18. package/src/agents/resolve-claude-permission-mode.ts +13 -0
  19. package/src/{daemon/start-headless-run.ts → agents/start-claude-headless-run.ts} +6 -53
  20. package/src/agents/write-atc-bridge.ts +2 -6
  21. package/src/cli.ts +8 -12
  22. package/src/client/boot-daemon.ts +47 -21
  23. package/src/client/format-protocol-mismatch.ts +37 -0
  24. package/src/client/index.ts +104 -2
  25. package/src/client/ui.ts +8 -0
  26. package/src/daemon/build-execution-targets.ts +49 -10
  27. package/src/daemon/build-imp-provider.ts +68 -0
  28. package/src/daemon/build-report-trail-entry.ts +6 -2
  29. package/src/daemon/build-report-view.ts +41 -0
  30. package/src/daemon/build-scoped-context.ts +9 -1
  31. package/src/daemon/build-session-lifecycle.ts +52 -0
  32. package/src/daemon/build-tar-archive.ts +85 -0
  33. package/src/daemon/daemon-connection.ts +134 -4
  34. package/src/daemon/daemon.ts +306 -42
  35. package/src/daemon/execution-provider.ts +109 -4
  36. package/src/daemon/hooks.ts +5 -17
  37. package/src/daemon/imp-client-port.ts +343 -0
  38. package/src/daemon/imp-harness.ts +618 -0
  39. package/src/daemon/imp-port-error.ts +18 -0
  40. package/src/daemon/imp-port.ts +246 -0
  41. package/src/daemon/imp-provider.ts +601 -0
  42. package/src/daemon/is-binding-current.ts +43 -0
  43. package/src/daemon/local-pty-provider.ts +62 -3
  44. package/src/daemon/materialize-workspace.ts +574 -0
  45. package/src/daemon/parse-hook-line.ts +31 -0
  46. package/src/daemon/pick-session-state.ts +15 -0
  47. package/src/daemon/restore-fleet.ts +41 -23
  48. package/src/daemon/screen-model.ts +7 -0
  49. package/src/daemon/sessions.ts +574 -61
  50. package/src/daemon/start-headless-turn.ts +1 -1
  51. package/src/daemon/start-session-bridge.ts +294 -0
  52. package/src/mcp/mcp-tools.ts +41 -3
  53. package/src/mcp/reconnecting-caller.ts +1 -0
  54. package/src/mcp/require-daemon-features.ts +4 -0
  55. package/src/mcp/run-tool.ts +32 -8
  56. package/src/protocol/daemon-features.ts +14 -0
  57. package/src/protocol/protocol.ts +23 -0
  58. package/src/protocol/request-param-schemas.ts +72 -0
  59. package/src/report.ts +37 -2
  60. package/src/run-bridge-tap.ts +241 -0
  61. package/src/shared/collect-clean-env.ts +9 -1
  62. package/src/shared/open-bridge-socket.ts +86 -0
  63. package/src/shared/send-bridge-request.ts +47 -0
  64. package/src/statusline.ts +34 -3
  65. package/src/store/fleet-entry.ts +19 -0
  66. package/src/store/run-migrations.ts +85 -0
  67. package/src/store/state-store.ts +226 -21
  68. package/src/store/trail-entry.ts +7 -0
  69. package/src/store/workspace-materialization.ts +70 -0
  70. package/src/tap.ts +11 -0
  71. package/src/workspace/check-url-credentials.ts +59 -0
  72. package/src/workspace/create-workspace-clone.ts +13 -1
  73. package/src/workspace/repository-env-vars.ts +22 -0
  74. package/src/workspace/run-git.ts +2 -18
  75. /package/src/{daemon → agents}/resolve-headless-executable.ts +0 -0
@@ -0,0 +1,246 @@
1
+ /**
2
+ * What the imp provider needs from impd, the imp host daemon, in atc's own
3
+ * terms. The real binding wraps `@zgeoff/imp-client`, and the test fixture
4
+ * runs imps as local pseudo-terminals, so nothing outside the imp modules
5
+ * depends on the client library. Every call that impd refuses rejects with
6
+ * an `ImpPortError` carrying impd's code and data.
7
+ */
8
+ export interface ImpPort {
9
+ // impd's capability flags; an impd without them has neither.
10
+ readonly readFeatures: () => Promise<ImpFeatures>;
11
+
12
+ // The imp under a name, or null when impd holds none.
13
+ readonly readImp: (name: string) => Promise<ImpView | null>;
14
+ readonly createImp: (spec: ImpCreateSpec) => Promise<ImpView>;
15
+
16
+ // Creates or extends the caller's lease under a label, booting or waking
17
+ // the imp first. The label `hold` is refused.
18
+ readonly acquireLease: (name: string, label: string, ttlSeconds: number) => Promise<ImpLease>;
19
+
20
+ // Extends the caller's unexpired lease; `LEASE_NOT_HELD` when it ended.
21
+ readonly renewLease: (name: string, label: string, ttlSeconds: number) => Promise<ImpLease>;
22
+
23
+ // Ends the caller's lease, and reports whether there was one.
24
+ readonly releaseLease: (name: string, label: string) => Promise<boolean>;
25
+
26
+ // Puts the imp to sleep without force: `LEASED` while any lease holds it.
27
+ readonly suspendImp: (name: string) => Promise<void>;
28
+ readonly destroyImp: (name: string) => Promise<void>;
29
+
30
+ // Opens one connection to a session's terminal, as impd's single
31
+ // attacher: a start runs the command under the session name, or attaches
32
+ // when it already runs; an attach only attaches.
33
+ readonly openSession: (
34
+ request: ImpSessionRequest,
35
+ handlers: ImpSessionHandlers,
36
+ ) => ImpSessionConnection;
37
+
38
+ // Runs a command in the imp to its exit, without a terminal.
39
+ // oxlint-disable-next-line prefer-readonly-parameter-types -- the command's input bytes have no readonly form
40
+ readonly runCommand: (name: string, command: ImpCommand) => Promise<ImpCommandResult>;
41
+
42
+ // Listens on a unix socket path inside the imp and hands each connection
43
+ // the guest opens there to the daemon.
44
+ readonly openReverseForward: (
45
+ name: string,
46
+ guestPath: string,
47
+ onConnection: (connection: ImpRelayConnection) => void,
48
+ ) => ImpReverseForward;
49
+ }
50
+
51
+ export interface ImpFeatures {
52
+ readonly sessionOffsets: boolean;
53
+ readonly leases: boolean;
54
+ }
55
+
56
+ export type ImpState = 'creating' | 'running' | 'sleeping' | 'stopped' | 'error';
57
+
58
+ export interface ImpView {
59
+ readonly name: string;
60
+ readonly state: ImpState;
61
+
62
+ // The caller's own leases in full, and a count of everyone else's.
63
+ readonly leases: readonly ImpLease[];
64
+ readonly otherLeaseCount: number;
65
+ }
66
+
67
+ export interface ImpLease {
68
+ readonly name: string;
69
+ readonly owner: { readonly principal: string; readonly display: string; readonly label: string };
70
+
71
+ // Epoch ms; null holds with no end.
72
+ readonly until: number | null;
73
+ }
74
+
75
+ export interface ImpCreateSpec {
76
+ readonly name: string;
77
+ readonly image?: string;
78
+ readonly memoryMib?: number;
79
+ }
80
+
81
+ export type ColdBootCause =
82
+ | 'start'
83
+ | 'wake_fallback'
84
+ | 'watchdog'
85
+ | 'restore'
86
+ | 'recovery'
87
+ | 'unknown';
88
+
89
+ export interface ColdBoot {
90
+ readonly bootId: string;
91
+ readonly cause: ColdBootCause;
92
+ readonly at: string;
93
+ }
94
+
95
+ // The last generation that ran under a session name in this boot, final
96
+ // once written.
97
+ export interface PreviousGeneration {
98
+ readonly executionGeneration: string;
99
+ readonly end: number;
100
+ readonly exitCode: number | null;
101
+ }
102
+
103
+ interface ResumeFrom {
104
+ readonly executionGeneration: string;
105
+ readonly offset: number;
106
+ }
107
+
108
+ export type ResumeResult =
109
+ | { readonly kind: 'exact' }
110
+ | { readonly kind: 'gap'; readonly from: number; readonly to: number }
111
+ | {
112
+ readonly kind: 'generation_changed';
113
+ readonly executionGeneration: string;
114
+ readonly firstOffset: number;
115
+ };
116
+
117
+ /**
118
+ * Where a connection's data sits in the session's output. `none` is an imp
119
+ * whose agent carries no offsets, which replays recent output on every
120
+ * connection.
121
+ */
122
+ type SessionOutput =
123
+ | { readonly continuity: 'none' }
124
+ | {
125
+ readonly continuity: 'offsets';
126
+ readonly bootId: string;
127
+ readonly executionGeneration: string;
128
+ readonly bufferStart: number;
129
+ readonly end: number;
130
+ readonly offset: number;
131
+ readonly prelude: number;
132
+ readonly coldBoots: readonly ColdBoot[];
133
+ readonly previous?: PreviousGeneration | undefined;
134
+ readonly resume?: ResumeResult | undefined;
135
+ };
136
+
137
+ export type ImpSessionRequest =
138
+ | {
139
+ readonly kind: 'start';
140
+ readonly name: string;
141
+ readonly session: string;
142
+ readonly argv: readonly string[];
143
+ readonly env: Readonly<Record<string, string>>;
144
+ readonly cwd: string;
145
+ readonly cols: number;
146
+ readonly rows: number;
147
+ readonly resumeFrom?: ResumeFrom;
148
+ }
149
+ | {
150
+ readonly kind: 'attach';
151
+ readonly name: string;
152
+ readonly session: string;
153
+ readonly cols: number;
154
+ readonly rows: number;
155
+ readonly resumeFrom?: ResumeFrom;
156
+
157
+ // false fails with `INVALID_STATE` instead of booting or waking the imp.
158
+ readonly wake: boolean;
159
+ };
160
+
161
+ export interface ImpSessionHandlers {
162
+ readonly onStarted: (started: ImpSessionStarted) => void;
163
+
164
+ // oxlint-disable-next-line prefer-readonly-parameter-types -- output bytes have no readonly form
165
+ readonly onOutput: (data: Uint8Array) => void;
166
+ }
167
+
168
+ export interface ImpSessionStarted {
169
+ // Whether this connection started the process rather than attaching.
170
+ readonly created: boolean;
171
+ readonly output: SessionOutput;
172
+ }
173
+
174
+ /**
175
+ * How a connection ended: the process exited, impd refused or failed the
176
+ * request, impd detached the connection while the process ran on, the
177
+ * socket closed, with its close code once it had opened, or a handler on
178
+ * the daemon's side threw and the client closed the socket.
179
+ */
180
+ export type ImpSessionOutcome =
181
+ | {
182
+ readonly kind: 'exit';
183
+ readonly code: number | null;
184
+ readonly signal: string | null;
185
+ readonly offset?: number;
186
+ }
187
+ | {
188
+ readonly kind: 'failed';
189
+ readonly code: string | null;
190
+ readonly message: string;
191
+ readonly data?: unknown;
192
+ }
193
+ | {
194
+ readonly kind: 'detached';
195
+ readonly reason: 'taken_over' | 'slow' | 'lost';
196
+ readonly offset?: number;
197
+ }
198
+ | { readonly kind: 'closed'; readonly reason: string; readonly closeCode?: number }
199
+ | { readonly kind: 'unreachable'; readonly detail: string }
200
+ | { readonly kind: 'unauthorized' }
201
+ | { readonly kind: 'bad_message'; readonly detail: string }
202
+ | { readonly kind: 'local_error'; readonly detail: string };
203
+
204
+ export interface ImpSessionConnection {
205
+ readonly outcome: Promise<ImpSessionOutcome>;
206
+
207
+ // oxlint-disable-next-line prefer-readonly-parameter-types -- input bytes have no readonly form
208
+ readonly write: (data: Uint8Array) => void;
209
+ readonly resize: (cols: number, rows: number) => void;
210
+
211
+ // Sends the process a signal by name, such as `SIGHUP`.
212
+ readonly sendSignal: (signal: string) => void;
213
+
214
+ // Closes the connection; the process runs on under its session name.
215
+ readonly close: () => void;
216
+ }
217
+
218
+ export interface ImpCommand {
219
+ readonly argv: readonly string[];
220
+ readonly cwd?: string;
221
+ readonly stdin?: Uint8Array;
222
+ }
223
+
224
+ export interface ImpCommandResult {
225
+ readonly code: number | null;
226
+ readonly stdout: Uint8Array;
227
+ readonly stderr: Uint8Array;
228
+ }
229
+
230
+ export interface ImpRelayConnection {
231
+ // oxlint-disable-next-line prefer-readonly-parameter-types -- relayed bytes have no readonly form
232
+ readonly onData: (listener: (data: Uint8Array) => void) => void;
233
+ readonly onClose: (listener: () => void) => void;
234
+
235
+ // Sends bytes to the guest's end of the connection, and settles once the
236
+ // relay has room for more.
237
+ // oxlint-disable-next-line prefer-readonly-parameter-types -- relayed bytes have no readonly form
238
+ readonly write: (data: Uint8Array) => Promise<void>;
239
+ readonly close: () => void;
240
+ }
241
+
242
+ export interface ImpReverseForward {
243
+ // Settles once impd listens on the guest path.
244
+ readonly listening: Promise<void>;
245
+ readonly stop: () => void;
246
+ }