@xopcai/xopc 0.0.221 → 0.0.223

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 (232) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{activity-store-NItGQmvR.js → activity-store--GBciH10.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-B8SBTGg3.js → agent-avatar-dicebear-C93T3FLD.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-Ba4XF0Im.js +68 -0
  6. package/dist/gateway/static/root/assets/{api-53ZXsuyr.js → api-HoIz_ija.js} +1 -1
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-BtmPXaqu.js → app-management-settings-panel-Bhl8Xluk.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-t7HoJyNN.js → appearance-settings-CywEN-kG.js} +1 -1
  9. package/dist/gateway/static/root/assets/apps-page-BZY09nuJ.js +1 -0
  10. package/dist/gateway/static/root/assets/{archive-plugin-D8o8CP9Y.js → archive-plugin-CteCVsBP.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-CsLXzas8.js → attachment-load-CbNE_EPd.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-page-D-zgoAKz.js → automations-page-Bcqk6tOx.js} +1 -1
  13. package/dist/gateway/static/root/assets/automations-workspace-DtI6vsMf.js +6 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-B-MjP_22.js → binary-plugins-M_i3WS5M.js} +1 -1
  15. package/dist/gateway/static/root/assets/block-editor-eg9LEH2o.js +184 -0
  16. package/dist/gateway/static/root/assets/browser-settings-page-kKwbvzjj.js +8 -0
  17. package/dist/gateway/static/root/assets/{browser-workflow-inputs-Bi6RSSpD.js → browser-workflow-inputs-1UZ0vnIy.js} +1 -1
  18. package/dist/gateway/static/root/assets/{browser-workflows-page-EEFHUm1i.js → browser-workflows-page-D8ATQqZ5.js} +2 -2
  19. package/dist/gateway/static/root/assets/capabilities-settings-panel-CZRcis8B.js +7 -0
  20. package/dist/gateway/static/root/assets/{capability-presets-api-pC2fno7r.js → capability-presets-api-yNqTIuyd.js} +1 -1
  21. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CgdRkwnk.js +3 -0
  22. package/dist/gateway/static/root/assets/{channel-recipient-api-B5Szgu_z.js → channel-recipient-api-krezGsKc.js} +1 -1
  23. package/dist/gateway/static/root/assets/channels-settings-DETCY0Ku.js +1 -0
  24. package/dist/gateway/static/root/assets/channels-status-swr-DMxB7slL.js +1 -0
  25. package/dist/gateway/static/root/assets/{chat-terminal-dock-DT-OFl4r.js → chat-terminal-dock-DQ5bB4Su.js} +2 -2
  26. package/dist/gateway/static/root/assets/connectors-page-D9D92u28.js +2 -0
  27. package/dist/gateway/static/root/assets/{cron-expression-MZ_AJqrm.js → cron-expression-CEwPkdhh.js} +1 -1
  28. package/dist/gateway/static/root/assets/{date-picker-Bv9hZLuD.js → date-picker-DDAxTir8.js} +1 -1
  29. package/dist/gateway/static/root/assets/{dependency-picker-D-6oO_Vk.js → dependency-picker-CYVoSZwI.js} +1 -1
  30. package/dist/gateway/static/root/assets/{desktop-pet-CiI5caKj.js → desktop-pet-CrNKfW_c.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-settings-DhkSOy4K.js → desktop-pet-settings-C06PaB46.js} +1 -1
  32. package/dist/gateway/static/root/assets/{directory-picker-path-field-C4s4Pz8I.js → directory-picker-path-field--D7laeLF.js} +1 -1
  33. package/dist/gateway/static/root/assets/dist-DaxPZj9e.js +1 -0
  34. package/dist/gateway/static/root/assets/{dist-DY4Af0fY.js → dist-J_ySZvz0.js} +1 -1
  35. package/dist/gateway/static/root/assets/{extension-debug-page-DE7J1-6W.js → extension-debug-page-Dn4Px9KQ.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-page-DSnACD7E.js → extension-page-DzLf3fMQ.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-settings-page-Dk7k2Pok.js → extension-settings-page-DIchwtvB.js} +1 -1
  38. package/dist/gateway/static/root/assets/floating-ui.dom-B7uQOZZW.js +1 -0
  39. package/dist/gateway/static/root/assets/{gateway-config-api-IOA1OlHx.js → gateway-config-api-SmTGLhqK.js} +1 -1
  40. package/dist/gateway/static/root/assets/{gateway-config-swr-BzTbnu4O.js → gateway-config-swr-yUxMMVjK.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-settings-FX7QsgSQ.js → gateway-settings-yCDkOYzA.js} +1 -1
  42. package/dist/gateway/static/root/assets/{gateway-startup-retry-B2zpkNvi.js → gateway-startup-retry-9mrsL84j.js} +1 -1
  43. package/dist/gateway/static/root/assets/heartbeat-settings-CquIULHX.js +1 -0
  44. package/dist/gateway/static/root/assets/home-page-COSGBh4c.js +1 -0
  45. package/dist/gateway/static/root/assets/{image-generation-api-BXRjz-iY.js → image-generation-api-CgFAGDi8.js} +1 -1
  46. package/dist/gateway/static/root/assets/index-5XkDWH_Q.css +2 -0
  47. package/dist/gateway/static/root/assets/index-wnDPGRTi.js +74 -0
  48. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BjYiM4xq.js → keyboard-shortcuts-settings-DI2To7yl.js} +1 -1
  49. package/dist/gateway/static/root/assets/local-app-workbench-page-CAF5SZXv.js +2 -0
  50. package/dist/gateway/static/root/assets/{local-apps-page-7DHvwSgA.js → local-apps-page-CSEJsQ1F.js} +1 -1
  51. package/dist/gateway/static/root/assets/{locale-store-iKjqemWG.js → locale-store-CPylQjH9.js} +1 -1
  52. package/dist/gateway/static/root/assets/logs-page-D1KFPHL1.js +2 -0
  53. package/dist/gateway/static/root/assets/{management-settings-BSxppo9Z.js → management-settings-CgyaIyuk.js} +1 -1
  54. package/dist/gateway/static/root/assets/{markdown-split-D4oDjZtl.js → markdown-split-paG9qap9.js} +1 -1
  55. package/dist/gateway/static/root/assets/markdown-view-CUsob4Pz.js +69 -0
  56. package/dist/gateway/static/root/assets/{media-plugins-BPNTGROA.js → media-plugins-0Q-c_QIy.js} +1 -1
  57. package/dist/gateway/static/root/assets/messages-B82nsi9k.js +3 -0
  58. package/dist/gateway/static/root/assets/notes-api-CfKZYrBC.js +1 -0
  59. package/dist/gateway/static/root/assets/{notes-page-DU5tKPTq.js → notes-page-Bvb-ycX_.js} +1 -1
  60. package/dist/gateway/static/root/assets/notes-workbench-DelxGBof.js +4 -0
  61. package/dist/gateway/static/root/assets/oauth-authorization-window-Cdolr2wE.js +1 -0
  62. package/dist/gateway/static/root/assets/{popover-select-BzdnTy3h.js → popover-select-CccGri5Y.js} +1 -1
  63. package/dist/gateway/static/root/assets/{product-open-page-CPoQvD6P.js → product-open-page-DrvBwAns.js} +1 -1
  64. package/dist/gateway/static/root/assets/project-detail-page-C2EnYXEQ.js +1 -0
  65. package/dist/gateway/static/root/assets/projects-page-BiMPU6Oa.js +1 -0
  66. package/dist/gateway/static/root/assets/{registry-api-Cx3JxXSL.js → registry-api-B_5D8epF.js} +1 -1
  67. package/dist/gateway/static/root/assets/remote-access-hub-CpbNivVs.js +1 -0
  68. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-xqnWcvIv.js → runtime-tools-settings-panel-DKjdN4Lr.js} +1 -1
  69. package/dist/gateway/static/root/assets/{schema-form-CuIDPYu6.js → schema-form-DskqaNqC.js} +1 -1
  70. package/dist/gateway/static/root/assets/sessions-page-BxKtzsKI.js +1 -0
  71. package/dist/gateway/static/root/assets/settings-advanced-gate-BfBKMMUx.js +1 -0
  72. package/dist/gateway/static/root/assets/{settings-form-section-DkAv9ncE.js → settings-form-section-CZlMvHA9.js} +1 -1
  73. package/dist/gateway/static/root/assets/{settings-loading-skeleton-BJUQiEKb.js → settings-loading-skeleton-DS6lcAg9.js} +1 -1
  74. package/dist/gateway/static/root/assets/{settings-page-KHRbLwmw.js → settings-page-oE5J9tsV.js} +1 -1
  75. package/dist/gateway/static/root/assets/{setup-status-panel-C8jd20Db.js → setup-status-panel-C7oFCIqD.js} +1 -1
  76. package/dist/gateway/static/root/assets/share-preview-page-CPFAj8NE.js +2 -0
  77. package/dist/gateway/static/root/assets/shares-settings-B46DWZK4.js +1 -0
  78. package/dist/gateway/static/root/assets/skill-reload-api-CyKolUZ-.js +1 -0
  79. package/dist/gateway/static/root/assets/{skills-page-CKhR9J81.js → skills-page-Bh4gFi8e.js} +1 -1
  80. package/dist/gateway/static/root/assets/{skills-page.utils-AYX-e5sD.js → skills-page.utils-BQaUWP9z.js} +1 -1
  81. package/dist/gateway/static/root/assets/{system-settings-panel-IIF-K9ML.js → system-settings-panel-COAZoh_O.js} +1 -1
  82. package/dist/gateway/static/root/assets/task-detail-page-CXkhprf-.js +1 -0
  83. package/dist/gateway/static/root/assets/{theme-store-BOD2D0dJ.js → theme-store-BN-32Fz5.js} +1 -1
  84. package/dist/gateway/static/root/assets/{use-autosave-DcktF1j1.js → use-autosave-BAkToU15.js} +1 -1
  85. package/dist/gateway/static/root/assets/user-context-page-Br5wgARM.js +1 -0
  86. package/dist/gateway/static/root/assets/{voice-api-key-field-BMkNTHHE.js → voice-api-key-field-CC4z9-ux.js} +1 -1
  87. package/dist/gateway/static/root/assets/{work-discovery-overlay-DVoiXekA.js → work-discovery-overlay-dLVUYSSE.js} +1 -1
  88. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-Cw4LQjuM.js → workflow-run-setup-panel-BYU5-0KF.js} +2 -2
  89. package/dist/gateway/static/root/assets/workflows-page-CJ24a1HM.js +5 -0
  90. package/dist/gateway/static/root/index.html +15 -14
  91. package/dist/package.js +1 -1
  92. package/dist/src/agent/agent-instance-gateway.d.ts +3 -0
  93. package/dist/src/agent/agent-manager.d.ts +3 -0
  94. package/dist/src/agent/agent-manager.js +34 -28
  95. package/dist/src/agent/embedded/index.d.ts +1 -1
  96. package/dist/src/agent/embedded/index.js +2 -2
  97. package/dist/src/agent/embedded/model-runtime.js +1 -1
  98. package/dist/src/agent/embedded/run-for-session.js +102 -63
  99. package/dist/src/agent/embedded/run-turn.js +56 -28
  100. package/dist/src/agent/embedded/runs.d.ts +16 -29
  101. package/dist/src/agent/embedded/runs.js +44 -65
  102. package/dist/src/agent/embedded/types.d.ts +4 -0
  103. package/dist/src/agent/image/tool-model-config.js +1 -1
  104. package/dist/src/agent/ipc/inbox.js +1 -1
  105. package/dist/src/agent/memory/create-memory-manager.js +1 -1
  106. package/dist/src/agent/orchestration/agent-run-supervisor.d.ts +28 -0
  107. package/dist/src/agent/orchestration/agent-run-supervisor.js +49 -0
  108. package/dist/src/agent/orchestration/agent-turn-policy.d.ts +18 -0
  109. package/dist/src/agent/orchestration/agent-turn-policy.js +36 -0
  110. package/dist/src/agent/orchestration/llm-turn-retry.d.ts +5 -0
  111. package/dist/src/agent/orchestration/llm-turn-retry.js +34 -4
  112. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.d.ts +10 -1
  113. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js +25 -4
  114. package/dist/src/agent/service/async-queue.d.ts +16 -3
  115. package/dist/src/agent/service/async-queue.js +29 -11
  116. package/dist/src/agent/service/process-direct-streaming.js +27 -4
  117. package/dist/src/agent/service.js +3 -2
  118. package/dist/src/agent/session/session-inspector.js +1 -1
  119. package/dist/src/agent/skills/hub-lock.js +1 -1
  120. package/dist/src/agent/tasks/task-judge-service.js +3 -3
  121. package/dist/src/agent/tools/exec-command.d.ts +2 -0
  122. package/dist/src/agent/tools/exec-command.js +70 -7
  123. package/dist/src/agent-runtime/index.js +1 -1
  124. package/dist/src/agent-runtime/runtime-profile.js +1 -1
  125. package/dist/src/ai-assist/text-assist.js +1 -1
  126. package/dist/src/auth/credentials.js +2 -2
  127. package/dist/src/auth/oauth/index.js +3 -3
  128. package/dist/src/auth/oauth/registry.d.ts +2 -0
  129. package/dist/src/auth/oauth/registry.js +20 -12
  130. package/dist/src/auth/oauth/xopc-cloud.d.ts +1 -0
  131. package/dist/src/auth/oauth/xopc-cloud.js +32 -16
  132. package/dist/src/auth/profiles/store.js +1 -1
  133. package/dist/src/auth/sync-provider-auth.js +2 -2
  134. package/dist/src/browser/providers/browser-ext-install.js +1 -1
  135. package/dist/src/channels/attachments/voice-stt-webchat.js +20 -3
  136. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  137. package/dist/src/cli/commands/models.js +1 -1
  138. package/dist/src/cli/commands/tunnel.js +4 -5
  139. package/dist/src/config/index.js +4 -4
  140. package/dist/src/config/loader.js +1 -1
  141. package/dist/src/config/models-json.js +1 -1
  142. package/dist/src/config/schema.d.ts +18 -0
  143. package/dist/src/config/schema.js +19 -1
  144. package/dist/src/connectors/learning-coordinator.js +3 -19
  145. package/dist/src/extensions/lockfile.js +1 -1
  146. package/dist/src/gateway/agents-admin.js +1 -1
  147. package/dist/src/gateway/file-path-classifier.js +1 -1
  148. package/dist/src/gateway/hono/app.js +2 -1
  149. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  150. package/dist/src/gateway/hono/oauth-async.d.ts +1 -0
  151. package/dist/src/gateway/hono/oauth-async.js +11 -4
  152. package/dist/src/gateway/hono/routes/config.js +10 -0
  153. package/dist/src/gateway/hono/routes/host-fs.js +40 -1
  154. package/dist/src/gateway/hono/routes/media.js +33 -1
  155. package/dist/src/gateway/hono/routes/models.js +1 -1
  156. package/dist/src/gateway/hono/routes/notes.js +162 -7
  157. package/dist/src/gateway/hono/routes/shares.js +141 -6
  158. package/dist/src/gateway/hono/routes/tunnel.js +47 -10
  159. package/dist/src/gateway/hono/routes/voice.d.ts +2 -6
  160. package/dist/src/gateway/hono/routes/voice.js +28 -5
  161. package/dist/src/gateway/service/config-coordinator.js +1 -1
  162. package/dist/src/gateway/service.js +3 -3
  163. package/dist/src/notes/service.d.ts +6 -2
  164. package/dist/src/notes/service.js +36 -4
  165. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
  166. package/dist/src/providers/index.js +3 -3
  167. package/dist/src/providers/model-registry.js +1 -1
  168. package/dist/src/providers/register-runtime-providers.js +1 -1
  169. package/dist/src/share/note-share-service.d.ts +67 -0
  170. package/dist/src/share/note-share-service.js +283 -0
  171. package/dist/src/share/share-config.js +32 -3
  172. package/dist/src/share/share-landing.d.ts +5 -1
  173. package/dist/src/share/share-landing.js +28 -1
  174. package/dist/src/share/share-store.d.ts +34 -5
  175. package/dist/src/share/share-store.js +125 -19
  176. package/dist/src/share/share-thumbnail.js +4 -2
  177. package/dist/src/share/share-types.d.ts +52 -14
  178. package/dist/src/share/share-types.js +9 -0
  179. package/dist/src/share/share-zip.d.ts +2 -2
  180. package/dist/src/tui/tui-startup-resources.js +1 -1
  181. package/dist/src/tui/tui.js +1 -1
  182. package/dist/src/tunnel/env.d.ts +12 -6
  183. package/dist/src/tunnel/env.js +34 -26
  184. package/dist/src/tunnel/gateway-lifecycle.js +2 -13
  185. package/dist/src/tunnel/index.d.ts +1 -1
  186. package/dist/src/tunnel/index.js +2 -2
  187. package/dist/src/tunnel/tunnel-audit.d.ts +1 -1
  188. package/dist/src/tunnel/tunnel-config.d.ts +1 -1
  189. package/dist/src/tunnel/tunnel-config.js +9 -4
  190. package/dist/src/tunnel/tunnel-service.d.ts +1 -1
  191. package/dist/src/tunnel/tunnel-service.js +3 -0
  192. package/dist/src/tunnel/well-known.d.ts +1 -1
  193. package/dist/src/tunnel/well-known.js +1 -1
  194. package/dist/src/tunnel/xopc-cloud-registration.d.ts +9 -0
  195. package/dist/src/tunnel/xopc-cloud-registration.js +42 -0
  196. package/dist/src/voice/audio/normalize.d.ts +7 -0
  197. package/dist/src/voice/audio/normalize.js +30 -4
  198. package/dist/src/voice/local/runtime-worker.js +26 -6
  199. package/dist/src/workflows/service/workflow-run-service.js +1 -1
  200. package/package.json +8 -14
  201. package/dist/gateway/static/root/assets/agents-Brs2-m6t.js +0 -68
  202. package/dist/gateway/static/root/assets/apps-page-DJ_Dsb1j.js +0 -1
  203. package/dist/gateway/static/root/assets/automations-workspace-DehLRXfd.js +0 -6
  204. package/dist/gateway/static/root/assets/block-editor-i4CfDqLG.js +0 -184
  205. package/dist/gateway/static/root/assets/browser-settings-page-C3QIO4Ti.js +0 -8
  206. package/dist/gateway/static/root/assets/capabilities-settings-panel-BHhUp7FW.js +0 -7
  207. package/dist/gateway/static/root/assets/capability-presets-settings-panel-DoClNVLz.js +0 -3
  208. package/dist/gateway/static/root/assets/channels-settings-Je-9a5ZC.js +0 -1
  209. package/dist/gateway/static/root/assets/channels-status-swr-ByRbxC6z.js +0 -1
  210. package/dist/gateway/static/root/assets/connectors-page-D8z9Trw4.js +0 -2
  211. package/dist/gateway/static/root/assets/dist-C230izr8.js +0 -1
  212. package/dist/gateway/static/root/assets/heartbeat-settings-D5CGVyci.js +0 -1
  213. package/dist/gateway/static/root/assets/home-page-DzfZFZx5.js +0 -1
  214. package/dist/gateway/static/root/assets/index-Dcj5KJRg.css +0 -2
  215. package/dist/gateway/static/root/assets/index-EvA8c7fo.js +0 -74
  216. package/dist/gateway/static/root/assets/local-app-workbench-page-hPyTaVYU.js +0 -2
  217. package/dist/gateway/static/root/assets/logs-page-DW0xMKWP.js +0 -2
  218. package/dist/gateway/static/root/assets/markdown-view-DYkxMtJA.js +0 -69
  219. package/dist/gateway/static/root/assets/messages-kx5W39dO.js +0 -3
  220. package/dist/gateway/static/root/assets/notes-api-DJizatxh.js +0 -1
  221. package/dist/gateway/static/root/assets/notes-workbench-D669d98r.js +0 -4
  222. package/dist/gateway/static/root/assets/project-detail-page-CpsQ9PTA.js +0 -1
  223. package/dist/gateway/static/root/assets/projects-page-CitO1LMc.js +0 -1
  224. package/dist/gateway/static/root/assets/remote-access-hub-C9-bL4V6.js +0 -1
  225. package/dist/gateway/static/root/assets/sessions-page-CPSARi4y.js +0 -1
  226. package/dist/gateway/static/root/assets/settings-advanced-gate-BxT80Fmr.js +0 -1
  227. package/dist/gateway/static/root/assets/share-preview-page-WCVtJ3sX.js +0 -2
  228. package/dist/gateway/static/root/assets/shares-settings-DrQ_cOXh.js +0 -1
  229. package/dist/gateway/static/root/assets/skill-reload-api-C_4cG30x.js +0 -1
  230. package/dist/gateway/static/root/assets/task-detail-page-CYjsecEr.js +0 -1
  231. package/dist/gateway/static/root/assets/user-context-page-Cy1D2hkt.js +0 -1
  232. package/dist/gateway/static/root/assets/workflows-page-DKKH1_Yt.js +0 -5
@@ -4,17 +4,16 @@ import { init_logger } from "../utils/logger.js";
4
4
  import { getSessionMetadata } from "../storage/sqlite/session-repository.js";
5
5
  import { getSessionConfig } from "../storage/sqlite/config-repository.js";
6
6
  import "../storage/sqlite/index.js";
7
- import { checkBoundary } from "../agent-runtime/boundary-guard.js";
8
7
  import { getAgentDefaultModelRef, init_schema } from "../config/schema.js";
8
+ import { applyConfigOverrides } from "../config/runtime-overrides.js";
9
9
  import { expandWorkspacePathString, init_workspace_path } from "../config/workspace-path.js";
10
10
  import { init_agent_scope, resolveAgentProfileDir } from "./agent-scope.js";
11
+ import { resolveEffectiveAgentProfile, resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
11
12
  import { init_paths, resolveBundledSkillsDir } from "../config/paths.js";
12
- import { CredentialResolver, init_credentials } from "../auth/credentials.js";
13
13
  import { init_sync_provider_auth, resolveProviderApiKeySync } from "../auth/sync-provider-auth.js";
14
+ import { CredentialResolver, init_credentials } from "../auth/credentials.js";
14
15
  import { getApiKeySync, getDefaultModelSync, init_providers, resolveModel } from "../providers/index.js";
15
16
  import { createExtensionAwareStreamFn } from "../providers/extension-stream-bridge.js";
16
- import { applyConfigOverrides } from "../config/runtime-overrides.js";
17
- import { resolveEffectiveAgentProfile, resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
18
17
  import { extractTextContent } from "./context/workspace.js";
19
18
  import { buildExecutionScopeContextForPrompt } from "./context/execution-scope.js";
20
19
  import { clearBootstrapSnapshot } from "./bootstrap/bootstrap-cache.js";
@@ -39,8 +38,10 @@ import { UserContextCoordinator } from "./memory/user-context-coordinator.js";
39
38
  import { WorkspaceRuntimeRegistry } from "./workspace-runtime/registry.js";
40
39
  import { BackgroundReviewCoordinator } from "./background-review/coordinator.js";
41
40
  import { maybeRequestChannelExecApproval } from "../channels/exec-approval-runtime.js";
41
+ import { checkBoundary } from "../agent-runtime/boundary-guard.js";
42
42
  import { SkillFilesystemWatcher } from "./skills/filesystem-watcher.js";
43
43
  import { ProjectTrustStore, hasTrustRequiringProjectResources } from "../project-trust/trust-store.js";
44
+ import { createAgentTurnPolicy } from "./orchestration/agent-turn-policy.js";
44
45
  import { basename, resolve, sep } from "node:path";
45
46
  import { Agent } from "@earendil-works/pi-agent-core";
46
47
  //#region src/agent/agent-manager.ts
@@ -728,6 +729,10 @@ var AgentManager = class {
728
729
  }, "Created new agent instance");
729
730
  return agent;
730
731
  }
732
+ createAgentTurnPolicy(sessionKey) {
733
+ const profile = this.agents.get(sessionKey)?.effectiveProfile ?? resolveEffectiveAgentProfileForSession(this.config.config, sessionKey);
734
+ return this.buildAgentTurnPolicy(sessionKey, profile);
735
+ }
731
736
  /**
732
737
  * Get existing agent for a session (if any)
733
738
  */
@@ -893,7 +898,7 @@ var AgentManager = class {
893
898
  });
894
899
  const registeredToolNames = tools.map((t) => t.name);
895
900
  const thinkingLevel = profile.thinkingDefault ?? this.config.thinkingLevel ?? "medium";
896
- const toolCalls = /* @__PURE__ */ new Map();
901
+ const turnPolicy = this.buildAgentTurnPolicy(sessionKey, profile);
897
902
  agent = new Agent({
898
903
  initialState: {
899
904
  systemPrompt: rt.systemPromptBuilder.build(contextFiles, {
@@ -917,25 +922,33 @@ var AgentManager = class {
917
922
  toolExecution: "parallel",
918
923
  streamFn: createExtensionAwareStreamFn(),
919
924
  getApiKey: (provider) => this.resolveApiKeyWithCache(provider),
920
- shouldStopAfterTurn: ({ newMessages, toolResults }) => {
921
- const maxTurns = profile.manifest.runtime?.maxTurns;
922
- const maxFailures = profile.manifest.runtime?.maxToolFailuresPerTurn;
923
- const shouldStop = Boolean(maxTurns && newMessages.filter((message) => message.role === "assistant").length >= maxTurns || maxFailures && toolResults.filter((result) => result.isError).length >= maxFailures);
924
- toolCalls.clear();
925
- return shouldStop;
925
+ shouldStopAfterTurn: turnPolicy.shouldStopAfterTurn,
926
+ beforeToolCall: turnPolicy.beforeToolCall,
927
+ afterToolCall: turnPolicy.afterToolCall
928
+ });
929
+ agent.subscribe((event) => {
930
+ if (event.type === "agent_start") turnPolicy.reset();
931
+ });
932
+ return {
933
+ agent,
934
+ registeredToolNames
935
+ };
936
+ }
937
+ buildAgentTurnPolicy(sessionKey, profile) {
938
+ return createAgentTurnPolicy({
939
+ maxTurns: profile.manifest.runtime?.maxTurns,
940
+ maxToolFailures: profile.manifest.runtime?.maxToolFailuresPerTurn,
941
+ resolveToolLimit: (toolName, args) => {
942
+ const policy = this.resolveToolPolicy(profile, toolName, args);
943
+ const maxCalls = policy?.limits?.maxCallsPerTurn;
944
+ return policy && maxCalls ? {
945
+ id: policy.id,
946
+ maxCalls
947
+ } : void 0;
926
948
  },
927
- beforeToolCall: async ({ toolCall, args }) => {
949
+ authorizeToolCall: async ({ toolCall, args }) => {
928
950
  const toolName = toolCall.name;
929
951
  const policy = this.resolveToolPolicy(profile, toolName, args);
930
- if (policy?.limits?.maxCallsPerTurn) {
931
- const count = (toolCalls.get(policy.id) ?? 0) + 1;
932
- toolCalls.set(policy.id, count);
933
- if (count > policy.limits.maxCallsPerTurn) return {
934
- block: true,
935
- terminate: true,
936
- reason: `${policy.id} exceeded its per-turn call limit.`
937
- };
938
- }
939
952
  const detail = JSON.stringify(args ?? {});
940
953
  const boundary = checkBoundary({
941
954
  manifest: profile.manifest,
@@ -991,13 +1004,6 @@ var AgentManager = class {
991
1004
  };
992
1005
  }
993
1006
  });
994
- agent.subscribe((event) => {
995
- if (event.type === "agent_start") toolCalls.clear();
996
- });
997
- return {
998
- agent,
999
- registeredToolNames
1000
- };
1001
1007
  }
1002
1008
  resolveToolPolicy(profile, toolName, args) {
1003
1009
  if (toolName !== "xopc_tool_execute") {
@@ -9,5 +9,5 @@ export { acquireEmbeddedSessionRunner, buildEmbeddedRunnerFingerprint, evictAllE
9
9
  export { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, resolveLiveToolResultMaxChars, truncateToolResultMessage, } from './tool-result-truncation.js';
10
10
  export { runXopcEmbeddedTurn, abortEmbeddedRun, queueEmbeddedSteer } from './run-turn.js';
11
11
  export { runEmbeddedTurnForSession } from './run-for-session.js';
12
- export { registerEmbeddedRun, getEmbeddedRunBySessionKey } from './runs.js';
12
+ export { getEmbeddedRunBySessionKey } from './runs.js';
13
13
  export type { RunXopcEmbeddedTurnParams, RunXopcEmbeddedTurnResult, EmbeddedStreamEvent } from './types.js';
@@ -1,7 +1,7 @@
1
1
  import { DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, resolveLiveToolResultMaxChars, truncateToolResultMessage } from "./tool-result-truncation.js";
2
2
  import { getRawSessionAppendMessage, guardSessionManager, installSessionToolResultGuard } from "./session-tool-result-guard.js";
3
3
  import { acquireEmbeddedSessionRunner, buildEmbeddedRunnerFingerprint, evictAllEmbeddedSessionRunners, evictEmbeddedSessionRunner, getEmbeddedSessionRunnerIdleTtlMs, getEmbeddedSessionRunnerStats, isEmbeddedSessionRunnerEnabled, resetEmbeddedSessionRunnerForTest } from "./session-runner.js";
4
- import { abortEmbeddedRun, getEmbeddedRunBySessionKey, queueEmbeddedSteer, registerEmbeddedRun } from "./runs.js";
4
+ import { abortEmbeddedRun, getEmbeddedRunBySessionKey, queueEmbeddedSteer } from "./runs.js";
5
5
  import { repairAssistantUsageInSessionManager } from "./session-manager-init.js";
6
6
  import { openSqliteHydratingSessionManager } from "./sqlite-hydrating-session-manager.js";
7
7
  import { InMemoryTranscriptRuntime, createSqliteTranscriptRuntime } from "./transcript-runtime.js";
@@ -9,4 +9,4 @@ import { runXopcEmbeddedTurn } from "./run-turn.js";
9
9
  import { getEmbeddedExecutionSession, runWithEmbeddedExecutionSession } from "./execution-context.js";
10
10
  import { runEmbeddedTurnForSession } from "./run-for-session.js";
11
11
  import { CURRENT_SESSION_VERSION, PiTranscriptManager } from "./pi-transcript.js";
12
- export { CURRENT_SESSION_VERSION, DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, InMemoryTranscriptRuntime, PiTranscriptManager, abortEmbeddedRun, acquireEmbeddedSessionRunner, buildEmbeddedRunnerFingerprint, createSqliteTranscriptRuntime, evictAllEmbeddedSessionRunners, evictEmbeddedSessionRunner, getEmbeddedExecutionSession, getEmbeddedRunBySessionKey, getEmbeddedSessionRunnerIdleTtlMs, getEmbeddedSessionRunnerStats, getRawSessionAppendMessage, guardSessionManager, installSessionToolResultGuard, isEmbeddedSessionRunnerEnabled, openSqliteHydratingSessionManager, queueEmbeddedSteer, registerEmbeddedRun, repairAssistantUsageInSessionManager, resetEmbeddedSessionRunnerForTest, resolveLiveToolResultMaxChars, runEmbeddedTurnForSession, runWithEmbeddedExecutionSession, runXopcEmbeddedTurn, truncateToolResultMessage };
12
+ export { CURRENT_SESSION_VERSION, DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS, InMemoryTranscriptRuntime, PiTranscriptManager, abortEmbeddedRun, acquireEmbeddedSessionRunner, buildEmbeddedRunnerFingerprint, createSqliteTranscriptRuntime, evictAllEmbeddedSessionRunners, evictEmbeddedSessionRunner, getEmbeddedExecutionSession, getEmbeddedRunBySessionKey, getEmbeddedSessionRunnerIdleTtlMs, getEmbeddedSessionRunnerStats, getRawSessionAppendMessage, guardSessionManager, installSessionToolResultGuard, isEmbeddedSessionRunnerEnabled, openSqliteHydratingSessionManager, queueEmbeddedSteer, repairAssistantUsageInSessionManager, resetEmbeddedSessionRunnerForTest, resolveLiveToolResultMaxChars, runEmbeddedTurnForSession, runWithEmbeddedExecutionSession, runXopcEmbeddedTurn, truncateToolResultMessage };
@@ -1,6 +1,6 @@
1
1
  import { init_paths, resolveModelsJsonPath } from "../../config/paths.js";
2
- import { CredentialResolver, init_credentials } from "../../auth/credentials.js";
3
2
  import { init_sync_provider_auth, resolveProviderApiKeySync } from "../../auth/sync-provider-auth.js";
3
+ import { CredentialResolver, init_credentials } from "../../auth/credentials.js";
4
4
  import { XopcModelCredentialStore, init_model_credential_store } from "../../auth/model-credential-store.js";
5
5
  import { init_register_bundled_oauth_flows, registerBundledOAuthFlows } from "../../providers/register-bundled-oauth-flows.js";
6
6
  import { init_register_runtime_providers, registerRuntimeProviders } from "../../providers/register-runtime-providers.js";
@@ -1,13 +1,14 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { init_providers, resolveModel } from "../../providers/index.js";
4
3
  import { resolveEffectiveAgentProfileForSession } from "../../config/agent-profile.js";
4
+ import { init_providers, resolveModel } from "../../providers/index.js";
5
5
  import { resolveAgentTurnTimeoutMs } from "../orchestration/run-agent-turn-with-timeout.js";
6
6
  import { resolvePromptCachePolicy } from "../../providers/prompt-cache-plan.js";
7
7
  import { evaluateContextBudget } from "../memory/context-budget.js";
8
8
  import { resolveCompactionPolicy } from "../memory/compaction-policy.js";
9
9
  import { runXopcEmbeddedTurn } from "./run-turn.js";
10
10
  import { applyStartupContextToUserMessage } from "../reply/apply-turn-user-enrichment.js";
11
+ import { AgentRunSupervisor } from "../orchestration/agent-run-supervisor.js";
11
12
  import crypto from "node:crypto";
12
13
  //#region src/agent/embedded/run-for-session.ts
13
14
  init_logger();
@@ -16,67 +17,93 @@ const log = createLogger("EmbeddedTurnForSession");
16
17
  async function runEmbeddedTurnForSession(params) {
17
18
  const { sessionKey, agentManager, modelManager, sessionStore, userMessage } = params;
18
19
  const runId = params.runId ?? crypto.randomUUID();
19
- await params.beforeTurn?.();
20
- const agent = agentManager.getOrCreateAgent(sessionKey);
21
- const mm = modelManager;
22
- const configuredModelRef = String(mm.getModelForSession(sessionKey));
23
- const candidates = typeof mm.getFallbackCandidatesForSession === "function" ? mm.getFallbackCandidatesForSession(sessionKey) : [];
24
- const candidateModelRefs = candidates.length > 0 ? candidates.map((candidate) => `${candidate.provider}/${candidate.model}`) : [configuredModelRef];
25
- const resolvedCandidates = [];
26
- for (const [index, ref] of candidateModelRefs.entries()) try {
27
- const resolved = index === 0 && typeof mm.getResolvedModelForSession === "function" ? mm.getResolvedModelForSession(sessionKey) : resolveModel(ref);
28
- resolvedCandidates.push({
29
- ref,
30
- model: resolved
31
- });
32
- } catch (err) {
33
- log.warn({
34
- err,
35
- sessionKey,
36
- runId,
37
- modelRef: ref
38
- }, "Skipping unavailable model candidate");
39
- }
40
- const primary = resolvedCandidates[0];
41
- if (!primary) throw new Error(`No available model candidates for '${configuredModelRef}'`);
42
- const modelRef = primary.ref;
43
- const model = primary.model;
44
- if (typeof mm.applyResolvedModel === "function") mm.applyResolvedModel(agent, model, modelRef);
45
- else await mm.applyModelForSession(agent, sessionKey);
46
- agentManager.setModelForSession(sessionKey, modelRef);
47
- const tools = agent.state.tools;
48
- const systemPrompt = agent.state.systemPrompt ?? "";
49
- const thinkingLevel = params.thinkingOverride ?? agent.state.thinkingLevel;
50
- const workspaceDir = agentManager.getResolvedWorkspaceForSession(sessionKey);
51
20
  const config = params.getConfig?.();
52
- const promptCachePolicy = resolvePromptCachePolicy(config ? resolveEffectiveAgentProfileForSession(config, sessionKey).manifest.runtime?.promptCache : void 0);
53
- const configuredTurnTimeoutMs = resolveAgentTurnTimeoutMs(config, sessionKey);
54
- const turnTimeoutMs = params.deadlineAtMs === void 0 ? configuredTurnTimeoutMs : Math.max(1, Math.min(configuredTurnTimeoutMs, params.deadlineAtMs - Date.now()));
55
- let userMessageForTurn = userMessage;
56
- if (params.applyStartupContext !== false) userMessageForTurn = await applyStartupContextToUserMessage({
57
- userMessage,
58
- sessionKey,
59
- workspaceDir,
60
- cfg: config,
61
- sessionStore,
62
- startupAction: params.startupAction,
63
- force: params.forceStartupContext
21
+ const supervisor = new AgentRunSupervisor({
22
+ timeoutMs: resolveAgentTurnTimeoutMs(config, sessionKey),
23
+ deadlineAtMs: params.deadlineAtMs,
24
+ parentSignal: params.abortSignal
64
25
  });
65
- return await (async () => {
66
- await maybeAutoCompactBeforeTurn({
67
- sessionKey,
68
- sessionStore,
69
- agentManager,
70
- model,
71
- config,
72
- systemPrompt,
73
- userMessage: userMessageForTurn,
74
- tools,
75
- imageCount: params.llmImages?.length ?? 0,
76
- fallbackModels: resolvedCandidates.slice(1).map((candidate) => candidate.model),
77
- abortSignal: params.abortSignal,
78
- onEvent: params.onEvent
79
- });
26
+ try {
27
+ await params.beforeTurn?.();
28
+ if (supervisor.signal.aborted) return {
29
+ ok: false,
30
+ errorMessage: "aborted"
31
+ };
32
+ const agent = agentManager.getOrCreateAgent(sessionKey);
33
+ const mm = modelManager;
34
+ const configuredModelRef = String(mm.getModelForSession(sessionKey));
35
+ const candidates = typeof mm.getFallbackCandidatesForSession === "function" ? mm.getFallbackCandidatesForSession(sessionKey) : [];
36
+ const candidateModelRefs = candidates.length > 0 ? candidates.map((candidate) => `${candidate.provider}/${candidate.model}`) : [configuredModelRef];
37
+ const resolvedCandidates = [];
38
+ for (const [index, ref] of candidateModelRefs.entries()) try {
39
+ const resolved = index === 0 && typeof mm.getResolvedModelForSession === "function" ? mm.getResolvedModelForSession(sessionKey) : resolveModel(ref);
40
+ resolvedCandidates.push({
41
+ ref,
42
+ model: resolved
43
+ });
44
+ } catch (err) {
45
+ log.warn({
46
+ err,
47
+ sessionKey,
48
+ runId,
49
+ modelRef: ref
50
+ }, "Skipping unavailable model candidate");
51
+ }
52
+ const primary = resolvedCandidates[0];
53
+ if (!primary) throw new Error(`No available model candidates for '${configuredModelRef}'`);
54
+ const modelRef = primary.ref;
55
+ const model = primary.model;
56
+ if (typeof mm.applyResolvedModel === "function") mm.applyResolvedModel(agent, model, modelRef);
57
+ else await mm.applyModelForSession(agent, sessionKey);
58
+ agentManager.setModelForSession(sessionKey, modelRef);
59
+ const tools = agent.state.tools;
60
+ const turnPolicy = agentManager.createAgentTurnPolicy(sessionKey);
61
+ const systemPrompt = agent.state.systemPrompt ?? "";
62
+ const thinkingLevel = params.thinkingOverride ?? agent.state.thinkingLevel;
63
+ const workspaceDir = agentManager.getResolvedWorkspaceForSession(sessionKey);
64
+ const promptCachePolicy = resolvePromptCachePolicy(config ? resolveEffectiveAgentProfileForSession(config, sessionKey).manifest.runtime?.promptCache : void 0);
65
+ let userMessageForTurn = userMessage;
66
+ if (params.applyStartupContext !== false) {
67
+ userMessageForTurn = await applyStartupContextToUserMessage({
68
+ userMessage,
69
+ sessionKey,
70
+ workspaceDir,
71
+ cfg: config,
72
+ sessionStore,
73
+ startupAction: params.startupAction,
74
+ force: params.forceStartupContext
75
+ });
76
+ if (supervisor.signal.aborted) return {
77
+ ok: false,
78
+ errorMessage: "aborted"
79
+ };
80
+ }
81
+ try {
82
+ await maybeAutoCompactBeforeTurn({
83
+ sessionKey,
84
+ sessionStore,
85
+ agentManager,
86
+ model,
87
+ config,
88
+ systemPrompt,
89
+ userMessage: userMessageForTurn,
90
+ tools,
91
+ imageCount: params.llmImages?.length ?? 0,
92
+ fallbackModels: resolvedCandidates.slice(1).map((candidate) => candidate.model),
93
+ abortSignal: supervisor.signal,
94
+ onEvent: params.onEvent
95
+ });
96
+ } catch (err) {
97
+ if (supervisor.signal.aborted) return {
98
+ ok: false,
99
+ errorMessage: "aborted"
100
+ };
101
+ throw err;
102
+ }
103
+ if (supervisor.signal.aborted) return {
104
+ ok: false,
105
+ errorMessage: "aborted"
106
+ };
80
107
  log.info({
81
108
  sessionKey,
82
109
  runId,
@@ -94,6 +121,14 @@ async function runEmbeddedTurnForSession(params) {
94
121
  const isFallbackAttempt = i > 0;
95
122
  const candidateModel = candidate.model;
96
123
  const hasNextCandidate = i + 1 < resolvedCandidates.length;
124
+ const attemptPlan = supervisor.planModelAttempt(isFallbackAttempt);
125
+ if (attemptPlan.ok === false) {
126
+ lastResult = {
127
+ ok: false,
128
+ errorMessage: attemptPlan.reason
129
+ };
130
+ break;
131
+ }
97
132
  const rowsBeforeAttempt = hasNextCandidate ? await sessionStore.loadTranscriptRows(sessionKey) : void 0;
98
133
  if (typeof mm.applyResolvedModel === "function") mm.applyResolvedModel(agent, candidateModel, candidateModelRef);
99
134
  agentManager.setModelForSession(sessionKey, candidateModelRef);
@@ -119,8 +154,9 @@ async function runEmbeddedTurnForSession(params) {
119
154
  promptCachePolicy,
120
155
  workspaceDir,
121
156
  sessionStore,
122
- timeoutMs: turnTimeoutMs,
123
- abortSignal: params.abortSignal,
157
+ timeoutMs: attemptPlan.timeoutMs,
158
+ turnPolicy,
159
+ abortSignal: supervisor.signal,
124
160
  onEvent: params.onEvent,
125
161
  resumeLastUserMessage
126
162
  });
@@ -136,6 +172,7 @@ async function runEmbeddedTurnForSession(params) {
136
172
  return turnResult;
137
173
  }
138
174
  lastResult = turnResult;
175
+ if (supervisor.signal.aborted || turnResult.retryable === false) break;
139
176
  log.warn({
140
177
  sessionKey,
141
178
  runId,
@@ -179,7 +216,9 @@ async function runEmbeddedTurnForSession(params) {
179
216
  ok: false,
180
217
  errorMessage: "No model candidates available"
181
218
  };
182
- })();
219
+ } finally {
220
+ supervisor.dispose();
221
+ }
183
222
  }
184
223
  function serializedTranscriptBytes(messages) {
185
224
  return Buffer.byteLength(JSON.stringify(messages), "utf8");
@@ -1,17 +1,17 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { resolveAgentTurnTimeoutMs, runAgentTurnWithTimeout } from "../orchestration/run-agent-turn-with-timeout.js";
3
+ import { isAgentTurnUnsettledError, resolveAgentTurnTimeoutMs, runAgentTurnWithTimeout } from "../orchestration/run-agent-turn-with-timeout.js";
4
4
  import { resolvePromptCachePolicy } from "../../providers/prompt-cache-plan.js";
5
5
  import { wrapStreamFnForXopcExtensions } from "./xopc-stream-bridge.js";
6
- import { acquireEmbeddedSessionRunner } from "./session-runner.js";
6
+ import { acquireEmbeddedSessionRunner, evictEmbeddedSessionRunner } from "./session-runner.js";
7
+ import { isContextOverflowError } from "../orchestration/context-overflow.js";
7
8
  import { getAssistantTurnErrorMessage, isAssistantTurnAborted, isAssistantTurnFailed, maybeRetryTurnAfterTransientLlmFailure, stripTrailingErrorAssistantMessages } from "../orchestration/llm-turn-retry.js";
8
9
  import { projectContextForModel } from "../memory/context-budget.js";
9
- import { abortEmbeddedRun, queueEmbeddedSteer, registerEmbeddedRun, unregisterEmbeddedRun } from "./runs.js";
10
+ import { EmbeddedRunConflictError, abortEmbeddedRun, acquireEmbeddedRunLease, queueEmbeddedSteer } from "./runs.js";
10
11
  import { lastAssistantPlainText, subscribeEmbeddedSessionEvents } from "./subscribe-session.js";
11
12
  import { detectToolLoops } from "../orchestration/loop-guard.js";
12
13
  import { tryApplySessionTranscriptHygiene } from "../transcript/transcript-hygiene.js";
13
14
  import { createSqliteTranscriptRuntime } from "./transcript-runtime.js";
14
- import { isContextOverflowError } from "../orchestration/context-overflow.js";
15
15
  import { buildPromptCacheSnapshot, observePromptCacheSnapshot } from "../../providers/prompt-cache-observability.js";
16
16
  import { isPromptCacheExpired, recordPromptCacheTouch } from "../../providers/prompt-cache-lifecycle.js";
17
17
  //#region src/agent/embedded/run-turn.ts
@@ -83,9 +83,10 @@ async function maybeRecoverContextOverflow(params) {
83
83
  type: "compaction",
84
84
  status: "started"
85
85
  });
86
- const result = await params.transcriptRuntime.compact(messages, params.model, "Preserve the current pending user request verbatim and retain every fact needed to answer it.", true);
86
+ const result = await params.transcriptRuntime.compact(messages, params.model, "Preserve the current pending user request verbatim and retain every fact needed to answer it.", true, { signal: params.abortSignal });
87
87
  if (!result.compacted) throw new Error(`Context overflow recovery could not find a safe compaction range: ${errorMessage}`);
88
88
  params.agent.state.messages = await params.transcriptRuntime.loadMessages();
89
+ params.abortSignal?.throwIfAborted();
89
90
  params.onEvent?.({
90
91
  type: "compaction",
91
92
  status: "completed",
@@ -109,7 +110,14 @@ async function runXopcEmbeddedTurn(params) {
109
110
  if (!transcriptRuntime) throw new Error("Embedded run requires a transcript runtime");
110
111
  let runner;
111
112
  let unsubscribe;
113
+ let quarantineRunner = false;
114
+ let runLease;
112
115
  try {
116
+ runLease = acquireEmbeddedRunLease({
117
+ sessionKey,
118
+ sessionId: transcriptRuntime.sessionId,
119
+ runId
120
+ });
113
121
  runner = await acquireEmbeddedSessionRunner({
114
122
  runtimeId: transcriptRuntime.runtimeId,
115
123
  sessionId: transcriptRuntime.sessionId,
@@ -122,6 +130,15 @@ async function runXopcEmbeddedTurn(params) {
122
130
  transcriptRuntime
123
131
  });
124
132
  const { session, reused } = runner;
133
+ await runLease.attach(session, async () => session.abort());
134
+ const runAbortSignal = params.abortSignal ? AbortSignal.any([runLease.signal, params.abortSignal]) : runLease.signal;
135
+ if (runAbortSignal.aborted) {
136
+ await session.abort();
137
+ return {
138
+ ok: false,
139
+ errorMessage: "aborted"
140
+ };
141
+ }
125
142
  runner.piSm.setActiveTurnId?.(runId);
126
143
  const streamFnWithXopcExtensions = wrapStreamFnForXopcExtensions(session.agent.streamFunction, params.promptCachePolicy);
127
144
  const loggingStreamFn = (streamModel, context, options) => {
@@ -197,6 +214,10 @@ async function runXopcEmbeddedTurn(params) {
197
214
  return streamFnWithXopcExtensions(streamModel, effectiveContext, { ...options });
198
215
  };
199
216
  session.agent.streamFunction = loggingStreamFn;
217
+ params.turnPolicy?.reset();
218
+ session.agent.beforeToolCall = params.turnPolicy?.beforeToolCall;
219
+ session.agent.afterToolCall = params.turnPolicy?.afterToolCall;
220
+ session.agent.shouldStopAfterTurn = params.turnPolicy?.shouldStopAfterTurn;
200
221
  unsubscribe = subscribeEmbeddedSessionEvents(session, (event) => {
201
222
  if (event.type === "message_end" && event.message.role === "assistant") recordPromptCacheTouch(sessionKey, resolvedModel, event.message.usage);
202
223
  onEvent?.({
@@ -204,29 +225,10 @@ async function runXopcEmbeddedTurn(params) {
204
225
  runId
205
226
  });
206
227
  });
207
- const handle = {
208
- sessionKey,
209
- sessionId: transcriptRuntime.sessionId,
210
- runId,
211
- session,
212
- abort: async () => {
213
- await session.abort();
214
- }
215
- };
216
- registerEmbeddedRun(handle);
217
228
  const abortListener = () => {
218
229
  session.abort();
219
230
  };
220
- if (params.abortSignal) {
221
- if (params.abortSignal.aborted) {
222
- await session.abort();
223
- return {
224
- ok: false,
225
- errorMessage: "aborted"
226
- };
227
- }
228
- params.abortSignal.addEventListener("abort", abortListener, { once: true });
229
- }
231
+ runAbortSignal.addEventListener("abort", abortListener, { once: true });
230
232
  try {
231
233
  await runAgentTurnWithTimeout(session.agent, async () => {
232
234
  if (params.resumeLastUserMessage) await session.agent.continue();
@@ -238,16 +240,22 @@ async function runXopcEmbeddedTurn(params) {
238
240
  await session.agent.waitForIdle();
239
241
  await maybeRetryTurnAfterTransientLlmFailure(session.agent, {
240
242
  sessionKey,
241
- log
243
+ log,
244
+ signal: runAbortSignal
242
245
  });
243
246
  await maybeRecoverContextOverflow({
244
247
  agent: session.agent,
245
248
  model: resolvedModel,
246
249
  sessionKey,
247
250
  transcriptRuntime,
251
+ abortSignal: runAbortSignal,
248
252
  onEvent
249
253
  });
250
254
  }, timeoutMs);
255
+ if (runAbortSignal.aborted) return {
256
+ ok: false,
257
+ errorMessage: "aborted"
258
+ };
251
259
  if (isAssistantTurnAborted(session.agent)) return {
252
260
  ok: true,
253
261
  lastAssistantText: lastAssistantPlainText(session)
@@ -262,10 +270,28 @@ async function runXopcEmbeddedTurn(params) {
262
270
  lastAssistantText: lastAssistantPlainText(session)
263
271
  };
264
272
  } finally {
265
- params.abortSignal?.removeEventListener("abort", abortListener);
266
- unregisterEmbeddedRun(handle);
273
+ runAbortSignal.removeEventListener("abort", abortListener);
267
274
  }
268
275
  } catch (err) {
276
+ if (err instanceof EmbeddedRunConflictError) {
277
+ const em = err.message;
278
+ log.warn({
279
+ sessionKey,
280
+ runId,
281
+ activeRunId: err.activeRunId
282
+ }, `Embedded run rejected: ${em}`);
283
+ onEvent?.({
284
+ type: "error",
285
+ content: em,
286
+ runId
287
+ });
288
+ return {
289
+ ok: false,
290
+ retryable: false,
291
+ errorMessage: em
292
+ };
293
+ }
294
+ quarantineRunner = isAgentTurnUnsettledError(err);
269
295
  const em = err instanceof Error ? err.message : String(err);
270
296
  log.error({
271
297
  err,
@@ -282,11 +308,13 @@ async function runXopcEmbeddedTurn(params) {
282
308
  errorMessage: em
283
309
  };
284
310
  } finally {
311
+ runLease?.release();
285
312
  unsubscribe?.();
286
313
  try {
287
314
  runner?.piSm.flushPendingToolResults?.();
288
315
  } catch {}
289
316
  runner?.piSm.setActiveTurnId?.(null);
317
+ if (quarantineRunner) evictEmbeddedSessionRunner(transcriptRuntime.runtimeId, "unsettled_after_timeout");
290
318
  runner?.release();
291
319
  }
292
320
  }
@@ -1,46 +1,33 @@
1
1
  import type { AgentSession } from '@earendil-works/pi-coding-agent';
2
- export type EmbeddedRunHandle = {
2
+ export type EmbeddedRunIdentity = {
3
3
  sessionKey: string;
4
4
  sessionId: string;
5
5
  runId: string;
6
+ };
7
+ export type EmbeddedRunHandle = EmbeddedRunIdentity & {
6
8
  session: AgentSession;
7
9
  abort: () => Promise<void>;
8
10
  };
9
- /**
10
- * Tracks in-flight embedded agent turns so we can abort/steer them by sessionKey or runId.
11
- *
12
- * The previous implementation used two module-level Maps; this class is the supported
13
- * single-instance state, while {@link defaultEmbeddedRunRegistry} preserves the previous
14
- * free-function API for callers that have not yet been moved to dependency injection.
15
- */
11
+ export declare class EmbeddedRunConflictError extends Error {
12
+ readonly sessionKey: string;
13
+ readonly activeRunId: string;
14
+ constructor(sessionKey: string, activeRunId: string);
15
+ }
16
+ export type EmbeddedRunLease = {
17
+ signal: AbortSignal;
18
+ attach(session: AgentSession, abort: () => Promise<void>): Promise<void>;
19
+ release(): void;
20
+ };
21
+ /** Owns one process-local execution lease per session while leaving steer/abort on the control plane. */
16
22
  export declare class EmbeddedRunRegistry {
17
23
  private readonly bySessionKey;
18
- private readonly byRunId;
19
- /**
20
- * Register a new in-flight run. If a run is already registered for the same
21
- * sessionKey we drop the stale handle (the new run takes ownership). The caller
22
- * is responsible for aborting the previous run before re-registering if needed —
23
- * we warn loudly so this does not happen silently.
24
- */
25
- register(handle: EmbeddedRunHandle): void;
26
- unregister(handle: EmbeddedRunHandle): void;
24
+ acquire(identity: EmbeddedRunIdentity): EmbeddedRunLease;
27
25
  getBySessionKey(sessionKey: string): EmbeddedRunHandle | undefined;
28
- getByRunId(runId: string): EmbeddedRunHandle | undefined;
29
26
  abortBySessionKey(sessionKey: string): Promise<boolean>;
30
27
  steerBySessionKey(sessionKey: string, text: string): Promise<boolean>;
31
- /** Test-only helper to drop all registrations without touching live sessions. */
32
- resetForTest(): void;
33
28
  size(): number;
34
29
  }
35
- /**
36
- * Process-wide default registry. Prefer constructing a dedicated `EmbeddedRunRegistry`
37
- * and injecting it; the module-level singleton stays so the existing free functions
38
- * keep working until every caller has been migrated.
39
- */
40
- export declare const defaultEmbeddedRunRegistry: EmbeddedRunRegistry;
41
- export declare function registerEmbeddedRun(handle: EmbeddedRunHandle): void;
42
- export declare function unregisterEmbeddedRun(handle: EmbeddedRunHandle): void;
30
+ export declare function acquireEmbeddedRunLease(identity: EmbeddedRunIdentity): EmbeddedRunLease;
43
31
  export declare function getEmbeddedRunBySessionKey(sessionKey: string): EmbeddedRunHandle | undefined;
44
- export declare function getEmbeddedRunByRunId(runId: string): EmbeddedRunHandle | undefined;
45
32
  export declare function abortEmbeddedRun(sessionKey: string): Promise<boolean>;
46
33
  export declare function queueEmbeddedSteer(sessionKey: string, text: string): Promise<boolean>;