@code-yeongyu/senpi 2026.11.6-2 → 2026.11.9-2

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 (244) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/bundle/chunks/{bun-extension-importer-RCGPQTI2.js → bun-extension-importer-GQ6IKEXD.js} +2 -2
  3. package/dist/bundle/chunks/{chunk-RQ6CHXQ2.js → chunk-3TZQXZKH.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-QN2BHP3M.js → chunk-44BJPS5I.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-WGRKM3N6.js → chunk-45MVD7BU.js} +1 -1
  6. package/dist/bundle/chunks/chunk-4JIBEZZE.js +106 -0
  7. package/dist/bundle/chunks/{chunk-CF5OGBC4.js → chunk-4NYNMAYF.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-X5ZVYNOL.js → chunk-4UR6UFBH.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-LNPAT22L.js → chunk-527Q2UYM.js} +9 -8
  10. package/dist/bundle/chunks/{chunk-HLM46XEB.js → chunk-622VYTRI.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-D5MRJWUK.js → chunk-7CFORZPE.js} +221 -229
  12. package/dist/bundle/chunks/{chunk-GKAYPJ4E.js → chunk-7PROV6BB.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-4E7GDG4H.js → chunk-7VMEE62L.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-C7OKUC7L.js → chunk-K3UHGDUQ.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-7III44PV.js → chunk-LHSWAJJL.js} +1 -1
  16. package/dist/bundle/chunks/{chunk-47PPZKPO.js → chunk-LM6DOWQH.js} +4 -4
  17. package/dist/bundle/chunks/chunk-LPYBKMPT.js +2 -0
  18. package/dist/bundle/chunks/{chunk-2FDW2LQL.js → chunk-M7GF73FO.js} +2 -2
  19. package/dist/bundle/chunks/{chunk-ECW7VBMB.js → chunk-NDSWTAYJ.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-GSKCWXXG.js → chunk-Q6NR6644.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-TIPKAMSS.js → chunk-SGAFIMAU.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-7PSBD5KS.js → chunk-STGOYIXC.js} +1 -1
  23. package/dist/bundle/chunks/{chunk-DSWDJ7HT.js → chunk-WQ4RBPMS.js} +1 -1
  24. package/dist/bundle/chunks/chunk-XZDCMDUH.js +2 -0
  25. package/dist/bundle/chunks/{chunk-GNSMNSL7.js → chunk-Y5L4UFMI.js} +2 -2
  26. package/dist/bundle/chunks/{cli-main-T74YJGIF.js → cli-main-XBEFQW5A.js} +1 -1
  27. package/dist/bundle/chunks/{dispatch-BAK2YZKD.js → dispatch-VDIYSXQN.js} +1 -1
  28. package/dist/bundle/chunks/{hand-entry-AYRUGE2A.js → hand-entry-QFEY3JMQ.js} +1 -1
  29. package/dist/bundle/chunks/{headless-host-ES74ZB6M.js → headless-host-YXUGONR3.js} +1 -1
  30. package/dist/bundle/chunks/{host-command-YRXZE4TV.js → host-command-VHX2ILLM.js} +1 -1
  31. package/dist/bundle/chunks/{host-session-command-N7IN7ZDV.js → host-session-command-P3MRW63Z.js} +1 -1
  32. package/dist/bundle/chunks/interactive-mode-YGL3QHU6.js +2 -0
  33. package/dist/bundle/chunks/main-5SD4DHMC.js +2 -0
  34. package/dist/bundle/chunks/{multi-session-host-XSUJDHFZ.js → multi-session-host-PGRHY3JP.js} +20 -17
  35. package/dist/bundle/chunks/{package-manager-cli-GLUNPQJU.js → package-manager-cli-SADLU7QX.js} +1 -1
  36. package/dist/bundle/chunks/{role-RRFFHZF5.js → role-4BUU46VW.js} +3 -3
  37. package/dist/bundle/chunks/{rotation-stream-BN2YK2MC.js → rotation-stream-TODBIHXJ.js} +1 -1
  38. package/dist/bundle/chunks/rpc-mode-27TC7W6K.js +2 -0
  39. package/dist/bundle/chunks/{run-attached-tui-W236SEOR.js → run-attached-tui-MXCC5IZR.js} +1 -1
  40. package/dist/bundle/chunks/{runtime-session-OBXYK4PI.js → runtime-session-VO3X4OE3.js} +1 -1
  41. package/dist/bundle/chunks/{schedule-command-B2EJTBOE.js → schedule-command-MX56D6UE.js} +1 -1
  42. package/dist/bundle/chunks/{session-control-endpoint-YIMLHGLG.js → session-control-endpoint-IL6ERD4U.js} +1 -1
  43. package/dist/bundle/chunks/{session-picker-E6ZONUJB.js → session-picker-6VRC25MJ.js} +1 -1
  44. package/dist/bundle/chunks/session-worker.js +114 -110
  45. package/dist/bundle/cli.js +2 -2
  46. package/dist/bundle/index.js +1 -1
  47. package/dist/bundle/rpc-entry.js +1 -1
  48. package/dist/bundle/runtime-manifest.json +1 -1
  49. package/dist/core/agent-session-runtime.d.ts +2 -0
  50. package/dist/core/agent-session-runtime.js +9 -2
  51. package/dist/core/agent-session-services.d.ts +2 -0
  52. package/dist/core/agent-session-services.js +1 -0
  53. package/dist/core/agent-session.d.ts +32 -3
  54. package/dist/core/agent-session.js +142 -29
  55. package/dist/core/credential-pool/rotation-slots.d.ts +3 -3
  56. package/dist/core/credential-pool/state-store.d.ts +1 -1
  57. package/dist/core/egress/classify.d.ts +8 -3
  58. package/dist/core/egress/classify.js +177 -158
  59. package/dist/core/egress/console-redactor.d.ts +7 -0
  60. package/dist/core/egress/console-redactor.js +74 -0
  61. package/dist/core/egress/credentials.d.ts +73 -0
  62. package/dist/core/egress/credentials.js +20 -0
  63. package/dist/core/egress/device-custody.d.ts +1 -1
  64. package/dist/core/egress/device-custody.js +18 -15
  65. package/dist/core/egress/device-proxy-call.d.ts +59 -0
  66. package/dist/core/egress/device-proxy-call.js +148 -0
  67. package/dist/core/egress/device-proxy-lost-calls.d.ts +27 -0
  68. package/dist/core/egress/device-proxy-lost-calls.js +28 -0
  69. package/dist/core/egress/device-proxy-ports.d.ts +49 -0
  70. package/dist/core/egress/device-proxy-ports.js +2 -0
  71. package/dist/core/egress/device-proxy-request.d.ts +34 -0
  72. package/dist/core/egress/device-proxy-request.js +69 -0
  73. package/dist/core/egress/device-proxy-waits.d.ts +40 -0
  74. package/dist/core/egress/device-proxy-waits.js +67 -0
  75. package/dist/core/egress/device-proxy.d.ts +3 -0
  76. package/dist/core/egress/device-proxy.js +214 -0
  77. package/dist/core/egress/endpoint.js +2 -1
  78. package/dist/core/egress/entry-verify.d.ts +13 -0
  79. package/dist/core/egress/entry-verify.js +33 -0
  80. package/dist/core/egress/hpke.d.ts +63 -0
  81. package/dist/core/egress/hpke.js +192 -0
  82. package/dist/core/egress/key-policy-template.d.ts +7 -1
  83. package/dist/core/egress/ledger-schema.d.ts +80 -0
  84. package/dist/core/egress/ledger-schema.js +117 -0
  85. package/dist/core/egress/ledger.d.ts +55 -0
  86. package/dist/core/egress/ledger.js +159 -0
  87. package/dist/core/egress/pinned-infra.d.ts +23 -2
  88. package/dist/core/egress/pinned-infra.js +9 -2
  89. package/dist/core/egress/placement-claim.d.ts +69 -0
  90. package/dist/core/egress/placement-claim.js +115 -0
  91. package/dist/core/egress/pool-state.d.ts +18 -0
  92. package/dist/core/egress/pool-state.js +5 -0
  93. package/dist/core/egress/redact.d.ts +10 -0
  94. package/dist/core/egress/redact.js +134 -0
  95. package/dist/core/egress/sealed-format.d.ts +101 -0
  96. package/dist/core/egress/sealed-format.js +174 -0
  97. package/dist/core/egress/sealed-source.d.ts +35 -0
  98. package/dist/core/egress/sealed-source.js +140 -0
  99. package/dist/core/egress/verified-keys.js +1 -1
  100. package/dist/core/egress/wire.d.ts +228 -0
  101. package/dist/core/egress/wire.js +49 -0
  102. package/dist/core/extensions/builtin/ask-user/escape-residue.d.ts +50 -0
  103. package/dist/core/extensions/builtin/ask-user/escape-residue.js +113 -0
  104. package/dist/core/extensions/builtin/ask-user/extension.js +7 -1
  105. package/dist/core/extensions/builtin/ask-user/resume.js +2 -1
  106. package/dist/core/extensions/builtin/ask-user/tool.d.ts +3 -0
  107. package/dist/core/extensions/builtin/ask-user/tool.js +8 -0
  108. package/dist/core/extensions/builtin/goal/index.js +46 -0
  109. package/dist/core/extensions/builtin/goal/stop-pause.d.ts +1 -0
  110. package/dist/core/extensions/builtin/goal/stop-pause.js +4 -2
  111. package/dist/core/extensions/builtin/monitor-state-event.d.ts +26 -0
  112. package/dist/core/extensions/builtin/monitor-state-event.js +2 -0
  113. package/dist/core/extensions/builtin/permission-system/index.js +10 -4
  114. package/dist/core/extensions/builtin/permission-system/preset-registry.d.ts +2 -0
  115. package/dist/core/extensions/builtin/permission-system/preset-registry.js +11 -0
  116. package/dist/core/extensions/builtin/permission-system/settings.d.ts +2 -0
  117. package/dist/core/extensions/builtin/permission-system/settings.js +10 -1
  118. package/dist/core/extensions/builtin/terminal/background-age.d.ts +25 -0
  119. package/dist/core/extensions/builtin/terminal/background-age.js +91 -0
  120. package/dist/core/extensions/builtin/terminal/extension-state.d.ts +1 -1
  121. package/dist/core/extensions/builtin/terminal/extension-state.js +21 -3
  122. package/dist/core/extensions/builtin/terminal/notify.d.ts +6 -0
  123. package/dist/core/extensions/builtin/terminal/notify.js +39 -5
  124. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +9 -2
  125. package/dist/core/extensions/builtin/terminal/session-bundle.js +36 -1
  126. package/dist/core/extensions/builtin/terminal/settings.d.ts +2 -0
  127. package/dist/core/extensions/builtin/terminal/settings.js +8 -0
  128. package/dist/core/extensions/builtin/terminal/tools/background-auto-reap.d.ts +15 -0
  129. package/dist/core/extensions/builtin/terminal/tools/background-auto-reap.js +87 -0
  130. package/dist/core/extensions/builtin/terminal/tools/bash.d.ts +3 -1
  131. package/dist/core/extensions/builtin/terminal/tools/bash.js +44 -15
  132. package/dist/core/extensions/builtin/terminal/tools/child-placement.d.ts +8 -0
  133. package/dist/core/extensions/builtin/terminal/tools/child-placement.js +43 -6
  134. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +14 -2
  135. package/dist/core/extensions/bun-extension-importer.js +4 -3
  136. package/dist/core/extensions/bun-extension-load-turns.d.ts +4 -0
  137. package/dist/core/extensions/bun-extension-load-turns.js +45 -0
  138. package/dist/core/extensions/bun-extension-registry.d.ts +1 -0
  139. package/dist/core/extensions/bun-extension-registry.js +19 -1
  140. package/dist/core/extensions/loader.js +5 -0
  141. package/dist/core/extensions/session-control-types.d.ts +15 -0
  142. package/dist/core/extensions/session-control-types.js +11 -0
  143. package/dist/core/extensions/session-dialog-api.d.ts +5 -0
  144. package/dist/core/extensions/session-dialog-api.js +13 -0
  145. package/dist/core/extensions/types.d.ts +14 -1
  146. package/dist/core/external-admission.d.ts +16 -2
  147. package/dist/core/external-admission.js +22 -4
  148. package/dist/core/permission-decision.d.ts +22 -0
  149. package/dist/core/permission-decision.js +53 -0
  150. package/dist/core/permission-mode.d.ts +56 -0
  151. package/dist/core/permission-mode.js +133 -0
  152. package/dist/core/retry-fallback/controller-types.d.ts +6 -0
  153. package/dist/core/retry-fallback/controller.d.ts +9 -0
  154. package/dist/core/retry-fallback/controller.js +34 -3
  155. package/dist/core/retry-fallback/unusable.d.ts +25 -1
  156. package/dist/core/retry-fallback/unusable.js +87 -2
  157. package/dist/core/retry-fallback/usage-limit.d.ts +6 -1
  158. package/dist/core/retry-fallback/usage-limit.js +12 -4
  159. package/dist/core/sdk.d.ts +6 -1
  160. package/dist/core/sdk.js +3 -0
  161. package/dist/core/session-log.js +1 -1
  162. package/dist/core/stall-warning.d.ts +41 -0
  163. package/dist/core/stall-warning.js +72 -0
  164. package/dist/core/terminal-settings.d.ts +1 -0
  165. package/dist/core/turn-origin.d.ts +31 -0
  166. package/dist/core/turn-origin.js +64 -0
  167. package/dist/modes/interactive/components/remote-delivery-message.d.ts +2 -1
  168. package/dist/modes/interactive/components/remote-delivery-message.js +13 -5
  169. package/dist/modes/rpc/admit-external-message-command.d.ts +37 -0
  170. package/dist/modes/rpc/admit-external-message-command.js +61 -0
  171. package/dist/modes/rpc/admit-external-message-wire.d.ts +42 -0
  172. package/dist/modes/rpc/admit-external-message-wire.js +55 -0
  173. package/dist/modes/rpc/client-input-handler.d.ts +2 -0
  174. package/dist/modes/rpc/client-input-handler.js +2 -0
  175. package/dist/modes/rpc/connection-handler.js +30 -5
  176. package/dist/modes/rpc/connection-roles.d.ts +6 -0
  177. package/dist/modes/rpc/connection-roles.js +13 -0
  178. package/dist/modes/rpc/delegated-drive-origin.d.ts +9 -0
  179. package/dist/modes/rpc/delegated-drive-origin.js +16 -0
  180. package/dist/modes/rpc/effect-commands.d.ts +7 -1
  181. package/dist/modes/rpc/host-bare-endpoint-registration.d.ts +7 -0
  182. package/dist/modes/rpc/host-bare-endpoint-registration.js +35 -0
  183. package/dist/modes/rpc/host-capabilities.d.ts +2 -0
  184. package/dist/modes/rpc/host-capabilities.js +3 -0
  185. package/dist/modes/rpc/host-handover-parked.d.ts +11 -0
  186. package/dist/modes/rpc/host-handover-parked.js +17 -0
  187. package/dist/modes/rpc/host-handover-reopen.js +18 -6
  188. package/dist/modes/rpc/host-handover-resident-writer.d.ts +16 -0
  189. package/dist/modes/rpc/host-handover-resident-writer.js +57 -0
  190. package/dist/modes/rpc/host-idle-handover.js +1 -0
  191. package/dist/modes/rpc/host-warm.d.ts +16 -1
  192. package/dist/modes/rpc/host-warm.js +33 -6
  193. package/dist/modes/rpc/method-classes-schema-delivery.d.ts +32 -0
  194. package/dist/modes/rpc/method-classes-schema-delivery.js +22 -0
  195. package/dist/modes/rpc/method-classes-schema-session.d.ts +36 -0
  196. package/dist/modes/rpc/method-classes-schema-session.js +3 -0
  197. package/dist/modes/rpc/method-classes-schema.d.ts +36 -0
  198. package/dist/modes/rpc/method-classes.d.ts +2 -0
  199. package/dist/modes/rpc/method-classes.js +4 -0
  200. package/dist/modes/rpc/multi-session-host.js +6 -1
  201. package/dist/modes/rpc/permission-preset-command.d.ts +6 -0
  202. package/dist/modes/rpc/permission-preset-command.js +27 -0
  203. package/dist/modes/rpc/rpc-input-validation.js +4 -3
  204. package/dist/modes/rpc/rpc-mutation-response-types.d.ts +7 -0
  205. package/dist/modes/rpc/rpc-response-types.d.ts +2 -1
  206. package/dist/modes/rpc/rpc-session-command-types.d.ts +6 -1
  207. package/dist/modes/rpc/rpc-session-event-types.d.ts +5 -2
  208. package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -1
  209. package/dist/modes/rpc/rpc-session-state.js +2 -0
  210. package/dist/modes/rpc/session-attachment-commands.js +3 -3
  211. package/dist/modes/rpc/session-event-writer-connections.js +1 -0
  212. package/dist/modes/rpc/session-event-writer.d.ts +1 -1
  213. package/dist/modes/rpc/session-registry-attach.d.ts +0 -9
  214. package/dist/modes/rpc/session-registry-attach.js +16 -3
  215. package/dist/modes/rpc/session-registry-types.d.ts +2 -0
  216. package/dist/modes/rpc/session-registry.d.ts +2 -0
  217. package/dist/modes/rpc/session-registry.js +16 -3
  218. package/dist/modes/rpc/session-router-close.js +43 -6
  219. package/dist/modes/rpc/session-router-context.js +2 -0
  220. package/dist/modes/rpc/session-router-occupancy.js +2 -11
  221. package/dist/modes/rpc/session-router-open-profile.js +4 -0
  222. package/dist/modes/rpc/session-router-open.d.ts +1 -1
  223. package/dist/modes/rpc/session-router-open.js +6 -17
  224. package/dist/modes/rpc/session-router-residency.d.ts +24 -0
  225. package/dist/modes/rpc/session-router-residency.js +90 -0
  226. package/dist/modes/rpc/session-router-routing.js +7 -1
  227. package/dist/modes/rpc/session-status.js +4 -1
  228. package/dist/modes/rpc/session-worker.js +4 -0
  229. package/dist/modes/rpc/tool-child/terminal-jobs.d.ts +1 -0
  230. package/dist/modes/rpc/tool-child/terminal-jobs.js +17 -1
  231. package/dist/modes/rpc/worker-session-registry.js +5 -2
  232. package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
  233. package/docs/engine-socket/fixtures/rpc-inbound-samples.json +29 -2
  234. package/docs/extensions.md +12 -0
  235. package/docs/rpc.md +129 -12
  236. package/docs/terminal-tools.md +25 -1
  237. package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
  238. package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
  239. package/package.json +6 -6
  240. package/dist/bundle/chunks/chunk-C7HEEBFH.js +0 -2
  241. package/dist/bundle/chunks/chunk-XCMII2VQ.js +0 -98
  242. package/dist/bundle/chunks/interactive-mode-B5SQDLH7.js +0 -2
  243. package/dist/bundle/chunks/main-UQFVFCNY.js +0 -2
  244. package/dist/bundle/chunks/rpc-mode-JYN3E7P7.js +0 -2
@@ -56,6 +56,7 @@ import { CompactionLifecycleCoordinator } from "./compaction/lifecycle.js";
56
56
  import { isTurnStuckOnContextOverflow } from "./compaction/stuck-overflow.js";
57
57
  import { isWarmSummaryAnchorValid } from "./compaction/warm-anchor.js";
58
58
  import { CONTINUE_FROM_LEAF_CUSTOM_TYPE, CONTINUE_FROM_LEAF_DIRECTIVE, ContinueFromLeafError, trackTurnAdmission, } from "./continue-from-leaf.js";
59
+ import { getCredentialAccounts } from "./credential-accounts.js";
59
60
  import { credentialRecoveryRevision } from "./credential-recovery-revision.js";
60
61
  import { admitCursorHistory, cursorAdmissionBudgetBytes } from "./cursor-history-admission.js";
61
62
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
@@ -95,6 +96,8 @@ import { deliveryIdOf, ExternalAdmission } from "./external-admission.js";
95
96
  import { HELD_INPUT_ENTRY, HeldInputs, heldInputHistory } from "./held-inputs.js";
96
97
  import { shouldWarnHighReasoning } from "./high-reasoning-warning.js";
97
98
  import { customTurnOrigin, inputAttributionOf, inputOriginOf } from "./input-origin.js";
99
+ import { copyInputTurnMark, hasInputTurnMark, inputTurnMark, markInputTurnOrigin, noteRunStart, noteUserMessageStart, } from "./turn-origin.js";
100
+ import { releaseDecisionPreset } from "./permission-decision.js";
98
101
  import { markInterrupted } from "./interrupted-tool-result.js";
99
102
  import { deliveredReceipts, itemNoticeMessage, QueuedDeliveryCleared, } from "./itemized-send.js";
100
103
  import { LazyToolActivation } from "./lazy-tool-activation.js";
@@ -134,7 +137,7 @@ import { createFallbackLogger } from "./retry-fallback/log.js";
134
137
  import { createModelConfigErrorReporter, isModelConfigFailure, } from "./retry-fallback/model-config-error.js";
135
138
  import { ProbeBackScheduler } from "./retry-fallback/probe-scheduler.js";
136
139
  import { REQUEST_SHAPE_FAILURE_TEXT, REQUEST_SHAPE_RECOVERY_ENTRY_TYPE, REQUEST_SHAPE_RECOVERY_NOTE, RequestShapeRecovery, } from "./retry-fallback/request-shape.js";
137
- import { AccountUsageLimitError, describeUsageLimitEntry, describeUsageLimitMessage, usageLimitScope, } from "./retry-fallback/usage-limit.js";
140
+ import { AccountUsageLimitError, formatResetTime, describeUsageLimitEntry, describeUsageLimitMessage, usageLimitScope, } from "./retry-fallback/usage-limit.js";
138
141
  import { recordRungFailure } from "./retry-fallback/exhaustion.js";
139
142
  import { limitResetAt } from "./retry-fallback/limit-reset.js";
140
143
  import { validateFallbackChains } from "./retry-fallback/validate.js";
@@ -156,6 +159,7 @@ import { formatSkillInvocationPrompt, formatSkillReferenceBody, parseSkillInvoca
156
159
  import { BUILTIN_SLASH_COMMANDS } from "./slash-commands.js";
157
160
  import { bootstrapRuntime, buildToolRegistry, isBuiltinExtensionPath, } from "./runtime/bootstrap.js";
158
161
  import { toolInfoOf } from "./tool-info.js";
162
+ import { PendingQueueLedger, queueEntryLabel, STALL_WARNING_MS, watchForStall, } from "./stall-warning.js";
159
163
  import { stopResumeDirective } from "./stop-resume-directive.js";
160
164
  import { StructuredSessionLane } from "./structured-session-lane.js";
161
165
  import { clampThinkingSelection, getSupportedThinkingLevels, getThinkingClampNotice, supportsMax, supportsXhigh, } from "./thinking-levels.js";
@@ -436,6 +440,8 @@ export class AgentSession {
436
440
  this._unregisterMemoryReport = () => { };
437
441
  this._eventListeners = [];
438
442
  this._agentEventQueue = Promise.resolve();
443
+ /** What `_agentEventQueue` still holds, so a tool call stalled behind it can say what it waits on (senpi#6189). */
444
+ this._agentEventQueueLedger = new PendingQueueLedger();
439
445
  this._lifecycleEvents = new LifecycleEventSequencer();
440
446
  /**
441
447
  * Exact message objects whose message_end persistence is still queued.
@@ -669,7 +675,10 @@ export class AgentSession {
669
675
  if (event.type === "agent_start") {
670
676
  this._lifecycleBinding.startRun();
671
677
  this._requestSelectedModel = undefined;
678
+ noteRunStart(this.sessionManager);
672
679
  }
680
+ if (event.type === "message_start" && (event.message.role === "user" || hasInputTurnMark(event.message)))
681
+ noteUserMessageStart(this.sessionManager, event.message);
673
682
  // Bind now: the core can start another run before this event's asynchronous delivery.
674
683
  const bound = event.type === "agent_start" ? { ...event, runId: this._lifecycleBinding.controller.ledger.runId } : event;
675
684
  queuedEvent = bound;
@@ -758,7 +767,8 @@ export class AgentSession {
758
767
  this._lifecycleEvents.release(heldLifecycle, (held) => this._emit(held));
759
768
  return this._processAgentEvent(queuedEvent, signal);
760
769
  };
761
- const processing = this._agentEventQueue.then(deliverThenProcess, deliverThenProcess);
770
+ const tracked = this._agentEventQueueLedger.track(queueEntryLabel(queuedEvent), deliverThenProcess);
771
+ const processing = this._agentEventQueue.then(tracked, tracked);
762
772
  this._agentEventQueue =
763
773
  pendingEnd !== undefined
764
774
  ? processing.finally(() => {
@@ -780,7 +790,8 @@ export class AgentSession {
780
790
  this._structuredLane = new StructuredSessionLane(config.internalChannel, (deliver) => {
781
791
  if (!this._unsubscribeAgent)
782
792
  return;
783
- this._agentEventQueue = this._agentEventQueue.then(deliver, deliver);
793
+ const tracked = this._agentEventQueueLedger.track("structured_record", deliver);
794
+ this._agentEventQueue = this._agentEventQueue.then(tracked, tracked);
784
795
  }, (control) => this._recoveryIdentity?.own(control));
785
796
  this.sessionManager = config.sessionManager;
786
797
  this._unsubscribeRecovery = this.sessionManager.onRecovery((recovery) => {
@@ -1147,7 +1158,7 @@ export class AgentSession {
1147
1158
  this._modelRuntime.hasConfiguredAuth(model.provider));
1148
1159
  if (available)
1149
1160
  return available;
1150
- throw new AccountUsageLimitError(sessionModel.provider, this._retryFallback.accountError(sessionModel.provider), this._retryFallback.accountResetAt(sessionModel.provider));
1161
+ throw new AccountUsageLimitError(sessionModel.provider, this._retryFallback.accountError(sessionModel.provider), this._retryFallback.accountResetAt(sessionModel.provider), this._retryFallback.widenedFamiliesOf(sessionModel.provider));
1151
1162
  }
1152
1163
  _logCompactionModelFallback(override, reason, sessionModel) {
1153
1164
  const notice = `${override}\u0000${reason}`;
@@ -1290,33 +1301,56 @@ export class AgentSession {
1290
1301
  };
1291
1302
  }
1292
1303
  async preflightToolCall(toolCall, args, options = {}) {
1304
+ // Neither wait below has a deadline of its own: one that never settles is logged once, never cut short (senpi#6189).
1305
+ // The call's own `timeout` bounds its execution, not this phase, which includes a human permission decision.
1306
+ const thresholdMs = STALL_WARNING_MS;
1307
+ const warnStalled = (event, elapsedMs, queue) => this._sessionLogger.warn(event, {
1308
+ toolCallId: toolCall.id,
1309
+ toolName: toolCall.name,
1310
+ ...(options.parentToolCallId ? { parentToolCallId: options.parentToolCallId } : {}),
1311
+ thresholdMs,
1312
+ elapsedMs,
1313
+ ...queue,
1314
+ });
1293
1315
  if (options.waitForEventQueue !== false) {
1294
- await this._agentEventQueue;
1295
- }
1296
- const signal = options.signal ?? this.agent.signal;
1297
- // A call released by Stop never runs: block it without asking its hooks (a permission prompt) about it.
1298
- // Callers such as executeTool only honor an explicit block, so returning nothing here would let it run.
1299
- if ((await this._waitForToolBarrier(toolCall, signal)) && signal?.aborted) {
1300
- return { block: true, reason: "Operation aborted" };
1301
- }
1302
- const runner = this._extensionRunner;
1303
- if (!runner.hasHandlers("tool_call")) {
1304
- return undefined;
1316
+ const queueWatch = watchForStall(thresholdMs, (elapsedMs) => warnStalled("tool_call_event_queue_stalled", elapsedMs, this._agentEventQueueLedger.snapshot()));
1317
+ try {
1318
+ await this._agentEventQueue;
1319
+ }
1320
+ finally {
1321
+ queueWatch.stop();
1322
+ }
1305
1323
  }
1324
+ const preflightWatch = watchForStall(thresholdMs, (elapsedMs) => warnStalled("tool_call_preflight_stalled", elapsedMs));
1306
1325
  try {
1307
- return await runner.emitToolCall({
1308
- type: "tool_call",
1309
- toolName: toolCall.name,
1310
- toolCallId: toolCall.id,
1311
- ...(options.parentToolCallId ? { parentToolCallId: options.parentToolCallId } : {}),
1312
- input: args,
1313
- });
1314
- }
1315
- catch (err) {
1316
- if (err instanceof Error) {
1317
- throw err;
1326
+ const signal = options.signal ?? this.agent.signal;
1327
+ // A call released by Stop never runs: block it without asking its hooks (a permission prompt) about it.
1328
+ // Callers such as executeTool only honor an explicit block, so returning nothing here would let it run.
1329
+ if ((await this._waitForToolBarrier(toolCall, signal)) && signal?.aborted) {
1330
+ return { block: true, reason: "Operation aborted" };
1331
+ }
1332
+ const runner = this._extensionRunner;
1333
+ if (!runner.hasHandlers("tool_call")) {
1334
+ return undefined;
1318
1335
  }
1319
- throw new Error(`Extension failed, blocking execution: ${String(err)}`);
1336
+ try {
1337
+ return await runner.emitToolCall({
1338
+ type: "tool_call",
1339
+ toolName: toolCall.name,
1340
+ toolCallId: toolCall.id,
1341
+ ...(options.parentToolCallId ? { parentToolCallId: options.parentToolCallId } : {}),
1342
+ input: args,
1343
+ });
1344
+ }
1345
+ catch (err) {
1346
+ if (err instanceof Error) {
1347
+ throw err;
1348
+ }
1349
+ throw new Error(`Extension failed, blocking execution: ${String(err)}`);
1350
+ }
1351
+ }
1352
+ finally {
1353
+ preflightWatch.stop();
1320
1354
  }
1321
1355
  }
1322
1356
  async _emitAfterToolCallHooks(toolCall, args, result, isError, parentToolCallId) {
@@ -3448,6 +3482,7 @@ export class AgentSession {
3448
3482
  */
3449
3483
  dispose() {
3450
3484
  this._disposed = true;
3485
+ releaseDecisionPreset(this.sessionManager);
3451
3486
  this._unsubscribeSecurityNotices?.();
3452
3487
  this._unfollowProviderAccountChanges();
3453
3488
  this.securityNotices.dispose();
@@ -3717,6 +3752,19 @@ export class AgentSession {
3717
3752
  _warnDeadline(event, detail) {
3718
3753
  this._sessionLogger.warn(event, { detail });
3719
3754
  }
3755
+ /**
3756
+ * End the running turn because the process hosting this session is stopping (an RPC host shutting down or
3757
+ * restarting): a system abort whose `agent_end` carries `abortCause: { kind: "host_shutdown" }`, never a user abort.
3758
+ */
3759
+ async abortForHostShutdown() {
3760
+ if (!this.isStreaming)
3761
+ return;
3762
+ if (this._abortCause === undefined)
3763
+ this._abortCause = { kind: "host_shutdown" };
3764
+ await this._abortActiveAgentAndRetry("system");
3765
+ if (!this.isIdle)
3766
+ await this._waitForIdleAfterAbort();
3767
+ }
3720
3768
  /** Adapter for the deadline owner interface: end the turn through the normal system abort. */
3721
3769
  async abortTurnForDeadline() {
3722
3770
  await this._abortActiveAgentAndRetry("system");
@@ -4302,6 +4350,42 @@ export class AgentSession {
4302
4350
  * @throws Error if streaming and no streamingBehavior specified
4303
4351
  * @throws Error if no model selected or no API key available (when not streaming)
4304
4352
  */
4353
+ /**
4354
+ * A provider whose stored credential pools two or more accounts: whether any of them can serve `modelId` now
4355
+ * (no live block, and no live block on that model), and the accounts with why each is out. A cooling account is
4356
+ * not usable until its cooldown ends, so an automated loop never probes a spent pool turn after turn (senpi#3131).
4357
+ * Undefined for a single-credential provider, or when pool health cannot be read (the hold then stays).
4358
+ * Reads pool health only, never key material.
4359
+ */
4360
+ async _pooledAccountHealth(provider, modelId) {
4361
+ const storage = this._modelRuntime.authStorage;
4362
+ if (storage === undefined)
4363
+ return undefined;
4364
+ let summaries;
4365
+ try {
4366
+ summaries = await getCredentialAccounts(storage, provider);
4367
+ }
4368
+ catch {
4369
+ return undefined;
4370
+ }
4371
+ const accounts = summaries.filter((account) => account.unused !== true);
4372
+ if (accounts.length < 2)
4373
+ return undefined;
4374
+ const now = Date.now();
4375
+ const blocksModel = (blocked) => blocked.until > now &&
4376
+ (blocked.model === modelId ||
4377
+ new RegExp(`(?:^|[^a-z])${blocked.model}(?:[^a-z]|$)`, "i").test(modelId));
4378
+ const usable = accounts.some((account) => !account.blocked && !(account.blockedModels ?? []).some(blocksModel));
4379
+ const accountsOut = accounts
4380
+ .map((account) => {
4381
+ const reason = account.blockedReason ?? "blocked";
4382
+ return account.blockedUntil === undefined
4383
+ ? `${account.name} (${reason})`
4384
+ : `${account.name} (${reason} until ${formatResetTime(account.blockedUntil)})`;
4385
+ })
4386
+ .join(", ");
4387
+ return { usable, accountsOut };
4388
+ }
4305
4389
  async prompt(text, options) {
4306
4390
  this._standingRecovery = undefined;
4307
4391
  const barrierError = options?.toolBarrier === undefined ? undefined : toolBarrierSpecError(options.toolBarrier);
@@ -4586,6 +4670,13 @@ export class AgentSession {
4586
4670
  const model = this.model;
4587
4671
  // A turn the user started asks the provider again unless it stated when the limit resets (senpi#5568);
4588
4672
  // automated turns keep the remembered hold so a loop never hammers a spent account (senpi#3131).
4673
+ // A pooled provider is out only when no account can serve: one account's billing or limit is that
4674
+ // account's, and the pool's own block already routes around it (senpi#5590).
4675
+ const pool = model && !this._pendingModelSwitch && this._retryFallback.isProviderSpent(model.provider)
4676
+ ? await this._pooledAccountHealth(model.provider, model.id)
4677
+ : undefined;
4678
+ if (model && pool?.usable)
4679
+ this._retryFallback.releaseAccountHold(model.provider);
4589
4680
  if (model &&
4590
4681
  !this._pendingModelSwitch &&
4591
4682
  this._retryFallback.isProviderSpent(model.provider) &&
@@ -4595,7 +4686,21 @@ export class AgentSession {
4595
4686
  const errorMessage = this._retryFallback.accountError(model.provider);
4596
4687
  // Routing around a remembered failure did not observe another provider failure.
4597
4688
  if (!(await this._retryFallback.tryFallback("transient", { errorMessage }, false))) {
4598
- throw new AccountUsageLimitError(model.provider, errorMessage, this._retryFallback.accountResetAt(model.provider));
4689
+ throw new AccountUsageLimitError(model.provider, errorMessage, this._retryFallback.accountResetAt(model.provider), this._retryFallback.widenedFamiliesOf(model.provider), pool?.accountsOut);
4690
+ }
4691
+ }
4692
+ // A family held by a weekly limit that named no family binds automated turns the same way, so a loop
4693
+ // moves to another family or stops instead of hammering the held one (senpi#5596, senpi#3131).
4694
+ const heldFamily = this.model && !this._pendingModelSwitch && attributionOf(options) !== "user"
4695
+ ? this._retryFallback.heldFamily(this.model)
4696
+ : undefined;
4697
+ if (this.model && heldFamily) {
4698
+ this._retryFallback.resetTurn();
4699
+ this._fallbackTurnFailures = [];
4700
+ if (!(await this._retryFallback.tryFallback("transient", { errorMessage: heldFamily.errorMessage }, false))) {
4701
+ throw new AccountUsageLimitError(this.model.provider, heldFamily.errorMessage, undefined, [
4702
+ heldFamily.family,
4703
+ ]);
4599
4704
  }
4600
4705
  }
4601
4706
  }
@@ -4890,6 +4995,7 @@ export class AgentSession {
4890
4995
  ...clientMessageIdentity(options),
4891
4996
  };
4892
4997
  this._userInputSources.set(userMessage, options?.source ?? "interactive");
4998
+ markInputTurnOrigin(userMessage, inputTurnMark(options ?? {}));
4893
4999
  messages.push(userMessage);
4894
5000
  // Consume next-turn messages transactionally: a final admission rejection
4895
5001
  // restores them in their original order rather than dropping or duplicating
@@ -5402,6 +5508,9 @@ export class AgentSession {
5402
5508
  // stays unstamped: unknown origin, never defaulted to a trusted source.
5403
5509
  if (input.source !== undefined)
5404
5510
  this._userInputSources.set(message, input.source);
5511
+ // Restored input with no source keeps no mark: an unmarked turn is engine work, never a trusted origin.
5512
+ if (input.source !== undefined)
5513
+ markInputTurnOrigin(message, inputTurnMark(input));
5405
5514
  if (input.inputId !== undefined)
5406
5515
  this._queuedInputDispositions.track(message, input.inputId);
5407
5516
  this._queuedMessages.set(queuedId, message);
@@ -5575,6 +5684,7 @@ export class AgentSession {
5575
5684
  async sendCustomMessage(message, options, deferredTurnClaim, releasedWake, itemized, onQueued) {
5576
5685
  // Normalize the legacy untyped content boundary before persisting a retry payload.
5577
5686
  const deliveryMessage = { ...message, content: message.content ?? [] };
5687
+ copyInputTurnMark(message, deliveryMessage);
5578
5688
  if (this._sizesOnlyStartup && options?.triggerTurn === true) {
5579
5689
  // Size inspection makes no model call (#3483) and owes no work: no ticket, custody record or session file.
5580
5690
  this._pendingNextTurnMessages.push({ role: "custom", ...deliveryMessage, timestamp: Date.now() });
@@ -5789,6 +5899,8 @@ export class AgentSession {
5789
5899
  : message.details,
5790
5900
  timestamp: Date.now(),
5791
5901
  };
5902
+ // A delivery that found a run already streaming becomes a steer or follow-up: it keeps the origin it was admitted with.
5903
+ copyInputTurnMark(message, appMessage);
5792
5904
  if (this._sizesOnlyStartup && options?.triggerTurn === true) {
5793
5905
  // Size inspection makes no model call (#3483): keep the message for a turn that never starts.
5794
5906
  this._pendingNextTurnMessages.push(appMessage);
@@ -6376,7 +6488,8 @@ export class AgentSession {
6376
6488
  return id;
6377
6489
  },
6378
6490
  emit: (event) => this._lifecycleEvents.emit(event, (lifecycle) => this._emit(lifecycle), (run) => {
6379
- this._agentEventQueue = this._agentEventQueue.then(run, run);
6491
+ const tracked = this._agentEventQueueLedger.track(queueEntryLabel(event), run);
6492
+ this._agentEventQueue = this._agentEventQueue.then(tracked, tracked);
6380
6493
  void this._agentEventQueue.catch(() => { });
6381
6494
  }),
6382
6495
  holdInterruptedRun: () => {
@@ -1,11 +1,11 @@
1
1
  import type { Credential } from "@earendil-works/pi-ai";
2
2
  import { type ConfigValue } from "@earendil-works/pi-ai/utils/config-reference";
3
+ import type { PoolState } from "../egress/pool-state.ts";
3
4
  import type { RunSlot } from "./failover.ts";
4
5
  import type { AccountStrategy } from "./selection-order.ts";
5
- import { type CredentialSlotRepository } from "./state-store.ts";
6
6
  export type RotationLane = "stored" | "env";
7
7
  /** Hands a half-open probe lease back, if the slot still holds that exact lease. */
8
- export declare function releaseProbeLease(repository: CredentialSlotRepository, providerId: string, lane: RotationLane, slotName: string, leaseId: string): Promise<void>;
8
+ export declare function releaseProbeLease(repository: PoolState, providerId: string, lane: RotationLane, slotName: string, leaseId: string): Promise<void>;
9
9
  export type RotationSlot = RunSlot & {
10
10
  lane: RotationLane;
11
11
  /** Env-lane key material for the attempt; never serialized or persisted. */
@@ -25,7 +25,7 @@ export type RotationSources = {
25
25
  providerId: string;
26
26
  credential: Credential | undefined;
27
27
  env: (name: string) => string | undefined;
28
- repository: CredentialSlotRepository;
28
+ repository: PoolState;
29
29
  policy?: {
30
30
  affinity?: boolean;
31
31
  accountStrategy?: AccountStrategy;
@@ -149,7 +149,7 @@ export type HalfOpenLease = {
149
149
  * wins the probe lease; everyone else keeps treating the slot as unavailable
150
150
  * until the lease expires or the probe settles the block.
151
151
  */
152
- export declare function acquireHalfOpenLease(repository: CredentialSlotRepository, providerId: string, laneId: string, slotId: string, options?: {
152
+ export declare function acquireHalfOpenLease(repository: Pick<CredentialSlotRepository, "mutateSlotState">, providerId: string, laneId: string, slotId: string, options?: {
153
153
  now?: number;
154
154
  leaseTtlMs?: number;
155
155
  }): Promise<HalfOpenLease | undefined>;
@@ -12,8 +12,13 @@ export type FieldClass = "public" | "sealed" | "device-only" | "endpoint" | "for
12
12
  export type Custody = "sealed" | "device";
13
13
  export type ProviderCapability = "cloud" | "local-only" | "ambient-credential" | "callback-stream";
14
14
  /** models.json, keyed by schema path: `.` nests, `[]` is an array item, `.{key}` a record key, `.*` a record value. */
15
- export declare const MODELS_JSON_FIELD_CLASSES: Readonly<Record<string, FieldClass>>;
15
+ export declare function modelsJsonFieldClasses(): Readonly<Record<string, FieldClass>>;
16
16
  /** Fields the runtime types add on top of the models.json schema (`model-config.ts`). */
17
- export declare const MODELS_JSON_TYPE_ONLY_FIELDS: Readonly<Record<string, FieldClass>>;
18
- export declare function classifyModelsJsonField(path: string): FieldClass | undefined;
17
+ export declare const MODELS_JSON_TYPE_ONLY_FIELDS: {
18
+ readonly "providers.*.models[].samplingParams.{key}": "public";
19
+ readonly "providers.*.models[].samplingParams.*": "forces-device";
20
+ readonly "providers.*.modelOverrides.*.samplingParams.{key}": "public";
21
+ readonly "providers.*.modelOverrides.*.samplingParams.*": "forces-device";
22
+ };
23
+ export declare function classifyModelsJsonField(path: string, classes?: Readonly<Record<string, FieldClass>>): FieldClass | undefined;
19
24
  //# sourceMappingURL=classify.d.ts.map