@inhuman.tools/turn 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/LICENSE.md +7 -0
  2. package/dist/agent-calls/acceptance.d.ts +12 -0
  3. package/dist/agent-calls/acceptance.d.ts.map +1 -0
  4. package/dist/agent-calls/acceptance.js +64 -0
  5. package/dist/agent-calls/acceptance.js.map +1 -0
  6. package/dist/agent-calls/callbacks.d.ts +14 -0
  7. package/dist/agent-calls/callbacks.d.ts.map +1 -0
  8. package/dist/agent-calls/callbacks.js +59 -0
  9. package/dist/agent-calls/callbacks.js.map +1 -0
  10. package/dist/agent-calls/delivery.d.ts +7 -0
  11. package/dist/agent-calls/delivery.d.ts.map +1 -0
  12. package/dist/agent-calls/delivery.js +16 -0
  13. package/dist/agent-calls/delivery.js.map +1 -0
  14. package/dist/agent-calls/index.d.ts +7 -0
  15. package/dist/agent-calls/index.d.ts.map +1 -0
  16. package/dist/agent-calls/index.js +6 -0
  17. package/dist/agent-calls/index.js.map +1 -0
  18. package/dist/agent-calls/operator.d.ts +21 -0
  19. package/dist/agent-calls/operator.d.ts.map +1 -0
  20. package/dist/agent-calls/operator.js +78 -0
  21. package/dist/agent-calls/operator.js.map +1 -0
  22. package/dist/agent-calls/runtime.d.ts +99 -0
  23. package/dist/agent-calls/runtime.d.ts.map +1 -0
  24. package/dist/agent-calls/runtime.js +245 -0
  25. package/dist/agent-calls/runtime.js.map +1 -0
  26. package/dist/agent-calls/types.d.ts +31 -0
  27. package/dist/agent-calls/types.d.ts.map +1 -0
  28. package/dist/agent-calls/types.js +2 -0
  29. package/dist/agent-calls/types.js.map +1 -0
  30. package/dist/context/compact-floors.d.ts +43 -0
  31. package/dist/context/compact-floors.d.ts.map +1 -0
  32. package/dist/context/compact-floors.js +52 -0
  33. package/dist/context/compact-floors.js.map +1 -0
  34. package/dist/context/compact.d.ts +42 -0
  35. package/dist/context/compact.d.ts.map +1 -0
  36. package/dist/context/compact.js +155 -0
  37. package/dist/context/compact.js.map +1 -0
  38. package/dist/context/delivery.d.ts +13 -0
  39. package/dist/context/delivery.d.ts.map +1 -0
  40. package/dist/context/delivery.js +6 -0
  41. package/dist/context/delivery.js.map +1 -0
  42. package/dist/context/history.d.ts +66 -0
  43. package/dist/context/history.d.ts.map +1 -0
  44. package/dist/context/history.js +573 -0
  45. package/dist/context/history.js.map +1 -0
  46. package/dist/context/index.d.ts +10 -0
  47. package/dist/context/index.d.ts.map +1 -0
  48. package/dist/context/index.js +10 -0
  49. package/dist/context/index.js.map +1 -0
  50. package/dist/context/observations.d.ts +7 -0
  51. package/dist/context/observations.d.ts.map +1 -0
  52. package/dist/context/observations.js +11 -0
  53. package/dist/context/observations.js.map +1 -0
  54. package/dist/context/retention/defaults.d.ts +10 -0
  55. package/dist/context/retention/defaults.d.ts.map +1 -0
  56. package/dist/context/retention/defaults.js +47 -0
  57. package/dist/context/retention/defaults.js.map +1 -0
  58. package/dist/context/retention/index.d.ts +14 -0
  59. package/dist/context/retention/index.d.ts.map +1 -0
  60. package/dist/context/retention/index.js +21 -0
  61. package/dist/context/retention/index.js.map +1 -0
  62. package/dist/context/retention/narrow.d.ts +22 -0
  63. package/dist/context/retention/narrow.d.ts.map +1 -0
  64. package/dist/context/retention/narrow.js +140 -0
  65. package/dist/context/retention/narrow.js.map +1 -0
  66. package/dist/context/retention/render.d.ts +37 -0
  67. package/dist/context/retention/render.d.ts.map +1 -0
  68. package/dist/context/retention/render.js +149 -0
  69. package/dist/context/retention/render.js.map +1 -0
  70. package/dist/context/retention/resolve.d.ts +51 -0
  71. package/dist/context/retention/resolve.d.ts.map +1 -0
  72. package/dist/context/retention/resolve.js +254 -0
  73. package/dist/context/retention/resolve.js.map +1 -0
  74. package/dist/context/retention/schema.d.ts +345 -0
  75. package/dist/context/retention/schema.d.ts.map +1 -0
  76. package/dist/context/retention/schema.js +149 -0
  77. package/dist/context/retention/schema.js.map +1 -0
  78. package/dist/context/retention/stamp.d.ts +25 -0
  79. package/dist/context/retention/stamp.d.ts.map +1 -0
  80. package/dist/context/retention/stamp.js +103 -0
  81. package/dist/context/retention/stamp.js.map +1 -0
  82. package/dist/context/retention/step.d.ts +33 -0
  83. package/dist/context/retention/step.d.ts.map +1 -0
  84. package/dist/context/retention/step.js +74 -0
  85. package/dist/context/retention/step.js.map +1 -0
  86. package/dist/context/retention/view.d.ts +57 -0
  87. package/dist/context/retention/view.d.ts.map +1 -0
  88. package/dist/context/retention/view.js +121 -0
  89. package/dist/context/retention/view.js.map +1 -0
  90. package/dist/context/squeeze.d.ts +45 -0
  91. package/dist/context/squeeze.d.ts.map +1 -0
  92. package/dist/context/squeeze.js +182 -0
  93. package/dist/context/squeeze.js.map +1 -0
  94. package/dist/context/tools.d.ts +16 -0
  95. package/dist/context/tools.d.ts.map +1 -0
  96. package/dist/context/tools.js +73 -0
  97. package/dist/context/tools.js.map +1 -0
  98. package/dist/context/transcript.d.ts +151 -0
  99. package/dist/context/transcript.d.ts.map +1 -0
  100. package/dist/context/transcript.js +659 -0
  101. package/dist/context/transcript.js.map +1 -0
  102. package/dist/policy-processor/bypass.d.ts +95 -0
  103. package/dist/policy-processor/bypass.d.ts.map +1 -0
  104. package/dist/policy-processor/bypass.js +188 -0
  105. package/dist/policy-processor/bypass.js.map +1 -0
  106. package/dist/policy-processor/classifier.d.ts +47 -0
  107. package/dist/policy-processor/classifier.d.ts.map +1 -0
  108. package/dist/policy-processor/classifier.js +671 -0
  109. package/dist/policy-processor/classifier.js.map +1 -0
  110. package/dist/policy-processor/cluster.d.ts +7 -0
  111. package/dist/policy-processor/cluster.d.ts.map +1 -0
  112. package/dist/policy-processor/cluster.js +32 -0
  113. package/dist/policy-processor/cluster.js.map +1 -0
  114. package/dist/policy-processor/evidence.d.ts +26 -0
  115. package/dist/policy-processor/evidence.d.ts.map +1 -0
  116. package/dist/policy-processor/evidence.js +204 -0
  117. package/dist/policy-processor/evidence.js.map +1 -0
  118. package/dist/policy-processor/explain.d.ts +59 -0
  119. package/dist/policy-processor/explain.d.ts.map +1 -0
  120. package/dist/policy-processor/explain.js +224 -0
  121. package/dist/policy-processor/explain.js.map +1 -0
  122. package/dist/policy-processor/index.d.ts +6 -0
  123. package/dist/policy-processor/index.d.ts.map +1 -0
  124. package/dist/policy-processor/index.js +6 -0
  125. package/dist/policy-processor/index.js.map +1 -0
  126. package/dist/policy-processor/license.d.ts +23 -0
  127. package/dist/policy-processor/license.d.ts.map +1 -0
  128. package/dist/policy-processor/license.js +47 -0
  129. package/dist/policy-processor/license.js.map +1 -0
  130. package/dist/policy-processor/peel-cwd.d.ts +2 -0
  131. package/dist/policy-processor/peel-cwd.d.ts.map +1 -0
  132. package/dist/policy-processor/peel-cwd.js +2 -0
  133. package/dist/policy-processor/peel-cwd.js.map +1 -0
  134. package/dist/policy-processor/real-path.d.ts +7 -0
  135. package/dist/policy-processor/real-path.d.ts.map +1 -0
  136. package/dist/policy-processor/real-path.js +42 -0
  137. package/dist/policy-processor/real-path.js.map +1 -0
  138. package/dist/policy-processor/scope.d.ts +111 -0
  139. package/dist/policy-processor/scope.d.ts.map +1 -0
  140. package/dist/policy-processor/scope.js +629 -0
  141. package/dist/policy-processor/scope.js.map +1 -0
  142. package/dist/policy-processor/self-protect.d.ts +10 -0
  143. package/dist/policy-processor/self-protect.d.ts.map +1 -0
  144. package/dist/policy-processor/self-protect.js +48 -0
  145. package/dist/policy-processor/self-protect.js.map +1 -0
  146. package/dist/policy-processor/soft-deny.d.ts +75 -0
  147. package/dist/policy-processor/soft-deny.d.ts.map +1 -0
  148. package/dist/policy-processor/soft-deny.js +185 -0
  149. package/dist/policy-processor/soft-deny.js.map +1 -0
  150. package/dist/policy-processor/unattended-guard.d.ts +28 -0
  151. package/dist/policy-processor/unattended-guard.d.ts.map +1 -0
  152. package/dist/policy-processor/unattended-guard.js +145 -0
  153. package/dist/policy-processor/unattended-guard.js.map +1 -0
  154. package/dist/session/cancel.d.ts +14 -0
  155. package/dist/session/cancel.d.ts.map +1 -0
  156. package/dist/session/cancel.js +32 -0
  157. package/dist/session/cancel.js.map +1 -0
  158. package/dist/session/commands.d.ts +41 -0
  159. package/dist/session/commands.d.ts.map +1 -0
  160. package/dist/session/commands.js +241 -0
  161. package/dist/session/commands.js.map +1 -0
  162. package/dist/session/compact-actor.d.ts +23 -0
  163. package/dist/session/compact-actor.d.ts.map +1 -0
  164. package/dist/session/compact-actor.js +91 -0
  165. package/dist/session/compact-actor.js.map +1 -0
  166. package/dist/session/compact-env.d.ts +54 -0
  167. package/dist/session/compact-env.d.ts.map +1 -0
  168. package/dist/session/compact-env.js +345 -0
  169. package/dist/session/compact-env.js.map +1 -0
  170. package/dist/session/compact.d.ts +42 -0
  171. package/dist/session/compact.d.ts.map +1 -0
  172. package/dist/session/compact.js +68 -0
  173. package/dist/session/compact.js.map +1 -0
  174. package/dist/session/index.d.ts +9 -0
  175. package/dist/session/index.d.ts.map +1 -0
  176. package/dist/session/index.js +8 -0
  177. package/dist/session/index.js.map +1 -0
  178. package/dist/session/park.d.ts +16 -0
  179. package/dist/session/park.d.ts.map +1 -0
  180. package/dist/session/park.js +73 -0
  181. package/dist/session/park.js.map +1 -0
  182. package/dist/session/side-channel-reminder.d.ts +41 -0
  183. package/dist/session/side-channel-reminder.d.ts.map +1 -0
  184. package/dist/session/side-channel-reminder.js +132 -0
  185. package/dist/session/side-channel-reminder.js.map +1 -0
  186. package/dist/session/types.d.ts +44 -0
  187. package/dist/session/types.d.ts.map +1 -0
  188. package/dist/session/types.js +2 -0
  189. package/dist/session/types.js.map +1 -0
  190. package/dist/tool-catalog/catalog.d.ts +77 -0
  191. package/dist/tool-catalog/catalog.d.ts.map +1 -0
  192. package/dist/tool-catalog/catalog.js +380 -0
  193. package/dist/tool-catalog/catalog.js.map +1 -0
  194. package/dist/tool-catalog/index.d.ts +4 -0
  195. package/dist/tool-catalog/index.d.ts.map +1 -0
  196. package/dist/tool-catalog/index.js +3 -0
  197. package/dist/tool-catalog/index.js.map +1 -0
  198. package/dist/tool-catalog/recipes.d.ts +36 -0
  199. package/dist/tool-catalog/recipes.d.ts.map +1 -0
  200. package/dist/tool-catalog/recipes.js +121 -0
  201. package/dist/tool-catalog/recipes.js.map +1 -0
  202. package/dist/tool-catalog/types.d.ts +103 -0
  203. package/dist/tool-catalog/types.d.ts.map +1 -0
  204. package/dist/tool-catalog/types.js +5 -0
  205. package/dist/tool-catalog/types.js.map +1 -0
  206. package/dist/tool-runtime/approval/approved.d.ts +13 -0
  207. package/dist/tool-runtime/approval/approved.d.ts.map +1 -0
  208. package/dist/tool-runtime/approval/approved.js +20 -0
  209. package/dist/tool-runtime/approval/approved.js.map +1 -0
  210. package/dist/tool-runtime/approval/dist.d.ts +6 -0
  211. package/dist/tool-runtime/approval/dist.d.ts.map +1 -0
  212. package/dist/tool-runtime/approval/dist.js +28 -0
  213. package/dist/tool-runtime/approval/dist.js.map +1 -0
  214. package/dist/tool-runtime/approval/inbound.d.ts +7 -0
  215. package/dist/tool-runtime/approval/inbound.d.ts.map +1 -0
  216. package/dist/tool-runtime/approval/inbound.js +48 -0
  217. package/dist/tool-runtime/approval/inbound.js.map +1 -0
  218. package/dist/tool-runtime/approval/unattended.d.ts +8 -0
  219. package/dist/tool-runtime/approval/unattended.d.ts.map +1 -0
  220. package/dist/tool-runtime/approval/unattended.js +23 -0
  221. package/dist/tool-runtime/approval/unattended.js.map +1 -0
  222. package/dist/tool-runtime/blob-port.d.ts +46 -0
  223. package/dist/tool-runtime/blob-port.d.ts.map +1 -0
  224. package/dist/tool-runtime/blob-port.js +47 -0
  225. package/dist/tool-runtime/blob-port.js.map +1 -0
  226. package/dist/tool-runtime/extra-root.d.ts +3 -0
  227. package/dist/tool-runtime/extra-root.d.ts.map +1 -0
  228. package/dist/tool-runtime/extra-root.js +14 -0
  229. package/dist/tool-runtime/extra-root.js.map +1 -0
  230. package/dist/tool-runtime/handler.d.ts +29 -0
  231. package/dist/tool-runtime/handler.d.ts.map +1 -0
  232. package/dist/tool-runtime/handler.js +460 -0
  233. package/dist/tool-runtime/handler.js.map +1 -0
  234. package/dist/tool-runtime/index.d.ts +14 -0
  235. package/dist/tool-runtime/index.d.ts.map +1 -0
  236. package/dist/tool-runtime/index.js +14 -0
  237. package/dist/tool-runtime/index.js.map +1 -0
  238. package/dist/tool-runtime/peel.d.ts +19 -0
  239. package/dist/tool-runtime/peel.d.ts.map +1 -0
  240. package/dist/tool-runtime/peel.js +74 -0
  241. package/dist/tool-runtime/peel.js.map +1 -0
  242. package/dist/tool-runtime/plugin-context.d.ts +13 -0
  243. package/dist/tool-runtime/plugin-context.d.ts.map +1 -0
  244. package/dist/tool-runtime/plugin-context.js +55 -0
  245. package/dist/tool-runtime/plugin-context.js.map +1 -0
  246. package/dist/tool-runtime/recall.d.ts +18 -0
  247. package/dist/tool-runtime/recall.d.ts.map +1 -0
  248. package/dist/tool-runtime/recall.js +100 -0
  249. package/dist/tool-runtime/recall.js.map +1 -0
  250. package/dist/tool-runtime/run-system-tool.d.ts +13 -0
  251. package/dist/tool-runtime/run-system-tool.d.ts.map +1 -0
  252. package/dist/tool-runtime/run-system-tool.js +230 -0
  253. package/dist/tool-runtime/run-system-tool.js.map +1 -0
  254. package/dist/tool-runtime/tool-error.d.ts +6 -0
  255. package/dist/tool-runtime/tool-error.d.ts.map +1 -0
  256. package/dist/tool-runtime/tool-error.js +6 -0
  257. package/dist/tool-runtime/tool-error.js.map +1 -0
  258. package/dist/tool-runtime/worker-tools.d.ts +8 -0
  259. package/dist/tool-runtime/worker-tools.d.ts.map +1 -0
  260. package/dist/tool-runtime/worker-tools.js +65 -0
  261. package/dist/tool-runtime/worker-tools.js.map +1 -0
  262. package/dist/tool-runtime/write-admit.d.ts +21 -0
  263. package/dist/tool-runtime/write-admit.d.ts.map +1 -0
  264. package/dist/tool-runtime/write-admit.js +89 -0
  265. package/dist/tool-runtime/write-admit.js.map +1 -0
  266. package/package.json +114 -3
  267. package/policy/auto-mode-ask.scm +201 -0
  268. package/policy/bypass-detect.prompt +55 -0
  269. package/policy/classifier-bash-incomplete.prompt +46 -0
  270. package/policy/classifier-bash.prompt +45 -0
  271. package/policy/classifier-mcp.prompt +45 -0
  272. package/policy/classifier-read.prompt +44 -0
  273. package/policy/classifier-write-outside.prompt +46 -0
  274. package/policy/classifier-write-risky.prompt +45 -0
  275. package/policy/classifier-write-workspace.prompt +46 -0
  276. package/policy/classifier-write.prompt +45 -0
  277. package/policy/soft-deny-round1.prompt +51 -0
  278. package/policy/soft-deny-round2.prompt +60 -0
  279. package/src/agent-calls/acceptance.ts +71 -0
  280. package/src/agent-calls/callbacks.ts +75 -0
  281. package/src/agent-calls/delivery.ts +15 -0
  282. package/src/agent-calls/index.ts +16 -0
  283. package/src/agent-calls/operator.ts +99 -0
  284. package/src/agent-calls/runtime.ts +310 -0
  285. package/src/agent-calls/types.ts +33 -0
  286. package/src/context/compact-floors.ts +76 -0
  287. package/src/context/compact.ts +176 -0
  288. package/src/context/delivery.ts +15 -0
  289. package/src/context/history.ts +725 -0
  290. package/src/context/index.ts +35 -0
  291. package/src/context/observations.ts +13 -0
  292. package/src/context/retention/defaults.ts +58 -0
  293. package/src/context/retention/index.ts +45 -0
  294. package/src/context/retention/narrow.ts +123 -0
  295. package/src/context/retention/render.ts +177 -0
  296. package/src/context/retention/resolve.ts +306 -0
  297. package/src/context/retention/schema.ts +173 -0
  298. package/src/context/retention/stamp.ts +121 -0
  299. package/src/context/retention/step.ts +85 -0
  300. package/src/context/retention/view.ts +161 -0
  301. package/src/context/squeeze.ts +218 -0
  302. package/src/context/tools.ts +92 -0
  303. package/src/context/transcript.ts +705 -0
  304. package/src/policy-processor/README.md +43 -0
  305. package/src/policy-processor/bypass.ts +246 -0
  306. package/src/policy-processor/classifier.ts +711 -0
  307. package/src/policy-processor/cluster.ts +31 -0
  308. package/src/policy-processor/evidence.ts +214 -0
  309. package/src/policy-processor/explain.ts +310 -0
  310. package/src/policy-processor/index.ts +6 -0
  311. package/src/policy-processor/license.ts +45 -0
  312. package/src/policy-processor/peel-cwd.ts +1 -0
  313. package/src/policy-processor/real-path.ts +41 -0
  314. package/src/policy-processor/scope.ts +655 -0
  315. package/src/policy-processor/self-protect.ts +46 -0
  316. package/src/policy-processor/soft-deny.ts +225 -0
  317. package/src/policy-processor/unattended-guard.ts +158 -0
  318. package/src/session/cancel.ts +34 -0
  319. package/src/session/commands.ts +292 -0
  320. package/src/session/compact-actor.ts +132 -0
  321. package/src/session/compact-env.ts +539 -0
  322. package/src/session/compact.ts +95 -0
  323. package/src/session/index.ts +18 -0
  324. package/src/session/park.ts +73 -0
  325. package/src/session/side-channel-reminder.ts +148 -0
  326. package/src/session/types.ts +50 -0
  327. package/src/tool-catalog/catalog.ts +282 -0
  328. package/src/tool-catalog/index.ts +26 -0
  329. package/src/tool-catalog/recipes.ts +154 -0
  330. package/src/tool-catalog/types.ts +110 -0
  331. package/src/tool-runtime/approval/approved.ts +20 -0
  332. package/src/tool-runtime/approval/dist.ts +29 -0
  333. package/src/tool-runtime/approval/inbound.ts +50 -0
  334. package/src/tool-runtime/approval/unattended.ts +24 -0
  335. package/src/tool-runtime/blob-port.ts +64 -0
  336. package/src/tool-runtime/extra-root.ts +16 -0
  337. package/src/tool-runtime/handler.ts +576 -0
  338. package/src/tool-runtime/index.ts +21 -0
  339. package/src/tool-runtime/peel.ts +91 -0
  340. package/src/tool-runtime/plugin-context.ts +68 -0
  341. package/src/tool-runtime/recall.ts +104 -0
  342. package/src/tool-runtime/run-system-tool.ts +273 -0
  343. package/src/tool-runtime/tool-error.ts +6 -0
  344. package/src/tool-runtime/worker-tools.ts +76 -0
  345. package/src/tool-runtime/write-admit.ts +90 -0
  346. package/README.md +0 -3
@@ -0,0 +1,671 @@
1
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
+ var useValue = arguments.length > 2;
3
+ for (var i = 0; i < initializers.length; i++) {
4
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
5
+ }
6
+ return useValue ? value : void 0;
7
+ };
8
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
+ var _, done = false;
14
+ for (var i = decorators.length - 1; i >= 0; i--) {
15
+ var context = {};
16
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
+ if (kind === "accessor") {
21
+ if (result === void 0) continue;
22
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
+ if (_ = accept(result.get)) descriptor.get = _;
24
+ if (_ = accept(result.set)) descriptor.set = _;
25
+ if (_ = accept(result.init)) initializers.unshift(_);
26
+ }
27
+ else if (_ = accept(result)) {
28
+ if (kind === "field") initializers.unshift(_);
29
+ else descriptor[key] = _;
30
+ }
31
+ }
32
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
+ done = true;
34
+ };
35
+ /**
36
+ * Remainder-classifier handle. Auto ask (floor ask + hatch). Cache is
37
+ * remainder skip. Empty connection or model gates the classifier, not
38
+ * identity — `dotprompt/run` requires both, and a run plan with one
39
+ * missing retries the invariant forever.
40
+ * `start` owns the run-plan reaction; minting does not begin I/O.
41
+ * Eligibility lives on the scope key set — this handle re-runs when the
42
+ * plan inputs change. Timer ticks are events (backoff). Floor `block`
43
+ * never mints these. Cluster ports share this handle. Classifier
44
+ * prompt files load on first require, not at import.
45
+ */
46
+ import { readFileSync } from "node:fs";
47
+ import path from "node:path";
48
+ import { fileURLToPath } from "node:url";
49
+ import { disposeRunContext, execState, jsToScheme, makeCallCtx, parse, toJS, } from "@here.build/arrival";
50
+ import { applyCallback } from "@here.build/arrival/host-internals";
51
+ import { ASymbol } from "@here.build/arrival/reflect-internals";
52
+ import { loaderFromResolver } from "@here.build/arrival-modules";
53
+ import { DefaultedMap } from "@here.build/collections";
54
+ import { promptIsolatedCapability } from "@inhuman.tools/env-capability-module-prompt";
55
+ import { fileQuestion, isJsonSchemaUnsupported, JUDGE_FAILS_FINAL, MCPToolJob, clusterOf, ruleStampFor, workspacePath, } from "@inhuman.tools/harness-model";
56
+ import { classifierResources, policyEnvCapability } from "@inhuman.tools/policy/floor";
57
+ import { parseTirithEvidence, tirithCoverage } from "@inhuman.tools/policy-tirith";
58
+ import { action, comparer, observable, reaction, runInAction } from "mobx";
59
+ import { classifierArgs } from "./cluster.js";
60
+ import { evidenceCuts, evidenceIdentity } from "./evidence.js";
61
+ import { buildSoftDenySections, identityTokens, parseSoftDenyVote, } from "./soft-deny.js";
62
+ function trace(name, message) {
63
+ const debug = process.env.DEBUG;
64
+ if (debug == null)
65
+ return;
66
+ const parts = debug.trim() === "" ? ["*"] : debug.split(/[\s,]+/).filter((part) => part !== "");
67
+ const on = parts.some((part) => {
68
+ if (part.startsWith("-"))
69
+ return false;
70
+ if (part === "*" || part === name)
71
+ return true;
72
+ if (part.endsWith("*"))
73
+ return name.startsWith(part.slice(0, -1));
74
+ return name.startsWith(`${part}:`);
75
+ });
76
+ if (!on)
77
+ return;
78
+ process.stderr.write(`${name} ${message}\n`);
79
+ }
80
+ /**
81
+ * Every remainder attempt is deadline-bounded. The first is short because the
82
+ * first vote is what the human is waiting on — a miss must open the approval
83
+ * window fast. Retries get longer: a slow-but-live model may still deliver a
84
+ * late allow, and the loop never gives up (overnight runs rely on that). But no
85
+ * attempt may run unbounded — with `scope.cap` a stuck classifier squatting a slot
86
+ * starves every job behind it, which is the hang.
87
+ *
88
+ * Below the stall timer on purpose: a live stream that trickles a token every
89
+ * few minutes resets the stall timer forever, so the wall clock is the only
90
+ * bound that fires.
91
+ */
92
+ export const FIRST_ATTEMPT_MS = 12_000;
93
+ export const RETRY_ATTEMPT_MS = 5 * 60_000;
94
+ function voteOf(value) {
95
+ switch (typeof value) {
96
+ case "string":
97
+ switch (value) {
98
+ case "allow":
99
+ case "deny":
100
+ return { decision: value };
101
+ default:
102
+ return value.trim() === "" ? { fail: "classifier did not vote" } : { fail: value };
103
+ }
104
+ case "object": {
105
+ if (value === null || Array.isArray(value))
106
+ return { fail: "classifier did not vote" };
107
+ const rec = value;
108
+ const decision = rec.decision ?? rec[":decision"];
109
+ switch (decision) {
110
+ case "allow":
111
+ case "deny":
112
+ return { decision };
113
+ default:
114
+ return { fail: "classifier did not vote" };
115
+ }
116
+ }
117
+ default:
118
+ return { fail: "classifier did not vote" };
119
+ }
120
+ }
121
+ async function applyClassifier(runCtx, fn, args) {
122
+ const boxed = applyCallback(fn, args.map((value) => jsToScheme(runCtx, value)), makeCallCtx(runCtx));
123
+ const signal = runCtx.signal;
124
+ if (signal === undefined || signal.aborted)
125
+ return toJS(await boxed);
126
+ const gate = new AbortController();
127
+ const winner = AbortSignal.any([signal, gate.signal]);
128
+ const abort = new Promise((_resolve, reject) => {
129
+ winner.addEventListener("abort", () => {
130
+ reject(signal.reason instanceof Error ? signal.reason : new Error("classifier aborted"));
131
+ }, { once: true });
132
+ });
133
+ try {
134
+ return toJS(await Promise.race([Promise.resolve(boxed), abort]));
135
+ }
136
+ finally {
137
+ gate.abort();
138
+ }
139
+ }
140
+ const here = path.dirname(fileURLToPath(import.meta.url));
141
+ const classifierCache = new DefaultedMap((p) => {
142
+ switch (p) {
143
+ case "classifier-bash.prompt":
144
+ return readFileSync(path.join(here, "../../policy/classifier-bash.prompt"), "utf8");
145
+ case "classifier-read.prompt":
146
+ return readFileSync(path.join(here, "../../policy/classifier-read.prompt"), "utf8");
147
+ case "classifier-write.prompt":
148
+ return readFileSync(path.join(here, "../../policy/classifier-write.prompt"), "utf8");
149
+ case "classifier-write-outside.prompt":
150
+ return readFileSync(path.join(here, "../../policy/classifier-write-outside.prompt"), "utf8");
151
+ case "classifier-write-workspace.prompt":
152
+ return readFileSync(path.join(here, "../../policy/classifier-write-workspace.prompt"), "utf8");
153
+ case "classifier-write-risky.prompt":
154
+ return readFileSync(path.join(here, "../../policy/classifier-write-risky.prompt"), "utf8");
155
+ case "classifier-mcp.prompt":
156
+ return readFileSync(path.join(here, "../../policy/classifier-mcp.prompt"), "utf8");
157
+ case "classifier-bash-incomplete.prompt":
158
+ return readFileSync(path.join(here, "../../policy/classifier-bash-incomplete.prompt"), "utf8");
159
+ case "soft-deny-round1.prompt":
160
+ return readFileSync(path.join(here, "../../policy/soft-deny-round1.prompt"), "utf8");
161
+ case "soft-deny-round2.prompt":
162
+ return readFileSync(path.join(here, "../../policy/soft-deny-round2.prompt"), "utf8");
163
+ case "bypass-detect.prompt":
164
+ return readFileSync(path.join(here, "../../policy/bypass-detect.prompt"), "utf8");
165
+ default:
166
+ throw new Error(`no such file: ${p}`);
167
+ }
168
+ });
169
+ export const classifierFiles = loaderFromResolver((p) => classifierCache.get(p));
170
+ export function classifierRoute(kvs, opts) {
171
+ const fallback = opts?.fallback ?? {};
172
+ const connection = (kvs.judgeConnection || fallback.judgeConnection || kvs.connection || "").trim();
173
+ const judgeModel = (kvs.judgeModel || fallback.judgeModel || "").trim();
174
+ const configured = connection !== "" && opts?.configuredModel != null ? opts.configuredModel(connection).trim() : "";
175
+ const model = (judgeModel || configured || kvs.model || "").trim();
176
+ return { connection, model, key: `${connection}\n${model}` };
177
+ }
178
+ const programReads = observable.map({}, { deep: false });
179
+ const programReadsStarted = new Set();
180
+ function startProgramRead(source) {
181
+ if (programReadsStarted.has(source))
182
+ return;
183
+ programReadsStarted.add(source);
184
+ void parse(source).then((forms) => runInAction(() => programReads.set(source, { state: "forms", forms })), () => runInAction(() => programReads.set(source, { state: "broken" })));
185
+ }
186
+ /** One symbol. The reader has already dropped comments. A string `"allow"` is not this. */
187
+ function literalOf(forms) {
188
+ const form = forms.length === 1 ? forms[0] : undefined;
189
+ if (!(form instanceof ASymbol))
190
+ return undefined;
191
+ return form.__name__ === "allow" || form.__name__ === "deny" ? form.__name__ : undefined;
192
+ }
193
+ /** The owner's `describe` for an MCP call, from the session's layers (a project file's words are not included). */
194
+ function ownerDescription(job) {
195
+ if (job.authCluster !== "mcp")
196
+ return undefined;
197
+ return ruleStampFor(job.authorizationLayers, job.callSubject).describe;
198
+ }
199
+ function remainderPlan(scope, job, attempt) {
200
+ const source = scope.root.judgeAskProgram;
201
+ const route = classifierRoute(scope.root.kvs, {
202
+ fallback: scope.judgeFallbackKvs,
203
+ configuredModel: scope.judgeConfiguredModel,
204
+ });
205
+ const cuts = evidenceCuts(job);
206
+ const identity = evidenceIdentity(cuts);
207
+ const cached = scope.remainderHit(job, identity);
208
+ if (cached !== null)
209
+ return { kind: "cache", decision: cached, attempt };
210
+ const soft = job.judgeKind === "soft-deny";
211
+ const resolver = job.resolution?.resolver;
212
+ const params = resolver?.kind === "judge" ? resolver.params : {};
213
+ // No user words, no explicit request: nothing to ask a model.
214
+ if (soft && (cuts.userMessages ?? []).length === 0) {
215
+ return { kind: "verdict", decision: "deny", reason: "no user message asks for this", attempt };
216
+ }
217
+ const read = programReads.get(source);
218
+ if (read === undefined)
219
+ return { kind: "unread", source, attempt };
220
+ if (read.state === "forms") {
221
+ const decision = literalOf(read.forms);
222
+ // A literal program is the ask judge's shortcut. It is never a soft-deny license: the host must
223
+ // see the user's words, so only a literal deny applies here.
224
+ if (decision !== undefined && (!soft || decision === "deny"))
225
+ return { kind: "literal", decision, identity, attempt };
226
+ if (decision !== undefined)
227
+ return { kind: "noprogram", attempt };
228
+ // No forms is the empty result exec returns. A top-level define is #<void>, not empty.
229
+ if (read.forms.length === 0)
230
+ return { kind: "empty", attempt };
231
+ }
232
+ if (scope.schemaHaltKeys.has(route.key))
233
+ return { kind: "halt", attempt };
234
+ if (route.connection === "" || route.model === "")
235
+ return { kind: "noconnect", attempt };
236
+ if (source.trim() === "")
237
+ return { kind: "empty", attempt };
238
+ const base = {
239
+ kind: "run",
240
+ source,
241
+ connection: route.connection,
242
+ model: route.model,
243
+ attempt,
244
+ identity,
245
+ privileged: cuts.privileged,
246
+ priorTurn: cuts.priorTurn,
247
+ quarantined: cuts.quarantined,
248
+ policy: cuts.policy ?? "",
249
+ margin: params.margin ?? 0,
250
+ };
251
+ if (!soft)
252
+ return base;
253
+ const layer = job.resolution?.layer;
254
+ const rounds = (params.rounds ?? 2);
255
+ const unattended = job.unattended;
256
+ const userMessages = cuts.userMessages ?? [];
257
+ const agentAnswer = cuts.agentAnswer ?? "";
258
+ const votes = job.policyVotes;
259
+ const cluster = clusterOf(job.name);
260
+ const sections = buildSoftDenySections({
261
+ classes: job.policyClasses,
262
+ votes,
263
+ userMessages,
264
+ agentAnswer,
265
+ allowAgentAnswer: rounds === 2 && !unattended,
266
+ tool: job.name,
267
+ cluster,
268
+ args: job.args,
269
+ describe: ownerDescription(job),
270
+ });
271
+ const tokens = identityTokens({
272
+ cluster,
273
+ tool: job.name,
274
+ args: job.args,
275
+ votes,
276
+ serverToolName: job instanceof MCPToolJob ? job.serverToolName : undefined,
277
+ });
278
+ return {
279
+ ...base,
280
+ soft: {
281
+ sections,
282
+ rounds,
283
+ unattended,
284
+ hasAgentAnswer: sections.agentAnswerJson !== "",
285
+ callTier: "soft-deny",
286
+ callClasses: job.policyClasses.join(","),
287
+ callRules: votes.map((vote) => vote.ruleId).join(","),
288
+ check: {
289
+ userMessages,
290
+ agentAnswer,
291
+ identityTokens: tokens,
292
+ // Only a layer the human wrote may turn the quote check off.
293
+ requireQuote: !(params.requireQuote === false &&
294
+ (layer === "user" || layer === "session" || layer === "managed")),
295
+ margin: params.margin ?? 0,
296
+ },
297
+ },
298
+ };
299
+ }
300
+ /**
301
+ * One bypass-check attempt: the program's `bypass/judge` sink, applied once, under the per-attempt
302
+ * deadline. Throws on any failure (no connection, no sink, timeout, a crash); the caller decides what
303
+ * a failed check means. The parent job is the denied call that triggered the check, which is still open.
304
+ */
305
+ export async function runBypassJudge(scope, job, sections) {
306
+ const source = scope.root.judgeAskProgram;
307
+ const route = classifierRoute(scope.root.kvs, {
308
+ fallback: scope.judgeFallbackKvs,
309
+ configuredModel: scope.judgeConfiguredModel,
310
+ });
311
+ if (route.connection === "" || route.model === "")
312
+ throw new Error("no model connection");
313
+ if (scope.schemaHaltKeys.has(route.key))
314
+ throw new Error("json_schema unsupported");
315
+ if (source.trim() === "")
316
+ throw new Error("auto-mode classifier program is empty");
317
+ const attemptMs = scope.judgeDeadlineMs(0);
318
+ const abort = new AbortController();
319
+ const deadline = setTimeout(() => abort.abort(new Error(`bypass check timed out after ${attemptMs}ms`)), attemptMs);
320
+ deadline.unref();
321
+ try {
322
+ const cuts = evidenceCuts(job);
323
+ const state = await execState(source, {
324
+ capabilities: [policyEnvCapability, promptIsolatedCapability],
325
+ signal: abort.signal,
326
+ config: {
327
+ privileged: cuts.privileged,
328
+ taskJson: sections.taskJson,
329
+ restrictionsJson: sections.restrictionsJson,
330
+ callsJson: sections.callsJson,
331
+ hintsJson: sections.hintsJson,
332
+ notesJson: sections.notesJson,
333
+ loader: classifierFiles,
334
+ job,
335
+ connectionId: route.connection,
336
+ completionModel: route.model,
337
+ complete: scope.complete,
338
+ },
339
+ });
340
+ try {
341
+ const fn = classifierResources(state.runCtx).bypass;
342
+ if (fn === undefined)
343
+ throw new Error("no bypass/judge sink in the classifier program");
344
+ return await applyClassifier(state.runCtx, fn, []);
345
+ }
346
+ finally {
347
+ await disposeRunContext(state.runCtx);
348
+ }
349
+ }
350
+ catch (error) {
351
+ const text = error instanceof Error ? error.message : String(error);
352
+ if (isJsonSchemaUnsupported(text))
353
+ scope.schemaHaltKeys.add(route.key);
354
+ throw error;
355
+ }
356
+ finally {
357
+ clearTimeout(deadline);
358
+ if (!abort.signal.aborted)
359
+ abort.abort();
360
+ }
361
+ }
362
+ let ClassifierHandle = (() => {
363
+ let _instanceExtraInitializers = [];
364
+ let _attempt_decorators;
365
+ let _attempt_initializers = [];
366
+ let _attempt_extraInitializers = [];
367
+ let _start_decorators;
368
+ let _writeFail_decorators;
369
+ let _writeFailSticky_decorators;
370
+ return class ClassifierHandle {
371
+ static {
372
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
373
+ _attempt_decorators = [observable];
374
+ _start_decorators = [action];
375
+ _writeFail_decorators = [action];
376
+ _writeFailSticky_decorators = [action];
377
+ __esDecorate(this, null, _attempt_decorators, { kind: "accessor", name: "attempt", static: false, private: false, access: { has: obj => "attempt" in obj, get: obj => obj.attempt, set: (obj, value) => { obj.attempt = value; } }, metadata: _metadata }, _attempt_initializers, _attempt_extraInitializers);
378
+ __esDecorate(this, null, _start_decorators, { kind: "method", name: "start", static: false, private: false, access: { has: obj => "start" in obj, get: obj => obj.start }, metadata: _metadata }, null, _instanceExtraInitializers);
379
+ __esDecorate(this, null, _writeFail_decorators, { kind: "method", name: "writeFail", static: false, private: false, access: { has: obj => "writeFail" in obj, get: obj => obj.writeFail }, metadata: _metadata }, null, _instanceExtraInitializers);
380
+ __esDecorate(this, null, _writeFailSticky_decorators, { kind: "method", name: "writeFailSticky", static: false, private: false, access: { has: obj => "writeFailSticky" in obj, get: obj => obj.writeFailSticky }, metadata: _metadata }, null, _instanceExtraInitializers);
381
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
382
+ }
383
+ job = __runInitializers(this, _instanceExtraInitializers);
384
+ scope;
385
+ #attempt_accessor_storage = __runInitializers(this, _attempt_initializers, 0);
386
+ get attempt() { return this.#attempt_accessor_storage; }
387
+ set attempt(value) { this.#attempt_accessor_storage = value; }
388
+ #timer = __runInitializers(this, _attempt_extraInitializers);
389
+ #attemptAbort;
390
+ #stop;
391
+ #seq = 0;
392
+ constructor(job, scope) {
393
+ this.job = job;
394
+ this.scope = scope;
395
+ }
396
+ async #classifierApplyArgs() {
397
+ const args = classifierArgs(this.job.name, this.job.args);
398
+ if (args === undefined)
399
+ return undefined;
400
+ const cluster = clusterOf(this.job.name);
401
+ if (cluster === "write") {
402
+ const question = fileQuestion(this.job.name, this.job.args ?? undefined);
403
+ if (question == null)
404
+ return undefined;
405
+ const source = this.scope.root.policyProgram.trim() === "" ? this.scope.distSource() : this.scope.root.policyProgram;
406
+ const policy = await this.scope.compiledPolicy(source);
407
+ // A floor deny names its remainder kind; an ordinary in-tree ask is judged on the task.
408
+ const kind = policy.writeKind(workspacePath(this.scope.root.path, question)) ??
409
+ (this.job.policyResult === "ask" ? "workspace" : "outside");
410
+ return [...args, kind];
411
+ }
412
+ if (cluster === "mcp") {
413
+ const describe = ownerDescription(this.job);
414
+ if (describe === undefined)
415
+ return args;
416
+ // The program appends the second string under the operation: a labelled owner note, JSON-encoded.
417
+ return [
418
+ args[0],
419
+ `${String(args[1])}\nOWNER NOTE (written by the policy owner, not the agent): ${JSON.stringify(describe)}`,
420
+ ];
421
+ }
422
+ if (cluster === "bash") {
423
+ const report = parseTirithEvidence(this.job.policyEvidenceJson);
424
+ if (report != null && tirithCoverage(report) === "incomplete")
425
+ return [...args, "incomplete"];
426
+ }
427
+ return args;
428
+ }
429
+ start() {
430
+ if (this.#stop !== undefined || this.scope.closed)
431
+ return;
432
+ this.#stop = reaction(() => remainderPlan(this.scope, this.job, this.attempt), async (plan) => {
433
+ this.#clearTimer();
434
+ const seq = ++this.#seq;
435
+ const jobId = this.job.toolCallId || this.job.name;
436
+ trace("harness:judge", `plan ${plan.kind} job=${jobId} tool=${this.job.name} attempt=${plan.attempt}${plan.kind === "run" ? ` model=${plan.model}` : ""}`);
437
+ switch (plan.kind) {
438
+ case "cache":
439
+ runInAction(() => {
440
+ if (this.job.classifierResult !== null)
441
+ return;
442
+ this.job.classifierResult = plan.decision;
443
+ if (plan.decision === "deny" && this.job.judgeKind === "soft-deny") {
444
+ this.job.judgeReason = "already denied this turn";
445
+ }
446
+ });
447
+ return;
448
+ case "verdict":
449
+ runInAction(() => {
450
+ if (this.job.classifierResult !== null)
451
+ return;
452
+ this.job.classifierResult = plan.decision;
453
+ this.job.judgeReason = plan.reason;
454
+ });
455
+ return;
456
+ case "noprogram":
457
+ this.writeFailSticky("auto-mode classifier program has no soft-deny sink (soft-deny/<cluster>); a person decides");
458
+ return;
459
+ case "literal":
460
+ runInAction(() => {
461
+ if (this.job.classifierResult !== null)
462
+ return;
463
+ this.job.classifierResult = plan.decision;
464
+ this.scope.remainderStore(this.job, plan.identity, plan.decision);
465
+ });
466
+ return;
467
+ case "halt":
468
+ this.writeFailSticky("json_schema unsupported: auto mode needs a model that supports structured output");
469
+ return;
470
+ case "noconnect":
471
+ this.writeFail("no model connection");
472
+ return;
473
+ case "empty":
474
+ // An empty program can never vote. Abstain so the call reaches the human
475
+ // cover (and the diagnostic is shown) instead of waiting forever.
476
+ this.writeFailSticky("auto-mode classifier program is empty or unreadable (check autoModeClassifier in config.json)");
477
+ return;
478
+ case "unread":
479
+ startProgramRead(plan.source);
480
+ return;
481
+ case "run":
482
+ break;
483
+ }
484
+ const started = Date.now();
485
+ const attemptMs = this.scope.judgeDeadlineMs(plan.attempt);
486
+ this.#attemptAbort = new AbortController();
487
+ const attemptAbort = this.#attemptAbort;
488
+ const deadline = setTimeout(() => {
489
+ attemptAbort.abort(new Error(`classifier timed out after ${attemptMs}ms`));
490
+ }, attemptMs);
491
+ deadline.unref();
492
+ try {
493
+ let vote = { fail: "classifier did not vote" };
494
+ let state;
495
+ try {
496
+ state = await execState(plan.source, {
497
+ capabilities: [policyEnvCapability, promptIsolatedCapability],
498
+ signal: attemptAbort.signal,
499
+ config: {
500
+ privileged: plan.privileged,
501
+ priorTurn: plan.priorTurn,
502
+ quarantined: plan.quarantined,
503
+ policy: plan.policy,
504
+ riskMargin: plan.margin,
505
+ ...(plan.soft === undefined
506
+ ? {}
507
+ : {
508
+ floorJson: plan.soft.sections.floorJson,
509
+ userJson: plan.soft.sections.userJson,
510
+ callJson: plan.soft.sections.callJson,
511
+ agentAnswerJson: plan.soft.sections.agentAnswerJson,
512
+ callTier: plan.soft.callTier,
513
+ callClasses: plan.soft.callClasses,
514
+ callRules: plan.soft.callRules,
515
+ }),
516
+ loader: classifierFiles,
517
+ job: this.job,
518
+ connectionId: plan.connection,
519
+ completionModel: plan.model,
520
+ complete: this.scope.complete,
521
+ },
522
+ });
523
+ }
524
+ catch (error) {
525
+ vote = { fail: error instanceof Error ? error.message : String(error) };
526
+ }
527
+ if (state !== undefined) {
528
+ try {
529
+ const cluster = clusterOf(this.job.name);
530
+ if (plan.soft !== undefined) {
531
+ vote = await this.#softDeny(state.runCtx, cluster, plan.soft);
532
+ }
533
+ else {
534
+ const fn = classifierResources(state.runCtx)[cluster];
535
+ if (fn === undefined) {
536
+ vote = { fail: `no classifier/${cluster}` };
537
+ }
538
+ else {
539
+ const args = await this.#classifierApplyArgs();
540
+ vote =
541
+ args === undefined
542
+ ? { fail: "classifier did not vote" }
543
+ : voteOf(await applyClassifier(state.runCtx, fn, args));
544
+ }
545
+ }
546
+ }
547
+ finally {
548
+ await disposeRunContext(state.runCtx);
549
+ }
550
+ }
551
+ if (seq !== this.#seq) {
552
+ trace("harness:judge", `stale job=${jobId} ms=${Date.now() - started}`);
553
+ return;
554
+ }
555
+ runInAction(() => {
556
+ if (this.job.classifierResult !== null)
557
+ return;
558
+ if ("fail" in vote) {
559
+ trace("harness:judge", `fail job=${jobId} ms=${Date.now() - started} ${vote.fail}`);
560
+ if (isJsonSchemaUnsupported(vote.fail)) {
561
+ this.scope.schemaHaltKeys.add(`${plan.connection}\n${plan.model}`);
562
+ this.writeFailSticky(vote.fail);
563
+ return;
564
+ }
565
+ this.writeFail(vote.fail);
566
+ return;
567
+ }
568
+ trace("harness:judge", `vote ${vote.decision} job=${jobId} ms=${Date.now() - started}`);
569
+ this.job.classifierResult = vote.decision;
570
+ if (vote.reason !== undefined)
571
+ this.job.judgeReason = vote.reason;
572
+ if (vote.round !== undefined)
573
+ this.job.softDenyRound = vote.round;
574
+ this.scope.remainderStore(this.job, plan.identity, vote.decision);
575
+ });
576
+ }
577
+ catch (error) {
578
+ if (seq !== this.#seq)
579
+ return;
580
+ trace("harness:judge", `throw job=${jobId} ms=${Date.now() - started} ${error instanceof Error ? error.message : String(error)}`);
581
+ runInAction(() => {
582
+ const text = error instanceof Error ? error.message : String(error);
583
+ if (isJsonSchemaUnsupported(text)) {
584
+ this.scope.schemaHaltKeys.add(`${plan.connection}\n${plan.model}`);
585
+ this.writeFailSticky(text);
586
+ return;
587
+ }
588
+ this.writeFail(text);
589
+ });
590
+ }
591
+ finally {
592
+ clearTimeout(deadline);
593
+ if (!attemptAbort.signal.aborted)
594
+ attemptAbort.abort();
595
+ if (this.#attemptAbort === attemptAbort)
596
+ this.#attemptAbort = undefined;
597
+ }
598
+ }, { equals: comparer.structural, fireImmediately: true });
599
+ }
600
+ dispose() {
601
+ this.#seq += 1;
602
+ this.#clearTimer();
603
+ this.#attemptAbort?.abort();
604
+ this.#attemptAbort = undefined;
605
+ this.#stop?.();
606
+ this.#stop = undefined;
607
+ }
608
+ /**
609
+ * The soft-deny rounds. The program's sink is applied with the round; the host parses and verifies
610
+ * each vote (quote, identity of the action, risk x authorization). Round 2 runs only when round 1
611
+ * asked for the agent's last answer, the cell allows two rounds, one exists, and a person is watching.
612
+ */
613
+ async #softDeny(runCtx, cluster, plan) {
614
+ const fn = classifierResources(runCtx).softDeny[cluster];
615
+ if (fn === undefined)
616
+ return { fail: `no soft-deny/${cluster}` };
617
+ const first = parseSoftDenyVote(await this.#applySoft(runCtx, fn, 1), 1, plan.check);
618
+ const settle = (outcome, round) => {
619
+ if (outcome.kind === "fail")
620
+ return { fail: outcome.reason };
621
+ if (outcome.kind === "need-agent-answer")
622
+ return { decision: "deny", reason: outcome.reason, round };
623
+ return { decision: outcome.decision, reason: outcome.reason, round: outcome.round };
624
+ };
625
+ if (first.kind !== "need-agent-answer")
626
+ return settle(first, 1);
627
+ if (plan.rounds === 1 || plan.unattended || !plan.hasAgentAnswer) {
628
+ return {
629
+ decision: "deny",
630
+ reason: plan.hasAgentAnswer ? first.reason : "confirmation with no proposal to refer to",
631
+ round: 1,
632
+ };
633
+ }
634
+ return settle(parseSoftDenyVote(await this.#applySoft(runCtx, fn, 2), 2, plan.check), 2);
635
+ }
636
+ async #applySoft(runCtx, fn, round) {
637
+ return await applyClassifier(runCtx, fn, [round]);
638
+ }
639
+ writeFail(text) {
640
+ if (this.job.classifierResult !== null)
641
+ return;
642
+ if (text.trim() !== "")
643
+ this.job.classifierAbstain = text;
644
+ this.job.judgeFails += 1;
645
+ if (this.scope.closed)
646
+ return;
647
+ this.#clearTimer();
648
+ const n = this.attempt + 1;
649
+ this.#timer = setTimeout(action("judgeBackoff", () => {
650
+ this.#timer = undefined;
651
+ this.attempt = n;
652
+ }), this.scope.backoffMs(n));
653
+ }
654
+ writeFailSticky(text) {
655
+ if (this.job.classifierResult !== null)
656
+ return;
657
+ if (text.trim() !== "")
658
+ this.job.classifierAbstain = text;
659
+ this.job.judgeFails = JUDGE_FAILS_FINAL;
660
+ this.#clearTimer();
661
+ }
662
+ #clearTimer() {
663
+ if (this.#timer === undefined)
664
+ return;
665
+ clearTimeout(this.#timer);
666
+ this.#timer = undefined;
667
+ }
668
+ };
669
+ })();
670
+ export { ClassifierHandle };
671
+ //# sourceMappingURL=classifier.js.map