@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
@@ -10,6 +10,7 @@ import { SessionAttachOrder } from "./session-attach-order.js";
10
10
  import { tearDownReleased } from "./session-router-close.js";
11
11
  import { routingIdOf } from "./session-router-errors.js";
12
12
  import { sweepIdleSessions } from "./session-router-occupancy.js";
13
+ import { followRetainedRecord } from "./session-router-residency.js";
13
14
  import { registryStatusSource } from "./session-status-source.js";
14
15
  export class RouterContext {
15
16
  constructor(registry, writer, defaults, createBinding = createRpcSessionBinding, connectionOptions = {}, idle) {
@@ -74,6 +75,7 @@ export class RouterContext {
74
75
  : { effectId: latest.effectId, kind: latest.kind, state: latest.state };
75
76
  });
76
77
  writer.afterRecord = (sessionId) => {
78
+ followRetainedRecord(this, sessionId);
77
79
  const session = this.registry.peek(sessionId)?.runtime?.session;
78
80
  if (session === undefined)
79
81
  return;
@@ -1,10 +1,8 @@
1
- import { recordHandoverParkedSession } from "./host-handover-parked.js";
2
- import { parkedEntryFromProfile } from "./host-handover-parked-open.js";
3
- import { hostInstanceId } from "./protocol-identity.js";
4
1
  import { readDrainVerdicts } from "./session-drain.js";
5
2
  import { holdUnattendedTurns } from "./session-drain-hold.js";
6
3
  import { forgetSessionOwnership } from "./session-router-attachments.js";
7
4
  import { finalizeClose, tryClaimClose } from "./session-router-close.js";
5
+ import { recordRetainedSession } from "./session-router-residency.js";
8
6
  import { selectSweepEvictions } from "./session-sweep.js";
9
7
  const DRAIN_SWEEP_MS = 50;
10
8
  /**
@@ -128,14 +126,7 @@ export async function evictIdleSession(host, sessionId, reason) {
128
126
  parkedPath !== undefined &&
129
127
  retained?.retainOnDisconnect === true &&
130
128
  retained.sessionPath !== undefined) {
131
- await recordHandoverParkedSession(host.handoverParkedDir, {
132
- ...parkedEntryFromProfile({
133
- sessionPath: retained.sessionPath,
134
- contextOverlay: retained.contextOverlay,
135
- ...(retained.durableSessionId !== undefined ? { durableSessionId: retained.durableSessionId } : {}),
136
- }, retained.profile, Object.keys(host.defaults.hostContext ?? {})),
137
- parkedBy: host.defaults.hostContext?.host_instance ?? hostInstanceId(),
138
- }).catch((cause) => {
129
+ await recordRetainedSession(host, retained, retained.sessionPath, false).catch((cause) => {
139
130
  process.stderr.write(`senpi rpc handover park record ${sessionId} failed: ${String(cause)}\n`);
140
131
  });
141
132
  }
@@ -70,6 +70,10 @@ export function validatedOpenProfile(host, command, options) {
70
70
  : { host_socket: host.defaults.hostContext.host_socket }),
71
71
  },
72
72
  permissionPreset: command.permissionPreset ?? defaults?.permissionPreset,
73
+ // An embedder's host-level preset is the host default, never a client's choice (remote approval-mode rule).
74
+ ...(command.permissionPreset === undefined && defaults?.permissionPreset !== undefined
75
+ ? { permissionPresetSource: "host_default" }
76
+ : {}),
73
77
  creationModel: command.provider && command.modelId
74
78
  ? { provider: command.provider, modelId: command.modelId }
75
79
  : defaults?.creationModel,
@@ -1,5 +1,5 @@
1
1
  import { type OpenAcceptance } from "./compact-open-admission.ts";
2
- import { type HandoverParkedEntry } from "./host-handover-parked.ts";
2
+ import type { HandoverParkedEntry } from "./host-handover-parked.ts";
3
3
  import type { RpcCommand, RpcResponse } from "./rpc-types.ts";
4
4
  import type { RouterContext } from "./session-router-context.ts";
5
5
  export declare function openWithBarrier(host: RouterContext, command: Extract<RpcCommand, {
@@ -1,10 +1,10 @@
1
1
  import { buildRpcSessionState } from "./connection-handler.js";
2
2
  import { compactUpdatesEnabled } from "../../core/engine-socket-flag.js";
3
3
  import { startOpenTimer } from "../../core/open-timing.js";
4
+ import { openReplyPermissionMode } from "../../core/permission-mode.js";
4
5
  import { compactOpen } from "./compact-open-admission.js";
5
6
  import { CompactAdmissionError } from "./compact-admission-error.js";
6
7
  import { runAdmissionProducer } from "./compact-admission-producer.js";
7
- import { clearHandoverParkedSession } from "./host-handover-parked.js";
8
8
  import { parkedOpenCommand } from "./host-handover-parked-open.js";
9
9
  import { sessionHeldCheck } from "./session-held.js";
10
10
  import { forgetSessionOwnership, releaseOwnedSession, releaseOwnerAttachment } from "./session-router-attachments.js";
@@ -13,6 +13,7 @@ import { code, detail, error } from "./session-router-errors.js";
13
13
  import { sweepDrain } from "./session-router-occupancy.js";
14
14
  import { invalidSpawnedBy, validatedOpenProfile } from "./session-router-open-profile.js";
15
15
  import { relocationConnection } from "./session-router-relocation.js";
16
+ import { syncRetainedRecord } from "./session-router-residency.js";
16
17
  import { liveToolMediaScope, toolMediaPersister } from "./tool-media-store.js";
17
18
  export function openWithBarrier(host, command) {
18
19
  if (compactUpdatesEnabled() && command.admissionId !== undefined)
@@ -48,22 +49,6 @@ function openPhysicalWithBarrier(host, command, accept, signal) {
48
49
  // A late open's physical session is released by its admission (the accept/rollback path, 4272-admission-
49
50
  // abandonment), so the late response itself owns nothing to release here.
50
51
  const opened = signal === undefined ? start() : runAdmissionProducer(signal, start, async () => { });
51
- // A manual reopen-by-path supersedes the handover auto-reopen for that session: clear its
52
- // record entry, or a later generation would reopen a second copy (MEDIUM-1, senpi#3282 review).
53
- // Cleared only after the open SUCCEEDS (MEDIUM-A): if the open fails - a session_path_in_use
54
- // during a handover, or a bad profile - the entry must stay so a later generation can still
55
- // reopen the session. open() resolves `undefined` on success (the response is written through
56
- // the writer) and an RpcResponse only on failure, so success is `response === undefined`.
57
- if (command.sessionPath !== undefined && host.handoverParkedDir !== undefined) {
58
- const dir = host.handoverParkedDir;
59
- const sessionPath = command.sessionPath;
60
- void opened.then((response) => {
61
- if (response === undefined)
62
- void clearHandoverParkedSession(dir, sessionPath).catch(() => { });
63
- }, () => {
64
- // A rejection means the open failed before producing a response; the entry stays.
65
- });
66
- }
67
52
  return opened.finally(() => {
68
53
  resolveBarrier();
69
54
  opens.delete(barrier);
@@ -214,6 +199,9 @@ export async function open(host, command, owner, options) {
214
199
  releaseOwnerAttachment(host, owner, openedSession.sessionId);
215
200
  await releaseOwnedSession(host, openedSession.sessionId);
216
201
  }
202
+ // After the open SUCCEEDS (MEDIUM-A): a failed open keeps any record entry, so a later start
203
+ // still reopens that session.
204
+ await syncRetainedRecord(host, openedSession.sessionId, command.sessionPath);
217
205
  if (options?.accept) {
218
206
  return undefined;
219
207
  }
@@ -230,6 +218,7 @@ export async function open(host, command, owner, options) {
230
218
  data: {
231
219
  sessionId: opened.sessionId,
232
220
  state: replyState,
221
+ ...openReplyPermissionMode(replyState),
233
222
  ...(opened.attached ? { attached: true } : {}),
234
223
  ...(host.defaults.sessionPresence
235
224
  ? { presence: host.defaults.sessionPresence.view(replyState.sessionId) }
@@ -0,0 +1,24 @@
1
+ import type { RouterContext } from "./session-router-context.ts";
2
+ import type { RpcSessionEntry } from "./session-registry-types.ts";
3
+ /**
4
+ * Writes the record entry of a retained session under `sessionPath`, stamped with this host instance, and drops
5
+ * the entry it was recorded under before when the session has since moved to another transcript (senpi#6190).
6
+ * `resident` stamps the writing process: the session is open here. A park writes it unstamped.
7
+ */
8
+ export declare function recordRetainedSession(host: RouterContext, retained: RpcSessionEntry, sessionPath: string, resident: boolean): Promise<void>;
9
+ /**
10
+ * Brings the record in line with a session this host just opened or attached: a retained session is
11
+ * recorded as resident here, so a restart reopens it; a non-retained open by path supersedes any
12
+ * earlier park of that path, whose entry is cleared so no later start reopens a second copy
13
+ * (MEDIUM-1, senpi#3282 review). Runs only after the open succeeded, so a failed open keeps the entry.
14
+ */
15
+ export declare function syncRetainedRecord(host: RouterContext, sessionId: string, requestedPath: string | undefined): Promise<void>;
16
+ /**
17
+ * Called after every record a session publishes: a retained session whose transcript was switched (`new_session`,
18
+ * `switch_session`, a fork) moves its record entry to the new file at once, so a crash restores the transcript the
19
+ * session is on, never the one it left. A string compare until the path actually moves.
20
+ */
21
+ export declare function followRetainedRecord(host: RouterContext, sessionId: string): void;
22
+ /** An explicit close or reclaim ends the session for good: drop every key its record was written under. */
23
+ export declare function clearRetainedRecord(host: RouterContext, entry: RpcSessionEntry | undefined): Promise<void>;
24
+ //# sourceMappingURL=session-router-residency.d.ts.map
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The retained-session record a host keeps for the NEXT start of its endpoint: one per-session
3
+ * entry in the handover-parked record (`host-handover-parked.ts`), written when a session becomes
4
+ * retained-and-resident here, moved when the session switches to another transcript, and rewritten
5
+ * when it parks. A restart on the same socket and agent dir - after a handoff, a crash, or a kill
6
+ * mid-turn - reopens every entry a previous process wrote by its session file, the way a TUI resumes
7
+ * from its session file, so `list_sessions` (and the relay that mirrors it) lists the session again
8
+ * without anyone re-opening it by hand.
9
+ */
10
+ import { clearHandoverParkedSession, recordHandoverParkedSession } from "./host-handover-parked.js";
11
+ import { parkedEntryFromProfile } from "./host-handover-parked-open.js";
12
+ import { currentResidentWriter } from "./host-handover-resident-writer.js";
13
+ import { hostInstanceId } from "./protocol-identity.js";
14
+ const reportRecordFailure = (sessionId) => (cause) => {
15
+ process.stderr.write(`senpi rpc retained session record ${sessionId} failed: ${String(cause)}\n`);
16
+ };
17
+ /**
18
+ * Writes the record entry of a retained session under `sessionPath`, stamped with this host instance, and drops
19
+ * the entry it was recorded under before when the session has since moved to another transcript (senpi#6190).
20
+ * `resident` stamps the writing process: the session is open here. A park writes it unstamped.
21
+ */
22
+ export async function recordRetainedSession(host, retained, sessionPath, resident) {
23
+ const dir = host.handoverParkedDir;
24
+ if (dir === undefined)
25
+ return;
26
+ const previous = retained.recordedPath;
27
+ retained.recordedPath = sessionPath;
28
+ if (previous !== undefined && previous !== sessionPath)
29
+ await clearHandoverParkedSession(dir, previous);
30
+ const durableSessionId = retained.runtime?.session.sessionId ?? retained.durableSessionId;
31
+ await recordHandoverParkedSession(dir, {
32
+ ...parkedEntryFromProfile({
33
+ sessionPath,
34
+ contextOverlay: retained.contextOverlay,
35
+ ...(durableSessionId !== undefined ? { durableSessionId } : {}),
36
+ }, retained.profile, Object.keys(host.defaults.hostContext ?? {})),
37
+ parkedBy: host.defaults.hostContext?.host_instance ?? hostInstanceId(),
38
+ ...(resident ? { residentWriter: currentResidentWriter() } : {}),
39
+ });
40
+ }
41
+ /** The session path a live entry writes now; a replacement moves it before the registry's metadata catches up. */
42
+ const currentPath = (entry) => entry.runtime?.session.sessionFile ?? entry.sessionPath;
43
+ /**
44
+ * Brings the record in line with a session this host just opened or attached: a retained session is
45
+ * recorded as resident here, so a restart reopens it; a non-retained open by path supersedes any
46
+ * earlier park of that path, whose entry is cleared so no later start reopens a second copy
47
+ * (MEDIUM-1, senpi#3282 review). Runs only after the open succeeded, so a failed open keeps the entry.
48
+ */
49
+ export async function syncRetainedRecord(host, sessionId, requestedPath) {
50
+ const dir = host.handoverParkedDir;
51
+ if (dir === undefined)
52
+ return;
53
+ const entry = host.registry.peek(sessionId);
54
+ const sessionPath = entry === undefined ? undefined : currentPath(entry);
55
+ if (entry?.retainOnDisconnect === true && sessionPath !== undefined) {
56
+ await recordRetainedSession(host, entry, sessionPath, true).catch(reportRecordFailure(sessionId));
57
+ if (requestedPath === undefined || requestedPath === sessionPath)
58
+ return;
59
+ }
60
+ if (requestedPath !== undefined)
61
+ await clearHandoverParkedSession(dir, requestedPath).catch(() => { });
62
+ }
63
+ /**
64
+ * Called after every record a session publishes: a retained session whose transcript was switched (`new_session`,
65
+ * `switch_session`, a fork) moves its record entry to the new file at once, so a crash restores the transcript the
66
+ * session is on, never the one it left. A string compare until the path actually moves.
67
+ */
68
+ export function followRetainedRecord(host, sessionId) {
69
+ if (host.handoverParkedDir === undefined)
70
+ return;
71
+ const entry = host.registry.peek(sessionId);
72
+ if (entry?.retainOnDisconnect !== true || entry.recordedPath === undefined || entry.state !== "open")
73
+ return;
74
+ const sessionPath = currentPath(entry);
75
+ if (sessionPath === undefined || sessionPath === entry.recordedPath)
76
+ return;
77
+ void recordRetainedSession(host, entry, sessionPath, true).catch(reportRecordFailure(sessionId));
78
+ }
79
+ /** An explicit close or reclaim ends the session for good: drop every key its record was written under. */
80
+ export async function clearRetainedRecord(host, entry) {
81
+ const dir = host.handoverParkedDir;
82
+ if (dir === undefined || entry === undefined)
83
+ return;
84
+ const keys = new Set([entry.recordedPath, entry.sessionPath, entry.runtime?.session.sessionFile]);
85
+ entry.recordedPath = undefined;
86
+ for (const key of keys)
87
+ if (key !== undefined)
88
+ await clearHandoverParkedSession(dir, key).catch(() => { });
89
+ }
90
+ //# sourceMappingURL=session-router-residency.js.map
@@ -24,6 +24,7 @@ import { answerListSessions, answerProtocolInfo, answerSubscriptionLimits } from
24
24
  import { acknowledgePrompt, otherRequestsSettled } from "./session-router-observation.js";
25
25
  import { sweepDrain } from "./session-router-occupancy.js";
26
26
  import { openWithBarrier } from "./session-router-open.js";
27
+ import { syncRetainedRecord } from "./session-router-residency.js";
27
28
  import { answerSetSessionContext, answerSubscribeSessions, answerUnsubscribeSessions, notAttachedRefusal, } from "./session-status-commands.js";
28
29
  import { unloadSession } from "./session-unload.js";
29
30
  /**
@@ -245,7 +246,12 @@ export function setSessionContext(host, command) {
245
246
  return notAttachedRefusal(command);
246
247
  try {
247
248
  const entry = host.registry.getForCommand(command.sessionId, command.type);
248
- return answerSetSessionContext(command, entry, host.writer.status);
249
+ const answered = answerSetSessionContext(command, entry, host.writer.status);
250
+ // The record carries the overlay a restart reopens the session with (a terminal task_status
251
+ // is not reopened at all), so it follows each accepted patch.
252
+ if (answered.success)
253
+ void syncRetainedRecord(host, command.sessionId, undefined);
254
+ return answered;
249
255
  }
250
256
  catch (cause) {
251
257
  return error(command.id, command.type, code(cause), detail(cause));
@@ -57,7 +57,10 @@ export function deriveSessionStatus(entry, runtime, fanout, terminal, cause) {
57
57
  export function terminalOfLifecycle(record) {
58
58
  const reason = typeof record.reason === "string" ? record.reason : undefined;
59
59
  if (record.type === "session_parked")
60
- return { status: "unloaded", reason: reason === "unloaded" ? "unload_session" : "idle" };
60
+ return {
61
+ status: "unloaded",
62
+ reason: reason === "unloaded" ? "unload_session" : reason === "host_shutdown" ? "host_shutdown" : "idle",
63
+ };
61
64
  if (record.type !== "session_closed")
62
65
  return undefined;
63
66
  if (reason === "handoff_parked")
@@ -278,6 +278,8 @@ async function handle(message) {
278
278
  const bound = roleConnection(message.connection);
279
279
  if (message.delegation !== undefined && connectionRoles.retain(bound, message.delegation).kind === "conflict")
280
280
  throw new Error(DELEGATION_AUTHORITY_CONFLICT);
281
+ if (message.delegation !== undefined)
282
+ connectionRoles.noteCarriedDelegation(bound);
281
283
  if (message.roles !== undefined)
282
284
  connectionRoles.adopt(bound, message.roles, message.delegation?.incarnation);
283
285
  const bindingEntry = entry;
@@ -336,6 +338,8 @@ async function handle(message) {
336
338
  const resolved = message.delegation === undefined ? undefined : connectionRoles.retain(connection, message.delegation);
337
339
  if (resolved?.kind !== "conflict" && message.roles !== undefined)
338
340
  connectionRoles.adopt(connection, message.roles, message.delegation?.incarnation);
341
+ if (message.delegation !== undefined)
342
+ connectionRoles.noteCarriedDelegation(connection);
339
343
  if (message.origin !== undefined)
340
344
  connectionRoles.declareOrigin(connection, message.origin);
341
345
  const activeBinding = binding;
@@ -14,6 +14,7 @@ export interface TerminalJobArgs {
14
14
  readonly defaultRows: number;
15
15
  readonly timeoutAction?: TimeoutAction;
16
16
  readonly redactSecretsInOutput?: boolean;
17
+ readonly backgroundAutoReapMinutes?: number;
17
18
  }
18
19
  /**
19
20
  * The handlers. The child's one terminal manager (shared by every terminal job of this child) is made on the first job,
@@ -21,6 +21,7 @@ function parseArgs(value) {
21
21
  throw new ToolChildJobError("terminal_job_bad_args", "a terminal job needs cwd, toolCallId and env");
22
22
  const shellPath = field("shellPath");
23
23
  const timeoutAction = field("timeoutAction");
24
+ const autoReap = field("backgroundAutoReapMinutes");
24
25
  return {
25
26
  params: field("params"),
26
27
  toolCallId,
@@ -31,6 +32,7 @@ function parseArgs(value) {
31
32
  defaultRows: Number(field("defaultRows")) || 0,
32
33
  redactSecretsInOutput: field("redactSecretsInOutput") === true,
33
34
  ...(timeoutAction === "background" || timeoutAction === "kill" ? { timeoutAction } : {}),
35
+ ...(typeof autoReap === "number" && Number.isFinite(autoReap) ? { backgroundAutoReapMinutes: autoReap } : {}),
34
36
  };
35
37
  }
36
38
  function barrierFor(job) {
@@ -67,6 +69,9 @@ function exitSummary(id, runtime) {
67
69
  ? null
68
70
  : { exitCode: exit.exitCode, signal: exit.signal, cancelled: exit.cancelled, timedOut: exit.timedOut },
69
71
  output: output.length > NOTICE_TAIL_MAX_CHARS ? output.slice(output.length - NOTICE_TAIL_MAX_CHARS - 1) : output,
72
+ ...(runtime.autoReapedAfterMinutes === undefined
73
+ ? {}
74
+ : { autoReapedAfterMinutes: runtime.autoReapedAfterMinutes }),
70
75
  };
71
76
  }
72
77
  /**
@@ -87,8 +92,19 @@ export function createTerminalJobHandlers(injected) {
87
92
  const contextFor = (args, job) => ({
88
93
  manager: managerFor(job.generation),
89
94
  ...(args.timeoutAction === undefined ? {} : { timeoutAction: args.timeoutAction }),
90
- onBackgroundStart: (id, description, startedAtMs) => tell(job, { event: "background_start", id, description, startedAtMs }),
95
+ ...(args.backgroundAutoReapMinutes === undefined
96
+ ? {}
97
+ : { backgroundAutoReapMinutes: args.backgroundAutoReapMinutes }),
98
+ onBackgroundStart: (id, description, startedAtMs, command, autoReapMinutes) => tell(job, {
99
+ event: "background_start",
100
+ id,
101
+ description,
102
+ startedAtMs,
103
+ ...(command === undefined ? {} : { command }),
104
+ ...(autoReapMinutes === undefined ? {} : { autoReapMinutes }),
105
+ }),
91
106
  onBackgroundExit: (id, runtime) => tell(job, exitSummary(id, runtime)),
107
+ onBackgroundReapFailed: (id, autoReapMinutes, reason) => tell(job, { event: "background_reap_failed", id, autoReapMinutes, reason }),
92
108
  cwd: args.cwd,
93
109
  ...(args.shellPath === undefined ? {} : { shellPath: args.shellPath }),
94
110
  defaultCols: args.defaultCols,
@@ -1,3 +1,4 @@
1
+ import { clientNamedPreset, withClientPreset } from "../../core/permission-mode.js";
1
2
  import { outsideCommand } from "./command-authority.js";
2
3
  import { refreshesSessionActivity } from "./session-command-activity.js";
3
4
  import { publishedSessionContext } from "./session-context-patch.js";
@@ -163,7 +164,9 @@ export class WorkerSessionRegistry {
163
164
  return undefined;
164
165
  }
165
166
  async attach(owner, path, options, requested = { cwd: "" }) {
166
- const { promptSurface, browserEngine, permissionPreset } = requested;
167
+ const { promptSurface, browserEngine } = requested;
168
+ // A host-default preset folded into a preset-less attach is not a client choice (see session-registry-attach.ts).
169
+ const permissionPreset = clientNamedPreset(requested);
167
170
  const entry = this.entries.get(owner);
168
171
  if (entry?.state !== "open" || !entry.worker?.bindingReady || entry.worker.snapshot?.sessionPath !== path)
169
172
  throw new RpcSessionRegistryError("session_path_in_use");
@@ -178,7 +181,7 @@ export class WorkerSessionRegistry {
178
181
  }
179
182
  // Sent even when the record already names it: only the worker sees its live session (senpi#2842).
180
183
  if (permissionPreset !== undefined) {
181
- entry.profile = frozenProfile({ ...entry.profile, permissionPreset });
184
+ entry.profile = frozenProfile(withClientPreset(entry.profile, permissionPreset));
182
185
  await entry.worker.setPermissionPreset(permissionPreset);
183
186
  }
184
187
  const result = this.openResult(owner, entry);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "rpcClientRequests": "{\"type\":\"get_protocol_info\",\"id\":\"rpc-auth-protocol\",\"observe\":true,\"auth\":{\"client_nonce\":\"<public-nonce>\"}}\n{\"type\":\"set_client_info\",\"width\":100,\"capabilities\":[],\"id\":\"req_1\"}\n{\"type\":\"open_session\",\"cwd\":\"<root>/cwd\",\"sessionPath\":\"<root>/cwd/session.jsonl\",\"id\":\"req_2\"}\n{\"type\":\"get_entries\",\"sessionId\":\"rpc-1\",\"id\":\"req_3\"}\n{\"type\":\"close_session\",\"sessionId\":\"rpc-1\",\"id\":\"req_4\"}\n",
3
- "rpcClientFrames": "{\"id\":\"rpc-auth-protocol\",\"type\":\"response\",\"command\":\"get_protocol_info\",\"success\":true,\"data\":{\"protocolVersion\":1,\"serverVersion\":\"<server-version>\",\"capabilities\":[\"<capabilities>\"],\"mode\":\"multi\",\"instanceId\":\"<uuid-1>\",\"generation\":0,\"engineVersion\":\"<server-version>\",\"engineOrdinal\":\"<engine-ordinal>\",\"launch_profile\":{\"profile_id\":\"ca7a49435f02009fdda5dac49d29408dcfeb1ab5374353784c5a9def467f5be1\",\"core\":{\"extensions\":[],\"multi_session\":false,\"session_runtime\":\"worker\"}},\"agentDir\":\"<root>/agent\",\"method_classes\":{\"version\":1,\"digest\":\"3860136b2a1b07c20373ed361df07c045bd1e84fce001917e271ca0e4b903e63\",\"classes\":{\"get_protocol_info\":\"observe\",\"get_subscription_limits\":\"observe\",\"list_sessions\":\"observe\",\"list_hands\":\"observe\",\"get_state\":\"observe\",\"get_snapshot\":\"observe\",\"get_entries\":\"observe\",\"get_messages\":\"observe\",\"get_tree\":\"observe\",\"get_fork_messages\":\"observe\",\"get_last_assistant_text\":\"observe\",\"get_session_stats\":\"observe\",\"get_effect_status\":\"dynamic\",\"get_pending_inputs\":\"observe\",\"get_follow_up_messages\":\"observe\",\"get_steering_messages\":\"observe\",\"get_commands\":\"observe\",\"get_loaded_surfaces\":\"observe\",\"get_project_trust\":\"observe\",\"get_available_models\":\"observe\",\"get_available_thinking_levels\":\"observe\",\"get_model_thinking_level\":\"observe\",\"get_fast_mode\":\"observe\",\"get_media\":\"observe\",\"memory_report\":\"observe\",\"eval_cells.list\":\"observe\",\"eval_cells.peek\":\"observe\",\"prompt\":\"dynamic\",\"steer\":\"dynamic\",\"follow_up\":\"dynamic\",\"abort\":\"drive\",\"interrupt\":\"drive\",\"abort_retry\":\"drive\",\"abort_compaction\":\"drive\",\"abort_branch_summary\":\"drive\",\"compact\":\"drive\",\"clear_queue\":\"drive\",\"dequeue_message\":\"drive\",\"edit_queued_message\":\"drive\",\"hold_input\":\"drive\",\"withdraw_held_input\":\"drive\",\"append_user_message\":\"drive\",\"edit_user_message\":\"drive\",\"continue_from_leaf\":\"drive\",\"navigate_tree\":\"drive\",\"set_label\":\"drive\",\"set_session_name\":\"drive\",\"set_thinking_level\":\"dynamic\",\"set_model\":\"dynamic\",\"set_auto_compaction\":\"drive\",\"set_retry_fallback\":\"drive\",\"pause_for_run\":\"drive\",\"resume_for_run\":\"drive\",\"wake\":\"drive\",\"release_tool_barrier\":\"drive\",\"request_self_settle\":\"drive\",\"eval_cells.stop\":\"drive\",\"append_session_entry\":\"dynamic\",\"extension_ui_response\":\"answer\",\"extension_ui_progress\":\"answer\",\"new_session\":\"dynamic\",\"open_session\":\"dynamic\",\"resume_session\":\"thread\",\"fork\":\"thread\",\"clone\":\"thread\",\"close_session\":\"thread\",\"unload_session\":\"thread\",\"set_session_context\":\"thread\",\"attach_session\":\"thread\",\"select_hand\":\"device_admin\",\"bash\":\"device_admin\",\"abort_bash\":\"device_admin\",\"record_bash_result\":\"device_admin\",\"cleanup_bash_output\":\"device_admin\",\"extension_request\":\"device_admin\",\"reload\":\"device_admin\",\"check_reload_veto\":\"device_admin\",\"resolve_path\":\"device_admin\",\"login_start\":\"device_admin\",\"login_cancel\":\"device_admin\",\"login_api_key\":\"device_admin\",\"logout\":\"device_admin\",\"verify_api_key\":\"device_admin\",\"get_auth_providers\":\"device_admin\",\"get_provider_accounts\":\"device_admin\",\"account_order\":\"device_admin\",\"account_pin\":\"device_admin\",\"account_remove\":\"device_admin\",\"account_reset\":\"device_admin\",\"test_provider_account\":\"device_admin\",\"set_favorite_models\":\"device_admin\",\"set_scoped_models\":\"device_admin\",\"cycle_model\":\"device_admin\",\"cycle_thinking_level\":\"device_admin\",\"set_fast_mode\":\"device_admin\",\"set_steering_mode\":\"device_admin\",\"set_follow_up_mode\":\"device_admin\",\"set_auto_retry\":\"device_admin\",\"set_project_trust\":\"device_admin\",\"get_project_settings\":\"device_admin\",\"set_project_setting\":\"device_admin\",\"edit_assistant_message\":\"device_admin\",\"send_custom_message\":\"device_admin\",\"switch_session\":\"device_admin\",\"import_jsonl\":\"device_admin\",\"export_html\":\"device_admin\",\"export_jsonl\":\"device_admin\",\"set_client_info\":\"connection_local\",\"subscribe_sessions\":\"connection_local\",\"unsubscribe_sessions\":\"connection_local\",\"record_user_activity\":\"connection_local\",\"detach_connection\":\"connection_local\",\"release_session\":\"connection_local\",\"warm\":\"connection_local\",\"host_response\":\"connection_local\",\"release_attachment\":\"connection_local\",\"reclaim_session\":\"connection_local\",\"move_session\":\"connection_local\",\"get_recovery_page\":\"connection_local\",\"release_recovery\":\"connection_local\",\"cancel_attempt\":\"connection_local\",\"reset_stream\":\"connection_local\"},\"dynamic_versions\":{\"get_effect_status\":1,\"prompt\":1,\"steer\":1,\"follow_up\":1,\"set_thinking_level\":1,\"set_model\":1,\"append_session_entry\":1,\"new_session\":1,\"open_session\":2}},\"memory_pressure\":false,\"path_identity_resolver\":{\"state\":\"not_started\",\"detail\":null}}}\n{\"id\":\"req_1\",\"type\":\"response\",\"command\":\"set_client_info\",\"success\":true}\n{\"type\":\"queued\",\"for_request\":\"req_2\",\"position\":1,\"in_flight\":0}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-2>\",\"method\":\"setWidget\",\"widgetKey\":\"todo-sidebar\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-3>\",\"method\":\"setStatus\",\"statusKey\":\"anthropic-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-4>\",\"method\":\"setWidget\",\"widgetKey\":\"anthropic-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-5>\",\"method\":\"setStatus\",\"statusKey\":\"openai-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-6>\",\"method\":\"setWidget\",\"widgetKey\":\"openai-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-7>\",\"method\":\"setStatus\",\"statusKey\":\"monitors\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-8>\",\"method\":\"setStatus\",\"statusKey\":\"pi-websearch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-9>\",\"method\":\"setWidget\",\"widgetKey\":\"pi-websearch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-10>\",\"method\":\"setStatus\",\"statusKey\":\"pi-webfetch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-11>\",\"method\":\"setWidget\",\"widgetKey\":\"pi-webfetch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"entry_appended\",\"entry\":{\"type\":\"custom\",\"customType\":\"pi-rules.scan\",\"data\":{\"cwd\":\"<root>/cwd\",\"reason\":\"startup\"},\"startupBookkeeping\":true,\"id\":\"<entry-1>\",\"parentId\":\"<entry-2>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\"},\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-12>\",\"method\":\"setStatus\",\"statusKey\":\"todo-waiting\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-13>\",\"method\":\"setStatus\",\"statusKey\":\"goal\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"session_opened\",\"sessionId\":\"rpc-1\",\"durableSessionId\":\"<uuid-14>\",\"sessionPath\":\"<root>/cwd/session.jsonl\",\"kind\":\"interactive\",\"cwd\":\"<root>/cwd\"}\n{\"id\":\"req_2\",\"type\":\"response\",\"command\":\"open_session\",\"success\":true,\"data\":{\"sessionId\":\"rpc-1\",\"state\":{\"answeredUiRequests\":[],\"paused\":false,\"pausedNoticeCount\":0,\"holds\":[],\"lifecycle\":{\"state\":\"done\",\"seq\":0,\"epoch\":0,\"epochOrigin\":\"automatic\",\"humanInputRevision\":0,\"selfSettleEligible\":true,\"restoring\":false,\"persisted\":false,\"runPaused\":false,\"since\":\"2026-10-29T00:00:00.000Z\",\"role\":\"root\",\"rootSessionId\":\"<uuid-14>\",\"activityRevision\":{\"instanceId\":\"<uuid-15>\",\"revision\":0},\"waitingOn\":{\"children\":0,\"background\":[],\"human\":[]},\"holds\":[],\"observedAt\":\"2026-10-29T00:00:00.000Z\"},\"pendingQuestions\":[],\"pendingDialogs\":[],\"model\":{\"id\":\"faux-a\",\"name\":\"faux-a\",\"api\":\"faux:fixture\",\"provider\":\"faux\",\"baseUrl\":\"http://localhost:0\",\"reasoning\":false,\"input\":[\"text\",\"image\"],\"cost\":{\"input\":0,\"output\":0,\"cacheRead\":0,\"cacheWrite\":0},\"contextWindow\":128000,\"maxTokens\":16384},\"pendingModelSwitch\":null,\"thinkingLevel\":\"off\",\"thinkingLevelSource\":\"default\",\"sessionModelThinkingLevels\":{\"faux/faux-a\":\"off\"},\"fastMode\":false,\"isStreaming\":false,\"pausedHeldInputCount\":0,\"runPaused\":false,\"pause\":{\"pauseSeq\":0,\"records\":[]},\"isCompacting\":false,\"retryAttempt\":0,\"isBashRunning\":false,\"steeringMode\":\"all\",\"followUpMode\":\"one-at-a-time\",\"sessionFile\":\"<root>/cwd/session.jsonl\",\"sessionId\":\"<uuid-14>\",\"cwd\":\"<root>/cwd\",\"projectTrusted\":false,\"entries\":[{\"type\":\"model_change\",\"id\":\"<entry-3>\",\"parentId\":null,\"timestamp\":\"2026-10-29T00:00:00.000Z\",\"provider\":\"faux\",\"modelId\":\"faux-a\"},{\"type\":\"thinking_level_change\",\"id\":\"<entry-2>\",\"parentId\":\"<entry-3>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\",\"thinkingLevel\":\"off\",\"provider\":\"faux\",\"modelId\":\"faux-a\"},{\"type\":\"custom\",\"customType\":\"pi-rules.scan\",\"data\":{\"cwd\":\"<root>/cwd\",\"reason\":\"startup\"},\"startupBookkeeping\":true,\"id\":\"<entry-1>\",\"parentId\":\"<entry-2>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\"}],\"steering\":[],\"followUp\":[],\"ordered\":[],\"autoCompactionEnabled\":true,\"messageCount\":0,\"pendingMessageCount\":0,\"usageTotals\":{\"input\":0,\"output\":0,\"cacheRead\":0,\"cacheWrite\":0,\"cost\":0},\"contextUsage\":{\"tokens\":0,\"contextWindow\":128000,\"percent\":0},\"favoriteModels\":[],\"scopedModels\":[]}},\"sessionId\":\"rpc-1\"}\n{\"id\":\"req_3\",\"type\":\"response\",\"command\":\"get_entries\",\"success\":true,\"data\":{\"entries\":[{\"type\":\"model_change\",\"id\":\"<entry-3>\",\"parentId\":null,\"timestamp\":\"2026-10-29T00:00:00.000Z\",\"provider\":\"faux\",\"modelId\":\"faux-a\"},{\"type\":\"thinking_level_change\",\"id\":\"<entry-2>\",\"parentId\":\"<entry-3>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\",\"thinkingLevel\":\"off\",\"provider\":\"faux\",\"modelId\":\"faux-a\"},{\"type\":\"custom\",\"customType\":\"pi-rules.scan\",\"data\":{\"cwd\":\"<root>/cwd\",\"reason\":\"startup\"},\"startupBookkeeping\":true,\"id\":\"<entry-1>\",\"parentId\":\"<entry-2>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\"}],\"leafId\":\"<entry-1>\"},\"sessionId\":\"rpc-1\"}\n{\"type\":\"session_closed\",\"sessionId\":\"rpc-1\",\"reason\":\"client_close\"}\n{\"id\":\"req_4\",\"type\":\"response\",\"command\":\"close_session\",\"success\":true,\"data\":{},\"sessionId\":\"rpc-1\"}\n",
3
+ "rpcClientFrames": "{\"id\":\"rpc-auth-protocol\",\"type\":\"response\",\"command\":\"get_protocol_info\",\"success\":true,\"data\":{\"protocolVersion\":1,\"serverVersion\":\"<server-version>\",\"capabilities\":[\"<capabilities>\"],\"mode\":\"multi\",\"instanceId\":\"<uuid-1>\",\"generation\":0,\"engineVersion\":\"<server-version>\",\"engineOrdinal\":\"<engine-ordinal>\",\"launch_profile\":{\"profile_id\":\"ca7a49435f02009fdda5dac49d29408dcfeb1ab5374353784c5a9def467f5be1\",\"core\":{\"extensions\":[],\"multi_session\":false,\"session_runtime\":\"worker\"}},\"agentDir\":\"<root>/agent\",\"method_classes\":{\"version\":1,\"digest\":\"7ea18de0fb5d28f89aabdc17cbaca72e973cdea7d5928fd82d91ccced025000e\",\"classes\":{\"get_protocol_info\":\"observe\",\"get_subscription_limits\":\"observe\",\"list_sessions\":\"observe\",\"list_hands\":\"observe\",\"get_state\":\"observe\",\"get_snapshot\":\"observe\",\"get_entries\":\"observe\",\"get_messages\":\"observe\",\"get_tree\":\"observe\",\"get_fork_messages\":\"observe\",\"get_last_assistant_text\":\"observe\",\"get_session_stats\":\"observe\",\"get_effect_status\":\"dynamic\",\"get_pending_inputs\":\"observe\",\"get_follow_up_messages\":\"observe\",\"get_steering_messages\":\"observe\",\"get_commands\":\"observe\",\"get_loaded_surfaces\":\"observe\",\"get_project_trust\":\"observe\",\"get_available_models\":\"observe\",\"get_available_thinking_levels\":\"observe\",\"get_model_thinking_level\":\"observe\",\"get_fast_mode\":\"observe\",\"get_media\":\"observe\",\"memory_report\":\"observe\",\"eval_cells.list\":\"observe\",\"eval_cells.peek\":\"observe\",\"prompt\":\"dynamic\",\"steer\":\"dynamic\",\"follow_up\":\"dynamic\",\"abort\":\"drive\",\"interrupt\":\"drive\",\"abort_retry\":\"drive\",\"abort_compaction\":\"drive\",\"abort_branch_summary\":\"drive\",\"compact\":\"drive\",\"clear_queue\":\"drive\",\"dequeue_message\":\"drive\",\"edit_queued_message\":\"drive\",\"hold_input\":\"drive\",\"withdraw_held_input\":\"drive\",\"append_user_message\":\"drive\",\"edit_user_message\":\"drive\",\"continue_from_leaf\":\"drive\",\"navigate_tree\":\"drive\",\"set_label\":\"drive\",\"set_session_name\":\"drive\",\"set_thinking_level\":\"dynamic\",\"set_model\":\"dynamic\",\"set_auto_compaction\":\"drive\",\"set_permission_preset\":\"device_admin\",\"set_retry_fallback\":\"drive\",\"pause_for_run\":\"drive\",\"resume_for_run\":\"drive\",\"wake\":\"drive\",\"admit_external_message\":\"drive\",\"release_tool_barrier\":\"drive\",\"request_self_settle\":\"drive\",\"eval_cells.stop\":\"drive\",\"append_session_entry\":\"dynamic\",\"extension_ui_response\":\"answer\",\"extension_ui_progress\":\"answer\",\"new_session\":\"dynamic\",\"open_session\":\"dynamic\",\"resume_session\":\"thread\",\"fork\":\"thread\",\"clone\":\"thread\",\"close_session\":\"thread\",\"unload_session\":\"thread\",\"set_session_context\":\"thread\",\"attach_session\":\"thread\",\"select_hand\":\"device_admin\",\"bash\":\"device_admin\",\"abort_bash\":\"device_admin\",\"record_bash_result\":\"device_admin\",\"cleanup_bash_output\":\"device_admin\",\"extension_request\":\"device_admin\",\"reload\":\"device_admin\",\"check_reload_veto\":\"device_admin\",\"resolve_path\":\"device_admin\",\"login_start\":\"device_admin\",\"login_cancel\":\"device_admin\",\"login_api_key\":\"device_admin\",\"logout\":\"device_admin\",\"verify_api_key\":\"device_admin\",\"get_auth_providers\":\"device_admin\",\"get_provider_accounts\":\"device_admin\",\"account_order\":\"device_admin\",\"account_pin\":\"device_admin\",\"account_remove\":\"device_admin\",\"account_reset\":\"device_admin\",\"test_provider_account\":\"device_admin\",\"set_favorite_models\":\"device_admin\",\"set_scoped_models\":\"device_admin\",\"cycle_model\":\"device_admin\",\"cycle_thinking_level\":\"device_admin\",\"set_fast_mode\":\"device_admin\",\"set_steering_mode\":\"device_admin\",\"set_follow_up_mode\":\"device_admin\",\"set_auto_retry\":\"device_admin\",\"set_project_trust\":\"device_admin\",\"get_project_settings\":\"device_admin\",\"set_project_setting\":\"device_admin\",\"edit_assistant_message\":\"device_admin\",\"send_custom_message\":\"device_admin\",\"switch_session\":\"device_admin\",\"import_jsonl\":\"device_admin\",\"export_html\":\"device_admin\",\"export_jsonl\":\"device_admin\",\"set_client_info\":\"connection_local\",\"subscribe_sessions\":\"connection_local\",\"unsubscribe_sessions\":\"connection_local\",\"record_user_activity\":\"connection_local\",\"detach_connection\":\"connection_local\",\"release_session\":\"connection_local\",\"warm\":\"connection_local\",\"host_response\":\"connection_local\",\"release_attachment\":\"connection_local\",\"reclaim_session\":\"connection_local\",\"move_session\":\"connection_local\",\"get_recovery_page\":\"connection_local\",\"release_recovery\":\"connection_local\",\"cancel_attempt\":\"connection_local\",\"reset_stream\":\"connection_local\"},\"dynamic_versions\":{\"get_effect_status\":1,\"prompt\":1,\"steer\":1,\"follow_up\":1,\"set_thinking_level\":1,\"set_model\":1,\"append_session_entry\":1,\"new_session\":1,\"open_session\":2}},\"memory_pressure\":false,\"path_identity_resolver\":{\"state\":\"not_started\",\"detail\":null}}}\n{\"id\":\"req_1\",\"type\":\"response\",\"command\":\"set_client_info\",\"success\":true}\n{\"type\":\"queued\",\"for_request\":\"req_2\",\"position\":1,\"in_flight\":0}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-2>\",\"method\":\"setWidget\",\"widgetKey\":\"todo-sidebar\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-3>\",\"method\":\"setStatus\",\"statusKey\":\"anthropic-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-4>\",\"method\":\"setWidget\",\"widgetKey\":\"anthropic-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-5>\",\"method\":\"setStatus\",\"statusKey\":\"openai-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-6>\",\"method\":\"setWidget\",\"widgetKey\":\"openai-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-7>\",\"method\":\"setStatus\",\"statusKey\":\"monitors\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-8>\",\"method\":\"setStatus\",\"statusKey\":\"pi-websearch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-9>\",\"method\":\"setWidget\",\"widgetKey\":\"pi-websearch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-10>\",\"method\":\"setStatus\",\"statusKey\":\"pi-webfetch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-11>\",\"method\":\"setWidget\",\"widgetKey\":\"pi-webfetch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"entry_appended\",\"entry\":{\"type\":\"custom\",\"customType\":\"pi-rules.scan\",\"data\":{\"cwd\":\"<root>/cwd\",\"reason\":\"startup\"},\"startupBookkeeping\":true,\"id\":\"<entry-1>\",\"parentId\":\"<entry-2>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\"},\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-12>\",\"method\":\"setStatus\",\"statusKey\":\"todo-waiting\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-13>\",\"method\":\"setStatus\",\"statusKey\":\"goal\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"session_opened\",\"sessionId\":\"rpc-1\",\"durableSessionId\":\"<uuid-14>\",\"sessionPath\":\"<root>/cwd/session.jsonl\",\"kind\":\"interactive\",\"cwd\":\"<root>/cwd\"}\n{\"id\":\"req_2\",\"type\":\"response\",\"command\":\"open_session\",\"success\":true,\"data\":{\"sessionId\":\"rpc-1\",\"state\":{\"answeredUiRequests\":[],\"paused\":false,\"pausedNoticeCount\":0,\"holds\":[],\"lifecycle\":{\"state\":\"done\",\"seq\":0,\"epoch\":0,\"epochOrigin\":\"automatic\",\"humanInputRevision\":0,\"selfSettleEligible\":true,\"restoring\":false,\"persisted\":false,\"runPaused\":false,\"since\":\"2026-10-29T00:00:00.000Z\",\"role\":\"root\",\"rootSessionId\":\"<uuid-14>\",\"activityRevision\":{\"instanceId\":\"<uuid-15>\",\"revision\":0},\"waitingOn\":{\"children\":0,\"background\":[],\"human\":[]},\"holds\":[],\"observedAt\":\"2026-10-29T00:00:00.000Z\"},\"pendingQuestions\":[],\"pendingDialogs\":[],\"permissionPreset\":\"full-access\",\"permissionPresetSource\":\"host_default\",\"permissionPresetStatus\":\"applied\",\"effectivePermissionPreset\":\"full-access\",\"model\":{\"id\":\"faux-a\",\"name\":\"faux-a\",\"api\":\"faux:fixture\",\"provider\":\"faux\",\"baseUrl\":\"http://localhost:0\",\"reasoning\":false,\"input\":[\"text\",\"image\"],\"cost\":{\"input\":0,\"output\":0,\"cacheRead\":0,\"cacheWrite\":0},\"contextWindow\":128000,\"maxTokens\":16384},\"pendingModelSwitch\":null,\"thinkingLevel\":\"off\",\"thinkingLevelSource\":\"default\",\"sessionModelThinkingLevels\":{\"faux/faux-a\":\"off\"},\"fastMode\":false,\"isStreaming\":false,\"pausedHeldInputCount\":0,\"runPaused\":false,\"pause\":{\"pauseSeq\":0,\"records\":[]},\"isCompacting\":false,\"retryAttempt\":0,\"isBashRunning\":false,\"steeringMode\":\"all\",\"followUpMode\":\"one-at-a-time\",\"sessionFile\":\"<root>/cwd/session.jsonl\",\"sessionId\":\"<uuid-14>\",\"cwd\":\"<root>/cwd\",\"projectTrusted\":false,\"entries\":[{\"type\":\"model_change\",\"id\":\"<entry-3>\",\"parentId\":null,\"timestamp\":\"2026-10-29T00:00:00.000Z\",\"provider\":\"faux\",\"modelId\":\"faux-a\"},{\"type\":\"thinking_level_change\",\"id\":\"<entry-2>\",\"parentId\":\"<entry-3>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\",\"thinkingLevel\":\"off\",\"provider\":\"faux\",\"modelId\":\"faux-a\"},{\"type\":\"custom\",\"customType\":\"pi-rules.scan\",\"data\":{\"cwd\":\"<root>/cwd\",\"reason\":\"startup\"},\"startupBookkeeping\":true,\"id\":\"<entry-1>\",\"parentId\":\"<entry-2>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\"}],\"steering\":[],\"followUp\":[],\"ordered\":[],\"autoCompactionEnabled\":true,\"messageCount\":0,\"pendingMessageCount\":0,\"usageTotals\":{\"input\":0,\"output\":0,\"cacheRead\":0,\"cacheWrite\":0,\"cost\":0},\"contextUsage\":{\"tokens\":0,\"contextWindow\":128000,\"percent\":0},\"favoriteModels\":[],\"scopedModels\":[]},\"permissionPreset\":\"full-access\",\"permissionPresetSource\":\"host_default\",\"permissionPresetStatus\":\"applied\"},\"sessionId\":\"rpc-1\"}\n{\"id\":\"req_3\",\"type\":\"response\",\"command\":\"get_entries\",\"success\":true,\"data\":{\"entries\":[{\"type\":\"model_change\",\"id\":\"<entry-3>\",\"parentId\":null,\"timestamp\":\"2026-10-29T00:00:00.000Z\",\"provider\":\"faux\",\"modelId\":\"faux-a\"},{\"type\":\"thinking_level_change\",\"id\":\"<entry-2>\",\"parentId\":\"<entry-3>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\",\"thinkingLevel\":\"off\",\"provider\":\"faux\",\"modelId\":\"faux-a\"},{\"type\":\"custom\",\"customType\":\"pi-rules.scan\",\"data\":{\"cwd\":\"<root>/cwd\",\"reason\":\"startup\"},\"startupBookkeeping\":true,\"id\":\"<entry-1>\",\"parentId\":\"<entry-2>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\"}],\"leafId\":\"<entry-1>\"},\"sessionId\":\"rpc-1\"}\n{\"type\":\"session_closed\",\"sessionId\":\"rpc-1\",\"reason\":\"client_close\"}\n{\"id\":\"req_4\",\"type\":\"response\",\"command\":\"close_session\",\"success\":true,\"data\":{},\"sessionId\":\"rpc-1\"}\n",
4
4
  "subscribeRequests": "{\"type\":\"subscribe_sessions\",\"mirror\":\"all\",\"id\":\"subscriber-1\"}\n{\"type\":\"unsubscribe_sessions\",\"subscriptionId\":\"sub-1\",\"id\":\"subscriber-2\"}\n",
5
5
  "subscribeFrames": "{\"id\":\"subscriber-1\",\"type\":\"response\",\"command\":\"subscribe_sessions\",\"success\":true,\"data\":{\"subscriptionId\":\"sub-1\",\"snapshot\":[],\"seq\":0,\"hostGeneration\":\"0\",\"hostInstanceId\":\"<uuid-1>\"}}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-2>\",\"method\":\"setWidget\",\"widgetKey\":\"todo-sidebar\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-3>\",\"method\":\"setStatus\",\"statusKey\":\"anthropic-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-4>\",\"method\":\"setWidget\",\"widgetKey\":\"anthropic-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-5>\",\"method\":\"setStatus\",\"statusKey\":\"openai-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-6>\",\"method\":\"setWidget\",\"widgetKey\":\"openai-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-7>\",\"method\":\"setStatus\",\"statusKey\":\"monitors\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-8>\",\"method\":\"setStatus\",\"statusKey\":\"pi-websearch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-9>\",\"method\":\"setWidget\",\"widgetKey\":\"pi-websearch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-10>\",\"method\":\"setStatus\",\"statusKey\":\"pi-webfetch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-11>\",\"method\":\"setWidget\",\"widgetKey\":\"pi-webfetch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"entry_appended\",\"entry\":{\"type\":\"custom\",\"customType\":\"pi-rules.scan\",\"data\":{\"cwd\":\"<root>/cwd\",\"reason\":\"startup\"},\"startupBookkeeping\":true,\"id\":\"<entry-1>\",\"parentId\":\"<entry-2>\",\"timestamp\":\"2026-10-29T00:00:00.000Z\"},\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-12>\",\"method\":\"setStatus\",\"statusKey\":\"todo-waiting\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-13>\",\"method\":\"setStatus\",\"statusKey\":\"goal\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"session_opened\",\"sessionId\":\"rpc-1\",\"durableSessionId\":\"<uuid-14>\",\"sessionPath\":\"<root>/cwd/session.jsonl\",\"kind\":\"interactive\",\"cwd\":\"<root>/cwd\"}\n{\"type\":\"session_status\",\"subscriptionId\":\"sub-1\",\"seq\":1,\"sessionId\":\"rpc-1\",\"status\":\"idle\",\"kind\":\"interactive\",\"cwd\":\"<root>/cwd\",\"attachments\":1,\"durableSessionId\":\"<uuid-14>\",\"sessionPath\":\"<root>/cwd/session.jsonl\",\"model\":{\"provider\":\"faux\",\"id\":\"faux-a\"},\"thinkingLevel\":\"off\",\"pendingQueue\":{\"steering\":0,\"followUp\":0},\"lastChange\":{\"at\":\"2026-10-29T00:00:00.000Z\",\"cause\":\"open\",\"by\":\"client\"}}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-15>\",\"method\":\"setStatus\",\"statusKey\":\"anthropic-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-16>\",\"method\":\"setWidget\",\"widgetKey\":\"anthropic-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-17>\",\"method\":\"setStatus\",\"statusKey\":\"openai-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-18>\",\"method\":\"setWidget\",\"widgetKey\":\"openai-web-search\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-19>\",\"method\":\"setStatus\",\"statusKey\":\"monitors\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-20>\",\"method\":\"setStatus\",\"statusKey\":\"pi-websearch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-21>\",\"method\":\"setWidget\",\"widgetKey\":\"pi-websearch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-22>\",\"method\":\"setStatus\",\"statusKey\":\"pi-webfetch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-23>\",\"method\":\"setWidget\",\"widgetKey\":\"pi-webfetch\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<uuid-24>\",\"method\":\"setStatus\",\"statusKey\":\"loop\",\"sessionId\":\"rpc-1\"}\n{\"type\":\"session_closed\",\"sessionId\":\"rpc-1\",\"reason\":\"client_close\"}\n{\"type\":\"session_status\",\"subscriptionId\":\"sub-1\",\"seq\":2,\"sessionId\":\"rpc-1\",\"status\":\"closed\",\"reason\":\"client_close\",\"kind\":\"interactive\",\"cwd\":\"<root>/cwd\",\"attachments\":1,\"durableSessionId\":\"<uuid-14>\",\"sessionPath\":\"<root>/cwd/session.jsonl\",\"model\":{\"provider\":\"faux\",\"id\":\"faux-a\"},\"thinkingLevel\":\"off\",\"pendingQueue\":{\"steering\":0,\"followUp\":0},\"lastChange\":{\"at\":\"2026-10-29T00:00:00.000Z\",\"cause\":\"close\",\"by\":\"client\"}}\n{\"id\":\"subscriber-2\",\"type\":\"response\",\"command\":\"unsubscribe_sessions\",\"success\":true}\n"
6
6
  }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "methodClassesVersion": 1,
3
- "methodClassesDigest": "3860136b2a1b07c20373ed361df07c045bd1e84fce001917e271ca0e4b903e63",
3
+ "methodClassesDigest": "7ea18de0fb5d28f89aabdc17cbaca72e973cdea7d5928fd82d91ccced025000e",
4
4
  "sampleId": "00000000-0000-7000-8000-000000000000",
5
5
  "samplePathRoot": "/nonexistent/senpi-rpc-inbound-sample",
6
- "methodCount": 122,
6
+ "methodCount": 124,
7
7
  "methods": [
8
8
  {
9
9
  "method": "abort",
@@ -94,6 +94,23 @@
94
94
  "name": "rpc-sample-account"
95
95
  }
96
96
  },
97
+ {
98
+ "method": "admit_external_message",
99
+ "class": "drive",
100
+ "sampleClass": "drive",
101
+ "connectionLocal": false,
102
+ "sample": {
103
+ "type": "admit_external_message",
104
+ "deliveryId": "00000000-0000-7000-8000-000000000000",
105
+ "text": "Sample delivery.",
106
+ "sender": {
107
+ "kind": "agent",
108
+ "session_id": "00000000-0000-7000-8000-000000000000"
109
+ },
110
+ "deliverAs": "followUp",
111
+ "whenIdle": "queue"
112
+ }
113
+ },
97
114
  {
98
115
  "method": "append_session_entry",
99
116
  "class": "dynamic",
@@ -1065,6 +1082,16 @@
1065
1082
  "modelId": "rpc-sample-model"
1066
1083
  }
1067
1084
  },
1085
+ {
1086
+ "method": "set_permission_preset",
1087
+ "class": "device_admin",
1088
+ "sampleClass": "device_admin",
1089
+ "connectionLocal": false,
1090
+ "sample": {
1091
+ "type": "set_permission_preset",
1092
+ "permissionPreset": "ask"
1093
+ }
1094
+ },
1068
1095
  {
1069
1096
  "method": "set_project_setting",
1070
1097
  "class": "device_admin",
@@ -885,6 +885,18 @@ pi.on("ui_prompt_end", async (event, ctx) => {
885
885
  });
886
886
  ```
887
887
 
888
+ #### Permission mode and session-wide dialogs
889
+
890
+ `pi.getPermissionPreset()` returns the preset the permission system enforces for this session right now, or `undefined` when no permission system runs for it or its setup failed.
891
+
892
+ When a client moves a live session to another preset (`set_permission_preset`, or an attach naming one), the session announces it on the `pi.events` bus. A session's initial preset is not announced; read it with `pi.getPermissionPreset()`.
893
+
894
+ | Channel | Payload | Meaning |
895
+ |---------|---------|---------|
896
+ | `permission_preset_changed` | `{ permissionPreset, permissionPresetSource, permissionPresetStatus, effectivePermissionPreset }` | The session's approval mode changed; the payload is the same mode `get_state` reports. |
897
+
898
+ `pi.sessionDialog.select / confirm / input / editor` raise a dialog addressed to every client attached to the session, not only to the connection whose command is running. Use it for a prompt raised after that command returned, such as a background child's permission request a task runner forwards. The first answer wins; a later answer to the same dialog is refused with `already_answered`. In the terminal UI it shows like any other dialog. The same addressing is available on `ctx.ui` dialogs with the `audience: "session"` option.
899
+
888
900
  #### Pending-question bus events
889
901
 
890
902
  These additive events use `pi.events.on(...)`, not `pi.on(...)`: