@inhuman.tools/turn 0.2.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 +29 -16
  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
@@ -24,8 +24,9 @@ import {
24
24
  skillMatchesVerb,
25
25
  tapeCompletion,
26
26
  userVisualsJson,
27
+ decideRound,
27
28
  type UserVisualIdentity,
28
- } from "@inhuman.tools/harness-model";
29
+ } from "@inhuman.tools/harness-model/session";
29
30
 
30
31
  import type { SessionCommandHost } from "./types.js";
31
32
  import { compactRejectReason, stampCompactInputs } from "../context/index.js";
@@ -73,10 +74,12 @@ function retentionStamp(host: SessionCommandHost): { readonly retentionJson?: st
73
74
  }
74
75
 
75
76
  function mintContinueTurn(host: SessionCommandHost, head: SessionTurn): void {
77
+ const continuation = new ReACTChunk();
78
+ continuation.decision = decideRound([{ cause: { kind: "user", sources: [head.uuid] } }]);
76
79
  const turn = new SessionTurn({
77
80
  turnTail: head,
78
81
  prompt: asUserPrompt(""),
79
- queue: [new ReACTChunk()],
82
+ queue: [continuation],
80
83
  toolsDefinition: host.toolsFor(head),
81
84
  ...retentionStamp(host),
82
85
  });
@@ -9,7 +9,8 @@ import {
9
9
  liveJobsCheckIn,
10
10
  SessionRoot,
11
11
  SessionTurn,
12
- } from "@inhuman.tools/harness-model";
12
+ } from "@inhuman.tools/harness-model/session";
13
+ import { runInAction } from "mobx";
13
14
 
14
15
  import { applyCompact } from "./compact-env.js";
15
16
  import { loadCompactProgram, type CompactProgram } from "./compact.js";
@@ -22,24 +23,37 @@ import {
22
23
  textOfCell,
23
24
  Transcript,
24
25
  } from "../context/index.js";
26
+ import { parseCompactSnapshot, restoreCompactProgram, snapshotCompactProgram } from "../resources/compact-snapshot.js";
27
+ import { pinModuleLoader } from "../resources/module-bundle.js";
25
28
 
26
29
  /** Human tape only. Not injected into the next model prompt. */
27
30
  const COMPACT_FAILED_NOTICE = "couldn't compact — try again later";
28
31
 
29
- export type CompactHost = {
32
+ type CompactRunHost = {
30
33
  readonly turn: SessionTurn;
31
34
  readonly session: SessionRoot;
32
35
  readonly compactKeepTurns: number;
33
- readonly compactStrategy: string;
34
- readonly kernelFsRoot: string;
35
36
  readonly connectionId: string;
36
37
  readonly model: string;
37
38
  complete?: PromptComplete;
38
- loadProgram?: (spec: string | undefined, kernelFsRoot: string) => CompactProgram;
39
39
  log?: (message: string) => void;
40
40
  fail?: (label: string, error: unknown) => void;
41
41
  };
42
42
 
43
+ export type CompactHost = CompactRunHost &
44
+ (
45
+ | {
46
+ readonly program: CompactProgram | Promise<CompactProgram>;
47
+ checkpoint(job: CompactJob): Promise<void>;
48
+ }
49
+ | {
50
+ readonly compactStrategy: string;
51
+ readonly kernelFsRoot: string;
52
+ loadProgram?: (spec: string | undefined, kernelFsRoot: string) => CompactProgram | Promise<CompactProgram>;
53
+ checkpoint?: (job: CompactJob) => Promise<void>;
54
+ }
55
+ );
56
+
43
57
  function compactRead(
44
58
  configFloor: number,
45
59
  kv: Readonly<Record<string, string>>,
@@ -51,21 +65,56 @@ function compactRead(
51
65
  }
52
66
 
53
67
  function failCompact(job: CompactJob): void {
54
- job.failed = true;
55
- job.notice = COMPACT_FAILED_NOTICE;
56
- job.standIn = "";
57
- job.markComplete();
68
+ runInAction(() => {
69
+ job.failed = true;
70
+ job.notice = COMPACT_FAILED_NOTICE;
71
+ job.standIn = "";
72
+ job.markComplete();
73
+ });
58
74
  }
59
75
 
60
- function loadStrategy(host: CompactHost): CompactProgram {
76
+ async function loadStrategy(host: CompactHost): Promise<CompactProgram> {
77
+ if ("program" in host) return host.program;
61
78
  return (host.loadProgram ?? loadCompactProgram)(host.compactStrategy, host.kernelFsRoot);
62
79
  }
63
80
 
81
+ async function prepareStrategy(job: CompactJob, host: CompactHost, signal?: AbortSignal): Promise<CompactProgram> {
82
+ signal?.throwIfAborted();
83
+ if (job.resourceSnapshotJson != null && job.resourceSnapshotJson !== "") {
84
+ const program = restoreCompactProgram(parseCompactSnapshot(job.resourceSnapshotJson));
85
+ await host.checkpoint?.(job);
86
+ signal?.throwIfAborted();
87
+ return program;
88
+ }
89
+ const program = await loadStrategy(host);
90
+ const snapshot = snapshotCompactProgram(program);
91
+ const persist = async (): Promise<void> => {
92
+ signal?.throwIfAborted();
93
+ runInAction(() => {
94
+ job.resourceSnapshotJson = JSON.stringify(snapshot);
95
+ });
96
+ await host.checkpoint?.(job);
97
+ signal?.throwIfAborted();
98
+ };
99
+ await persist();
100
+ return program.moduleLoader === undefined
101
+ ? program
102
+ : {
103
+ ...program,
104
+ moduleLoader: pinModuleLoader(program.moduleLoader, async (path, content) => {
105
+ snapshot.modules[path] = content;
106
+ await persist();
107
+ }),
108
+ };
109
+ }
110
+
64
111
  /** Abort leaves the job open: an incomplete compact is not a cutoff, and its owner settles it. */
65
112
  export async function runCompactJob(job: CompactJob, host: CompactHost, signal?: AbortSignal): Promise<void> {
113
+ if (signal?.aborted) return;
66
114
  const rejected = compactRejectReason(host.turn);
67
115
  if (rejected != null) {
68
116
  host.log?.(`compact ${rejected}`);
117
+ if (signal?.aborted) return;
69
118
  job.markComplete();
70
119
  return;
71
120
  }
@@ -81,11 +130,13 @@ export async function runCompactJob(job: CompactJob, host: CompactHost, signal?:
81
130
  const zones = sliceCompactZones(eat, floors);
82
131
  if (zones.prefix.cells.length === 0) {
83
132
  host.log?.("compact keep-all");
133
+ signal?.throwIfAborted();
84
134
  job.markComplete();
85
135
  return;
86
136
  }
87
137
  const focus = job.reason === "user" ? host.turn.prompt.trim() || need?.focus || "" : (need?.focus ?? "");
88
- const strategy = loadStrategy(host);
138
+ const strategy = await prepareStrategy(job, host, signal);
139
+ signal?.throwIfAborted();
89
140
  if (strategy.failsafe != null) {
90
141
  // Keep-first is a failsafe, not a strategy: it keeps one cell and drops the
91
142
  // rest. Say so, or a broken install looks like a working compaction.
@@ -98,18 +149,23 @@ export async function runCompactJob(job: CompactJob, host: CompactHost, signal?:
98
149
  kv: session.kvs,
99
150
  forced: job.reason === "user",
100
151
  sourcePath: strategy.sourcePath,
152
+ moduleLoader: strategy.moduleLoader,
101
153
  job: host.turn,
102
154
  complete: host.complete,
103
155
  connectionId: host.connectionId === "" ? undefined : host.connectionId,
104
156
  completionModel: host.model === "" ? undefined : host.model,
105
157
  signal,
106
158
  });
159
+ signal?.throwIfAborted();
107
160
  if (!result.ok) {
108
161
  host.fail?.("compact strategy", result.reason);
162
+ signal?.throwIfAborted();
109
163
  failCompact(job);
110
164
  return;
111
165
  }
112
- const jobs = liveJobsCheckIn(await liveBackgroundJobs(session.motionWork()));
166
+ const background = await liveBackgroundJobs(session.motionWork());
167
+ signal?.throwIfAborted();
168
+ const jobs = liveJobsCheckIn(background);
113
169
  const body = result.checkIn
114
170
  .map((cell) => textOfCell(cell).trim())
115
171
  .filter((line) => line !== "")
@@ -117,16 +173,20 @@ export async function runCompactJob(job: CompactJob, host: CompactHost, signal?:
117
173
  const checkIn = [jobs, body].filter((line) => line !== "").join("\n\n");
118
174
  if (checkIn === "") {
119
175
  host.fail?.("compact strategy", "empty-check-in");
176
+ signal?.throwIfAborted();
120
177
  failCompact(job);
121
178
  return;
122
179
  }
123
- job.standIn = checkIn;
124
- job.failed = false;
125
- job.notice = "";
126
- job.markComplete();
180
+ runInAction(() => {
181
+ job.standIn = checkIn;
182
+ job.failed = false;
183
+ job.notice = "";
184
+ job.markComplete();
185
+ });
127
186
  } catch (error) {
128
187
  if (signal?.aborted) return;
129
188
  host.fail?.("compact strategy", error);
189
+ if (signal?.aborted) return;
130
190
  failCompact(job);
131
191
  }
132
192
  }
@@ -24,8 +24,6 @@
24
24
  * are `pipe` — pure transforms that mint nothing. `transcript/warm?` is the
25
25
  * one argument-reader that stays `source`: `isWarm` reads the live clock.
26
26
  */
27
- import { dirname } from "node:path";
28
-
29
27
  import {
30
28
  EnvCapability,
31
29
  execState,
@@ -36,7 +34,7 @@ import {
36
34
  type SchemeValue,
37
35
  } from "@here.build/arrival";
38
36
  import { applyCallback, is_callable_value } from "@here.build/arrival/host-internals";
39
- import { arrivalLoaderCapability } from "@here.build/arrival-modules";
37
+ import { arrivalLoaderCapability, type Loader } from "@here.build/arrival-modules";
40
38
  import { promptIsolatedCapability, type PromptComplete } from "@inhuman.tools/env-capability-module-prompt";
41
39
  import {
42
40
  CompactJob,
@@ -48,9 +46,8 @@ import {
48
46
  TurnApprovalNoteChunk,
49
47
  TurnNotificationChunk,
50
48
  TurnSteerChunk,
51
- } from "@inhuman.tools/harness-model";
49
+ } from "@inhuman.tools/harness-model/session";
52
50
  import { Job } from "@inhuman.tools/plexus-expectations";
53
- import { programSourceLoader } from "@inhuman.tools/scheme-program/fs";
54
51
  import invariant from "tiny-invariant";
55
52
  import { z } from "zod";
56
53
 
@@ -63,6 +60,7 @@ import {
63
60
  Transcript,
64
61
  type EatCell,
65
62
  } from "../context/index.js";
63
+ import { spawnHistoryText } from "../context/spawn-history.js";
66
64
  import { charsOf } from "../context/transcript.js";
67
65
 
68
66
  /** Same door as scheme_repl `load`: last-write-wins over `require`. */
@@ -95,11 +93,10 @@ export type CompactStrategyOpts = {
95
93
  readonly plan?: string | null;
96
94
  readonly forced?: boolean;
97
95
  /**
98
- * Absolute path the program source was read from. Enables `(require "…")` —
99
- * `.prompt` pretreats ship next to the strategy — jailed under its dirname.
100
- * Null for the inline fallback: no `require` surface.
96
+ * Source label for diagnostics; imports require an explicitly supplied loader.
101
97
  */
102
98
  readonly sourcePath?: string | null;
99
+ readonly moduleLoader?: Loader;
103
100
  /**
104
101
  * Live completion wiring for the summarizer call a `.prompt` performs. The
105
102
  * prompt capability admits an agent/ask on this job; absent → the strategy
@@ -316,6 +313,9 @@ export const compactReducerCapability = EnvCapability.define("harness/compact",
316
313
  if (cell instanceof TurnNotificationChunk) return textOfCell(cell);
317
314
  if (cell instanceof TurnApprovalNoteChunk) return `user: ${cell.approvalNoteText()}`;
318
315
  if (cell instanceof TurnSteerChunk) return `user: ${cell.completionMessage}`;
316
+ const spawnText =
317
+ cell instanceof ToolCallJob || cell instanceof ProgrammaticJob ? spawnHistoryText(cell) : null;
318
+ if (spawnText !== null) return spawnText;
319
319
  if (cell instanceof ToolCallJob) {
320
320
  const scheme = cell.children.find(
321
321
  (child): child is ProgrammaticJob => child instanceof ProgrammaticJob,
@@ -404,32 +404,41 @@ export async function applyCompact(
404
404
  fullTranscript: Transcript,
405
405
  opts: CompactStrategyOpts = {},
406
406
  ): Promise<CompactApplyResult> {
407
+ opts.signal?.throwIfAborted();
407
408
  invariant(program.trim() !== "", "compact strategy program is empty");
408
409
  const names = opts.forced === true ? (["forced-compact", "compact"] as const) : (["compact"] as const);
409
- // `(require "compact.prompt")` — the threaded-snapshot strategy is a
410
- // `.scm` + `.prompt` pair. `require` is a loader symbol, and the loader is
411
- // jailed under the strategy file's directory (same rule as scheme_repl:
412
- // `programSourceLoader`). Without these the strategy throws
413
- // `Unbound variable require` and the compact silently degrades to the fallback.
414
- const moduleRoot = opts.sourcePath != null && opts.sourcePath !== "" ? dirname(opts.sourcePath) : undefined;
410
+ const suppliedLoader = opts.moduleLoader;
411
+ const loader: Loader | undefined =
412
+ suppliedLoader === undefined
413
+ ? undefined
414
+ : {
415
+ resolve: (specifier, fromDir) => suppliedLoader.resolve(specifier, fromDir),
416
+ async read(path) {
417
+ opts.signal?.throwIfAborted();
418
+ const content = await suppliedLoader.read(path);
419
+ opts.signal?.throwIfAborted();
420
+ return content;
421
+ },
422
+ resolvers: suppliedLoader.resolvers,
423
+ };
415
424
  // `require` only needs the loader. The isolated host is assembled when a
416
425
  // parent job and a connection exist; without them a stub strategy still
417
426
  // loads, and `dotprompt/run` is not a symbol.
418
427
  const connectionId = opts.connectionId ?? "";
419
- const isolated = moduleRoot != null && opts.job != null && connectionId !== "";
428
+ const isolated = loader != null && opts.job != null && connectionId !== "";
420
429
  const state = await execState(program, {
421
430
  capabilities:
422
- moduleRoot == null
431
+ loader == null
423
432
  ? [compactReducerCapability]
424
433
  : isolated
425
434
  ? [compactReducerCapability, promptIsolatedCapability, schemeLoadAliasCapability]
426
435
  : [compactReducerCapability, schemeLoadAliasCapability],
427
436
  config: {
428
437
  ...compactConfig(opts, fullTranscript.cells),
429
- ...(moduleRoot == null
438
+ ...(loader == null
430
439
  ? {}
431
440
  : {
432
- loader: programSourceLoader(moduleRoot),
441
+ loader,
433
442
  dirname: "",
434
443
  ...(isolated
435
444
  ? {
@@ -13,17 +13,22 @@
13
13
  * `failsafe` set tells the caller to log a diagnostic.
14
14
  */
15
15
 
16
+ import type { Loader } from "@here.build/arrival-modules";
17
+
18
+ import type { ResourceIdentity } from "../resources/module-bundle.js";
19
+
16
20
  const INLINE_KEEP_FIRST =
17
21
  "(define (compact compact-transcript full-transcript) (let ((xs (transcript/cells compact-transcript))) (if (null? xs) xs (list (car xs)))))";
18
22
 
19
23
  export type CompactProgram = {
20
24
  readonly source: string;
21
25
  /**
22
- * Absolute path the source was read from. `require` (`.prompt` pretreats) is
23
- * jailed under its dirname; absent for the inline fallback and a dist stub
24
- * that has no `compact.prompt` next to it.
26
+ * Source label for diagnostics. Local adapters can supply an absolute path;
27
+ * it never grants module access by itself.
25
28
  */
26
29
  readonly sourcePath: string | null;
30
+ readonly resource?: ResourceIdentity;
31
+ readonly moduleLoader?: Loader;
27
32
  /**
28
33
  * Set only on the inline keep-first failsafe: why no real program could be
29
34
  * loaded. The caller must surface it; a silent failsafe would hide a broken
@@ -1,9 +1,14 @@
1
1
  export { type SessionCommandHost } from "./types.js";
2
- export { failLeftoverApprovables, turnLoopStopped } from "./cancel.js";
2
+ export { failLeftoverApprovables, isWorkReachable, turnLoopStopped } from "./cancel.js";
3
3
  export { abortParkableWork, dispatchedMcpSettled, liveEvalWork, mcpJobOnWire } from "./park.js";
4
4
  export { admitCompact, admitTurn, cancelTurnCommand, drainPromptQueue, retryTurn, rewindTurn } from "./commands.js";
5
5
  export { type CompactProgram, type CompactStrategyResolve, loadCompactProgram } from "./compact.js";
6
- export { applyCompact, compactReducerCapability } from "./compact-env.js";
6
+ export {
7
+ applyCompact,
8
+ compactReducerCapability,
9
+ type CompactApplyResult,
10
+ type CompactStrategyOpts,
11
+ } from "./compact-env.js";
7
12
  export { type CompactHost, runCompactJob } from "./compact-actor.js";
8
13
  export {
9
14
  CONTEXT_NEED_REMINDER,
@@ -3,7 +3,13 @@
3
3
  * program / turn handlers do not seal. On-wire MCP and live eval bags hold
4
4
  * the wrap until they settle; undispatched MCP is aborted without a receipt.
5
5
  */
6
- import { evalProgramOf, MCPToolJob, McpToolExpectation, SessionRoot, SessionTurn } from "@inhuman.tools/harness-model";
6
+ import {
7
+ evalProgramOf,
8
+ MCPToolJob,
9
+ McpToolExpectation,
10
+ SessionRoot,
11
+ SessionTurn,
12
+ } from "@inhuman.tools/harness-model/session";
7
13
  import { Job, walkWork } from "@inhuman.tools/plexus-expectations";
8
14
 
9
15
  import type { ParkWork } from "./types.js";
@@ -34,7 +34,7 @@ import {
34
34
  UPDATE_METADATA_TOOL_NAME,
35
35
  type SessionUnit,
36
36
  type TurnChunk,
37
- } from "@inhuman.tools/harness-model";
37
+ } from "@inhuman.tools/harness-model/session";
38
38
 
39
39
  /** Quiet sealed session rounds before the armed round carries the nag. */
40
40
  export const SIDE_CHANNEL_REMINDER_EVERY = 5;
@@ -10,7 +10,7 @@ import type {
10
10
  SessionSystemPrompt,
11
11
  SessionTurn,
12
12
  SkillSpec,
13
- } from "@inhuman.tools/harness-model";
13
+ } from "@inhuman.tools/harness-model/session";
14
14
  import type { Job, WorkExpectation } from "@inhuman.tools/plexus-expectations";
15
15
 
16
16
  export type SessionCommandHost = {
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Glance-sized sitting-ask question. Family + optional description.
3
+ * Not pretty-mcp interpolation — command/path stay off KernelRoot.
4
+ */
5
+ import {
6
+ AgentMessageJob,
7
+ type ApprovableJob,
8
+ BotCallJob,
9
+ CallJob,
10
+ clampChars,
11
+ flattenOverviewLine,
12
+ GLANCE_ASK_DESCRIPTION_MAX_CHARS,
13
+ GLANCE_ASK_LINE_MAX_CHARS,
14
+ MCPToolJob,
15
+ ToolCallJob,
16
+ } from "@inhuman.tools/harness-model/session";
17
+
18
+ export function glanceAskLine(head: ApprovableJob | null): string {
19
+ if (head == null) return "";
20
+ if (!(head instanceof ToolCallJob)) return "";
21
+ const args = head.args;
22
+ const description =
23
+ args != null && typeof args.description === "string"
24
+ ? flattenOverviewLine(args.description, GLANCE_ASK_DESCRIPTION_MAX_CHARS)
25
+ : "";
26
+ if (description !== "") return clampChars(`approve ${description}?`, GLANCE_ASK_LINE_MAX_CHARS);
27
+ if (head instanceof BotCallJob) return "confirm bot?";
28
+ if (head instanceof CallJob || head instanceof AgentMessageJob) return "confirm agent?";
29
+ if (head instanceof MCPToolJob) {
30
+ const tool = head.serverToolName.trim() || head.name.trim();
31
+ return tool === "" ? "" : clampChars(`confirm ${tool}?`, GLANCE_ASK_LINE_MAX_CHARS);
32
+ }
33
+ const name = head.name.trim();
34
+ if (name === "shell") return "confirm shell command?";
35
+ if (name === "edit_file") return "confirm edit?";
36
+ if (name === "write_file") return "confirm write?";
37
+ if (name === "") return "";
38
+ return clampChars(`confirm ${name}?`, GLANCE_ASK_LINE_MAX_CHARS);
39
+ }
@@ -0,0 +1,41 @@
1
+ export type SessionCheckpoint = {
2
+ readonly sessionId: string;
3
+ readonly incarnation: string;
4
+ readonly revision: number;
5
+ readonly state: Uint8Array;
6
+ };
7
+
8
+ export type SessionReceipt = {
9
+ readonly sessionId: string;
10
+ readonly incarnation: string;
11
+ readonly revision: number;
12
+ };
13
+
14
+ export type SessionDurability =
15
+ | { readonly kind: "memory" }
16
+ | {
17
+ readonly kind: "durable";
18
+ commit(checkpoint: SessionCheckpoint): Promise<SessionReceipt>;
19
+ };
20
+
21
+ export function receiptCovers(receipt: SessionReceipt, checkpoint: SessionCheckpoint): boolean {
22
+ return (
23
+ receipt.sessionId === checkpoint.sessionId &&
24
+ receipt.incarnation === checkpoint.incarnation &&
25
+ receipt.revision >= checkpoint.revision
26
+ );
27
+ }
28
+
29
+ export class SessionPersistenceError extends Error {
30
+ constructor(message = "session checkpoint was not durably acknowledged") {
31
+ super(message);
32
+ this.name = "SessionPersistenceError";
33
+ }
34
+ }
35
+
36
+ export class SessionOwnershipError extends Error {
37
+ constructor(message = "session execution no longer owns this document") {
38
+ super(message);
39
+ this.name = "SessionOwnershipError";
40
+ }
41
+ }
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+
3
+ export type SessionEffectRequest = {
4
+ readonly sessionId: string;
5
+ readonly operationId: string;
6
+ readonly incarnation: string;
7
+ readonly method: string;
8
+ readonly payload: string;
9
+ };
10
+
11
+ export const sessionEffectOutcomeSchema = z.discriminatedUnion("kind", [
12
+ z.object({ kind: z.literal("success"), payload: z.string() }),
13
+ z.object({ kind: z.literal("refused"), message: z.string() }),
14
+ z.object({ kind: z.literal("unknown"), message: z.string() }),
15
+ ]);
16
+
17
+ export type SessionEffectOutcome = z.infer<typeof sessionEffectOutcomeSchema>;
18
+
19
+ export type SessionEffectPort = {
20
+ invoke(request: SessionEffectRequest, signal: AbortSignal): Promise<SessionEffectOutcome>;
21
+ reconcile?(request: SessionEffectRequest, signal: AbortSignal): Promise<SessionEffectOutcome>;
22
+ };
23
+
24
+ const requestSchema = z.object({
25
+ sessionId: z.string(),
26
+ operationId: z.string(),
27
+ incarnation: z.string(),
28
+ method: z.string(),
29
+ payload: z.string(),
30
+ });
31
+
32
+ export const effectRecordSchema = z.discriminatedUnion("phase", [
33
+ z.object({ phase: z.literal("intent"), request: requestSchema }),
34
+ z.object({ phase: z.literal("result"), request: requestSchema, outcome: sessionEffectOutcomeSchema }),
35
+ ]);
@@ -0,0 +1,19 @@
1
+ export { SessionRuntime, type SessionRecoveryPolicy } from "./runtime.js";
2
+ export {
3
+ receiptCovers,
4
+ SessionOwnershipError,
5
+ SessionPersistenceError,
6
+ type SessionCheckpoint,
7
+ type SessionDurability,
8
+ type SessionReceipt,
9
+ } from "./durability.js";
10
+ export {
11
+ sessionEffectOutcomeSchema,
12
+ type SessionEffectOutcome,
13
+ type SessionEffectPort,
14
+ type SessionEffectRequest,
15
+ } from "./effects.js";
16
+ export { recoverSessionJobs, type JobRecoveryOptions, type RecoveredJob } from "./recovery.js";
17
+ export { interruptSession } from "./interrupt.js";
18
+ export { sessionSummary, observeSessionSummary, type SessionSummary } from "./summary.js";
19
+ export { glanceAskLine } from "./ask-line.js";
@@ -0,0 +1,34 @@
1
+ import {
2
+ CallJob,
3
+ CompactJob,
4
+ CompletionJob,
5
+ ProgrammaticJob,
6
+ ToolCallJob,
7
+ type SessionRoot,
8
+ } from "@inhuman.tools/harness-model/session";
9
+ import { WorkExpectation, walkWork } from "@inhuman.tools/plexus-expectations";
10
+ import { runInAction } from "mobx";
11
+
12
+ export function interruptSession(root: SessionRoot, message: string): void {
13
+ runInAction(() => {
14
+ for (const turn of root.liveTurns) {
15
+ if (turn.reducerExited) continue;
16
+ for (const node of walkWork(turn)) {
17
+ if (node instanceof CallJob) continue;
18
+ if (node instanceof WorkExpectation && !node.isDone) node.fail("cancel", message, "null");
19
+ if ((node instanceof ToolCallJob || node instanceof ProgrammaticJob) && !node.completed) {
20
+ node.outcomeSnap ??= { ok: false, text: message };
21
+ node.markComplete();
22
+ } else if (node instanceof CompactJob && !node.completed) {
23
+ node.failed = true;
24
+ node.notice = message;
25
+ node.markComplete();
26
+ } else if (node instanceof CompletionJob && !node.completed) node.markComplete();
27
+ }
28
+ turn.cancelRequested = true;
29
+ turn.broke = message;
30
+ turn.reducerExited = true;
31
+ turn.markComplete();
32
+ }
33
+ });
34
+ }