@zgeoff/atc 2.10.4 → 2.12.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.
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/agents/agent-adapter.ts +72 -0
- package/src/agents/atc-bridge-files.ts +1 -1
- package/src/agents/build-args-without-flags.ts +25 -0
- package/src/agents/build-claude-override-args.ts +26 -0
- package/src/agents/claude-adapter.ts +52 -1
- package/src/agents/claude-effort-levels.ts +5 -0
- package/src/agents/codex-adapter.ts +46 -1
- package/src/agents/find-flag-value.ts +27 -0
- package/src/agents/gateway-adapter.ts +75 -1
- package/src/agents/grok-adapter.ts +22 -0
- package/src/cli.ts +22 -0
- package/src/client/boot-daemon.ts +6 -0
- package/src/client/daemon-client.ts +14 -2
- package/src/client/spawn-picker.ts +27 -4
- package/src/daemon/build-agent-list.ts +94 -0
- package/src/daemon/build-config-revision.ts +27 -0
- package/src/daemon/build-execution-targets.ts +34 -0
- package/src/daemon/build-fleet-events.ts +2 -1
- package/src/daemon/build-payload-hash.ts +31 -0
- package/src/daemon/build-scoped-context.ts +232 -0
- package/src/daemon/build-target-access.ts +33 -0
- package/src/daemon/build-target-forbidden-error.ts +13 -0
- package/src/daemon/build-target-identity.ts +22 -0
- package/src/daemon/build-target-list.ts +50 -0
- package/src/daemon/daemon-connection.ts +371 -99
- package/src/daemon/daemon.ts +537 -90
- package/src/daemon/effect-remains-error.ts +13 -0
- package/src/daemon/execution-provider.ts +103 -0
- package/src/daemon/find-execution-refusal.ts +104 -0
- package/src/daemon/idempotency-ledger.ts +164 -0
- package/src/daemon/local-pty-provider.ts +83 -0
- package/src/daemon/mint-session-id.ts +5 -5
- package/src/daemon/parse-report.ts +29 -3
- package/src/daemon/parse-spawn-overrides.ts +94 -0
- package/src/daemon/permission-registry.ts +14 -4
- package/src/daemon/restore-fleet.ts +66 -19
- package/src/daemon/session-runtime.ts +10 -0
- package/src/daemon/sessions.ts +287 -65
- package/src/daemon/start-headless-run.ts +11 -0
- package/src/daemon/start-headless-turn.ts +11 -2
- package/src/daemon/target-access.ts +36 -0
- package/src/mcp/answer-mcp-request.ts +12 -4
- package/src/mcp/answer-rpc-request.ts +40 -6
- package/src/mcp/build-principal-caller.ts +15 -0
- package/src/mcp/build-spawn-descriptions.ts +45 -0
- package/src/mcp/build-tool-list.ts +88 -7
- package/src/mcp/mcp-tools.ts +245 -24
- package/src/mcp/parse-idempotency-key.ts +29 -0
- package/src/mcp/reconnecting-caller.ts +77 -15
- package/src/mcp/require-daemon-features.ts +40 -0
- package/src/mcp/run-tool.ts +139 -35
- package/src/mcp/start-mcp-http-server.ts +130 -24
- package/src/mcp/types.ts +9 -1
- package/src/mcp-http-server.ts +6 -1
- package/src/mcp-server.ts +13 -1
- package/src/protocol/daemon-error.ts +5 -1
- package/src/protocol/daemon-features.ts +47 -0
- package/src/protocol/parse-daemon-features.ts +19 -0
- package/src/protocol/protocol.ts +44 -11
- package/src/protocol/request-param-schemas.ts +78 -12
- package/src/report.ts +20 -3
- package/src/shared/agent-session-id.ts +1 -1
- package/src/shared/collect-principals.ts +51 -0
- package/src/shared/collect-targets.ts +144 -0
- package/src/shared/config.ts +139 -14
- package/src/shared/daemon-id.ts +8 -0
- package/src/shared/format-json-kind.ts +21 -0
- package/src/shared/sort-json-keys.ts +19 -0
- package/src/shared/to-daemon-id.ts +11 -0
- package/src/store/fleet-entry.ts +42 -9
- package/src/store/idempotency-record.ts +48 -0
- package/src/store/message-owner.ts +1 -1
- package/src/store/message-record.ts +3 -0
- package/src/store/run-migrations.ts +272 -7
- package/src/store/state-store.ts +503 -34
- package/src/workspace/check-workspace-completeness.ts +90 -0
- package/src/workspace/create-workspace-clone.ts +239 -0
- package/src/workspace/normalize-git-url.ts +59 -0
- package/src/workspace/read-workspace-tar.ts +38 -0
- package/src/workspace/resolve-path-source.ts +170 -0
- package/src/workspace/run-git.ts +103 -0
- package/src/workspace/sanitize-workspace-clone.ts +146 -0
- package/src/workspace/workspace-provenance.ts +11 -0
- package/src/workspace/workspace-source.ts +8 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { DaemonError } from '../protocol/daemon-error';
|
|
2
2
|
import type { SessionID } from '../shared/session-id';
|
|
3
|
+
import type { FleetEntry } from '../store/fleet-entry';
|
|
3
4
|
import type { StateStore } from '../store/state-store';
|
|
4
5
|
import { ScreenModel } from './screen-model';
|
|
5
6
|
import type { SessionRuntime } from './session-runtime';
|
|
@@ -33,15 +34,17 @@ export async function restoreFleet(params: RestoreFleetParams): Promise<number>
|
|
|
33
34
|
const rows = params.rows;
|
|
34
35
|
const capMs = params.capMs;
|
|
35
36
|
|
|
36
|
-
const hasLiveSession = (
|
|
37
|
+
const hasLiveSession = (entry: FleetEntry) =>
|
|
37
38
|
mgr.sessions.some(
|
|
38
39
|
(s) =>
|
|
39
|
-
s
|
|
40
|
+
isSameSession(s, entry) &&
|
|
40
41
|
(s.pty !== null || (s.kind === 'headless' && s.state !== 'exited')),
|
|
41
42
|
);
|
|
42
43
|
|
|
43
|
-
const hasAnySession = (
|
|
44
|
-
|
|
44
|
+
const hasAnySession = (entry: FleetEntry) => mgr.sessions.some((s) => isSameSession(s, entry));
|
|
45
|
+
|
|
46
|
+
const findRecency = (entry: FleetEntry) =>
|
|
47
|
+
entry.agentSessionID === undefined ? '' : (recency.get(entry.agentSessionID) ?? '');
|
|
45
48
|
|
|
46
49
|
const recency = await store.collectFleetRecency();
|
|
47
50
|
const stored = await store.loadFleet();
|
|
@@ -52,14 +55,8 @@ export async function restoreFleet(params: RestoreFleetParams): Promise<number>
|
|
|
52
55
|
// dedupe against every listed session, so repeated restores never double
|
|
53
56
|
// up the killed archive.
|
|
54
57
|
const kept = stored
|
|
55
|
-
.filter((entry) =>
|
|
56
|
-
|
|
57
|
-
? !hasAnySession(entry.agentSessionID)
|
|
58
|
-
: !hasLiveSession(entry.agentSessionID),
|
|
59
|
-
)
|
|
60
|
-
.toSorted((a, b) =>
|
|
61
|
-
(recency.get(b.agentSessionID) ?? '').localeCompare(recency.get(a.agentSessionID) ?? ''),
|
|
62
|
-
);
|
|
58
|
+
.filter((entry) => (entry.exited === true ? !hasAnySession(entry) : !hasLiveSession(entry)))
|
|
59
|
+
.toSorted((a, b) => findRecency(b).localeCompare(findRecency(a)));
|
|
63
60
|
|
|
64
61
|
// Sub-sessions register after every top-level entry, so each one links
|
|
65
62
|
// to a parent that is already listed; the wrangling session also boots
|
|
@@ -72,19 +69,32 @@ export async function restoreFleet(params: RestoreFleetParams): Promise<number>
|
|
|
72
69
|
// The whole fleet registers as terminal-less sessions up front, so the
|
|
73
70
|
// list shows every incoming session immediately instead of revealing them
|
|
74
71
|
// one boot at a time. Exited entries only register — they stay killed
|
|
75
|
-
// until revived by hand, so no terminal is adopted for them.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
// until revived by hand, so no terminal is adopted for them. An entry
|
|
73
|
+
// whose session is still listed, dead, revives that session in place
|
|
74
|
+
// rather than listing a second session under the same id.
|
|
75
|
+
const registered = entries.map((entry) => {
|
|
76
|
+
const listed = mgr.sessions.find((s) => s.id === entry.sessionID);
|
|
77
|
+
|
|
78
|
+
return listed === undefined
|
|
79
|
+
? { session: mgr.restore(entry), revive: false }
|
|
80
|
+
: { session: listed, revive: true };
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const queued = registered
|
|
84
|
+
.filter((r) => r.revive || r.session.state !== 'exited')
|
|
85
|
+
.map((r) => r.session);
|
|
86
|
+
|
|
87
|
+
// A session whose target refuses to start a terminal stays listed without
|
|
88
|
+
// one, and the restore moves on to the next.
|
|
79
89
|
const adoptQueued = (s: Session): boolean => {
|
|
80
|
-
if (mgr
|
|
90
|
+
if (tryAdoptTerminal(mgr, s.id, cols, rows) === null) {
|
|
81
91
|
return false;
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
const runtime = findRuntime(s.id);
|
|
85
95
|
|
|
86
96
|
if (runtime !== undefined) {
|
|
87
|
-
runtime.
|
|
97
|
+
runtime.resetBoot({ cols, rows });
|
|
88
98
|
|
|
89
99
|
runtime.screen ??= new ScreenModel(cols, rows);
|
|
90
100
|
}
|
|
@@ -147,3 +157,40 @@ export async function restoreFleet(params: RestoreFleetParams): Promise<number>
|
|
|
147
157
|
|
|
148
158
|
return registered.length;
|
|
149
159
|
}
|
|
160
|
+
|
|
161
|
+
// A stored entry matches a listed session by its atc session id, or by its
|
|
162
|
+
// agent session id when a listed session resumed the same agent session.
|
|
163
|
+
function isSameSession(s: Session, entry: FleetEntry): boolean {
|
|
164
|
+
return (
|
|
165
|
+
s.id === entry.sessionID ||
|
|
166
|
+
(entry.agentSessionID !== undefined && s.agentSessionID === entry.agentSessionID)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// The refusals that leave one session without a terminal: its target is
|
|
171
|
+
// misconfigured, gone from the config, changed, has no provider here, or
|
|
172
|
+
// cannot start a terminal.
|
|
173
|
+
const REFUSED_ADOPT_CODES: ReadonlySet<string> = new Set([
|
|
174
|
+
'unsupported_operation',
|
|
175
|
+
'unknown_target',
|
|
176
|
+
'target_unavailable',
|
|
177
|
+
'target_changed',
|
|
178
|
+
'target_config_invalid',
|
|
179
|
+
]);
|
|
180
|
+
|
|
181
|
+
function tryAdoptTerminal(
|
|
182
|
+
mgr: SessionManager,
|
|
183
|
+
id: SessionID,
|
|
184
|
+
cols: number,
|
|
185
|
+
rows: number,
|
|
186
|
+
): Session | null {
|
|
187
|
+
try {
|
|
188
|
+
return mgr.adoptTerminal(id, cols, rows);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (error instanceof DaemonError && REFUSED_ADOPT_CODES.has(error.code)) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -46,6 +46,16 @@ export class SessionRuntime {
|
|
|
46
46
|
// later dropped may be restarting, so it keeps the inbox open.
|
|
47
47
|
tapAttached = false;
|
|
48
48
|
|
|
49
|
+
// Returns the boot-scoped state to how a fresh terminal starts, at the
|
|
50
|
+
// dims it boots with: no SessionStart yet and no tap since. Every path
|
|
51
|
+
// that boots a new terminal for an existing session runs it, so a revived
|
|
52
|
+
// session never inherits its previous process's message gate.
|
|
53
|
+
resetBoot(dims: Dims): void {
|
|
54
|
+
this.dims = dims;
|
|
55
|
+
this.startedAt = null;
|
|
56
|
+
this.tapAttached = false;
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
dispose(): void {
|
|
50
60
|
clearTimeout(this.resizeTimer);
|
|
51
61
|
clearTimeout(this.detectTimer);
|