@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,310 @@
1
+ /**
2
+ * Call settlement and reclaim. Worker never Plexus-writes the caller.
3
+ * `completed` is kernel-only (respond / void / exceptional).
4
+ *
5
+ * Child calls reclaim their owned worker after the last callback row. Bot
6
+ * requests release their callback without reclaiming the bot. Leftovers become
7
+ * completed+orchestrated rejected even if approved. keepAlive true stretches
8
+ * the same loop. Two open rows: first respond is not final.
9
+ */
10
+ import {
11
+ AgentCallJob,
12
+ CallJob,
13
+ AgentCallbackJob,
14
+ AgentNotifyJob,
15
+ ApprovableJob,
16
+ ProgrammaticJob,
17
+ tapeCompletion,
18
+ demoteSteers,
19
+ SessionRoot,
20
+ SessionTurn,
21
+ ToolCallJob,
22
+ FailureOutcome,
23
+ cancelUnstarted,
24
+ type CallFailCode,
25
+ BotCallJob,
26
+ CompactJob,
27
+ CompletionJob,
28
+ ReACTChunk,
29
+ TurnApprovalNoteChunk,
30
+ TurnNotificationChunk,
31
+ TurnPromptChunk,
32
+ TurnSteerChunk,
33
+ } from "@inhuman.tools/harness-model";
34
+ import { Job, walkWork, WorkExpectation } from "@inhuman.tools/plexus-expectations";
35
+ import { runInAction } from "mobx";
36
+
37
+ import { shouldSkipTurnCancel } from "./acceptance.js";
38
+ import type { AgentRuntimeHost, CallsCatalog } from "./types.js";
39
+
40
+ function rejectLeaf(leaf: ApprovableJob, reason?: unknown): void {
41
+ if (leaf.completed || leaf.cancelled) return;
42
+ cancelUnstarted(leaf, reason);
43
+ if (leaf instanceof CallJob) return;
44
+ }
45
+
46
+ /** Omitted-spec session bit, or this bag's enclosing Call. Not every open Call. */
47
+ export function schemeDeskReadonly(session: SessionRoot, enclosingCall?: CallJob | null): boolean {
48
+ return session.agent.spec.readonly || enclosingCall?.readonly === true;
49
+ }
50
+
51
+ /**
52
+ * Honest host cancel / timeout. Completes the Call, deletes the worker row,
53
+ * does not fabricate a respond. First-write-wins: a later respond cannot
54
+ * replace this receipt.
55
+ */
56
+ export function completeCallExceptionally(
57
+ call: CallJob,
58
+ host: AgentRuntimeHost,
59
+ code: CallFailCode,
60
+ message = "",
61
+ abort?: (job: Job) => void,
62
+ ): void {
63
+ call.settleResult(new FailureOutcome({ code, message }));
64
+ const workerId = call.workerSessionId;
65
+ if (workerId === "") return;
66
+ const worker = host.scope.replica(workerId);
67
+ if (worker == null) return;
68
+ worker.root.removeAgentCallback(call.uuid);
69
+ // The request owns its receipt, not the independently managed bot process.
70
+ if (call instanceof BotCallJob) return;
71
+ if (!(call instanceof AgentCallJob)) return;
72
+ forceStopLoop(
73
+ worker.root,
74
+ abort ?? (host.scope.abortJob == null ? undefined : (leaf) => host.scope.abortJob?.(workerId, leaf)),
75
+ undefined,
76
+ (nested) => completeCallExceptionally(nested, host, "rejected"),
77
+ host.catalog,
78
+ );
79
+ }
80
+
81
+ /**
82
+ * keepAlive false + last row: leftovers on the originating turn become
83
+ * `cancelled` + completed with a rejected receipt. `approved` stays —
84
+ * it is a human write. Abort the turn so ReACT cannot mint further calls.
85
+ */
86
+ export function forceStopLoop(
87
+ root: SessionRoot,
88
+ abort?: (job: Job) => void,
89
+ except?: ApprovableJob,
90
+ cancelCall?: (call: CallJob) => void,
91
+ catalog?: CallsCatalog,
92
+ ): void {
93
+ if (root.agent.spec.keepAlive) return;
94
+ if (Object.keys(root.agentCallbacks).length > 0) return;
95
+ // Originating turn for `except`. A closed origin must not stop a successor head.
96
+ let turn: SessionTurn | null = except == null ? root.findRunningTurn() : null;
97
+ if (except != null) {
98
+ for (const live of root.liveTurns) {
99
+ for (const node of walkWork(live)) {
100
+ if (node === except) {
101
+ turn = live;
102
+ break;
103
+ }
104
+ }
105
+ if (turn != null) break;
106
+ }
107
+ }
108
+ if (turn == null) return;
109
+ closeTurn(turn, { kind: "forced", except }, { abort, cancelCall, catalog, session: root });
110
+ }
111
+
112
+ /**
113
+ * How a turn ends. Every ending closes what is open under the turn; the
114
+ * cause only says how the leaves are told and what the turn records.
115
+ *
116
+ * - `finish`: the reducer returned. Background work outlives the turn.
117
+ * - `cancel`: the user stopped it (`cancelRequested`). Acks with `cancelled`.
118
+ * - `broke`: the reducer threw, or a boundary refused. `detail` lands on `turn.broke`.
119
+ * - `rewound`: the turn left the live chain before or while it ran.
120
+ * - `forced`: the last Call row of a worker that must not outlive it.
121
+ */
122
+ export type TurnEnding =
123
+ | { readonly kind: "finish" }
124
+ | { readonly kind: "cancel" }
125
+ | { readonly kind: "broke"; readonly detail: string }
126
+ | { readonly kind: "rewound" }
127
+ | { readonly kind: "forced"; readonly except?: ApprovableJob };
128
+
129
+ export type CloseTurnOptions = {
130
+ readonly catalog?: CallsCatalog;
131
+ /** Reclaim a Call's worker before the Call is rejected. */
132
+ readonly releaseCall?: (call: CallJob) => void;
133
+ /** Complete a worker Call exceptionally before it is rejected (forced ending). */
134
+ readonly cancelCall?: (call: CallJob) => void;
135
+ /** Abort an in-flight body or executor. */
136
+ readonly abort?: (job: Job) => void;
137
+ /** End an open expectation through its actor, when it has one. */
138
+ readonly terminate?: (expectation: WorkExpectation) => void;
139
+ /**
140
+ * The session inbox. Passed, the close demotes the steers composed against
141
+ * this turn, but only while the turn is on the live chain: the close of a
142
+ * turn that is not touches nothing but itself.
143
+ */
144
+ readonly session?: SessionRoot;
145
+ };
146
+
147
+ /**
148
+ * THE turn close. The executor's `finally`, a cancel that finds no executor
149
+ * inside the turn, recovery of a cancelled turn, a forced stop and a rewound
150
+ * away turn all end a turn here, so the same obligations close in every case:
151
+ *
152
+ * 1. the failure, if any, is recorded on the turn (`broke`);
153
+ * 2. every open Job and expectation under the turn ends (a finish leaves
154
+ * them alone: background work outlives the turn);
155
+ * 3. the cancel ack (`cancelled`) follows a cancel request;
156
+ * 4. unclaimed steers composed against this turn move to the queue lane (also
157
+ * on cancel: Stop does not discard what the user already sent), only for a
158
+ * turn on the live chain;
159
+ * 5. `reducerExited` and `markComplete` last, in the same transaction, so no
160
+ * observer sees a closed turn with work still open under it.
161
+ *
162
+ * Idempotent: a second close sweeps what is still open and leaves the rest.
163
+ */
164
+ export function closeTurn(turn: SessionTurn, ending: TurnEnding, options: CloseTurnOptions = {}): void {
165
+ runInAction(() => {
166
+ const first = !turn.reducerExited;
167
+ if (ending.kind === "broke" && turn.broke === "")
168
+ turn.broke = ending.detail === "" ? "turn stopped" : ending.detail;
169
+ // A normal finish leaves what is open (background jobs, their attempts) to outlive the turn.
170
+ for (const node of ending.kind === "finish" ? [] : walkWork(turn)) {
171
+ if (node instanceof WorkExpectation) {
172
+ if (!node.isDone) endExpectation(node, options);
173
+ continue;
174
+ }
175
+ if (!(node instanceof Job) || node.completed) continue;
176
+ if (ending.kind === "forced") {
177
+ if (node === ending.except) continue;
178
+ endForced(node, options);
179
+ } else {
180
+ endCancelled(node, options);
181
+ }
182
+ }
183
+ const cancelled = ending.kind === "cancel" || turn.cancelRequested;
184
+ if (cancelled) turn.cancelled = true;
185
+ const session = options.session;
186
+ if (first && session != null && session.liveChain.includes(turn)) demoteSteers(session, turn.uuid);
187
+ turn.reducerExited = true;
188
+ turn.markComplete();
189
+ if (ending.kind === "forced" && first) options.abort?.(turn);
190
+ });
191
+ }
192
+
193
+ function endExpectation(expectation: WorkExpectation, options: CloseTurnOptions): void {
194
+ if (options.terminate != null) options.terminate(expectation);
195
+ if (!expectation.isDone) expectation.fail("cancel", "terminated with the turn", "null");
196
+ }
197
+
198
+ function endForced(node: Job, options: CloseTurnOptions): void {
199
+ if (node instanceof AgentCallbackJob || node instanceof AgentNotifyJob) return;
200
+ if (node instanceof CallJob && node.workerSessionId !== "") {
201
+ if (shouldSkipTurnCancel(node, options.catalog)) return;
202
+ options.cancelCall?.(node);
203
+ options.abort?.(node);
204
+ node.cancelled = true;
205
+ node.settleResult(new FailureOutcome({ code: "rejected", message: "rejected" }));
206
+ return;
207
+ }
208
+ if (node instanceof ApprovableJob) {
209
+ if (node.orchestrated) options.abort?.(node);
210
+ node.cancelled = true;
211
+ if (node instanceof CallJob) node.settleResult(new FailureOutcome({ code: "rejected", message: "rejected" }));
212
+ else if (node instanceof ToolCallJob && (node.receiptJson == null || node.receiptJson === "")) {
213
+ node.outcomeSnap = { ok: false, text: "rejected" };
214
+ }
215
+ node.markComplete();
216
+ return;
217
+ }
218
+ if (node instanceof ProgrammaticJob) {
219
+ options.abort?.(node);
220
+ node.outcomeSnap ??= { ok: false, text: "rejected" };
221
+ node.cancelled = true;
222
+ node.markComplete();
223
+ }
224
+ }
225
+
226
+ function endCancelled(node: Job, options: CloseTurnOptions): void {
227
+ if (node instanceof CallJob) {
228
+ if (shouldSkipTurnCancel(node, options.catalog)) return;
229
+ options.releaseCall?.(node);
230
+ rejectLeaf(node);
231
+ if (!node.completed) node.markComplete();
232
+ return;
233
+ }
234
+ if (node instanceof ApprovableJob) {
235
+ if (node.orchestrated) options.abort?.(node);
236
+ rejectLeaf(node);
237
+ if (!node.completed) node.markComplete();
238
+ return;
239
+ }
240
+ if (node instanceof ProgrammaticJob) {
241
+ if (node.orchestrated) options.abort?.(node);
242
+ node.outcomeSnap ??= { ok: false, text: "cancelled" };
243
+ node.cancelled = true;
244
+ node.markComplete();
245
+ }
246
+ }
247
+
248
+ /** Roster names plus live Call worker ids — reattach walks this, not a second index. */
249
+ export function namedWorkerIds(root: SessionRoot): readonly string[] {
250
+ const ids = new Set<string>(Object.values(root.botsRoster));
251
+ for (const node of root.motionWork()) {
252
+ if (node instanceof CallJob && node.workerSessionId !== "") ids.add(node.workerSessionId);
253
+ }
254
+ return [...ids];
255
+ }
256
+
257
+ /** Close a cancelled turn. Does not reclaim the worker. */
258
+ export function settleCancelledTurn(
259
+ turn: SessionTurn,
260
+ catalog?: CallsCatalog,
261
+ releaseCall?: (call: CallJob) => void,
262
+ session?: SessionRoot,
263
+ ): void {
264
+ closeTurn(turn, { kind: "cancel" }, { catalog, releaseCall, session });
265
+ }
266
+
267
+ /**
268
+ * The cell the loop had already shifted is still the live seat: an open
269
+ * completion or tool, or a loop token still owed a round (`queuedContinuation`).
270
+ * Claim cells appended behind the token stay claimed.
271
+ *
272
+ * A model round the park or a restart interrupted is not replayed: a
273
+ * CompletionJob is one attempt, and recovery closes it as unknown. The turn
274
+ * gets a fresh round (a new attempt) unless the user stopped it.
275
+ */
276
+ export function restorePendingRound(turn: SessionTurn): void {
277
+ if (turn.reducerExited || !turn.orchestrated || turn.completed) return;
278
+ let index = turn.doneWork.length - 1;
279
+ while (index >= 0 && isClaimCell(turn.doneWork[index])) index -= 1;
280
+ const cell = turn.doneWork[index];
281
+ if (cell == null || !interrupted(cell, turn)) return;
282
+ if (cell instanceof CompletionJob && cell.orchestrated) {
283
+ if (turn.cancelRequested) return;
284
+ runInAction(() => turn.enqueue(new ReACTChunk()));
285
+ return;
286
+ }
287
+ runInAction(() => {
288
+ turn.doneWork.splice(index, 1);
289
+ turn.queue.unshift(cell);
290
+ });
291
+ }
292
+
293
+ function isClaimCell(cell: unknown): boolean {
294
+ return (
295
+ cell instanceof TurnSteerChunk || cell instanceof TurnNotificationChunk || cell instanceof TurnApprovalNoteChunk
296
+ );
297
+ }
298
+
299
+ function interrupted(cell: unknown, turn: SessionTurn): boolean {
300
+ if (
301
+ cell instanceof TurnPromptChunk ||
302
+ (cell instanceof ReACTChunk && !cell.skipped) ||
303
+ (cell instanceof CompactJob && !cell.completed)
304
+ ) {
305
+ return turn.queuedContinuation;
306
+ }
307
+ const pew = tapeCompletion(cell);
308
+ if (pew != null) return !pew.isDone;
309
+ return (cell instanceof ProgrammaticJob || cell instanceof ToolCallJob) && !cell.completed;
310
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Catalog and replica surfaces this package observes.
3
+ * Creation/activation of sessions stays in runtime composition.
4
+ */
5
+ import type { SessionRoot } from "@inhuman.tools/harness-model";
6
+ import type { Job } from "@inhuman.tools/plexus-expectations";
7
+
8
+ import type { CatalogEntry } from "../tool-catalog/index.js";
9
+
10
+ export type CallsCatalog = {
11
+ catalogEntry(wireName: string): CatalogEntry | undefined;
12
+ readonly botRevisions: ReadonlyMap<string, string>;
13
+ readonly pluginInstall?: {
14
+ readonly plugins: ReadonlyMap<string, { readonly definition: unknown }>;
15
+ };
16
+ };
17
+
18
+ /** Already-acquired session replica. Lookup here must not mint or hydrate. */
19
+ export type AcquiredReplica = {
20
+ readonly root: SessionRoot;
21
+ loadEntity(uuid: string): unknown;
22
+ };
23
+
24
+ export type AgentCallScope = {
25
+ replica(sessionId: string): AcquiredReplica | undefined;
26
+ abortJob?(sessionId: string, job: Job): void;
27
+ };
28
+
29
+ export type AgentRuntimeHost = {
30
+ readonly caller: SessionRoot;
31
+ readonly catalog: CallsCatalog;
32
+ readonly scope: AgentCallScope;
33
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Keep floors. The dial is `compactKeepTurns`; a ContextNeed preserve and the
3
+ * session kv only raise it. Empty is never valid — 0 orphans the in-flight
4
+ * exchange — so the mode default stands in when nothing is set.
5
+ */
6
+ import type { CompactJob, ContextNeed } from "@inhuman.tools/harness-model";
7
+
8
+ /**
9
+ * Minimum recent rounds kept when the floors are otherwise empty. 0 replaces the
10
+ * whole eat — the in-flight exchange is orphaned and the model re-does work.
11
+ * Mid-turn keeps the current round; explicit `/compact` keeps a little more
12
+ * thread when the user opted in. A config dial >= 1 replaces both.
13
+ */
14
+ export const DEFAULT_COMPACT_KEEP_TURNS = {
15
+ midTurn: 1,
16
+ explicit: 2,
17
+ } as const;
18
+
19
+ /** Session kv `compact.keep-turns` as a positive integer; 0 when unset or invalid. */
20
+ export function kvKeepTurnsOf(kv: Readonly<Record<string, string>> = {}): number {
21
+ const raw = kv["compact.keep-turns"] ?? kv[":compact.keep-turns"];
22
+ const parsed = raw == null ? Number.NaN : Number(raw);
23
+ return Number.isInteger(parsed) && parsed >= 1 ? parsed : 0;
24
+ }
25
+
26
+ /**
27
+ * Record on the job the settings its cut is computed from (F8). Call once, when
28
+ * the job is minted. After this the job's boundaries come from these stamped
29
+ * values only: a later change to `compactKeepTurns` or session kv
30
+ * `compact.keep-turns` moves neither this cutoff nor any earlier one, so a
31
+ * round is neither lost nor shown twice. The ContextNeed `preserve` widths are
32
+ * the other input; they are facts on the tape and read from there.
33
+ */
34
+ export function stampCompactInputs(
35
+ job: CompactJob,
36
+ configFloor: number,
37
+ kv: Readonly<Record<string, string>> = {},
38
+ ): void {
39
+ job.keepTurns = Math.max(Number.isInteger(configFloor) ? configFloor : 1, 1);
40
+ job.kvKeepTurns = kvKeepTurnsOf(kv);
41
+ }
42
+
43
+ /**
44
+ * Floors for a job's cut. A stamped job reads only its own stamps; the live
45
+ * `kv` and `configFloor` apply to a job that was never stamped.
46
+ */
47
+ export function keepFloorsOfJob(
48
+ job: CompactJob,
49
+ need: ContextNeed | null,
50
+ live: { readonly kv: Readonly<Record<string, string>>; readonly configFloor: number },
51
+ ): { readonly userTurns: number; readonly rounds: number } {
52
+ if (job.keepTurns == null || job.keepTurns < 1) {
53
+ return keepFloors(need, live.kv, job.reason === "user", live.configFloor);
54
+ }
55
+ const kv: Record<string, string> = job.kvKeepTurns >= 1 ? { "compact.keep-turns": String(job.kvKeepTurns) } : {};
56
+ return keepFloors(need, kv, job.reason === "user", job.keepTurns);
57
+ }
58
+
59
+ export function keepFloors(
60
+ need: ContextNeed | null,
61
+ kv: Readonly<Record<string, string>> = {},
62
+ forced = false,
63
+ configFloor = 0,
64
+ ): { readonly userTurns: number; readonly rounds: number } {
65
+ const raw = kv["compact.keep-turns"] ?? kv[":compact.keep-turns"];
66
+ const parsed = raw == null ? Number.NaN : Number(raw);
67
+ const fromKv = Number.isInteger(parsed) && parsed >= 1 ? parsed : 0;
68
+ const fromConfig = Number.isInteger(configFloor) && configFloor >= 1 ? configFloor : 0;
69
+ const highest = Math.max(fromKv, fromConfig);
70
+ const floor =
71
+ highest > 0 ? highest : forced ? DEFAULT_COMPACT_KEEP_TURNS.explicit : DEFAULT_COMPACT_KEEP_TURNS.midTurn;
72
+ return {
73
+ userTurns: Math.max(floor, need?.preserve?.user_turns ?? 0),
74
+ rounds: Math.max(floor, need?.preserve?.rounds ?? 0),
75
+ };
76
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Compact calculations. The Scheme program that writes the card is session-runtime.
3
+ *
4
+ * Occupancy that fires compact is config.json compactTriggers (hard / soft).
5
+ * Empty eat and compact-on-compact REJECT before a strategy runs.
6
+ * Keep-all (empty prefix, nonempty keep) skips auto compact, including hard overflow.
7
+ */
8
+ import {
9
+ CompactJob,
10
+ CompletionExpectation,
11
+ jobTriggersReactLoop,
12
+ reactChunks,
13
+ ProgrammaticJob,
14
+ SessionTurn,
15
+ ToolCallJob,
16
+ TurnApprovalNoteChunk,
17
+ TurnNotificationChunk,
18
+ TurnSteerChunk,
19
+ type CompletionMessage,
20
+ type ContextNeed,
21
+ type SessionUnit,
22
+ } from "@inhuman.tools/harness-model";
23
+
24
+ import { Transcript, walkTranscript } from "./transcript.js";
25
+
26
+ export function compactDue(head: SessionUnit | null, ratio: number | null): boolean {
27
+ if (head == null || ratio == null || !(ratio > 0)) return false;
28
+ if (!(head instanceof SessionTurn)) return false;
29
+ const used = head.contextUsed;
30
+ const limit = head.contextLimit;
31
+ if (!(used > 0 && limit > 0)) return false;
32
+ return used / limit >= ratio;
33
+ }
34
+
35
+ /** Unknown occupancy is not a deficit. */
36
+ export function needDeficit(turn: SessionTurn, need: ContextNeed | null): boolean {
37
+ if (need?.min_remaining_tokens == null) return false;
38
+ const used = turn.contextUsed;
39
+ const limit = turn.contextLimit;
40
+ if (!(used > 0 && limit > 0)) return false;
41
+ const want = Math.min(Math.max(0, need.min_remaining_tokens), limit);
42
+ return limit - used < want;
43
+ }
44
+
45
+ /**
46
+ * Keep floors + requested runway cannot exceed the window.
47
+ * Card budget is not estimated — keep occupancy is the host retain.
48
+ */
49
+ export function needFeasible(turn: SessionTurn, need: ContextNeed | null, keepTokens: number): boolean {
50
+ if (need?.min_remaining_tokens == null) return true;
51
+ const limit = turn.contextLimit;
52
+ if (!(limit > 0)) return false;
53
+ const runway = Math.min(Math.max(0, need.min_remaining_tokens), limit);
54
+ return keepTokens + runway <= limit;
55
+ }
56
+
57
+ /**
58
+ * Forced tail splice. Slash is `#mintCompactTurn`, not this.
59
+ * Keep-all (empty prefix, nonempty keep) vetoes hard overflow.
60
+ * Does not read `contextNeed` — a need is an ambient option, not a tail node.
61
+ * Null = do not enqueue `CompactJob`.
62
+ */
63
+ export function tailCompactCandidate(
64
+ turn: SessionTurn,
65
+ hardRatio: number | null,
66
+ sliced?: { readonly prefix: Transcript; readonly keep: Transcript },
67
+ ): "overflow" | null {
68
+ if (sliced != null && sliced.prefix.cells.length === 0 && sliced.keep.cells.length > 0) return null;
69
+ if (!compactDue(turn, hardRatio)) return null;
70
+ return "overflow";
71
+ }
72
+
73
+ /**
74
+ * Ambient option, not a cutoff. A live need with a real prefix is worth a
75
+ * background completion when the runway is short, the need names a preserve
76
+ * cutoff, or the phase is settled. Hard overflow is `tailCompactCandidate`.
77
+ * Null = do not spend a summarizer. Infeasible retain does not block the option.
78
+ */
79
+ export function compactDecision(
80
+ turn: SessionTurn,
81
+ sliced?: { readonly prefix: Transcript; readonly keep: Transcript },
82
+ ): { readonly focus: string } | null {
83
+ if (sliced == null || sliced.prefix.cells.length === 0) return null;
84
+ const need = turn.contextNeed;
85
+ if (need == null) return null;
86
+ if (compactRejectReason(turn) != null) return null;
87
+ const named = needDeficit(turn, need) || need.preserve != null || need.current_phase_settled === true;
88
+ if (!named) return null;
89
+ return { focus: need.focus ?? "" };
90
+ }
91
+
92
+ export type CompactReject = "empty" | "already-compacted";
93
+
94
+ export function compactRejectReason(unit: SessionUnit | null, current?: CompactJob): CompactReject | null {
95
+ if (unit == null) return "empty";
96
+ const seen = new Set<SessionUnit>();
97
+ for (
98
+ let cell: SessionUnit | null = unit;
99
+ cell != null && !seen.has(cell);
100
+ cell = cell instanceof SessionTurn ? cell.turnTail : null
101
+ ) {
102
+ seen.add(cell);
103
+ if (!(cell instanceof SessionTurn)) continue;
104
+ const own = eatSinceCutoff(reactChunks(cell).filter((chunk) => chunk !== current));
105
+ if (Array.isArray(own)) return null;
106
+ if (own === "already-compacted") return "already-compacted";
107
+ }
108
+ return "empty";
109
+ }
110
+
111
+ function eatSinceCutoff(chunks: readonly unknown[]): CompletionMessage[] | CompactReject {
112
+ let sawCutoff = false;
113
+ let last: CompletionExpectation | null = null;
114
+ const tools: Array<ToolCallJob | ProgrammaticJob> = [];
115
+ const notifies: TurnNotificationChunk[] = [];
116
+ const notes: TurnApprovalNoteChunk[] = [];
117
+ const steers: TurnSteerChunk[] = [];
118
+ for (const event of walkTranscript(chunks)) {
119
+ switch (event.kind) {
120
+ case "compact": {
121
+ sawCutoff = true;
122
+ last = null;
123
+ tools.length = 0;
124
+ notifies.length = 0;
125
+ notes.length = 0;
126
+ steers.length = 0;
127
+
128
+ break;
129
+ }
130
+ case "completion": {
131
+ last = event.chunk;
132
+ tools.length = 0;
133
+ notifies.length = 0;
134
+ notes.length = 0;
135
+ steers.length = 0;
136
+
137
+ break;
138
+ }
139
+ case "tool": {
140
+ if (jobTriggersReactLoop(event.chunk)) tools.push(event.chunk);
141
+
142
+ break;
143
+ }
144
+ case "scheme": {
145
+ tools.push(event.chunk);
146
+
147
+ break;
148
+ }
149
+ case "notify": {
150
+ notifies.push(event.chunk);
151
+
152
+ break;
153
+ }
154
+ case "approval-note": {
155
+ notes.push(event.chunk);
156
+
157
+ break;
158
+ }
159
+ case "steer": {
160
+ steers.push(event.chunk);
161
+
162
+ break;
163
+ }
164
+ case "cell": {
165
+ break;
166
+ }
167
+ default: {
168
+ const _kind: never = event;
169
+ void _kind;
170
+ }
171
+ }
172
+ }
173
+ if (last != null) return [...last.messages, last, ...tools, ...notifies, ...notes, ...steers];
174
+ if (notifies.length > 0 || notes.length > 0 || steers.length > 0) return [...notifies, ...notes, ...steers];
175
+ return sawCutoff ? "already-compacted" : "empty";
176
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Pure call-delivery projection for history.
3
+ * Acceptance, leases, and settlement stay in agent-calls.
4
+ */
5
+ import type { CatalogEntry } from "../tool-catalog/index.js";
6
+
7
+ export type DeliveryCatalog = {
8
+ catalogEntry(wireName: string): CatalogEntry | undefined;
9
+ };
10
+
11
+ /** Omit / turn catalog visibility. Not a lease or marker. */
12
+ export function usesGenericSilentProjection(call: { readonly name: string }, catalog: DeliveryCatalog): boolean {
13
+ const meta = catalog.catalogEntry(call.name)?.packMeta;
14
+ return meta?.kind === "bot-call" && (meta.show === "omit" || meta.show === "turn");
15
+ }