@zgeoff/atc 0.1.11 → 1.0.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 (74) hide show
  1. package/README.md +54 -15
  2. package/package.json +7 -2
  3. package/src/{agent-adapter.ts → agents/agent-adapter.ts} +31 -14
  4. package/src/{build-cli-command.ts → agents/build-cli-command.ts} +1 -1
  5. package/src/agents/build-hook-settings.ts +59 -0
  6. package/src/{claude-adapter.ts → agents/claude-adapter.ts} +41 -80
  7. package/src/{codex-adapter.ts → agents/codex-adapter.ts} +41 -30
  8. package/src/agents/gateway-adapter.ts +106 -0
  9. package/src/{grok-adapter.ts → agents/grok-adapter.ts} +83 -46
  10. package/src/agents/resolve-agent-home.ts +13 -0
  11. package/src/agents/truncate-detail.ts +7 -0
  12. package/src/agents/write-hook-settings.ts +37 -0
  13. package/src/cli.ts +25 -14
  14. package/src/{boot-daemon.ts → client/boot-daemon.ts} +8 -7
  15. package/src/client/build-client-machine.ts +119 -0
  16. package/src/client/collect-agent-picks.ts +31 -0
  17. package/src/{daemon-client.ts → client/daemon-client.ts} +4 -4
  18. package/src/{dirs.ts → client/dirs.ts} +8 -0
  19. package/src/client/format-overlay-agent-mark.ts +15 -0
  20. package/src/{index.ts → client/index.ts} +181 -550
  21. package/src/client/keys.ts +111 -0
  22. package/src/client/parse-daemon-event.ts +114 -0
  23. package/src/{pick-tab-target.ts → client/pick-tab-target.ts} +2 -2
  24. package/src/client/spawn-picker.ts +305 -0
  25. package/src/client/to-mirror-session.ts +65 -0
  26. package/src/{ui.ts → client/ui.ts} +13 -8
  27. package/src/{attach-registry.ts → daemon/attach-registry.ts} +12 -10
  28. package/src/daemon/build-session-event.ts +43 -0
  29. package/src/{daemon-connection.ts → daemon/daemon-connection.ts} +194 -108
  30. package/src/{daemon.ts → daemon/daemon.ts} +142 -301
  31. package/src/{hooks.ts → daemon/hooks.ts} +6 -4
  32. package/src/daemon/mint-session-id.ts +12 -0
  33. package/src/{permission-registry.ts → daemon/permission-registry.ts} +5 -3
  34. package/src/daemon/restore-fleet.ts +141 -0
  35. package/src/daemon/run-eject-handoff.ts +33 -0
  36. package/src/{screen-model.ts → daemon/screen-model.ts} +1 -1
  37. package/src/daemon/session-runtime.ts +64 -0
  38. package/src/{sessions.ts → daemon/sessions.ts} +98 -144
  39. package/src/{start-headless-run.ts → daemon/start-headless-run.ts} +9 -3
  40. package/src/daemon/start-headless-turn.ts +57 -0
  41. package/src/hook-report.ts +2 -2
  42. package/src/mcp-server.ts +33 -44
  43. package/src/protocol/parse-request-params.ts +31 -0
  44. package/src/{protocol.ts → protocol/protocol.ts} +1 -1
  45. package/src/protocol/request-param-schemas.ts +109 -0
  46. package/src/shared/agent-session-id.ts +8 -0
  47. package/src/shared/build-optional-boolean.ts +10 -0
  48. package/src/shared/build-optional-string-array.ts +14 -0
  49. package/src/shared/build-optional-string.ts +10 -0
  50. package/src/shared/collect-gateways.ts +113 -0
  51. package/src/{config.ts → shared/config.ts} +47 -31
  52. package/src/{get-build.ts → shared/get-build.ts} +21 -6
  53. package/src/shared/get-record.ts +20 -0
  54. package/src/shared/session-id.ts +8 -0
  55. package/src/shared/to-agent-session-id.ts +12 -0
  56. package/src/shared/to-session-id.ts +11 -0
  57. package/src/shared/to-shell-arg.ts +7 -0
  58. package/src/statusline.ts +2 -2
  59. package/src/store/bun-sqlite-driver.ts +96 -0
  60. package/src/store/fleet-entry.ts +72 -0
  61. package/src/store/run-migrations.ts +242 -0
  62. package/src/store/state-store.ts +226 -0
  63. package/src/format-overlay-agent-mark.ts +0 -17
  64. package/src/state-store.ts +0 -227
  65. /package/src/{normalize-hook-event.ts → agents/normalize-hook-event.ts} +0 -0
  66. /package/src/{print-codex-hook-file.ts → agents/print-codex-hook-file.ts} +0 -0
  67. /package/src/{print-grok-hook-file.ts → agents/print-grok-hook-file.ts} +0 -0
  68. /package/src/{build-leader-chords.ts → client/build-leader-chords.ts} +0 -0
  69. /package/src/{daemon-error.ts → protocol/daemon-error.ts} +0 -0
  70. /package/src/{outbound-queue.ts → protocol/outbound-queue.ts} +0 -0
  71. /package/src/{collect-clean-env.ts → shared/collect-clean-env.ts} +0 -0
  72. /package/src/{report.ts → shared/report.ts} +0 -0
  73. /package/src/{reset-input-modes.ts → shared/reset-input-modes.ts} +0 -0
  74. /package/src/{resolve-repo-root.ts → shared/resolve-repo-root.ts} +0 -0
@@ -1,227 +0,0 @@
1
- import { Database } from 'bun:sqlite';
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { toAgentKind } from './agent-adapter';
4
- import type { AgentKind } from './agent-adapter';
5
- import type { HookEvent } from './hooks';
6
- import { parseFleetEntry } from './sessions';
7
- import type { FleetEntry } from './sessions';
8
-
9
- /**
10
- * Daemon state in one SQLite store: the restorable fleet, the hook-event
11
- * debug trail, and the spawn-directory history. The statusline contract
12
- * file (status.json) stays a plain file because reporters inside wrangled
13
- * sessions read it without speaking to the daemon. An existing fleet.json
14
- * seeds the fleet table once, so upgrading never loses a restorable fleet.
15
- */
16
- export class StateStore {
17
- private readonly db: Database;
18
-
19
- constructor(dbPath: string, legacyFleetPath?: string) {
20
- this.db = new Database(dbPath, { create: true });
21
-
22
- this.db.run('PRAGMA journal_mode = WAL;');
23
-
24
- this.db.run(`
25
- CREATE TABLE IF NOT EXISTS fleet (
26
- agent_session_id TEXT PRIMARY KEY,
27
- name TEXT NOT NULL,
28
- cwd TEXT NOT NULL,
29
- pinned INTEGER NOT NULL DEFAULT 0,
30
- last_attached INTEGER,
31
- agent TEXT NOT NULL DEFAULT 'claude'
32
- );
33
- CREATE TABLE IF NOT EXISTS events (
34
- id INTEGER PRIMARY KEY AUTOINCREMENT,
35
- ts TEXT NOT NULL,
36
- atc_id TEXT NOT NULL,
37
- event TEXT NOT NULL,
38
- message TEXT,
39
- session_id TEXT
40
- );
41
- CREATE TABLE IF NOT EXISTS spawn_history (
42
- cwd TEXT PRIMARY KEY,
43
- last_spawn INTEGER NOT NULL
44
- );
45
- CREATE TABLE IF NOT EXISTS prefs (
46
- key TEXT PRIMARY KEY,
47
- value TEXT NOT NULL
48
- );
49
- `);
50
-
51
- // Stores created before pinning, attach recency, or agent kind existed
52
- // lack the columns; the create above only applies to fresh databases.
53
- const fleetColumns = new Set(
54
- this.db
55
- .query<{ name: string }, []>('PRAGMA table_info(fleet)')
56
- .all()
57
- .map((c) => c.name),
58
- );
59
-
60
- // Stores created before the id column was agent-neutral carry it under
61
- // its Claude-era name.
62
- if (fleetColumns.has('claude_id')) {
63
- this.db.run('ALTER TABLE fleet RENAME COLUMN claude_id TO agent_session_id');
64
- }
65
-
66
- if (!fleetColumns.has('pinned')) {
67
- this.db.run('ALTER TABLE fleet ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0');
68
- }
69
-
70
- if (!fleetColumns.has('last_attached')) {
71
- this.db.run('ALTER TABLE fleet ADD COLUMN last_attached INTEGER');
72
- }
73
-
74
- if (!fleetColumns.has('agent')) {
75
- this.db.run("ALTER TABLE fleet ADD COLUMN agent TEXT NOT NULL DEFAULT 'claude'");
76
- }
77
-
78
- if (legacyFleetPath !== undefined) {
79
- this.adoptLegacyFleet(legacyFleetPath);
80
- }
81
- }
82
-
83
- loadFleet(): FleetEntry[] {
84
- const rows = this.db
85
- .query<
86
- {
87
- agent_session_id: string;
88
- name: string;
89
- cwd: string;
90
- pinned: number;
91
- last_attached: number | null;
92
- agent: string | null;
93
- },
94
- []
95
- >('SELECT agent_session_id, name, cwd, pinned, last_attached, agent FROM fleet')
96
- .all();
97
-
98
- const entries: FleetEntry[] = [];
99
-
100
- for (const row of rows) {
101
- entries.push({
102
- agentSessionID: row.agent_session_id,
103
- name: row.name,
104
- cwd: row.cwd,
105
- agent: toAgentKind(row.agent),
106
- ...(row.pinned === 0 ? {} : { pinned: true }),
107
- ...(row.last_attached === null ? {} : { lastAttachedAt: row.last_attached }),
108
- });
109
- }
110
-
111
- return entries;
112
- }
113
-
114
- // The latest hook-event timestamp per agent session id, from the event
115
- // trail. Sessions that never reported an event are absent.
116
- collectFleetRecency(): Map<string, string> {
117
- const rows = this.db
118
- .query<{ session_id: string; ts: string }, []>(
119
- 'SELECT session_id, MAX(ts) AS ts FROM events WHERE session_id IS NOT NULL GROUP BY session_id',
120
- )
121
- .all();
122
-
123
- return new Map(rows.map((row) => [row.session_id, row.ts]));
124
- }
125
-
126
- writeFleet(entries: readonly FleetEntry[]): void {
127
- const replace = this.db.transaction((all: readonly FleetEntry[]) => {
128
- this.db.run('DELETE FROM fleet');
129
-
130
- const insert = this.db.query(
131
- 'INSERT OR REPLACE INTO fleet (agent_session_id, name, cwd, pinned, last_attached, agent) VALUES (?1, ?2, ?3, ?4, ?5, ?6)',
132
- );
133
-
134
- for (const entry of all) {
135
- const pinned = entry.pinned === true ? 1 : 0;
136
-
137
- insert.run(
138
- entry.agentSessionID,
139
- entry.name,
140
- entry.cwd,
141
- pinned,
142
- entry.lastAttachedAt ?? null,
143
- entry.agent,
144
- );
145
- }
146
- });
147
-
148
- replace(entries);
149
- }
150
-
151
- recordEvent(e: HookEvent): void {
152
- const rawMessage = e.payload['message'];
153
- const rawSessionID = e.payload['session_id'] ?? e.payload['sessionId'];
154
- const message = typeof rawMessage === 'string' ? rawMessage : null;
155
- const sessionID = typeof rawSessionID === 'string' ? rawSessionID : null;
156
-
157
- this.db
158
- .query(
159
- 'INSERT INTO events (ts, atc_id, event, message, session_id) VALUES (?1, ?2, ?3, ?4, ?5)',
160
- )
161
- .run(new Date().toISOString(), e.atcId, e.event, message, sessionID);
162
- }
163
-
164
- recordSpawnDir(cwd: string): void {
165
- this.db
166
- .query(
167
- 'INSERT INTO spawn_history (cwd, last_spawn) VALUES (?1, ?2) ON CONFLICT(cwd) DO UPDATE SET last_spawn = ?2',
168
- )
169
- .run(cwd, Date.now());
170
- }
171
-
172
- collectSpawnDirs(): string[] {
173
- const rows = this.db
174
- .query<{ cwd: string }, []>('SELECT cwd FROM spawn_history ORDER BY last_spawn DESC')
175
- .all();
176
-
177
- return rows.map((row) => row.cwd);
178
- }
179
-
180
- loadLastUsedAgent(): AgentKind {
181
- const row = this.db
182
- .query<{ value: string }, []>("SELECT value FROM prefs WHERE key = 'last_used_agent'")
183
- .get();
184
-
185
- return toAgentKind(row?.value);
186
- }
187
-
188
- writeLastUsedAgent(agent: AgentKind): void {
189
- this.db
190
- .query(
191
- 'INSERT INTO prefs (key, value) VALUES (?1, ?2) ON CONFLICT(key) DO UPDATE SET value = ?2',
192
- )
193
- .run('last_used_agent', agent);
194
- }
195
-
196
- stop(): void {
197
- this.db.close();
198
- }
199
-
200
- private adoptLegacyFleet(legacyFleetPath: string): void {
201
- const count = this.db.query<{ n: number }, []>('SELECT COUNT(*) AS n FROM fleet').get();
202
-
203
- if (count === null || count.n > 0 || !existsSync(legacyFleetPath)) {
204
- return;
205
- }
206
-
207
- try {
208
- const parsed: unknown = JSON.parse(readFileSync(legacyFleetPath, 'utf8'));
209
-
210
- if (!Array.isArray(parsed)) {
211
- return;
212
- }
213
-
214
- const entries: FleetEntry[] = [];
215
-
216
- for (const entry of parsed) {
217
- const parsedEntry = parseFleetEntry(entry);
218
-
219
- if (parsedEntry !== undefined) {
220
- entries.push(parsedEntry);
221
- }
222
- }
223
-
224
- this.writeFleet(entries);
225
- } catch {}
226
- }
227
- }
File without changes