@inhuman.tools/turn 0.1.0 → 0.3.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 (284) hide show
  1. package/README.md +38 -0
  2. package/dist/agent-calls/acceptance.d.ts +1 -1
  3. package/dist/agent-calls/acceptance.d.ts.map +1 -1
  4. package/dist/agent-calls/acceptance.js +1 -1
  5. package/dist/agent-calls/acceptance.js.map +1 -1
  6. package/dist/agent-calls/callbacks.d.ts +1 -1
  7. package/dist/agent-calls/callbacks.d.ts.map +1 -1
  8. package/dist/agent-calls/callbacks.js +1 -1
  9. package/dist/agent-calls/callbacks.js.map +1 -1
  10. package/dist/agent-calls/delivery.d.ts +1 -1
  11. package/dist/agent-calls/delivery.d.ts.map +1 -1
  12. package/dist/agent-calls/delivery.js +1 -1
  13. package/dist/agent-calls/delivery.js.map +1 -1
  14. package/dist/agent-calls/operator.d.ts +1 -1
  15. package/dist/agent-calls/operator.d.ts.map +1 -1
  16. package/dist/agent-calls/operator.js +1 -1
  17. package/dist/agent-calls/operator.js.map +1 -1
  18. package/dist/agent-calls/runtime.d.ts +4 -2
  19. package/dist/agent-calls/runtime.d.ts.map +1 -1
  20. package/dist/agent-calls/runtime.js +17 -11
  21. package/dist/agent-calls/runtime.js.map +1 -1
  22. package/dist/agent-calls/types.d.ts +1 -1
  23. package/dist/agent-calls/types.d.ts.map +1 -1
  24. package/dist/context/compact-floors.d.ts +1 -1
  25. package/dist/context/compact-floors.d.ts.map +1 -1
  26. package/dist/context/compact.d.ts +1 -1
  27. package/dist/context/compact.d.ts.map +1 -1
  28. package/dist/context/compact.js +5 -4
  29. package/dist/context/compact.js.map +1 -1
  30. package/dist/context/history.d.ts +1 -1
  31. package/dist/context/history.d.ts.map +1 -1
  32. package/dist/context/history.js +35 -3
  33. package/dist/context/history.js.map +1 -1
  34. package/dist/context/retention/defaults.js +1 -1
  35. package/dist/context/retention/defaults.js.map +1 -1
  36. package/dist/context/retention/resolve.js +1 -1
  37. package/dist/context/retention/resolve.js.map +1 -1
  38. package/dist/context/retention/schema.js +1 -1
  39. package/dist/context/retention/schema.js.map +1 -1
  40. package/dist/context/retention/stamp.d.ts +1 -1
  41. package/dist/context/retention/stamp.d.ts.map +1 -1
  42. package/dist/context/retention/stamp.js +5 -2
  43. package/dist/context/retention/stamp.js.map +1 -1
  44. package/dist/context/retention/step.d.ts +1 -1
  45. package/dist/context/retention/step.d.ts.map +1 -1
  46. package/dist/context/retention/step.js +7 -4
  47. package/dist/context/retention/step.js.map +1 -1
  48. package/dist/context/retention/view.d.ts +1 -1
  49. package/dist/context/retention/view.d.ts.map +1 -1
  50. package/dist/context/retention/view.js +1 -1
  51. package/dist/context/retention/view.js.map +1 -1
  52. package/dist/context/spawn-history.d.ts +13 -0
  53. package/dist/context/spawn-history.d.ts.map +1 -0
  54. package/dist/context/spawn-history.js +82 -0
  55. package/dist/context/spawn-history.js.map +1 -0
  56. package/dist/context/squeeze.d.ts +1 -1
  57. package/dist/context/squeeze.d.ts.map +1 -1
  58. package/dist/context/squeeze.js +1 -1
  59. package/dist/context/squeeze.js.map +1 -1
  60. package/dist/context/tools.d.ts +2 -2
  61. package/dist/context/tools.d.ts.map +1 -1
  62. package/dist/context/tools.js +2 -2
  63. package/dist/context/tools.js.map +1 -1
  64. package/dist/context/transcript.d.ts +3 -1
  65. package/dist/context/transcript.d.ts.map +1 -1
  66. package/dist/context/transcript.js +17 -4
  67. package/dist/context/transcript.js.map +1 -1
  68. package/dist/policy-processor/classifier.d.ts.map +1 -1
  69. package/dist/policy-processor/classifier.js +3 -1
  70. package/dist/policy-processor/classifier.js.map +1 -1
  71. package/dist/policy-processor/scope.d.ts +1 -0
  72. package/dist/policy-processor/scope.d.ts.map +1 -1
  73. package/dist/policy-processor/scope.js +9 -2
  74. package/dist/policy-processor/scope.js.map +1 -1
  75. package/dist/policy-processor/unattended-guard.js +8 -2
  76. package/dist/policy-processor/unattended-guard.js.map +1 -1
  77. package/dist/resources/compact-assets.d.ts +5 -0
  78. package/dist/resources/compact-assets.d.ts.map +1 -0
  79. package/dist/resources/compact-assets.js +6 -0
  80. package/dist/resources/compact-assets.js.map +1 -0
  81. package/dist/resources/compact-snapshot.d.ts +21 -0
  82. package/dist/resources/compact-snapshot.d.ts.map +1 -0
  83. package/dist/resources/compact-snapshot.js +44 -0
  84. package/dist/resources/compact-snapshot.js.map +1 -0
  85. package/dist/resources/index.d.ts +6 -0
  86. package/dist/resources/index.d.ts.map +1 -0
  87. package/dist/resources/index.js +19 -0
  88. package/dist/resources/index.js.map +1 -0
  89. package/dist/resources/module-bundle.d.ts +16 -0
  90. package/dist/resources/module-bundle.d.ts.map +1 -0
  91. package/dist/resources/module-bundle.js +78 -0
  92. package/dist/resources/module-bundle.js.map +1 -0
  93. package/dist/runtime/ambient-compact.d.ts +38 -0
  94. package/dist/runtime/ambient-compact.d.ts.map +1 -0
  95. package/dist/runtime/ambient-compact.js +123 -0
  96. package/dist/runtime/ambient-compact.js.map +1 -0
  97. package/dist/runtime/index.d.ts +9 -0
  98. package/dist/runtime/index.d.ts.map +1 -0
  99. package/dist/runtime/index.js +8 -0
  100. package/dist/runtime/index.js.map +1 -0
  101. package/dist/runtime/ports.d.ts +33 -0
  102. package/dist/runtime/ports.d.ts.map +1 -0
  103. package/dist/runtime/ports.js +2 -0
  104. package/dist/runtime/ports.js.map +1 -0
  105. package/dist/runtime/prompt-message-of.d.ts +4 -0
  106. package/dist/runtime/prompt-message-of.d.ts.map +1 -0
  107. package/dist/runtime/prompt-message-of.js +8 -0
  108. package/dist/runtime/prompt-message-of.js.map +1 -0
  109. package/dist/runtime/re-act-loop-intent-detected-trigger.d.ts +32 -0
  110. package/dist/runtime/re-act-loop-intent-detected-trigger.d.ts.map +1 -0
  111. package/dist/runtime/re-act-loop-intent-detected-trigger.js +132 -0
  112. package/dist/runtime/re-act-loop-intent-detected-trigger.js.map +1 -0
  113. package/dist/runtime/round-boundary.d.ts +13 -0
  114. package/dist/runtime/round-boundary.d.ts.map +1 -0
  115. package/dist/runtime/round-boundary.js +210 -0
  116. package/dist/runtime/round-boundary.js.map +1 -0
  117. package/dist/runtime/round-stop.d.ts +9 -0
  118. package/dist/runtime/round-stop.d.ts.map +1 -0
  119. package/dist/runtime/round-stop.js +19 -0
  120. package/dist/runtime/round-stop.js.map +1 -0
  121. package/dist/runtime/tool-call.d.ts +4 -0
  122. package/dist/runtime/tool-call.d.ts.map +1 -0
  123. package/dist/runtime/tool-call.js +8 -0
  124. package/dist/runtime/tool-call.js.map +1 -0
  125. package/dist/runtime/turn-iterator.d.ts +32 -0
  126. package/dist/runtime/turn-iterator.d.ts.map +1 -0
  127. package/dist/runtime/turn-iterator.js +251 -0
  128. package/dist/runtime/turn-iterator.js.map +1 -0
  129. package/dist/runtime/turn-resolvers.d.ts +17 -0
  130. package/dist/runtime/turn-resolvers.d.ts.map +1 -0
  131. package/dist/runtime/turn-resolvers.js +29 -0
  132. package/dist/runtime/turn-resolvers.js.map +1 -0
  133. package/dist/session/cancel.d.ts +4 -1
  134. package/dist/session/cancel.d.ts.map +1 -1
  135. package/dist/session/cancel.js +10 -2
  136. package/dist/session/cancel.js.map +1 -1
  137. package/dist/session/commands.d.ts +1 -1
  138. package/dist/session/commands.d.ts.map +1 -1
  139. package/dist/session/commands.js +4 -2
  140. package/dist/session/commands.js.map +1 -1
  141. package/dist/session/compact-actor.d.ts +12 -5
  142. package/dist/session/compact-actor.d.ts.map +1 -1
  143. package/dist/session/compact-actor.js +64 -12
  144. package/dist/session/compact-actor.js.map +1 -1
  145. package/dist/session/compact-env.d.ts +31 -5
  146. package/dist/session/compact-env.d.ts.map +1 -1
  147. package/dist/session/compact-env.js +23 -13
  148. package/dist/session/compact-env.js.map +1 -1
  149. package/dist/session/compact.d.ts +6 -3
  150. package/dist/session/compact.d.ts.map +1 -1
  151. package/dist/session/compact.js.map +1 -1
  152. package/dist/session/index.d.ts +2 -2
  153. package/dist/session/index.d.ts.map +1 -1
  154. package/dist/session/index.js +2 -2
  155. package/dist/session/index.js.map +1 -1
  156. package/dist/session/park.d.ts +1 -1
  157. package/dist/session/park.d.ts.map +1 -1
  158. package/dist/session/park.js +1 -1
  159. package/dist/session/park.js.map +1 -1
  160. package/dist/session/side-channel-reminder.d.ts +1 -1
  161. package/dist/session/side-channel-reminder.d.ts.map +1 -1
  162. package/dist/session/side-channel-reminder.js +1 -1
  163. package/dist/session/side-channel-reminder.js.map +1 -1
  164. package/dist/session/types.d.ts +1 -1
  165. package/dist/session/types.d.ts.map +1 -1
  166. package/dist/session-runtime/ask-line.d.ts +7 -0
  167. package/dist/session-runtime/ask-line.d.ts.map +1 -0
  168. package/dist/session-runtime/ask-line.js +36 -0
  169. package/dist/session-runtime/ask-line.js.map +1 -0
  170. package/dist/session-runtime/durability.d.ts +25 -0
  171. package/dist/session-runtime/durability.d.ts.map +1 -0
  172. package/dist/session-runtime/durability.js +18 -0
  173. package/dist/session-runtime/durability.js.map +1 -0
  174. package/dist/session-runtime/effects.d.ts +53 -0
  175. package/dist/session-runtime/effects.d.ts.map +1 -0
  176. package/dist/session-runtime/effects.js +18 -0
  177. package/dist/session-runtime/effects.js.map +1 -0
  178. package/dist/session-runtime/index.d.ts +8 -0
  179. package/dist/session-runtime/index.d.ts.map +1 -0
  180. package/dist/session-runtime/index.js +8 -0
  181. package/dist/session-runtime/index.js.map +1 -0
  182. package/dist/session-runtime/interrupt.d.ts +3 -0
  183. package/dist/session-runtime/interrupt.d.ts.map +1 -0
  184. package/dist/session-runtime/interrupt.js +33 -0
  185. package/dist/session-runtime/interrupt.js.map +1 -0
  186. package/dist/session-runtime/recovery.d.ts +17 -0
  187. package/dist/session-runtime/recovery.d.ts.map +1 -0
  188. package/dist/session-runtime/recovery.js +119 -0
  189. package/dist/session-runtime/recovery.js.map +1 -0
  190. package/dist/session-runtime/runtime.d.ts +50 -0
  191. package/dist/session-runtime/runtime.d.ts.map +1 -0
  192. package/dist/session-runtime/runtime.js +533 -0
  193. package/dist/session-runtime/runtime.js.map +1 -0
  194. package/dist/session-runtime/summary.d.ts +26 -0
  195. package/dist/session-runtime/summary.d.ts.map +1 -0
  196. package/dist/session-runtime/summary.js +33 -0
  197. package/dist/session-runtime/summary.js.map +1 -0
  198. package/dist/tool-catalog/bindings.d.ts +6 -0
  199. package/dist/tool-catalog/bindings.d.ts.map +1 -0
  200. package/dist/tool-catalog/bindings.js +37 -0
  201. package/dist/tool-catalog/bindings.js.map +1 -0
  202. package/dist/tool-catalog/catalog.d.ts +1 -3
  203. package/dist/tool-catalog/catalog.d.ts.map +1 -1
  204. package/dist/tool-catalog/catalog.js +6 -34
  205. package/dist/tool-catalog/catalog.js.map +1 -1
  206. package/dist/tool-catalog/types.d.ts +8 -0
  207. package/dist/tool-catalog/types.d.ts.map +1 -1
  208. package/dist/tool-catalog/types.js.map +1 -1
  209. package/dist/tool-runtime/approval/approved.d.ts +1 -1
  210. package/dist/tool-runtime/approval/approved.d.ts.map +1 -1
  211. package/dist/tool-runtime/approval/approved.js +1 -1
  212. package/dist/tool-runtime/approval/approved.js.map +1 -1
  213. package/dist/tool-runtime/handler.d.ts.map +1 -1
  214. package/dist/tool-runtime/handler.js +291 -237
  215. package/dist/tool-runtime/handler.js.map +1 -1
  216. package/dist/tool-runtime/run-system-tool.d.ts +1 -0
  217. package/dist/tool-runtime/run-system-tool.d.ts.map +1 -1
  218. package/dist/tool-runtime/run-system-tool.js +30 -10
  219. package/dist/tool-runtime/run-system-tool.js.map +1 -1
  220. package/dist/tool-runtime/worker-tools.d.ts.map +1 -1
  221. package/dist/tool-runtime/worker-tools.js +2 -2
  222. package/dist/tool-runtime/worker-tools.js.map +1 -1
  223. package/package.json +31 -18
  224. package/src/agent-calls/acceptance.ts +1 -1
  225. package/src/agent-calls/callbacks.ts +1 -1
  226. package/src/agent-calls/delivery.ts +1 -1
  227. package/src/agent-calls/operator.ts +7 -1
  228. package/src/agent-calls/runtime.ts +23 -11
  229. package/src/agent-calls/types.ts +1 -1
  230. package/src/context/compact-floors.ts +1 -1
  231. package/src/context/compact.ts +4 -5
  232. package/src/context/history.ts +46 -4
  233. package/src/context/retention/defaults.ts +1 -1
  234. package/src/context/retention/resolve.ts +1 -1
  235. package/src/context/retention/schema.ts +1 -1
  236. package/src/context/retention/stamp.ts +7 -2
  237. package/src/context/retention/step.ts +7 -4
  238. package/src/context/retention/view.ts +1 -1
  239. package/src/context/spawn-history.ts +95 -0
  240. package/src/context/squeeze.ts +1 -1
  241. package/src/context/tools.ts +3 -2
  242. package/src/context/transcript.ts +15 -4
  243. package/src/policy-processor/README.md +1 -1
  244. package/src/policy-processor/classifier.ts +3 -1
  245. package/src/policy-processor/scope.ts +7 -2
  246. package/src/policy-processor/unattended-guard.ts +5 -2
  247. package/src/resources/compact-assets.ts +9 -0
  248. package/src/resources/compact-snapshot.ts +51 -0
  249. package/src/resources/index.ts +27 -0
  250. package/src/resources/module-bundle.ts +90 -0
  251. package/src/runtime/ambient-compact.ts +147 -0
  252. package/src/runtime/index.ts +17 -0
  253. package/src/runtime/ports.ts +28 -0
  254. package/src/runtime/prompt-message-of.ts +14 -0
  255. package/src/runtime/re-act-loop-intent-detected-trigger.ts +177 -0
  256. package/src/runtime/round-boundary.ts +248 -0
  257. package/src/runtime/round-stop.ts +24 -0
  258. package/src/runtime/tool-call.ts +10 -0
  259. package/src/runtime/turn-iterator.ts +312 -0
  260. package/src/runtime/turn-resolvers.ts +43 -0
  261. package/src/session/cancel.ts +11 -3
  262. package/src/session/commands.ts +5 -2
  263. package/src/session/compact-actor.ts +76 -16
  264. package/src/session/compact-env.ts +27 -18
  265. package/src/session/compact.ts +8 -3
  266. package/src/session/index.ts +7 -2
  267. package/src/session/park.ts +7 -1
  268. package/src/session/side-channel-reminder.ts +1 -1
  269. package/src/session/types.ts +1 -1
  270. package/src/session-runtime/ask-line.ts +39 -0
  271. package/src/session-runtime/durability.ts +41 -0
  272. package/src/session-runtime/effects.ts +35 -0
  273. package/src/session-runtime/index.ts +19 -0
  274. package/src/session-runtime/interrupt.ts +34 -0
  275. package/src/session-runtime/recovery.ts +145 -0
  276. package/src/session-runtime/runtime.ts +535 -0
  277. package/src/session-runtime/summary.ts +67 -0
  278. package/src/tool-catalog/bindings.ts +37 -0
  279. package/src/tool-catalog/catalog.ts +6 -31
  280. package/src/tool-catalog/types.ts +9 -0
  281. package/src/tool-runtime/approval/approved.ts +1 -1
  282. package/src/tool-runtime/handler.ts +40 -24
  283. package/src/tool-runtime/run-system-tool.ts +27 -11
  284. package/src/tool-runtime/worker-tools.ts +2 -2
@@ -0,0 +1,67 @@
1
+ import {
2
+ AskAgentSpec,
3
+ latestUserPrompt,
4
+ outgoingAgentRelations,
5
+ SessionTurn,
6
+ type AgentNature,
7
+ type SessionUnfinished,
8
+ type SessionPlexus,
9
+ type SessionRoot,
10
+ } from "@inhuman.tools/harness-model/session";
11
+ import { comparer, reaction } from "mobx";
12
+
13
+ import { glanceAskLine } from "./ask-line.js";
14
+
15
+ export type SessionSummary = {
16
+ readonly title: string;
17
+ readonly recipeScope: AgentNature;
18
+ readonly specName: string;
19
+ readonly resident: boolean;
20
+ readonly keepAlive: boolean;
21
+ readonly oneShot: boolean;
22
+ readonly model: string;
23
+ readonly status: string;
24
+ readonly midMotion: boolean;
25
+ readonly openTurn: boolean;
26
+ readonly openToolCount: number;
27
+ readonly periodArmed: boolean;
28
+ readonly openCallbackCount: number;
29
+ readonly asking: boolean;
30
+ readonly unfinished: SessionUnfinished;
31
+ readonly lastPrompt: string;
32
+ readonly askLine: string;
33
+ readonly questionLine: string;
34
+ readonly path: string;
35
+ readonly calls: ReturnType<typeof outgoingAgentRelations>;
36
+ };
37
+
38
+ export function sessionSummary(root: SessionRoot): SessionSummary {
39
+ const spec = root.agent.spec;
40
+ const unfinished = root.unfinished;
41
+ return {
42
+ title: root.title,
43
+ recipeScope: spec.recipeScope,
44
+ specName: spec.name,
45
+ resident: spec.resident,
46
+ keepAlive: spec.keepAlive,
47
+ oneShot: spec instanceof AskAgentSpec,
48
+ model: (root.kvs.model ?? "").trim(),
49
+ status: root.turnDescription,
50
+ midMotion: root.midMotion,
51
+ openTurn: root.openTurn,
52
+ openToolCount: root.openToolCount,
53
+ periodArmed: root.periodArmed,
54
+ openCallbackCount: root.openCallbackCount,
55
+ asking: root.asking,
56
+ unfinished,
57
+ lastPrompt: latestUserPrompt(root.head),
58
+ askLine: unfinished === "ask" ? glanceAskLine(root.head?.approvalHead ?? null) : "",
59
+ questionLine: unfinished === "question" && root.head instanceof SessionTurn ? root.head.questionLine : "",
60
+ path: root.path,
61
+ calls: outgoingAgentRelations(root),
62
+ };
63
+ }
64
+
65
+ export function observeSessionSummary(session: SessionPlexus, publish: (summary: SessionSummary) => void): () => void {
66
+ return reaction(() => sessionSummary(session.root), publish, { equals: comparer.structural, fireImmediately: true });
67
+ }
@@ -0,0 +1,37 @@
1
+ import invariant from "tiny-invariant";
2
+
3
+ import type { KernelToolCatalog } from "./catalog.js";
4
+
5
+ const catalogsByRoot = new WeakMap<object, KernelToolCatalog>();
6
+ const rootsByCatalog = new WeakMap<KernelToolCatalog, Set<object>>();
7
+
8
+ /** Bind a session (or test) root to a kernel catalog. */
9
+ export function bindSessionCatalog(root: object, catalog: KernelToolCatalog): void {
10
+ invariant(!catalog.disposed, "bindSessionCatalog: catalog is disposed");
11
+ const prior = catalogsByRoot.get(root);
12
+ if (prior != null && prior !== catalog) rootsByCatalog.get(prior)?.delete(root);
13
+ catalogsByRoot.set(root, catalog);
14
+ let roots = rootsByCatalog.get(catalog);
15
+ if (roots == null) {
16
+ roots = new Set();
17
+ rootsByCatalog.set(catalog, roots);
18
+ }
19
+ roots.add(root);
20
+ }
21
+
22
+ export function catalogOf(root: object | undefined | null): KernelToolCatalog | undefined {
23
+ if (root == null) return undefined;
24
+ const catalog = catalogsByRoot.get(root);
25
+ if (catalog == null || catalog.disposed) return undefined;
26
+ return catalog;
27
+ }
28
+
29
+ export function releaseCatalogRoots(catalog: KernelToolCatalog): void {
30
+ const roots = rootsByCatalog.get(catalog);
31
+ if (roots == null) return;
32
+ for (const root of roots) {
33
+ if (catalogsByRoot.get(root) === catalog) catalogsByRoot.delete(root);
34
+ }
35
+ roots.clear();
36
+ rootsByCatalog.delete(catalog);
37
+ }
@@ -12,9 +12,12 @@ import { DisposableClass, disposableGuard, prompt, type SystemPrompt } from "@in
12
12
  import { action, computed, observable } from "mobx";
13
13
  import invariant from "tiny-invariant";
14
14
 
15
+ import { releaseCatalogRoots } from "./bindings.js";
15
16
  import { type CatalogRecipe, dedupeRecipes, findRecipe, invokeCyclePath } from "./recipes.js";
16
17
  import { type CatalogEntry, PLAIN_WORKER_HOST, type WorkerToolHost } from "./types.js";
17
18
 
19
+ export { bindSessionCatalog, catalogOf } from "./bindings.js";
20
+
18
21
  export type CompiledPluginContribution = {
19
22
  readonly compiledRecipes: readonly CatalogRecipe[];
20
23
  };
@@ -224,21 +227,16 @@ export class KernelToolCatalog extends DisposableClass {
224
227
  this.behaviorEntries = [];
225
228
  this.connectionHost = undefined;
226
229
  this.botRevisions = new Map();
227
- const roots = rootsByCatalog.get(this);
228
- if (roots == null) return;
229
- for (const root of roots) {
230
- if (catalogsByRoot.get(root) === this) catalogsByRoot.delete(root);
231
- }
232
- roots.clear();
233
- rootsByCatalog.delete(this);
230
+ releaseCatalogRoots(this);
234
231
  }
235
232
  }
236
233
 
237
234
  export function catalogTriggersReactLoop(catalog: KernelToolCatalog, wire: string): boolean {
238
235
  const entry = catalog.catalogEntry(wire);
236
+ if (entry?.continuation != null) return entry.continuation.kind === "observation";
239
237
  if (entry?.triggersReactLoop === false) return false;
240
238
  if (entry?.triggersReactLoop === true) return true;
241
- return entry?.isSideChannel !== true;
239
+ return true;
242
240
  }
243
241
 
244
242
  export function requireCatalog<T extends KernelToolCatalog>(catalog: T | undefined, what: string): T {
@@ -246,8 +244,6 @@ export function requireCatalog<T extends KernelToolCatalog>(catalog: T | undefin
246
244
  return catalog;
247
245
  }
248
246
 
249
- const catalogsByRoot = new WeakMap<object, KernelToolCatalog>();
250
- const rootsByCatalog = new WeakMap<KernelToolCatalog, Set<object>>();
251
247
  const behaviorEntriesByRoot = observable.map<object, readonly CatalogEntry[]>(new Map(), { deep: false });
252
248
 
253
249
  /** Tools the session's pinned behavior declares. Replaced whenever the pin reattaches. */
@@ -259,24 +255,3 @@ export const bindBehaviorEntries = action((root: object, entries: readonly Catal
259
255
  export function behaviorEntriesOf(root: object | undefined | null): readonly CatalogEntry[] {
260
256
  return root == null ? [] : (behaviorEntriesByRoot.get(root) ?? []);
261
257
  }
262
-
263
- /** Bind a session (or test) root to a kernel catalog. */
264
- export function bindSessionCatalog(root: object, catalog: KernelToolCatalog): void {
265
- invariant(!catalog.disposed, "bindSessionCatalog: catalog is disposed");
266
- const prior = catalogsByRoot.get(root);
267
- if (prior != null && prior !== catalog) rootsByCatalog.get(prior)?.delete(root);
268
- catalogsByRoot.set(root, catalog);
269
- let roots = rootsByCatalog.get(catalog);
270
- if (roots == null) {
271
- roots = new Set();
272
- rootsByCatalog.set(catalog, roots);
273
- }
274
- roots.add(root);
275
- }
276
-
277
- export function catalogOf(root: object | undefined | null): KernelToolCatalog | undefined {
278
- if (root == null) return undefined;
279
- const catalog = catalogsByRoot.get(root);
280
- if (catalog == null || catalog.disposed) return undefined;
281
- return catalog;
282
- }
@@ -2,6 +2,8 @@
2
2
  * Catalog row identity. wireName is a model-edge projection — never durable
3
3
  * routing. Connection tools route by connectionKey + serverToolName.
4
4
  */
5
+ import type { ToolContinuation } from "@inhuman.tools/harness-commons";
6
+
5
7
  import type { RuleInput } from "../context/retention/schema.js";
6
8
 
7
9
  export type CatalogSource =
@@ -23,6 +25,11 @@ export type CatalogExecute = (
23
25
  catalog?: unknown;
24
26
  onPriorContent?: (text: string | null) => void;
25
27
  invocation?: unknown;
28
+ storage?: {
29
+ readonly session: unknown;
30
+ readonly invocation: unknown;
31
+ propose(action: unknown): void;
32
+ };
26
33
  shellTailBytes?: number;
27
34
  onTail?: (frame: { readonly text: string; readonly totalBytes: number }) => void;
28
35
  },
@@ -62,6 +69,8 @@ export type CatalogEntry = {
62
69
  */
63
70
  readonly execute?: CatalogExecute;
64
71
  readonly packMeta?: PackToolMeta;
72
+ readonly continuation?: ToolContinuation;
73
+ /** @deprecated Use continuation; explicit policy takes precedence. */
65
74
  readonly triggersReactLoop?: boolean;
66
75
  /** Skips the license floor. Copied from the tool that declared it. */
67
76
  readonly isSideChannel?: boolean;
@@ -3,7 +3,7 @@
3
3
  * in harness-model. Cancel writes `cancelled`, not `approved`. Human
4
4
  * `approvalComment` is a `TurnApprovalNoteChunk`, not this receipt.
5
5
  */
6
- import { ApprovableJob, applyToolOutcomeReceipt, cancelUnstarted } from "@inhuman.tools/harness-model";
6
+ import { ApprovableJob, applyToolOutcomeReceipt, cancelUnstarted } from "@inhuman.tools/harness-model/session";
7
7
  import { action } from "mobx";
8
8
 
9
9
  export function stampToolOutcome(leaf: ApprovableJob, text: "denied" | "cancelled", reason?: unknown): void {
@@ -2,13 +2,14 @@
2
2
  * Project-bound mint of one model tool_call. Does not execute.
3
3
  * Connection-sourced names are MCPToolJob; write_file / edit_file are
4
4
  * FileMutateToolJob; pack / unknown are SystemToolJob.
5
- * `agentCallFromWire` mints `agent__ask` / `agent__send`. `admit` wraps it; the turn calls it directly.
5
+ * `agentCallFromWire` mints `agent__ask` / `agent__send`. `admit` wraps it with frozen policy and retention facts.
6
6
  * Both append onto `AdmitContext.parent` when the caller passes one. They do not enqueue.
7
7
  * Bot-call pack tools and standing bot invocations mint BotCallJob.
8
8
  * `scheme_repl` is a ProgrammaticJob bag. Inner admits reuse this door.
9
9
  */
10
10
  import { createHash } from "node:crypto";
11
11
 
12
+ import type { ToolContinuation } from "@inhuman.tools/harness-commons";
12
13
  import {
13
14
  AgentCallJob,
14
15
  BotCallJob,
@@ -23,8 +24,6 @@ import {
23
24
  AGENT_SEND,
24
25
  AGENT_VOID,
25
26
  CallDelivery,
26
- CONTEXT_NEED_TOOL_NAME,
27
- ContextNeedToolJob,
28
27
  FailureOutcome,
29
28
  FileMutateToolJob,
30
29
  MCPToolJob,
@@ -50,6 +49,7 @@ import {
50
49
  } from "@inhuman.tools/harness-model";
51
50
  import { parseJsonRecord } from "@inhuman.tools/harness-model/json-record";
52
51
  import type { ToolCall } from "@inhuman.tools/tool-call-normalize";
52
+ import { action } from "mobx";
53
53
 
54
54
  import { externalizePrior } from "./blob-port.js";
55
55
  import { peelAgentCallArgs, requestIdOf } from "./peel.js";
@@ -57,7 +57,12 @@ import { workerToolAllowed } from "./worker-tools.js";
57
57
  import { lineStartOfSpan, writeFileAdmitSnap } from "./write-admit.js";
58
58
  import { policyOfTurn, stampJobRetention } from "../context/retention/index.js";
59
59
  import { peelShellCwd } from "../policy-processor/peel-cwd.js";
60
- import { agentCallSupportsNotify, type CatalogEntry, type KernelToolCatalog } from "../tool-catalog/index.js";
60
+ import {
61
+ agentCallSupportsNotify,
62
+ behaviorEntriesOf,
63
+ type CatalogEntry,
64
+ type KernelToolCatalog,
65
+ } from "../tool-catalog/index.js";
61
66
 
62
67
  /** Catalog face the handler proxies to. A kernel host satisfies this. */
63
68
  export type ToolCallKernel = {
@@ -106,12 +111,17 @@ function priorProps(text: string | null): { readonly priorContent?: string; read
106
111
  }
107
112
 
108
113
  function catalogFlags(entry: CatalogEntry | undefined): {
114
+ readonly continuationJson: string;
109
115
  readonly triggersReactLoop: boolean;
110
116
  readonly tapeHidden: boolean;
111
117
  readonly isSideChannel: boolean;
112
118
  } {
119
+ const continuation: ToolContinuation = entry?.continuation ?? {
120
+ kind: entry?.triggersReactLoop === false ? "none" : "observation",
121
+ };
113
122
  return {
114
- triggersReactLoop: entry?.triggersReactLoop ?? true,
123
+ continuationJson: JSON.stringify(continuation),
124
+ triggersReactLoop: continuation.kind === "observation",
115
125
  tapeHidden: entry?.tapeHidden ?? false,
116
126
  isSideChannel: entry?.isSideChannel === true,
117
127
  };
@@ -171,15 +181,15 @@ function admitExam(call: ToolCall, ctx: AdmitContext, entry: CatalogEntry): Prog
171
181
  bag.outcomeSnap ??= { ok: true, value: { job_id: bag.toolCallId, background: true } };
172
182
  }
173
183
  ctx.originTurn?.pushChild(bag);
174
- const action = exam.admit(prefix, input);
175
- let jobUuid = "";
184
+ const admittedAction = exam.admit(prefix, input);
185
+ let jobUuid: string;
176
186
  try {
177
187
  jobUuid = bag.uuid;
178
188
  } catch {
179
189
  jobUuid = "";
180
190
  }
181
- const actionRec = asRecord(action);
182
- writePluginMeta(bag, pluginId, actionRec == null ? action : { ...actionRec, jobUuid }, true);
191
+ const actionRec = asRecord(admittedAction);
192
+ writePluginMeta(bag, pluginId, actionRec == null ? admittedAction : { ...actionRec, jobUuid }, true);
183
193
  return bag;
184
194
  }
185
195
 
@@ -233,6 +243,11 @@ export function agentCallFromWire(
233
243
  if (ctx.session != null && !workerToolAllowed(catalog, ctx.session.agent.spec.name, name, ctx.session)) {
234
244
  return appendJob(ctx, ceilingJob(call));
235
245
  }
246
+ const flags = catalogFlags(
247
+ catalog.catalogEntry(call.function.name) ??
248
+ catalog.privateWorkerEntries.find((row) => row.wireName === call.function.name) ??
249
+ behaviorEntriesOf(ctx.session).find((row) => row.wireName === call.function.name),
250
+ );
236
251
  const peeled = peelAgentCallArgs(call.function.arguments);
237
252
  const spec = parseJsonRecord(peeled.argsJson)?.spec;
238
253
  // Select the explicit child or bot node at admission;
@@ -246,6 +261,7 @@ export function agentCallFromWire(
246
261
  argsJson: peeled.argsJson,
247
262
  botName: spec.trim(),
248
263
  voidLegal: name === AGENT_SEND ? peeled.voidLegal : false,
264
+ ...flags,
249
265
  readonly: peeled.readonly,
250
266
  responseSchema: peeled.responseSchema,
251
267
  ...stamp(peeled.argsJson),
@@ -260,6 +276,7 @@ export function agentCallFromWire(
260
276
  argsJson: peeled.argsJson,
261
277
  regenerateable: false,
262
278
  voidLegal: name === AGENT_SEND ? peeled.voidLegal : false,
279
+ ...flags,
263
280
  readonly: peeled.readonly,
264
281
  responseSchema: peeled.responseSchema,
265
282
  constitution: peeled.constitution,
@@ -275,16 +292,26 @@ export function agentCallFromWire(
275
292
  export class ToolCallHandler {
276
293
  constructor(readonly kernel: ToolCallKernel) {}
277
294
 
295
+ @action
278
296
  admit(call: ToolCall, catalog: KernelToolCatalog = this.kernel.catalog, ctx: AdmitContext = {}): AdmittedCall {
279
- const job = this.#admit(call, catalog, ctx);
297
+ const job = this.#admit(call, catalog, { ...ctx, parent: undefined });
280
298
  // R2: the horizon is resolved once, here, from the turn's R1 policy and the catalog as it is now.
281
- const entry = catalog.catalogEntry(call.function.name) ?? this.kernel.catalog.catalogEntry(call.function.name);
299
+ const entry =
300
+ catalog.catalogEntry(call.function.name) ??
301
+ this.kernel.catalog.catalogEntry(call.function.name) ??
302
+ catalog.privateWorkerEntries.find((row) => row.wireName === call.function.name) ??
303
+ behaviorEntriesOf(ctx.session).find((row) => row.wireName === call.function.name);
304
+ const policy = catalogFlags(entry);
305
+ job.continuationJson = policy.continuationJson;
306
+ job.triggersReactLoop = policy.triggersReactLoop;
307
+ job.isSideChannel = policy.isSideChannel;
308
+ job.tapeHidden = policy.tapeHidden;
282
309
  const turn = ctx.originTurn ?? ctx.session?.head ?? null;
283
310
  stampJobRetention(job, policyOfTurn(turn instanceof SessionTurn ? turn : null), {
284
311
  ...(entry?.class === undefined ? {} : { entryClass: entry.class }),
285
312
  ...(entry?.retention === undefined ? {} : { hint: entry.retention }),
286
313
  });
287
- return job;
314
+ return appendJob(ctx, job);
288
315
  }
289
316
 
290
317
  #admit(call: ToolCall, catalog: KernelToolCatalog, ctx: AdmitContext): AdmittedCall {
@@ -302,6 +329,7 @@ export class ToolCallHandler {
302
329
  toolName: name,
303
330
  toolCallId,
304
331
  argsJson,
332
+ ...catalogFlags(catalog.catalogEntry(name) ?? this.kernel.catalog.catalogEntry(name)),
305
333
  ...flagged,
306
334
  });
307
335
  if (job.background && job.toolCallId.trim() !== "") {
@@ -437,18 +465,6 @@ export class ToolCallHandler {
437
465
  }),
438
466
  );
439
467
  }
440
- if (name === CONTEXT_NEED_TOOL_NAME) {
441
- return place(
442
- new ContextNeedToolJob({
443
- toolCallId,
444
- name,
445
- argsJson,
446
- regenerateable: false,
447
- ...stamp(argsJson),
448
- ...catalogFlags(entry),
449
- }),
450
- );
451
- }
452
468
  if (name === WEB_SEARCH_TOOL_NAME) {
453
469
  const parsed = parseWebSearchArgs(argsJson);
454
470
  return place(
@@ -27,7 +27,6 @@ import { z } from "zod";
27
27
  import { externalizePrior } from "./blob-port.js";
28
28
  import { runWithExtraRoot } from "./extra-root.js";
29
29
  import { pendingPropose, pluginCallContextOf } from "./plugin-context.js";
30
- import { CONTEXT_RECALL_TOOL_NAME, recallContext } from "./recall.js";
31
30
  import { ToolError } from "./tool-error.js";
32
31
  import { workerToolAllowed } from "./worker-tools.js";
33
32
  import { lineStartOfSpan } from "./write-admit.js";
@@ -39,7 +38,10 @@ function cancelledSnap(signal: AbortSignal): ToolOutcomeSnap {
39
38
 
40
39
  const TAIL_MS = 50;
41
40
 
42
- function tailPublisher(job: SystemToolJob): {
41
+ function tailPublisher(
42
+ job: SystemToolJob,
43
+ canApplyResults: () => boolean,
44
+ ): {
43
45
  push: (frame: { readonly text: string; readonly totalBytes: number }) => void;
44
46
  flush: () => void;
45
47
  } {
@@ -47,6 +49,7 @@ function tailPublisher(job: SystemToolJob): {
47
49
  let timer: ReturnType<typeof setTimeout> | undefined;
48
50
  let latest: { text: string; totalBytes: number } | undefined;
49
51
  const send = (frame: { text: string; totalBytes: number }): void => {
52
+ if (!canApplyResults()) return;
50
53
  if (job.receiptJson != null && job.receiptJson !== "") return;
51
54
  latest = undefined;
52
55
  lastAt = Date.now();
@@ -54,6 +57,7 @@ function tailPublisher(job: SystemToolJob): {
54
57
  };
55
58
  return {
56
59
  push(frame) {
60
+ if (!canApplyResults()) return;
57
61
  const now = Date.now();
58
62
  if (now - lastAt >= TAIL_MS) {
59
63
  if (timer !== undefined) clearTimeout(timer);
@@ -104,6 +108,7 @@ function asToolResult(out: unknown): ToolOutcomeSnap {
104
108
  ok?: unknown;
105
109
  text?: unknown;
106
110
  value?: unknown;
111
+ json?: unknown;
107
112
  truncated?: unknown;
108
113
  };
109
114
  if (typeof row.ok === "boolean") {
@@ -111,6 +116,7 @@ function asToolResult(out: unknown): ToolOutcomeSnap {
111
116
  ok: row.ok,
112
117
  ...(typeof row.text === "string" ? { text: row.text } : {}),
113
118
  ...("value" in row ? { value: row.value } : {}),
119
+ ...("json" in row ? { json: row.json } : {}),
114
120
  ...(row.truncated === true ? { truncated: true } : {}),
115
121
  };
116
122
  }
@@ -133,14 +139,19 @@ export async function executeSystemTool(
133
139
  signal: AbortSignal,
134
140
  opts?: {
135
141
  readonly shellTailBytes?: number;
142
+ readonly canApplyResults?: () => boolean;
136
143
  /** Host list/stop for `job_stop`. The tool body has no job table of its own. */
137
144
  readonly stopJob?: (jobId: string) => JobStopReport | Promise<JobStopReport>;
138
145
  },
139
146
  ): Promise<void> {
147
+ const canApplyResults = opts?.canApplyResults ?? (() => true);
140
148
  const seal = (outcome: ToolOutcomeSnap): void => {
149
+ if (!canApplyResults()) return;
141
150
  sealToolCall(job, outcome);
142
151
  };
152
+ pendingPropose.delete(job);
143
153
  try {
154
+ if (!canApplyResults() || job.outcomeSnap != null) return;
144
155
  if (signal.aborted) {
145
156
  seal(cancelledSnap(signal));
146
157
  return;
@@ -180,11 +191,6 @@ export async function executeSystemTool(
180
191
  return;
181
192
  }
182
193
 
183
- if (name === CONTEXT_RECALL_TOOL_NAME) {
184
- seal(recallContext(session, args));
185
- return;
186
- }
187
-
188
194
  if (name === "job_stop") {
189
195
  const jobIdRaw = args.job_id;
190
196
  const jobId = typeof jobIdRaw === "string" ? jobIdRaw : "";
@@ -203,7 +209,7 @@ export async function executeSystemTool(
203
209
  // reads under it (a session read root), so it opens the same door through `licenseAllows`; nothing is remembered
204
210
  // from a single outside read.
205
211
  const tailBytes = parseShellTailBytes(opts?.shellTailBytes ?? DEFAULT_SHELL_TAIL_BYTES);
206
- const live = name === "shell" && tailBytes > 0 ? tailPublisher(job) : undefined;
212
+ const live = name === "shell" && tailBytes > 0 ? tailPublisher(job, canApplyResults) : undefined;
207
213
  let dispatched: unknown;
208
214
  try {
209
215
  ToolError.invariant(entry?.execute, () =>
@@ -214,13 +220,13 @@ export async function executeSystemTool(
214
220
  try {
215
221
  dispatched = await runWithExtraRoot(job.licenseAllows, () =>
216
222
  entry.execute!(args, {
217
- root,
218
223
  catalog,
219
224
  signal,
220
225
  sessionId: session.sessionId,
221
226
  onPriorContent:
222
227
  job instanceof FileMutateToolJob
223
228
  ? (text) => {
229
+ if (!canApplyResults()) return;
224
230
  stampPrior(job, text);
225
231
  if (typeof text === "string" && name === "edit_file") {
226
232
  const old = typeof args.old_string === "string" ? args.old_string : "";
@@ -232,6 +238,7 @@ export async function executeSystemTool(
232
238
  shellTailBytes: tailBytes,
233
239
  onTail: live?.push,
234
240
  ...pluginCallContextOf(session, job, signal, entry.source.kind === "pack" ? entry.source.pluginName : ""),
241
+ root,
235
242
  }),
236
243
  );
237
244
  } catch (error) {
@@ -246,7 +253,13 @@ export async function executeSystemTool(
246
253
  }
247
254
  const proposed = pendingPropose.get(job);
248
255
  pendingPropose.delete(job);
249
- if (proposed != null) {
256
+ if (!canApplyResults()) return;
257
+ const outcome = asToolResult(dispatched);
258
+ if (signal.aborted) {
259
+ seal(outcome.ok ? cancelledSnap(signal) : outcome);
260
+ return;
261
+ }
262
+ if (proposed != null && outcome.ok) {
250
263
  const projection = projectionsOf(job).get(proposed.pluginId);
251
264
  if (projection?.hostOnly?.(proposed.action) === true) {
252
265
  seal({ ok: false, text: `${proposed.pluginId}: host-only metadata` });
@@ -254,8 +267,9 @@ export async function executeSystemTool(
254
267
  }
255
268
  writePluginMeta(job, proposed.pluginId, proposed.action, false);
256
269
  }
257
- seal(asToolResult(dispatched));
270
+ seal(outcome);
258
271
  } catch (error) {
272
+ if (!canApplyResults()) return;
259
273
  if (signal.aborted) {
260
274
  seal(cancelledSnap(signal));
261
275
  return;
@@ -269,5 +283,7 @@ export async function executeSystemTool(
269
283
  return;
270
284
  }
271
285
  seal({ ok: false, text: `error: ${error instanceof Error ? error.message : String(error)}` });
286
+ } finally {
287
+ pendingPropose.delete(job);
272
288
  }
273
289
  }
@@ -15,7 +15,7 @@ import {
15
15
  SessionRoot,
16
16
  } from "@inhuman.tools/harness-model";
17
17
 
18
- import { behaviorEntriesOf, catalogTriggersReactLoop, type KernelToolCatalog } from "../tool-catalog/index.js";
18
+ import { behaviorEntriesOf, type KernelToolCatalog } from "../tool-catalog/index.js";
19
19
 
20
20
  function isAgentComms(wire: string): boolean {
21
21
  return (
@@ -67,7 +67,7 @@ export function workerToolAllowed(
67
67
  if (
68
68
  wire === SCHEME_REPL_TOOL_NAME ||
69
69
  wire === SCHEME_WATCH_TOOL_NAME ||
70
- !catalogTriggersReactLoop(catalog, wire) ||
70
+ entry.isSideChannel === true ||
71
71
  isAgentComms(wire)
72
72
  ) {
73
73
  return true;