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.
Files changed (227) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/LICENSE +21 -0
  3. package/README.md +309 -0
  4. package/commands/agy-ceiling.md +31 -0
  5. package/dist/broker/blockers.d.ts +86 -0
  6. package/dist/broker/blockers.d.ts.map +1 -0
  7. package/dist/broker/blockers.js +390 -0
  8. package/dist/broker/blockers.js.map +1 -0
  9. package/dist/broker/outcome.d.ts +85 -0
  10. package/dist/broker/outcome.d.ts.map +1 -0
  11. package/dist/broker/outcome.js +147 -0
  12. package/dist/broker/outcome.js.map +1 -0
  13. package/dist/broker/reconcile.d.ts +74 -0
  14. package/dist/broker/reconcile.d.ts.map +1 -0
  15. package/dist/broker/reconcile.js +415 -0
  16. package/dist/broker/reconcile.js.map +1 -0
  17. package/dist/broker/result.d.ts +88 -0
  18. package/dist/broker/result.d.ts.map +1 -0
  19. package/dist/broker/result.js +234 -0
  20. package/dist/broker/result.js.map +1 -0
  21. package/dist/broker/verify.d.ts +52 -0
  22. package/dist/broker/verify.d.ts.map +1 -0
  23. package/dist/broker/verify.js +174 -0
  24. package/dist/broker/verify.js.map +1 -0
  25. package/dist/contract/errors.d.ts +119 -0
  26. package/dist/contract/errors.d.ts.map +1 -0
  27. package/dist/contract/errors.js +99 -0
  28. package/dist/contract/errors.js.map +1 -0
  29. package/dist/contract/index.d.ts +10 -0
  30. package/dist/contract/index.d.ts.map +1 -0
  31. package/dist/contract/index.js +10 -0
  32. package/dist/contract/index.js.map +1 -0
  33. package/dist/contract/paths.d.ts +139 -0
  34. package/dist/contract/paths.d.ts.map +1 -0
  35. package/dist/contract/paths.js +359 -0
  36. package/dist/contract/paths.js.map +1 -0
  37. package/dist/contract/schema.sql +64 -0
  38. package/dist/contract/types.d.ts +1113 -0
  39. package/dist/contract/types.d.ts.map +1 -0
  40. package/dist/contract/types.js +114 -0
  41. package/dist/contract/types.js.map +1 -0
  42. package/dist/events/cursor.d.ts +65 -0
  43. package/dist/events/cursor.d.ts.map +1 -0
  44. package/dist/events/cursor.js +286 -0
  45. package/dist/events/cursor.js.map +1 -0
  46. package/dist/events/detect.d.ts +58 -0
  47. package/dist/events/detect.d.ts.map +1 -0
  48. package/dist/events/detect.js +183 -0
  49. package/dist/events/detect.js.map +1 -0
  50. package/dist/events/normalize.d.ts +30 -0
  51. package/dist/events/normalize.d.ts.map +1 -0
  52. package/dist/events/normalize.js +158 -0
  53. package/dist/events/normalize.js.map +1 -0
  54. package/dist/events/parse.d.ts +41 -0
  55. package/dist/events/parse.d.ts.map +1 -0
  56. package/dist/events/parse.js +89 -0
  57. package/dist/events/parse.js.map +1 -0
  58. package/dist/gate/bind.d.ts +28 -0
  59. package/dist/gate/bind.d.ts.map +1 -0
  60. package/dist/gate/bind.js +154 -0
  61. package/dist/gate/bind.js.map +1 -0
  62. package/dist/gate/gate.d.ts +139 -0
  63. package/dist/gate/gate.d.ts.map +1 -0
  64. package/dist/gate/gate.js +488 -0
  65. package/dist/gate/gate.js.map +1 -0
  66. package/dist/gate/hooks-file.d.ts +55 -0
  67. package/dist/gate/hooks-file.d.ts.map +1 -0
  68. package/dist/gate/hooks-file.js +104 -0
  69. package/dist/gate/hooks-file.js.map +1 -0
  70. package/dist/gate.d.ts +3 -0
  71. package/dist/gate.d.ts.map +1 -0
  72. package/dist/gate.js +20 -0
  73. package/dist/gate.js.map +1 -0
  74. package/dist/policy/ceiling-review.d.ts +22 -0
  75. package/dist/policy/ceiling-review.d.ts.map +1 -0
  76. package/dist/policy/ceiling-review.js +170 -0
  77. package/dist/policy/ceiling-review.js.map +1 -0
  78. package/dist/policy/ceiling.d.ts +128 -0
  79. package/dist/policy/ceiling.d.ts.map +1 -0
  80. package/dist/policy/ceiling.js +273 -0
  81. package/dist/policy/ceiling.js.map +1 -0
  82. package/dist/policy/containment.d.ts +100 -0
  83. package/dist/policy/containment.d.ts.map +1 -0
  84. package/dist/policy/containment.js +1071 -0
  85. package/dist/policy/containment.js.map +1 -0
  86. package/dist/policy/hard-deny.d.ts +28 -0
  87. package/dist/policy/hard-deny.d.ts.map +1 -0
  88. package/dist/policy/hard-deny.js +78 -0
  89. package/dist/policy/hard-deny.js.map +1 -0
  90. package/dist/policy/profiles.d.ts +82 -0
  91. package/dist/policy/profiles.d.ts.map +1 -0
  92. package/dist/policy/profiles.js +317 -0
  93. package/dist/policy/profiles.js.map +1 -0
  94. package/dist/policy/rules.d.ts +138 -0
  95. package/dist/policy/rules.d.ts.map +1 -0
  96. package/dist/policy/rules.js +1180 -0
  97. package/dist/policy/rules.js.map +1 -0
  98. package/dist/policy/seatbelt.d.ts +32 -0
  99. package/dist/policy/seatbelt.d.ts.map +1 -0
  100. package/dist/policy/seatbelt.js +59 -0
  101. package/dist/policy/seatbelt.js.map +1 -0
  102. package/dist/policy/tools.d.ts +36 -0
  103. package/dist/policy/tools.d.ts.map +1 -0
  104. package/dist/policy/tools.js +94 -0
  105. package/dist/policy/tools.js.map +1 -0
  106. package/dist/runner/gate-watchdog.d.ts +93 -0
  107. package/dist/runner/gate-watchdog.d.ts.map +1 -0
  108. package/dist/runner/gate-watchdog.js +153 -0
  109. package/dist/runner/gate-watchdog.js.map +1 -0
  110. package/dist/runner/idle.d.ts +51 -0
  111. package/dist/runner/idle.d.ts.map +1 -0
  112. package/dist/runner/idle.js +81 -0
  113. package/dist/runner/idle.js.map +1 -0
  114. package/dist/runner/inbox.d.ts +56 -0
  115. package/dist/runner/inbox.d.ts.map +1 -0
  116. package/dist/runner/inbox.js +134 -0
  117. package/dist/runner/inbox.js.map +1 -0
  118. package/dist/runner/reap.d.ts +65 -0
  119. package/dist/runner/reap.d.ts.map +1 -0
  120. package/dist/runner/reap.js +168 -0
  121. package/dist/runner/reap.js.map +1 -0
  122. package/dist/runner/runner.d.ts +51 -0
  123. package/dist/runner/runner.d.ts.map +1 -0
  124. package/dist/runner/runner.js +289 -0
  125. package/dist/runner/runner.js.map +1 -0
  126. package/dist/runner/spawn.d.ts +98 -0
  127. package/dist/runner/spawn.d.ts.map +1 -0
  128. package/dist/runner/spawn.js +230 -0
  129. package/dist/runner/spawn.js.map +1 -0
  130. package/dist/runner/verify.d.ts +24 -0
  131. package/dist/runner/verify.d.ts.map +1 -0
  132. package/dist/runner/verify.js +108 -0
  133. package/dist/runner/verify.js.map +1 -0
  134. package/dist/runner.d.ts +3 -0
  135. package/dist/runner.d.ts.map +1 -0
  136. package/dist/runner.js +15 -0
  137. package/dist/runner.js.map +1 -0
  138. package/dist/server/context.d.ts +41 -0
  139. package/dist/server/context.d.ts.map +1 -0
  140. package/dist/server/context.js +59 -0
  141. package/dist/server/context.js.map +1 -0
  142. package/dist/server/instructions.d.ts +13 -0
  143. package/dist/server/instructions.d.ts.map +1 -0
  144. package/dist/server/instructions.js +121 -0
  145. package/dist/server/instructions.js.map +1 -0
  146. package/dist/server/server.d.ts +20 -0
  147. package/dist/server/server.d.ts.map +1 -0
  148. package/dist/server/server.js +55 -0
  149. package/dist/server/server.js.map +1 -0
  150. package/dist/server/tools/cancel.d.ts +24 -0
  151. package/dist/server/tools/cancel.d.ts.map +1 -0
  152. package/dist/server/tools/cancel.js +59 -0
  153. package/dist/server/tools/cancel.js.map +1 -0
  154. package/dist/server/tools/capabilities.d.ts +36 -0
  155. package/dist/server/tools/capabilities.d.ts.map +1 -0
  156. package/dist/server/tools/capabilities.js +116 -0
  157. package/dist/server/tools/capabilities.js.map +1 -0
  158. package/dist/server/tools/ceiling.d.ts +31 -0
  159. package/dist/server/tools/ceiling.d.ts.map +1 -0
  160. package/dist/server/tools/ceiling.js +121 -0
  161. package/dist/server/tools/ceiling.js.map +1 -0
  162. package/dist/server/tools/index.d.ts +38 -0
  163. package/dist/server/tools/index.d.ts.map +1 -0
  164. package/dist/server/tools/index.js +115 -0
  165. package/dist/server/tools/index.js.map +1 -0
  166. package/dist/server/tools/listJobs.d.ts +30 -0
  167. package/dist/server/tools/listJobs.d.ts.map +1 -0
  168. package/dist/server/tools/listJobs.js +37 -0
  169. package/dist/server/tools/listJobs.js.map +1 -0
  170. package/dist/server/tools/logs.d.ts +31 -0
  171. package/dist/server/tools/logs.d.ts.map +1 -0
  172. package/dist/server/tools/logs.js +96 -0
  173. package/dist/server/tools/logs.js.map +1 -0
  174. package/dist/server/tools/result.d.ts +32 -0
  175. package/dist/server/tools/result.d.ts.map +1 -0
  176. package/dist/server/tools/result.js +95 -0
  177. package/dist/server/tools/result.js.map +1 -0
  178. package/dist/server/tools/send.d.ts +37 -0
  179. package/dist/server/tools/send.d.ts.map +1 -0
  180. package/dist/server/tools/send.js +71 -0
  181. package/dist/server/tools/send.js.map +1 -0
  182. package/dist/server/tools/sessions.d.ts +28 -0
  183. package/dist/server/tools/sessions.d.ts.map +1 -0
  184. package/dist/server/tools/sessions.js +54 -0
  185. package/dist/server/tools/sessions.js.map +1 -0
  186. package/dist/server/tools/start.d.ts +107 -0
  187. package/dist/server/tools/start.d.ts.map +1 -0
  188. package/dist/server/tools/start.js +482 -0
  189. package/dist/server/tools/start.js.map +1 -0
  190. package/dist/server/tools/wait.d.ts +29 -0
  191. package/dist/server/tools/wait.d.ts.map +1 -0
  192. package/dist/server/tools/wait.js +120 -0
  193. package/dist/server/tools/wait.js.map +1 -0
  194. package/dist/server.d.ts +3 -0
  195. package/dist/server.d.ts.map +1 -0
  196. package/dist/server.js +17 -0
  197. package/dist/server.js.map +1 -0
  198. package/dist/setup/install.d.ts +52 -0
  199. package/dist/setup/install.d.ts.map +1 -0
  200. package/dist/setup/install.js +108 -0
  201. package/dist/setup/install.js.map +1 -0
  202. package/dist/setup.d.ts +3 -0
  203. package/dist/setup.d.ts.map +1 -0
  204. package/dist/setup.js +24 -0
  205. package/dist/setup.js.map +1 -0
  206. package/dist/store/db.d.ts +49 -0
  207. package/dist/store/db.d.ts.map +1 -0
  208. package/dist/store/db.js +101 -0
  209. package/dist/store/db.js.map +1 -0
  210. package/dist/store/jobs.d.ts +45 -0
  211. package/dist/store/jobs.d.ts.map +1 -0
  212. package/dist/store/jobs.js +150 -0
  213. package/dist/store/jobs.js.map +1 -0
  214. package/dist/store/locks.d.ts +60 -0
  215. package/dist/store/locks.d.ts.map +1 -0
  216. package/dist/store/locks.js +172 -0
  217. package/dist/store/locks.js.map +1 -0
  218. package/dist/store/sessions.d.ts +37 -0
  219. package/dist/store/sessions.d.ts.map +1 -0
  220. package/dist/store/sessions.js +117 -0
  221. package/dist/store/sessions.js.map +1 -0
  222. package/dist/trace/digest.d.ts +77 -0
  223. package/dist/trace/digest.d.ts.map +1 -0
  224. package/dist/trace/digest.js +481 -0
  225. package/dist/trace/digest.js.map +1 -0
  226. package/package.json +68 -0
  227. 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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"}