@xopcai/xopc 0.0.222 → 0.0.224

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 (213) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/telegram/xopc.extension.json +1 -1
  4. package/dist/gateway/static/root/assets/{activity-store-NItGQmvR.js → activity-store--GBciH10.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DsR_2U9-.js → agent-avatar-dicebear-K7V4XXt7.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-BsxTpXyq.js +68 -0
  7. package/dist/gateway/static/root/assets/{api-53ZXsuyr.js → api-HoIz_ija.js} +1 -1
  8. package/dist/gateway/static/root/assets/{app-management-settings-panel-BCzVX09V.js → app-management-settings-panel-xQCmkAHh.js} +1 -1
  9. package/dist/gateway/static/root/assets/{appearance-settings-DgSpwqQI.js → appearance-settings-BRoeA4kU.js} +1 -1
  10. package/dist/gateway/static/root/assets/apps-page-BXgpBA8w.js +1 -0
  11. package/dist/gateway/static/root/assets/{archive-plugin-D8o8CP9Y.js → archive-plugin-CteCVsBP.js} +1 -1
  12. package/dist/gateway/static/root/assets/{attachment-load-BQHcAR6J.js → attachment-load-D7xNA2jB.js} +1 -1
  13. package/dist/gateway/static/root/assets/{automations-page-CswK3HFb.js → automations-page-BD1vK7r5.js} +1 -1
  14. package/dist/gateway/static/root/assets/automations-workspace-wrgf-z7D.js +6 -0
  15. package/dist/gateway/static/root/assets/{binary-plugins-BlpyTJ1b.js → binary-plugins-C3DaJa0W.js} +1 -1
  16. package/dist/gateway/static/root/assets/block-editor-lTkcHTve.js +184 -0
  17. package/dist/gateway/static/root/assets/browser-settings-page-zhzNkeVG.js +8 -0
  18. package/dist/gateway/static/root/assets/{browser-workflow-inputs-Bi6RSSpD.js → browser-workflow-inputs-1UZ0vnIy.js} +1 -1
  19. package/dist/gateway/static/root/assets/{browser-workflows-page-CZNOOQY4.js → browser-workflows-page-BHuJOF4M.js} +2 -2
  20. package/dist/gateway/static/root/assets/capabilities-settings-panel-DuCmg2Ej.js +7 -0
  21. package/dist/gateway/static/root/assets/{capability-presets-api-pC2fno7r.js → capability-presets-api-yNqTIuyd.js} +1 -1
  22. package/dist/gateway/static/root/assets/capability-presets-settings-panel-DjiP3mQQ.js +3 -0
  23. package/dist/gateway/static/root/assets/{channel-recipient-api-B5Szgu_z.js → channel-recipient-api-krezGsKc.js} +1 -1
  24. package/dist/gateway/static/root/assets/channels-settings-B4AtvQHk.js +1 -0
  25. package/dist/gateway/static/root/assets/channels-status-swr-DMxB7slL.js +1 -0
  26. package/dist/gateway/static/root/assets/{chat-terminal-dock-2fpzufzt.js → chat-terminal-dock-BRrL2iHA.js} +2 -2
  27. package/dist/gateway/static/root/assets/connectors-page-fmda2NEP.js +2 -0
  28. package/dist/gateway/static/root/assets/{cron-expression-VePLfPUR.js → cron-expression-Bl-2DzBQ.js} +1 -1
  29. package/dist/gateway/static/root/assets/{date-picker-CJegmPTJ.js → date-picker-CxTl6Wlz.js} +1 -1
  30. package/dist/gateway/static/root/assets/{dependency-picker-B2o9unBk.js → dependency-picker-DSl0mOPa.js} +1 -1
  31. package/dist/gateway/static/root/assets/{desktop-pet-BH4Np-ay.js → desktop-pet-DTeCmCdb.js} +1 -1
  32. package/dist/gateway/static/root/assets/{desktop-pet-settings-Duu3ctxD.js → desktop-pet-settings-BNebxg0V.js} +1 -1
  33. package/dist/gateway/static/root/assets/{directory-picker-path-field-BBCUZOg2.js → directory-picker-path-field-Dn9XpmXY.js} +1 -1
  34. package/dist/gateway/static/root/assets/dist-DaxPZj9e.js +1 -0
  35. package/dist/gateway/static/root/assets/{dist-DY4Af0fY.js → dist-J_ySZvz0.js} +1 -1
  36. package/dist/gateway/static/root/assets/{extension-debug-page-DS_o3F97.js → extension-debug-page-D-J-eOTb.js} +1 -1
  37. package/dist/gateway/static/root/assets/{extension-page-BTLBZdCq.js → extension-page-CpST24nU.js} +1 -1
  38. package/dist/gateway/static/root/assets/{extension-settings-page-KTGrYbgk.js → extension-settings-page-4nQXFnFq.js} +1 -1
  39. package/dist/gateway/static/root/assets/floating-ui.dom-B7uQOZZW.js +1 -0
  40. package/dist/gateway/static/root/assets/{gateway-config-api-D3YJVfzY.js → gateway-config-api-BHWHduDi.js} +1 -1
  41. package/dist/gateway/static/root/assets/{gateway-config-swr-BzTbnu4O.js → gateway-config-swr-yUxMMVjK.js} +1 -1
  42. package/dist/gateway/static/root/assets/{gateway-settings-B8--s3yC.js → gateway-settings-BimMKmkM.js} +1 -1
  43. package/dist/gateway/static/root/assets/{gateway-startup-retry-B2zpkNvi.js → gateway-startup-retry-9mrsL84j.js} +1 -1
  44. package/dist/gateway/static/root/assets/heartbeat-settings-MUJ3mEC9.js +1 -0
  45. package/dist/gateway/static/root/assets/home-page-DSM6aPHF.js +1 -0
  46. package/dist/gateway/static/root/assets/{image-generation-api-BXRjz-iY.js → image-generation-api-CgFAGDi8.js} +1 -1
  47. package/dist/gateway/static/root/assets/index-B3zYJBhK.js +74 -0
  48. package/dist/gateway/static/root/assets/index-D3_xSYKd.css +2 -0
  49. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-D-Ib-suu.js → keyboard-shortcuts-settings-6Ar5rfX4.js} +1 -1
  50. package/dist/gateway/static/root/assets/local-app-workbench-page-tzM0wTL8.js +2 -0
  51. package/dist/gateway/static/root/assets/{local-apps-page-Bsy1lvAc.js → local-apps-page-B5n-x544.js} +1 -1
  52. package/dist/gateway/static/root/assets/{locale-store-iKjqemWG.js → locale-store-CPylQjH9.js} +1 -1
  53. package/dist/gateway/static/root/assets/logs-page-CgoQlera.js +2 -0
  54. package/dist/gateway/static/root/assets/{management-settings-D-akydG4.js → management-settings-CXBLyU-0.js} +1 -1
  55. package/dist/gateway/static/root/assets/{markdown-split-DHH4Cu2i.js → markdown-split-CtmQemJi.js} +1 -1
  56. package/dist/gateway/static/root/assets/markdown-view-CUsob4Pz.js +69 -0
  57. package/dist/gateway/static/root/assets/{media-plugins-BwHtUXkj.js → media-plugins-Dsd-IEOW.js} +1 -1
  58. package/dist/gateway/static/root/assets/messages-B82nsi9k.js +3 -0
  59. package/dist/gateway/static/root/assets/notes-api-CfKZYrBC.js +1 -0
  60. package/dist/gateway/static/root/assets/{notes-page-BxM1p6hz.js → notes-page-BKbOTE2n.js} +1 -1
  61. package/dist/gateway/static/root/assets/notes-workbench-BScELo01.js +4 -0
  62. package/dist/gateway/static/root/assets/oauth-authorization-window-Cdolr2wE.js +1 -0
  63. package/dist/gateway/static/root/assets/{popover-select-BzdnTy3h.js → popover-select-CccGri5Y.js} +1 -1
  64. package/dist/gateway/static/root/assets/{product-open-page-DrfWH1ir.js → product-open-page-DqB9bsDB.js} +1 -1
  65. package/dist/gateway/static/root/assets/project-detail-page-DUoyty8X.js +1 -0
  66. package/dist/gateway/static/root/assets/projects-page-PeXUdGdO.js +1 -0
  67. package/dist/gateway/static/root/assets/{registry-api-Cx3JxXSL.js → registry-api-B_5D8epF.js} +1 -1
  68. package/dist/gateway/static/root/assets/remote-access-hub-sHho-uXP.js +1 -0
  69. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DF7syQRd.js → runtime-tools-settings-panel-DBTH_t0O.js} +1 -1
  70. package/dist/gateway/static/root/assets/{schema-form-BQ_qRIsM.js → schema-form-CqMKAwV0.js} +1 -1
  71. package/dist/gateway/static/root/assets/sessions-page-DyvNgaP9.js +1 -0
  72. package/dist/gateway/static/root/assets/settings-advanced-gate-CnYf1R7z.js +1 -0
  73. package/dist/gateway/static/root/assets/{settings-form-section-BBHGOQHU.js → settings-form-section-dPriuuYq.js} +1 -1
  74. package/dist/gateway/static/root/assets/{settings-loading-skeleton-CiwjP2-b.js → settings-loading-skeleton-B-Mbm8Va.js} +1 -1
  75. package/dist/gateway/static/root/assets/{settings-page-Rl9waapk.js → settings-page-Dyj3w-Ml.js} +1 -1
  76. package/dist/gateway/static/root/assets/setup-status-panel-C4FxY5J3.js +1 -0
  77. package/dist/gateway/static/root/assets/share-preview-page-emEdx7YL.js +2 -0
  78. package/dist/gateway/static/root/assets/shares-settings-BQBVq-sN.js +1 -0
  79. package/dist/gateway/static/root/assets/skill-reload-api-BFoEpQ4I.js +1 -0
  80. package/dist/gateway/static/root/assets/{skills-page-YbtR_zEU.js → skills-page-BNtYnrRT.js} +1 -1
  81. package/dist/gateway/static/root/assets/{skills-page.utils-DTmx2Zza.js → skills-page.utils-DjnbNFs5.js} +1 -1
  82. package/dist/gateway/static/root/assets/{system-settings-panel-BZDJcKdS.js → system-settings-panel-DDStf-jZ.js} +1 -1
  83. package/dist/gateway/static/root/assets/task-detail-page-ZVwr8KbS.js +1 -0
  84. package/dist/gateway/static/root/assets/{theme-store-BOD2D0dJ.js → theme-store-BN-32Fz5.js} +1 -1
  85. package/dist/gateway/static/root/assets/{use-autosave-Be9aR9qA.js → use-autosave-C2mKZ-ua.js} +1 -1
  86. package/dist/gateway/static/root/assets/user-context-page-CHjIinCW.js +1 -0
  87. package/dist/gateway/static/root/assets/{voice-api-key-field-IMc3Ua6C.js → voice-api-key-field-D7OHeQby.js} +1 -1
  88. package/dist/gateway/static/root/assets/{work-discovery-overlay-TC-xQenr.js → work-discovery-overlay-CIiXie7F.js} +1 -1
  89. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-B2auxcRv.js → workflow-run-setup-panel-9_dAGW3_.js} +2 -2
  90. package/dist/gateway/static/root/assets/workflows-page-DXpYJxao.js +5 -0
  91. package/dist/gateway/static/root/index.html +15 -14
  92. package/dist/package.js +1 -1
  93. package/dist/src/agent/agent-instance-gateway.d.ts +3 -0
  94. package/dist/src/agent/agent-manager.d.ts +3 -0
  95. package/dist/src/agent/agent-manager.js +30 -24
  96. package/dist/src/agent/embedded/index.d.ts +1 -1
  97. package/dist/src/agent/embedded/index.js +2 -2
  98. package/dist/src/agent/embedded/run-for-session.js +101 -62
  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/orchestration/agent-run-supervisor.d.ts +28 -0
  104. package/dist/src/agent/orchestration/agent-run-supervisor.js +49 -0
  105. package/dist/src/agent/orchestration/agent-turn-policy.d.ts +18 -0
  106. package/dist/src/agent/orchestration/agent-turn-policy.js +36 -0
  107. package/dist/src/agent/orchestration/llm-turn-retry.d.ts +5 -0
  108. package/dist/src/agent/orchestration/llm-turn-retry.js +34 -4
  109. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.d.ts +10 -1
  110. package/dist/src/agent/orchestration/run-agent-turn-with-timeout.js +25 -4
  111. package/dist/src/agent/service/async-queue.d.ts +16 -3
  112. package/dist/src/agent/service/async-queue.js +29 -11
  113. package/dist/src/agent/service/process-direct-streaming.js +27 -4
  114. package/dist/src/agent/service.js +1 -0
  115. package/dist/src/agent/tools/exec-command.d.ts +2 -0
  116. package/dist/src/agent/tools/exec-command.js +70 -7
  117. package/dist/src/auth/oauth/index.js +3 -3
  118. package/dist/src/auth/oauth/registry.d.ts +2 -0
  119. package/dist/src/auth/oauth/registry.js +20 -12
  120. package/dist/src/auth/oauth/xopc-cloud.d.ts +1 -0
  121. package/dist/src/auth/oauth/xopc-cloud.js +32 -16
  122. package/dist/src/cli/commands/channels.js +1 -1
  123. package/dist/src/cli/commands/connectors.js +1 -1
  124. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  125. package/dist/src/cli/commands/models.js +1 -1
  126. package/dist/src/cli/commands/onboard.js +1 -1
  127. package/dist/src/cli/commands/tunnel.js +4 -5
  128. package/dist/src/config/agent-profile.js +1 -1
  129. package/dist/src/config/schema.d.ts +18 -0
  130. package/dist/src/config/schema.js +19 -1
  131. package/dist/src/extensions/sdk/index.js +1 -1
  132. package/dist/src/gateway/agents-admin.js +1 -1
  133. package/dist/src/gateway/hono/app.js +2 -1
  134. package/dist/src/gateway/hono/oauth-async.d.ts +1 -0
  135. package/dist/src/gateway/hono/oauth-async.js +11 -4
  136. package/dist/src/gateway/hono/routes/config.js +10 -0
  137. package/dist/src/gateway/hono/routes/host-fs.js +40 -1
  138. package/dist/src/gateway/hono/routes/media.js +33 -1
  139. package/dist/src/gateway/hono/routes/notes.js +162 -7
  140. package/dist/src/gateway/hono/routes/shares.js +141 -6
  141. package/dist/src/gateway/hono/routes/tunnel.js +47 -10
  142. package/dist/src/gateway/hono/routes/voice.d.ts +2 -6
  143. package/dist/src/gateway/hono/routes/voice.js +20 -4
  144. package/dist/src/gateway/hono/routes/workflows.js +2 -2
  145. package/dist/src/gateway/run-loop.js +3 -0
  146. package/dist/src/gateway/server.d.ts +3 -0
  147. package/dist/src/gateway/server.js +30 -5
  148. package/dist/src/gateway/service.js +4 -4
  149. package/dist/src/notes/service.d.ts +6 -2
  150. package/dist/src/notes/service.js +36 -4
  151. package/dist/src/providers/index.js +2 -2
  152. package/dist/src/share/note-share-service.d.ts +67 -0
  153. package/dist/src/share/note-share-service.js +283 -0
  154. package/dist/src/share/share-config.js +32 -3
  155. package/dist/src/share/share-landing.d.ts +5 -1
  156. package/dist/src/share/share-landing.js +28 -1
  157. package/dist/src/share/share-store.d.ts +34 -5
  158. package/dist/src/share/share-store.js +125 -19
  159. package/dist/src/share/share-thumbnail.js +4 -2
  160. package/dist/src/share/share-types.d.ts +52 -14
  161. package/dist/src/share/share-types.js +9 -0
  162. package/dist/src/share/share-zip.d.ts +2 -2
  163. package/dist/src/tui/backends/embedded-backend.js +1 -1
  164. package/dist/src/tunnel/env.d.ts +12 -6
  165. package/dist/src/tunnel/env.js +34 -26
  166. package/dist/src/tunnel/gateway-lifecycle.js +2 -13
  167. package/dist/src/tunnel/index.d.ts +1 -1
  168. package/dist/src/tunnel/index.js +2 -2
  169. package/dist/src/tunnel/tunnel-audit.d.ts +1 -1
  170. package/dist/src/tunnel/tunnel-config.d.ts +1 -1
  171. package/dist/src/tunnel/tunnel-config.js +9 -4
  172. package/dist/src/tunnel/tunnel-service.d.ts +1 -1
  173. package/dist/src/tunnel/tunnel-service.js +3 -0
  174. package/dist/src/tunnel/well-known.d.ts +1 -1
  175. package/dist/src/tunnel/well-known.js +1 -1
  176. package/dist/src/tunnel/xopc-cloud-registration.d.ts +9 -0
  177. package/dist/src/tunnel/xopc-cloud-registration.js +42 -0
  178. package/dist/src/voice/stt/transcribe-core.js +17 -1
  179. package/dist/src/voice/stt/types.d.ts +1 -1
  180. package/package.json +2 -2
  181. package/dist/gateway/static/root/assets/agents-DijFbC4G.js +0 -68
  182. package/dist/gateway/static/root/assets/apps-page-BFAmf9hq.js +0 -1
  183. package/dist/gateway/static/root/assets/automations-workspace-B8us5JBf.js +0 -6
  184. package/dist/gateway/static/root/assets/block-editor-C3mwGsms.js +0 -184
  185. package/dist/gateway/static/root/assets/browser-settings-page-BeBd17-3.js +0 -8
  186. package/dist/gateway/static/root/assets/capabilities-settings-panel-Bv0z3KRq.js +0 -7
  187. package/dist/gateway/static/root/assets/capability-presets-settings-panel-BczD-pWE.js +0 -3
  188. package/dist/gateway/static/root/assets/channels-settings-2KQYq6xl.js +0 -1
  189. package/dist/gateway/static/root/assets/channels-status-swr-ByRbxC6z.js +0 -1
  190. package/dist/gateway/static/root/assets/connectors-page-PB1boaNy.js +0 -2
  191. package/dist/gateway/static/root/assets/dist-C230izr8.js +0 -1
  192. package/dist/gateway/static/root/assets/heartbeat-settings-5YQ5BUwp.js +0 -1
  193. package/dist/gateway/static/root/assets/home-page-DfIVWJl3.js +0 -1
  194. package/dist/gateway/static/root/assets/index-BXGC3VFm.css +0 -2
  195. package/dist/gateway/static/root/assets/index-jqDCfet4.js +0 -74
  196. package/dist/gateway/static/root/assets/local-app-workbench-page-CAb0odzu.js +0 -2
  197. package/dist/gateway/static/root/assets/logs-page-CFxKbDZl.js +0 -2
  198. package/dist/gateway/static/root/assets/markdown-view-DYkxMtJA.js +0 -69
  199. package/dist/gateway/static/root/assets/messages-kx5W39dO.js +0 -3
  200. package/dist/gateway/static/root/assets/notes-api-DJizatxh.js +0 -1
  201. package/dist/gateway/static/root/assets/notes-workbench-BGjEC89c.js +0 -4
  202. package/dist/gateway/static/root/assets/project-detail-page-Dj9kEQwg.js +0 -1
  203. package/dist/gateway/static/root/assets/projects-page-CBRy5Ezz.js +0 -1
  204. package/dist/gateway/static/root/assets/remote-access-hub-DSwz9RBh.js +0 -1
  205. package/dist/gateway/static/root/assets/sessions-page-WtattyVX.js +0 -1
  206. package/dist/gateway/static/root/assets/settings-advanced-gate-CNAz_q1F.js +0 -1
  207. package/dist/gateway/static/root/assets/setup-status-panel-CNo4Ajhn.js +0 -1
  208. package/dist/gateway/static/root/assets/share-preview-page-B7PI3yZ6.js +0 -2
  209. package/dist/gateway/static/root/assets/shares-settings-DpOH0zWB.js +0 -1
  210. package/dist/gateway/static/root/assets/skill-reload-api-BOf4xCat.js +0 -1
  211. package/dist/gateway/static/root/assets/task-detail-page-DrMX5Yhj.js +0 -1
  212. package/dist/gateway/static/root/assets/user-context-page-ewIK2ddC.js +0 -1
  213. package/dist/gateway/static/root/assets/workflows-page-Bdn82kFQ.js +0 -5
@@ -0,0 +1,49 @@
1
+ //#region src/agent/orchestration/agent-run-supervisor.ts
2
+ /** Minimal run-level coordination: root cancellation and per-attempt deadline planning. */
3
+ var AgentRunSupervisor = class {
4
+ deadlineAtMs;
5
+ signal;
6
+ controller = new AbortController();
7
+ timeout;
8
+ forwardParentAbort;
9
+ timeoutMs;
10
+ minimumFallbackMs;
11
+ parentSignal;
12
+ constructor(options) {
13
+ this.timeoutMs = options.timeoutMs;
14
+ this.minimumFallbackMs = options.minimumFallbackMs ?? 5e3;
15
+ this.deadlineAtMs = options.deadlineAtMs ?? Date.now() + options.timeoutMs;
16
+ this.parentSignal = options.parentSignal;
17
+ this.signal = this.controller.signal;
18
+ this.forwardParentAbort = () => this.controller.abort(this.parentSignal?.reason);
19
+ if (this.parentSignal?.aborted) this.forwardParentAbort();
20
+ else this.parentSignal?.addEventListener("abort", this.forwardParentAbort, { once: true });
21
+ this.timeout = setTimeout(() => this.controller.abort(/* @__PURE__ */ new Error("Agent run deadline expired")), Math.max(0, this.deadlineAtMs - Date.now()));
22
+ }
23
+ planModelAttempt(isFallback) {
24
+ if (this.signal.aborted) return {
25
+ ok: false,
26
+ reason: "Agent run aborted"
27
+ };
28
+ const remainingMs = this.deadlineAtMs - Date.now();
29
+ if (remainingMs <= 0) return {
30
+ ok: false,
31
+ reason: "Agent run deadline expired before the next model attempt"
32
+ };
33
+ if (isFallback && remainingMs < this.minimumFallbackMs) return {
34
+ ok: false,
35
+ reason: "Agent run deadline left too little time for a fallback model attempt"
36
+ };
37
+ return {
38
+ ok: true,
39
+ remainingMs,
40
+ timeoutMs: Math.max(1, Math.min(this.timeoutMs, remainingMs))
41
+ };
42
+ }
43
+ dispose() {
44
+ clearTimeout(this.timeout);
45
+ this.parentSignal?.removeEventListener("abort", this.forwardParentAbort);
46
+ }
47
+ };
48
+ //#endregion
49
+ export { AgentRunSupervisor };
@@ -0,0 +1,18 @@
1
+ import type { AfterToolCallContext, BeforeToolCallContext, BeforeToolCallResult, ShouldStopAfterTurnContext } from '@earendil-works/pi-agent-core';
2
+ export interface AgentTurnPolicy {
3
+ reset(): void;
4
+ beforeToolCall(context: BeforeToolCallContext, signal?: AbortSignal): Promise<BeforeToolCallResult | undefined>;
5
+ afterToolCall(context: AfterToolCallContext): Promise<undefined>;
6
+ shouldStopAfterTurn(context: ShouldStopAfterTurnContext): boolean;
7
+ }
8
+ export interface AgentTurnPolicyOptions {
9
+ maxTurns?: number;
10
+ maxToolFailures?: number;
11
+ resolveToolLimit?: (toolName: string, args: unknown) => {
12
+ id: string;
13
+ maxCalls: number;
14
+ } | undefined;
15
+ authorizeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
16
+ }
17
+ /** One user-visible run policy. A fresh instance is created for every embedded run. */
18
+ export declare function createAgentTurnPolicy(options: AgentTurnPolicyOptions): AgentTurnPolicy;
@@ -0,0 +1,36 @@
1
+ //#region src/agent/orchestration/agent-turn-policy.ts
2
+ function assistantMessageCount(messages) {
3
+ return messages.filter((message) => message.role === "assistant").length;
4
+ }
5
+ /** One user-visible run policy. A fresh instance is created for every embedded run. */
6
+ function createAgentTurnPolicy(options) {
7
+ const toolCalls = /* @__PURE__ */ new Map();
8
+ let toolFailures = 0;
9
+ return {
10
+ reset() {
11
+ toolCalls.clear();
12
+ toolFailures = 0;
13
+ },
14
+ async beforeToolCall(context, signal) {
15
+ const limit = options.resolveToolLimit?.(context.toolCall.name, context.args);
16
+ if (limit) {
17
+ const count = (toolCalls.get(limit.id) ?? 0) + 1;
18
+ toolCalls.set(limit.id, count);
19
+ if (count > limit.maxCalls) return {
20
+ block: true,
21
+ terminate: true,
22
+ reason: `${limit.id} exceeded its per-turn call limit.`
23
+ };
24
+ }
25
+ return options.authorizeToolCall?.(context, signal);
26
+ },
27
+ async afterToolCall(context) {
28
+ if (context.isError) toolFailures += 1;
29
+ },
30
+ shouldStopAfterTurn(context) {
31
+ return Boolean(options.maxTurns && assistantMessageCount(context.newMessages) >= options.maxTurns || options.maxToolFailures && toolFailures >= options.maxToolFailures);
32
+ }
33
+ };
34
+ }
35
+ //#endregion
36
+ export { createAgentTurnPolicy };
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import type { Agent, AgentMessage } from '@earendil-works/pi-agent-core';
8
8
  export declare function isTransientLlmErrorMessage(message: string): boolean;
9
+ export type LlmFailureKind = 'transient_network' | 'context_overflow' | 'aborted' | 'permanent';
10
+ export declare function classifyLlmFailure(message: string): LlmFailureKind;
9
11
  export declare function getLastAssistantMessage(messages: AgentMessage[]): AgentMessage | undefined;
10
12
  /** Raw provider/LLM error from the last failed assistant message, if any. */
11
13
  export declare function getAssistantTurnErrorMessage(agent: Agent): string | undefined;
@@ -20,6 +22,9 @@ export declare function stripTrailingErrorAssistantMessages(messages: AgentMessa
20
22
  export interface RetryTransientTurnOptions {
21
23
  /** Extra turns after a failed assistant message (default 2). */
22
24
  maxContinues?: number;
25
+ /** Initial deterministic backoff. Each later retry doubles it. */
26
+ baseDelayMs?: number;
27
+ signal?: AbortSignal;
23
28
  sessionKey: string;
24
29
  log: {
25
30
  warn: (obj: Record<string, unknown>, msg: string) => void;
@@ -1,3 +1,4 @@
1
+ import { isContextOverflowError } from "./context-overflow.js";
1
2
  //#region src/agent/orchestration/llm-turn-retry.ts
2
3
  const TRANSIENT_LLM_ERROR_SUBSTRINGS = [
3
4
  "fetch failed",
@@ -16,6 +17,13 @@ function isTransientLlmErrorMessage(message) {
16
17
  const lower = message.toLowerCase();
17
18
  return TRANSIENT_LLM_ERROR_SUBSTRINGS.some((s) => lower.includes(s));
18
19
  }
20
+ function classifyLlmFailure(message) {
21
+ const normalized = message.trim().toLowerCase();
22
+ if (normalized === "aborted" || normalized.includes("aborterror")) return "aborted";
23
+ if (isContextOverflowError(message)) return "context_overflow";
24
+ if (isTransientLlmErrorMessage(message)) return "transient_network";
25
+ return "permanent";
26
+ }
19
27
  function getLastAssistantMessage(messages) {
20
28
  for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "assistant") return messages[i];
21
29
  }
@@ -56,32 +64,54 @@ function stripTrailingErrorAssistantMessages(messages) {
56
64
  }
57
65
  return out;
58
66
  }
67
+ async function waitForRetryDelay(delayMs, signal) {
68
+ signal?.throwIfAborted();
69
+ if (delayMs <= 0) return;
70
+ await new Promise((resolve, reject) => {
71
+ const onAbort = () => {
72
+ clearTimeout(timer);
73
+ reject(signal?.reason ?? new DOMException("Aborted", "AbortError"));
74
+ };
75
+ const timer = setTimeout(() => {
76
+ signal?.removeEventListener("abort", onAbort);
77
+ resolve();
78
+ }, delayMs);
79
+ signal?.addEventListener("abort", onAbort, { once: true });
80
+ });
81
+ }
59
82
  /**
60
83
  * After waitForIdle(), call this to optionally re-run the last user turn when the
61
84
  * assistant message only contains a transient provider/network error.
62
85
  */
63
86
  async function maybeRetryTurnAfterTransientLlmFailure(agent, options) {
64
87
  const maxContinues = options.maxContinues ?? 2;
88
+ const baseDelayMs = Math.max(0, options.baseDelayMs ?? 300);
65
89
  let continues = 0;
66
90
  while (continues < maxContinues) {
67
91
  const last = getLastAssistantMessage(agent.state.messages);
68
92
  if (!last) return;
69
93
  if (last.stopReason !== "error") return;
70
94
  const errMsg = String(last.errorMessage || "");
71
- if (!isTransientLlmErrorMessage(errMsg)) {
95
+ const failureKind = classifyLlmFailure(errMsg);
96
+ if (failureKind !== "transient_network") {
72
97
  options.log.warn({
73
98
  sessionKey: options.sessionKey,
74
- errorMessage: errMsg
99
+ errorMessage: errMsg,
100
+ failureKind
75
101
  }, "Assistant turn ended with error (not retrying as transient)");
76
102
  return;
77
103
  }
78
104
  continues += 1;
105
+ const retryDelayMs = baseDelayMs * 2 ** (continues - 1);
79
106
  options.log.warn({
80
107
  sessionKey: options.sessionKey,
81
108
  errorMessage: errMsg,
109
+ failureKind,
82
110
  continueAttempt: continues,
83
- maxContinues
111
+ maxContinues,
112
+ retryDelayMs
84
113
  }, "LLM request failed with a transient network error; retrying the same turn. If this persists, check outbound HTTPS to the provider API and HTTP(S)_PROXY.");
114
+ await waitForRetryDelay(retryDelayMs, options.signal);
85
115
  const trimmed = stripTrailingErrorAssistantMessages(agent.state.messages);
86
116
  agent.state.messages = trimmed;
87
117
  await agent.continue();
@@ -89,4 +119,4 @@ async function maybeRetryTurnAfterTransientLlmFailure(agent, options) {
89
119
  }
90
120
  }
91
121
  //#endregion
92
- export { getAssistantTurnErrorMessage, getLastAssistantMessage, isAssistantTurnAborted, isAssistantTurnFailed, isTransientLlmErrorMessage, maybeRetryTurnAfterTransientLlmFailure, stripTrailingErrorAssistantMessages };
122
+ export { classifyLlmFailure, getAssistantTurnErrorMessage, getLastAssistantMessage, isAssistantTurnAborted, isAssistantTurnFailed, isTransientLlmErrorMessage, maybeRetryTurnAfterTransientLlmFailure, stripTrailingErrorAssistantMessages };
@@ -10,6 +10,15 @@ export declare const MIN_AGENT_TURN_TIMEOUT_MS = 60000;
10
10
  export declare const MAX_AGENT_TURN_TIMEOUT_MS: number;
11
11
  /** Default per-turn timeout (30 minutes). */
12
12
  export declare const DEFAULT_AGENT_TURN_TIMEOUT_MS: number;
13
+ export declare const DEFAULT_AGENT_ABORT_GRACE_MS = 5000;
14
+ export declare class AgentTurnUnsettledError extends Error {
15
+ readonly timeoutMs: number;
16
+ readonly abortGraceMs: number;
17
+ constructor(timeoutMs: number, abortGraceMs: number);
18
+ }
19
+ export declare function isAgentTurnUnsettledError(err: unknown): err is AgentTurnUnsettledError;
13
20
  export declare function resolveAgentTurnTimeoutMs(config?: Config, sessionKey?: string): number;
14
21
  export declare function isAgentTurnTimeoutError(err: unknown): boolean;
15
- export declare function runAgentTurnWithTimeout(agent: Agent, runTurn: () => Promise<void>, timeoutMs: number): Promise<void>;
22
+ export declare function runAgentTurnWithTimeout(agent: Agent, runTurn: () => Promise<void>, timeoutMs: number, options?: {
23
+ abortGraceMs?: number;
24
+ }): Promise<void>;
@@ -6,6 +6,18 @@ const MIN_AGENT_TURN_TIMEOUT_MS = 6e4;
6
6
  const MAX_AGENT_TURN_TIMEOUT_MS = 14400 * 1e3;
7
7
  /** Default per-turn timeout (30 minutes). */
8
8
  const DEFAULT_AGENT_TURN_TIMEOUT_MS = 1800 * 1e3;
9
+ const DEFAULT_AGENT_ABORT_GRACE_MS = 5e3;
10
+ var AgentTurnUnsettledError = class extends Error {
11
+ constructor(timeoutMs, abortGraceMs) {
12
+ super(`Agent turn timed out after ${timeoutMs / 1e3}s and did not settle within ${abortGraceMs / 1e3}s`);
13
+ this.timeoutMs = timeoutMs;
14
+ this.abortGraceMs = abortGraceMs;
15
+ this.name = "AgentTurnUnsettledError";
16
+ }
17
+ };
18
+ function isAgentTurnUnsettledError(err) {
19
+ return err instanceof AgentTurnUnsettledError;
20
+ }
9
21
  function resolveAgentTurnTimeoutMs(config, sessionKey) {
10
22
  if (!config) return DEFAULT_AGENT_TURN_TIMEOUT_MS;
11
23
  const configured = resolveEffectiveAgentManifestForSession(config, sessionKey).runtime?.timeoutMs;
@@ -15,22 +27,31 @@ function resolveAgentTurnTimeoutMs(config, sessionKey) {
15
27
  function isAgentTurnTimeoutError(err) {
16
28
  return err instanceof Error && err.message.startsWith("Agent turn timed out after");
17
29
  }
18
- async function runAgentTurnWithTimeout(agent, runTurn, timeoutMs) {
30
+ async function runAgentTurnWithTimeout(agent, runTurn, timeoutMs, options = {}) {
19
31
  let timeoutId;
20
32
  const timeoutPromise = new Promise((_, reject) => {
21
33
  timeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error(`Agent turn timed out after ${timeoutMs / 1e3}s`)), timeoutMs);
22
34
  });
35
+ const runTurnPromise = Promise.resolve().then(runTurn);
23
36
  try {
24
- await Promise.race([runTurn().finally(() => {
37
+ await Promise.race([runTurnPromise.finally(() => {
25
38
  if (timeoutId !== void 0) clearTimeout(timeoutId);
26
39
  }), timeoutPromise]);
27
40
  } catch (err) {
28
41
  if (isAgentTurnTimeoutError(err)) {
29
42
  agent.abort();
30
- await agent.waitForIdle();
43
+ const abortGraceMs = options.abortGraceMs ?? 5e3;
44
+ let graceTimer;
45
+ try {
46
+ await Promise.race([Promise.allSettled([runTurnPromise, agent.waitForIdle()]), new Promise((_, reject) => {
47
+ graceTimer = setTimeout(() => reject(new AgentTurnUnsettledError(timeoutMs, abortGraceMs)), abortGraceMs);
48
+ })]);
49
+ } finally {
50
+ if (graceTimer !== void 0) clearTimeout(graceTimer);
51
+ }
31
52
  }
32
53
  throw err;
33
54
  }
34
55
  }
35
56
  //#endregion
36
- export { DEFAULT_AGENT_TURN_TIMEOUT_MS, MAX_AGENT_TURN_TIMEOUT_MS, MIN_AGENT_TURN_TIMEOUT_MS, isAgentTurnTimeoutError, resolveAgentTurnTimeoutMs, runAgentTurnWithTimeout };
57
+ export { AgentTurnUnsettledError, DEFAULT_AGENT_ABORT_GRACE_MS, DEFAULT_AGENT_TURN_TIMEOUT_MS, MAX_AGENT_TURN_TIMEOUT_MS, MIN_AGENT_TURN_TIMEOUT_MS, isAgentTurnTimeoutError, isAgentTurnUnsettledError, resolveAgentTurnTimeoutMs, runAgentTurnWithTimeout };
@@ -3,15 +3,28 @@
3
3
  *
4
4
  * Replaces the ad-hoc `eventQueue + resolveWaiting` single-slot pattern that
5
5
  * the streaming direct turn used to manage run events. The previous pattern was
6
- * lossy if two pushes landed before the single waiter resumed; this queue keeps
7
- * the buffer growing until consumed, and closes cleanly so the iterator drains.
6
+ * lossy if two pushes landed before the single waiter resumed; this queue buffers
7
+ * until consumed, supports an explicit bound, and drains cleanly on close.
8
8
  */
9
+ export interface AsyncQueueOptions<T> {
10
+ /** Maximum buffer size. Lossy events are evicted; critical-only overflow fails fast. */
11
+ maxBuffered?: number;
12
+ isDroppable?: (value: T) => boolean;
13
+ }
14
+ export declare class AsyncQueueOverflowError extends Error {
15
+ readonly maxBuffered: number;
16
+ constructor(maxBuffered: number);
17
+ }
9
18
  export declare class AsyncQueue<T> implements AsyncIterable<T> {
19
+ private readonly options;
10
20
  private readonly buffer;
11
21
  private readonly waiters;
12
22
  private closed;
23
+ private dropped;
24
+ constructor(options?: AsyncQueueOptions<T>);
13
25
  /** Push a value to the queue. No-op when the queue is closed. */
14
- push(value: T): void;
26
+ push(value: T): boolean;
27
+ get droppedCount(): number;
15
28
  /** Close the queue. Pending iterators drain any buffered values and then complete. */
16
29
  close(): void;
17
30
  /** True if the queue is closed and the internal buffer is drained. */
@@ -1,25 +1,43 @@
1
1
  //#region src/agent/service/async-queue.ts
2
- /**
3
- * Async FIFO queue with `for await` support.
4
- *
5
- * Replaces the ad-hoc `eventQueue + resolveWaiting` single-slot pattern that
6
- * the streaming direct turn used to manage run events. The previous pattern was
7
- * lossy if two pushes landed before the single waiter resumed; this queue keeps
8
- * the buffer growing until consumed, and closes cleanly so the iterator drains.
9
- */
2
+ var AsyncQueueOverflowError = class extends Error {
3
+ constructor(maxBuffered) {
4
+ super(`Async queue exceeded its ${maxBuffered}-event buffer`);
5
+ this.maxBuffered = maxBuffered;
6
+ this.name = "AsyncQueueOverflowError";
7
+ }
8
+ };
10
9
  var AsyncQueue = class {
11
10
  buffer = [];
12
11
  waiters = [];
13
12
  closed = false;
13
+ dropped = 0;
14
+ constructor(options = {}) {
15
+ this.options = options;
16
+ }
14
17
  /** Push a value to the queue. No-op when the queue is closed. */
15
18
  push(value) {
16
- if (this.closed) return;
19
+ if (this.closed) return false;
17
20
  const waiter = this.waiters.shift();
18
21
  if (waiter) {
19
22
  waiter(value);
20
- return;
23
+ return true;
24
+ }
25
+ const maxBuffered = this.options.maxBuffered;
26
+ if (maxBuffered && this.buffer.length >= maxBuffered) {
27
+ const droppableIndex = this.options.isDroppable ? this.buffer.findIndex(this.options.isDroppable) : -1;
28
+ if (droppableIndex >= 0) {
29
+ this.buffer.splice(droppableIndex, 1);
30
+ this.dropped += 1;
31
+ } else if (this.options.isDroppable?.(value)) {
32
+ this.dropped += 1;
33
+ return false;
34
+ } else throw new AsyncQueueOverflowError(maxBuffered);
21
35
  }
22
36
  this.buffer.push(value);
37
+ return true;
38
+ }
39
+ get droppedCount() {
40
+ return this.dropped;
23
41
  }
24
42
  /** Close the queue. Pending iterators drain any buffered values and then complete. */
25
43
  close() {
@@ -48,4 +66,4 @@ var AsyncQueue = class {
48
66
  };
49
67
  const CLOSE_SENTINEL = Symbol("AsyncQueue.close");
50
68
  //#endregion
51
- export { AsyncQueue };
69
+ export { AsyncQueue, AsyncQueueOverflowError };
@@ -11,9 +11,16 @@ import { isVoiceLikeAttachment, mergeVoiceTranscriptsIntoUserText } from "../../
11
11
  import { applyReasoningVisibility } from "../streaming/reasoning-visibility.js";
12
12
  import { isSessionSourceBinding, summarizeSourceContext } from "../source-context/types.js";
13
13
  import { injectSourceContextsIntoUserMessage } from "../source-context/injector.js";
14
- import { AsyncQueue } from "./async-queue.js";
14
+ import { AsyncQueue, AsyncQueueOverflowError } from "./async-queue.js";
15
15
  import { hydratePerTurnState, runDirectAgentTurn, tryRunSlashCommand } from "./direct-turn-helpers.js";
16
16
  //#region src/agent/service/process-direct-streaming.ts
17
+ const MAX_BUFFERED_STREAM_EVENTS = 512;
18
+ const LOSSY_STREAM_EVENT_TYPES = new Set([
19
+ "message_update",
20
+ "tool_execution_update",
21
+ "progress",
22
+ "review_delta"
23
+ ]);
17
24
  function isReviewOutput(value) {
18
25
  return !!value && typeof value === "object" && !Array.isArray(value) && value.type === "review";
19
26
  }
@@ -118,7 +125,10 @@ async function* runProcessDirectStreaming(deps, input) {
118
125
  const sessionKey = input.sessionKey ?? "agent:main:main";
119
126
  const { channel, chatId } = await deps.resolveSessionEndpoint(sessionKey);
120
127
  const context = deps.initDirectStreamingSession(sessionKey, channel, chatId, input.origin);
121
- const queue = new AsyncQueue();
128
+ const queue = new AsyncQueue({
129
+ maxBuffered: MAX_BUFFERED_STREAM_EVENTS,
130
+ isDroppable: (event) => LOSSY_STREAM_EVENT_TYPES.has(event.type)
131
+ });
122
132
  let reasoningLevel = channel === "webchat" ? resolveConfiguredActivityDetailDefault(deps.getConfig()) : "stream";
123
133
  const pushVisible = (event) => {
124
134
  const visible = channel === "webchat" ? event : applyReasoningVisibility(event, reasoningLevel);
@@ -144,6 +154,7 @@ async function* runProcessDirectStreaming(deps, input) {
144
154
  let ranSlashCommand = false;
145
155
  let mergedUserText = input.content;
146
156
  let webchatSlashReceipt;
157
+ let streamOverflowed = false;
147
158
  let slashCommandMetadata;
148
159
  const slashTraceRows = [];
149
160
  const taskPromise = (async () => {
@@ -301,12 +312,24 @@ async function* runProcessDirectStreaming(deps, input) {
301
312
  clearPendingTranscriptUserMessage(sessionKey, pendingUserMessage);
302
313
  }
303
314
  } catch (err) {
304
- if (!abortHandled) pushVisible({
315
+ if (err instanceof AsyncQueueOverflowError) {
316
+ streamOverflowed = true;
317
+ await abortEmbeddedRun(sessionKey).catch(() => false);
318
+ deps.log.warn({
319
+ err,
320
+ sessionKey,
321
+ maxBuffered: err.maxBuffered
322
+ }, "Aborted agent stream because the consumer could not keep up with critical events");
323
+ } else if (!abortHandled) pushVisible({
305
324
  type: "error",
306
325
  content: formatStreamError(err instanceof Error ? err.message : String(err))
307
326
  });
308
327
  } finally {
309
- if (!userAborted && channel === "webchat") try {
328
+ if (queue.droppedCount > 0) deps.log.warn({
329
+ sessionKey,
330
+ droppedEvents: queue.droppedCount
331
+ }, "Coalesced stream progress events because the consumer fell behind");
332
+ if (!userAborted && !streamOverflowed && channel === "webchat") try {
310
333
  const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);
311
334
  if (ttsAudioEvent) queue.push(ttsAudioEvent);
312
335
  } catch (ttsErr) {
@@ -602,6 +602,7 @@ var AgentService = class {
602
602
  workspaceDir,
603
603
  transcriptRuntime: params.transcriptRuntime,
604
604
  timeoutMs: resolveAgentTurnTimeoutMs(this.effectiveAppConfig(), params.parentSessionKey),
605
+ turnPolicy: this.agentManager.createAgentTurnPolicy(params.parentSessionKey),
605
606
  abortSignal: params.abortSignal,
606
607
  onEvent: params.onEvent
607
608
  }));
@@ -9,6 +9,8 @@ export interface ExecCommandDetails {
9
9
  truncated: boolean;
10
10
  truncatedBy?: 'lines' | 'bytes' | null;
11
11
  outputBytes: number;
12
+ totalOutputBytes: number;
13
+ captureTruncated: boolean;
12
14
  stdout: string;
13
15
  stderr: string;
14
16
  aggregatedOutput: string;
@@ -25,6 +25,45 @@ function commandStatus(exitCode, timedOut) {
25
25
  if (exitCode === 0) return "success";
26
26
  return "failed";
27
27
  }
28
+ function appendBoundedTail(current, chunk, maxChars) {
29
+ const combined = current + chunk;
30
+ if (combined.length <= maxChars) return {
31
+ value: combined,
32
+ truncated: false
33
+ };
34
+ return {
35
+ value: combined.slice(-maxChars),
36
+ truncated: true
37
+ };
38
+ }
39
+ function terminateProcessTree(proc) {
40
+ if (!proc.pid) return;
41
+ if (process.platform === "win32") {
42
+ const killer = spawn("taskkill", [
43
+ "/pid",
44
+ String(proc.pid),
45
+ "/t",
46
+ "/f"
47
+ ], {
48
+ stdio: "ignore",
49
+ windowsHide: true
50
+ });
51
+ killer.once("error", () => {
52
+ try {
53
+ proc.kill("SIGKILL");
54
+ } catch {}
55
+ });
56
+ killer.unref();
57
+ return;
58
+ }
59
+ try {
60
+ process.kill(-proc.pid, "SIGKILL");
61
+ return;
62
+ } catch {}
63
+ try {
64
+ proc.kill("SIGKILL");
65
+ } catch {}
66
+ }
28
67
  function commandFailureHint(details) {
29
68
  if (details.status === "success") return void 0;
30
69
  if (details.status === "timed_out") return "The command timed out. Re-run a narrower command, increase timeoutMs if the long run is expected, or inspect partial output before retrying.";
@@ -36,6 +75,7 @@ function formatOutputForModel(details, maxOutputChars) {
36
75
  let prefix = "";
37
76
  if (details.timedOut) prefix += `Command timed out after ${Math.round(details.durationMs / 1e3)}s\n`;
38
77
  if (details.exitCode !== 0 && details.exitCode != null) prefix += `Command exited with code ${details.exitCode}\n`;
78
+ if (details.captureTruncated) prefix += `Earlier command output was discarded after ${formatSize(details.totalOutputBytes)}; showing the latest captured output.\n`;
39
79
  const truncation = truncateTail(details.aggregatedOutput, { maxBytes: Math.min(maxOutputChars, DEFAULT_MAX_OUTPUT_CHARS) });
40
80
  let text = `${prefix}${truncation.content}`.trimEnd();
41
81
  if (!text) text = "(no output)";
@@ -78,6 +118,8 @@ function createExecCommandTool(workspaceCwd, options) {
78
118
  timedOut: false,
79
119
  truncated: false,
80
120
  outputBytes: 0,
121
+ totalOutputBytes: 0,
122
+ captureTruncated: false,
81
123
  stdout: "",
82
124
  stderr: "cmd is required",
83
125
  aggregatedOutput: "cmd is required",
@@ -106,6 +148,8 @@ function createExecCommandTool(workspaceCwd, options) {
106
148
  timedOut: false,
107
149
  truncated: false,
108
150
  outputBytes: Buffer.byteLength(text),
151
+ totalOutputBytes: Buffer.byteLength(text),
152
+ captureTruncated: false,
109
153
  stdout: "",
110
154
  stderr: text,
111
155
  aggregatedOutput: text,
@@ -120,11 +164,14 @@ function createExecCommandTool(workspaceCwd, options) {
120
164
  let stdout = "";
121
165
  let stderr = "";
122
166
  let aggregatedOutput = "";
167
+ let totalOutputBytes = 0;
168
+ let captureTruncated = false;
123
169
  let timedOut = false;
124
170
  let settled = false;
125
171
  return new Promise((resolveTool) => {
126
172
  const proc = spawn(command, [], {
127
173
  shell: true,
174
+ detached: process.platform !== "win32",
128
175
  cwd: policy.effectiveCwd,
129
176
  env: {
130
177
  ...runtimeEnv,
@@ -134,9 +181,19 @@ function createExecCommandTool(workspaceCwd, options) {
134
181
  const publishDelta = (stream, raw) => {
135
182
  const text = raw.toString();
136
183
  if (!text) return;
137
- if (stream === "stdout") stdout += text;
138
- else stderr += text;
139
- aggregatedOutput += text;
184
+ totalOutputBytes += Buffer.byteLength(text);
185
+ if (stream === "stdout") {
186
+ const next = appendBoundedTail(stdout, text, maxOutputChars);
187
+ stdout = next.value;
188
+ captureTruncated ||= next.truncated;
189
+ } else {
190
+ const next = appendBoundedTail(stderr, text, maxOutputChars);
191
+ stderr = next.value;
192
+ captureTruncated ||= next.truncated;
193
+ }
194
+ const nextAggregate = appendBoundedTail(aggregatedOutput, text, maxOutputChars);
195
+ aggregatedOutput = nextAggregate.value;
196
+ captureTruncated ||= nextAggregate.truncated;
140
197
  const delta = text.length > STREAM_DELTA_MAX_CHARS ? `${text.slice(0, STREAM_DELTA_MAX_CHARS)}\n[stream delta truncated]` : text;
141
198
  onUpdate?.({
142
199
  content: [],
@@ -155,8 +212,12 @@ function createExecCommandTool(workspaceCwd, options) {
155
212
  clearTimeout(timeout);
156
213
  signal?.removeEventListener("abort", abort);
157
214
  if (errorText) {
158
- stderr += errorText;
159
- aggregatedOutput += errorText;
215
+ totalOutputBytes += Buffer.byteLength(errorText);
216
+ const nextStderr = appendBoundedTail(stderr, errorText, maxOutputChars);
217
+ stderr = nextStderr.value;
218
+ const nextAggregate = appendBoundedTail(aggregatedOutput, errorText, maxOutputChars);
219
+ aggregatedOutput = nextAggregate.value;
220
+ captureTruncated ||= nextStderr.truncated || nextAggregate.truncated;
160
221
  }
161
222
  const durationMs = Date.now() - startTime;
162
223
  const baseDetails = {
@@ -168,6 +229,8 @@ function createExecCommandTool(workspaceCwd, options) {
168
229
  timedOut,
169
230
  truncated: false,
170
231
  outputBytes: Buffer.byteLength(aggregatedOutput),
232
+ totalOutputBytes,
233
+ captureTruncated,
171
234
  stdout,
172
235
  stderr,
173
236
  aggregatedOutput
@@ -190,11 +253,11 @@ function createExecCommandTool(workspaceCwd, options) {
190
253
  };
191
254
  const abort = () => {
192
255
  timedOut = true;
193
- proc.kill("SIGKILL");
256
+ terminateProcessTree(proc);
194
257
  };
195
258
  const timeout = setTimeout(() => {
196
259
  timedOut = true;
197
- proc.kill("SIGKILL");
260
+ terminateProcessTree(proc);
198
261
  }, timeoutMs);
199
262
  if (signal?.aborted) abort();
200
263
  else signal?.addEventListener("abort", abort, { once: true });
@@ -8,8 +8,8 @@ import { init_kimi_coding, kimiCodingOAuthProvider } from "./kimi-coding.js";
8
8
  import { init_minimax_cn, minimaxCnOAuthProvider } from "./minimax-cn.js";
9
9
  import { init_minimax, minimaxOAuthProvider } from "./minimax.js";
10
10
  import { init_openai_codex, loginOpenAICodex, openaiCodexOAuthProvider, refreshOpenAICodexToken } from "./openai-codex.js";
11
- import { init_xopc_cloud, xopcCloudOAuthProvider } from "./xopc-cloud.js";
12
- import { OAUTH_PROVIDER_DEFINITIONS, getOAuthProviderDefinition, getOAuthProviderIds, getOAuthProviderInterfaces, init_registry, isOAuthOnlyProvider } from "./registry.js";
11
+ import { init_xopc_cloud, xopcCloudOAuthProvider, xopcTunnelOAuthProvider } from "./xopc-cloud.js";
12
+ import { OAUTH_PROVIDER_DEFINITIONS, getModelOAuthProviderIds, getOAuthProviderDefinition, getOAuthProviderIds, getOAuthProviderInterfaces, init_registry, isOAuthOnlyProvider } from "./registry.js";
13
13
  //#region src/auth/oauth/index.ts
14
14
  init_anthropic();
15
15
  init_minimax();
@@ -22,4 +22,4 @@ init_openai_codex();
22
22
  init_xopc_cloud();
23
23
  init_registry();
24
24
  //#endregion
25
- export { OAUTH_PROVIDER_DEFINITIONS, anthropicOAuthProvider, getOAuthProviderDefinition, getOAuthProviderIds, getOAuthProviderInterfaces, githubCopilotOAuthProvider, antigravityOAuthProvider as googleAntigravityOAuthProvider, geminiCliOAuthProvider as googleGeminiCliOAuthProvider, isOAuthOnlyProvider, kimiCodingOAuthProvider, loginAnthropic, loginAntigravity, loginGeminiCli, loginOpenAICodex, minimaxCnOAuthProvider, minimaxOAuthProvider, openaiCodexOAuthProvider, refreshAnthropicToken, refreshAntigravityToken, refreshGoogleCloudToken, refreshOpenAICodexToken, xopcCloudOAuthProvider };
25
+ export { OAUTH_PROVIDER_DEFINITIONS, anthropicOAuthProvider, getModelOAuthProviderIds, getOAuthProviderDefinition, getOAuthProviderIds, getOAuthProviderInterfaces, githubCopilotOAuthProvider, antigravityOAuthProvider as googleAntigravityOAuthProvider, geminiCliOAuthProvider as googleGeminiCliOAuthProvider, isOAuthOnlyProvider, kimiCodingOAuthProvider, loginAnthropic, loginAntigravity, loginGeminiCli, loginOpenAICodex, minimaxCnOAuthProvider, minimaxOAuthProvider, openaiCodexOAuthProvider, refreshAnthropicToken, refreshAntigravityToken, refreshGoogleCloudToken, refreshOpenAICodexToken, xopcCloudOAuthProvider, xopcTunnelOAuthProvider };
@@ -2,6 +2,7 @@ import type { OAuthProviderInterface } from './types.js';
2
2
  export interface OAuthProviderDefinition {
3
3
  displayName: string;
4
4
  oauthOnly?: boolean;
5
+ purpose: 'models' | 'tunnel';
5
6
  provider: OAuthProviderInterface;
6
7
  profileId: string;
7
8
  urlPrompt: string;
@@ -10,4 +11,5 @@ export declare const OAUTH_PROVIDER_DEFINITIONS: Readonly<Record<string, OAuthPr
10
11
  export declare function getOAuthProviderDefinition(provider: string): OAuthProviderDefinition | undefined;
11
12
  export declare function getOAuthProviderInterfaces(): Record<string, OAuthProviderInterface>;
12
13
  export declare function getOAuthProviderIds(): string[];
14
+ export declare function getModelOAuthProviderIds(): string[];
13
15
  export declare function isOAuthOnlyProvider(provider: string): boolean;