agy-worker-mcp 0.3.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/CHANGELOG.md +141 -0
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/commands/agy-ceiling.md +31 -0
- package/dist/broker/blockers.d.ts +86 -0
- package/dist/broker/blockers.d.ts.map +1 -0
- package/dist/broker/blockers.js +390 -0
- package/dist/broker/blockers.js.map +1 -0
- package/dist/broker/outcome.d.ts +85 -0
- package/dist/broker/outcome.d.ts.map +1 -0
- package/dist/broker/outcome.js +147 -0
- package/dist/broker/outcome.js.map +1 -0
- package/dist/broker/reconcile.d.ts +74 -0
- package/dist/broker/reconcile.d.ts.map +1 -0
- package/dist/broker/reconcile.js +415 -0
- package/dist/broker/reconcile.js.map +1 -0
- package/dist/broker/result.d.ts +88 -0
- package/dist/broker/result.d.ts.map +1 -0
- package/dist/broker/result.js +234 -0
- package/dist/broker/result.js.map +1 -0
- package/dist/broker/verify.d.ts +52 -0
- package/dist/broker/verify.d.ts.map +1 -0
- package/dist/broker/verify.js +174 -0
- package/dist/broker/verify.js.map +1 -0
- package/dist/contract/errors.d.ts +119 -0
- package/dist/contract/errors.d.ts.map +1 -0
- package/dist/contract/errors.js +99 -0
- package/dist/contract/errors.js.map +1 -0
- package/dist/contract/index.d.ts +10 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +10 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/paths.d.ts +139 -0
- package/dist/contract/paths.d.ts.map +1 -0
- package/dist/contract/paths.js +359 -0
- package/dist/contract/paths.js.map +1 -0
- package/dist/contract/schema.sql +64 -0
- package/dist/contract/types.d.ts +1113 -0
- package/dist/contract/types.d.ts.map +1 -0
- package/dist/contract/types.js +114 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/events/cursor.d.ts +65 -0
- package/dist/events/cursor.d.ts.map +1 -0
- package/dist/events/cursor.js +286 -0
- package/dist/events/cursor.js.map +1 -0
- package/dist/events/detect.d.ts +58 -0
- package/dist/events/detect.d.ts.map +1 -0
- package/dist/events/detect.js +183 -0
- package/dist/events/detect.js.map +1 -0
- package/dist/events/normalize.d.ts +30 -0
- package/dist/events/normalize.d.ts.map +1 -0
- package/dist/events/normalize.js +158 -0
- package/dist/events/normalize.js.map +1 -0
- package/dist/events/parse.d.ts +41 -0
- package/dist/events/parse.d.ts.map +1 -0
- package/dist/events/parse.js +89 -0
- package/dist/events/parse.js.map +1 -0
- package/dist/gate/bind.d.ts +28 -0
- package/dist/gate/bind.d.ts.map +1 -0
- package/dist/gate/bind.js +154 -0
- package/dist/gate/bind.js.map +1 -0
- package/dist/gate/gate.d.ts +139 -0
- package/dist/gate/gate.d.ts.map +1 -0
- package/dist/gate/gate.js +488 -0
- package/dist/gate/gate.js.map +1 -0
- package/dist/gate/hooks-file.d.ts +55 -0
- package/dist/gate/hooks-file.d.ts.map +1 -0
- package/dist/gate/hooks-file.js +104 -0
- package/dist/gate/hooks-file.js.map +1 -0
- package/dist/gate.d.ts +3 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +20 -0
- package/dist/gate.js.map +1 -0
- package/dist/policy/ceiling-review.d.ts +22 -0
- package/dist/policy/ceiling-review.d.ts.map +1 -0
- package/dist/policy/ceiling-review.js +170 -0
- package/dist/policy/ceiling-review.js.map +1 -0
- package/dist/policy/ceiling.d.ts +128 -0
- package/dist/policy/ceiling.d.ts.map +1 -0
- package/dist/policy/ceiling.js +273 -0
- package/dist/policy/ceiling.js.map +1 -0
- package/dist/policy/containment.d.ts +100 -0
- package/dist/policy/containment.d.ts.map +1 -0
- package/dist/policy/containment.js +1071 -0
- package/dist/policy/containment.js.map +1 -0
- package/dist/policy/hard-deny.d.ts +28 -0
- package/dist/policy/hard-deny.d.ts.map +1 -0
- package/dist/policy/hard-deny.js +78 -0
- package/dist/policy/hard-deny.js.map +1 -0
- package/dist/policy/profiles.d.ts +82 -0
- package/dist/policy/profiles.d.ts.map +1 -0
- package/dist/policy/profiles.js +317 -0
- package/dist/policy/profiles.js.map +1 -0
- package/dist/policy/rules.d.ts +138 -0
- package/dist/policy/rules.d.ts.map +1 -0
- package/dist/policy/rules.js +1180 -0
- package/dist/policy/rules.js.map +1 -0
- package/dist/policy/seatbelt.d.ts +32 -0
- package/dist/policy/seatbelt.d.ts.map +1 -0
- package/dist/policy/seatbelt.js +59 -0
- package/dist/policy/seatbelt.js.map +1 -0
- package/dist/policy/tools.d.ts +36 -0
- package/dist/policy/tools.d.ts.map +1 -0
- package/dist/policy/tools.js +94 -0
- package/dist/policy/tools.js.map +1 -0
- package/dist/runner/gate-watchdog.d.ts +93 -0
- package/dist/runner/gate-watchdog.d.ts.map +1 -0
- package/dist/runner/gate-watchdog.js +153 -0
- package/dist/runner/gate-watchdog.js.map +1 -0
- package/dist/runner/idle.d.ts +51 -0
- package/dist/runner/idle.d.ts.map +1 -0
- package/dist/runner/idle.js +81 -0
- package/dist/runner/idle.js.map +1 -0
- package/dist/runner/inbox.d.ts +56 -0
- package/dist/runner/inbox.d.ts.map +1 -0
- package/dist/runner/inbox.js +134 -0
- package/dist/runner/inbox.js.map +1 -0
- package/dist/runner/reap.d.ts +65 -0
- package/dist/runner/reap.d.ts.map +1 -0
- package/dist/runner/reap.js +168 -0
- package/dist/runner/reap.js.map +1 -0
- package/dist/runner/runner.d.ts +51 -0
- package/dist/runner/runner.d.ts.map +1 -0
- package/dist/runner/runner.js +289 -0
- package/dist/runner/runner.js.map +1 -0
- package/dist/runner/spawn.d.ts +98 -0
- package/dist/runner/spawn.d.ts.map +1 -0
- package/dist/runner/spawn.js +230 -0
- package/dist/runner/spawn.js.map +1 -0
- package/dist/runner/verify.d.ts +24 -0
- package/dist/runner/verify.d.ts.map +1 -0
- package/dist/runner/verify.js +108 -0
- package/dist/runner/verify.js.map +1 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +15 -0
- package/dist/runner.js.map +1 -0
- package/dist/server/context.d.ts +41 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +59 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/instructions.d.ts +13 -0
- package/dist/server/instructions.d.ts.map +1 -0
- package/dist/server/instructions.js +121 -0
- package/dist/server/instructions.js.map +1 -0
- package/dist/server/server.d.ts +20 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +55 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/tools/cancel.d.ts +24 -0
- package/dist/server/tools/cancel.d.ts.map +1 -0
- package/dist/server/tools/cancel.js +59 -0
- package/dist/server/tools/cancel.js.map +1 -0
- package/dist/server/tools/capabilities.d.ts +36 -0
- package/dist/server/tools/capabilities.d.ts.map +1 -0
- package/dist/server/tools/capabilities.js +116 -0
- package/dist/server/tools/capabilities.js.map +1 -0
- package/dist/server/tools/ceiling.d.ts +31 -0
- package/dist/server/tools/ceiling.d.ts.map +1 -0
- package/dist/server/tools/ceiling.js +121 -0
- package/dist/server/tools/ceiling.js.map +1 -0
- package/dist/server/tools/index.d.ts +38 -0
- package/dist/server/tools/index.d.ts.map +1 -0
- package/dist/server/tools/index.js +115 -0
- package/dist/server/tools/index.js.map +1 -0
- package/dist/server/tools/listJobs.d.ts +30 -0
- package/dist/server/tools/listJobs.d.ts.map +1 -0
- package/dist/server/tools/listJobs.js +37 -0
- package/dist/server/tools/listJobs.js.map +1 -0
- package/dist/server/tools/logs.d.ts +31 -0
- package/dist/server/tools/logs.d.ts.map +1 -0
- package/dist/server/tools/logs.js +96 -0
- package/dist/server/tools/logs.js.map +1 -0
- package/dist/server/tools/result.d.ts +32 -0
- package/dist/server/tools/result.d.ts.map +1 -0
- package/dist/server/tools/result.js +95 -0
- package/dist/server/tools/result.js.map +1 -0
- package/dist/server/tools/send.d.ts +37 -0
- package/dist/server/tools/send.d.ts.map +1 -0
- package/dist/server/tools/send.js +71 -0
- package/dist/server/tools/send.js.map +1 -0
- package/dist/server/tools/sessions.d.ts +28 -0
- package/dist/server/tools/sessions.d.ts.map +1 -0
- package/dist/server/tools/sessions.js +54 -0
- package/dist/server/tools/sessions.js.map +1 -0
- package/dist/server/tools/start.d.ts +107 -0
- package/dist/server/tools/start.d.ts.map +1 -0
- package/dist/server/tools/start.js +482 -0
- package/dist/server/tools/start.js.map +1 -0
- package/dist/server/tools/wait.d.ts +29 -0
- package/dist/server/tools/wait.d.ts.map +1 -0
- package/dist/server/tools/wait.js +120 -0
- package/dist/server/tools/wait.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +17 -0
- package/dist/server.js.map +1 -0
- package/dist/setup/install.d.ts +52 -0
- package/dist/setup/install.d.ts.map +1 -0
- package/dist/setup/install.js +108 -0
- package/dist/setup/install.js.map +1 -0
- package/dist/setup.d.ts +3 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +24 -0
- package/dist/setup.js.map +1 -0
- package/dist/store/db.d.ts +49 -0
- package/dist/store/db.d.ts.map +1 -0
- package/dist/store/db.js +101 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/jobs.d.ts +45 -0
- package/dist/store/jobs.d.ts.map +1 -0
- package/dist/store/jobs.js +150 -0
- package/dist/store/jobs.js.map +1 -0
- package/dist/store/locks.d.ts +60 -0
- package/dist/store/locks.d.ts.map +1 -0
- package/dist/store/locks.js +172 -0
- package/dist/store/locks.js.map +1 -0
- package/dist/store/sessions.d.ts +37 -0
- package/dist/store/sessions.d.ts.map +1 -0
- package/dist/store/sessions.js +117 -0
- package/dist/store/sessions.js.map +1 -0
- package/dist/trace/digest.d.ts +77 -0
- package/dist/trace/digest.d.ts.map +1 -0
- package/dist/trace/digest.js +481 -0
- package/dist/trace/digest.js.map +1 -0
- package/package.json +68 -0
- package/skills/agy-ceiling/SKILL.md +72 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { ENV } from '../contract/types.js';
|
|
2
|
+
import { canonicalize, ensureJobDirs, jobPaths, projectPaths, readJsonIfExists, resolveProjectRoot, writeFileAtomic, writeJsonAtomic, } from '../contract/paths.js';
|
|
3
|
+
import { startGateWatchdog } from './gate-watchdog.js';
|
|
4
|
+
import { startIdleWatchdog } from './idle.js';
|
|
5
|
+
import { startInboxRelay } from './inbox.js';
|
|
6
|
+
import { killProcessGroup, sleep } from './reap.js';
|
|
7
|
+
import { spawnAgyDetached } from './spawn.js';
|
|
8
|
+
import { runVerifyCommand } from './verify.js';
|
|
9
|
+
function writeState(statePath, state) {
|
|
10
|
+
writeJsonAtomic(statePath, state);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Run one job to completion.
|
|
14
|
+
*
|
|
15
|
+
* Sequence: read `effective-config.json` → spawn detached with file-backed stdio →
|
|
16
|
+
* start the inbox relay when `session_mode === 'session'` → wait for exit, racing
|
|
17
|
+
* `deadline_at` → run `config.verify` once, if set and the deadline did not fire
|
|
18
|
+
* (PR6 §6.2) → **always** write the final `jobs/<id>/state.json`, then
|
|
19
|
+
* `jobs/<id>/exit_code`.
|
|
20
|
+
*
|
|
21
|
+
* `exit_code` existing is the finalization signal every server polls for; if
|
|
22
|
+
* the runner can die without writing it, `reconcile` has to guess, and a job
|
|
23
|
+
* that finished cleanly gets reported as `process_error`. Writing the final
|
|
24
|
+
* `state.json` *first* is what makes that signal trustworthy the instant it
|
|
25
|
+
* appears — see the comment at the write site.
|
|
26
|
+
*/
|
|
27
|
+
export async function runJob(opts) {
|
|
28
|
+
const root = canonicalize(opts.projectRoot);
|
|
29
|
+
const project = projectPaths(root);
|
|
30
|
+
const paths = jobPaths(project, opts.jobId);
|
|
31
|
+
ensureJobDirs(paths);
|
|
32
|
+
const config = readJsonIfExists(paths.effectiveConfig);
|
|
33
|
+
if (!config) {
|
|
34
|
+
throw new Error(`agy-worker-runner: effective-config.json missing for job ${opts.jobId} at ${paths.effectiveConfig}`);
|
|
35
|
+
}
|
|
36
|
+
const wallStart = Date.now();
|
|
37
|
+
writeState(paths.state, {
|
|
38
|
+
job_id: opts.jobId,
|
|
39
|
+
lifecycle: 'starting',
|
|
40
|
+
pid: null,
|
|
41
|
+
pgid: null,
|
|
42
|
+
proc_start_time: null,
|
|
43
|
+
started_at: null,
|
|
44
|
+
finished_at: null,
|
|
45
|
+
updated_at: Date.now(),
|
|
46
|
+
phase: 'agy',
|
|
47
|
+
runner_pid: process.pid,
|
|
48
|
+
});
|
|
49
|
+
const stdinPipe = config.session_mode === 'session';
|
|
50
|
+
const spawned = spawnAgyDetached({ config, paths, stdinPipe });
|
|
51
|
+
writeState(paths.state, {
|
|
52
|
+
job_id: opts.jobId,
|
|
53
|
+
lifecycle: 'running',
|
|
54
|
+
pid: spawned.pid,
|
|
55
|
+
pgid: spawned.pgid,
|
|
56
|
+
proc_start_time: spawned.procStartTime,
|
|
57
|
+
started_at: spawned.startedAt,
|
|
58
|
+
finished_at: null,
|
|
59
|
+
updated_at: Date.now(),
|
|
60
|
+
phase: 'agy',
|
|
61
|
+
runner_pid: process.pid,
|
|
62
|
+
});
|
|
63
|
+
// I4: confirm the PreToolUse gate
|
|
64
|
+
// actually loaded, for every job — not just session-mode ones. Started right
|
|
65
|
+
// after `spawned.pgid` exists so `onMissing` always has something to kill.
|
|
66
|
+
let gateConfirmed = null;
|
|
67
|
+
const gateWatchdog = startGateWatchdog({
|
|
68
|
+
eventsPath: paths.events,
|
|
69
|
+
gateLogPath: paths.gateLog,
|
|
70
|
+
onConfirmed: () => {
|
|
71
|
+
gateConfirmed = true;
|
|
72
|
+
},
|
|
73
|
+
onMissing: () => {
|
|
74
|
+
gateConfirmed = false;
|
|
75
|
+
// Fire-and-forget: the main await below (the deadline race, or the bare
|
|
76
|
+
// `await exitPromise`) observes the resulting exit exactly like any other
|
|
77
|
+
// kill — no separate wiring needed for the exit-code / state-write path.
|
|
78
|
+
void killProcessGroup(spawned.pgid);
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
let relay = null;
|
|
82
|
+
let idleWatchdog = null;
|
|
83
|
+
let idleClosed = false;
|
|
84
|
+
if (stdinPipe && spawned.stdin) {
|
|
85
|
+
// Idle timeout only applies to session_mode:'session' (`idle_timeout_ms`
|
|
86
|
+
// is null otherwise). Created before the relay so
|
|
87
|
+
// `onUserLineSent` below always has something to call.
|
|
88
|
+
if (config.idle_timeout_ms != null) {
|
|
89
|
+
idleWatchdog = startIdleWatchdog({
|
|
90
|
+
eventsPath: paths.events,
|
|
91
|
+
idleTimeoutMs: config.idle_timeout_ms,
|
|
92
|
+
stdin: spawned.stdin,
|
|
93
|
+
onIdleClose: () => {
|
|
94
|
+
idleClosed = true;
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
relay = startInboxRelay({
|
|
99
|
+
inboxPath: paths.inbox,
|
|
100
|
+
stdin: spawned.stdin,
|
|
101
|
+
onUserLineSent: () => idleWatchdog?.noteActivity(),
|
|
102
|
+
// An explicit agy_send(close:true) ends the session on its own terms;
|
|
103
|
+
// stop the idle watchdog so it cannot also race to end an already-ended
|
|
104
|
+
// stdin.
|
|
105
|
+
onClose: () => idleWatchdog?.stop(),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Signal agy died by, if any: an `agy_cancel` / `on_denial: 'abort'` kill
|
|
109
|
+
// arrives as SIGTERM/SIGKILL on the process group, and verify must not run
|
|
110
|
+
// after that (see the verify block below).
|
|
111
|
+
let agySignal = null;
|
|
112
|
+
const exitPromise = new Promise((resolveExit) => {
|
|
113
|
+
spawned.child.once('exit', (code, signal) => {
|
|
114
|
+
if (signal)
|
|
115
|
+
agySignal = signal;
|
|
116
|
+
if (code !== null)
|
|
117
|
+
resolveExit(code);
|
|
118
|
+
else
|
|
119
|
+
resolveExit(signal ? 128 : 1);
|
|
120
|
+
});
|
|
121
|
+
spawned.child.once('error', () => {
|
|
122
|
+
// A post-spawn error (e.g. an EPIPE writing to a stdin pipe). The process
|
|
123
|
+
// may or may not still be running; treat it as a failure exit and let the
|
|
124
|
+
// deadline / pgid cleanup below reconcile the rest.
|
|
125
|
+
resolveExit(1);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
let timedOut = false;
|
|
129
|
+
let exitCode;
|
|
130
|
+
try {
|
|
131
|
+
if (config.deadline_at != null) {
|
|
132
|
+
const remaining = Math.max(0, config.deadline_at - Date.now());
|
|
133
|
+
let deadlineTimer = null;
|
|
134
|
+
const deadlineHit = new Promise((res) => {
|
|
135
|
+
deadlineTimer = setTimeout(() => res('deadline'), remaining);
|
|
136
|
+
});
|
|
137
|
+
const winner = await Promise.race([
|
|
138
|
+
exitPromise.then((code) => ({ tag: 'exit', code })),
|
|
139
|
+
deadlineHit.then(() => ({ tag: 'deadline' })),
|
|
140
|
+
]);
|
|
141
|
+
if (deadlineTimer)
|
|
142
|
+
clearTimeout(deadlineTimer);
|
|
143
|
+
if (winner.tag === 'deadline') {
|
|
144
|
+
timedOut = true;
|
|
145
|
+
await killProcessGroup(spawned.pgid);
|
|
146
|
+
exitCode = await exitPromise;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
exitCode = winner.code;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
exitCode = await exitPromise;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
relay?.stop();
|
|
158
|
+
idleWatchdog?.stop();
|
|
159
|
+
// finalize(), not stop(): the process is known to be gone by this point,
|
|
160
|
+
// so this does one last synchronous check instead of trusting a poll tick
|
|
161
|
+
// or the grace/cap timer to have already fired — both races a fast job
|
|
162
|
+
// can win (see `gate-watchdog.ts`'s own doc comment on `finalize`).
|
|
163
|
+
gateWatchdog.finalize();
|
|
164
|
+
}
|
|
165
|
+
// PR6: `verify_command` runs once,
|
|
166
|
+
// after agy has exited normally, against the final workspace state — before
|
|
167
|
+
// the job is recorded as finished, so a caller can never observe
|
|
168
|
+
// `lifecycle: 'finished'` without `verification.verify` already settled
|
|
169
|
+
// (`jobs/<id>/exit_code` is the finalization signal every server polls for;
|
|
170
|
+
// see the comment on that write below).
|
|
171
|
+
//
|
|
172
|
+
// Skipped when the deadline killed agy (`timedOut === true`): that already
|
|
173
|
+
// means the job did not run to completion, so there is nothing meaningful
|
|
174
|
+
// left to verify, and `config.verify.timeout_ms` is a separate clock from
|
|
175
|
+
// `deadline_at` on purpose — running it anyway could keep the job alive
|
|
176
|
+
// well past a timeout the caller already asked to be enforced.
|
|
177
|
+
//
|
|
178
|
+
// Also skipped when agy was killed by a signal (`agy_cancel`, the gate's
|
|
179
|
+
// `on_denial: 'abort'` mark reconciled into a group kill) or exited non-zero:
|
|
180
|
+
// `computeOutcome` already resolves those to `canceled` / `failed` ahead of
|
|
181
|
+
// anything verify could say, so running it would only keep a job the caller
|
|
182
|
+
// just cancelled alive for up to `verify.timeout_ms` more.
|
|
183
|
+
let verifyDone = false;
|
|
184
|
+
if (!timedOut && agySignal === null && exitCode === 0 && config.verify) {
|
|
185
|
+
// 0.2.2 (PR1): announce the phase *before* verify starts.
|
|
186
|
+
// From here until the final write below, agy's pid is gone and `exit_code`
|
|
187
|
+
// does not exist — indistinguishable, to a reconciler, from a runner that
|
|
188
|
+
// was killed. `phase: 'verifying'` plus `runner_pid` is what lets it wait
|
|
189
|
+
// for us instead (`reconcileJob`, row 2 / row 3).
|
|
190
|
+
writeState(paths.state, {
|
|
191
|
+
job_id: opts.jobId,
|
|
192
|
+
lifecycle: 'running',
|
|
193
|
+
pid: spawned.pid,
|
|
194
|
+
pgid: spawned.pgid,
|
|
195
|
+
proc_start_time: spawned.procStartTime,
|
|
196
|
+
started_at: spawned.startedAt,
|
|
197
|
+
finished_at: null,
|
|
198
|
+
updated_at: Date.now(),
|
|
199
|
+
idle_closed: idleClosed,
|
|
200
|
+
gate_confirmed: gateConfirmed,
|
|
201
|
+
phase: 'verifying',
|
|
202
|
+
runner_pid: process.pid,
|
|
203
|
+
});
|
|
204
|
+
const verifyRecord = await runVerifyCommand({
|
|
205
|
+
command: config.verify.command,
|
|
206
|
+
cwd: config.cwd,
|
|
207
|
+
env: config.env,
|
|
208
|
+
timeoutMs: config.verify.timeout_ms,
|
|
209
|
+
logPath: paths.verifyLog,
|
|
210
|
+
});
|
|
211
|
+
writeJsonAtomic(paths.verifyJson, verifyRecord);
|
|
212
|
+
verifyDone = true;
|
|
213
|
+
}
|
|
214
|
+
const finishedAt = Date.now();
|
|
215
|
+
// `state.json` first, `exit_code` second — deliberately reordered from a
|
|
216
|
+
// naive "write both" (PR3). `exit_code`
|
|
217
|
+
// existing is the signal every reconciler polls for and finalizes on the
|
|
218
|
+
// instant it sees it (`reconcileJob`'s own doc comment); `exit_code` written
|
|
219
|
+
// first would leave a real window where a reconciler observes it, but reads
|
|
220
|
+
// `state.json` before this call has landed and gets the *previous* write
|
|
221
|
+
// (the plain 'running' state, with no `timed_out`/`idle_closed`/
|
|
222
|
+
// `gate_confirmed` at all) — silently losing exactly the fact PR3 depends on
|
|
223
|
+
// being there every time. Writing state first makes that window impossible:
|
|
224
|
+
// by the time `exit_code` exists, `state.json` already carries the complete
|
|
225
|
+
// final record.
|
|
226
|
+
writeState(paths.state, {
|
|
227
|
+
job_id: opts.jobId,
|
|
228
|
+
lifecycle: 'finished',
|
|
229
|
+
pid: spawned.pid,
|
|
230
|
+
pgid: spawned.pgid,
|
|
231
|
+
proc_start_time: spawned.procStartTime,
|
|
232
|
+
started_at: spawned.startedAt,
|
|
233
|
+
finished_at: finishedAt,
|
|
234
|
+
updated_at: finishedAt,
|
|
235
|
+
timed_out: timedOut,
|
|
236
|
+
idle_closed: idleClosed,
|
|
237
|
+
gate_confirmed: gateConfirmed,
|
|
238
|
+
verify_done: verifyDone,
|
|
239
|
+
phase: 'done',
|
|
240
|
+
runner_pid: process.pid,
|
|
241
|
+
});
|
|
242
|
+
writeExitCode(paths.exitCode, exitCode);
|
|
243
|
+
return {
|
|
244
|
+
jobId: opts.jobId,
|
|
245
|
+
exitCode,
|
|
246
|
+
timedOut,
|
|
247
|
+
durationMs: finishedAt - wallStart,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Enforce the deadline: `SIGTERM` to the process group, grace period, then
|
|
252
|
+
* `SIGKILL`. Signalling the *group* rather than the pid is what reaches agy's own
|
|
253
|
+
* children (verified by checking `pgrep -g` afterwards).
|
|
254
|
+
*
|
|
255
|
+
* Standalone entry point for callers other than {@link runJob} (e.g. `reconcile`
|
|
256
|
+
* discovering a job whose deadline has already passed). `runJob` itself does its
|
|
257
|
+
* own cancellable wait so it is not left holding a dangling timer once the
|
|
258
|
+
* process exits on its own — this function, called directly, always waits out
|
|
259
|
+
* the deadline first.
|
|
260
|
+
*/
|
|
261
|
+
export async function enforceDeadline(pgid, deadlineAt, graceMs) {
|
|
262
|
+
const remaining = deadlineAt - Date.now();
|
|
263
|
+
if (remaining > 0)
|
|
264
|
+
await sleep(remaining);
|
|
265
|
+
return killProcessGroup(pgid, { graceMs });
|
|
266
|
+
}
|
|
267
|
+
/** Write the exit code file atomically. Called on every exit path, including throws. */
|
|
268
|
+
export function writeExitCode(exitCodePath, code) {
|
|
269
|
+
writeFileAtomic(exitCodePath, `${code}\n`);
|
|
270
|
+
}
|
|
271
|
+
/** `dist/runner.js` entry point. Reads the job id from argv or `AGY_WORKER_JOB_ID`. */
|
|
272
|
+
export async function main(argv = []) {
|
|
273
|
+
const jobId = argv[0] ?? process.env[ENV.JOB_ID];
|
|
274
|
+
if (!jobId) {
|
|
275
|
+
process.stderr.write('agy-worker-runner: missing job id (pass it as argv[0] or set AGY_WORKER_JOB_ID)\n');
|
|
276
|
+
return 2;
|
|
277
|
+
}
|
|
278
|
+
const projectRoot = resolveProjectRoot().root;
|
|
279
|
+
try {
|
|
280
|
+
await runJob({ jobId, projectRoot });
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
catch (e) {
|
|
284
|
+
const detail = e instanceof Error ? (e.stack ?? e.message) : String(e);
|
|
285
|
+
process.stderr.write(`agy-worker-runner: job ${jobId} failed: ${detail}\n`);
|
|
286
|
+
return 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/runner/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
import type { Writable } from 'node:stream';
|
|
3
|
+
import { type EffectiveConfig } from '../contract/types.js';
|
|
4
|
+
import type { JobPaths } from '../contract/paths.js';
|
|
5
|
+
/**
|
|
6
|
+
* Typed allowlist for argv construction. There is no free-form
|
|
7
|
+
* `extra_args` and no shell string anywhere; `spawn` always receives an array.
|
|
8
|
+
*/
|
|
9
|
+
export interface AgyArgvInput {
|
|
10
|
+
/** Emitted as `--print=<prompt>`. Empty string is legal and is what stream-json input uses. */
|
|
11
|
+
prompt: string;
|
|
12
|
+
/**
|
|
13
|
+
* Every `--add-dir` value, one flag per element, repeated in order.
|
|
14
|
+
* **Always non-empty.** `addDirs[0]` must be the canonical workspace (the
|
|
15
|
+
* caller's contract, not re-derived here) — without it registered, tools run
|
|
16
|
+
* in agy's scratch directory, and `<ws>/.agents/hooks.json` — our whole gate —
|
|
17
|
+
* is not loaded (§3). Later elements are extra read-only roots (M4:
|
|
18
|
+
* `--add-dir` also extends the `sandbox-exec` allowlist, not just read/write
|
|
19
|
+
* containment) — always `[]` beyond the workspace until PR4 populates
|
|
20
|
+
* `policy.add_dirs` from the human ceiling file. Duplicates are
|
|
21
|
+
* dropped, order preserved.
|
|
22
|
+
*/
|
|
23
|
+
addDirs: string[];
|
|
24
|
+
model?: string | null;
|
|
25
|
+
effort?: string | null;
|
|
26
|
+
mode?: string | null;
|
|
27
|
+
/** Resume an existing conversation. Never `--continue`, which is global. */
|
|
28
|
+
conversationId?: string | null;
|
|
29
|
+
/** Only for `session_mode: 'session'`; requires `--output-format stream-json`. */
|
|
30
|
+
inputFormat?: 'stream-json' | null;
|
|
31
|
+
outputFormat: 'stream-json';
|
|
32
|
+
/** Rendered as agy's duration syntax, e.g. `60m`. */
|
|
33
|
+
printTimeoutMs: number;
|
|
34
|
+
jsonSchemaPath?: string | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build the argv.
|
|
38
|
+
*
|
|
39
|
+
* ⚠ `--print` **must** use the `=` form. Measured: a bare `-p` / `--print`
|
|
40
|
+
* swallows the next flag as its prompt and exits 2 (§2).
|
|
41
|
+
*
|
|
42
|
+
* @throws {ValidationError} if any member of `FORBIDDEN_AGY_FLAGS` would appear,
|
|
43
|
+
* or if `addDirs` is empty or contains a non-absolute entry.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildAgyArgv(input: AgyArgvInput): string[];
|
|
46
|
+
/**
|
|
47
|
+
* `60000` → `"1m"`-style duration string accepted by `--print-timeout`
|
|
48
|
+
* (Go's `time.ParseDuration` syntax: `1h2m3s`, `500ms`, `0s`, ...).
|
|
49
|
+
*/
|
|
50
|
+
export declare function formatDuration(ms: number): string;
|
|
51
|
+
/**
|
|
52
|
+
* Environment for the child, built from an allowlist (PATH, HOME, locale, agy's
|
|
53
|
+
* own config vars). The server's full environment is never inherited wholesale.
|
|
54
|
+
*
|
|
55
|
+
* `projectRoot`/`stateHome` are injected explicitly (not merely allowed through
|
|
56
|
+
* from `base`) so the gate process agy spawns for every tool call resolves the
|
|
57
|
+
* *same* project root and state home this server did — without them, the gate's
|
|
58
|
+
* own `openStore()` re-derives its root from its own `process.cwd()` (the
|
|
59
|
+
* workspace directory, not necessarily what `AGY_WORKER_PROJECT` pinned) and can
|
|
60
|
+
* open a different, empty database, silently disabling the whole policy gate
|
|
61
|
+
* (finding 10).
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildChildEnv(base?: NodeJS.ProcessEnv, overrides?: {
|
|
64
|
+
projectRoot?: string;
|
|
65
|
+
stateHome?: string;
|
|
66
|
+
}): Record<string, string>;
|
|
67
|
+
/** Resolved agy executable: `AGY_WORKER_AGY_BIN`, else `agy` from PATH. */
|
|
68
|
+
export declare function resolveAgyBin(): string;
|
|
69
|
+
export interface SpawnAgyOptions {
|
|
70
|
+
config: EffectiveConfig;
|
|
71
|
+
paths: JobPaths;
|
|
72
|
+
/** Open a pipe on fd 0 so the inbox relay can write turns. Session mode only. */
|
|
73
|
+
stdinPipe: boolean;
|
|
74
|
+
}
|
|
75
|
+
export interface SpawnResult {
|
|
76
|
+
pid: number;
|
|
77
|
+
/** Equals `pid` because `detached: true` makes the child a group leader. */
|
|
78
|
+
pgid: number;
|
|
79
|
+
procStartTime: string | null;
|
|
80
|
+
startedAt: number;
|
|
81
|
+
/** The live child handle. The runner needs this to await exit and to end stdin. */
|
|
82
|
+
child: ChildProcess;
|
|
83
|
+
/** agy's stdin, when `stdinPipe` was requested. Never a pipe for stdout/stderr. */
|
|
84
|
+
stdin: Writable | null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Spawn agy detached, with stdout and stderr pointed at real file descriptors.
|
|
88
|
+
*
|
|
89
|
+
* ⚠ Do not use `'pipe'` for stdout/stderr. Pass fds from `fs.openSync` directly in
|
|
90
|
+
* the `stdio` array: with a pipe, the child dies of EPIPE the moment the client
|
|
91
|
+
* that spawned it goes away, which destroys the entire detached design.
|
|
92
|
+
* File redirection still flushes incrementally.
|
|
93
|
+
*
|
|
94
|
+
* `detached: true` also gives the child its own process group, which is what makes
|
|
95
|
+
* a whole-tree kill possible later.
|
|
96
|
+
*/
|
|
97
|
+
export declare function spawnAgyDetached(opts: SpawnAgyOptions): SpawnResult;
|
|
98
|
+
//# sourceMappingURL=spawn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../src/runner/spawn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,YAAY,EAAqB,MAAM,oBAAoB,CAAA;AAGhF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,EAA4B,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAErF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAGpD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,+FAA+F;IAC/F,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;;;;;OAUG;IACH,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,kFAAkF;IAClF,WAAW,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAClC,YAAY,EAAE,aAAa,CAAA;IAC3B,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,CAuF1D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAiBjD;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,IAAI,GAAE,MAAM,CAAC,UAAwB,EACrC,SAAS,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASxB;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAA;IACvB,KAAK,EAAE,QAAQ,CAAA;IACf,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,mFAAmF;IACnF,KAAK,EAAE,YAAY,CAAA;IACnB,mFAAmF;IACnF,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAA;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CA8CnE"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { chmodSync, closeSync, openSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute } from 'node:path';
|
|
4
|
+
import { FORBIDDEN_AGY_FLAGS, ENV } from '../contract/types.js';
|
|
5
|
+
import { ValidationError } from '../contract/errors.js';
|
|
6
|
+
import { getProcStartTime } from './reap.js';
|
|
7
|
+
/**
|
|
8
|
+
* Build the argv.
|
|
9
|
+
*
|
|
10
|
+
* ⚠ `--print` **must** use the `=` form. Measured: a bare `-p` / `--print`
|
|
11
|
+
* swallows the next flag as its prompt and exits 2 (§2).
|
|
12
|
+
*
|
|
13
|
+
* @throws {ValidationError} if any member of `FORBIDDEN_AGY_FLAGS` would appear,
|
|
14
|
+
* or if `addDirs` is empty or contains a non-absolute entry.
|
|
15
|
+
*/
|
|
16
|
+
export function buildAgyArgv(input) {
|
|
17
|
+
if (!Array.isArray(input.addDirs) || input.addDirs.length === 0) {
|
|
18
|
+
throw new ValidationError({
|
|
19
|
+
field: 'addDirs',
|
|
20
|
+
value: input.addDirs,
|
|
21
|
+
expected: 'a non-empty array whose first element is the canonical workspace — required, or the workspace is never registered',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
for (const dir of input.addDirs) {
|
|
25
|
+
if (typeof dir !== 'string' || !dir.trim() || !isAbsolute(dir)) {
|
|
26
|
+
throw new ValidationError({
|
|
27
|
+
field: 'addDirs',
|
|
28
|
+
value: dir,
|
|
29
|
+
expected: 'every addDirs entry to be a non-empty absolute path',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Order-preserving dedupe: agy accepts repeats, but there is no reason to
|
|
34
|
+
// hand it the same root twice (e.g. a ceiling read_roots entry that
|
|
35
|
+
// happens to equal the workspace).
|
|
36
|
+
const addDirs = Array.from(new Set(input.addDirs));
|
|
37
|
+
if (typeof input.prompt !== 'string') {
|
|
38
|
+
throw new ValidationError({ field: 'prompt', value: input.prompt, expected: 'a string (may be empty)' });
|
|
39
|
+
}
|
|
40
|
+
// Measured against agy 1.1.23: the two are mutually exclusive, and agy says so
|
|
41
|
+
// itself rather than silently ignoring one —
|
|
42
|
+
// Error: --input-format stream-json reads prompts from stdin, so a prompt
|
|
43
|
+
// given on the command line would be ignored
|
|
44
|
+
// Working shape uses `--print=''` plus stdin;
|
|
45
|
+
// this makes it impossible to build the combination that fails.
|
|
46
|
+
if (input.inputFormat === 'stream-json' && input.prompt !== '') {
|
|
47
|
+
throw new ValidationError({
|
|
48
|
+
field: 'prompt',
|
|
49
|
+
value: input.prompt,
|
|
50
|
+
expected: 'an empty string when inputFormat is stream-json — agy rejects a command-line prompt there; queue the turn on stdin instead',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const argv = [];
|
|
54
|
+
argv.push(`--print=${input.prompt}`);
|
|
55
|
+
for (const dir of addDirs)
|
|
56
|
+
argv.push('--add-dir', dir);
|
|
57
|
+
// agy's own approval engine is switched off for every job (0.2.0, PR5).
|
|
58
|
+
// Measured on agy 1.1.24 (M3, variants A/B/C): under this flag our
|
|
59
|
+
// PreToolUse hook still runs, its `deny` still wins, and its
|
|
60
|
+
// `overwrite.BypassSandbox: false` still puts the command under
|
|
61
|
+
// `sandbox-exec` — the flag removes only agy's approval layer, never the OS
|
|
62
|
+
// isolation layer. That layer is redundant for our jobs because the gate is
|
|
63
|
+
// the sole authority, which rests on exactly three invariants:
|
|
64
|
+
// I1 a bound job's gate never answers `ask` (src/gate/gate.ts `decide`),
|
|
65
|
+
// I2 every `run_command` allow sets `BypassSandbox` explicitly, `true`
|
|
66
|
+
// only from the human ceiling (`matchesUnsandboxed`),
|
|
67
|
+
// I4 the runner verifies the gate actually fired before the first tool
|
|
68
|
+
// call completed (src/runner/gate-watchdog.ts).
|
|
69
|
+
// If any of those three is ever removed, this line must go back into
|
|
70
|
+
// FORBIDDEN_AGY_FLAGS: without them, `ask` under this flag means "execute
|
|
71
|
+
// with whatever BypassSandbox the model asked for" (M3-C).
|
|
72
|
+
argv.push('--dangerously-skip-permissions');
|
|
73
|
+
if (input.model)
|
|
74
|
+
argv.push('--model', input.model);
|
|
75
|
+
if (input.effort)
|
|
76
|
+
argv.push('--effort', input.effort);
|
|
77
|
+
if (input.mode)
|
|
78
|
+
argv.push('--mode', input.mode);
|
|
79
|
+
argv.push('--output-format', input.outputFormat);
|
|
80
|
+
if (input.inputFormat)
|
|
81
|
+
argv.push('--input-format', input.inputFormat);
|
|
82
|
+
if (input.conversationId)
|
|
83
|
+
argv.push('--conversation', input.conversationId);
|
|
84
|
+
if (input.jsonSchemaPath)
|
|
85
|
+
argv.push('--json-schema', input.jsonSchemaPath);
|
|
86
|
+
argv.push('--print-timeout', formatDuration(input.printTimeoutMs));
|
|
87
|
+
for (const forbidden of FORBIDDEN_AGY_FLAGS) {
|
|
88
|
+
// Exact-match `includes` alone only catches a forbidden flag passed as its
|
|
89
|
+
// own argv element; `model`/`effort`/`mode` land in argv as free-form
|
|
90
|
+
// strings (e.g. `--model`, `<value>`), so a value shaped like
|
|
91
|
+
// `--continue=1` or `--dangerously-skip-permissions=true` would pass the
|
|
92
|
+
// exact check and still reach agy's own argv parser (finding 18). Also
|
|
93
|
+
// reject anything that starts with the forbidden flag followed by `=`.
|
|
94
|
+
if (argv.includes(forbidden) || argv.some((a) => a.startsWith(`${forbidden}=`))) {
|
|
95
|
+
throw new ValidationError({
|
|
96
|
+
field: 'argv',
|
|
97
|
+
value: forbidden,
|
|
98
|
+
expected: `never one of the forbidden flags`,
|
|
99
|
+
allowed: FORBIDDEN_AGY_FLAGS.slice(),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return argv;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* `60000` → `"1m"`-style duration string accepted by `--print-timeout`
|
|
107
|
+
* (Go's `time.ParseDuration` syntax: `1h2m3s`, `500ms`, `0s`, ...).
|
|
108
|
+
*/
|
|
109
|
+
export function formatDuration(ms) {
|
|
110
|
+
if (!Number.isFinite(ms) || ms < 0) {
|
|
111
|
+
throw new ValidationError({ field: 'printTimeoutMs', value: ms, expected: 'a non-negative finite number of milliseconds' });
|
|
112
|
+
}
|
|
113
|
+
let totalMs = Math.round(ms);
|
|
114
|
+
const hours = Math.floor(totalMs / 3_600_000);
|
|
115
|
+
totalMs -= hours * 3_600_000;
|
|
116
|
+
const minutes = Math.floor(totalMs / 60_000);
|
|
117
|
+
totalMs -= minutes * 60_000;
|
|
118
|
+
const seconds = totalMs / 1000;
|
|
119
|
+
let out = '';
|
|
120
|
+
if (hours > 0)
|
|
121
|
+
out += `${hours}h`;
|
|
122
|
+
if (minutes > 0 || hours > 0)
|
|
123
|
+
out += `${minutes}m`;
|
|
124
|
+
if (Number.isInteger(seconds))
|
|
125
|
+
out += `${seconds}s`;
|
|
126
|
+
else
|
|
127
|
+
out += `${Number(seconds.toFixed(3))}s`;
|
|
128
|
+
return out.length > 0 ? out : '0s';
|
|
129
|
+
}
|
|
130
|
+
/** Environment variable names allowed through to the child. Nothing else survives. */
|
|
131
|
+
const ALLOWED_ENV_KEYS = [
|
|
132
|
+
'PATH',
|
|
133
|
+
'HOME',
|
|
134
|
+
'LANG',
|
|
135
|
+
'LC_ALL',
|
|
136
|
+
'LC_CTYPE',
|
|
137
|
+
'TMPDIR',
|
|
138
|
+
'USER',
|
|
139
|
+
'LOGNAME',
|
|
140
|
+
'SHELL',
|
|
141
|
+
'TERM',
|
|
142
|
+
];
|
|
143
|
+
/**
|
|
144
|
+
* Environment for the child, built from an allowlist (PATH, HOME, locale, agy's
|
|
145
|
+
* own config vars). The server's full environment is never inherited wholesale.
|
|
146
|
+
*
|
|
147
|
+
* `projectRoot`/`stateHome` are injected explicitly (not merely allowed through
|
|
148
|
+
* from `base`) so the gate process agy spawns for every tool call resolves the
|
|
149
|
+
* *same* project root and state home this server did — without them, the gate's
|
|
150
|
+
* own `openStore()` re-derives its root from its own `process.cwd()` (the
|
|
151
|
+
* workspace directory, not necessarily what `AGY_WORKER_PROJECT` pinned) and can
|
|
152
|
+
* open a different, empty database, silently disabling the whole policy gate
|
|
153
|
+
* (finding 10).
|
|
154
|
+
*/
|
|
155
|
+
export function buildChildEnv(base = process.env, overrides) {
|
|
156
|
+
const env = {};
|
|
157
|
+
for (const key of ALLOWED_ENV_KEYS) {
|
|
158
|
+
const value = base[key];
|
|
159
|
+
if (typeof value === 'string' && value.length > 0)
|
|
160
|
+
env[key] = value;
|
|
161
|
+
}
|
|
162
|
+
if (overrides?.projectRoot)
|
|
163
|
+
env[ENV.PROJECT_ROOT] = overrides.projectRoot;
|
|
164
|
+
if (overrides?.stateHome)
|
|
165
|
+
env[ENV.STATE_HOME] = overrides.stateHome;
|
|
166
|
+
return env;
|
|
167
|
+
}
|
|
168
|
+
/** Resolved agy executable: `AGY_WORKER_AGY_BIN`, else `agy` from PATH. */
|
|
169
|
+
export function resolveAgyBin() {
|
|
170
|
+
const override = process.env[ENV.AGY_BIN];
|
|
171
|
+
return override && override.trim() ? override.trim() : 'agy';
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Spawn agy detached, with stdout and stderr pointed at real file descriptors.
|
|
175
|
+
*
|
|
176
|
+
* ⚠ Do not use `'pipe'` for stdout/stderr. Pass fds from `fs.openSync` directly in
|
|
177
|
+
* the `stdio` array: with a pipe, the child dies of EPIPE the moment the client
|
|
178
|
+
* that spawned it goes away, which destroys the entire detached design.
|
|
179
|
+
* File redirection still flushes incrementally.
|
|
180
|
+
*
|
|
181
|
+
* `detached: true` also gives the child its own process group, which is what makes
|
|
182
|
+
* a whole-tree kill possible later.
|
|
183
|
+
*/
|
|
184
|
+
export function spawnAgyDetached(opts) {
|
|
185
|
+
const { config, stdinPipe } = opts;
|
|
186
|
+
const outFd = openSync(opts.paths.events, 'a', 0o600);
|
|
187
|
+
const errFd = openSync(opts.paths.stderr, 'a', 0o600);
|
|
188
|
+
try {
|
|
189
|
+
chmodSync(opts.paths.events, 0o600);
|
|
190
|
+
chmodSync(opts.paths.stderr, 0o600);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// best-effort
|
|
194
|
+
}
|
|
195
|
+
let child;
|
|
196
|
+
try {
|
|
197
|
+
const stdio = [stdinPipe ? 'pipe' : 'ignore', outFd, errFd];
|
|
198
|
+
child = spawn(config.agy_bin, config.argv, {
|
|
199
|
+
cwd: config.cwd,
|
|
200
|
+
env: config.env,
|
|
201
|
+
detached: true,
|
|
202
|
+
stdio,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
// Node dups fds passed as numbers into the child; closing our copies here
|
|
207
|
+
// does not affect the child's descriptors and avoids leaking fds across the
|
|
208
|
+
// lifetime of a long-running job.
|
|
209
|
+
closeSync(outFd);
|
|
210
|
+
closeSync(errFd);
|
|
211
|
+
}
|
|
212
|
+
const startedAt = Date.now();
|
|
213
|
+
const pid = child.pid;
|
|
214
|
+
if (pid == null) {
|
|
215
|
+
throw new Error(`agy-worker: spawn of "${config.agy_bin}" produced no pid`);
|
|
216
|
+
}
|
|
217
|
+
// detached:true makes the child the leader of a brand-new process group, so its
|
|
218
|
+
// pgid equals its pid — this is what later lets us `kill(-pgid)` the whole tree.
|
|
219
|
+
const pgid = pid;
|
|
220
|
+
const procStartTime = getProcStartTime(pid);
|
|
221
|
+
return {
|
|
222
|
+
pid,
|
|
223
|
+
pgid,
|
|
224
|
+
procStartTime,
|
|
225
|
+
startedAt,
|
|
226
|
+
child,
|
|
227
|
+
stdin: stdinPipe ? (child.stdin ?? null) : null,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../src/runner/spawn.ts"],"names":[],"mappings":"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"}
|