@code-yeongyu/senpi 2026.11.10 → 2026.11.11

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 (320) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/dist/bundle/{SKILL-B4R7BVOT.md → SKILL-R7FEIQKG.md} +1 -1
  3. package/dist/bundle/chunks/{SKILL-AZ6ONO52.js → SKILL-5O2UKORG.js} +1 -1
  4. package/dist/bundle/chunks/{SKILL-B4R7BVOT.md → SKILL-R7FEIQKG.md} +1 -1
  5. package/dist/bundle/chunks/{args-77GYOPEO.js → args-HFYB6URH.js} +1 -1
  6. package/dist/bundle/chunks/{azure-openai-responses-YFZ734T3.js → azure-openai-responses-MNN2TINI.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-ZUQCYIRY.js → chunk-2ODGHBUD.js} +1 -1
  8. package/dist/bundle/chunks/chunk-3IYEGHBI.js +2 -0
  9. package/dist/bundle/chunks/{chunk-I5ASHSLZ.js → chunk-4JMGWVRH.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-YKYWL2NO.js → chunk-5F2XC6UC.js} +3 -3
  11. package/dist/bundle/chunks/{chunk-MSDBBOB2.js → chunk-5H43O4MA.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-R3OP7IA4.js → chunk-5PIZWSNG.js} +4 -4
  13. package/dist/bundle/chunks/{chunk-33MZJTQ3.js → chunk-B4WD5XHP.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-Q63YLZMN.js → chunk-BPEA5SWW.js} +2 -2
  15. package/dist/bundle/chunks/{chunk-MOVJQVMC.js → chunk-BUDJLVMF.js} +3 -3
  16. package/dist/bundle/chunks/{chunk-V5FSENW6.js → chunk-COKX65TE.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-2EJ4BGBM.js → chunk-DHORIIVQ.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-NH7SYIGN.js → chunk-DLPV4B3R.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-RCHIMH7N.js → chunk-DQY26AX3.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-Y7Z7XPUF.js → chunk-DZQA5LVN.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-2CNSCMYC.js → chunk-E5O4SP5I.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-3Q5ZENSD.js → chunk-E6ZBTSC6.js} +1 -1
  23. package/dist/bundle/chunks/{chunk-GVPZPVVO.js → chunk-IBDAV2AL.js} +3 -3
  24. package/dist/bundle/chunks/chunk-IREUIMFE.js +11 -0
  25. package/dist/bundle/chunks/{chunk-KU7OCWJF.js → chunk-J4ADZ2OK.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-T2SMQPRE.js → chunk-K3GAGIPX.js} +1 -1
  27. package/dist/bundle/chunks/{chunk-BELNOPAI.js → chunk-L3KVP7VJ.js} +3 -3
  28. package/dist/bundle/chunks/{chunk-IYSJFQUU.js → chunk-L6DF5G6K.js} +1 -1
  29. package/dist/bundle/chunks/{chunk-7MN7YNO5.js → chunk-L7DGTA3L.js} +1 -1
  30. package/dist/bundle/chunks/chunk-LJFQYCFH.js +2 -0
  31. package/dist/bundle/chunks/{chunk-J2NGJKXL.js → chunk-LXINMDMX.js} +1 -1
  32. package/dist/bundle/chunks/{chunk-H33A33M4.js → chunk-M2VJVJMK.js} +1 -1
  33. package/dist/bundle/chunks/{chunk-2U6HXWXS.js → chunk-NNQ34ZDW.js} +1 -1
  34. package/dist/bundle/chunks/chunk-NQKCJQHB.js +2 -0
  35. package/dist/bundle/chunks/{chunk-EJB5E2JK.js → chunk-NRZAJUUO.js} +2 -2
  36. package/dist/bundle/chunks/{chunk-R2Z7CX77.js → chunk-OSNR3IRD.js} +1 -1
  37. package/dist/bundle/chunks/{chunk-O3FG6HML.js → chunk-PJI5GT7U.js} +1 -1
  38. package/dist/bundle/chunks/{chunk-FDNM2DAE.js → chunk-PUZDKFRT.js} +164 -164
  39. package/dist/bundle/chunks/{chunk-BPNSOZ2C.js → chunk-QUVSIW2R.js} +1 -1
  40. package/dist/bundle/chunks/{chunk-WXYMAUFX.js → chunk-QX7WD3IM.js} +1 -1
  41. package/dist/bundle/chunks/{chunk-Y4ONXQMR.js → chunk-RGHVPUUX.js} +1 -1
  42. package/dist/bundle/chunks/{chunk-RYPM5BZQ.js → chunk-S7NAJQB3.js} +1 -1
  43. package/dist/bundle/chunks/{chunk-2BH54YQR.js → chunk-SANWO37B.js} +1 -1
  44. package/dist/bundle/chunks/{chunk-YQZNARTX.js → chunk-SS5VTBAV.js} +1 -1
  45. package/dist/bundle/chunks/{chunk-LT353NSF.js → chunk-T4CUD7RF.js} +1 -1
  46. package/dist/bundle/chunks/{chunk-J7FW7MED.js → chunk-TCZORL3V.js} +1 -1
  47. package/dist/bundle/chunks/{chunk-KU72IKV5.js → chunk-VGSWOLIY.js} +1 -1
  48. package/dist/bundle/chunks/{chunk-DBQBJTMF.js → chunk-VUY4BZDI.js} +1 -1
  49. package/dist/bundle/chunks/chunk-XI6THO4F.js +2 -0
  50. package/dist/bundle/chunks/{chunk-FAFIGBUD.js → chunk-XUDKT4UH.js} +3 -3
  51. package/dist/bundle/chunks/{cli-main-BX5AFWJQ.js → cli-main-7II7GCIR.js} +1 -1
  52. package/dist/bundle/chunks/{credential-accounts-LVVPKFEY.js → credential-accounts-N3B5YJS7.js} +1 -1
  53. package/dist/bundle/chunks/{dispatch-EP35KKJ3.js → dispatch-YUYZQXRP.js} +1 -1
  54. package/dist/bundle/chunks/github-copilot.js +1 -1
  55. package/dist/bundle/chunks/{hand-entry-F6PWKNPJ.js → hand-entry-ONOLRMVB.js} +3 -3
  56. package/dist/bundle/chunks/{headless-host-HKGNJGYT.js → headless-host-53UMJMM6.js} +1 -1
  57. package/dist/bundle/chunks/{health-write-FBF74QJ7.js → health-write-JLSF5EVV.js} +1 -1
  58. package/dist/bundle/chunks/{help-fast-path-5SCPY3OH.js → help-fast-path-552QLMWT.js} +1 -1
  59. package/dist/bundle/chunks/{host-command-47QQKJEY.js → host-command-KWMJLPBS.js} +1 -1
  60. package/dist/bundle/chunks/{host-lifecycle-VSAH4M3R.js → host-lifecycle-4WQDCN3Y.js} +1 -1
  61. package/dist/bundle/chunks/host-lifecycle.js +5 -5
  62. package/dist/bundle/chunks/{host-session-command-SL4AN5WD.js → host-session-command-ZDYEKCUB.js} +1 -1
  63. package/dist/bundle/chunks/{host-socket-request-DMYXC56U.js → host-socket-request-QN4Q2P5E.js} +1 -1
  64. package/dist/bundle/chunks/{interactive-mode-JDMIHJMQ.js → interactive-mode-EPE4M32G.js} +1 -1
  65. package/dist/bundle/chunks/{login-health-EVXVLHB2.js → login-health-TOSQT5RG.js} +1 -1
  66. package/dist/bundle/chunks/{main-4RITUAQF.js → main-OM7BKVZG.js} +1 -1
  67. package/dist/bundle/chunks/{multi-session-host-SKDD4OWM.js → multi-session-host-DB6XMY53.js} +11 -8
  68. package/dist/bundle/chunks/openai-codex-responses-VRGOAFEG.js +10 -0
  69. package/dist/bundle/chunks/{openai-responses-NIVBWE2H.js → openai-responses-I7PXRWCM.js} +2 -2
  70. package/dist/bundle/chunks/{package-manager-cli-B5VA4KFO.js → package-manager-cli-GLU5Q2KV.js} +1 -1
  71. package/dist/bundle/chunks/{role-HQUN4XXK.js → role-AEOLJRTP.js} +1 -1
  72. package/dist/bundle/chunks/{rotation-stream-F3BEMXBE.js → rotation-stream-E3FJFHXJ.js} +1 -1
  73. package/dist/bundle/chunks/rpc-mode-SDJVBT36.js +2 -0
  74. package/dist/bundle/chunks/{rpc-monitor-source-C6VQ3BC4.js → rpc-monitor-source-PGTDSGWP.js} +1 -1
  75. package/dist/bundle/chunks/{run-attached-tui-R3RB5P7G.js → run-attached-tui-4AENRP7P.js} +1 -1
  76. package/dist/bundle/chunks/{schedule-command-FPEQCX33.js → schedule-command-5SQD7M5N.js} +1 -1
  77. package/dist/bundle/chunks/session-control-endpoint-R25A2QGS.js +3 -0
  78. package/dist/bundle/chunks/{session-picker-2FZSNFS7.js → session-picker-GZM5CVQL.js} +1 -1
  79. package/dist/bundle/chunks/{session-slot-CZGQQCOC.js → session-slot-RH7JLO3F.js} +1 -1
  80. package/dist/bundle/chunks/session-worker.js +335 -332
  81. package/dist/bundle/chunks/{state-store-S7QAESIF.js → state-store-IECVTYXC.js} +1 -1
  82. package/dist/bundle/cli.js +2 -2
  83. package/dist/bundle/host-supervisor.js +5 -5
  84. package/dist/bundle/index.js +1 -1
  85. package/dist/bundle/rpc-entry.js +1 -1
  86. package/dist/bundle/runtime-manifest.json +1 -1
  87. package/dist/cli.js +9 -3
  88. package/dist/core/agent-session-runtime.d.ts +2 -0
  89. package/dist/core/agent-session-runtime.js +5 -0
  90. package/dist/core/agent-session.d.ts +15 -3
  91. package/dist/core/agent-session.js +91 -18
  92. package/dist/core/assistant-error-redaction.d.ts +9 -0
  93. package/dist/core/assistant-error-redaction.js +27 -0
  94. package/dist/core/config-reference-redaction.js +90 -10
  95. package/dist/core/context-files.d.ts +10 -0
  96. package/dist/core/context-files.js +58 -12
  97. package/dist/core/credential-accounts.d.ts +2 -0
  98. package/dist/core/credential-accounts.js +10 -1
  99. package/dist/core/dynamic-prompt/intent-gate.js +2 -2
  100. package/dist/core/dynamic-prompt/style.js +2 -2
  101. package/dist/core/dynamic-prompt/verification.js +2 -2
  102. package/dist/core/egress/wire.d.ts +13 -0
  103. package/dist/core/extensions/builtin/anthropic-subscription/accounts.d.ts +3 -0
  104. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +3 -2
  105. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-account-test.js +3 -1
  106. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.d.ts +3 -1
  107. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.js +47 -16
  108. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.d.ts +1 -1
  109. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.js +4 -2
  110. package/dist/core/extensions/builtin/anthropic-subscription/claude-settings-env.d.ts +20 -0
  111. package/dist/core/extensions/builtin/anthropic-subscription/claude-settings-env.js +39 -0
  112. package/dist/core/extensions/builtin/anthropic-subscription/context-window.d.ts +17 -0
  113. package/dist/core/extensions/builtin/anthropic-subscription/context-window.js +23 -0
  114. package/dist/core/extensions/builtin/anthropic-subscription/errors.d.ts +1 -0
  115. package/dist/core/extensions/builtin/anthropic-subscription/errors.js +14 -5
  116. package/dist/core/extensions/builtin/anthropic-subscription/oauth-login.js +3 -3
  117. package/dist/core/extensions/builtin/anthropic-subscription/prompt-cache-ttl.d.ts +10 -4
  118. package/dist/core/extensions/builtin/anthropic-subscription/prompt-cache-ttl.js +14 -5
  119. package/dist/core/extensions/builtin/anthropic-subscription/query-environment.d.ts +3 -2
  120. package/dist/core/extensions/builtin/anthropic-subscription/query-environment.js +5 -4
  121. package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.lazy.d.ts +2 -1
  122. package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.lazy.js +1 -0
  123. package/dist/core/extensions/builtin/anthropic-subscription/stream.js +11 -1
  124. package/dist/core/extensions/builtin/config-reload/change-groups.js +6 -2
  125. package/dist/core/extensions/builtin/goal/continuation.d.ts +6 -0
  126. package/dist/core/extensions/builtin/goal/continuation.js +3 -2
  127. package/dist/core/extensions/builtin/goal/index.js +14 -26
  128. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +8 -6
  129. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +77 -39
  130. package/dist/core/extensions/builtin/goal/monitor-continuation.js +66 -7
  131. package/dist/core/extensions/builtin/goal/persistence.js +20 -0
  132. package/dist/core/extensions/builtin/goal/provider-retry.d.ts +9 -0
  133. package/dist/core/extensions/builtin/goal/provider-retry.js +16 -0
  134. package/dist/core/extensions/builtin/goal/reload-reengagement.d.ts +3 -2
  135. package/dist/core/extensions/builtin/goal/reload-reengagement.js +4 -4
  136. package/dist/core/extensions/builtin/goal/session-start-continuation.d.ts +28 -0
  137. package/dist/core/extensions/builtin/goal/session-start-continuation.js +75 -0
  138. package/dist/core/extensions/builtin/goal/store.d.ts +3 -2
  139. package/dist/core/extensions/builtin/goal/store.js +9 -2
  140. package/dist/core/extensions/builtin/goal/types.d.ts +9 -0
  141. package/dist/core/extensions/builtin/handoff-hint/index.js +15 -11
  142. package/dist/core/extensions/builtin/handoff-hint/replacement-relay.d.ts +16 -0
  143. package/dist/core/extensions/builtin/handoff-hint/replacement-relay.js +17 -0
  144. package/dist/core/extensions/builtin/html-render/tool.js +13 -3
  145. package/dist/core/extensions/builtin/imagegen/skill/SKILL.md +1 -1
  146. package/dist/core/extensions/builtin/imagegen/tool.js +4 -14
  147. package/dist/core/extensions/builtin/mcp/auth/catalog-identity.js +32 -8
  148. package/dist/core/extensions/builtin/mcp/auth/commands-auth.js +2 -1
  149. package/dist/core/extensions/builtin/mcp/auth/context.js +3 -0
  150. package/dist/core/extensions/builtin/mcp/auth/oauth-grant.d.ts +43 -0
  151. package/dist/core/extensions/builtin/mcp/auth/oauth-grant.js +196 -0
  152. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.d.ts +7 -1
  153. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +13 -0
  154. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +6 -17
  155. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +67 -88
  156. package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.d.ts +1 -1
  157. package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +25 -4
  158. package/dist/core/extensions/builtin/mcp/auth/oauth.js +10 -7
  159. package/dist/core/extensions/builtin/mcp/auth/registered-redirect.d.ts +19 -0
  160. package/dist/core/extensions/builtin/mcp/auth/registered-redirect.js +55 -0
  161. package/dist/core/extensions/builtin/mcp/auth/token-store.d.ts +23 -1
  162. package/dist/core/extensions/builtin/mcp/auth/token-store.js +3 -0
  163. package/dist/core/extensions/builtin/mcp/catalog-cache.js +7 -6
  164. package/dist/core/extensions/builtin/mcp/catalog.js +3 -2
  165. package/dist/core/extensions/builtin/mcp/invocation.js +6 -0
  166. package/dist/core/extensions/builtin/mcp/service-register.js +16 -2
  167. package/dist/core/extensions/builtin/mcp/service-tools-changed.js +61 -54
  168. package/dist/core/extensions/builtin/mcp/startup-race.d.ts +2 -0
  169. package/dist/core/extensions/builtin/mcp/startup-race.js +25 -7
  170. package/dist/core/extensions/builtin/permission-system/parsers.js +1 -0
  171. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +3 -3
  172. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-2.js +9 -8
  173. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +3 -3
  174. package/dist/core/extensions/builtin/prompt-preset/claude-haiku-5-5.js +3 -3
  175. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5-5.js +3 -3
  176. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +4 -4
  177. package/dist/core/extensions/builtin/prompt-preset/claude-sonnet-5-5.js +3 -3
  178. package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.js +3 -3
  179. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +3 -3
  180. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +2 -2
  181. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +3 -3
  182. package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js +1 -1
  183. package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +4 -4
  184. package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +5 -5
  185. package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.js +4 -4
  186. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +7 -6
  187. package/dist/core/extensions/loader.js +24 -4
  188. package/dist/core/extensions/runner.d.ts +9 -1
  189. package/dist/core/extensions/runner.js +9 -4
  190. package/dist/core/extensions/types.d.ts +19 -3
  191. package/dist/core/hand/hook-advertise.d.ts +41 -0
  192. package/dist/core/hand/hook-advertise.js +98 -0
  193. package/dist/core/hand/hook-contract.d.ts +66 -0
  194. package/dist/core/hand/hook-contract.js +138 -0
  195. package/dist/core/open-timing.d.ts +2 -0
  196. package/dist/core/open-timing.js +7 -1
  197. package/dist/core/resource-loader.js +21 -3
  198. package/dist/core/retry-fallback/controller-types.d.ts +2 -0
  199. package/dist/core/retry-fallback/controller.js +9 -2
  200. package/dist/core/retry-fallback/cooldown.d.ts +7 -0
  201. package/dist/core/retry-fallback/cooldown.js +13 -1
  202. package/dist/core/retry-fallback/credential-recovery.d.ts +7 -0
  203. package/dist/core/retry-fallback/credential-recovery.js +24 -0
  204. package/dist/core/retry-fallback/log.d.ts +7 -1
  205. package/dist/core/retry-fallback/log.js +82 -5
  206. package/dist/core/retry-fallback/model-config-error.js +2 -1
  207. package/dist/core/retry-fallback/settings.d.ts +8 -5
  208. package/dist/core/retry-fallback/settings.js +9 -6
  209. package/dist/core/session-manager.d.ts +2 -0
  210. package/dist/core/session-manager.js +15 -1
  211. package/dist/core/settings-manager.d.ts +6 -0
  212. package/dist/core/settings-manager.js +8 -0
  213. package/dist/core/turn-origin.d.ts +18 -10
  214. package/dist/core/turn-origin.js +34 -30
  215. package/dist/core/ultrafast-lanes.d.ts +1 -0
  216. package/dist/core/ultrafast-lanes.js +4 -2
  217. package/dist/modes/interactive/components/footer.d.ts +23 -5
  218. package/dist/modes/interactive/components/footer.js +45 -16
  219. package/dist/modes/interactive/interactive-mode.js +9 -1
  220. package/dist/modes/interactive/session-control-rpc-commands.d.ts +1 -1
  221. package/dist/modes/interactive/session-control-rpc-commands.js +5 -0
  222. package/dist/modes/rpc/admit-external-message-command.d.ts +2 -0
  223. package/dist/modes/rpc/admit-external-message-command.js +5 -1
  224. package/dist/modes/rpc/compact-admission-validation.js +4 -1
  225. package/dist/modes/rpc/connection-authority.d.ts +2 -1
  226. package/dist/modes/rpc/connection-authority.js +11 -9
  227. package/dist/modes/rpc/connection-handler.d.ts +2 -0
  228. package/dist/modes/rpc/connection-handler.js +33 -7
  229. package/dist/modes/rpc/connection-roles.d.ts +7 -0
  230. package/dist/modes/rpc/connection-roles.js +15 -0
  231. package/dist/modes/rpc/custom-capability.d.ts +6 -0
  232. package/dist/modes/rpc/custom-capability.js +6 -0
  233. package/dist/modes/rpc/delegated-drive-origin.d.ts +6 -6
  234. package/dist/modes/rpc/delegated-drive-origin.js +7 -8
  235. package/dist/modes/rpc/delegation-declaration.d.ts +2 -0
  236. package/dist/modes/rpc/delegation-declaration.js +7 -3
  237. package/dist/modes/rpc/effect-commands.d.ts +33 -0
  238. package/dist/modes/rpc/host-capabilities.d.ts +1 -0
  239. package/dist/modes/rpc/host-capabilities.js +7 -1
  240. package/dist/modes/rpc/host-gc-evidence.d.ts +1 -1
  241. package/dist/modes/rpc/host-gc-evidence.js +9 -3
  242. package/dist/modes/rpc/host-gc.js +4 -1
  243. package/dist/modes/rpc/host-observe-request.d.ts +2 -0
  244. package/dist/modes/rpc/host-observe-request.js +6 -1
  245. package/dist/modes/rpc/host-park-ledger.d.ts +19 -0
  246. package/dist/modes/rpc/host-park-ledger.js +110 -0
  247. package/dist/modes/rpc/lifecycle-delivery.js +4 -1
  248. package/dist/modes/rpc/loopless-tool-handlers.d.ts +6 -0
  249. package/dist/modes/rpc/loopless-tool-handlers.js +14 -1
  250. package/dist/modes/rpc/media-placeholders.js +1 -0
  251. package/dist/modes/rpc/method-classes-schema-host.d.ts +42 -1
  252. package/dist/modes/rpc/method-classes-schema-host.js +24 -21
  253. package/dist/modes/rpc/method-classes-schema-session.d.ts +20 -0
  254. package/dist/modes/rpc/method-classes-schema-session.js +6 -0
  255. package/dist/modes/rpc/method-classes-schema.d.ts +62 -1
  256. package/dist/modes/rpc/method-classes-schema.js +5 -2
  257. package/dist/modes/rpc/method-classes.d.ts +4 -0
  258. package/dist/modes/rpc/method-classes.js +6 -0
  259. package/dist/modes/rpc/multi-session-host.js +1 -1
  260. package/dist/modes/rpc/project-file-commands.d.ts +32 -0
  261. package/dist/modes/rpc/project-file-commands.js +195 -0
  262. package/dist/modes/rpc/project-file-path.d.ts +34 -0
  263. package/dist/modes/rpc/project-file-path.js +93 -0
  264. package/dist/modes/rpc/rpc-error-types.d.ts +9 -1
  265. package/dist/modes/rpc/rpc-error-types.js +8 -0
  266. package/dist/modes/rpc/rpc-host-command-types.d.ts +14 -1
  267. package/dist/modes/rpc/rpc-host-response-types.d.ts +21 -0
  268. package/dist/modes/rpc/rpc-mutation-response-types.d.ts +7 -0
  269. package/dist/modes/rpc/rpc-read-response-types.d.ts +7 -0
  270. package/dist/modes/rpc/rpc-session-command-types.d.ts +10 -0
  271. package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -0
  272. package/dist/modes/rpc/session-attachment-gate.js +6 -0
  273. package/dist/modes/rpc/session-binding.d.ts +1 -0
  274. package/dist/modes/rpc/session-binding.js +6 -1
  275. package/dist/modes/rpc/session-command-activity.js +1 -0
  276. package/dist/modes/rpc/session-connection-context.d.ts +2 -0
  277. package/dist/modes/rpc/session-connection-context.js +4 -0
  278. package/dist/modes/rpc/session-registry-attach.d.ts +2 -0
  279. package/dist/modes/rpc/session-registry-attach.js +15 -11
  280. package/dist/modes/rpc/session-registry-claims.js +2 -0
  281. package/dist/modes/rpc/session-registry-open.js +13 -7
  282. package/dist/modes/rpc/session-registry-opening.js +2 -1
  283. package/dist/modes/rpc/session-registry-types.d.ts +4 -0
  284. package/dist/modes/rpc/session-registry.d.ts +3 -0
  285. package/dist/modes/rpc/session-registry.js +27 -3
  286. package/dist/modes/rpc/session-router-close.js +1 -0
  287. package/dist/modes/rpc/session-router-context.d.ts +4 -2
  288. package/dist/modes/rpc/session-router-context.js +3 -0
  289. package/dist/modes/rpc/session-router-host-reads.js +1 -0
  290. package/dist/modes/rpc/session-router-occupancy.js +10 -1
  291. package/dist/modes/rpc/session-router-open-result.d.ts +12 -0
  292. package/dist/modes/rpc/session-router-open-result.js +63 -0
  293. package/dist/modes/rpc/session-router-open.d.ts +3 -0
  294. package/dist/modes/rpc/session-router-open.js +15 -63
  295. package/dist/modes/rpc/session-router-park.d.ts +15 -0
  296. package/dist/modes/rpc/session-router-park.js +176 -0
  297. package/dist/modes/rpc/session-router-routing.d.ts +1 -1
  298. package/dist/modes/rpc/session-router-routing.js +7 -0
  299. package/dist/modes/rpc/session-status-source.js +2 -2
  300. package/dist/private-fd-startup-fillers.d.ts +2 -2
  301. package/dist/private-fd-startup-fillers.js +34 -4
  302. package/dist/senpi +9 -3
  303. package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
  304. package/docs/engine-socket/fixtures/rpc-inbound-samples.json +44 -2
  305. package/docs/extensions.md +11 -1
  306. package/docs/rpc.md +106 -2
  307. package/docs/settings.md +5 -3
  308. package/docs/skills.md +1 -1
  309. package/docs/usage.md +1 -1
  310. package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
  311. package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
  312. package/package.json +6 -6
  313. package/dist/bundle/chunks/chunk-436DNN4Z.js +0 -2
  314. package/dist/bundle/chunks/chunk-DLAY7CIU.js +0 -2
  315. package/dist/bundle/chunks/chunk-SJI6UB23.js +0 -2
  316. package/dist/bundle/chunks/chunk-XH2MWTXA.js +0 -11
  317. package/dist/bundle/chunks/chunk-YK3ZZCHI.js +0 -2
  318. package/dist/bundle/chunks/openai-codex-responses-ABB2J43U.js +0 -10
  319. package/dist/bundle/chunks/rpc-mode-ZDUGW2IM.js +0 -2
  320. package/dist/bundle/chunks/session-control-endpoint-MEDV66PS.js +0 -3
@@ -6,5 +6,11 @@ export interface FallbackLogger {
6
6
  export interface FallbackLoggerOptions {
7
7
  maxBytes?: number;
8
8
  }
9
- export declare function createFallbackLogger(agentDir: string, options?: FallbackLoggerOptions): FallbackLogger;
9
+ export interface SettledFallbackLogger extends FallbackLogger {
10
+ /** Resolves once the one-time removal of earlier log generations has finished and every line logged before it was written. */
11
+ settled(): Promise<void>;
12
+ }
13
+ /** Resolves once every fallback logger created so far has finished its first-start removal and flushed its queue. */
14
+ export declare function settledFallbackLoggers(): Promise<void>;
15
+ export declare function createFallbackLogger(agentDir: string, options?: FallbackLoggerOptions): SettledFallbackLogger;
10
16
  //# sourceMappingURL=log.d.ts.map
@@ -1,19 +1,81 @@
1
1
  import { closeSync, fchmodSync, mkdirSync, openSync, writeSync } from "node:fs";
2
+ import { access, mkdir, rm, writeFile } from "node:fs/promises";
2
3
  import { dirname, join } from "node:path";
3
4
  import { rotateLogIfNeeded } from "../log-file-rotation.js";
4
- import { redactSecrets } from "../sensitive-output.js";
5
+ import { redactSecrets, redactSensitiveTokenValues } from "../sensitive-output.js";
5
6
  const DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
6
7
  const MAX_STRING_LENGTH = 200;
8
+ const MAX_QUEUED_LINES = 256;
7
9
  const BLOCKED_KEY = /^(?:__proto__|constructor|prototype|headers?|env(?:ironment)?|authorization|credential(?:s)?|password|secret|token|api_?key|client_?secret)$/i;
8
10
  const ALLOWED_DATA_KEY = /^(?:selector|candidate|currentSelector|originalSelector|from|to|model|provider|chainKey|reason|skipReason|classification|thinkingLevel|durationMs|retryAfterMs|error|errorMessage|warning|trigger)$/;
9
11
  const SENSITIVE_TEXT = /((?:authorization\s*[:=]\s*(?:bearer|basic)\s+)|(?:bearer\s+)|(?:[?&](?:api[_-]?key|token|secret|password|auth(?:orization)?)=))[^\s&,"'}\]]+/gi;
12
+ const unsettledLoggers = new Set();
13
+ /** Resolves once every fallback logger created so far has finished its first-start removal and flushed its queue. */
14
+ export async function settledFallbackLoggers() {
15
+ await Promise.all([...unsettledLoggers]);
16
+ }
17
+ function errorCode(failure) {
18
+ if (typeof failure === "object" && failure !== null && "code" in failure && typeof failure.code === "string") {
19
+ return failure.code;
20
+ }
21
+ return "unknown";
22
+ }
10
23
  export function createFallbackLogger(agentDir, options = {}) {
11
24
  const filePath = join(agentDir, "logs", "fallback.log");
12
25
  const maxBytes = validMaxBytes(options.maxBytes);
13
26
  let reportedWriteFailure = false;
27
+ // Lines logged while the earlier generations are still being removed wait here, so the removal can never delete them.
28
+ // They are redacted before they enter the queue and the queue is bounded: the newest lines win, the drops are counted.
29
+ let queued = [];
30
+ let dropped = 0;
31
+ function write(line) {
32
+ try {
33
+ writeLine(filePath, line, maxBytes);
34
+ }
35
+ catch (error) {
36
+ if (!reportedWriteFailure) {
37
+ reportedWriteFailure = true;
38
+ console.error("Unable to write retry fallback debug log", error);
39
+ }
40
+ }
41
+ }
42
+ const settled = retireUnsharedRegistryGenerations(filePath)
43
+ .then(() => undefined)
44
+ .catch((error) => {
45
+ reportedWriteFailure = true;
46
+ console.error("Unable to remove earlier retry fallback debug log generations", error);
47
+ return error;
48
+ })
49
+ .then((failure) => {
50
+ const lines = queued ?? [];
51
+ queued = undefined;
52
+ for (const line of lines)
53
+ write(line);
54
+ if (dropped > 0) {
55
+ write(formatLine("warn", "queued_lines_dropped", {
56
+ reason: `${dropped} lines dropped while earlier log generations were removed`,
57
+ }));
58
+ }
59
+ if (failure !== undefined) {
60
+ // The code only: the error text names the agent dir's absolute path.
61
+ write(formatLine("warn", "retire_generations_failed", {
62
+ errorMessage: `removal failed: ${errorCode(failure)}`,
63
+ }));
64
+ }
65
+ });
66
+ unsettledLoggers.add(settled);
67
+ void settled.then(() => unsettledLoggers.delete(settled));
14
68
  function log(level, event, data) {
15
69
  try {
16
70
  const line = formatLine(level, event, data);
71
+ if (queued !== undefined) {
72
+ queued.push(redactSensitiveTokenValues(line, "[redacted]"));
73
+ if (queued.length > MAX_QUEUED_LINES) {
74
+ queued.shift();
75
+ dropped += 1;
76
+ }
77
+ return;
78
+ }
17
79
  writeLine(filePath, line, maxBytes);
18
80
  }
19
81
  catch (error) {
@@ -27,8 +89,23 @@ export function createFallbackLogger(agentDir, options = {}) {
27
89
  debug: (event, data) => log("debug", event, data),
28
90
  info: (event, data) => log("info", event, data),
29
91
  warn: (event, data) => log("warn", event, data),
92
+ settled: () => settled,
30
93
  };
31
94
  }
95
+ /**
96
+ * Generations written before every engine copy in a process shared one resolved-value registry (omo#12965) are
97
+ * removed once per agent dir, the first time an engine with the shared registry starts on it; the marker makes every
98
+ * later start leave the logs alone.
99
+ */
100
+ async function retireUnsharedRegistryGenerations(filePath) {
101
+ const marker = join(dirname(filePath), ".fallback-log-v2");
102
+ if (await access(marker).then(() => true, () => false))
103
+ return;
104
+ await rm(`${filePath}.1`, { force: true });
105
+ await rm(filePath, { force: true });
106
+ await mkdir(dirname(filePath), { recursive: true, mode: 0o700 });
107
+ await writeFile(marker, "", { mode: 0o600 });
108
+ }
32
109
  function validMaxBytes(value) {
33
110
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : DEFAULT_MAX_BYTES;
34
111
  }
@@ -51,7 +128,7 @@ function formatLine(level, event, data) {
51
128
  }
52
129
  function writeLine(filePath, line, maxBytes) {
53
130
  mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
54
- const text = `${line}\n`;
131
+ const text = `${redactSensitiveTokenValues(line, "[redacted]")}\n`;
55
132
  rotateLogIfNeeded(filePath, Buffer.byteLength(text), maxBytes);
56
133
  const descriptor = openSync(filePath, "a", 0o600);
57
134
  try {
@@ -66,13 +143,13 @@ function serializeValue(value, seen) {
66
143
  if (typeof value === "string")
67
144
  return safeText(value);
68
145
  if (typeof value === "bigint")
69
- return value.toString();
146
+ return safeText(value.toString());
70
147
  if (typeof value === "number" || typeof value === "boolean" || value === null)
71
148
  return value;
72
149
  if (typeof value === "undefined")
73
150
  return undefined;
74
151
  if (typeof value === "function" || typeof value === "symbol")
75
- return String(value);
152
+ return safeText(String(value));
76
153
  if (seen.has(value))
77
154
  return "[Circular]";
78
155
  seen.add(value);
@@ -83,7 +160,7 @@ function serializeValue(value, seen) {
83
160
  if (!BLOCKED_KEY.test(key)) {
84
161
  const safeValue = serializeValue(item, seen);
85
162
  if (safeValue !== undefined)
86
- result[key] = safeValue;
163
+ result[safeText(key)] = safeValue;
87
164
  }
88
165
  }
89
166
  return result;
@@ -1,3 +1,4 @@
1
+ import { redactSecrets } from "../sensitive-output.js";
1
2
  const EXCERPT_LIMIT = 500;
2
3
  /**
3
4
  * A model configuration error is never retried or fallen back: another model would answer a request the caller did
@@ -16,7 +17,7 @@ export function createModelConfigErrorReporter(logger, emit) {
16
17
  if (reported.has(key))
17
18
  return;
18
19
  reported.add(key);
19
- const errorMessage = message.errorMessage ?? "";
20
+ const errorMessage = redactSecrets(message.errorMessage ?? "");
20
21
  logger.warn("model_config_error", { model, provider: message.provider, errorMessage });
21
22
  emit({
22
23
  type: "model_config_error",
@@ -33,11 +33,14 @@ export interface ResolvedRetryFallbackSettings {
33
33
  * follows Fable 5 whichever provider serves it - the builtin Anthropic provider, the
34
34
  * Anthropic Subscription extension, a gateway, or Bedrock.
35
35
  *
36
- * The ladder never leaves the Anthropic Opus family. The previous shipped default was
37
- * removed because it led with cross-family rungs (`k3`, `kimi-k3`), which moved a Claude
38
- * session onto another vendor mid-turn and could rank a guaranteed-refusal OAuth lane
39
- * first; a same-family step-down carries neither problem. There is still deliberately no
40
- * wildcard lane.
36
+ * The ladder never leaves Anthropic. The previous shipped default was removed because it
37
+ * led with cross-family rungs (`k3`, `kimi-k3`), which moved a Claude session onto another
38
+ * vendor mid-turn and could rank a guaranteed-refusal OAuth lane first; an Anthropic-only
39
+ * ladder carries neither problem. There is still deliberately no wildcard lane.
40
+ *
41
+ * Opus 5.5 steps to Fable 5.2 before Opus 5: Anthropic meters Opus on its own quota window,
42
+ * so a rate limit on Opus 5.5 also holds every other Opus rung on that account or gateway
43
+ * pool (senpi#6385).
41
44
  *
42
45
  * Rungs stay on current-generation Opus: a Claude session must never step down to
43
46
  * `claude-opus-4-8` or `claude-opus-4-6` (senpi#3727).
@@ -4,11 +4,14 @@
4
4
  * follows Fable 5 whichever provider serves it - the builtin Anthropic provider, the
5
5
  * Anthropic Subscription extension, a gateway, or Bedrock.
6
6
  *
7
- * The ladder never leaves the Anthropic Opus family. The previous shipped default was
8
- * removed because it led with cross-family rungs (`k3`, `kimi-k3`), which moved a Claude
9
- * session onto another vendor mid-turn and could rank a guaranteed-refusal OAuth lane
10
- * first; a same-family step-down carries neither problem. There is still deliberately no
11
- * wildcard lane.
7
+ * The ladder never leaves Anthropic. The previous shipped default was removed because it
8
+ * led with cross-family rungs (`k3`, `kimi-k3`), which moved a Claude session onto another
9
+ * vendor mid-turn and could rank a guaranteed-refusal OAuth lane first; an Anthropic-only
10
+ * ladder carries neither problem. There is still deliberately no wildcard lane.
11
+ *
12
+ * Opus 5.5 steps to Fable 5.2 before Opus 5: Anthropic meters Opus on its own quota window,
13
+ * so a rate limit on Opus 5.5 also holds every other Opus rung on that account or gateway
14
+ * pool (senpi#6385).
12
15
  *
13
16
  * Rungs stay on current-generation Opus: a Claude session must never step down to
14
17
  * `claude-opus-4-8` or `claude-opus-4-6` (senpi#3727).
@@ -18,7 +21,7 @@ export const DEFAULT_FALLBACK_CHAINS = {
18
21
  "claude-fable-5-2": ["claude-opus-5-5", "claude-opus-5"],
19
22
  "claude-fable-5-1": ["claude-opus-5-5", "claude-opus-5"],
20
23
  "claude-fable-5": ["claude-opus-5-5", "claude-opus-5"],
21
- "claude-opus-5-5": ["claude-opus-5"],
24
+ "claude-opus-5-5": ["claude-fable-5-2", "claude-opus-5"],
22
25
  };
23
26
  function cloneDefaultFallbackChains() {
24
27
  const chains = {};
@@ -440,6 +440,8 @@ export declare class SessionManager {
440
440
  private _transcriptEntryBytes;
441
441
  private _serializedFileEntries;
442
442
  isPersisted(): boolean;
443
+ /** Give an unclaimed in-memory session its first file without rebuilding or changing its identity. */
444
+ enablePersistence(): Promise<void>;
443
445
  getCwd(): string;
444
446
  getSessionDir(): string;
445
447
  usesDefaultSessionDir(): boolean;
@@ -1,7 +1,7 @@
1
1
  import { getCurrentSystemMessage, normalizeProviderId } from "@earendil-works/pi-ai";
2
2
  import { randomBytes, randomUUID } from "crypto";
3
3
  import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readSync, rmSync, statSync, writeFileSync, } from "fs";
4
- import { appendFile, open, readdir, rm } from "fs/promises";
4
+ import { appendFile, mkdir, open, readdir, rm } from "fs/promises";
5
5
  import { basename, join, resolve } from "path";
6
6
  import { StringDecoder } from "string_decoder";
7
7
  import { APP_NAME, getAgentDir as getDefaultAgentDir, getSessionsDir } from "../config.js";
@@ -1230,6 +1230,20 @@ export class SessionManager {
1230
1230
  isPersisted() {
1231
1231
  return this.persist;
1232
1232
  }
1233
+ /** Give an unclaimed in-memory session its first file without rebuilding or changing its identity. */
1234
+ async enablePersistence() {
1235
+ if (this.persist)
1236
+ return;
1237
+ const dir = getDefaultSessionDirPath(this.cwd);
1238
+ await mkdir(dir, { recursive: true });
1239
+ const header = this.fileEntries[0];
1240
+ const path = join(dir, `${header.timestamp.replace(/[:.]/g, "-")}_${this.sessionId}.jsonl`);
1241
+ reserveSessionWrite(path);
1242
+ this.sessionDir = dir;
1243
+ this.sessionFile = path;
1244
+ this.persist = true;
1245
+ registerSessionWriter(this);
1246
+ }
1233
1247
  getCwd() {
1234
1248
  return this.cwd;
1235
1249
  }
@@ -302,6 +302,12 @@ export declare class SettingsManager {
302
302
  getSettings(): Settings;
303
303
  getGlobalSettings(): Settings;
304
304
  getProjectSettings(): Settings;
305
+ /**
306
+ * `contextFiles.globalPath` from the user's own layers only: session overrides, then global
307
+ * settings. A project's `.senpi/settings.json` never redirects the user's global context file
308
+ * (senpi#5567), so the project layer is deliberately skipped.
309
+ */
310
+ getGlobalContextFilePath(): string | undefined;
305
311
  /** The project settings file at `path` as stored: parsed and migrated like a load, but never gated by trust. */
306
312
  readProjectSettingsFile(path: string): ProjectSettingsFile;
307
313
  /**
@@ -697,6 +697,14 @@ export class SettingsManager {
697
697
  getProjectSettings() {
698
698
  return structuredClone(this.projectSettings);
699
699
  }
700
+ /**
701
+ * `contextFiles.globalPath` from the user's own layers only: session overrides, then global
702
+ * settings. A project's `.senpi/settings.json` never redirects the user's global context file
703
+ * (senpi#5567), so the project layer is deliberately skipped.
704
+ */
705
+ getGlobalContextFilePath() {
706
+ return this.runtimeOverrides.contextFiles?.globalPath ?? this.globalSettings.contextFiles?.globalPath;
707
+ }
700
708
  /** The project settings file at `path` as stored: parsed and migrated like a load, but never gated by trust. */
701
709
  readProjectSettingsFile(path) {
702
710
  let content;
@@ -1,15 +1,15 @@
1
- import type { SessionManager } from "./session-manager.ts";
2
1
  /**
3
- * Who started a turn, for the delegated-default permission rule (remote approval-mode work):
4
- * - `local`: a local connection's or the TUI's own input;
5
- * - `delegated`: input that arrived on a delegated (remote device) connection;
2
+ * Who started a turn, for the remote approval-mode rule (senpi#6342: a turn follows the permission setting of the machine
3
+ * it runs on, and no origin outlives the turn it started):
4
+ * - `local`: this machine's own input, or input from a device the forwarder stated is the same account;
5
+ * - `delegated`: input from a device the forwarder did not state is the same account, or the one run such a device's
6
+ * record started;
6
7
  * - `engine`: a turn no client record started (an extension's message, a goal or loop turn, restored input).
7
8
  * A steer joins the running turn (`join`) and never changes its origin.
8
9
  */
9
10
  export type TurnOrigin = "local" | "delegated" | "engine";
10
11
  export type InputTurnMark = TurnOrigin | "join";
11
12
  export type ConnectionOrigin = "local" | "delegated";
12
- type OriginSessionManager = Pick<SessionManager, "getEntries" | "appendCustomEntry">;
13
13
  /** The mark an input gets where it is created: explicit for client records, `local` for the TUI, else `engine`. */
14
14
  export declare function inputTurnMark(input: {
15
15
  readonly mode?: "steer" | "followUp";
@@ -21,11 +21,19 @@ export declare function markInputTurnOrigin(message: object, mark: InputTurnMark
21
21
  export declare function copyInputTurnMark(from: object, to: object): void;
22
22
  /** Whether a message carries an origin mark: a user message always opens a turn's origin, any other only when marked. */
23
23
  export declare function hasInputTurnMark(message: object): boolean;
24
- /** A run starts as engine work; the user message that opens a turn sets its origin, a steer keeps it. */
24
+ /** Runs `handle`, and every async task it starts, as the record `origin` describes. */
25
+ export declare function runInRecordScope<T>(origin: ConnectionOrigin, handle: () => Promise<T>): Promise<T>;
26
+ /** Whether the work being created belongs to an open cross-account record's own handling. */
27
+ export declare function recordIsDelegated(): boolean;
28
+ /**
29
+ * A run starts as engine work, or as delegated work when a cross-account record's own handling starts it; a retry keeps
30
+ * the turn it continues. The user message that opens a turn sets its origin, a steer keeps it.
31
+ */
25
32
  export declare function noteRunStart(sessionManager: object): void;
33
+ /** A retry or recovery run repeats the turn it continues, so it keeps that turn's origin until the continuation ends. */
34
+ export declare function noteTurnContinuation(sessionManager: object): void;
35
+ export declare function endTurnContinuation(sessionManager: object): void;
26
36
  export declare function noteUserMessageStart(sessionManager: object, message: object): void;
27
- export declare function recordDelegatedOrigin(sessionManager: OriginSessionManager): void;
28
- /** Whether the deciding turn counts as delegated-origin: a remote device started it, or nobody did on a remote-driven session. */
29
- export declare function decidingTurnIsDelegated(sessionManager: Pick<SessionManager, "getEntries">): boolean;
30
- export {};
37
+ /** Whether the deciding turn counts as delegated-origin: a cross-account device started this turn or this run. */
38
+ export declare function decidingTurnIsDelegated(sessionManager: object): boolean;
31
39
  //# sourceMappingURL=turn-origin.d.ts.map
@@ -1,13 +1,16 @@
1
- const DELEGATED_ORIGIN_ENTRY = "delegated_origin";
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
2
  const inputMarks = new WeakMap();
3
3
  const currentOrigins = new WeakMap();
4
- const delegatedSessions = new WeakSet();
4
+ const recordScopes = new AsyncLocalStorage();
5
+ const continuingTurns = new WeakSet();
5
6
  /** The mark an input gets where it is created: explicit for client records, `local` for the TUI, else `engine`. */
6
7
  export function inputTurnMark(input) {
7
8
  if (input.mode === "steer")
8
9
  return "join";
9
10
  if (input.connectionOrigin !== undefined)
10
11
  return input.connectionOrigin;
12
+ if (recordIsDelegated())
13
+ return "delegated";
11
14
  return input.source === undefined || input.source === "interactive" ? "local" : "engine";
12
15
  }
13
16
  export function markInputTurnOrigin(message, mark) {
@@ -23,9 +26,34 @@ export function copyInputTurnMark(from, to) {
23
26
  export function hasInputTurnMark(message) {
24
27
  return inputMarks.has(message);
25
28
  }
26
- /** A run starts as engine work; the user message that opens a turn sets its origin, a steer keeps it. */
29
+ /** Runs `handle`, and every async task it starts, as the record `origin` describes. */
30
+ export function runInRecordScope(origin, handle) {
31
+ return recordScopes.run({ origin, open: true }, handle);
32
+ }
33
+ /** Whether the work being created belongs to an open cross-account record's own handling. */
34
+ export function recordIsDelegated() {
35
+ const scope = recordScopes.getStore();
36
+ return scope?.open === true && scope.origin === "delegated";
37
+ }
38
+ /**
39
+ * A run starts as engine work, or as delegated work when a cross-account record's own handling starts it; a retry keeps
40
+ * the turn it continues. The user message that opens a turn sets its origin, a steer keeps it.
41
+ */
27
42
  export function noteRunStart(sessionManager) {
28
- currentOrigins.set(sessionManager, "engine");
43
+ if (continuingTurns.has(sessionManager))
44
+ return;
45
+ const delegated = recordIsDelegated();
46
+ const scope = recordScopes.getStore();
47
+ if (scope !== undefined)
48
+ scope.open = false;
49
+ currentOrigins.set(sessionManager, delegated ? "delegated" : "engine");
50
+ }
51
+ /** A retry or recovery run repeats the turn it continues, so it keeps that turn's origin until the continuation ends. */
52
+ export function noteTurnContinuation(sessionManager) {
53
+ continuingTurns.add(sessionManager);
54
+ }
55
+ export function endTurnContinuation(sessionManager) {
56
+ continuingTurns.delete(sessionManager);
29
57
  }
30
58
  export function noteUserMessageStart(sessionManager, message) {
31
59
  const mark = inputMarks.get(message);
@@ -33,32 +61,8 @@ export function noteUserMessageStart(sessionManager, message) {
33
61
  return;
34
62
  currentOrigins.set(sessionManager, mark ?? "engine");
35
63
  }
36
- export function recordDelegatedOrigin(sessionManager) {
37
- if (delegatedOriginOf(sessionManager))
38
- return;
39
- delegatedSessions.add(sessionManager);
40
- sessionManager.appendCustomEntry(DELEGATED_ORIGIN_ENTRY, { delegatedOrigin: true }, { startupBookkeeping: true });
41
- }
42
- // How many entries a session's scan has already found free of the marker: each gated decision reads only new entries.
43
- const scannedEntries = new WeakMap();
44
- function delegatedOriginOf(sessionManager) {
45
- if (delegatedSessions.has(sessionManager))
46
- return true;
47
- const entries = sessionManager.getEntries();
48
- const persisted = entries
49
- .slice(scannedEntries.get(sessionManager) ?? 0)
50
- .some((entry) => entry.type === "custom" && entry.customType === DELEGATED_ORIGIN_ENTRY);
51
- if (persisted)
52
- delegatedSessions.add(sessionManager);
53
- else
54
- scannedEntries.set(sessionManager, entries.length);
55
- return persisted;
56
- }
57
- /** Whether the deciding turn counts as delegated-origin: a remote device started it, or nobody did on a remote-driven session. */
64
+ /** Whether the deciding turn counts as delegated-origin: a cross-account device started this turn or this run. */
58
65
  export function decidingTurnIsDelegated(sessionManager) {
59
- const origin = currentOrigins.get(sessionManager) ?? "engine";
60
- if (origin === "delegated")
61
- return true;
62
- return origin === "engine" && delegatedOriginOf(sessionManager);
66
+ return currentOrigins.get(sessionManager) === "delegated";
63
67
  }
64
68
  //# sourceMappingURL=turn-origin.js.map
@@ -4,5 +4,6 @@ export declare function serviceTierForProvider(provider: string | undefined, ser
4
4
  export declare function ultrafastSelectionWarning(model: {
5
5
  readonly provider: string;
6
6
  readonly id: string;
7
+ readonly upstreamModelId?: string;
7
8
  }, serviceTier: ServiceTier | undefined): string | undefined;
8
9
  //# sourceMappingURL=ultrafast-lanes.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { ULTRAFAST_DOCUMENTED_MODEL_IDS } from "@earendil-works/pi-ai";
1
2
  /**
2
3
  * Providers that receive `service_tier: "ultrafast"`: OpenAI's own API and the ChatGPT
3
4
  * subscription backend. Gateways and other providers serving an OpenAI model on the Responses
@@ -5,7 +6,6 @@
5
6
  * sends Ultrafast only to first-party OpenAI and Codex models).
6
7
  */
7
8
  const ULTRAFAST_PROVIDERS = new Set(["openai", "chatgpt-subscription"]);
8
- const ULTRAFAST_DOCUMENTED_MODEL_IDS = new Set(["gpt-6-astra", "gpt-6.1-sol"]);
9
9
  export function serviceTierForProvider(provider, serviceTier) {
10
10
  if (serviceTier !== "ultrafast")
11
11
  return serviceTier;
@@ -18,7 +18,9 @@ export function ultrafastSelectionWarning(model, serviceTier) {
18
18
  if (!ULTRAFAST_PROVIDERS.has(model.provider)) {
19
19
  return `Ultrafast is only sent to OpenAI and ChatGPT Subscription; ${model.provider}/${model.id} runs at its default tier`;
20
20
  }
21
- if (ULTRAFAST_DOCUMENTED_MODEL_IDS.has(model.id))
21
+ // A tier-pinned row (for example `gpt-6.1-sol-ultrafast`) is documented by its upstream model,
22
+ // as the pricing code already checks (senpi#2978).
23
+ if (ULTRAFAST_DOCUMENTED_MODEL_IDS.has(model.upstreamModelId ?? model.id))
22
24
  return undefined;
23
25
  return `Ultrafast is documented for GPT-6 Astra and GPT-6.1 Sol; ${model.provider}/${model.id} may reject or ignore it`;
24
26
  }
@@ -10,11 +10,26 @@ export declare function formatTokens(count: number): string;
10
10
  /**
11
11
  * Mirrors the provider-shown-only-when->1 rule for accounts: the active account
12
12
  * name appears only when the provider actually pools more than one slot. The
13
- * pick shown is the pin when present, else the first account of the user order
14
- * (senpi#3591), else the session's HRW winner - the same ranking the rotation
15
- * engine uses, so the footer names the slot that will serve.
13
+ * pick shown is the effective pin (CLI > settings > stored, senpi#5983) when
14
+ * present, else the first account of the user order (senpi#3591), else the
15
+ * session's HRW winner - the same ranking the rotation engine uses, so the
16
+ * footer names the slot that will serve. The effective pin only reaches the
17
+ * footer for the Anthropic subscription provider (senpi#6222 review); other
18
+ * pooled providers keep their stored pin even when a slot shares the pin's
19
+ * name. A blocked pin (blockedUntil in the
20
+ * future) is skipped for the first usable slot in that ranking, and the
21
+ * suffix marks the blocked pin (`@B (A blocked until 14:00)`); when every
22
+ * slot is blocked it names the top-ranked slot and marks it blocked instead
23
+ * of silently naming a slot that cannot serve. Block state rides on the
24
+ * stored slots (SlotBlockState); slots without it are treated as usable.
16
25
  */
17
- export declare function accountFooterSuffix(credential: Credential | undefined, sessionId: string): string;
26
+ export type AccountFooterOptions = {
27
+ /** Effective pin from CLI/settings, outranking the stored pin (senpi#5983). */
28
+ pinnedAccount?: string;
29
+ /** Override for "now" (tests); defaults to `Date.now()`. */
30
+ now?: number;
31
+ };
32
+ export declare function accountFooterSuffix(credential: Credential | undefined, sessionId: string, options?: AccountFooterOptions): string;
18
33
  export declare function formatCwdForFooter(cwd: string, home: string | undefined): string;
19
34
  /**
20
35
  * Footer component that shows pwd, token stats, and context usage.
@@ -25,7 +40,10 @@ export declare class FooterComponent implements Component {
25
40
  private footerData;
26
41
  private autoCompactEnabled;
27
42
  private compactionDelegated;
28
- constructor(session: AgentSession, footerData: ReadonlyFooterDataProvider);
43
+ private resolveAccountPin;
44
+ constructor(session: AgentSession, footerData: ReadonlyFooterDataProvider, options?: {
45
+ resolveAccountPin?: (sessionId: string) => string | undefined;
46
+ });
29
47
  setSession(session: AgentSession): void;
30
48
  setAutoCompactEnabled(enabled: boolean): void;
31
49
  /**
@@ -4,6 +4,7 @@ import { slotsOfOwnKind } from "@earendil-works/pi-ai/auth/pool/kinds";
4
4
  import { applyAccountOrder, rendezvousOrder } from "@earendil-works/pi-ai/auth/pool/select";
5
5
  import { accountLabel } from "@earendil-works/pi-ai/auth/pool/slots";
6
6
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
7
+ import { ANTHROPIC_SUBSCRIPTION_PROVIDER_ID } from "../../../core/extensions/builtin/anthropic-subscription/provider-id.js";
7
8
  import { theme } from "../theme/theme.js";
8
9
  import { planFooterLayout } from "./footer-layout.js";
9
10
  import { buildRightLabel, colorRightSide } from "./footer-right-label.js";
@@ -39,25 +40,43 @@ export function formatTokens(count) {
39
40
  return `${trim1(n / 1_000_000_000)}B`;
40
41
  return `${Math.round(n / 1_000_000_000)}B`;
41
42
  }
42
- /**
43
- * Mirrors the provider-shown-only-when->1 rule for accounts: the active account
44
- * name appears only when the provider actually pools more than one slot. The
45
- * pick shown is the pin when present, else the first account of the user order
46
- * (senpi#3591), else the session's HRW winner - the same ranking the rotation
47
- * engine uses, so the footer names the slot that will serve.
48
- */
49
- export function accountFooterSuffix(credential, sessionId) {
43
+ function formatBlockedUntil(until) {
44
+ const date = new Date(until);
45
+ return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
46
+ }
47
+ export function accountFooterSuffix(credential, sessionId, options = {}) {
50
48
  const pooled = credential;
51
49
  // Only slots the credential can serve with; a mixed pool's other-kind slot never does (senpi#3486).
52
50
  const slots = credential ? slotsOfOwnKind(credential) : [];
53
51
  if (slots.length < 2)
54
52
  return "";
55
- const pinned = Object.entries(credential ?? {}).find(([key]) => key === "pinned")?.[1];
56
- const pinnedSlot = slots.find((slot) => slot.name === pinned);
57
- if (pinnedSlot)
58
- return `@${footerAccountLabel(pinnedSlot)}`;
59
- const winner = applyAccountOrder(pooled?.order, rendezvousOrder(sessionId, slots, (input) => createHash("sha256").update(input).digest().readBigUInt64BE(0)))[0];
60
- return winner === undefined ? "" : `@${footerAccountLabel(winner)}`;
53
+ const now = options.now ?? Date.now();
54
+ // Block state rides on the stored slots at runtime (SlotBlockState) but
55
+ // is not part of the CredentialSlot type, so read it through unknown.
56
+ const blockedUntilOf = (slot) => slot.blockedUntil;
57
+ const isBlocked = (slot) => {
58
+ const until = blockedUntilOf(slot);
59
+ return until !== undefined && until > now;
60
+ };
61
+ const ranked = applyAccountOrder(pooled?.order, rendezvousOrder(sessionId, slots, (input) => createHash("sha256").update(input).digest().readBigUInt64BE(0)));
62
+ const storedPinned = Object.entries(credential ?? {}).find(([key]) => key === "pinned")?.[1];
63
+ const pinnedName = options.pinnedAccount ?? storedPinned;
64
+ const pinnedSlot = pinnedName === undefined ? undefined : slots.find((slot) => slot.name === pinnedName);
65
+ if (pinnedSlot) {
66
+ if (!isBlocked(pinnedSlot))
67
+ return `@${footerAccountLabel(pinnedSlot)}`;
68
+ const until = blockedUntilOf(pinnedSlot);
69
+ const serving = ranked.find((slot) => slot !== pinnedSlot && !isBlocked(slot));
70
+ if (serving)
71
+ return `@${footerAccountLabel(serving)} (${footerAccountLabel(pinnedSlot)} blocked until ${formatBlockedUntil(until)})`;
72
+ return `@${footerAccountLabel(pinnedSlot)} (blocked until ${formatBlockedUntil(until)})`;
73
+ }
74
+ const winner = ranked.find((slot) => !isBlocked(slot)) ?? ranked[0];
75
+ if (winner === undefined)
76
+ return "";
77
+ if (isBlocked(winner))
78
+ return `@${footerAccountLabel(winner)} (blocked until ${formatBlockedUntil(blockedUntilOf(winner))})`;
79
+ return `@${footerAccountLabel(winner)}`;
61
80
  }
62
81
  /**
63
82
  * Widest account label the provider segment carries. A label wider than this
@@ -90,11 +109,12 @@ export function formatCwdForFooter(cwd, home) {
90
109
  * Computes token/context stats from session, gets git branch and extension statuses from provider.
91
110
  */
92
111
  export class FooterComponent {
93
- constructor(session, footerData) {
112
+ constructor(session, footerData, options = {}) {
94
113
  this.autoCompactEnabled = true;
95
114
  this.compactionDelegated = false;
96
115
  this.session = session;
97
116
  this.footerData = footerData;
117
+ this.resolveAccountPin = options.resolveAccountPin;
98
118
  }
99
119
  setSession(session) {
100
120
  this.session = session;
@@ -201,7 +221,16 @@ export class FooterComponent {
201
221
  let accountSuffix = "";
202
222
  if (state.model) {
203
223
  try {
204
- accountSuffix = accountFooterSuffix(this.session.modelRegistry.authStorage.get(state.model.provider), this.session.sessionManager.getSessionId());
224
+ const sessionId = this.session.sessionManager.getSessionId();
225
+ // The resolver carries the Anthropic CLI/settings pin; resolve
226
+ // it only when the current model uses the Anthropic
227
+ // subscription provider. A same-named slot of another pooled
228
+ // provider (e.g. openai-codex "default") keeps its own stored
229
+ // pin instead (senpi#6222 review).
230
+ const pinnedAccount = state.model.provider === ANTHROPIC_SUBSCRIPTION_PROVIDER_ID
231
+ ? this.resolveAccountPin?.(sessionId)
232
+ : undefined;
233
+ accountSuffix = accountFooterSuffix(this.session.modelRegistry.authStorage.get(state.model.provider), sessionId, pinnedAccount === undefined ? undefined : { pinnedAccount });
205
234
  }
206
235
  catch {
207
236
  // The footer must render even when credential storage is unreadable.
@@ -25,6 +25,8 @@ import { collectEntriesForBranchSummary } from "../../core/compaction/branch-sum
25
25
  import { findExtensionStackMatches } from "../../core/crash-log.js";
26
26
  import { AssistantEditError, assistantTextEquals } from "../../core/edited-assistant-message.js";
27
27
  import { subscribeProviderAccountEvents } from "../../core/extensions/builtin/anthropic-subscription/account-events.js";
28
+ import { getSessionClaudeAccountPin, resolveClaudeAccountPin, } from "../../core/extensions/builtin/anthropic-subscription/account-command.js";
29
+ import { loadAnthropicSubscriptionProviderSettingsFromDisk } from "../../core/extensions/builtin/anthropic-subscription/settings.js";
28
30
  import { formatAccountFailoverNotice } from "./account-failover-notice.js";
29
31
  import { askUserAnswerDisplayText, formatUserMessage, showsPartialAnswers, } from "../../core/extensions/builtin/ask-user/format.js";
30
32
  import { askUserRenderers } from "../../core/extensions/builtin/ask-user/render.js";
@@ -817,7 +819,13 @@ export class InteractiveMode {
817
819
  this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
818
820
  this.footer = this.chrome
819
821
  ? this.chrome.createFooter(this.session, this.footerDataProvider)
820
- : new FooterComponent(this.session, this.footerDataProvider);
822
+ : new FooterComponent(this.session, this.footerDataProvider, {
823
+ // CLI > settings > stored pin, resolved per render so a
824
+ // mid-session /claude-account pin or settings change is
825
+ // picked up (senpi#5983); the stored pin stays the
826
+ // fallback inside accountFooterSuffix.
827
+ resolveAccountPin: (sessionId) => resolveClaudeAccountPin(getSessionClaudeAccountPin(sessionId), loadAnthropicSubscriptionProviderSettingsFromDisk(this.sessionManager.getCwd()).pinnedAccount, undefined),
828
+ });
821
829
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
822
830
  this.footerContainer = new Container();
823
831
  this.footerContainer.addChild(this.footer);
@@ -1,7 +1,7 @@
1
1
  import type { AgentSession } from "../../core/agent-session.ts";
2
2
  import { type ControlAttachments } from "./session-control-attach.ts";
3
3
  import { type ControlCommand, type ControlConnection } from "./session-control-server.ts";
4
- export declare const TUI_RPC_COMMANDS: readonly ["open_session", "get_snapshot", "subscribe_sessions", "unsubscribe_sessions", "steer", "follow_up", "prompt", "admit_external_message", "get_effect_status", "set_client_info", "attach_session", "release_attachment", "abort", "mcp_presenter_context", "mcp_form_presented", "mcp_form_response", "mcp_form_sync", "mcp_presenter_secret_answer"];
4
+ export declare const TUI_RPC_COMMANDS: readonly ["open_session", "park_session", "claim_session", "get_snapshot", "subscribe_sessions", "unsubscribe_sessions", "steer", "follow_up", "prompt", "admit_external_message", "get_effect_status", "set_client_info", "attach_session", "release_attachment", "abort", "mcp_presenter_context", "mcp_form_presented", "mcp_form_response", "mcp_form_sync", "mcp_presenter_secret_answer"];
5
5
  export interface RpcCommandContext {
6
6
  /** This endpoint's instance id: the `expectedHostInstance` an `attach_session` may name. */
7
7
  readonly instanceId: string;