@zgeoff/atc 0.1.12 → 1.0.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/README.md +50 -12
- package/package.json +7 -2
- package/src/{agent-adapter.ts → agents/agent-adapter.ts} +31 -14
- package/src/{build-cli-command.ts → agents/build-cli-command.ts} +1 -1
- package/src/agents/build-hook-settings.ts +59 -0
- package/src/{claude-adapter.ts → agents/claude-adapter.ts} +41 -80
- package/src/{codex-adapter.ts → agents/codex-adapter.ts} +41 -30
- package/src/agents/gateway-adapter.ts +106 -0
- package/src/{grok-adapter.ts → agents/grok-adapter.ts} +83 -46
- package/src/agents/resolve-agent-home.ts +13 -0
- package/src/agents/truncate-detail.ts +7 -0
- package/src/agents/write-hook-settings.ts +37 -0
- package/src/cli.ts +25 -14
- package/src/{boot-daemon.ts → client/boot-daemon.ts} +8 -7
- package/src/client/build-client-machine.ts +119 -0
- package/src/client/collect-agent-picks.ts +31 -0
- package/src/{daemon-client.ts → client/daemon-client.ts} +4 -4
- package/src/{dirs.ts → client/dirs.ts} +8 -0
- package/src/client/format-overlay-agent-mark.ts +15 -0
- package/src/{index.ts → client/index.ts} +181 -550
- package/src/client/keys.ts +111 -0
- package/src/client/parse-daemon-event.ts +114 -0
- package/src/{pick-tab-target.ts → client/pick-tab-target.ts} +2 -2
- package/src/client/plan-vacated-rows.ts +26 -0
- package/src/client/spawn-picker.ts +305 -0
- package/src/client/to-mirror-session.ts +65 -0
- package/src/{ui.ts → client/ui.ts} +27 -8
- package/src/{attach-registry.ts → daemon/attach-registry.ts} +12 -10
- package/src/daemon/build-session-event.ts +43 -0
- package/src/{daemon-connection.ts → daemon/daemon-connection.ts} +194 -108
- package/src/{daemon.ts → daemon/daemon.ts} +148 -313
- package/src/{hooks.ts → daemon/hooks.ts} +6 -4
- package/src/daemon/mint-session-id.ts +12 -0
- package/src/{permission-registry.ts → daemon/permission-registry.ts} +5 -3
- package/src/daemon/restore-fleet.ts +141 -0
- package/src/daemon/run-eject-handoff.ts +33 -0
- package/src/{screen-model.ts → daemon/screen-model.ts} +1 -1
- package/src/daemon/session-runtime.ts +64 -0
- package/src/{sessions.ts → daemon/sessions.ts} +77 -133
- package/src/{start-headless-run.ts → daemon/start-headless-run.ts} +9 -3
- package/src/daemon/start-headless-turn.ts +57 -0
- package/src/hook-report.ts +2 -2
- package/src/mcp-server.ts +33 -44
- package/src/protocol/parse-request-params.ts +31 -0
- package/src/{protocol.ts → protocol/protocol.ts} +1 -1
- package/src/protocol/request-param-schemas.ts +109 -0
- package/src/shared/agent-session-id.ts +8 -0
- package/src/shared/build-optional-boolean.ts +10 -0
- package/src/shared/build-optional-string-array.ts +14 -0
- package/src/shared/build-optional-string.ts +10 -0
- package/src/shared/collect-gateways.ts +113 -0
- package/src/{config.ts → shared/config.ts} +47 -31
- package/src/{get-build.ts → shared/get-build.ts} +21 -6
- package/src/shared/get-record.ts +20 -0
- package/src/shared/session-id.ts +8 -0
- package/src/shared/to-agent-session-id.ts +12 -0
- package/src/shared/to-session-id.ts +11 -0
- package/src/shared/to-shell-arg.ts +7 -0
- package/src/statusline.ts +2 -2
- package/src/store/bun-sqlite-driver.ts +96 -0
- package/src/store/fleet-entry.ts +72 -0
- package/src/store/run-migrations.ts +242 -0
- package/src/store/state-store.ts +226 -0
- package/src/format-overlay-agent-mark.ts +0 -17
- package/src/state-store.ts +0 -236
- /package/src/{normalize-hook-event.ts → agents/normalize-hook-event.ts} +0 -0
- /package/src/{print-codex-hook-file.ts → agents/print-codex-hook-file.ts} +0 -0
- /package/src/{print-grok-hook-file.ts → agents/print-grok-hook-file.ts} +0 -0
- /package/src/{build-leader-chords.ts → client/build-leader-chords.ts} +0 -0
- /package/src/{daemon-error.ts → protocol/daemon-error.ts} +0 -0
- /package/src/{outbound-queue.ts → protocol/outbound-queue.ts} +0 -0
- /package/src/{collect-clean-env.ts → shared/collect-clean-env.ts} +0 -0
- /package/src/{report.ts → shared/report.ts} +0 -0
- /package/src/{reset-input-modes.ts → shared/reset-input-modes.ts} +0 -0
- /package/src/{resolve-repo-root.ts → shared/resolve-repo-root.ts} +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AgentID } from '../agents/agent-adapter';
|
|
2
|
+
import { PINNED_GROUP_KEY } from '../daemon/sessions';
|
|
3
|
+
import type { SessionState } from '../daemon/sessions';
|
|
4
|
+
import { RESET_INPUT_MODES } from '../shared/reset-input-modes';
|
|
2
5
|
import { formatDir } from './dirs';
|
|
3
6
|
import { formatOverlayAgentMark } from './format-overlay-agent-mark';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import type { SessionState } from './sessions';
|
|
7
|
+
import { planVacatedRows } from './plan-vacated-rows';
|
|
8
|
+
import type { BoxExtent } from './plan-vacated-rows';
|
|
7
9
|
|
|
8
10
|
// The slice of a session the drawing layer needs; satisfied by both the
|
|
9
11
|
// daemon's sessions and the wire descriptors a client mirrors.
|
|
@@ -119,12 +121,24 @@ interface Row {
|
|
|
119
121
|
readonly width: number;
|
|
120
122
|
}
|
|
121
123
|
|
|
124
|
+
// The extent of the last box drawn, so the next draw can erase the rows a
|
|
125
|
+
// shrinking or shifting box leaves behind. Erasing an already-blank row is a
|
|
126
|
+
// no-op, so a stale extent after a full clear is harmless.
|
|
127
|
+
let lastBoxExtent: BoxExtent | null = null;
|
|
128
|
+
|
|
122
129
|
function drawBox(rowsList: readonly Row[]) {
|
|
123
130
|
const boxWidth = Math.max(...rowsList.map((r) => r.width));
|
|
124
131
|
const top = Math.max(1, Math.floor((rows() - 1 - rowsList.length) / 2));
|
|
125
132
|
const left = Math.max(1, Math.floor((cols() - boxWidth) / 2));
|
|
133
|
+
const extent: BoxExtent = { top, height: rowsList.length };
|
|
126
134
|
let buf = ansi.hideCursor;
|
|
127
135
|
|
|
136
|
+
for (const row of planVacatedRows(lastBoxExtent, extent)) {
|
|
137
|
+
buf += `${ansi.moveTo(row, 1)}${ESC}[2K`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
lastBoxExtent = extent;
|
|
141
|
+
|
|
128
142
|
for (const [i, r] of rowsList.entries()) {
|
|
129
143
|
buf += ansi.moveTo(top + i, left) + r.styled;
|
|
130
144
|
}
|
|
@@ -163,13 +177,18 @@ export interface OverlaySessionView extends SessionView {
|
|
|
163
177
|
readonly alive: boolean;
|
|
164
178
|
readonly kind: 'pty' | 'headless';
|
|
165
179
|
readonly resumable: boolean;
|
|
166
|
-
readonly
|
|
180
|
+
readonly canEject: boolean;
|
|
181
|
+
readonly agent: AgentID;
|
|
167
182
|
readonly pinned: boolean;
|
|
168
183
|
readonly repoRoot: string;
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
export interface OverlayView {
|
|
172
187
|
sessions: readonly OverlaySessionView[];
|
|
188
|
+
|
|
189
|
+
// The overlay column character per agent id, for the ids that do not use
|
|
190
|
+
// the built-in one.
|
|
191
|
+
agentMarks: Readonly<Record<AgentID, string>>;
|
|
173
192
|
selected: number;
|
|
174
193
|
confirmKill: boolean;
|
|
175
194
|
filter: string | null;
|
|
@@ -209,8 +228,8 @@ export function drawOverlay(view: OverlayView) {
|
|
|
209
228
|
const msg = truncate(s.lastMsg, msgWidth).padEnd(msgWidth);
|
|
210
229
|
const unread = s.unread ? `${ESC}[1;33m!${ESC}[0m` : ' ';
|
|
211
230
|
const pin = s.pinned ? `${ESC}[93m⋆${ESC}[0m` : ' ';
|
|
212
|
-
const mark = formatOverlayAgentMark(s.agent);
|
|
213
|
-
const styledMark = mark === '
|
|
231
|
+
const mark = formatOverlayAgentMark(s.agent, view.agentMarks);
|
|
232
|
+
const styledMark = mark === ' ' ? mark : `${ESC}[90m${mark}${ESC}[0m`;
|
|
214
233
|
const body = `${name} ${state}${dir} ${msg}`;
|
|
215
234
|
const styledBody = sel ? `${ESC}[7m${body}${ESC}[0m` : body;
|
|
216
235
|
|
|
@@ -269,7 +288,7 @@ export function buildOverlayHint(s: OverlaySessionView | undefined): string {
|
|
|
269
288
|
actions.push('a ack');
|
|
270
289
|
}
|
|
271
290
|
|
|
272
|
-
if (s.
|
|
291
|
+
if (s.canEject) {
|
|
273
292
|
actions.push('H headless');
|
|
274
293
|
}
|
|
275
294
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { SessionID } from '../shared/session-id';
|
|
2
|
+
|
|
1
3
|
export interface Dims {
|
|
2
4
|
readonly cols: number;
|
|
3
5
|
readonly rows: number;
|
|
@@ -10,16 +12,16 @@ export interface Dims {
|
|
|
10
12
|
* the whole screen.
|
|
11
13
|
*/
|
|
12
14
|
export class AttachRegistry<TClient> {
|
|
13
|
-
private readonly bySession = new Map<
|
|
15
|
+
private readonly bySession = new Map<SessionID, Map<TClient, Dims>>();
|
|
14
16
|
|
|
15
|
-
attach(sessionID:
|
|
17
|
+
attach(sessionID: SessionID, client: TClient, dims: Dims): void {
|
|
16
18
|
const clients = this.bySession.get(sessionID) ?? new Map<TClient, Dims>();
|
|
17
19
|
|
|
18
20
|
clients.set(client, dims);
|
|
19
21
|
this.bySession.set(sessionID, clients);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
detach(sessionID:
|
|
24
|
+
detach(sessionID: SessionID, client: TClient): void {
|
|
23
25
|
const clients = this.bySession.get(sessionID);
|
|
24
26
|
|
|
25
27
|
clients?.delete(client);
|
|
@@ -29,8 +31,8 @@ export class AttachRegistry<TClient> {
|
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
detachAll(client: TClient):
|
|
33
|
-
const affected:
|
|
34
|
+
detachAll(client: TClient): SessionID[] {
|
|
35
|
+
const affected: SessionID[] = [];
|
|
34
36
|
|
|
35
37
|
for (const [sessionID, clients] of this.bySession) {
|
|
36
38
|
if (clients.delete(client)) {
|
|
@@ -45,15 +47,15 @@ export class AttachRegistry<TClient> {
|
|
|
45
47
|
return affected;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
removeSession(sessionID:
|
|
50
|
+
removeSession(sessionID: SessionID): void {
|
|
49
51
|
this.bySession.delete(sessionID);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
hasClient(sessionID:
|
|
54
|
+
hasClient(sessionID: SessionID, client: TClient): boolean {
|
|
53
55
|
return this.bySession.get(sessionID)?.has(client) ?? false;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
updateDims(sessionID:
|
|
58
|
+
updateDims(sessionID: SessionID, client: TClient, dims: Dims): boolean {
|
|
57
59
|
const clients = this.bySession.get(sessionID);
|
|
58
60
|
|
|
59
61
|
if (clients === undefined || !clients.has(client)) {
|
|
@@ -65,11 +67,11 @@ export class AttachRegistry<TClient> {
|
|
|
65
67
|
return true;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
collectClients(sessionID:
|
|
70
|
+
collectClients(sessionID: SessionID): TClient[] {
|
|
69
71
|
return [...(this.bySession.get(sessionID)?.keys() ?? [])];
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
findEffectiveDims(sessionID:
|
|
74
|
+
findEffectiveDims(sessionID: SessionID): Dims | null {
|
|
73
75
|
const clients = this.bySession.get(sessionID);
|
|
74
76
|
|
|
75
77
|
if (clients === undefined || clients.size === 0) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { PROTOCOL_V } from '../protocol/protocol';
|
|
2
|
+
import type { EventMsg } from '../protocol/protocol';
|
|
3
|
+
import type { SessionID } from '../shared/session-id';
|
|
4
|
+
import type { Session, SessionDescriptor, SessionEventKind, SessionManager } from './sessions';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builds the wire event for a session-manager notification, or null when the
|
|
8
|
+
* notification carries no descriptor to send: `added` and `state` fold in
|
|
9
|
+
* the current descriptor and skip emitting if the session is gone by the
|
|
10
|
+
* time the descriptor is collected.
|
|
11
|
+
*/
|
|
12
|
+
export function buildSessionEvent(
|
|
13
|
+
mgr: SessionManager,
|
|
14
|
+
kind: SessionEventKind,
|
|
15
|
+
s: Session,
|
|
16
|
+
): EventMsg | null {
|
|
17
|
+
const builders: Record<SessionEventKind, () => EventMsg | null> = {
|
|
18
|
+
added: () => {
|
|
19
|
+
const session = findDescriptor(mgr, s.id);
|
|
20
|
+
|
|
21
|
+
return session === null ? null : { v: PROTOCOL_V, ev: 'session.added', session };
|
|
22
|
+
},
|
|
23
|
+
state: () => {
|
|
24
|
+
const session = findDescriptor(mgr, s.id);
|
|
25
|
+
|
|
26
|
+
return session === null ? null : { v: PROTOCOL_V, ev: 'session.state', session };
|
|
27
|
+
},
|
|
28
|
+
renamed: () => ({
|
|
29
|
+
v: PROTOCOL_V,
|
|
30
|
+
ev: 'session.renamed',
|
|
31
|
+
s: s.id,
|
|
32
|
+
name: s.name,
|
|
33
|
+
namedBy: s.namedBy,
|
|
34
|
+
}),
|
|
35
|
+
removed: () => ({ v: PROTOCOL_V, ev: 'session.removed', s: s.id }),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return builders[kind]();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function findDescriptor(mgr: SessionManager, id: SessionID): SessionDescriptor | null {
|
|
42
|
+
return mgr.collectDescriptors().find((x) => x.id === id) ?? null;
|
|
43
|
+
}
|