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,390 @@
1
+ import { ceilingAbsenceHint } from '../policy/ceiling.js';
2
+ import { AGY_ENGINE_REFUSAL_SIGNATURES, } from '../contract/types.js';
3
+ /**
4
+ * The single place that answers "who refused, and can the caller fix it".
5
+ *
6
+ * `outcome`, the judgement packet's `counts`, `verification.warnings` and
7
+ * `agy_start`'s own reply are all projections of the `Blocker` list built here.
8
+ * Before 0.1.1 each of those re-derived the rule for itself, and they disagreed:
9
+ * one real job came back with `counts.permission_denials: 1` next to
10
+ * `outcome: "success_unverified"`, because the packet counted every Class 1
11
+ * event while `decideOutcome` counted only the ones our gate authored.
12
+ *
13
+ * The mapping, and nothing may re-implement a row of it:
14
+ *
15
+ * | source | what | actionable | remedy | blocks_outcome |
16
+ * | ---------------- | ------------------------------------- | ---------- | ----------------------------- | -------------- |
17
+ * | `policy_ceiling` | `rejected_allow` entry (pre-flight) | yes | drop it / change profile | n/a (no job yet) |
18
+ * | `gate` | our gate's confirmed refusal | yes | its `required_rule` | yes |
19
+ * | `gate` (containment) | command left the workspace | **no** | none — no rule grants this | yes |
20
+ * | `agy_engine` | agy's own permission engine refused | no | none — outside our policy | **no** |
21
+ * | `sandbox` | Class 2 signature match, sandboxed job | depends | why the job was sandboxed: `permissions.sandboxed` (retry without it), `research_readonly` (use general_worker), ceiling `sandboxed: true` (human) | yes |
22
+ * | `broker` | missing `expected_artifacts` entry | yes | which artifact is missing | yes |
23
+ * | `broker` | I4: gate never confirmed itself (`gate_confirmed === false`) | **no** | none — hooks.json never loaded, nothing to fix by rule | yes (and forces `outcome: 'process_error'` directly, not just `blocked` — see `decideOutcome`) |
24
+ * | `tool_error` | a failing tool call, no signature | no | none — not a permission issue | **no** |
25
+ *
26
+ * `agy_engine` and `tool_error` do not force `blocked` for the same reason:
27
+ * a non-gate `state: 'ERROR'` step is indistinguishable from an ordinary
28
+ * command failure by shape alone (finding 17), so treating them as blocks would
29
+ * report every failing test as a permission problem.
30
+ *
31
+ * The two `broker` rows are deliberately different shapes (`blockerFromMissingArtifact`
32
+ * is actionable, `blockerFromGateMissing` is not) — `source` alone does not
33
+ * imply a fixed `actionable`/`remedy`; read the specific constructor.
34
+ */
35
+ /** Human label per source, used by both the warning lines and the headline. */
36
+ const LABEL = {
37
+ policy_ceiling: 'ceiling rejection',
38
+ gate: 'gate denial',
39
+ agy_engine: 'agy-engine refusal',
40
+ sandbox: 'sandbox block',
41
+ broker: 'broker check',
42
+ tool_error: 'tool error',
43
+ };
44
+ /**
45
+ * Whether a non-gate refusal came from agy's own permission engine.
46
+ *
47
+ * Substring match against measured wording, the same technique `detectClass2`
48
+ * uses on environment blocks and for the same reason: agy emits no structured
49
+ * field saying who refused.
50
+ */
51
+ export function isAgyEngineRefusal(message) {
52
+ return AGY_ENGINE_REFUSAL_SIGNATURES.some((sig) => message.includes(sig));
53
+ }
54
+ function asDetail(value) {
55
+ return value;
56
+ }
57
+ /**
58
+ * A Class 1 event → the blocker it actually is.
59
+ *
60
+ * Three outcomes, because three different next actions: our gate refused
61
+ * (fixable with a rule), agy's own engine refused (nothing of ours applies), or
62
+ * a command simply failed (not a permission matter at all).
63
+ */
64
+ export function blockerFromDenial(d) {
65
+ const base = { tool: d.tool, command: d.command, detail: asDetail(d) };
66
+ if (d.source === 'gate') {
67
+ if (d.policy === 'containment') {
68
+ return {
69
+ ...base,
70
+ source: 'gate',
71
+ actionable: false,
72
+ remedy: null,
73
+ blocks_outcome: true,
74
+ message: `our gate refused this because it wrote outside the workspace; containment is checked before any rule, so no permissions.allow entry can grant it — change the command or the workspace. ${d.message}`,
75
+ };
76
+ }
77
+ if (d.policy === 'unsupported') {
78
+ // classifyToolCall (src/policy/tools.ts) marks this tool unconditionally —
79
+ // a subagent tool (M2) or one outside the 7-tool + manage_task table —
80
+ // before any rule list is even consulted, so no `permissions.allow`
81
+ // entry changes the outcome. required_rule is always null here (gate.ts).
82
+ return {
83
+ ...base,
84
+ source: 'gate',
85
+ actionable: false,
86
+ remedy: null,
87
+ blocks_outcome: true,
88
+ message: `our gate refused this because the tool itself is not one permissions.allow can grant (unsupported, checked before any rule list). ${d.message}`,
89
+ };
90
+ }
91
+ return {
92
+ ...base,
93
+ source: 'gate',
94
+ actionable: true,
95
+ remedy: d.required_rule,
96
+ blocks_outcome: true,
97
+ message: d.required_rule
98
+ ? `our permission gate refused this; nothing in the job's effective allow list covers it. Rule that would: ${d.required_rule}. If agy_capabilities shows the ceiling already covers it, retry without permissions.allow (allow only narrows); otherwise a human must add it to the ceiling's allow (or, for a rule the profile denies, exceptions). ${d.message}`
99
+ : `our permission gate refused this. ${d.message}`,
100
+ };
101
+ }
102
+ if (isAgyEngineRefusal(d.message)) {
103
+ return {
104
+ ...base,
105
+ source: 'agy_engine',
106
+ actionable: false,
107
+ remedy: null,
108
+ blocks_outcome: false,
109
+ message: `agy's own permission engine refused this. Since 0.2.0 that engine is disabled for our jobs (--dangerously-skip-permissions, measured on agy 1.1.24, M3), so this should not occur on a current job — if it does, the flag or the hook wiring regressed; report it. Seen on archived v1 results this is expected. No permissions.allow rule will help, and we cannot confirm it as a block either. ${d.message}`,
110
+ };
111
+ }
112
+ return {
113
+ ...base,
114
+ source: 'tool_error',
115
+ actionable: false,
116
+ remedy: null,
117
+ blocks_outcome: false,
118
+ message: `the tool call failed with no refusal signature we recognize, so this is not a permission problem we can confirm. ${d.message}`,
119
+ };
120
+ }
121
+ /** A Class 2 signature match. Recovery depends on whether and why the job ran sandboxed. */
122
+ export function blockerFromEnvironmentBlock(b, policy) {
123
+ const base = {
124
+ source: 'sandbox',
125
+ blocks_outcome: true,
126
+ tool: b.tool,
127
+ command: b.command,
128
+ detail: asDetail(b),
129
+ };
130
+ // `verify.ts` never calls this for a job that ran with `bypass_sandbox:
131
+ // true` — without a sandbox the same signature is an ordinary command
132
+ // failure and goes to `warnings` instead. So every blocker built here comes
133
+ // from a job that really was sandboxed, and the only question is why.
134
+ // Job ran sandboxed. Identify which sandbox and why: request, ceiling, or profile.
135
+ if (policy?.sandbox === 'seatbelt') {
136
+ const source = policy.sandbox_source;
137
+ return {
138
+ ...base,
139
+ actionable: source === 'request',
140
+ remedy: source === 'request'
141
+ ? 'retry without permissions.sandbox, or add the directory to write_roots in the project ceiling if the write is legitimate'
142
+ : 'a human must add the directory to write_roots in the project ceiling (~/.agy-worker/projects/<hash>/policy.json), or set sandbox to "none" there',
143
+ message: `our seatbelt profile refused a write outside write_roots (signature "${b.signature}"; sandbox: seatbelt set by ${source}): ${b.excerpt}`,
144
+ };
145
+ }
146
+ const reason = policy?.sandbox_forced_by ??
147
+ (policy?.profile === 'research_readonly' ? 'profile' : null);
148
+ const additionalDirsGuidance = 'add the path to read_roots in the project ceiling (~/.agy-worker/projects/<hash>/policy.json) if it is a blocked read/exec of a toolchain outside the workspace.';
149
+ if (reason === 'request') {
150
+ return {
151
+ ...base,
152
+ actionable: true,
153
+ remedy: `retry without permissions.sandboxed; ${additionalDirsGuidance}`,
154
+ message: `agy's sandbox blocked this silently (signature "${b.signature}") because permissions.sandboxed was set for this job: ${b.excerpt}`,
155
+ };
156
+ }
157
+ if (reason === 'profile') {
158
+ return {
159
+ ...base,
160
+ actionable: true,
161
+ remedy: `start the job with profile "general_worker" if the task needs to write; ${additionalDirsGuidance}`,
162
+ message: `agy's sandbox blocked this silently (signature "${b.signature}") because profile "${policy?.profile ?? 'research_readonly'}" is always sandboxed: ${b.excerpt}`,
163
+ };
164
+ }
165
+ // reason === 'ceiling' or generic/unknown sandboxed (e.g. policy not provided)
166
+ const isCeiling = reason === 'ceiling';
167
+ return {
168
+ ...base,
169
+ actionable: false,
170
+ remedy: `a human must set "sandbox": "none" (or "seatbelt") in the project ceiling (~/.agy-worker/projects/<hash>/policy.json); ${additionalDirsGuidance}`,
171
+ message: isCeiling
172
+ ? `agy's sandbox blocked this silently (signature "${b.signature}") because the project ceiling has sandbox: agy: ${b.excerpt}`
173
+ : `agy's sandbox blocked this silently — signature "${b.signature}": ${b.excerpt}`,
174
+ };
175
+ }
176
+ /**
177
+ * I4: the runner's own gate watchdog
178
+ * killed the job because `jobs/<id>/gate-log.jsonl` never received a line
179
+ * within 3s of the first tool step — our PreToolUse hook never actually ran,
180
+ * so nothing before the kill was policy-checked at all. Never actionable — no
181
+ * `permissions.allow` entry fixes a hook that never loaded — and
182
+ * `decideOutcome` forces `outcome: 'process_error'` for this directly, not
183
+ * merely `blocked`, since `blocked` would imply the gate ran and refused.
184
+ */
185
+ export function blockerFromGateMissing() {
186
+ return {
187
+ source: 'broker',
188
+ actionable: false,
189
+ remedy: null,
190
+ blocks_outcome: true,
191
+ tool: null,
192
+ command: null,
193
+ message: 'gate hook never fired; hooks.json was not loaded',
194
+ };
195
+ }
196
+ /** A broker-side check that failed. Today that is only a missing expected artifact. */
197
+ export function blockerFromMissingArtifact(a) {
198
+ return {
199
+ source: 'broker',
200
+ actionable: true,
201
+ remedy: `have the job create ${a.path}, or drop it from expected_artifacts`,
202
+ blocks_outcome: true,
203
+ tool: null,
204
+ command: null,
205
+ message: `expected artifact missing: ${a.path} (${a.absolute})`,
206
+ detail: asDetail(a),
207
+ };
208
+ }
209
+ /**
210
+ * Build blockers from gate-log entries.
211
+ *
212
+ * When an entry carries `abort_reason: 'max_denials'`, emit an extra blocker
213
+ * reporting that the job was aborted after reaching the denial limit.
214
+ */
215
+ export function blockersFromGateLog(log) {
216
+ let entries;
217
+ if (typeof log === 'string') {
218
+ entries = [];
219
+ for (const line of log.split('\n')) {
220
+ const trimmed = line.trim();
221
+ if (!trimmed)
222
+ continue;
223
+ try {
224
+ const parsed = JSON.parse(trimmed);
225
+ if (parsed && typeof parsed === 'object') {
226
+ entries.push(parsed);
227
+ }
228
+ }
229
+ catch {
230
+ // tolerate malformed lines
231
+ }
232
+ }
233
+ }
234
+ else {
235
+ entries = [...log];
236
+ }
237
+ const maxDenialEntry = entries.find((e) => e.abort_reason === 'max_denials');
238
+ if (!maxDenialEntry)
239
+ return [];
240
+ const denialCount = entries.filter((e) => e.decision === 'deny').length;
241
+ return [
242
+ {
243
+ source: 'gate',
244
+ actionable: true,
245
+ remedy: 'raise max_denials or fix the prompt so the job stops retrying denied actions',
246
+ blocks_outcome: true,
247
+ tool: null,
248
+ command: null,
249
+ message: `job aborted after ${denialCount} gate denials (max_denials)`,
250
+ detail: asDetail(maxDenialEntry),
251
+ },
252
+ ];
253
+ }
254
+ /**
255
+ * The "no project ceiling" line for a finished job, or null. Said only when a
256
+ * gate blocker's remedy is a rule string — exactly what a ceiling `allow` /
257
+ * `exceptions` entry would open — and the policy recorded that no ceiling
258
+ * file existed. A foreign hook's denial (no rule), containment, unsupported
259
+ * tools and sandbox blocks are not the ceiling's business, so they stay quiet.
260
+ * Legacy job files without `ceiling_present` never trigger it.
261
+ */
262
+ export function ceilingAbsenceWarning(policy, blockers) {
263
+ if (!policy || policy.ceiling_present !== false)
264
+ return null;
265
+ const opensWithRule = blockers.some((b) => b.source === 'gate' && b.actionable && typeof b.remedy === 'string' && /^\w+\(/.test(b.remedy));
266
+ return opensWithRule ? ceilingAbsenceHint(policy.ceiling_path ?? null) : null;
267
+ }
268
+ /**
269
+ * Pre-flight blockers from a resolved policy: every `permissions.allow` entry
270
+ * the profile ceiling refused, every `permissions.read_roots` entry the
271
+ * human ceiling refused, plus the collapse case.
272
+ *
273
+ * `blocks_outcome` is false throughout — there is no job yet, so there is no
274
+ * outcome to block; these never reach `verification`.
275
+ */
276
+ export function policyCeilingBlockers(policy) {
277
+ const blockers = policy.rejected_allow.map((rule) => ({
278
+ source: 'policy_ceiling',
279
+ actionable: true,
280
+ remedy: `drop ${rule} from permissions.allow, or start on a profile whose ceiling covers it`,
281
+ blocks_outcome: false,
282
+ tool: null,
283
+ command: null,
284
+ message: `permissions.allow entry ${rule} is outside the ${policy.profile} ceiling and was dropped; clients can only narrow a profile, never widen it`,
285
+ detail: { rule, profile: policy.profile },
286
+ }));
287
+ for (const dir of policy.rejected_read_roots) {
288
+ blockers.push({
289
+ source: 'policy_ceiling',
290
+ actionable: true,
291
+ remedy: `add a glob covering ${dir} to the project ceiling's read_roots, in ~/.agy-worker/projects/<hash>/policy.json — or drop it from permissions.read_roots`,
292
+ blocks_outcome: false,
293
+ tool: null,
294
+ command: null,
295
+ message: `permissions.read_roots entry ${dir} matched no glob in the project ceiling's read_roots, so it was dropped`,
296
+ detail: { dir, profile: policy.profile },
297
+ });
298
+ }
299
+ // The trap this exists for: `allow` is the *intersection* of the request with
300
+ // the ceiling, so a request that the ceiling refuses wholesale leaves `allow`
301
+ // empty — taking the profile's own defaults (read/write, git, pytest …) with
302
+ // it. Measured: a request of three build commands collapsed a general_worker
303
+ // job's allow list to [] and nothing in the reply said so.
304
+ if (policy.allow.length === 0) {
305
+ blockers.push({
306
+ source: 'policy_ceiling',
307
+ actionable: true,
308
+ remedy: 'start again with no permissions.allow at all, which restores the full profile ceiling',
309
+ blocks_outcome: false,
310
+ tool: null,
311
+ command: null,
312
+ message: `the effective allow list is empty: allow is intersected with the ${policy.profile} ceiling, so a fully rejected request also drops the profile's own default allowances. Nothing is explicitly allowed on this job.`,
313
+ detail: { profile: policy.profile, rejected_allow: policy.rejected_allow },
314
+ });
315
+ }
316
+ return blockers;
317
+ }
318
+ /**
319
+ * One warning line per blocker. The only renderer — a caller must never have to
320
+ * infer "can I fix this" from prose wording.
321
+ */
322
+ export function renderBlocker(b) {
323
+ const where = b.tool ? ` [${b.tool}${b.command ? `: ${b.command}` : ''}]` : '';
324
+ const tail = b.actionable
325
+ ? ` — remedy: ${b.remedy ?? 'see message'}`
326
+ : ' — not actionable: no permissions.allow rule will change this.';
327
+ return `${LABEL[b.source]}${where}: ${b.message}${tail}`;
328
+ }
329
+ export function renderBlockers(blockers) {
330
+ return blockers.map(renderBlocker);
331
+ }
332
+ /**
333
+ * `1 gate denial (actionable), 1 sandbox block` — grouped by source, in first
334
+ * appearance order, so a headline says who refused without listing everything.
335
+ */
336
+ export function summarizeBlockers(blockers) {
337
+ const order = [];
338
+ const groups = new Map();
339
+ for (const b of blockers) {
340
+ let g = groups.get(b.source);
341
+ if (!g) {
342
+ g = { count: 0, actionable: 0 };
343
+ groups.set(b.source, g);
344
+ order.push(b.source);
345
+ }
346
+ g.count++;
347
+ if (b.actionable)
348
+ g.actionable++;
349
+ }
350
+ return order
351
+ .map((source) => {
352
+ const g = groups.get(source);
353
+ const label = `${g.count} ${LABEL[source]}${g.count === 1 ? '' : 's'}`;
354
+ return g.actionable > 0 ? `${label} (actionable)` : label;
355
+ })
356
+ .join(', ');
357
+ }
358
+ export function countActionable(blockers) {
359
+ return blockers.filter((b) => b.actionable).length;
360
+ }
361
+ /** The single predicate `outcome` is decided by. */
362
+ export function hasOutcomeBlocker(blockers) {
363
+ return blockers.some((b) => b.blocks_outcome);
364
+ }
365
+ /**
366
+ * Describe a resolved policy in the same vocabulary a finished job is judged
367
+ * in, for both the `dry_run` and the real `agy_start` reply.
368
+ *
369
+ * Built in one place so the two replies cannot drift: before 0.1.1 the real
370
+ * reply said nothing at all about permissions, and a caller whose
371
+ * `permissions.allow` had been rejected wholesale had no way to notice.
372
+ */
373
+ export function describePolicy(policy) {
374
+ const blockers = policyCeilingBlockers(policy);
375
+ return {
376
+ policy_summary: {
377
+ profile: policy.profile,
378
+ allow_count: policy.allow.length,
379
+ bypass_sandbox: policy.bypass_sandbox,
380
+ sandbox_forced_by: policy.sandbox_forced_by,
381
+ sandbox: policy.sandbox,
382
+ sandbox_source: policy.sandbox_source,
383
+ add_dirs: policy.add_dirs,
384
+ add_dirs_source: policy.add_dirs_source,
385
+ },
386
+ blockers,
387
+ warnings: [...renderBlockers(blockers), ...(policy.warnings ?? [])],
388
+ };
389
+ }
390
+ //# sourceMappingURL=blockers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blockers.js","sourceRoot":"","sources":["../../src/broker/blockers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import type { AgentStatus, ContractStatus, Lifecycle, Outcome, Verification } from '../contract/types.js';
2
+ /**
3
+ * Decide `outcome` and `headline` — the broker's verdict, kept strictly separate
4
+ * from agy's self-report.
5
+ *
6
+ * ⚠ Neither `exit_code === 0` nor `status === 'SUCCESS'` may be treated as
7
+ * success on its own. Measured: a hook denial and a sandbox network block both
8
+ * produce exit 0 and SUCCESS. `verified_success` requires that nothing
9
+ * was blocked *and* every expected artifact exists.
10
+ */
11
+ export interface OutcomeInput {
12
+ lifecycle: Lifecycle;
13
+ exitCode: number | null;
14
+ /** agy's own claim. An input, never the decision. */
15
+ agentStatus: AgentStatus;
16
+ verification: Verification;
17
+ /** Set when the deadline fired. */
18
+ timedOut: boolean;
19
+ /** Set when `agy_cancel` killed it. */
20
+ canceled: boolean;
21
+ /**
22
+ * I4: `state.json.gate_confirmed
23
+ * === false` — the runner's own watchdog killed the job because
24
+ * `gate-log.jsonl` never received a line within 3s of the first tool step, so
25
+ * `hooks.json` never actually loaded and nothing before the kill was
26
+ * policy-checked at all. Forces `process_error`, never `blocked`: a
27
+ * `blocked` verdict implies our gate ran and refused something, which is
28
+ * exactly what did not happen here.
29
+ */
30
+ gateMissing: boolean;
31
+ /** Set when the runner vanished without writing `exit_code`. */
32
+ runnerLost: boolean;
33
+ /** Set when the recorded pid now belongs to a different process. */
34
+ pidReused: boolean;
35
+ /** Whether the caller asked for anything verifiable at all. */
36
+ hadExpectations: boolean;
37
+ }
38
+ export interface OutcomeDecision {
39
+ outcome: Outcome;
40
+ contract_status: ContractStatus;
41
+ /** One sentence. The first thing a calling agent reads. */
42
+ headline: string;
43
+ /** Reasons `verified_success` was withheld, in caller-facing wording. */
44
+ warnings: string[];
45
+ }
46
+ /**
47
+ * Fixed precedence, most certain fact first:
48
+ *
49
+ * 1. `canceled` — an explicit `agy_cancel` always wins, even over a deadline
50
+ * that also happened to pass.
51
+ * 2. `timedOut` — the broker's own deadline enforcement fired.
52
+ * 3. `gateMissing` — our own watchdog killed the job because the gate never
53
+ * confirmed itself (I4); nothing before that point was policy-checked, so
54
+ * this outranks any verdict `verification.blockers` might otherwise imply.
55
+ * 4. `runnerLost` — the runner vanished without a trace; nothing below this
56
+ * point can be trusted, because there was nothing to observe it.
57
+ * 5. `pidReused` — the recorded pid is alive but is provably not our process.
58
+ * 6. Otherwise: agy actually ran to some conclusion. Judge it from the exit
59
+ * code and agy's own status first.
60
+ * 7. `hasOutcomeBlocker` — a confirmed refusal (gate, sandbox, missing
61
+ * artifact) is `blocked`. Blockers outrank `verify` here on purpose:
62
+ * "who refused" is a different
63
+ * question from "did the check pass", and a job that was denied something
64
+ * is `blocked` even if its `verify_command` also happened to fail.
65
+ * 8. `verify` — PR6. A configured `verify_command` that exited non-zero or hit
66
+ * its own `verify_timeout_ms` is a job **failure**, never a `Blocker` (that
67
+ * vocabulary means "who refused", and nothing refused here) — so this reads
68
+ * `verification.verify` directly rather than going through
69
+ * `hasOutcomeBlocker`.
70
+ * 9. Otherwise `verified_success` — either because `expected_artifacts`/
71
+ * `json_schema` were requested and all satisfied, or because a configured
72
+ * `verify_command` already passed (having survived step 8): a passing
73
+ * check counts as "actually verified" on its own, even with no
74
+ * `expected_artifacts` at all. With neither, `success_unverified`.
75
+ */
76
+ export declare function decideOutcome(input: OutcomeInput): OutcomeDecision;
77
+ /** True when every check that could run did run and did pass. */
78
+ export declare function isVerifiedSuccess(input: OutcomeInput): boolean;
79
+ /**
80
+ * One sentence that, together with the log tail, is enough to choose the next
81
+ * action. It states what the broker concluded and why — never a
82
+ * paraphrase of agy's own response text.
83
+ */
84
+ export declare function buildHeadline(outcome: Outcome, input: OutcomeInput): string;
85
+ //# sourceMappingURL=outcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outcome.d.ts","sourceRoot":"","sources":["../../src/broker/outcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,SAAS,EACT,OAAO,EACP,YAAY,EACb,MAAM,sBAAsB,CAAA;AAG7B;;;;;;;;GAQG;AAEH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,SAAS,CAAA;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,qDAAqD;IACrD,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,YAAY,CAAA;IAC1B,mCAAmC;IACnC,QAAQ,EAAE,OAAO,CAAA;IACjB,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;;;;;OAQG;IACH,WAAW,EAAE,OAAO,CAAA;IACpB,gEAAgE;IAChE,UAAU,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAA;IAClB,+DAA+D;IAC/D,eAAe,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,EAAE,cAAc,CAAA;IAC/B,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;IAChB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,eAAe,CASlE;AAkCD,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAgD3E"}
@@ -0,0 +1,147 @@
1
+ import { hasOutcomeBlocker, summarizeBlockers } from './blockers.js';
2
+ /**
3
+ * Fixed precedence, most certain fact first:
4
+ *
5
+ * 1. `canceled` — an explicit `agy_cancel` always wins, even over a deadline
6
+ * that also happened to pass.
7
+ * 2. `timedOut` — the broker's own deadline enforcement fired.
8
+ * 3. `gateMissing` — our own watchdog killed the job because the gate never
9
+ * confirmed itself (I4); nothing before that point was policy-checked, so
10
+ * this outranks any verdict `verification.blockers` might otherwise imply.
11
+ * 4. `runnerLost` — the runner vanished without a trace; nothing below this
12
+ * point can be trusted, because there was nothing to observe it.
13
+ * 5. `pidReused` — the recorded pid is alive but is provably not our process.
14
+ * 6. Otherwise: agy actually ran to some conclusion. Judge it from the exit
15
+ * code and agy's own status first.
16
+ * 7. `hasOutcomeBlocker` — a confirmed refusal (gate, sandbox, missing
17
+ * artifact) is `blocked`. Blockers outrank `verify` here on purpose:
18
+ * "who refused" is a different
19
+ * question from "did the check pass", and a job that was denied something
20
+ * is `blocked` even if its `verify_command` also happened to fail.
21
+ * 8. `verify` — PR6. A configured `verify_command` that exited non-zero or hit
22
+ * its own `verify_timeout_ms` is a job **failure**, never a `Blocker` (that
23
+ * vocabulary means "who refused", and nothing refused here) — so this reads
24
+ * `verification.verify` directly rather than going through
25
+ * `hasOutcomeBlocker`.
26
+ * 9. Otherwise `verified_success` — either because `expected_artifacts`/
27
+ * `json_schema` were requested and all satisfied, or because a configured
28
+ * `verify_command` already passed (having survived step 8): a passing
29
+ * check counts as "actually verified" on its own, even with no
30
+ * `expected_artifacts` at all. With neither, `success_unverified`.
31
+ */
32
+ export function decideOutcome(input) {
33
+ const outcome = computeOutcome(input);
34
+ const headline = buildHeadline(outcome, input);
35
+ return {
36
+ outcome,
37
+ contract_status: input.verification.contract_status,
38
+ headline,
39
+ warnings: input.verification.warnings,
40
+ };
41
+ }
42
+ function computeOutcome(input) {
43
+ if (input.canceled)
44
+ return 'canceled';
45
+ if (input.timedOut)
46
+ return 'timed_out';
47
+ if (input.gateMissing)
48
+ return 'process_error';
49
+ if (input.runnerLost)
50
+ return 'process_error';
51
+ if (input.pidReused)
52
+ return 'orphaned';
53
+ const exitFailed = input.exitCode !== null && input.exitCode !== 0;
54
+ const agentFailed = input.agentStatus === 'ERROR';
55
+ if (exitFailed || agentFailed)
56
+ return 'failed';
57
+ // One predicate, defined once in `blockers.ts`. A gate denial, a Class 2
58
+ // sandbox block and a missing expected artifact all carry
59
+ // `blocks_outcome: true`; an agy-engine refusal and a plain tool error do
60
+ // not, exactly as before — but the rule now lives in one place instead of
61
+ // three special cases here.
62
+ if (hasOutcomeBlocker(input.verification.blockers))
63
+ return 'blocked';
64
+ // PR6: a configured `verify_command` that failed or timed out is a job
65
+ // failure, checked after blockers (a denial wins) and before the
66
+ // expected_artifacts / hadExpectations logic below.
67
+ const verify = input.verification.verify;
68
+ if (verify && (verify.exit_code !== 0 || verify.timed_out))
69
+ return 'failed';
70
+ // Reaching here with `verify` non-null means it passed (exit 0, not timed
71
+ // out) — that alone is "a check actually passed", so it counts toward
72
+ // verified_success even when nothing else (expected_artifacts/json_schema)
73
+ // was requested.
74
+ if (verify || input.hadExpectations)
75
+ return 'verified_success';
76
+ return 'success_unverified';
77
+ }
78
+ /** True when every check that could run did run and did pass. */
79
+ export function isVerifiedSuccess(input) {
80
+ return computeOutcome(input) === 'verified_success';
81
+ }
82
+ /**
83
+ * One sentence that, together with the log tail, is enough to choose the next
84
+ * action. It states what the broker concluded and why — never a
85
+ * paraphrase of agy's own response text.
86
+ */
87
+ export function buildHeadline(outcome, input) {
88
+ const blockers = input.verification.blockers;
89
+ const totalArtifacts = input.verification.expected_artifacts.length;
90
+ // Blockers that did not force the verdict are still worth a fragment: an
91
+ // agy-engine refusal is invisible everywhere else in the packet, and staying
92
+ // silent about it in a success headline is how a half-done job reads as done.
93
+ // It is a fragment, never the verdict.
94
+ const nonBlocking = blockers.filter((b) => !b.blocks_outcome);
95
+ const nonBlockingSuffix = nonBlocking.length > 0 ? ` (non-blocking: ${summarizeBlockers(nonBlocking)})` : '';
96
+ let headline;
97
+ switch (outcome) {
98
+ case 'verified_success':
99
+ headline = `verified success: exit 0, agy reported ${input.agentStatus}, ${totalArtifacts} expected artifact(s) confirmed, no permission or environment blocks.${nonBlockingSuffix}`;
100
+ break;
101
+ case 'success_unverified':
102
+ headline = `agy exited 0 and reported ${input.agentStatus}, but nothing verifiable (expected_artifacts, json_schema, or a passing verify_command) was requested — cannot confirm the work actually happened.${nonBlockingSuffix}`;
103
+ break;
104
+ case 'blocked': {
105
+ const blocking = blockers.filter((b) => b.blocks_outcome);
106
+ headline = `blocked despite exit ${input.exitCode ?? '?'} / status ${input.agentStatus}: ${summarizeBlockers(blocking)}.${nonBlockingSuffix}`;
107
+ break;
108
+ }
109
+ case 'failed':
110
+ headline =
111
+ input.agentStatus === 'ERROR'
112
+ ? `failed: agy reported ERROR (exit ${input.exitCode ?? '?'}).`
113
+ : `failed: agy exited ${input.exitCode ?? '?'}.`;
114
+ break;
115
+ case 'timed_out':
116
+ headline = 'timed out: the job exceeded its deadline and the process group was killed.';
117
+ break;
118
+ case 'canceled':
119
+ headline = 'canceled: agy_cancel stopped this job.';
120
+ break;
121
+ case 'process_error':
122
+ headline = input.gateMissing
123
+ ? 'process error: gate hook never fired; hooks.json was not loaded — the job was killed before any of its tool calls could be trusted.'
124
+ : 'process error: the runner vanished without recording an exit code.';
125
+ break;
126
+ case 'orphaned':
127
+ headline = 'orphaned: the recorded pid is alive but belongs to a different process (pid reuse); the job was abandoned.';
128
+ break;
129
+ }
130
+ return headline + verifyFragment(input);
131
+ }
132
+ /**
133
+ * ` verify: exit <code> (<duration>s).` / ` verify: timed out after <s>s.`,
134
+ * appended whenever `verify_command` ran (PR6 §6.3) — regardless of `outcome`,
135
+ * since a caller reading any headline should be able to see the check ran and
136
+ * how it went, not just when it happened to be the deciding fact.
137
+ */
138
+ function verifyFragment(input) {
139
+ const v = input.verification.verify;
140
+ if (!v)
141
+ return '';
142
+ const seconds = (v.duration_ms / 1000).toFixed(1);
143
+ return v.timed_out
144
+ ? ` verify: timed out after ${seconds}s.`
145
+ : ` verify: exit ${v.exit_code ?? '?'} (${seconds}s).`;
146
+ }
147
+ //# sourceMappingURL=outcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../src/broker/outcome.ts"],"names":[],"mappings":"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"}