@code-yeongyu/senpi 2026.8.29 → 2026.8.30

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 (200) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/core/agent-session.d.ts +45 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +312 -25
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/bash-executor.d.ts.map +1 -1
  7. package/dist/core/bash-executor.js +155 -37
  8. package/dist/core/bash-executor.js.map +1 -1
  9. package/dist/core/credential-accounts.d.ts.map +1 -1
  10. package/dist/core/credential-accounts.js +23 -9
  11. package/dist/core/credential-accounts.js.map +1 -1
  12. package/dist/core/credential-pool/env-slots.d.ts.map +1 -1
  13. package/dist/core/credential-pool/env-slots.js +2 -0
  14. package/dist/core/credential-pool/env-slots.js.map +1 -1
  15. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -2
  16. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  17. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +160 -47
  18. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  19. package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
  21. package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
  22. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
  23. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
  24. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
  25. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  26. package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
  27. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  28. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  29. package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
  30. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  31. package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
  32. package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
  33. package/dist/core/extensions/builtin/terminal/manager.js +31 -3
  34. package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
  35. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
  36. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
  37. package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
  38. package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
  39. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
  40. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
  42. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  43. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
  45. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  46. package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
  48. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
  49. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
  50. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  51. package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
  52. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  53. package/dist/core/model-resolver.js +2 -2
  54. package/dist/core/model-resolver.js.map +1 -1
  55. package/dist/core/session-manager.d.ts.map +1 -1
  56. package/dist/core/session-manager.js +10 -1
  57. package/dist/core/session-manager.js.map +1 -1
  58. package/dist/core/settings-manager.d.ts +7 -0
  59. package/dist/core/settings-manager.d.ts.map +1 -1
  60. package/dist/core/settings-manager.js +6 -0
  61. package/dist/core/settings-manager.js.map +1 -1
  62. package/dist/core/tools/bash.d.ts.map +1 -1
  63. package/dist/core/tools/bash.js +97 -15
  64. package/dist/core/tools/bash.js.map +1 -1
  65. package/dist/core/tools/index.d.ts +1 -0
  66. package/dist/core/tools/index.d.ts.map +1 -1
  67. package/dist/core/tools/index.js +8 -0
  68. package/dist/core/tools/index.js.map +1 -1
  69. package/dist/core/tools/output-accumulator.d.ts +1 -1
  70. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  71. package/dist/core/tools/output-accumulator.js +76 -34
  72. package/dist/core/tools/output-accumulator.js.map +1 -1
  73. package/dist/modes/interactive/components/footer.d.ts +7 -0
  74. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  75. package/dist/modes/interactive/components/footer.js +34 -8
  76. package/dist/modes/interactive/components/footer.js.map +1 -1
  77. package/dist/modes/interactive/grok/chrome.d.ts +2 -0
  78. package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
  79. package/dist/modes/interactive/grok/chrome.js.map +1 -1
  80. package/dist/modes/interactive/interactive-host-runtime.d.ts +51 -1
  81. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  82. package/dist/modes/interactive/interactive-host-runtime.js +235 -20
  83. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  84. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  85. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  86. package/dist/modes/interactive/interactive-mode.js +114 -2
  87. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  88. package/dist/modes/rpc/connection-handler.d.ts +8 -1
  89. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  90. package/dist/modes/rpc/connection-handler.js +145 -49
  91. package/dist/modes/rpc/connection-handler.js.map +1 -1
  92. package/dist/modes/rpc/host-ensure.d.ts +2 -0
  93. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  94. package/dist/modes/rpc/host-ensure.js +15 -2
  95. package/dist/modes/rpc/host-ensure.js.map +1 -1
  96. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  97. package/dist/modes/rpc/host-lifecycle.js +31 -12
  98. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  99. package/dist/modes/rpc/host-watchdog.d.ts +2 -0
  100. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  101. package/dist/modes/rpc/host-watchdog.js +23 -12
  102. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  103. package/dist/modes/rpc/multi-session-host.js +6 -4
  104. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  105. package/dist/modes/rpc/rpc-client.d.ts +10 -4
  106. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  107. package/dist/modes/rpc/rpc-client.js +45 -10
  108. package/dist/modes/rpc/rpc-client.js.map +1 -1
  109. package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
  110. package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
  111. package/dist/modes/rpc/rpc-input-validation.js +66 -0
  112. package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
  113. package/dist/modes/rpc/rpc-types.d.ts +79 -1
  114. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  115. package/dist/modes/rpc/rpc-types.js.map +1 -1
  116. package/dist/modes/rpc/session-binding.d.ts +1 -0
  117. package/dist/modes/rpc/session-binding.d.ts.map +1 -1
  118. package/dist/modes/rpc/session-binding.js +21 -1
  119. package/dist/modes/rpc/session-binding.js.map +1 -1
  120. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  121. package/dist/modes/rpc/session-command-router.js +1 -0
  122. package/dist/modes/rpc/session-command-router.js.map +1 -1
  123. package/dist/modes/rpc/session-event-writer.d.ts +2 -0
  124. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  125. package/dist/modes/rpc/session-event-writer.js +54 -7
  126. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  127. package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
  128. package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
  129. package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
  130. package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
  131. package/dist/modes/rpc/session-registry.d.ts +10 -2
  132. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  133. package/dist/modes/rpc/session-registry.js +58 -4
  134. package/dist/modes/rpc/session-registry.js.map +1 -1
  135. package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
  136. package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
  137. package/dist/modes/rpc/socket-event-fanout.js +84 -0
  138. package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
  139. package/docs/rpc.md +3 -1
  140. package/docs/settings.md +34 -0
  141. package/docs/windows.md +2 -0
  142. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +38 -0
  143. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  144. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
  145. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
  146. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  147. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
  148. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  149. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
  150. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  151. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
  152. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
  153. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
  154. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
  155. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
  156. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  157. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  158. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +58 -31
  159. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  160. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
  161. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
  162. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
  163. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
  164. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
  165. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  166. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
  167. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
  169. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  171. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  173. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  174. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  175. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  176. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  177. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  178. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  179. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  180. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  181. package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  182. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  183. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  184. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  185. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  186. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  187. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
  188. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  189. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
  190. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  191. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
  192. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
  193. package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
  194. package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
  195. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
  196. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  197. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
  198. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  199. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  200. package/package.json +7 -7
@@ -14,6 +14,7 @@
14
14
  * go to `sink.writeRaw` instead of `writeRawStdout`, and the caller decides how
15
15
  * (and whether) to end the process.
16
16
  */
17
+ import type { AgentAbortSource } from "../../core/agent-abort-provenance.ts";
17
18
  import type { AgentSession } from "../../core/agent-session.ts";
18
19
  import type { AgentSessionRuntime } from "../../core/agent-session-runtime.ts";
19
20
  import type { RpcSessionState } from "./rpc-types.ts";
@@ -50,6 +51,8 @@ export interface RpcConnectionHandler {
50
51
  * host polls this after each command and decides how to tear down.
51
52
  */
52
53
  isShutdownRequested(): boolean;
54
+ /** Cancel UI requests that can no longer be answered by this connection. */
55
+ cancelPendingExtensionUiRequests(): void;
53
56
  /** Tear down subscriptions and dispose the runtime. Never calls process.exit. */
54
57
  dispose(): Promise<void>;
55
58
  }
@@ -59,8 +62,12 @@ export interface RpcConnectionHandler {
59
62
  * Shared with `open_session` (session-command-router) so both surfaces answer with the SAME
60
63
  * fields: a second hand-rolled literal silently drifts, which is how `serviceTier`/`fastMode`
61
64
  * would otherwise be missing from an opened session's initial state.
65
+ *
66
+ * `lastAbortSource` is passed in rather than read from the session: `session.currentAbortSource`
67
+ * is cleared once the turn settles, so only the caller that observed `agent_end` still knows
68
+ * who owned the abort.
62
69
  */
63
- export declare function buildRpcSessionState(session: AgentSession): RpcSessionState;
70
+ export declare function buildRpcSessionState(session: AgentSession, lastAbortSource?: AgentAbortSource): RpcSessionState;
64
71
  /**
65
72
  * Create a per-connection RPC handler bound to one runtime host and one sink.
66
73
  *
@@ -1 +1 @@
1
- {"version":3,"file":"connection-handler.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/connection-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,OAAO,KAAK,EAAE,YAAY,EAAqB,MAAM,6BAA6B,CAAC;AACnF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAwC/E,OAAO,KAAK,EAWX,eAAe,EAEf,MAAM,gBAAgB,CAAC;AAGxB,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IACpC,mFAAmF;IACnF,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,0DAA0D;IAC1D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACpC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,sEAAsE;IACtE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C;;;OAGG;IACH,mBAAmB,IAAI,OAAO,CAAC;IAC/B,iFAAiF;IACjF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AA4BD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,eAAe,CAsC3E;AA4BD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACzC,WAAW,EAAE,mBAAmB,EAChC,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,oBAAyB,GAChC,oBAAoB,CA4pCtB"}
1
+ {"version":3,"file":"connection-handler.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/connection-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAqB,MAAM,6BAA6B,CAAC;AACnF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAyC/E,OAAO,KAAK,EAYX,eAAe,EAEf,MAAM,gBAAgB,CAAC;AAGxB,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IACpC,mFAAmF;IACnF,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,0DAA0D;IAC1D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACpC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,sEAAsE;IACtE,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C;;;OAGG;IACH,mBAAmB,IAAI,OAAO,CAAC;IAC/B,4EAA4E;IAC5E,gCAAgC,IAAI,IAAI,CAAC;IACzC,iFAAiF;IACjF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AA4BD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,EAAE,eAAe,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAkD/G;AA4BD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACzC,WAAW,EAAE,mBAAmB,EAChC,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,oBAAyB,GAChC,oBAAoB,CA4uCtB"}
@@ -25,11 +25,12 @@ import { CLAUDE_SDK_OAUTH_PROVIDER_ID } from "../../core/extensions/builtin/clau
25
25
  import { isMcpControlInventoryChanged, MCP_CONTROL_INVENTORY_CHANGED_EVENT, MCP_CONTROL_INVENTORY_REQUEST_EVENT, } from "../../core/extensions/builtin/mcp/control-inventory.js";
26
26
  import { applyFastMode, resolveServiceTierMemoryModel, } from "../../core/extensions/builtin/service-tier.js";
27
27
  import { getSupportedThinkingLevels } from "../../core/thinking-levels.js";
28
+ import { ProjectTrustStore } from "../../core/trust-manager.js";
28
29
  import { theme } from "../interactive/theme/theme.js";
29
30
  import { buildCustomUnsupportedRequest, DEFAULT_CUSTOM_EXTENSION_LABEL, EXTENSION_EVENTS_CAPABILITY, } from "./custom-capability.js";
30
31
  import { createRpcEventOutputBuffer } from "./event-output-buffer.js";
31
32
  import { buildRpcCommandsForSession, createCommandsChangedEvent, rpcCommandListDigest } from "./rpc-command-surface.js";
32
- import { rpcCommandShapeError, rpcMessageLengthError } from "./rpc-input-validation.js";
33
+ import { rpcCommandPayloadError, rpcCommandShapeError, rpcMessageLengthError } from "./rpc-input-validation.js";
33
34
  import { SessionExtensionUiRequests } from "./session-extension-ui-requests.js";
34
35
  function loadedExtensionName(path) {
35
36
  const synthetic = /^<(?:builtin|inline):([^>]+)>$/.exec(path);
@@ -65,11 +66,24 @@ function loadedMcpStatus(server) {
65
66
  * Shared with `open_session` (session-command-router) so both surfaces answer with the SAME
66
67
  * fields: a second hand-rolled literal silently drifts, which is how `serviceTier`/`fastMode`
67
68
  * would otherwise be missing from an opened session's initial state.
69
+ *
70
+ * `lastAbortSource` is passed in rather than read from the session: `session.currentAbortSource`
71
+ * is cleared once the turn settles, so only the caller that observed `agent_end` still knows
72
+ * who owned the abort.
68
73
  */
69
- export function buildRpcSessionState(session) {
74
+ export function buildRpcSessionState(session, lastAbortSource) {
75
+ const cwd = session.sessionManager.getCwd();
76
+ // Trust gates project-source settings (shell prefixes, project resources), so an
77
+ // unverifiable verdict must fail closed: no agentDir means no authoritative store to
78
+ // consult, and the session's own manager only counts when it says trusted outright.
79
+ const projectTrusted = session.agentDir
80
+ ? new ProjectTrustStore(session.agentDir).get(cwd) === true
81
+ : session.settingsManager?.isProjectTrusted?.() === true;
70
82
  return {
71
83
  model: session.model,
72
84
  thinkingLevel: session.thinkingLevel,
85
+ ...(session.thinkingSelection ? { thinkingSelection: session.thinkingSelection } : {}),
86
+ ...(lastAbortSource ? { lastAbortSource } : {}),
73
87
  serviceTier: session.effectiveServiceTier,
74
88
  fastMode: session.isFastModeActive(),
75
89
  isStreaming: session.isStreaming,
@@ -81,8 +95,8 @@ export function buildRpcSessionState(session) {
81
95
  sessionFile: session.sessionFile,
82
96
  sessionId: session.sessionId,
83
97
  sessionName: session.sessionName,
84
- cwd: session.sessionManager.getCwd(),
85
- projectTrusted: session.settingsManager?.isProjectTrusted?.() ?? true,
98
+ cwd,
99
+ projectTrusted,
86
100
  ...(session.sessionFile &&
87
101
  !existsSync(session.sessionFile) &&
88
102
  session.sessionManager
@@ -99,6 +113,9 @@ export function buildRpcSessionState(session) {
99
113
  messageCount: session.messages.length,
100
114
  pendingMessageCount: session.pendingMessageCount,
101
115
  usageTotals: session.sessionManager.getUsageTotals(),
116
+ contextUsage: typeof session.getContextUsage === "function" ? session.getContextUsage() : undefined,
117
+ favoriteModels: session.favoriteModels?.map((entry) => ({ ...entry })) ?? [],
118
+ scopedModels: session.scopedModels?.map((entry) => ({ ...entry })) ?? [],
102
119
  };
103
120
  }
104
121
  function loadedMcpServers(snapshot) {
@@ -131,12 +148,17 @@ function loadedSurfacesFingerprint(session, extensions, mcpServers) {
131
148
  export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
132
149
  const clientCapabilities = options.capabilities;
133
150
  const routingSessionId = options.sessionId;
151
+ // True only while THIS connection's own command drives a replacement; the issuer
152
+ // already learns the new identity from its command response.
153
+ let replacementIssuedHere = false;
134
154
  let session = runtimeHost.session;
135
155
  let unsubscribe;
136
156
  let unsubscribeBackpressure;
137
157
  let unsubscribeLoadedSurfaces;
138
158
  let unsubscribeExtensionEvents;
139
159
  let mcpWireStatus = { servers: [] };
160
+ /** Abort owner of the last settled turn; `session.currentAbortSource` clears on settle. */
161
+ let lastAbortSource;
140
162
  let loadedSurfacesDigest;
141
163
  let rpcCommandsDigest;
142
164
  let suppressLoadedSurfaceEvents = false;
@@ -306,7 +328,6 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
306
328
  // Hidden thinking label not supported in RPC mode - requires TUI message rendering access
307
329
  },
308
330
  setWidget(key, content, options) {
309
- // Only support string arrays in RPC mode - factory functions are ignored
310
331
  if (content === undefined || Array.isArray(content)) {
311
332
  output({
312
333
  type: "extension_ui_request",
@@ -316,8 +337,14 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
316
337
  widgetLines: content,
317
338
  widgetPlacement: options?.placement,
318
339
  });
340
+ return;
319
341
  }
320
- // Component factories are not supported in RPC mode - would need TUI access
342
+ // A component factory closes over live TUI/theme state and cannot cross
343
+ // the RPC boundary. Never drop it silently: an opted-in client can show
344
+ // the same explicit degradation notice used by ctx.ui.custom.
345
+ const request = buildCustomUnsupportedRequest(clientCapabilities, "widget component");
346
+ if (request)
347
+ output(request);
321
348
  },
322
349
  setFooter(_factory) {
323
350
  // Custom footer not supported in RPC mode - requires TUI access
@@ -443,19 +470,49 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
443
470
  }
444
471
  };
445
472
  runtimeHost.setRebindSession(async () => {
446
- await rebindSession();
473
+ // AgentSessionRuntime invokes this callback while completing a replacement.
474
+ // Do not await extension binding here: extensions may wait for session work
475
+ // that the replacement still owns. The command response must acknowledge the
476
+ // committed replacement; derived surfaces refresh out of band afterwards.
477
+ await rebindSession(true);
447
478
  });
448
- const rebindSession = async () => {
479
+ const rebindAfterLocalReplacement = async () => {
480
+ replacementIssuedHere = true;
481
+ try {
482
+ await rebindSession();
483
+ }
484
+ finally {
485
+ replacementIssuedHere = false;
486
+ }
487
+ };
488
+ const rebindSession = async (deferRefresh = false) => {
449
489
  unsubscribeLoadedSurfaces?.();
450
490
  unsubscribeExtensionEvents?.();
491
+ const replacedSession = session !== runtimeHost.session;
451
492
  session = runtimeHost.session;
493
+ if (replacedSession) {
494
+ lastAbortSource = undefined;
495
+ if (routingSessionId !== undefined || !replacementIssuedHere) {
496
+ // A replacement can be driven by ANY attached client, so every connection must be
497
+ // told the live binding moved and given the new authoritative identity. Emitted
498
+ // before the derived-surface refresh so a client cannot act on the old identity in
499
+ // the window the refresh takes.
500
+ outputEvent({
501
+ type: "session_replaced",
502
+ sessionId: session.sessionId,
503
+ sessionFile: session.sessionFile,
504
+ cwd: session.sessionManager.getCwd(),
505
+ sessionName: session.sessionName,
506
+ });
507
+ }
508
+ }
452
509
  unsubscribeExtensionEvents = clientCapabilities?.includes(EXTENSION_EVENTS_CAPABILITY)
453
510
  ? session.extensionRunner.onRpcEvent(({ name, data }) => {
454
511
  outputEvent({ type: "extension_event", name, data });
455
512
  })
456
513
  : undefined;
457
514
  subscribeLoadedSurfaceEvents();
458
- await refreshLoadedSurfacesAfter(() => session.bindExtensions({
515
+ const refresh = refreshLoadedSurfacesAfter(() => session.bindExtensions({
459
516
  uiContext: createExtensionUIContext(),
460
517
  mode: "rpc",
461
518
  commandContextActions: {
@@ -500,22 +557,50 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
500
557
  });
501
558
  },
502
559
  }), true);
503
- unsubscribe?.();
504
- unsubscribeBackpressure?.();
505
- unsubscribe = session.subscribe((event) => {
506
- if (event.type === "skill_invocation") {
507
- outputEvent(event);
508
- return;
509
- }
510
- if (event.type === "command_invocation") {
560
+ const installSessionSubscriptions = () => {
561
+ unsubscribe?.();
562
+ unsubscribeBackpressure?.();
563
+ unsubscribe = session.subscribe((event) => {
564
+ if (event.type === "skill_invocation") {
565
+ outputEvent(event);
566
+ return;
567
+ }
568
+ if (event.type === "command_invocation") {
569
+ outputEvent(event);
570
+ return;
571
+ }
572
+ if (event.type === "thinking_level_changed" || event.type === "model_changed") {
573
+ // Core emits the effective level only; the selection provenance lives beside it
574
+ // on the session and is what distinguishes an explicit choice from a default.
575
+ const thinkingSelection = session.thinkingSelection;
576
+ outputEvent(thinkingSelection === undefined ? event : { ...event, thinkingSelection });
577
+ return;
578
+ }
579
+ if (event.type === "agent_end") {
580
+ // The subscribe-path `agent_end` carries only { type, messages, willRetry };
581
+ // the abort provenance the extension hook receives is stripped from it. Read
582
+ // it from the session, which still reports the owner at this point, and retain
583
+ // it for `get_state` because the getter clears once the turn settles.
584
+ const abortSource = session.currentAbortSource;
585
+ if (abortSource !== undefined)
586
+ lastAbortSource = abortSource;
587
+ outputEvent(abortSource === undefined ? event : { ...event, aborted: true, abortSource });
588
+ return;
589
+ }
511
590
  outputEvent(event);
512
- return;
513
- }
514
- outputEvent(event);
515
- });
516
- unsubscribeBackpressure = session.agent.subscribe(async () => {
517
- await waitForRpcBackpressure();
518
- });
591
+ });
592
+ unsubscribeBackpressure = session.agent.subscribe(async () => {
593
+ await waitForRpcBackpressure();
594
+ });
595
+ };
596
+ if (deferRefresh) {
597
+ void refresh.then(installSessionSubscriptions, (cause) => {
598
+ outputEvent({ type: "rpc_error", error: String(cause) });
599
+ });
600
+ return;
601
+ }
602
+ await refresh;
603
+ installSessionSubscriptions();
519
604
  };
520
605
  /**
521
606
  * Drive an OAuth login for one provider as fire-command-then-subscribe.
@@ -623,6 +708,7 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
623
708
  images: command.images,
624
709
  streamingBehavior: command.streamingBehavior,
625
710
  thinkingLevel: command.thinkingLevel,
711
+ sessionTitlePrompt: command.sessionTitlePrompt,
626
712
  expandPromptTemplates: command.expandPromptTemplates,
627
713
  source: "rpc",
628
714
  promptDisposition: (nextDisposition) => {
@@ -648,13 +734,11 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
648
734
  ? { role: "user", content, timestamp: Date.now() }
649
735
  : { role: "user", content, timestamp: Date.now() };
650
736
  session.sessionManager.appendMessage(message);
651
- session.messages.push(message);
737
+ session.agent.state.messages = session.sessionManager.buildSessionContext().messages;
652
738
  return success(id, "append_user_message");
653
739
  }
654
740
  case "append_session_entry": {
655
- session.sessionManager.appendEntry(command.entry);
656
- if (command.entry.type === "message")
657
- session.messages.push(command.entry.message);
741
+ session.appendSessionEntry(command.entry);
658
742
  return success(id, "append_session_entry");
659
743
  }
660
744
  case "send_custom_message": {
@@ -707,16 +791,15 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
707
791
  case "new_session": {
708
792
  const options = command.parentSession ? { parentSession: command.parentSession } : undefined;
709
793
  const result = await runtimeHost.newSession(options);
710
- if (!result.cancelled) {
711
- await rebindSession();
712
- }
794
+ if (routingSessionId === undefined && !result.cancelled && session !== runtimeHost.session)
795
+ await rebindAfterLocalReplacement();
713
796
  return success(id, "new_session", result);
714
797
  }
715
798
  // =================================================================
716
799
  // State
717
800
  // =================================================================
718
801
  case "get_state":
719
- return success(id, "get_state", buildRpcSessionState(session));
802
+ return success(id, "get_state", buildRpcSessionState(session, lastAbortSource));
720
803
  // =================================================================
721
804
  // Model
722
805
  // =================================================================
@@ -729,8 +812,14 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
729
812
  const systemPromptChange = await session.setModel(model);
730
813
  return success(id, "set_model", { ...model, systemPromptName: systemPromptChange?.systemPromptName });
731
814
  }
815
+ case "set_favorite_models":
816
+ session.setFavoriteModels(command.models);
817
+ return success(id, "set_favorite_models");
818
+ case "set_scoped_models":
819
+ session.setScopedModels(command.models);
820
+ return success(id, "set_scoped_models");
732
821
  case "cycle_model": {
733
- const result = await session.cycleModel();
822
+ const result = await session.cycleModel(command.direction);
734
823
  if (!result) {
735
824
  return success(id, "cycle_model", null);
736
825
  }
@@ -861,7 +950,8 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
861
950
  session.sessionManager.appendLabelChange(command.entryId, command.label);
862
951
  return success(id, "set_label");
863
952
  case "bash": {
864
- outputEvent({ type: "bash_start" });
953
+ if (routingSessionId !== undefined)
954
+ outputEvent({ type: "bash_start" });
865
955
  try {
866
956
  const eventResult = await session.extensionRunner.emitUserBash({
867
957
  type: "user_bash",
@@ -877,6 +967,7 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
877
967
  }
878
968
  const result = await session.executeBash(command.command, undefined, {
879
969
  excludeFromContext: command.excludeFromContext,
970
+ id: command.executionId,
880
971
  // Functions cannot cross JSONL. Host extensions may still provide the
881
972
  // executable operations object; client-supplied data is only a wire-safe hint.
882
973
  operations: eventResult?.operations,
@@ -884,13 +975,17 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
884
975
  return success(id, "bash", result);
885
976
  }
886
977
  finally {
887
- outputEvent({ type: "bash_end" });
978
+ if (routingSessionId !== undefined)
979
+ outputEvent({ type: "bash_end" });
888
980
  }
889
981
  }
890
982
  case "abort_bash": {
891
983
  session.abortBash();
892
984
  return success(id, "abort_bash");
893
985
  }
986
+ case "cleanup_bash_output":
987
+ await session.cleanupBashOutput(command.path);
988
+ return success(id, "cleanup_bash_output");
894
989
  // =================================================================
895
990
  // Session
896
991
  // =================================================================
@@ -899,7 +994,7 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
899
994
  return success(id, "get_session_stats", stats);
900
995
  }
901
996
  case "export_html": {
902
- const path = await session.exportToHtml(command.outputPath);
997
+ const path = await session.exportToHtml(command.outputPath, { themeName: command.themeName });
903
998
  return success(id, "export_html", { path });
904
999
  }
905
1000
  case "export_jsonl": {
@@ -907,16 +1002,14 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
907
1002
  }
908
1003
  case "switch_session": {
909
1004
  const result = await runtimeHost.switchSession(command.sessionPath, { cwdOverride: command.cwdOverride });
910
- if (!result.cancelled) {
911
- await rebindSession();
912
- }
1005
+ if (routingSessionId === undefined && !result.cancelled && session !== runtimeHost.session)
1006
+ await rebindAfterLocalReplacement();
913
1007
  return success(id, "switch_session", result);
914
1008
  }
915
1009
  case "fork": {
916
1010
  const result = await runtimeHost.fork(command.entryId, { position: command.position });
917
- if (!result.cancelled) {
918
- await rebindSession();
919
- }
1011
+ if (routingSessionId === undefined && !result.cancelled && session !== runtimeHost.session)
1012
+ await rebindAfterLocalReplacement();
920
1013
  return success(id, "fork", { text: result.selectedText, cancelled: result.cancelled });
921
1014
  }
922
1015
  case "clone": {
@@ -925,9 +1018,8 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
925
1018
  return error(id, "clone", "Cannot clone session: no current entry selected");
926
1019
  }
927
1020
  const result = await runtimeHost.fork(leafId, { position: "at" });
928
- if (!result.cancelled) {
929
- await rebindSession();
930
- }
1021
+ if (routingSessionId === undefined && !result.cancelled && session !== runtimeHost.session)
1022
+ await rebindAfterLocalReplacement();
931
1023
  return success(id, "clone", { cancelled: result.cancelled });
932
1024
  }
933
1025
  case "get_fork_messages": {
@@ -956,8 +1048,8 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
956
1048
  }
957
1049
  case "import_jsonl": {
958
1050
  const result = await runtimeHost.importFromJsonl(command.inputPath, command.cwdOverride);
959
- if (!result.cancelled)
960
- await rebindSession();
1051
+ if (routingSessionId === undefined && !result.cancelled && session !== runtimeHost.session)
1052
+ await rebindAfterLocalReplacement();
961
1053
  return success(id, "import_jsonl", result);
962
1054
  }
963
1055
  case "set_session_name": {
@@ -1079,9 +1171,10 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
1079
1171
  return;
1080
1172
  }
1081
1173
  const command = parsed;
1174
+ const payloadError = rpcCommandPayloadError(command);
1082
1175
  const messageLengthError = rpcMessageLengthError(command);
1083
- if (messageLengthError) {
1084
- output(error(command.id, command.type, messageLengthError));
1176
+ if (payloadError || messageLengthError) {
1177
+ output(error(command.id, command.type, payloadError ?? messageLengthError));
1085
1178
  await waitForRpcBackpressure();
1086
1179
  return;
1087
1180
  }
@@ -1125,6 +1218,9 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
1125
1218
  isShutdownRequested() {
1126
1219
  return shutdownRequested;
1127
1220
  },
1221
+ cancelPendingExtensionUiRequests() {
1222
+ pendingExtensionRequests.cancelAll();
1223
+ },
1128
1224
  async dispose() {
1129
1225
  await ready;
1130
1226
  await dispose();