@mono-agent/agent-app 0.19.1 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/README.md +307 -32
  2. package/dist/acp-bridge.d.ts.map +1 -1
  3. package/dist/acp-bridge.js +113 -54
  4. package/dist/acp-bridge.js.map +1 -1
  5. package/dist/acp-session-store.d.ts +12 -0
  6. package/dist/acp-session-store.d.ts.map +1 -0
  7. package/dist/acp-session-store.js +74 -0
  8. package/dist/acp-session-store.js.map +1 -0
  9. package/dist/agent-artifact-paths.d.ts +8 -0
  10. package/dist/agent-artifact-paths.d.ts.map +1 -0
  11. package/dist/agent-artifact-paths.js +11 -0
  12. package/dist/agent-artifact-paths.js.map +1 -0
  13. package/dist/agent-root-coordinator.d.ts +101 -0
  14. package/dist/agent-root-coordinator.d.ts.map +1 -0
  15. package/dist/agent-root-coordinator.js +470 -0
  16. package/dist/agent-root-coordinator.js.map +1 -0
  17. package/dist/app-controller-channels.d.ts +13 -1
  18. package/dist/app-controller-channels.d.ts.map +1 -1
  19. package/dist/app-controller-channels.js +152 -20
  20. package/dist/app-controller-channels.js.map +1 -1
  21. package/dist/app-controller-continuation.d.ts +2 -0
  22. package/dist/app-controller-continuation.d.ts.map +1 -1
  23. package/dist/app-controller-continuation.js.map +1 -1
  24. package/dist/app-controller-lifecycle.d.ts +5 -0
  25. package/dist/app-controller-lifecycle.d.ts.map +1 -1
  26. package/dist/app-controller-lifecycle.js +24 -5
  27. package/dist/app-controller-lifecycle.js.map +1 -1
  28. package/dist/app-controller-maintenance.d.ts +4 -0
  29. package/dist/app-controller-maintenance.d.ts.map +1 -1
  30. package/dist/app-controller-maintenance.js +34 -2
  31. package/dist/app-controller-maintenance.js.map +1 -1
  32. package/dist/app-controller-memory.d.ts +2 -0
  33. package/dist/app-controller-memory.d.ts.map +1 -1
  34. package/dist/app-controller-memory.js +6 -3
  35. package/dist/app-controller-memory.js.map +1 -1
  36. package/dist/app-controller-process-jobs.d.ts +49 -0
  37. package/dist/app-controller-process-jobs.d.ts.map +1 -0
  38. package/dist/app-controller-process-jobs.js +294 -0
  39. package/dist/app-controller-process-jobs.js.map +1 -0
  40. package/dist/app-controller-responder.d.ts +17 -0
  41. package/dist/app-controller-responder.d.ts.map +1 -1
  42. package/dist/app-controller-responder.js +164 -13
  43. package/dist/app-controller-responder.js.map +1 -1
  44. package/dist/app-controller-traceability.d.ts +8 -0
  45. package/dist/app-controller-traceability.d.ts.map +1 -1
  46. package/dist/app-controller-traceability.js +22 -1
  47. package/dist/app-controller-traceability.js.map +1 -1
  48. package/dist/app-controller-types.d.ts +1 -1
  49. package/dist/app-controller-types.d.ts.map +1 -1
  50. package/dist/app-controller-utils.d.ts +8 -0
  51. package/dist/app-controller-utils.d.ts.map +1 -1
  52. package/dist/app-controller-utils.js +24 -1
  53. package/dist/app-controller-utils.js.map +1 -1
  54. package/dist/app-controller.d.ts +45 -1
  55. package/dist/app-controller.d.ts.map +1 -1
  56. package/dist/app-controller.js +135 -39
  57. package/dist/app-controller.js.map +1 -1
  58. package/dist/background-log-maintenance.d.ts +85 -2
  59. package/dist/background-log-maintenance.d.ts.map +1 -1
  60. package/dist/background-log-maintenance.js +407 -136
  61. package/dist/background-log-maintenance.js.map +1 -1
  62. package/dist/background-runtime.d.ts.map +1 -1
  63. package/dist/background-runtime.js +22 -41
  64. package/dist/background-runtime.js.map +1 -1
  65. package/dist/background.d.ts +18 -26
  66. package/dist/background.d.ts.map +1 -1
  67. package/dist/background.js +105 -138
  68. package/dist/background.js.map +1 -1
  69. package/dist/channel-drivers/cron.d.ts +12 -1
  70. package/dist/channel-drivers/cron.d.ts.map +1 -1
  71. package/dist/channel-drivers/cron.js +121 -18
  72. package/dist/channel-drivers/cron.js.map +1 -1
  73. package/dist/channel-drivers/native-notify.d.ts +3 -0
  74. package/dist/channel-drivers/native-notify.d.ts.map +1 -1
  75. package/dist/channel-drivers/native-notify.js +2 -1
  76. package/dist/channel-drivers/native-notify.js.map +1 -1
  77. package/dist/channel-drivers/slack.d.ts.map +1 -1
  78. package/dist/channel-drivers/slack.js +41 -4
  79. package/dist/channel-drivers/slack.js.map +1 -1
  80. package/dist/channel-drivers/telegram.d.ts.map +1 -1
  81. package/dist/channel-drivers/telegram.js +46 -3
  82. package/dist/channel-drivers/telegram.js.map +1 -1
  83. package/dist/channel-drivers/tui.d.ts +13 -2
  84. package/dist/channel-drivers/tui.d.ts.map +1 -1
  85. package/dist/channel-drivers/tui.js +112 -3
  86. package/dist/channel-drivers/tui.js.map +1 -1
  87. package/dist/channels.d.ts +1 -1
  88. package/dist/channels.d.ts.map +1 -1
  89. package/dist/channels.js +4 -2
  90. package/dist/channels.js.map +1 -1
  91. package/dist/cli-args.d.ts +15 -1
  92. package/dist/cli-args.d.ts.map +1 -1
  93. package/dist/cli-args.js +82 -12
  94. package/dist/cli-args.js.map +1 -1
  95. package/dist/cli-background-command.d.ts +9 -2
  96. package/dist/cli-background-command.d.ts.map +1 -1
  97. package/dist/cli-background-command.js +179 -66
  98. package/dist/cli-background-command.js.map +1 -1
  99. package/dist/cli-commands.d.ts.map +1 -1
  100. package/dist/cli-commands.js +50 -12
  101. package/dist/cli-commands.js.map +1 -1
  102. package/dist/cli-help.d.ts.map +1 -1
  103. package/dist/cli-help.js +21 -1
  104. package/dist/cli-help.js.map +1 -1
  105. package/dist/config-reference.d.ts.map +1 -1
  106. package/dist/config-reference.js +105 -2
  107. package/dist/config-reference.js.map +1 -1
  108. package/dist/configured-agent.d.ts +64 -21
  109. package/dist/configured-agent.d.ts.map +1 -1
  110. package/dist/configured-agent.js +840 -156
  111. package/dist/configured-agent.js.map +1 -1
  112. package/dist/conversation-state-roots.d.ts +59 -0
  113. package/dist/conversation-state-roots.d.ts.map +1 -0
  114. package/dist/conversation-state-roots.js +248 -0
  115. package/dist/conversation-state-roots.js.map +1 -0
  116. package/dist/cron-control-store.d.ts +91 -0
  117. package/dist/cron-control-store.d.ts.map +1 -0
  118. package/dist/cron-control-store.js +1251 -0
  119. package/dist/cron-control-store.js.map +1 -0
  120. package/dist/cron-operator-service.d.ts +54 -0
  121. package/dist/cron-operator-service.d.ts.map +1 -0
  122. package/dist/cron-operator-service.js +352 -0
  123. package/dist/cron-operator-service.js.map +1 -0
  124. package/dist/doctor-session-history.d.ts +9 -0
  125. package/dist/doctor-session-history.d.ts.map +1 -0
  126. package/dist/doctor-session-history.js +313 -0
  127. package/dist/doctor-session-history.js.map +1 -0
  128. package/dist/doctor.d.ts +2 -1
  129. package/dist/doctor.d.ts.map +1 -1
  130. package/dist/doctor.js +356 -9
  131. package/dist/doctor.js.map +1 -1
  132. package/dist/index.d.ts +4 -0
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +2 -0
  135. package/dist/index.js.map +1 -1
  136. package/dist/interaction-bridge.d.ts +1 -0
  137. package/dist/interaction-bridge.d.ts.map +1 -1
  138. package/dist/interaction-bridge.js +54 -6
  139. package/dist/interaction-bridge.js.map +1 -1
  140. package/dist/jobs-command.d.ts +17 -0
  141. package/dist/jobs-command.d.ts.map +1 -0
  142. package/dist/jobs-command.js +207 -0
  143. package/dist/jobs-command.js.map +1 -0
  144. package/dist/launchd-lifecycle-lock.d.ts +36 -0
  145. package/dist/launchd-lifecycle-lock.d.ts.map +1 -0
  146. package/dist/launchd-lifecycle-lock.js +124 -0
  147. package/dist/launchd-lifecycle-lock.js.map +1 -0
  148. package/dist/launchd-log-monitor-status.d.ts +7 -0
  149. package/dist/launchd-log-monitor-status.d.ts.map +1 -0
  150. package/dist/launchd-log-monitor-status.js +224 -0
  151. package/dist/launchd-log-monitor-status.js.map +1 -0
  152. package/dist/launchd-logs.d.ts +9 -1
  153. package/dist/launchd-logs.d.ts.map +1 -1
  154. package/dist/launchd-logs.js +69 -16
  155. package/dist/launchd-logs.js.map +1 -1
  156. package/dist/launchd-maintenance-command.d.ts +10 -0
  157. package/dist/launchd-maintenance-command.d.ts.map +1 -0
  158. package/dist/launchd-maintenance-command.js +2 -0
  159. package/dist/launchd-maintenance-command.js.map +1 -0
  160. package/dist/launchd-maintenance-entry.d.ts +30 -0
  161. package/dist/launchd-maintenance-entry.d.ts.map +1 -0
  162. package/dist/launchd-maintenance-entry.js +206 -0
  163. package/dist/launchd-maintenance-entry.js.map +1 -0
  164. package/dist/launchd-maintenance-gate.d.ts +28 -0
  165. package/dist/launchd-maintenance-gate.d.ts.map +1 -0
  166. package/dist/launchd-maintenance-gate.js +80 -0
  167. package/dist/launchd-maintenance-gate.js.map +1 -0
  168. package/dist/launchd-private-files.d.ts +3 -0
  169. package/dist/launchd-private-files.d.ts.map +1 -0
  170. package/dist/launchd-private-files.js +58 -0
  171. package/dist/launchd-private-files.js.map +1 -0
  172. package/dist/launchd.d.ts +56 -2
  173. package/dist/launchd.d.ts.map +1 -1
  174. package/dist/launchd.js +210 -5
  175. package/dist/launchd.js.map +1 -1
  176. package/dist/local-configuration.d.ts.map +1 -1
  177. package/dist/local-configuration.js +34 -7
  178. package/dist/local-configuration.js.map +1 -1
  179. package/dist/managed-launchd-maintenance-environment.d.ts +3 -0
  180. package/dist/managed-launchd-maintenance-environment.d.ts.map +1 -0
  181. package/dist/managed-launchd-maintenance-environment.js +12 -0
  182. package/dist/managed-launchd-maintenance-environment.js.map +1 -0
  183. package/dist/managed-runtime-launch-proof.d.ts +11 -0
  184. package/dist/managed-runtime-launch-proof.d.ts.map +1 -0
  185. package/dist/managed-runtime-launch-proof.js +59 -0
  186. package/dist/managed-runtime-launch-proof.js.map +1 -0
  187. package/dist/managed-runtime-maintenance-entry.d.ts +10 -0
  188. package/dist/managed-runtime-maintenance-entry.d.ts.map +1 -0
  189. package/dist/managed-runtime-maintenance-entry.js +244 -0
  190. package/dist/managed-runtime-maintenance-entry.js.map +1 -0
  191. package/dist/managed-web-logs.d.ts +29 -11
  192. package/dist/managed-web-logs.d.ts.map +1 -1
  193. package/dist/managed-web-logs.js +244 -121
  194. package/dist/managed-web-logs.js.map +1 -1
  195. package/dist/managed-web-maintenance-environment.d.ts +6 -0
  196. package/dist/managed-web-maintenance-environment.d.ts.map +1 -0
  197. package/dist/managed-web-maintenance-environment.js +19 -0
  198. package/dist/managed-web-maintenance-environment.js.map +1 -0
  199. package/dist/mcp-apps.d.ts +37 -0
  200. package/dist/mcp-apps.d.ts.map +1 -0
  201. package/dist/mcp-apps.js +1753 -0
  202. package/dist/mcp-apps.js.map +1 -0
  203. package/dist/memory-command.d.ts +12 -0
  204. package/dist/memory-command.d.ts.map +1 -1
  205. package/dist/memory-command.js +510 -1
  206. package/dist/memory-command.js.map +1 -1
  207. package/dist/modules/known-tools.d.ts +1 -1
  208. package/dist/modules/known-tools.d.ts.map +1 -1
  209. package/dist/modules/known-tools.js +2 -0
  210. package/dist/modules/known-tools.js.map +1 -1
  211. package/dist/posted-reply-history.d.ts.map +1 -1
  212. package/dist/posted-reply-history.js +12 -0
  213. package/dist/posted-reply-history.js.map +1 -1
  214. package/dist/proactive-notify.d.ts +4 -1
  215. package/dist/proactive-notify.d.ts.map +1 -1
  216. package/dist/proactive-notify.js +43 -5
  217. package/dist/proactive-notify.js.map +1 -1
  218. package/dist/process-jobs-config.d.ts +84 -0
  219. package/dist/process-jobs-config.d.ts.map +1 -0
  220. package/dist/process-jobs-config.js +427 -0
  221. package/dist/process-jobs-config.js.map +1 -0
  222. package/dist/process-jobs-context.d.ts +24 -0
  223. package/dist/process-jobs-context.d.ts.map +1 -0
  224. package/dist/process-jobs-context.js +135 -0
  225. package/dist/process-jobs-context.js.map +1 -0
  226. package/dist/process-jobs-protection.d.ts +33 -0
  227. package/dist/process-jobs-protection.d.ts.map +1 -0
  228. package/dist/process-jobs-protection.js +74 -0
  229. package/dist/process-jobs-protection.js.map +1 -0
  230. package/dist/process-jobs-root-registry.d.ts +115 -0
  231. package/dist/process-jobs-root-registry.d.ts.map +1 -0
  232. package/dist/process-jobs-root-registry.js +978 -0
  233. package/dist/process-jobs-root-registry.js.map +1 -0
  234. package/dist/process-jobs-runtime.d.ts +38 -0
  235. package/dist/process-jobs-runtime.d.ts.map +1 -0
  236. package/dist/process-jobs-runtime.js +178 -0
  237. package/dist/process-jobs-runtime.js.map +1 -0
  238. package/dist/process-jobs-service.d.ts +70 -0
  239. package/dist/process-jobs-service.d.ts.map +1 -0
  240. package/dist/process-jobs-service.js +1989 -0
  241. package/dist/process-jobs-service.js.map +1 -0
  242. package/dist/process-jobs-store.d.ts +151 -0
  243. package/dist/process-jobs-store.d.ts.map +1 -0
  244. package/dist/process-jobs-store.js +1290 -0
  245. package/dist/process-jobs-store.js.map +1 -0
  246. package/dist/reply-artifacts.d.ts +65 -0
  247. package/dist/reply-artifacts.d.ts.map +1 -0
  248. package/dist/reply-artifacts.js +1147 -0
  249. package/dist/reply-artifacts.js.map +1 -0
  250. package/dist/reply-part-budget.d.ts +16 -0
  251. package/dist/reply-part-budget.d.ts.map +1 -0
  252. package/dist/reply-part-budget.js +57 -0
  253. package/dist/reply-part-budget.js.map +1 -0
  254. package/dist/request-model-override.d.ts +19 -0
  255. package/dist/request-model-override.d.ts.map +1 -1
  256. package/dist/request-model-override.js +54 -1
  257. package/dist/request-model-override.js.map +1 -1
  258. package/dist/request-scoped-mcp.d.ts +31 -0
  259. package/dist/request-scoped-mcp.d.ts.map +1 -0
  260. package/dist/request-scoped-mcp.js +197 -0
  261. package/dist/request-scoped-mcp.js.map +1 -0
  262. package/dist/run-history.d.ts.map +1 -1
  263. package/dist/run-history.js +50 -254
  264. package/dist/run-history.js.map +1 -1
  265. package/dist/runtime-option-extensions.d.ts +23 -0
  266. package/dist/runtime-option-extensions.d.ts.map +1 -1
  267. package/dist/runtime-option-extensions.js +55 -0
  268. package/dist/runtime-option-extensions.js.map +1 -1
  269. package/dist/runtime-routes.d.ts +7 -1
  270. package/dist/runtime-routes.d.ts.map +1 -1
  271. package/dist/runtime-routes.js +25 -0
  272. package/dist/runtime-routes.js.map +1 -1
  273. package/dist/secure-file-replace.d.ts +3 -0
  274. package/dist/secure-file-replace.d.ts.map +1 -1
  275. package/dist/secure-file-replace.js +31 -7
  276. package/dist/secure-file-replace.js.map +1 -1
  277. package/dist/session-history.d.ts +146 -0
  278. package/dist/session-history.d.ts.map +1 -0
  279. package/dist/session-history.js +347 -0
  280. package/dist/session-history.js.map +1 -0
  281. package/dist/sessions.d.ts +45 -2
  282. package/dist/sessions.d.ts.map +1 -1
  283. package/dist/sessions.js +849 -30
  284. package/dist/sessions.js.map +1 -1
  285. package/dist/web-command.d.ts +12 -11
  286. package/dist/web-command.d.ts.map +1 -1
  287. package/dist/web-command.js +554 -80
  288. package/dist/web-command.js.map +1 -1
  289. package/dist/web-log-maintenance-command.d.ts +7 -0
  290. package/dist/web-log-maintenance-command.d.ts.map +1 -0
  291. package/dist/web-log-maintenance-command.js +21 -0
  292. package/dist/web-log-maintenance-command.js.map +1 -0
  293. package/dist/web-log-maintenance-status.d.ts +26 -0
  294. package/dist/web-log-maintenance-status.d.ts.map +1 -0
  295. package/dist/web-log-maintenance-status.js +171 -0
  296. package/dist/web-log-maintenance-status.js.map +1 -0
  297. package/dist/web-log-maintenance.d.ts +32 -0
  298. package/dist/web-log-maintenance.d.ts.map +1 -0
  299. package/dist/web-log-maintenance.js +163 -0
  300. package/dist/web-log-maintenance.js.map +1 -0
  301. package/dist/web-maintenance-paths.d.ts +12 -0
  302. package/dist/web-maintenance-paths.d.ts.map +1 -0
  303. package/dist/web-maintenance-paths.js +24 -0
  304. package/dist/web-maintenance-paths.js.map +1 -0
  305. package/dist/wizard/prompts.d.ts.map +1 -1
  306. package/dist/wizard/prompts.js +1 -0
  307. package/dist/wizard/prompts.js.map +1 -1
  308. package/dist/wizard/run.js +1 -1
  309. package/dist/wizard/run.js.map +1 -1
  310. package/package.json +15 -15
  311. package/schema/mono-agent.config.schema.json +106 -1
  312. package/skills/mono-agent-composer/references/config-blueprint.md +27 -2
  313. package/skills/mono-agent-composer/references/discovery-questions.md +2 -2
  314. package/skills/mono-agent-composer/references/feature-coverage.md +6 -4
  315. package/skills/mono-agent-composer/references/package-map.md +19 -3
  316. package/skills/mono-agent-composer/references/playbooks.md +3 -3
  317. package/skills/mono-agent-composer/references/validation.md +4 -2
@@ -1,4 +1,4 @@
1
- import { createAgentHarness, createAgentResponder, createDurableHistoryStore, createToolPolicy, loadToolPolicyFromJsonFileSync, renderSkillIndexSection, } from "@mono-agent/agent-harness";
1
+ import { createAgentHarness, createAgentResponder, acquireToolHistoryWriter, createDurableHistoryStore, createToolPolicy, loadToolPolicyFromJsonFileSync, renderSkillIndexSection, ToolHistoryReader, toolHistoryLogicalConversationId, } from "@mono-agent/agent-harness";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { resolve as resolvePath } from "node:path";
@@ -6,11 +6,17 @@ import { setToolActivityPathRoots } from "@mono-agent/agent-contracts";
6
6
  import { resolveSupermemoryContainer } from "@mono-agent/config";
7
7
  import { createCompositeRunRecorder, createJsonlRunRecorder } from "@mono-agent/observability";
8
8
  import { createPhoenixRunExporter } from "@mono-agent/observability/otel";
9
- import { assertExecutionModeCompatible, createMonoRuntime, createPiOAuthApiKeyResolver, defaultExecutionModeForModel, describeMonoRuntimeSupport, modelReferenceKey, monoRuntimeSupportsSessionResume, parseMonoRuntimeModelReference, runtimeOptionsForLocalProvider, } from "@mono-agent/runtime-adapter";
9
+ import { assertExecutionModeCompatible, createMonoRuntime, createPiOAuthApiKeyResolver, createSrtSandboxEngine, defaultExecutionModeForModel, describeMonoRuntimeSupport, modelReferenceKey, monoRuntimeSupportsSessionResume, parseMonoRuntimeModelReference, runtimeOptionsForLocalProvider, mergeSandboxPolicies, } from "@mono-agent/runtime-adapter";
10
+ import { agentArtifactDerivedRoots } from "./agent-artifact-paths.js";
11
+ import { acquireAgentRootOwnership, assertAgentRootLeaseOutsideWorkspace, releaseAgentRootOwnershipWhenIdle, } from "./agent-root-coordinator.js";
10
12
  import { resolveMemoryRecallSettings } from "./memory-recall.js";
11
13
  import { BUILTIN_TOOL_NAMES, canonicalToolName, isAllowAllTools } from "./modules/known-tools.js";
12
14
  import { createSharedMemoryRecallRuntimeExtension, isSharedRecallStore, MemoryRetrievalService, } from "./memory-retrieval.js";
13
- import { composeRuntimeOptionExtensions } from "./runtime-option-extensions.js";
15
+ import { clearSessionsSandboxPolicy, composeRuntimeOptionExtensions, createClearSessionsRuntimeExtension, } from "./runtime-option-extensions.js";
16
+ import { configuredRuntimeFallbackModels, runtimeUsesFallbackRouter, } from "./runtime-routes.js";
17
+ import { resolveProcessJobsProtectionPosture, } from "./process-jobs-protection.js";
18
+ import { createProcessJobsRuntimeExtension, processJobsSandboxPolicy, PROCESS_JOBS_PI_NATIVE_REQUIRED_ERROR, PROCESS_JOBS_PROTECTION_UNAVAILABLE_ERROR, } from "./process-jobs-runtime.js";
19
+ import { attestProcessJobsRootRegistrySnapshot, loadProcessJobsRootRegistryProtection, processJobsProtectionPolicyRoots, registerProcessJobsRoot, } from "./process-jobs-root-registry.js";
14
20
  import { isReadSkillDenied } from "./skill-registry.js";
15
21
  import { loadSupermemoryPlugin } from "./supermemory-plugin.js";
16
22
  /**
@@ -148,10 +154,22 @@ function recorderCompositionDeps(config, options, internalHooks = {}) {
148
154
  export function createConfiguredAgentRuntime(input) {
149
155
  const config = isRuntimeOptions(input) ? input.config : input;
150
156
  const options = isRuntimeOptions(input) ? input : undefined;
157
+ return wrapOwnedConfiguredRuntime(createConfiguredAgentRuntimeBase(config, options), config, options?.cwd, options?.sandboxEngine);
158
+ }
159
+ /** @internal App/harness seam; callers are already inside the shared root/request choke point. */
160
+ export function createConfiguredAgentRuntimeForApp(input, internal = {}) {
161
+ const config = isRuntimeOptions(input) ? input.config : input;
162
+ const options = isRuntimeOptions(input) ? input : undefined;
163
+ return createConfiguredAgentRuntimeBase(config, options, internal.suppressSandboxEngine === true);
164
+ }
165
+ function createConfiguredAgentRuntimeBase(config, options, suppressSandboxEngine = false) {
151
166
  const fallback = fallbackChainForConfig(config, options);
167
+ const sandboxEngine = suppressSandboxEngine
168
+ ? undefined
169
+ : configuredSandboxEngine(config, options?.model ?? config.runtime.model, options?.sandboxEngine);
152
170
  const runtimeOptions = {
153
171
  ...runtimeHostOptionsForConfig(config),
154
- ...(options?.sandboxEngine === undefined ? {} : { sandboxEngine: options.sandboxEngine }),
172
+ ...(sandboxEngine === undefined ? {} : { sandboxEngine }),
155
173
  ...fallback,
156
174
  ...(fallback.fallbackChain === undefined
157
175
  ? {}
@@ -175,6 +193,107 @@ export function createConfiguredAgentRuntime(input) {
175
193
  };
176
194
  return createMonoRuntime(runtimeOptions);
177
195
  }
196
+ function configuredSandboxEngine(config, primaryModel, explicit) {
197
+ if (explicit !== undefined)
198
+ return explicit;
199
+ return [primaryModel, ...configuredRuntimeFallbackModels(config.runtime)]
200
+ .some((model) => model.sdk === "pi")
201
+ ? createSrtSandboxEngine()
202
+ : undefined;
203
+ }
204
+ function wrapOwnedConfiguredRuntime(runtime, config, agentRoot, configuredSandboxEngine) {
205
+ let security;
206
+ let disposed = false;
207
+ const loadSecurity = async () => {
208
+ const ownership = await acquireAgentRootOwnership(agentRoot);
209
+ try {
210
+ assertAgentRootLeaseOutsideWorkspace(ownership, config.runtime.workspace);
211
+ return { ownership };
212
+ }
213
+ catch (error) {
214
+ await releaseAgentRootOwnershipWhenIdle(ownership).catch(() => undefined);
215
+ throw error;
216
+ }
217
+ };
218
+ const secured = () => (security ??= loadSecurity());
219
+ const releaseOwnership = async () => {
220
+ const current = security;
221
+ security = undefined;
222
+ if (current === undefined)
223
+ return;
224
+ const { ownership } = await current.catch(() => ({ ownership: undefined }));
225
+ if (ownership !== undefined) {
226
+ await releaseAgentRootOwnershipWhenIdle(ownership);
227
+ }
228
+ };
229
+ return {
230
+ async run(systemPrompt, runOptions) {
231
+ if (disposed)
232
+ throw new Error("Configured runtime has been disposed.");
233
+ const { ownership } = await secured();
234
+ const registry = await loadProcessJobsRootRegistryProtection(ownership.agentRoot, config.runtime.workspace);
235
+ ownership.coordinator.synchronizeGeneration(registry.generation);
236
+ const boundary = await attestProcessJobsRootRegistrySnapshot(registry, config.runtime.workspace);
237
+ if (disposed)
238
+ throw new Error("Configured runtime has been disposed.");
239
+ const lease = ownership.coordinator.acquireRequestLease(boundary.generation);
240
+ try {
241
+ const attested = await attestProcessJobsRootRegistrySnapshot(boundary, config.runtime.workspace);
242
+ const protectedRoots = processJobsProtectionPolicyRoots(attested);
243
+ let effectiveOptions = runOptions;
244
+ if (protectedRoots.length > 0) {
245
+ if (!configuredRoutesOnlyPiNative(config, runOptions.model)) {
246
+ throw new Error(PROCESS_JOBS_PI_NATIVE_REQUIRED_ERROR);
247
+ }
248
+ if (runOptions.model.sdk !== "pi")
249
+ throw new Error(PROCESS_JOBS_PI_NATIVE_REQUIRED_ERROR);
250
+ const sandboxEngine = runOptions.sandboxEngine
251
+ ?? configuredSandboxEngine
252
+ ?? createSrtSandboxEngine();
253
+ if (!await sandboxEngine.isAvailable().catch(() => false)) {
254
+ throw new Error(PROCESS_JOBS_PROTECTION_UNAVAILABLE_ERROR);
255
+ }
256
+ const sandboxPolicy = mergeSandboxPolicies(runOptions.sandboxPolicy, processJobsSandboxPolicy({ coreConfig: config, protectedRoots }));
257
+ if (sandboxPolicy === undefined) {
258
+ throw new Error(PROCESS_JOBS_PROTECTION_UNAVAILABLE_ERROR);
259
+ }
260
+ effectiveOptions = {
261
+ ...runOptions,
262
+ sandboxPolicy,
263
+ sandboxEngine,
264
+ };
265
+ }
266
+ return await runtime.run(systemPrompt, effectiveOptions);
267
+ }
268
+ finally {
269
+ lease.releaseAfterSettlement();
270
+ }
271
+ },
272
+ ...(runtime.configureTools === undefined
273
+ ? {}
274
+ : { configureTools: runtime.configureTools.bind(runtime) }),
275
+ ...(runtime.syncSession === undefined ? {} : { syncSession: runtime.syncSession.bind(runtime) }),
276
+ ...(runtime.refreshSession === undefined ? {} : { refreshSession: runtime.refreshSession.bind(runtime) }),
277
+ ...(runtime.retireDurableSession === undefined
278
+ ? {}
279
+ : { retireDurableSession: runtime.retireDurableSession.bind(runtime) }),
280
+ ...(runtime.disposeSession === undefined ? {} : { disposeSession: runtime.disposeSession.bind(runtime) }),
281
+ ...(runtime.invalidateSession === undefined
282
+ ? {}
283
+ : { invalidateSession: runtime.invalidateSession.bind(runtime) }),
284
+ async disposeAllSessions() {
285
+ if (disposed)
286
+ return;
287
+ disposed = true;
288
+ try {
289
+ await runtime.disposeAllSessions?.();
290
+ }
291
+ finally {
292
+ await releaseOwnership();
293
+ }
294
+ },
295
+ };
296
+ }
178
297
  /**
179
298
  * Narrow the loaded MCP servers to the ones a profile names.
180
299
  *
@@ -327,6 +446,11 @@ function subagentsRuntimeOptions(config, deps) {
327
446
  const childSystemPrompt = childSkills === undefined
328
447
  ? request.systemPrompt
329
448
  : `${request.systemPrompt}\n\n${renderSkillIndexSection(childSkills)}`;
449
+ if (childModel.sdk !== "pi"
450
+ && (deps.processJobsProtectionPosture.requiresPiNative
451
+ || sandboxPolicyCarriesProtectedRoots(request.sandboxPolicy))) {
452
+ throw new Error(PROCESS_JOBS_PI_NATIVE_REQUIRED_ERROR);
453
+ }
330
454
  return await runtime.run(childSystemPrompt, {
331
455
  model: childModel,
332
456
  executionMode: childExecutionMode,
@@ -446,6 +570,27 @@ function fallbackChainForConfig(config, options) {
446
570
  ],
447
571
  };
448
572
  }
573
+ function configuredRoutesOnlyPiNative(config, primary) {
574
+ try {
575
+ const fallback = fallbackChainForConfig(config, { config, model: primary });
576
+ const routes = fallback.fallbackChain?.map((entry) => entry.model) ?? [primary];
577
+ return routes.length > 0 && routes.every((model) => model.sdk === "pi");
578
+ }
579
+ catch {
580
+ return false;
581
+ }
582
+ }
583
+ function sandboxPolicyCarriesProtectedRoots(policy) {
584
+ if (policy === undefined || policy === null || typeof policy !== "object")
585
+ return false;
586
+ try {
587
+ const roots = policy.protectedRoots;
588
+ return roots !== undefined && (!Array.isArray(roots) || roots.length > 0);
589
+ }
590
+ catch {
591
+ return true;
592
+ }
593
+ }
449
594
  let memoryBujoModule;
450
595
  let memorySearchModule;
451
596
  const loadMemoryBujoModule = async () => (memoryBujoModule ??= await import("@mono-agent/memory/bujo"));
@@ -455,156 +600,581 @@ export async function createConfiguredAgentHarness(options) {
455
600
  }
456
601
  async function createConfiguredAgentHarnessInternal(options, internalHooks = {}) {
457
602
  const config = options.config;
458
- // Chat activity lines are formatted deep in the streaming layer, which has no
459
- // per-message workspace to hand down. Without this the root falls back to
460
- // `process.cwd()` for a service-managed agent, whatever directory the
461
- // supervisor started it in — and tool previews expose the full machine layout.
462
- setToolActivityPathRoots({ workspaceRoot: config.runtime.workspace, homeDir: homedir() });
463
- const model = options.model ?? config.runtime.model;
464
- const executionMode = options.executionMode ?? config.runtime.executionMode;
465
- const runtime = options.runtime ?? createConfiguredAgentRuntime({
466
- config,
467
- model,
468
- executionMode,
469
- ...(options.sandboxEngine === undefined ? {} : { sandboxEngine: options.sandboxEngine }),
470
- });
471
- // The memory LLM must NOT ride the channel `runtime`: that runtime carries the
472
- // channel fallback chain whose primary is `config.runtime.model`, and the
473
- // fallback router overrides each run's per-call `model` — so reusing it would
474
- // execute memory capture on `config.runtime.model` instead of
475
- // `config.memory.llm.model`. createConfiguredMemory builds the memory LLM its own
476
- // fallback-free runtime when no `memoryRuntime` is injected.
477
- const configuredMemory = options.memory ?? (await createConfiguredMemory(config, {
478
- ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
479
- }));
480
- const memory = configuredMemoryForHarness(config, configuredMemory);
481
- const memoryRecall = resolveMemoryRecallSettings(config) === undefined
482
- || !(memory instanceof MemoryRetrievalService)
483
- ? undefined
484
- : createSharedMemoryRecallRuntimeExtension(memory, {
485
- ...(options.onMemoryRecallUnavailable === undefined
603
+ const ownership = await acquireAgentRootOwnership(options.cwd ?? process.cwd());
604
+ const agentRoot = ownership.agentRoot;
605
+ let ownershipTransferred = false;
606
+ try {
607
+ assertAgentRootLeaseOutsideWorkspace(ownership, config.runtime.workspace);
608
+ const loadedProcessJobsRegistry = internalHooks.processJobs?.registry
609
+ ?? (internalHooks.bootstrapProcessJobs?.stateDir === undefined
610
+ ? await loadProcessJobsRootRegistryProtection(ownership.agentRoot, config.runtime.workspace)
611
+ : (await registerProcessJobsRoot({
612
+ agentRoot: ownership.agentRoot,
613
+ workspace: config.runtime.workspace,
614
+ stateDir: internalHooks.bootstrapProcessJobs.stateDir,
615
+ coordinator: ownership.coordinator,
616
+ })).snapshot);
617
+ const processJobsRegistry = loadedProcessJobsRegistry.kind === "failed"
618
+ ? loadedProcessJobsRegistry
619
+ : await attestProcessJobsRootRegistrySnapshot(loadedProcessJobsRegistry, config.runtime.workspace);
620
+ ownership.coordinator.synchronizeGeneration(processJobsRegistry.generation);
621
+ const processJobsProtectionPosture = internalHooks.processJobs?.protectionPosture
622
+ ?? resolveProcessJobsProtectionPosture({
623
+ settings: internalHooks.bootstrapProcessJobs?.settings
624
+ ?? { enabled: false, unsafeAllowUnprotectedState: false },
625
+ registry: processJobsRegistry,
626
+ coreConfig: config,
627
+ });
628
+ const processJobsProtectedRoots = processJobsProtectionPolicyRoots(processJobsRegistry);
629
+ const artifactDerivedRoots = agentArtifactDerivedRoots(config.artifacts.dir);
630
+ // Chat activity lines are formatted deep in the streaming layer, which has no
631
+ // per-message workspace to hand down. Without this the root falls back to
632
+ // `process.cwd()` — for a service-managed agent, whatever directory the
633
+ // supervisor started it in — and tool previews expose the full machine layout.
634
+ setToolActivityPathRoots({ workspaceRoot: config.runtime.workspace, homeDir: homedir() });
635
+ const model = options.model ?? config.runtime.model;
636
+ const executionMode = options.executionMode ?? config.runtime.executionMode;
637
+ const fallbackModels = configuredRuntimeFallbackModels(config.runtime);
638
+ const sandboxEngine = processJobsProtectionPosture.suppressSyntheticSandbox
639
+ ? undefined
640
+ : configuredSandboxEngine(config, model, options.sandboxEngine);
641
+ const harnessSandboxPolicy = processJobsProtectionPosture.suppressSyntheticSandbox
642
+ ? config.sandbox
643
+ : processJobsProtectedRoots.length === 0
644
+ ? config.sandbox
645
+ : processJobsSandboxPolicy({ coreConfig: config, protectedRoots: processJobsProtectedRoots });
646
+ const runtime = options.runtime ?? createConfiguredAgentRuntimeForApp({
647
+ config,
648
+ cwd: agentRoot,
649
+ model,
650
+ executionMode,
651
+ ...(sandboxEngine === undefined ? {} : { sandboxEngine }),
652
+ }, {
653
+ suppressSandboxEngine: processJobsProtectionPosture.suppressSyntheticSandbox,
654
+ });
655
+ const clearSessionsBoundaryOptions = {
656
+ cwd: agentRoot,
657
+ workspace: config.runtime.workspace,
658
+ baseModel: model,
659
+ fallbackModels,
660
+ ...(harnessSandboxPolicy === undefined ? {} : { sandboxPolicy: harnessSandboxPolicy }),
661
+ ...(processJobsProtectionPosture.suppressSyntheticSandbox
662
+ ? { suppressSyntheticSandbox: true }
663
+ : {}),
664
+ };
665
+ const routedClearSessionsPolicy = runtimeUsesFallbackRouter(config.runtime)
666
+ ? clearSessionsSandboxPolicy(clearSessionsBoundaryOptions)
667
+ : undefined;
668
+ // Request policy is still attested and applied per turn below. Configure the
669
+ // same stable engine/policy into the routed ToolContext so Pi filesystem
670
+ // builtins resolve their native boundary there; per-route-native projection
671
+ // clears both fields from every non-Pi attempt.
672
+ runtime.configureTools?.({
673
+ workspace: config.runtime.workspace,
674
+ ...(sandboxEngine === undefined ? {} : { sandboxEngine }),
675
+ ...(routedClearSessionsPolicy === undefined ? {} : { sandboxPolicy: routedClearSessionsPolicy }),
676
+ });
677
+ // The memory LLM must NOT ride the channel `runtime`: that runtime carries the
678
+ // channel fallback chain whose primary is `config.runtime.model`, and the
679
+ // fallback router overrides each run's per-call `model` — so reusing it would
680
+ // execute memory capture on `config.runtime.model` instead of
681
+ // `config.memory.llm.model`. createConfiguredMemory builds the memory LLM its own
682
+ // fallback-free runtime when no `memoryRuntime` is injected.
683
+ const configuredMemory = options.memory ?? (await createConfiguredMemoryInternal(config, { cwd: agentRoot }, processJobsProtectionPosture));
684
+ const memory = configuredMemoryForHarness(config, configuredMemory);
685
+ const memoryRecall = resolveMemoryRecallSettings(config) === undefined
686
+ || !(memory instanceof MemoryRetrievalService)
687
+ ? undefined
688
+ : createSharedMemoryRecallRuntimeExtension(memory, {
689
+ ...(options.onMemoryRecallUnavailable === undefined
690
+ ? {}
691
+ : { onUnavailable: options.onMemoryRecallUnavailable }),
692
+ });
693
+ const composedRuntimeOptionsForRequest = composeRuntimeOptionExtensions([
694
+ memoryRecall,
695
+ options.runtimeOptionsForRequest,
696
+ ], {
697
+ // The app-owned, read-only MemoryRecall endpoint is part of every configured
698
+ // memory tier. Preserve only this exact extension under an authenticated
699
+ // request override; arbitrary caller/action MCP extensions remain excluded.
700
+ preserveMcpServersUnderOverride: memoryRecall === undefined ? [] : [memoryRecall],
701
+ });
702
+ const clearSessionsRuntimeOptionsForRequest = createClearSessionsRuntimeExtension(composedRuntimeOptionsForRequest, clearSessionsBoundaryOptions);
703
+ const runtimeOptionsForRequest = createProcessJobsRuntimeExtension({
704
+ next: clearSessionsRuntimeOptionsForRequest,
705
+ ownership,
706
+ registry: processJobsRegistry,
707
+ service: internalHooks.processJobs?.service,
708
+ coreConfig: config,
709
+ baseModel: model,
710
+ channelId: internalHooks.processJobs?.channelId,
711
+ sandboxEngine,
712
+ protectionPosture: processJobsProtectionPosture,
713
+ routesOnlyPiNative: internalHooks.processJobs?.routesOnlyPiNative
714
+ ?? (() => configuredRoutesOnlyPiNative(config, model)),
715
+ });
716
+ const subagents = subagentsRuntimeOptions(config, {
717
+ runtime,
718
+ baseModel: model,
719
+ baseExecutionMode: executionMode,
720
+ processJobsProtectionPosture,
721
+ ...(options.runtimeForModel === undefined ? {} : { runtimeForModel: options.runtimeForModel }),
722
+ });
723
+ const runtimeOptions = mergeStaticRuntimeOptions(runtimeOptionsForLocalProvider(model, config.providers?.local), configRuntimeFlags(config), sandboxEngine === undefined ? undefined : { sandboxEngine }, subagents, options.runtimeOptions);
724
+ const sessionOptions = {
725
+ mode: config.runtime.session.mode,
726
+ idleTimeoutMs: config.runtime.session.idleTimeoutMs,
727
+ // Any fallback makes the logical run stateless. A provider-owned session
728
+ // cannot safely cross the route boundary, even when both bridges happen to
729
+ // expose resume support. History replay remains available to every attempt.
730
+ supportsResume: hasConfiguredFallback(config)
731
+ ? false
732
+ : supportsSessionResume(model, executionMode),
733
+ ...(config.runtime.session.isolateProactive === undefined
734
+ ? {}
735
+ : { isolateProactive: config.runtime.session.isolateProactive }),
736
+ ...(options.onSessionEvent === undefined ? {} : { onSessionEvent: options.onSessionEvent }),
737
+ };
738
+ const piSessionsRoot = config.providers?.piNative?.piSessionsRoot;
739
+ const retireDurableSession = runtime.retireDurableSession?.bind(runtime);
740
+ const historyRoot = artifactDerivedRoots.history;
741
+ const baseHistoryStore = options.historyStore ?? createDurableHistoryStore({
742
+ root: historyRoot,
743
+ maxMessages: DEFAULT_HISTORY_MAX_MESSAGES,
744
+ ...(piSessionsRoot === undefined || retireDurableSession === undefined
486
745
  ? {}
487
- : { onUnavailable: options.onMemoryRecallUnavailable }),
746
+ : {
747
+ retireProviderSession: async (providerSessionId) => {
748
+ await retireDurableSession(providerSessionId, piSessionsRoot);
749
+ },
750
+ }),
488
751
  });
489
- const runtimeOptionsForRequest = composeRuntimeOptionExtensions([
490
- memoryRecall,
491
- options.runtimeOptionsForRequest,
492
- ], {
493
- // The app-owned, read-only MemoryRecall endpoint is part of every configured
494
- // memory tier. Preserve only this exact extension under an authenticated
495
- // request override; arbitrary caller/action MCP extensions remain excluded.
496
- preserveMcpServersUnderOverride: memoryRecall === undefined ? [] : [memoryRecall],
497
- });
498
- const subagents = subagentsRuntimeOptions(config, {
499
- runtime,
500
- baseModel: model,
501
- baseExecutionMode: executionMode,
502
- ...(options.runtimeForModel === undefined ? {} : { runtimeForModel: options.runtimeForModel }),
503
- });
504
- const runtimeOptions = mergeStaticRuntimeOptions(runtimeOptionsForLocalProvider(model, config.providers?.local), configRuntimeFlags(config), options.sandboxEngine === undefined ? undefined : { sandboxEngine: options.sandboxEngine }, subagents, options.runtimeOptions);
505
- const sessionOptions = {
506
- mode: config.runtime.session.mode,
507
- idleTimeoutMs: config.runtime.session.idleTimeoutMs,
508
- // Any fallback makes the logical run stateless. A provider-owned session
509
- // cannot safely cross the route boundary, even when both bridges happen to
510
- // expose resume support. History replay remains available to every attempt.
511
- supportsResume: hasConfiguredFallback(config)
512
- ? false
513
- : supportsSessionResume(model, executionMode),
514
- ...(config.runtime.session.isolateProactive === undefined
515
- ? {}
516
- : { isolateProactive: config.runtime.session.isolateProactive }),
517
- ...(options.onSessionEvent === undefined ? {} : { onSessionEvent: options.onSessionEvent }),
518
- };
519
- const piSessionsRoot = config.providers?.piNative?.piSessionsRoot;
520
- const retireDurableSession = runtime.retireDurableSession?.bind(runtime);
521
- const baseHistoryStore = options.historyStore ?? createDurableHistoryStore({
522
- root: resolvePath(config.artifacts.dir, "..", "history"),
523
- maxMessages: DEFAULT_HISTORY_MAX_MESSAGES,
524
- ...(piSessionsRoot === undefined || retireDurableSession === undefined
525
- ? {}
526
- : {
527
- retireProviderSession: async (providerSessionId) => {
528
- await retireDurableSession(providerSessionId, piSessionsRoot);
529
- },
530
- }),
531
- });
532
- const historyStore = internalHooks.wrapHistoryStore?.(baseHistoryStore) ?? baseHistoryStore;
533
- return createAgentHarness({
534
- identityPath: config.context.identityPath,
535
- ...(config.context.soulPath === undefined ? {} : { soulPath: config.context.soulPath }),
536
- ...(config.context.skillsRoot === undefined ? {} : { skillsRoot: config.context.skillsRoot }),
537
- ...(config.context.skillMaxBytes === undefined ? {} : { skillMaxBytes: config.context.skillMaxBytes }),
538
- ...(config.context.skillDisclosure === undefined ? {} : { skillDisclosure: config.context.skillDisclosure }),
539
- selectedSkills: config.context.selectedSkills,
540
- runtime,
541
- model,
542
- executionMode,
543
- cwd: config.runtime.workspace,
544
- ...(config.runtime.effort === undefined ? {} : { effort: config.runtime.effort }),
545
- ...(config.runtime.maxTurns === undefined ? {} : { maxTurns: config.runtime.maxTurns }),
546
- ...(config.providers?.piNative?.piSessionsRoot === undefined
547
- ? {}
548
- : { piSessionsRoot: config.providers.piNative.piSessionsRoot }),
549
- session: sessionOptions,
550
- ...(config.concurrency?.maxConcurrentRuns === undefined && config.concurrency?.maxPendingRuns === undefined
551
- ? {}
552
- : {
553
- concurrency: {
554
- ...(config.concurrency?.maxConcurrentRuns === undefined
555
- ? {}
556
- : { maxConcurrentRuns: config.concurrency.maxConcurrentRuns }),
557
- ...(config.concurrency?.maxPendingRuns === undefined
558
- ? {}
559
- : { maxPendingRuns: config.concurrency.maxPendingRuns }),
560
- },
561
- }),
562
- runtimeOptions,
563
- ...(runtimeOptionsForRequest === undefined
752
+ const historyStore = internalHooks.wrapHistoryStore?.(baseHistoryStore) ?? baseHistoryStore;
753
+ const rollover = internalHooks.sessionRollover ?? config.runtime.session.rollover;
754
+ // Conversation history and managed-tool history are distinct contracts. A
755
+ // caller-supplied message store remains the sole message-history owner, while
756
+ // the configured app keeps its independent lifecycle sidecar and
757
+ // SessionHistory capability. Acquisition stays lazy so tool-free runs do not
758
+ // create either the sidecar or its owner database.
759
+ const toolHistory = lazyConfiguredToolHistory({
760
+ root: historyRoot,
761
+ artifactRoot: resolvePath(config.artifacts.dir, "tool-output"),
762
+ rollover,
763
+ ...(options.onToolHistoryWarning === undefined
764
+ ? {}
765
+ : { onWarning: options.onToolHistoryWarning }),
766
+ });
767
+ try {
768
+ const harness = createAgentHarness({
769
+ identityPath: config.context.identityPath,
770
+ ...(config.context.soulPath === undefined ? {} : { soulPath: config.context.soulPath }),
771
+ ...(config.context.skillsRoot === undefined ? {} : { skillsRoot: config.context.skillsRoot }),
772
+ ...(config.context.skillMaxBytes === undefined ? {} : { skillMaxBytes: config.context.skillMaxBytes }),
773
+ ...(config.context.skillDisclosure === undefined ? {} : { skillDisclosure: config.context.skillDisclosure }),
774
+ selectedSkills: config.context.selectedSkills,
775
+ runtime,
776
+ model,
777
+ executionMode,
778
+ cwd: config.runtime.workspace,
779
+ ...(config.runtime.effort === undefined ? {} : { effort: config.runtime.effort }),
780
+ ...(config.runtime.maxTurns === undefined ? {} : { maxTurns: config.runtime.maxTurns }),
781
+ ...(config.providers?.piNative?.piSessionsRoot === undefined
782
+ ? {}
783
+ : { piSessionsRoot: config.providers.piNative.piSessionsRoot }),
784
+ session: sessionOptions,
785
+ ...(config.concurrency?.maxConcurrentRuns === undefined && config.concurrency?.maxPendingRuns === undefined
786
+ ? {}
787
+ : {
788
+ concurrency: {
789
+ ...(config.concurrency?.maxConcurrentRuns === undefined
790
+ ? {}
791
+ : { maxConcurrentRuns: config.concurrency.maxConcurrentRuns }),
792
+ ...(config.concurrency?.maxPendingRuns === undefined
793
+ ? {}
794
+ : { maxPendingRuns: config.concurrency.maxPendingRuns }),
795
+ },
796
+ }),
797
+ runtimeOptions,
798
+ ...(runtimeOptionsForRequest === undefined
799
+ ? {}
800
+ : { runtimeOptionsForRequest }),
801
+ ...(config.tools.mcpRequestContextServers === undefined
802
+ ? {}
803
+ : {
804
+ mcpRequestContext: {
805
+ serverNames: config.tools.mcpRequestContextServers,
806
+ runOutputRoot: artifactDerivedRoots.outbound,
807
+ ...(options.progressCapabilityIssuer === undefined
808
+ ? {}
809
+ : { progressCapabilityIssuer: options.progressCapabilityIssuer }),
810
+ },
811
+ }),
812
+ ...(config.tools.continuationServers === undefined || options.continuationCapabilityIssuer === undefined
813
+ ? {}
814
+ : {
815
+ continuationContext: {
816
+ serverNames: config.tools.continuationServers,
817
+ capabilityIssuer: options.continuationCapabilityIssuer,
818
+ },
819
+ }),
820
+ ...(options.runtimeForModel === undefined ? {} : { runtimeForModel: options.runtimeForModel }),
821
+ ...(memory === undefined ? {} : { memory }),
822
+ memoryWriteMode: config.memory?.writeMode ?? "disabled",
823
+ ...(options.onMemoryWarning === undefined ? {} : { onMemoryWarning: options.onMemoryWarning }),
824
+ historyStore,
825
+ toolHistory,
826
+ ...(options.turnHistoryEnricher === undefined ? {} : { turnHistoryEnricher: options.turnHistoryEnricher }),
827
+ // Inbound channel attachments are saved here (under the artifacts dir, which
828
+ // sits inside a sandbox-readable root) so the agent can open them by path.
829
+ attachmentsDir: artifactDerivedRoots.attachments,
830
+ toolPolicy: createToolPolicy(toolPolicyInput(config)),
831
+ ...(harnessSandboxPolicy === undefined ? {} : { sandboxPolicy: harnessSandboxPolicy }),
832
+ recorderFactory: ({ runId, conversationId, userInput, source, sourceDetail, isolated }) => composeRunRecorder(recorderCompositionDeps(config, options, internalHooks), {
833
+ runId,
834
+ conversationId,
835
+ runKind: "channel",
836
+ ...(isolated === undefined ? {} : { isolated }),
837
+ ...(userInput === undefined ? {} : { userInput }),
838
+ ...(source === undefined ? {} : { source }),
839
+ ...(sourceDetail === undefined ? {} : { sourceDetail }),
840
+ }),
841
+ ...(options.createRunId === undefined ? {} : { createRunId: options.createRunId }),
842
+ ...(options.now === undefined ? {} : { now: options.now }),
843
+ });
844
+ ownershipTransferred = true;
845
+ return harnessWithAgentRootOwnership(harness, ownership);
846
+ }
847
+ catch (error) {
848
+ await toolHistory?.release?.().catch(() => undefined);
849
+ throw error;
850
+ }
851
+ }
852
+ finally {
853
+ if (!ownershipTransferred) {
854
+ await releaseAgentRootOwnershipWhenIdle(ownership).catch(() => undefined);
855
+ }
856
+ }
857
+ }
858
+ function harnessWithAgentRootOwnership(harness, ownership) {
859
+ let disposePromise;
860
+ return {
861
+ run: harness.run.bind(harness),
862
+ ...(harness.submit === undefined ? {} : { submit: harness.submit.bind(harness) }),
863
+ ...(harness.offerLiveInput === undefined
564
864
  ? {}
565
- : { runtimeOptionsForRequest }),
566
- ...(config.tools.mcpRequestContextServers === undefined
865
+ : { offerLiveInput: harness.offerLiveInput.bind(harness) }),
866
+ ...(harness.cancel === undefined ? {} : { cancel: harness.cancel.bind(harness) }),
867
+ ...(harness.resetConversation === undefined
567
868
  ? {}
568
- : {
569
- mcpRequestContext: {
570
- serverNames: config.tools.mcpRequestContextServers,
571
- runOutputRoot: resolvePath(config.artifacts.dir, "outbound"),
572
- ...(options.progressCapabilityIssuer === undefined
573
- ? {}
574
- : { progressCapabilityIssuer: options.progressCapabilityIssuer }),
575
- },
576
- }),
577
- ...(config.tools.continuationServers === undefined || options.continuationCapabilityIssuer === undefined
869
+ : { resetConversation: harness.resetConversation.bind(harness) }),
870
+ ...(harness.appendVerbatimTurn === undefined
578
871
  ? {}
579
- : {
580
- continuationContext: {
581
- serverNames: config.tools.continuationServers,
582
- capabilityIssuer: options.continuationCapabilityIssuer,
583
- },
584
- }),
585
- ...(options.runtimeForModel === undefined ? {} : { runtimeForModel: options.runtimeForModel }),
586
- ...(memory === undefined ? {} : { memory }),
587
- memoryWriteMode: config.memory?.writeMode ?? "disabled",
588
- ...(options.onMemoryWarning === undefined ? {} : { onMemoryWarning: options.onMemoryWarning }),
589
- historyStore,
590
- ...(options.turnHistoryEnricher === undefined ? {} : { turnHistoryEnricher: options.turnHistoryEnricher }),
591
- // Inbound channel attachments are saved here (under the artifacts dir, which
592
- // sits inside a sandbox-readable root) so the agent can open them by path.
593
- attachmentsDir: resolvePath(config.artifacts.dir, "attachments"),
594
- toolPolicy: createToolPolicy(toolPolicyInput(config)),
595
- ...(config.sandbox === undefined ? {} : { sandboxPolicy: config.sandbox }),
596
- recorderFactory: ({ runId, conversationId, userInput, source, sourceDetail, isolated }) => composeRunRecorder(recorderCompositionDeps(config, options, internalHooks), {
597
- runId,
598
- conversationId,
599
- runKind: "channel",
600
- ...(isolated === undefined ? {} : { isolated }),
601
- ...(userInput === undefined ? {} : { userInput }),
602
- ...(source === undefined ? {} : { source }),
603
- ...(sourceDetail === undefined ? {} : { sourceDetail }),
604
- }),
605
- ...(options.createRunId === undefined ? {} : { createRunId: options.createRunId }),
606
- ...(options.now === undefined ? {} : { now: options.now }),
607
- });
872
+ : { appendVerbatimTurn: harness.appendVerbatimTurn.bind(harness) }),
873
+ dispose: () => {
874
+ disposePromise ??= Promise.resolve()
875
+ .then(async () => await harness.dispose?.())
876
+ .finally(async () => {
877
+ await releaseAgentRootOwnershipWhenIdle(ownership);
878
+ });
879
+ return disposePromise;
880
+ },
881
+ };
882
+ }
883
+ const TOOL_HISTORY_ACQUISITION_FAILURE_BACKOFF_MS = 30_000;
884
+ const TOOL_HISTORY_ACQUISITION_FAILURE_BACKOFF_MAX_MS = 5 * 60_000;
885
+ export function lazyConfiguredToolHistory(options) {
886
+ const failureBackoffMs = options.acquisitionFailureBackoffMs
887
+ ?? TOOL_HISTORY_ACQUISITION_FAILURE_BACKOFF_MS;
888
+ if (!Number.isSafeInteger(failureBackoffMs) || failureBackoffMs < 1) {
889
+ throw new TypeError("tool history acquisition failure backoff must be a positive integer.");
890
+ }
891
+ const failureBackoffMaxMs = options.acquisitionFailureBackoffMaxMs
892
+ ?? Math.max(TOOL_HISTORY_ACQUISITION_FAILURE_BACKOFF_MAX_MS, failureBackoffMs);
893
+ if (!Number.isSafeInteger(failureBackoffMaxMs)
894
+ || failureBackoffMaxMs < failureBackoffMs) {
895
+ throw new TypeError("tool history acquisition failure backoff maximum must be an integer at least as large as the initial backoff.");
896
+ }
897
+ const now = options.now ?? Date.now;
898
+ let sharedAttempt;
899
+ let latestTurnGeneration = 0;
900
+ const turnStates = new Map();
901
+ let resetTail = Promise.resolve();
902
+ let retirementTail = Promise.resolve();
903
+ let acquisitionOutage = false;
904
+ let consecutiveAcquisitionFailures = 0;
905
+ const reader = new ToolHistoryReader(options.root);
906
+ const warn = (message) => {
907
+ try {
908
+ options.onWarning?.(message);
909
+ }
910
+ catch { /* diagnostics are best-effort */ }
911
+ };
912
+ const turnKey = (conversationId, runId) => JSON.stringify([conversationId, runId]);
913
+ const stateFor = (conversationId, runId) => {
914
+ const key = turnKey(conversationId, runId);
915
+ const existing = turnStates.get(key);
916
+ if (existing !== undefined)
917
+ return existing;
918
+ const state = { generation: ++latestTurnGeneration };
919
+ if (sharedAttempt?.failed === true
920
+ && sharedAttempt.retryAtMs !== undefined
921
+ && now() < sharedAttempt.retryAtMs) {
922
+ state.attempt = sharedAttempt;
923
+ }
924
+ turnStates.set(key, state);
925
+ return state;
926
+ };
927
+ const failedPromise = (code) => {
928
+ const error = Object.assign(new Error("Tool history writer became unavailable; a fresh boundary must reacquire it."), { code });
929
+ const failure = Promise.reject(error);
930
+ void failure.catch(() => undefined);
931
+ return failure;
932
+ };
933
+ const nextFailureBackoffMs = () => {
934
+ let backoffMs = failureBackoffMs;
935
+ for (let index = 0; index < consecutiveAcquisitionFailures && backoffMs < failureBackoffMaxMs; index += 1) {
936
+ backoffMs = backoffMs > Math.floor(failureBackoffMaxMs / 2)
937
+ ? failureBackoffMaxMs
938
+ : Math.min(failureBackoffMaxMs, backoffMs * 2);
939
+ }
940
+ consecutiveAcquisitionFailures += 1;
941
+ return backoffMs;
942
+ };
943
+ const retire = (attempt) => {
944
+ if (attempt.retired || attempt.handle === undefined)
945
+ return;
946
+ attempt.retired = true;
947
+ retirementTail = retirementTail.then(async () => {
948
+ try {
949
+ await attempt.handle?.release();
950
+ }
951
+ catch { /* replacement acquisition must remain armed */ }
952
+ });
953
+ };
954
+ const latchExistingTurns = (attempt, failure) => {
955
+ const failedThrough = latestTurnGeneration;
956
+ for (const state of turnStates.values()) {
957
+ if (state.generation > failedThrough)
958
+ continue;
959
+ if (state.attempt === undefined || state.attempt === attempt)
960
+ state.failure = failure;
961
+ }
962
+ };
963
+ const invalidate = (attempt, code) => {
964
+ if (attempt.failed)
965
+ return attempt.failurePromise ?? attempt.promise;
966
+ const failure = failedPromise(code);
967
+ attempt.failed = true;
968
+ attempt.failurePromise = failure;
969
+ attempt.retryAtMs = now() + nextFailureBackoffMs();
970
+ latchExistingTurns(attempt, failure);
971
+ retire(attempt);
972
+ if (sharedAttempt === attempt) {
973
+ sharedAttempt = {
974
+ promise: failure,
975
+ failurePromise: failure,
976
+ failed: true,
977
+ retryAtMs: attempt.retryAtMs,
978
+ retired: true,
979
+ };
980
+ }
981
+ acquisitionOutage = true;
982
+ return failure;
983
+ };
984
+ const startAttempt = () => {
985
+ const acquisition = retirementTail.then(async () => await (options.acquireWriter ?? acquireToolHistoryWriter)({
986
+ root: options.root,
987
+ artifactRoot: options.artifactRoot,
988
+ ...(options.onWarning === undefined ? {} : { onWarning: options.onWarning }),
989
+ }));
990
+ const attempt = {
991
+ promise: acquisition,
992
+ failed: false,
993
+ retired: false,
994
+ };
995
+ attempt.promise = acquisition.then((handle) => {
996
+ attempt.handle = handle;
997
+ consecutiveAcquisitionFailures = 0;
998
+ if (acquisitionOutage) {
999
+ warn("Tool history writer acquisition recovered; lifecycle persistence resumed.");
1000
+ }
1001
+ acquisitionOutage = false;
1002
+ return handle;
1003
+ }).catch((error) => {
1004
+ attempt.failed = true;
1005
+ attempt.failurePromise = attempt.promise;
1006
+ const retryBackoffMs = nextFailureBackoffMs();
1007
+ attempt.retryAtMs = now() + retryBackoffMs;
1008
+ latchExistingTurns(attempt, attempt.promise);
1009
+ if (!acquisitionOutage) {
1010
+ acquisitionOutage = true;
1011
+ warn(`Tool history writer acquisition failed (${toolHistoryAcquisitionErrorCode(error)}); new turns fail fast for ${String(retryBackoffMs)} ms and repeated failures back off to ${String(failureBackoffMaxMs)} ms, while explicit reset may retry immediately.`);
1012
+ }
1013
+ throw error;
1014
+ });
1015
+ void attempt.promise.catch(() => undefined);
1016
+ sharedAttempt = attempt;
1017
+ return attempt;
1018
+ };
1019
+ const selectAttempt = (bypassFailureBackoff) => {
1020
+ if (sharedAttempt !== undefined) {
1021
+ if (sharedAttempt.handle?.writer.isClosed === true) {
1022
+ invalidate(sharedAttempt, "history_writer_closed");
1023
+ }
1024
+ const withinFailureBackoff = sharedAttempt.retryAtMs !== undefined
1025
+ && now() < sharedAttempt.retryAtMs;
1026
+ if (!sharedAttempt.failed
1027
+ || (!bypassFailureBackoff && withinFailureBackoff)) {
1028
+ return sharedAttempt;
1029
+ }
1030
+ sharedAttempt = undefined;
1031
+ }
1032
+ return startAttempt();
1033
+ };
1034
+ const acquireForTurn = async (state) => {
1035
+ if (state.failure !== undefined)
1036
+ return await state.failure;
1037
+ state.attempt ??= selectAttempt(false);
1038
+ const handle = await state.attempt.promise;
1039
+ if (handle.writer.isClosed) {
1040
+ state.failure = invalidate(state.attempt, "history_writer_closed");
1041
+ return await state.failure;
1042
+ }
1043
+ return handle;
1044
+ };
1045
+ const writer = {
1046
+ createSink(binding) {
1047
+ const state = stateFor(binding.conversationId, binding.runId);
1048
+ return async (event) => {
1049
+ const handle = await acquireForTurn(state);
1050
+ try {
1051
+ const result = await handle.writer.persist(binding, event);
1052
+ const terminalCode = terminalToolHistoryWriterFailureCode(result, handle);
1053
+ if (terminalCode !== undefined && state.attempt !== undefined) {
1054
+ state.failure = invalidate(state.attempt, terminalCode);
1055
+ }
1056
+ return result;
1057
+ }
1058
+ catch (error) {
1059
+ const terminalCode = terminalToolHistoryWriterFailureCode(error, handle);
1060
+ if (terminalCode !== undefined && state.attempt !== undefined) {
1061
+ state.failure = invalidate(state.attempt, terminalCode);
1062
+ }
1063
+ throw error;
1064
+ }
1065
+ };
1066
+ },
1067
+ async finishRun(binding, status, failureKind) {
1068
+ const key = turnKey(binding.conversationId, binding.runId);
1069
+ const state = turnStates.get(key);
1070
+ try {
1071
+ if (state?.attempt === undefined || state.failure !== undefined)
1072
+ return;
1073
+ const handle = await state.attempt.promise.catch(() => undefined);
1074
+ try {
1075
+ await handle?.writer.finishRun(binding, status, failureKind);
1076
+ }
1077
+ catch (error) {
1078
+ if (handle === undefined)
1079
+ return;
1080
+ const terminalCode = terminalToolHistoryWriterFailureCode(error, handle);
1081
+ if (terminalCode === undefined)
1082
+ throw error;
1083
+ invalidate(state.attempt, terminalCode);
1084
+ return;
1085
+ }
1086
+ const terminalCode = handle === undefined
1087
+ ? undefined
1088
+ : terminalToolHistoryWriterFailureCode(undefined, handle);
1089
+ if (terminalCode !== undefined)
1090
+ invalidate(state.attempt, terminalCode);
1091
+ }
1092
+ finally {
1093
+ turnStates.delete(key);
1094
+ }
1095
+ },
1096
+ async resetConversation(logicalConversationId) {
1097
+ const reset = resetTail.then(async () => {
1098
+ if (!await reader.exists())
1099
+ return;
1100
+ // Reset is serialized and host-explicit, so it may bypass only the
1101
+ // progressive post-failure cooldown. It still reuses a healthy/pending handle
1102
+ // and every fresh acquisition keeps the full restart-handoff ceiling.
1103
+ const observedAttempt = sharedAttempt;
1104
+ if (observedAttempt !== undefined && !observedAttempt.failed) {
1105
+ await observedAttempt.promise.catch(() => undefined);
1106
+ }
1107
+ let recoveryRetriesRemaining = 1;
1108
+ for (;;) {
1109
+ const attempt = selectAttempt(true);
1110
+ const handle = await attempt.promise;
1111
+ try {
1112
+ await handle.writer.resetConversation(logicalConversationId);
1113
+ }
1114
+ catch (error) {
1115
+ const terminalCode = reportedToolHistoryWriterFailureCode(error);
1116
+ if (terminalCode === undefined) {
1117
+ if (handle.writer.isClosed)
1118
+ invalidate(attempt, "history_writer_closed");
1119
+ throw error;
1120
+ }
1121
+ invalidate(attempt, terminalCode);
1122
+ if (recoveryRetriesRemaining === 0)
1123
+ throw error;
1124
+ recoveryRetriesRemaining -= 1;
1125
+ continue;
1126
+ }
1127
+ const terminalCode = terminalToolHistoryWriterFailureCode(undefined, handle);
1128
+ if (terminalCode !== undefined)
1129
+ invalidate(attempt, terminalCode);
1130
+ // A resolved reset was acknowledged only after its transaction
1131
+ // committed. Retire a concurrently closed handle, but never open a
1132
+ // second destructive reset window for an operation that succeeded.
1133
+ return;
1134
+ }
1135
+ });
1136
+ resetTail = reset.then(() => undefined, () => undefined);
1137
+ await reset;
1138
+ },
1139
+ };
1140
+ return {
1141
+ writer,
1142
+ reader,
1143
+ logicalConversationId: (conversationId) => toolHistoryLogicalConversationId(conversationId, options.rollover),
1144
+ async release() {
1145
+ const attempt = sharedAttempt;
1146
+ sharedAttempt = undefined;
1147
+ if (attempt !== undefined) {
1148
+ await attempt.promise.catch(() => undefined);
1149
+ retire(attempt);
1150
+ }
1151
+ await retirementTail;
1152
+ },
1153
+ };
1154
+ }
1155
+ function terminalToolHistoryWriterFailureCode(value, handle) {
1156
+ return reportedToolHistoryWriterFailureCode(value)
1157
+ ?? (handle.writer.isClosed ? "history_writer_closed" : undefined);
1158
+ }
1159
+ function reportedToolHistoryWriterFailureCode(value) {
1160
+ const candidate = typeof value === "object" && value !== null
1161
+ ? "errorCode" in value
1162
+ ? value.errorCode
1163
+ : "code" in value
1164
+ ? value.code
1165
+ : undefined
1166
+ : undefined;
1167
+ if (candidate === "history_writer_closed")
1168
+ return candidate;
1169
+ return undefined;
1170
+ }
1171
+ function toolHistoryAcquisitionErrorCode(error) {
1172
+ const candidate = typeof error === "object" && error !== null && "code" in error
1173
+ ? error.code
1174
+ : undefined;
1175
+ return typeof candidate === "string" && /^history_[a-z0-9_]+$/u.test(candidate)
1176
+ ? candidate
1177
+ : "history_writer_unavailable";
608
1178
  }
609
1179
  function configuredMemoryForHarness(config, memory) {
610
1180
  if (memory instanceof MemoryRetrievalService || config.memory === undefined || !isSharedRecallStore(memory)) {
@@ -654,6 +1224,13 @@ function supportsSessionResume(model, executionMode) {
654
1224
  // provider default (30s). The harness degrades recall to empty on timeout.
655
1225
  const DEFAULT_EMBEDDINGS_TIMEOUT_MS = 10_000;
656
1226
  export async function createConfiguredMemory(config, deps = {}) {
1227
+ return await createConfiguredMemoryInternal(config, deps);
1228
+ }
1229
+ /** @internal App-only unsafe authority; deliberately absent from the package root. */
1230
+ export async function createConfiguredMemoryForApp(config, deps, protectionPosture) {
1231
+ return await createConfiguredMemoryInternal(config, deps, protectionPosture);
1232
+ }
1233
+ async function createConfiguredMemoryInternal(config, deps, protectionPosture) {
657
1234
  if (config.memory === undefined) {
658
1235
  return undefined;
659
1236
  }
@@ -705,7 +1282,7 @@ export async function createConfiguredMemory(config, deps = {}) {
705
1282
  throw new Error(`memory.embeddings.apiKeyEnv ${embeddingsConfig.apiKeyEnv} is declared but has no resolved value; ` +
706
1283
  `set ${embeddingsConfig.apiKeyEnv} before starting managed memory.`);
707
1284
  }
708
- const embeddings = search.createCircuitBreakerEmbeddingProvider(search.createEmbeddingProvider({
1285
+ const embeddings = wrapOwnedDirectEmbeddingProvider(search.createCircuitBreakerEmbeddingProvider(search.createEmbeddingProvider({
709
1286
  provider: embeddingsConfig?.provider ?? "ollama",
710
1287
  model: embeddingsConfig?.model ?? "nomic-embed-text:v1.5",
711
1288
  ...(embeddingsConfig?.endpoint !== undefined && { endpoint: embeddingsConfig.endpoint }),
@@ -718,7 +1295,7 @@ export async function createConfiguredMemory(config, deps = {}) {
718
1295
  ...(embeddingsConfig?.circuitBreaker?.cooldownMs !== undefined && {
719
1296
  cooldownMs: embeddingsConfig.circuitBreaker.cooldownMs,
720
1297
  }),
721
- });
1298
+ }), config, deps.cwd ?? process.cwd(), protectionPosture);
722
1299
  const dim = embeddingsConfig?.dim ?? 768;
723
1300
  if (mode === "journal") {
724
1301
  if (embeddingsConfig === undefined) {
@@ -746,7 +1323,7 @@ export async function createConfiguredMemory(config, deps = {}) {
746
1323
  const recording = deps.observability === undefined
747
1324
  ? undefined
748
1325
  : recorderCompositionDeps(config, deps.observability);
749
- const llm = configuredMemoryLlm(bujo, config, llmConfig, deps.memoryRuntime, recording);
1326
+ const llm = configuredMemoryLlm(bujo, config, llmConfig, deps.memoryRuntime, recording, deps.cwd ?? process.cwd(), protectionPosture);
750
1327
  if (llm === undefined) {
751
1328
  throw new Error("memory.mode 'bujo' could not construct the required memory.llm.");
752
1329
  }
@@ -773,16 +1350,16 @@ function configuredMemoryLlm(bujo, config, llmConfig,
773
1350
  // Explicit memory-LLM runtime (tests). MUST be fallback-chain-free — see the
774
1351
  // `memoryRuntime` doc on createConfiguredMemory. When undefined the memory LLM
775
1352
  // builds its own fallback-free runtime so the per-call memory model is primary.
776
- memoryRuntimeOverride, recording) {
1353
+ memoryRuntimeOverride, recording, agentRoot, protectionPosture) {
777
1354
  if (llmConfig === undefined) {
778
1355
  return undefined;
779
1356
  }
780
1357
  if (llmConfig.provider === "ollama") {
781
1358
  // The ollama memory LLM does not ride `runtime.run`, so it is not recorded.
782
- return bujo.createOllamaLlm({
1359
+ return wrapOwnedDirectMemoryLlm(bujo.createOllamaLlm({
783
1360
  model: llmConfig.model,
784
1361
  ...(llmConfig.endpoint !== undefined && { endpoint: llmConfig.endpoint }),
785
- });
1362
+ }), config, agentRoot, protectionPosture);
786
1363
  }
787
1364
  const model = parseMonoRuntimeModelReference(llmConfig.model);
788
1365
  const executionMode = llmConfig.executionMode ?? defaultExecutionModeForModel(model);
@@ -795,7 +1372,7 @@ memoryRuntimeOverride, recording) {
795
1372
  // channel runtime (which carries the fallback chain whose primary is
796
1373
  // `config.runtime.model`) is intentionally NOT reused — see the `memoryRuntime`
797
1374
  // doc on createConfiguredMemory.
798
- const runtime = memoryRuntimeOverride ?? createMonoRuntime(runtimeHostOptionsForConfig(config));
1375
+ const runtime = wrapPerRunOwnedConfiguredRuntime(memoryRuntimeOverride ?? createMonoRuntime(runtimeHostOptionsForConfig(config)), config, agentRoot, protectionPosture);
799
1376
  return createAgentHostMemoryLlm({
800
1377
  runtime,
801
1378
  model,
@@ -812,6 +1389,113 @@ memoryRuntimeOverride, recording) {
812
1389
  : {}),
813
1390
  });
814
1391
  }
1392
+ /**
1393
+ * Memory completion is a model-running surface even when no channel harness is
1394
+ * present. Acquire the canonical-root owner and protection generation for the
1395
+ * exact duration of the provider call; release follows actual runtime
1396
+ * settlement, never its abort signal.
1397
+ */
1398
+ function wrapPerRunOwnedConfiguredRuntime(runtime, config, agentRoot, protectionPosture) {
1399
+ return {
1400
+ ...runtime,
1401
+ async run(systemPrompt, runOptions) {
1402
+ const ownership = await acquireAgentRootOwnership(agentRoot);
1403
+ let requestLease;
1404
+ try {
1405
+ assertAgentRootLeaseOutsideWorkspace(ownership, config.runtime.workspace);
1406
+ const loaded = await loadProcessJobsRootRegistryProtection(ownership.agentRoot, config.runtime.workspace);
1407
+ ownership.coordinator.synchronizeGeneration(loaded.generation);
1408
+ const boundary = await attestProcessJobsRootRegistrySnapshot(loaded, config.runtime.workspace);
1409
+ requestLease = ownership.coordinator.acquireRequestLease(boundary.generation);
1410
+ const attested = await attestProcessJobsRootRegistrySnapshot(boundary, config.runtime.workspace);
1411
+ const protectedRoots = processJobsProtectionPolicyRoots(attested);
1412
+ let effectiveOptions = runOptions;
1413
+ if (protectedRoots.length > 0) {
1414
+ if (runOptions.model.sdk !== "pi")
1415
+ throw new Error(PROCESS_JOBS_PI_NATIVE_REQUIRED_ERROR);
1416
+ if (protectionPosture?.suppressSyntheticSandbox !== true) {
1417
+ const sandboxEngine = runOptions.sandboxEngine ?? createSrtSandboxEngine();
1418
+ if (!await sandboxEngine.isAvailable().catch(() => false)) {
1419
+ throw new Error(PROCESS_JOBS_PROTECTION_UNAVAILABLE_ERROR);
1420
+ }
1421
+ const sandboxPolicy = mergeSandboxPolicies(runOptions.sandboxPolicy, processJobsSandboxPolicy({ coreConfig: config, protectedRoots }));
1422
+ if (sandboxPolicy === undefined) {
1423
+ throw new Error(PROCESS_JOBS_PROTECTION_UNAVAILABLE_ERROR);
1424
+ }
1425
+ effectiveOptions = {
1426
+ ...runOptions,
1427
+ sandboxPolicy,
1428
+ sandboxEngine,
1429
+ };
1430
+ }
1431
+ }
1432
+ return await runtime.run(systemPrompt, effectiveOptions);
1433
+ }
1434
+ finally {
1435
+ requestLease?.releaseAfterSettlement();
1436
+ await releaseAgentRootOwnershipWhenIdle(ownership);
1437
+ }
1438
+ },
1439
+ };
1440
+ }
1441
+ function wrapOwnedDirectMemoryLlm(llm, config, agentRoot, protectionPosture) {
1442
+ return {
1443
+ ...llm,
1444
+ async complete(prompt, options) {
1445
+ const ownership = await acquireAgentRootOwnership(agentRoot);
1446
+ let requestLease;
1447
+ try {
1448
+ assertAgentRootLeaseOutsideWorkspace(ownership, config.runtime.workspace);
1449
+ const loaded = await loadProcessJobsRootRegistryProtection(ownership.agentRoot, config.runtime.workspace);
1450
+ ownership.coordinator.synchronizeGeneration(loaded.generation);
1451
+ const boundary = await attestProcessJobsRootRegistrySnapshot(loaded, config.runtime.workspace);
1452
+ requestLease = ownership.coordinator.acquireRequestLease(boundary.generation);
1453
+ const attested = await attestProcessJobsRootRegistrySnapshot(boundary, config.runtime.workspace);
1454
+ if (processJobsProtectionPolicyRoots(attested).length > 0
1455
+ && protectionPosture?.suppressSyntheticSandbox !== true) {
1456
+ throw new Error(PROCESS_JOBS_PI_NATIVE_REQUIRED_ERROR);
1457
+ }
1458
+ return await llm.complete(prompt, options);
1459
+ }
1460
+ finally {
1461
+ requestLease?.releaseAfterSettlement();
1462
+ await releaseAgentRootOwnershipWhenIdle(ownership);
1463
+ }
1464
+ },
1465
+ };
1466
+ }
1467
+ /**
1468
+ * Configured embedding providers execute outside the channel harness. Hold the
1469
+ * same canonical-root owner and generation lease through the true embed()
1470
+ * settlement, and reject the non-Pi provider before invocation whenever a
1471
+ * retained process-job root makes native SRT confinement mandatory.
1472
+ */
1473
+ function wrapOwnedDirectEmbeddingProvider(provider, config, agentRoot, protectionPosture) {
1474
+ return {
1475
+ ...provider,
1476
+ async embed(texts) {
1477
+ const ownership = await acquireAgentRootOwnership(agentRoot);
1478
+ let requestLease;
1479
+ try {
1480
+ assertAgentRootLeaseOutsideWorkspace(ownership, config.runtime.workspace);
1481
+ const loaded = await loadProcessJobsRootRegistryProtection(ownership.agentRoot, config.runtime.workspace);
1482
+ ownership.coordinator.synchronizeGeneration(loaded.generation);
1483
+ const boundary = await attestProcessJobsRootRegistrySnapshot(loaded, config.runtime.workspace);
1484
+ requestLease = ownership.coordinator.acquireRequestLease(boundary.generation);
1485
+ const attested = await attestProcessJobsRootRegistrySnapshot(boundary, config.runtime.workspace);
1486
+ if (processJobsProtectionPolicyRoots(attested).length > 0
1487
+ && protectionPosture?.suppressSyntheticSandbox !== true) {
1488
+ throw new Error(PROCESS_JOBS_PI_NATIVE_REQUIRED_ERROR);
1489
+ }
1490
+ return await provider.embed(texts);
1491
+ }
1492
+ finally {
1493
+ requestLease?.releaseAfterSettlement();
1494
+ await releaseAgentRootOwnershipWhenIdle(ownership);
1495
+ }
1496
+ },
1497
+ };
1498
+ }
815
1499
  const MEMORY_LLM_SYSTEM_PROMPT = [
816
1500
  "You are the private memory maintenance LLM for mono-agent.",
817
1501
  "Return only the requested JSON or plain text.",