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,24 @@
1
+ import type { VerifyRecord } from '../contract/types.js';
2
+ export interface RunVerifyOptions {
3
+ command: string;
4
+ /** Canonical workspace — the same `cwd` agy itself ran in. */
5
+ cwd: string;
6
+ /** The same allowlisted child env agy's own process received. */
7
+ env: Record<string, string>;
8
+ timeoutMs: number;
9
+ /** `jobs/<id>/verify.log` — truncated fresh for this run. */
10
+ logPath: string;
11
+ }
12
+ /**
13
+ * Spawn `sh -c <command>` detached (its own process group, same technique as
14
+ * `spawnAgyDetached`), append stdout+stderr to `logPath` capped at 1 MiB, and
15
+ * wait up to `timeoutMs` — killing the whole group on timeout.
16
+ *
17
+ * Unlike agy's own spawn, stdio is genuinely piped rather than fd-redirected:
18
+ * the runner is alive and actively draining both streams for the entire
19
+ * lifetime of this call (there is no detached-parent-exits race to guard
20
+ * against here), which is what makes counting bytes for the 1 MiB cap
21
+ * possible in the first place.
22
+ */
23
+ export declare function runVerifyCommand(opts: RunVerifyOptions): Promise<VerifyRecord>;
24
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/runner/verify.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAkBxD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAuFpF"}
@@ -0,0 +1,108 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { closeSync, openSync, writeSync } from 'node:fs';
3
+ import { killProcessGroup } from './reap.js';
4
+ /**
5
+ * `verify_command` execution (PR6; see docs/permissions.md).
6
+ *
7
+ * Run once by the runner, after agy has exited normally, against the final
8
+ * workspace state — outside the model's own decisions entirely. Not a
9
+ * sandboxing feature: this runs as the user, with no isolation, at the same
10
+ * trust level as the parent agent running the command itself. Its own
11
+ * `deadline_at`-independent timeout (`timeoutMs`) means it may finish after
12
+ * the job's `deadline_at` has already passed; that is expected, not a bug —
13
+ * agy's deadline and the verify timeout are two separate clocks.
14
+ */
15
+ const MAX_VERIFY_LOG_BYTES = 1024 * 1024;
16
+ const TRUNCATION_MARKER = '\n[agy-worker: verify output truncated at 1 MiB]\n';
17
+ /**
18
+ * Spawn `sh -c <command>` detached (its own process group, same technique as
19
+ * `spawnAgyDetached`), append stdout+stderr to `logPath` capped at 1 MiB, and
20
+ * wait up to `timeoutMs` — killing the whole group on timeout.
21
+ *
22
+ * Unlike agy's own spawn, stdio is genuinely piped rather than fd-redirected:
23
+ * the runner is alive and actively draining both streams for the entire
24
+ * lifetime of this call (there is no detached-parent-exits race to guard
25
+ * against here), which is what makes counting bytes for the 1 MiB cap
26
+ * possible in the first place.
27
+ */
28
+ export async function runVerifyCommand(opts) {
29
+ const startedAt = Date.now();
30
+ const startHr = process.hrtime.bigint();
31
+ // 'w': each job runs verify_command at most once, so a fresh file is always
32
+ // correct — there is nothing from a previous run to append to.
33
+ const logFd = openSync(opts.logPath, 'w', 0o600);
34
+ let bytesWritten = 0;
35
+ let truncated = false;
36
+ const appendChunk = (chunk) => {
37
+ if (truncated)
38
+ return;
39
+ const remaining = MAX_VERIFY_LOG_BYTES - bytesWritten;
40
+ if (remaining <= 0) {
41
+ truncated = true;
42
+ writeSync(logFd, TRUNCATION_MARKER);
43
+ return;
44
+ }
45
+ const slice = chunk.length > remaining ? chunk.subarray(0, remaining) : chunk;
46
+ writeSync(logFd, slice);
47
+ bytesWritten += slice.length;
48
+ if (chunk.length > remaining) {
49
+ truncated = true;
50
+ writeSync(logFd, TRUNCATION_MARKER);
51
+ }
52
+ };
53
+ let child;
54
+ try {
55
+ child = spawn('sh', ['-c', opts.command], {
56
+ cwd: opts.cwd,
57
+ env: opts.env,
58
+ detached: true,
59
+ stdio: ['ignore', 'pipe', 'pipe'],
60
+ });
61
+ }
62
+ catch (e) {
63
+ const message = e instanceof Error ? e.message : String(e);
64
+ writeSync(logFd, `[agy-worker: verify spawn failed: ${message}]\n`);
65
+ closeSync(logFd);
66
+ const durationMs = Math.round(Number(process.hrtime.bigint() - startHr) / 1e6);
67
+ return {
68
+ command: opts.command,
69
+ exit_code: null,
70
+ signal: null,
71
+ started_at: startedAt,
72
+ duration_ms: durationMs,
73
+ timed_out: false,
74
+ };
75
+ }
76
+ const pid = child.pid;
77
+ if (pid == null) {
78
+ closeSync(logFd);
79
+ throw new Error('agy-worker-runner: verify spawn produced no pid');
80
+ }
81
+ // detached:true makes the child the leader of its own process group, so
82
+ // pgid === pid — same as spawnAgyDetached.
83
+ const pgid = pid;
84
+ child.stdout?.on('data', appendChunk);
85
+ child.stderr?.on('data', appendChunk);
86
+ const exitPromise = new Promise((resolveExit) => {
87
+ child.once('exit', (code, signal) => resolveExit({ code, signal }));
88
+ child.once('error', () => resolveExit({ code: null, signal: null }));
89
+ });
90
+ let timedOut = false;
91
+ const timeoutTimer = setTimeout(() => {
92
+ timedOut = true;
93
+ void killProcessGroup(pgid);
94
+ }, opts.timeoutMs);
95
+ const { code, signal } = await exitPromise;
96
+ clearTimeout(timeoutTimer);
97
+ closeSync(logFd);
98
+ const durationMs = Math.round(Number(process.hrtime.bigint() - startHr) / 1e6);
99
+ return {
100
+ command: opts.command,
101
+ exit_code: code,
102
+ signal,
103
+ started_at: startedAt,
104
+ duration_ms: durationMs,
105
+ timed_out: timedOut,
106
+ };
107
+ }
108
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/runner/verify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":""}
package/dist/runner.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Entry point for the detached runner. Compiles to `dist/runner.js`.
4
+ *
5
+ * Spawned with `detached: true` by whichever server started the job, and expected
6
+ * to keep running after that server is gone.
7
+ */
8
+ import { main } from './runner/runner.js';
9
+ main(process.argv.slice(2))
10
+ .then((code) => process.exit(code))
11
+ .catch((e) => {
12
+ process.stderr.write(`agy-worker-runner: ${e instanceof Error ? e.stack ?? e.message : String(e)}\n`);
13
+ process.exit(1);
14
+ });
15
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAEzC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KACxB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KAClC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;IACpB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IACrG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,41 @@
1
+ import type { Capabilities, ClientSnapshot } from '../contract/types.js';
2
+ import type { ProjectPaths } from '../contract/paths.js';
3
+ import { type Store } from '../store/db.js';
4
+ /** Shared state every tool handler receives. One per server process. */
5
+ export interface ToolContext {
6
+ store: Store;
7
+ paths: ProjectPaths;
8
+ version: string;
9
+ limits: Capabilities['limits'];
10
+ /**
11
+ * Set by `createServer` once the transport exists. Left undefined when a test
12
+ * (or any other caller) drives the handlers directly with no client attached.
13
+ */
14
+ getClient?: () => ClientSnapshot | null;
15
+ }
16
+ /**
17
+ * Standard MCP tool return. The JSON goes out as text content *and* as
18
+ * `structuredContent`: Codex's structured-output support is uneven, so text is
19
+ * the safe default while structured stays available for clients that use it
20
+ * (see docs/tools.md).
21
+ */
22
+ export interface ToolReply {
23
+ content: Array<{
24
+ type: 'text';
25
+ text: string;
26
+ }>;
27
+ structuredContent?: Record<string, unknown>;
28
+ isError?: boolean;
29
+ }
30
+ /** Wrap a JSON-serializable payload as text content plus structuredContent. */
31
+ export declare function reply(payload: unknown): ToolReply;
32
+ /**
33
+ * Turn any throw into a teaching error reply: the structured `detail` from
34
+ * `contract/errors.ts` plus its `remedy`, so the calling agent can self-correct
35
+ * without another round trip.
36
+ */
37
+ export declare function errorReply(e: unknown): ToolReply;
38
+ export declare const DEFAULT_LIMITS: Capabilities['limits'];
39
+ /** Build the context, opening the store for the discovered project root. */
40
+ export declare function createContext(cwd?: string): ToolContext;
41
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAGxD,OAAO,EAAa,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAEtD,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,EAAE,YAAY,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,cAAc,GAAG,IAAI,CAAA;CACxC;AAED;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC9C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC3C,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,+EAA+E;AAC/E,wBAAgB,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAOjD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,SAAS,CAOhD;AAYD,eAAO,MAAM,cAAc,EAAE,YAAY,CAAC,QAAQ,CAWjD,CAAA;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,CAQvD"}
@@ -0,0 +1,59 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { packageRoot } from '../contract/paths.js';
4
+ import { toErrorEnvelope } from '../contract/errors.js';
5
+ import { openStore } from '../store/db.js';
6
+ /** Wrap a JSON-serializable payload as text content plus structuredContent. */
7
+ export function reply(payload) {
8
+ const text = JSON.stringify(payload, null, 2);
9
+ const structuredContent = payload !== null && typeof payload === 'object' && !Array.isArray(payload)
10
+ ? payload
11
+ : { value: payload };
12
+ return { content: [{ type: 'text', text }], structuredContent };
13
+ }
14
+ /**
15
+ * Turn any throw into a teaching error reply: the structured `detail` from
16
+ * `contract/errors.ts` plus its `remedy`, so the calling agent can self-correct
17
+ * without another round trip.
18
+ */
19
+ export function errorReply(e) {
20
+ const envelope = toErrorEnvelope(e);
21
+ return {
22
+ content: [{ type: 'text', text: JSON.stringify(envelope, null, 2) }],
23
+ structuredContent: envelope,
24
+ isError: true,
25
+ };
26
+ }
27
+ function readPackageVersion() {
28
+ try {
29
+ const raw = readFileSync(join(packageRoot(), 'package.json'), 'utf8');
30
+ const pkg = JSON.parse(raw);
31
+ return typeof pkg.version === 'string' ? pkg.version : '0.0.0';
32
+ }
33
+ catch {
34
+ return '0.0.0';
35
+ }
36
+ }
37
+ export const DEFAULT_LIMITS = {
38
+ max_running_jobs: 3,
39
+ max_timeout_ms: 60 * 60 * 1000,
40
+ default_timeout_ms: 15 * 60 * 1000,
41
+ // See the comment on `Capabilities['limits']` for why
42
+ // this value is a judgment call, not a measurement.
43
+ default_idle_timeout_ms: 2 * 60 * 1000,
44
+ max_idle_timeout_ms: 60 * 60 * 1000,
45
+ default_verify_timeout_ms: 10 * 60 * 1000,
46
+ max_response_bytes: 32_000,
47
+ max_log_tail_lines: 30,
48
+ };
49
+ /** Build the context, opening the store for the discovered project root. */
50
+ export function createContext(cwd) {
51
+ const store = openStore({ cwd });
52
+ return {
53
+ store,
54
+ paths: store.paths,
55
+ version: readPackageVersion(),
56
+ limits: DEFAULT_LIMITS,
57
+ };
58
+ }
59
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAIhC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAc,MAAM,gBAAgB,CAAA;AA2BtD,+EAA+E;AAC/E,MAAM,UAAU,KAAK,CAAC,OAAgB;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAC7C,MAAM,iBAAiB,GACrB,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QACxE,CAAC,CAAE,OAAmC;QACtC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;IACxB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAA;AACjE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,CAAU;IACnC,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC,CAAA;IACnC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QACpE,iBAAiB,EAAE,QAA8C;QACjE,OAAO,EAAE,IAAI;KACd,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAA;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAA;QACnD,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAA;IAChB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD,gBAAgB,EAAE,CAAC;IACnB,cAAc,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IAC9B,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IAClC,sDAAsD;IACtD,oDAAoD;IACpD,uBAAuB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;IACtC,mBAAmB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IACnC,yBAAyB,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IACzC,kBAAkB,EAAE,MAAM;IAC1B,kBAAkB,EAAE,EAAE;CACvB,CAAA;AAED,4EAA4E;AAC5E,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;IAChC,OAAO;QACL,KAAK;QACL,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,kBAAkB,EAAE;QAC7B,MAAM,EAAE,cAAc;KACvB,CAAA;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Server instructions — returned in the MCP `initialize` response and injected
3
+ * into the calling agent's system prompt.
4
+ *
5
+ * This is the top layer of the three-layer tool surface:
6
+ * this file sets orientation and the traps that matter before the first call;
7
+ * per-tool `description` and per-parameter `.describe()` carry the call-by-call
8
+ * detail; `contract/errors.ts` carries the in-the-moment repair. Two rules for
9
+ * whoever edits it: never describe a capability that does not exist, and never
10
+ * omit a trap that does.
11
+ */
12
+ export declare const SERVER_INSTRUCTIONS = "agy-worker-mcp runs the Google Antigravity CLI (agy) as an asynchronous worker.\nJobs are detached: they survive this connection, and any client attached to the\nsame project can observe, wait on, or take over any job \u2014 including one another\nclient started.\n\nRecommended flow\n agy_capabilities -> profiles, models, limits, discovered project root\n agy_start -> returns a job_id immediately; nothing blocks\n agy_wait -> loop: call again with the returned cursor until\n lifecycle is \"finished\" (wait_ms: 0 = snapshot, no block)\n agy_result -> full verdict, denials, artifacts, response text\n agy_logs -> only if you need the raw or normalized stream itself,\n by cursor or tail\n\nTwo things that will mislead you if you forget them\n 1. Do not trust agy's self-report. Permission denials and sandbox blocks both\n come back as exit 0 with status SUCCESS. Read outcome and contract_status\n from the broker instead; agent_report is an unverified claim, never the\n basis for outcome.\n 2. verified_success means checks actually passed. success_unverified means\n nothing was checkable. blocked means something was denied (or, on a job\n that ran sandboxed, silently refused by the OS sandbox). Treat blocked as\n \"did not do what you asked,\" not as a lesser success.\n\nWhen a job is blocked\n agy_result -> verification.blockers[] is one list of everything that stood\n in the way. Read two fields first: \"actionable\" says whether a different\n agy_start can lift it, and \"remedy\" says what to change (a rule string like\n \"command(python -m pytest)\" for our gate; a rule the project ceiling lacks\n needs a human to widen the ceiling file, not agy_start).\n actionable: false means no agy_start argument will help \u2014 the command tried\n to leave the workspace, a subagent tool was used (subagents run outside this\n job's policy and are always denied), the ceiling refused the rule, or the\n ceiling forces the sandbox on. \"source\" says who refused: policy_ceiling,\n gate, agy_engine, sandbox, broker, tool_error. Our gate is the sole approval\n authority for a job (agy's own approval engine is disabled), so an\n agy_engine entry on a current job means something regressed \u2014 report it.\n Sandbox: allowed commands run WITHOUT agy's OS sandbox on general_worker\n (0.2.1) \u2014 the gate's allow list is the boundary, as in Claude Code \u2014 so an\n \"Operation not permitted\" there is the command's own failure and comes back\n as a warning, not a blocker. research_readonly is always sandboxed; a project\n can set sandbox: \"seatbelt\" (our write-only profile: writes stay inside the\n workspace and the ceiling's write_roots, builds still run) or sandbox: \"agy\"\n in its ceiling file, and the parent can raise it per job with\n permissions.sandbox. On agy 1.1.24 an agy-sandboxed job cannot\n write inside the workspace from a shell (builds, tests, git commit fail),\n though agy's own file tools still edit files. agy_start reports the same\n shape before a job runs:\n its policy_summary and blockers[] tell you if the ceiling rejected your\n permissions.allow. Note allow_count: 0 \u2014 a fully rejected request drops the\n profile's own defaults with it, and the fix is to start again with no\n permissions.allow at all.\n\nWhat is not possible\n You cannot interrupt or redirect a turn that is already running. agy_send\n only queues: the current turn finishes first, then the queued one runs, on a\n job started with session_mode \"session\".\n Sessions resume losslessly, so a finished oneshot job can be continued at\n any time with agy_start({ session_id }) \u2014 no need to keep a session-mode job\n running just to preserve context.\n\nBefore agy_start on general_worker (measured 2026-09-08 across 35 real jobs)\n - Give it something checkable: verify_command (the build or test command),\n expected_artifacts, or json_schema. Without one the best possible outcome\n is success_unverified and you will end up re-running the checks yourself;\n 12 of those 35 jobs went exactly that way.\n - Every shell command your prompt asks for must match the effective allow\n list (agy_capabilities: profiles[].allow plus ceiling.allow; a rule on\n profiles[].deny opens only through ceiling.exceptions). A prompt\n that says \"run JAVA_HOME=... ./gradlew\" against a list that only allows\n \"./gradlew\" guarantees a blocked job. Check with agy_start({ dry_run: true, expected_commands: [...] }).\n - One writing job per cwd at a time (a cwd_write lock; a second agy_start\n fails with LOCK_CONFLICT). For parallel work, give each job its own git\n worktree inside the project root as cwd.\n - When a blocker's remedy needs the ceiling (actionable: false, source\n policy_ceiling, or a rule the ceiling lacks), report the rule string and\n the ceiling file path (agy_capabilities -> ceiling.path) to the user\n verbatim and stop. Do not work around it by telling the next job not to\n run that command \u2014 that only moves the check back onto you. To propose a\n ceiling, call agy_ceiling (history + validation of a draft) and show the\n user the draft; never write policy.json without their explicit approval\n of that draft in the current conversation.\n - A warning starting \"no project ceiling at\" (agy_capabilities,\n agy_start, agy_result) means this project has no ceiling file and jobs\n get the profile as shipped. Not an error; do not repeat it to the user on\n every call. Mention it once when a job needs more than the profile\n allows, or when the user asks why something was denied: they can run\n /agy-ceiling themselves, or you can draft one via agy_ceiling / the\n agy-ceiling skill. Either way the user approves before anything is\n written.\n\nSaving quota\n agy_start({ dry_run: true }) resolves cwd, profile, policy and argv and\n returns the effective config without launching agy or spending quota. Use it\n to settle permissions before a real run, especially after a blocked job.\n\nverify_command\n agy_start({ verify_command }) runs a command once, after agy exits normally,\n against the final workspace state \u2014 outside the model's own decisions\n entirely; it cannot be skipped, reordered, or narrowed from inside the job.\n It is not a sandboxing feature: it runs as the user, unsandboxed, exactly the\n way the parent agent running the command itself would. A non-zero exit or a\n verify_timeout_ms timeout makes outcome \"failed\" \u2014 never a blocker, since\n nothing was refused. agy_result's verification.verify carries\n { command, exit_code, signal, duration_ms, timed_out, output_tail }, where\n output_tail is only the last 2 KiB; the full log is\n jobs/<job_id>/verify.log on disk. verify: null means no verify_command was\n configured (or the job's own deadline killed agy first, which skips verify\n entirely).";
13
+ //# sourceMappingURL=instructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../src/server/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,y4NA4GnB,CAAA"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Server instructions — returned in the MCP `initialize` response and injected
3
+ * into the calling agent's system prompt.
4
+ *
5
+ * This is the top layer of the three-layer tool surface:
6
+ * this file sets orientation and the traps that matter before the first call;
7
+ * per-tool `description` and per-parameter `.describe()` carry the call-by-call
8
+ * detail; `contract/errors.ts` carries the in-the-moment repair. Two rules for
9
+ * whoever edits it: never describe a capability that does not exist, and never
10
+ * omit a trap that does.
11
+ */
12
+ export const SERVER_INSTRUCTIONS = `agy-worker-mcp runs the Google Antigravity CLI (agy) as an asynchronous worker.
13
+ Jobs are detached: they survive this connection, and any client attached to the
14
+ same project can observe, wait on, or take over any job — including one another
15
+ client started.
16
+
17
+ Recommended flow
18
+ agy_capabilities -> profiles, models, limits, discovered project root
19
+ agy_start -> returns a job_id immediately; nothing blocks
20
+ agy_wait -> loop: call again with the returned cursor until
21
+ lifecycle is "finished" (wait_ms: 0 = snapshot, no block)
22
+ agy_result -> full verdict, denials, artifacts, response text
23
+ agy_logs -> only if you need the raw or normalized stream itself,
24
+ by cursor or tail
25
+
26
+ Two things that will mislead you if you forget them
27
+ 1. Do not trust agy's self-report. Permission denials and sandbox blocks both
28
+ come back as exit 0 with status SUCCESS. Read outcome and contract_status
29
+ from the broker instead; agent_report is an unverified claim, never the
30
+ basis for outcome.
31
+ 2. verified_success means checks actually passed. success_unverified means
32
+ nothing was checkable. blocked means something was denied (or, on a job
33
+ that ran sandboxed, silently refused by the OS sandbox). Treat blocked as
34
+ "did not do what you asked," not as a lesser success.
35
+
36
+ When a job is blocked
37
+ agy_result -> verification.blockers[] is one list of everything that stood
38
+ in the way. Read two fields first: "actionable" says whether a different
39
+ agy_start can lift it, and "remedy" says what to change (a rule string like
40
+ "command(python -m pytest)" for our gate; a rule the project ceiling lacks
41
+ needs a human to widen the ceiling file, not agy_start).
42
+ actionable: false means no agy_start argument will help — the command tried
43
+ to leave the workspace, a subagent tool was used (subagents run outside this
44
+ job's policy and are always denied), the ceiling refused the rule, or the
45
+ ceiling forces the sandbox on. "source" says who refused: policy_ceiling,
46
+ gate, agy_engine, sandbox, broker, tool_error. Our gate is the sole approval
47
+ authority for a job (agy's own approval engine is disabled), so an
48
+ agy_engine entry on a current job means something regressed — report it.
49
+ Sandbox: allowed commands run WITHOUT agy's OS sandbox on general_worker
50
+ (0.2.1) — the gate's allow list is the boundary, as in Claude Code — so an
51
+ "Operation not permitted" there is the command's own failure and comes back
52
+ as a warning, not a blocker. research_readonly is always sandboxed; a project
53
+ can set sandbox: "seatbelt" (our write-only profile: writes stay inside the
54
+ workspace and the ceiling's write_roots, builds still run) or sandbox: "agy"
55
+ in its ceiling file, and the parent can raise it per job with
56
+ permissions.sandbox. On agy 1.1.24 an agy-sandboxed job cannot
57
+ write inside the workspace from a shell (builds, tests, git commit fail),
58
+ though agy's own file tools still edit files. agy_start reports the same
59
+ shape before a job runs:
60
+ its policy_summary and blockers[] tell you if the ceiling rejected your
61
+ permissions.allow. Note allow_count: 0 — a fully rejected request drops the
62
+ profile's own defaults with it, and the fix is to start again with no
63
+ permissions.allow at all.
64
+
65
+ What is not possible
66
+ You cannot interrupt or redirect a turn that is already running. agy_send
67
+ only queues: the current turn finishes first, then the queued one runs, on a
68
+ job started with session_mode "session".
69
+ Sessions resume losslessly, so a finished oneshot job can be continued at
70
+ any time with agy_start({ session_id }) — no need to keep a session-mode job
71
+ running just to preserve context.
72
+
73
+ Before agy_start on general_worker (measured 2026-09-08 across 35 real jobs)
74
+ - Give it something checkable: verify_command (the build or test command),
75
+ expected_artifacts, or json_schema. Without one the best possible outcome
76
+ is success_unverified and you will end up re-running the checks yourself;
77
+ 12 of those 35 jobs went exactly that way.
78
+ - Every shell command your prompt asks for must match the effective allow
79
+ list (agy_capabilities: profiles[].allow plus ceiling.allow; a rule on
80
+ profiles[].deny opens only through ceiling.exceptions). A prompt
81
+ that says "run JAVA_HOME=... ./gradlew" against a list that only allows
82
+ "./gradlew" guarantees a blocked job. Check with agy_start({ dry_run: true, expected_commands: [...] }).
83
+ - One writing job per cwd at a time (a cwd_write lock; a second agy_start
84
+ fails with LOCK_CONFLICT). For parallel work, give each job its own git
85
+ worktree inside the project root as cwd.
86
+ - When a blocker's remedy needs the ceiling (actionable: false, source
87
+ policy_ceiling, or a rule the ceiling lacks), report the rule string and
88
+ the ceiling file path (agy_capabilities -> ceiling.path) to the user
89
+ verbatim and stop. Do not work around it by telling the next job not to
90
+ run that command — that only moves the check back onto you. To propose a
91
+ ceiling, call agy_ceiling (history + validation of a draft) and show the
92
+ user the draft; never write policy.json without their explicit approval
93
+ of that draft in the current conversation.
94
+ - A warning starting "no project ceiling at" (agy_capabilities,
95
+ agy_start, agy_result) means this project has no ceiling file and jobs
96
+ get the profile as shipped. Not an error; do not repeat it to the user on
97
+ every call. Mention it once when a job needs more than the profile
98
+ allows, or when the user asks why something was denied: they can run
99
+ /agy-ceiling themselves, or you can draft one via agy_ceiling / the
100
+ agy-ceiling skill. Either way the user approves before anything is
101
+ written.
102
+
103
+ Saving quota
104
+ agy_start({ dry_run: true }) resolves cwd, profile, policy and argv and
105
+ returns the effective config without launching agy or spending quota. Use it
106
+ to settle permissions before a real run, especially after a blocked job.
107
+
108
+ verify_command
109
+ agy_start({ verify_command }) runs a command once, after agy exits normally,
110
+ against the final workspace state — outside the model's own decisions
111
+ entirely; it cannot be skipped, reordered, or narrowed from inside the job.
112
+ It is not a sandboxing feature: it runs as the user, unsandboxed, exactly the
113
+ way the parent agent running the command itself would. A non-zero exit or a
114
+ verify_timeout_ms timeout makes outcome "failed" — never a blocker, since
115
+ nothing was refused. agy_result's verification.verify carries
116
+ { command, exit_code, signal, duration_ms, timed_out, output_tail }, where
117
+ output_tail is only the last 2 KiB; the full log is
118
+ jobs/<job_id>/verify.log on disk. verify: null means no verify_command was
119
+ configured (or the job's own deadline killed agy first, which skips verify
120
+ entirely).`;
121
+ //# sourceMappingURL=instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/server/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA4GtB,CAAA"}
@@ -0,0 +1,20 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ToolContext } from './context.js';
3
+ /**
4
+ * The stdio MCP server. One process per connected client —
5
+ * there is no daemon and no shared broker; coordination happens entirely through
6
+ * the project SQLite database and the job directories.
7
+ *
8
+ * Capabilities are `{ tools: {} }` only. No resources, no prompts.
9
+ */
10
+ export interface CreateServerOptions {
11
+ ctx: ToolContext;
12
+ version: string;
13
+ }
14
+ /** Build the server and register the nine tools. */
15
+ export declare function createServer(opts: CreateServerOptions): McpServer;
16
+ /** `dist/server.js` entry point. Connects a `StdioServerTransport` and stays up. */
17
+ export declare function main(): Promise<void>;
18
+ /** Close the store and flush state. Registered on SIGINT/SIGTERM. */
19
+ export declare function shutdown(ctx: ToolContext): void;
20
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAK/C;;;;;;GAMG;AAEH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,WAAW,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,oDAAoD;AACpD,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,SAAS,CAwBjE;AAED,oFAAoF;AACpF,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAgB1C;AAED,qEAAqE;AACrE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAM/C"}
@@ -0,0 +1,55 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createContext } from './context.js';
4
+ import { registerAllTools } from './tools/index.js';
5
+ import { SERVER_INSTRUCTIONS } from './instructions.js';
6
+ /** Build the server and register the nine tools. */
7
+ export function createServer(opts) {
8
+ const server = new McpServer({ name: 'agy-worker-mcp', version: opts.version }, {
9
+ capabilities: { tools: {} },
10
+ instructions: SERVER_INSTRUCTIONS,
11
+ });
12
+ // What the client declared at `initialize`. The SDK only fills these in once
13
+ // the handshake completed, so read them per call rather than snapshotting at
14
+ // construction time.
15
+ opts.ctx.getClient = () => {
16
+ const impl = server.server.getClientVersion();
17
+ const caps = server.server.getClientCapabilities();
18
+ if (!impl && !caps)
19
+ return null;
20
+ return {
21
+ name: impl?.name ?? null,
22
+ version: impl?.version ?? null,
23
+ capabilities: caps ? caps : null,
24
+ };
25
+ };
26
+ registerAllTools(server, opts.ctx);
27
+ return server;
28
+ }
29
+ /** `dist/server.js` entry point. Connects a `StdioServerTransport` and stays up. */
30
+ export async function main() {
31
+ const ctx = createContext();
32
+ const server = createServer({ ctx, version: ctx.version });
33
+ let shuttingDown = false;
34
+ const handleSignal = () => {
35
+ if (shuttingDown)
36
+ return;
37
+ shuttingDown = true;
38
+ shutdown(ctx);
39
+ process.exit(0);
40
+ };
41
+ process.on('SIGINT', handleSignal);
42
+ process.on('SIGTERM', handleSignal);
43
+ const transport = new StdioServerTransport();
44
+ await server.connect(transport);
45
+ }
46
+ /** Close the store and flush state. Registered on SIGINT/SIGTERM. */
47
+ export function shutdown(ctx) {
48
+ try {
49
+ ctx.store.close();
50
+ }
51
+ catch {
52
+ // best effort — the process is exiting either way
53
+ }
54
+ }
55
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAGhF,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAevD,oDAAoD;AACpD,MAAM,UAAU,YAAY,CAAC,IAAyB;IACpD,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EACjD;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,YAAY,EAAE,mBAAmB;KAClC,CACF,CAAA;IACD,6EAA6E;IAC7E,6EAA6E;IAC7E,qBAAqB;IACrB,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE;QACxB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAA;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAA;QAClD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAA;QAC/B,OAAO;YACL,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI;YACxB,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI;YAC9B,YAAY,EAAE,IAAI,CAAC,CAAC,CAAE,IAAgC,CAAC,CAAC,CAAC,IAAI;SAC9D,CAAA;IACH,CAAC,CAAA;IAED,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;IAClC,OAAO,MAAM,CAAA;AACf,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,GAAG,GAAG,aAAa,EAAE,CAAA;IAC3B,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;IAE1D,IAAI,YAAY,GAAG,KAAK,CAAA;IACxB,MAAM,YAAY,GAAG,GAAS,EAAE;QAC9B,IAAI,YAAY;YAAE,OAAM;QACxB,YAAY,GAAG,IAAI,CAAA;QACnB,QAAQ,CAAC,GAAG,CAAC,CAAA;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAA;IACD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IAClC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;IAEnC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;IAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;AACjC,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,QAAQ,CAAC,GAAgB;IACvC,IAAI,CAAC;QACH,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ import { type ToolContext, type ToolReply } from '../context.js';
3
+ /**
4
+ * `agy_cancel` — kill a running job and everything it spawned.
5
+ *
6
+ * Signals the process group, not the pid, so agy's own children die too
7
+ * (verified by checking `pgrep -g` afterwards).
8
+ */
9
+ export declare const cancelInput: z.ZodObject<{
10
+ job_id: z.ZodString;
11
+ reason: z.ZodOptional<z.ZodString>;
12
+ grace_ms: z.ZodOptional<z.ZodNumber>;
13
+ }, "strip", z.ZodTypeAny, {
14
+ job_id: string;
15
+ reason?: string | undefined;
16
+ grace_ms?: number | undefined;
17
+ }, {
18
+ job_id: string;
19
+ reason?: string | undefined;
20
+ grace_ms?: number | undefined;
21
+ }>;
22
+ export type CancelInput = z.infer<typeof cancelInput>;
23
+ export declare function handleCancel(ctx: ToolContext, input: CancelInput): Promise<ToolReply>;
24
+ //# sourceMappingURL=cancel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancel.d.ts","sourceRoot":"","sources":["../../../src/server/tools/cancel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAA;AAEnF;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;EAStB,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AAErD,wBAAsB,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAyC3F"}
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ import { reconcile } from '../../broker/reconcile.js';
3
+ import { isPidAlive, isSameProcess, killProcessGroup } from '../../runner/reap.js';
4
+ import { getJob, updateJob } from '../../store/jobs.js';
5
+ import { errorReply, reply } from '../context.js';
6
+ /**
7
+ * `agy_cancel` — kill a running job and everything it spawned.
8
+ *
9
+ * Signals the process group, not the pid, so agy's own children die too
10
+ * (verified by checking `pgrep -g` afterwards).
11
+ */
12
+ export const cancelInput = z.object({
13
+ job_id: z.string(),
14
+ reason: z.string().optional(),
15
+ grace_ms: z
16
+ .number()
17
+ .int()
18
+ .min(0)
19
+ .optional()
20
+ .describe('Milliseconds between SIGTERM and SIGKILL.'),
21
+ });
22
+ export async function handleCancel(ctx, input) {
23
+ try {
24
+ await reconcile(ctx.store);
25
+ let job = getJob(ctx.store, input.job_id);
26
+ if (job.lifecycle === 'finished') {
27
+ return reply({ job_id: job.job_id, lifecycle: job.lifecycle, killed: false, message: 'already finished' });
28
+ }
29
+ // Only signal the recorded pgid when we can confirm it still identifies
30
+ // *our* process: a null proc_start_time (nothing was ever recorded) or a pid
31
+ // that no longer matches it means the pgid may have been recycled by an
32
+ // unrelated process tree since (finding 6) — do not kill in that case.
33
+ let killed = false;
34
+ if (job.pgid !== null &&
35
+ job.pid !== null &&
36
+ job.proc_start_time !== null &&
37
+ isPidAlive(job.pid) &&
38
+ isSameProcess(job.pid, job.proc_start_time)) {
39
+ killed = await killProcessGroup(job.pgid, { graceMs: input.grace_ms });
40
+ }
41
+ job = updateJob(ctx.store, job.job_id, { lifecycle: 'canceling' });
42
+ // The runner still owns writing exit_code; the next reconcile finalizes
43
+ // this as `canceled` once it appears.
44
+ await reconcile(ctx.store);
45
+ job = getJob(ctx.store, job.job_id);
46
+ return reply({
47
+ job_id: job.job_id,
48
+ lifecycle: job.lifecycle,
49
+ outcome: job.outcome,
50
+ killed,
51
+ pgid: job.pgid,
52
+ reason: input.reason ?? null,
53
+ });
54
+ }
55
+ catch (e) {
56
+ return errorReply(e);
57
+ }
58
+ }
59
+ //# sourceMappingURL=cancel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cancel.js","sourceRoot":"","sources":["../../../src/server/tools/cancel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAClF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAoC,MAAM,eAAe,CAAA;AAEnF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,QAAQ,CAAC,2CAA2C,CAAC;CACzD,CAAC,CAAA;AAIF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAgB,EAAE,KAAkB;IACrE,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAC1B,IAAI,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;QAEzC,IAAI,GAAG,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;YACjC,OAAO,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAA;QAC5G,CAAC;QAED,wEAAwE;QACxE,6EAA6E;QAC7E,wEAAwE;QACxE,uEAAuE;QACvE,IAAI,MAAM,GAAG,KAAK,CAAA;QAClB,IACE,GAAG,CAAC,IAAI,KAAK,IAAI;YACjB,GAAG,CAAC,GAAG,KAAK,IAAI;YAChB,GAAG,CAAC,eAAe,KAAK,IAAI;YAC5B,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YACnB,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,eAAe,CAAC,EAC3C,CAAC;YACD,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;QACxE,CAAC;QAED,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAA;QAClE,wEAAwE;QACxE,sCAAsC;QACtC,MAAM,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAC1B,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;QAEnC,OAAO,KAAK,CAAC;YACX,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,MAAM;YACN,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;SAC7B,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,UAAU,CAAC,CAAC,CAAC,CAAA;IACtB,CAAC;AACH,CAAC"}