@code-yeongyu/senpi 2026.11.10 → 2026.11.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -0
- package/dist/bundle/{SKILL-B4R7BVOT.md → SKILL-R7FEIQKG.md} +1 -1
- package/dist/bundle/chunks/{SKILL-AZ6ONO52.js → SKILL-5O2UKORG.js} +1 -1
- package/dist/bundle/chunks/{SKILL-B4R7BVOT.md → SKILL-R7FEIQKG.md} +1 -1
- package/dist/bundle/chunks/{args-77GYOPEO.js → args-HFYB6URH.js} +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-YFZ734T3.js → azure-openai-responses-MNN2TINI.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZUQCYIRY.js → chunk-2ODGHBUD.js} +1 -1
- package/dist/bundle/chunks/chunk-3IYEGHBI.js +2 -0
- package/dist/bundle/chunks/{chunk-I5ASHSLZ.js → chunk-4JMGWVRH.js} +1 -1
- package/dist/bundle/chunks/{chunk-YKYWL2NO.js → chunk-5F2XC6UC.js} +3 -3
- package/dist/bundle/chunks/{chunk-MSDBBOB2.js → chunk-5H43O4MA.js} +1 -1
- package/dist/bundle/chunks/{chunk-R3OP7IA4.js → chunk-5PIZWSNG.js} +4 -4
- package/dist/bundle/chunks/{chunk-33MZJTQ3.js → chunk-B4WD5XHP.js} +1 -1
- package/dist/bundle/chunks/{chunk-Q63YLZMN.js → chunk-BPEA5SWW.js} +2 -2
- package/dist/bundle/chunks/{chunk-MOVJQVMC.js → chunk-BUDJLVMF.js} +3 -3
- package/dist/bundle/chunks/{chunk-V5FSENW6.js → chunk-COKX65TE.js} +1 -1
- package/dist/bundle/chunks/{chunk-2EJ4BGBM.js → chunk-DHORIIVQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-NH7SYIGN.js → chunk-DLPV4B3R.js} +1 -1
- package/dist/bundle/chunks/{chunk-RCHIMH7N.js → chunk-DQY26AX3.js} +1 -1
- package/dist/bundle/chunks/{chunk-Y7Z7XPUF.js → chunk-DZQA5LVN.js} +1 -1
- package/dist/bundle/chunks/{chunk-2CNSCMYC.js → chunk-E5O4SP5I.js} +1 -1
- package/dist/bundle/chunks/{chunk-3Q5ZENSD.js → chunk-E6ZBTSC6.js} +1 -1
- package/dist/bundle/chunks/{chunk-GVPZPVVO.js → chunk-IBDAV2AL.js} +3 -3
- package/dist/bundle/chunks/chunk-IREUIMFE.js +11 -0
- package/dist/bundle/chunks/{chunk-KU7OCWJF.js → chunk-J4ADZ2OK.js} +1 -1
- package/dist/bundle/chunks/{chunk-T2SMQPRE.js → chunk-K3GAGIPX.js} +1 -1
- package/dist/bundle/chunks/{chunk-BELNOPAI.js → chunk-L3KVP7VJ.js} +3 -3
- package/dist/bundle/chunks/{chunk-IYSJFQUU.js → chunk-L6DF5G6K.js} +1 -1
- package/dist/bundle/chunks/{chunk-7MN7YNO5.js → chunk-L7DGTA3L.js} +1 -1
- package/dist/bundle/chunks/chunk-LJFQYCFH.js +2 -0
- package/dist/bundle/chunks/{chunk-J2NGJKXL.js → chunk-LXINMDMX.js} +1 -1
- package/dist/bundle/chunks/{chunk-H33A33M4.js → chunk-M2VJVJMK.js} +1 -1
- package/dist/bundle/chunks/{chunk-2U6HXWXS.js → chunk-NNQ34ZDW.js} +1 -1
- package/dist/bundle/chunks/chunk-NQKCJQHB.js +2 -0
- package/dist/bundle/chunks/{chunk-EJB5E2JK.js → chunk-NRZAJUUO.js} +2 -2
- package/dist/bundle/chunks/{chunk-R2Z7CX77.js → chunk-OSNR3IRD.js} +1 -1
- package/dist/bundle/chunks/{chunk-O3FG6HML.js → chunk-PJI5GT7U.js} +1 -1
- package/dist/bundle/chunks/{chunk-FDNM2DAE.js → chunk-PUZDKFRT.js} +164 -164
- package/dist/bundle/chunks/{chunk-BPNSOZ2C.js → chunk-QUVSIW2R.js} +1 -1
- package/dist/bundle/chunks/{chunk-WXYMAUFX.js → chunk-QX7WD3IM.js} +1 -1
- package/dist/bundle/chunks/{chunk-Y4ONXQMR.js → chunk-RGHVPUUX.js} +1 -1
- package/dist/bundle/chunks/{chunk-RYPM5BZQ.js → chunk-S7NAJQB3.js} +1 -1
- package/dist/bundle/chunks/{chunk-2BH54YQR.js → chunk-SANWO37B.js} +1 -1
- package/dist/bundle/chunks/{chunk-YQZNARTX.js → chunk-SS5VTBAV.js} +1 -1
- package/dist/bundle/chunks/{chunk-LT353NSF.js → chunk-T4CUD7RF.js} +1 -1
- package/dist/bundle/chunks/{chunk-J7FW7MED.js → chunk-TCZORL3V.js} +1 -1
- package/dist/bundle/chunks/{chunk-KU72IKV5.js → chunk-VGSWOLIY.js} +1 -1
- package/dist/bundle/chunks/{chunk-DBQBJTMF.js → chunk-VUY4BZDI.js} +1 -1
- package/dist/bundle/chunks/chunk-XI6THO4F.js +2 -0
- package/dist/bundle/chunks/{chunk-FAFIGBUD.js → chunk-XUDKT4UH.js} +3 -3
- package/dist/bundle/chunks/{cli-main-BX5AFWJQ.js → cli-main-7II7GCIR.js} +1 -1
- package/dist/bundle/chunks/{credential-accounts-LVVPKFEY.js → credential-accounts-N3B5YJS7.js} +1 -1
- package/dist/bundle/chunks/{dispatch-EP35KKJ3.js → dispatch-YUYZQXRP.js} +1 -1
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{hand-entry-F6PWKNPJ.js → hand-entry-ONOLRMVB.js} +3 -3
- package/dist/bundle/chunks/{headless-host-HKGNJGYT.js → headless-host-53UMJMM6.js} +1 -1
- package/dist/bundle/chunks/{health-write-FBF74QJ7.js → health-write-JLSF5EVV.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-5SCPY3OH.js → help-fast-path-552QLMWT.js} +1 -1
- package/dist/bundle/chunks/{host-command-47QQKJEY.js → host-command-KWMJLPBS.js} +1 -1
- package/dist/bundle/chunks/{host-lifecycle-VSAH4M3R.js → host-lifecycle-4WQDCN3Y.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +5 -5
- package/dist/bundle/chunks/{host-session-command-SL4AN5WD.js → host-session-command-ZDYEKCUB.js} +1 -1
- package/dist/bundle/chunks/{host-socket-request-DMYXC56U.js → host-socket-request-QN4Q2P5E.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-JDMIHJMQ.js → interactive-mode-EPE4M32G.js} +1 -1
- package/dist/bundle/chunks/{login-health-EVXVLHB2.js → login-health-TOSQT5RG.js} +1 -1
- package/dist/bundle/chunks/{main-4RITUAQF.js → main-OM7BKVZG.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-SKDD4OWM.js → multi-session-host-DB6XMY53.js} +11 -8
- package/dist/bundle/chunks/openai-codex-responses-VRGOAFEG.js +10 -0
- package/dist/bundle/chunks/{openai-responses-NIVBWE2H.js → openai-responses-I7PXRWCM.js} +2 -2
- package/dist/bundle/chunks/{package-manager-cli-B5VA4KFO.js → package-manager-cli-GLU5Q2KV.js} +1 -1
- package/dist/bundle/chunks/{role-HQUN4XXK.js → role-AEOLJRTP.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-F3BEMXBE.js → rotation-stream-E3FJFHXJ.js} +1 -1
- package/dist/bundle/chunks/rpc-mode-SDJVBT36.js +2 -0
- package/dist/bundle/chunks/{rpc-monitor-source-C6VQ3BC4.js → rpc-monitor-source-PGTDSGWP.js} +1 -1
- package/dist/bundle/chunks/{run-attached-tui-R3RB5P7G.js → run-attached-tui-4AENRP7P.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-FPEQCX33.js → schedule-command-5SQD7M5N.js} +1 -1
- package/dist/bundle/chunks/session-control-endpoint-R25A2QGS.js +3 -0
- package/dist/bundle/chunks/{session-picker-2FZSNFS7.js → session-picker-GZM5CVQL.js} +1 -1
- package/dist/bundle/chunks/{session-slot-CZGQQCOC.js → session-slot-RH7JLO3F.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +335 -332
- package/dist/bundle/chunks/{state-store-S7QAESIF.js → state-store-IECVTYXC.js} +1 -1
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +5 -5
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/cli.js +9 -3
- package/dist/core/agent-session-runtime.d.ts +2 -0
- package/dist/core/agent-session-runtime.js +5 -0
- package/dist/core/agent-session.d.ts +15 -3
- package/dist/core/agent-session.js +91 -18
- package/dist/core/assistant-error-redaction.d.ts +9 -0
- package/dist/core/assistant-error-redaction.js +27 -0
- package/dist/core/config-reference-redaction.js +90 -10
- package/dist/core/context-files.d.ts +10 -0
- package/dist/core/context-files.js +58 -12
- package/dist/core/credential-accounts.d.ts +2 -0
- package/dist/core/credential-accounts.js +10 -1
- package/dist/core/dynamic-prompt/intent-gate.js +2 -2
- package/dist/core/dynamic-prompt/style.js +2 -2
- package/dist/core/dynamic-prompt/verification.js +2 -2
- package/dist/core/egress/wire.d.ts +13 -0
- package/dist/core/extensions/builtin/anthropic-subscription/accounts.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +3 -2
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-account-test.js +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.d.ts +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.js +47 -16
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.js +4 -2
- package/dist/core/extensions/builtin/anthropic-subscription/claude-settings-env.d.ts +20 -0
- package/dist/core/extensions/builtin/anthropic-subscription/claude-settings-env.js +39 -0
- package/dist/core/extensions/builtin/anthropic-subscription/context-window.d.ts +17 -0
- package/dist/core/extensions/builtin/anthropic-subscription/context-window.js +23 -0
- package/dist/core/extensions/builtin/anthropic-subscription/errors.d.ts +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/errors.js +14 -5
- package/dist/core/extensions/builtin/anthropic-subscription/oauth-login.js +3 -3
- package/dist/core/extensions/builtin/anthropic-subscription/prompt-cache-ttl.d.ts +10 -4
- package/dist/core/extensions/builtin/anthropic-subscription/prompt-cache-ttl.js +14 -5
- package/dist/core/extensions/builtin/anthropic-subscription/query-environment.d.ts +3 -2
- package/dist/core/extensions/builtin/anthropic-subscription/query-environment.js +5 -4
- package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.lazy.d.ts +2 -1
- package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.lazy.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +11 -1
- package/dist/core/extensions/builtin/config-reload/change-groups.js +6 -2
- package/dist/core/extensions/builtin/goal/continuation.d.ts +6 -0
- package/dist/core/extensions/builtin/goal/continuation.js +3 -2
- package/dist/core/extensions/builtin/goal/index.js +14 -26
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +8 -6
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +77 -39
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +66 -7
- package/dist/core/extensions/builtin/goal/persistence.js +20 -0
- package/dist/core/extensions/builtin/goal/provider-retry.d.ts +9 -0
- package/dist/core/extensions/builtin/goal/provider-retry.js +16 -0
- package/dist/core/extensions/builtin/goal/reload-reengagement.d.ts +3 -2
- package/dist/core/extensions/builtin/goal/reload-reengagement.js +4 -4
- package/dist/core/extensions/builtin/goal/session-start-continuation.d.ts +28 -0
- package/dist/core/extensions/builtin/goal/session-start-continuation.js +75 -0
- package/dist/core/extensions/builtin/goal/store.d.ts +3 -2
- package/dist/core/extensions/builtin/goal/store.js +9 -2
- package/dist/core/extensions/builtin/goal/types.d.ts +9 -0
- package/dist/core/extensions/builtin/handoff-hint/index.js +15 -11
- package/dist/core/extensions/builtin/handoff-hint/replacement-relay.d.ts +16 -0
- package/dist/core/extensions/builtin/handoff-hint/replacement-relay.js +17 -0
- package/dist/core/extensions/builtin/html-render/tool.js +13 -3
- package/dist/core/extensions/builtin/imagegen/skill/SKILL.md +1 -1
- package/dist/core/extensions/builtin/imagegen/tool.js +4 -14
- package/dist/core/extensions/builtin/mcp/auth/catalog-identity.js +32 -8
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.js +2 -1
- package/dist/core/extensions/builtin/mcp/auth/context.js +3 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-grant.d.ts +43 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-grant.js +196 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.d.ts +7 -1
- package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +13 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +6 -17
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +67 -88
- package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +25 -4
- package/dist/core/extensions/builtin/mcp/auth/oauth.js +10 -7
- package/dist/core/extensions/builtin/mcp/auth/registered-redirect.d.ts +19 -0
- package/dist/core/extensions/builtin/mcp/auth/registered-redirect.js +55 -0
- package/dist/core/extensions/builtin/mcp/auth/token-store.d.ts +23 -1
- package/dist/core/extensions/builtin/mcp/auth/token-store.js +3 -0
- package/dist/core/extensions/builtin/mcp/catalog-cache.js +7 -6
- package/dist/core/extensions/builtin/mcp/catalog.js +3 -2
- package/dist/core/extensions/builtin/mcp/invocation.js +6 -0
- package/dist/core/extensions/builtin/mcp/service-register.js +16 -2
- package/dist/core/extensions/builtin/mcp/service-tools-changed.js +61 -54
- package/dist/core/extensions/builtin/mcp/startup-race.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/startup-race.js +25 -7
- package/dist/core/extensions/builtin/permission-system/parsers.js +1 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-2.js +9 -8
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-haiku-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/claude-sonnet-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js +1 -1
- package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +5 -5
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +7 -6
- package/dist/core/extensions/loader.js +24 -4
- package/dist/core/extensions/runner.d.ts +9 -1
- package/dist/core/extensions/runner.js +9 -4
- package/dist/core/extensions/types.d.ts +19 -3
- package/dist/core/hand/hook-advertise.d.ts +41 -0
- package/dist/core/hand/hook-advertise.js +98 -0
- package/dist/core/hand/hook-contract.d.ts +66 -0
- package/dist/core/hand/hook-contract.js +138 -0
- package/dist/core/open-timing.d.ts +2 -0
- package/dist/core/open-timing.js +7 -1
- package/dist/core/resource-loader.js +21 -3
- package/dist/core/retry-fallback/controller-types.d.ts +2 -0
- package/dist/core/retry-fallback/controller.js +9 -2
- package/dist/core/retry-fallback/cooldown.d.ts +7 -0
- package/dist/core/retry-fallback/cooldown.js +13 -1
- package/dist/core/retry-fallback/credential-recovery.d.ts +7 -0
- package/dist/core/retry-fallback/credential-recovery.js +24 -0
- package/dist/core/retry-fallback/log.d.ts +7 -1
- package/dist/core/retry-fallback/log.js +82 -5
- package/dist/core/retry-fallback/model-config-error.js +2 -1
- package/dist/core/retry-fallback/settings.d.ts +8 -5
- package/dist/core/retry-fallback/settings.js +9 -6
- package/dist/core/session-manager.d.ts +2 -0
- package/dist/core/session-manager.js +15 -1
- package/dist/core/settings-manager.d.ts +6 -0
- package/dist/core/settings-manager.js +8 -0
- package/dist/core/turn-origin.d.ts +18 -10
- package/dist/core/turn-origin.js +34 -30
- package/dist/core/ultrafast-lanes.d.ts +1 -0
- package/dist/core/ultrafast-lanes.js +4 -2
- package/dist/modes/interactive/components/footer.d.ts +23 -5
- package/dist/modes/interactive/components/footer.js +45 -16
- package/dist/modes/interactive/interactive-mode.js +9 -1
- package/dist/modes/interactive/session-control-rpc-commands.d.ts +1 -1
- package/dist/modes/interactive/session-control-rpc-commands.js +5 -0
- package/dist/modes/rpc/admit-external-message-command.d.ts +2 -0
- package/dist/modes/rpc/admit-external-message-command.js +5 -1
- package/dist/modes/rpc/compact-admission-validation.js +4 -1
- package/dist/modes/rpc/connection-authority.d.ts +2 -1
- package/dist/modes/rpc/connection-authority.js +11 -9
- package/dist/modes/rpc/connection-handler.d.ts +2 -0
- package/dist/modes/rpc/connection-handler.js +33 -7
- package/dist/modes/rpc/connection-roles.d.ts +7 -0
- package/dist/modes/rpc/connection-roles.js +15 -0
- package/dist/modes/rpc/custom-capability.d.ts +6 -0
- package/dist/modes/rpc/custom-capability.js +6 -0
- package/dist/modes/rpc/delegated-drive-origin.d.ts +6 -6
- package/dist/modes/rpc/delegated-drive-origin.js +7 -8
- package/dist/modes/rpc/delegation-declaration.d.ts +2 -0
- package/dist/modes/rpc/delegation-declaration.js +7 -3
- package/dist/modes/rpc/effect-commands.d.ts +33 -0
- package/dist/modes/rpc/host-capabilities.d.ts +1 -0
- package/dist/modes/rpc/host-capabilities.js +7 -1
- package/dist/modes/rpc/host-gc-evidence.d.ts +1 -1
- package/dist/modes/rpc/host-gc-evidence.js +9 -3
- package/dist/modes/rpc/host-gc.js +4 -1
- package/dist/modes/rpc/host-observe-request.d.ts +2 -0
- package/dist/modes/rpc/host-observe-request.js +6 -1
- package/dist/modes/rpc/host-park-ledger.d.ts +19 -0
- package/dist/modes/rpc/host-park-ledger.js +110 -0
- package/dist/modes/rpc/lifecycle-delivery.js +4 -1
- package/dist/modes/rpc/loopless-tool-handlers.d.ts +6 -0
- package/dist/modes/rpc/loopless-tool-handlers.js +14 -1
- package/dist/modes/rpc/media-placeholders.js +1 -0
- package/dist/modes/rpc/method-classes-schema-host.d.ts +42 -1
- package/dist/modes/rpc/method-classes-schema-host.js +24 -21
- package/dist/modes/rpc/method-classes-schema-session.d.ts +20 -0
- package/dist/modes/rpc/method-classes-schema-session.js +6 -0
- package/dist/modes/rpc/method-classes-schema.d.ts +62 -1
- package/dist/modes/rpc/method-classes-schema.js +5 -2
- package/dist/modes/rpc/method-classes.d.ts +4 -0
- package/dist/modes/rpc/method-classes.js +6 -0
- package/dist/modes/rpc/multi-session-host.js +1 -1
- package/dist/modes/rpc/project-file-commands.d.ts +32 -0
- package/dist/modes/rpc/project-file-commands.js +195 -0
- package/dist/modes/rpc/project-file-path.d.ts +34 -0
- package/dist/modes/rpc/project-file-path.js +93 -0
- package/dist/modes/rpc/rpc-error-types.d.ts +9 -1
- package/dist/modes/rpc/rpc-error-types.js +8 -0
- package/dist/modes/rpc/rpc-host-command-types.d.ts +14 -1
- package/dist/modes/rpc/rpc-host-response-types.d.ts +21 -0
- package/dist/modes/rpc/rpc-mutation-response-types.d.ts +7 -0
- package/dist/modes/rpc/rpc-read-response-types.d.ts +7 -0
- package/dist/modes/rpc/rpc-session-command-types.d.ts +10 -0
- package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -0
- package/dist/modes/rpc/session-attachment-gate.js +6 -0
- package/dist/modes/rpc/session-binding.d.ts +1 -0
- package/dist/modes/rpc/session-binding.js +6 -1
- package/dist/modes/rpc/session-command-activity.js +1 -0
- package/dist/modes/rpc/session-connection-context.d.ts +2 -0
- package/dist/modes/rpc/session-connection-context.js +4 -0
- package/dist/modes/rpc/session-registry-attach.d.ts +2 -0
- package/dist/modes/rpc/session-registry-attach.js +15 -11
- package/dist/modes/rpc/session-registry-claims.js +2 -0
- package/dist/modes/rpc/session-registry-open.js +13 -7
- package/dist/modes/rpc/session-registry-opening.js +2 -1
- package/dist/modes/rpc/session-registry-types.d.ts +4 -0
- package/dist/modes/rpc/session-registry.d.ts +3 -0
- package/dist/modes/rpc/session-registry.js +27 -3
- package/dist/modes/rpc/session-router-close.js +1 -0
- package/dist/modes/rpc/session-router-context.d.ts +4 -2
- package/dist/modes/rpc/session-router-context.js +3 -0
- package/dist/modes/rpc/session-router-host-reads.js +1 -0
- package/dist/modes/rpc/session-router-occupancy.js +10 -1
- package/dist/modes/rpc/session-router-open-result.d.ts +12 -0
- package/dist/modes/rpc/session-router-open-result.js +63 -0
- package/dist/modes/rpc/session-router-open.d.ts +3 -0
- package/dist/modes/rpc/session-router-open.js +15 -63
- package/dist/modes/rpc/session-router-park.d.ts +15 -0
- package/dist/modes/rpc/session-router-park.js +176 -0
- package/dist/modes/rpc/session-router-routing.d.ts +1 -1
- package/dist/modes/rpc/session-router-routing.js +7 -0
- package/dist/modes/rpc/session-status-source.js +2 -2
- package/dist/private-fd-startup-fillers.d.ts +2 -2
- package/dist/private-fd-startup-fillers.js +34 -4
- package/dist/senpi +9 -3
- package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
- package/docs/engine-socket/fixtures/rpc-inbound-samples.json +44 -2
- package/docs/extensions.md +11 -1
- package/docs/rpc.md +106 -2
- package/docs/settings.md +5 -3
- package/docs/skills.md +1 -1
- package/docs/usage.md +1 -1
- package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
- package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
- package/package.json +6 -6
- package/dist/bundle/chunks/chunk-436DNN4Z.js +0 -2
- package/dist/bundle/chunks/chunk-DLAY7CIU.js +0 -2
- package/dist/bundle/chunks/chunk-SJI6UB23.js +0 -2
- package/dist/bundle/chunks/chunk-XH2MWTXA.js +0 -11
- package/dist/bundle/chunks/chunk-YK3ZZCHI.js +0 -2
- package/dist/bundle/chunks/openai-codex-responses-ABB2J43U.js +0 -10
- package/dist/bundle/chunks/rpc-mode-ZDUGW2IM.js +0 -2
- package/dist/bundle/chunks/session-control-endpoint-MEDV66PS.js +0 -3
|
@@ -28,7 +28,7 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
28
28
|
const requestedDurableId = profile.durableSessionId;
|
|
29
29
|
if (requestedDurableId !== undefined) {
|
|
30
30
|
for (const entry of host.entries.values()) {
|
|
31
|
-
if (entry.state === "closed")
|
|
31
|
+
if (entry.state === "closed" || entry.speculative)
|
|
32
32
|
continue;
|
|
33
33
|
if (entry.durableSessionId !== requestedDurableId)
|
|
34
34
|
continue;
|
|
@@ -101,7 +101,8 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
101
101
|
const { entry, storedProfile, runtimeProfile, sessionStartEvent, settleOpen } = opening;
|
|
102
102
|
host.entries.set(handle, entry);
|
|
103
103
|
claimSettled?.();
|
|
104
|
-
|
|
104
|
+
if (!entry.speculative)
|
|
105
|
+
host.options.onSizeChange?.([...host.entries.values()].filter((item) => !item.speculative).length);
|
|
105
106
|
const timer = startOpenTimer(`registry-open:${handle}`);
|
|
106
107
|
try {
|
|
107
108
|
const newSessionOptions = {
|
|
@@ -112,7 +113,9 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
112
113
|
? host.options.sessionFileLoad === "blocking"
|
|
113
114
|
? SessionManager.open(sessionPath, undefined, storedProfile.cwd, newSessionOptions)
|
|
114
115
|
: await SessionManager.openAsync(sessionPath, undefined, storedProfile.cwd, newSessionOptions, host.options.createSlicer?.())
|
|
115
|
-
:
|
|
116
|
+
: options?.speculative
|
|
117
|
+
? SessionManager.inMemory(storedProfile.cwd, newSessionOptions)
|
|
118
|
+
: SessionManager.create(storedProfile.cwd, undefined, newSessionOptions);
|
|
116
119
|
timer.mark("session_manager");
|
|
117
120
|
entry.runtime = await runWithProviderScope(entry.scope, () => createAgentSessionRuntime(host.options.createRuntime, {
|
|
118
121
|
cwd: manager.getCwd(),
|
|
@@ -125,19 +128,21 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
125
128
|
assertRequestedModelResolved(runtimeProfile, entry.runtime.diagnostics);
|
|
126
129
|
ClientAdmissions.sealQueuedOnReplace(entry.runtime);
|
|
127
130
|
EffectLedger.sealOnReplace(entry.runtime);
|
|
128
|
-
|
|
131
|
+
const durableSessionId = manager.getSessionId();
|
|
132
|
+
if (!entry.speculative)
|
|
133
|
+
entry.durableSessionId = durableSessionId;
|
|
129
134
|
entry.sessionPath ??= manager.getSessionFile();
|
|
130
135
|
// The accepted baseline precedes the open, so router admission and its reply come after it (R3.C1.1).
|
|
131
136
|
// Gated like a worker incarnation's lane: with the flag off no session owns one (RULING-112).
|
|
132
137
|
if (host.options.workerIsolate !== true && structuredLaneEnabled()) {
|
|
133
138
|
entry.acceptance = new SessionAcceptanceLane(undefined, undefined, host.options.agentDir);
|
|
134
|
-
await entry.acceptance.generations.bind(
|
|
139
|
+
await entry.acceptance.generations.bind(durableSessionId);
|
|
135
140
|
entry.acceptance.bootstrap({ transcript: manager.beginTranscriptJournal() });
|
|
136
141
|
setSessionRecoveryEpoch(entry.runtime.session, entry.acceptance.recoveryEpoch);
|
|
137
142
|
}
|
|
138
143
|
entry.state = "open";
|
|
139
144
|
settleOpen.resolve();
|
|
140
|
-
return { sessionId: handle, durableSessionId
|
|
145
|
+
return { sessionId: handle, durableSessionId, sessionPath: entry.sessionPath };
|
|
141
146
|
}
|
|
142
147
|
catch (error) {
|
|
143
148
|
// Runtime construction may have started extensions, watchers, and provider
|
|
@@ -156,7 +161,8 @@ export async function openRegistrySession(host, requested, options) {
|
|
|
156
161
|
}
|
|
157
162
|
finally {
|
|
158
163
|
host.entries.delete(handle);
|
|
159
|
-
|
|
164
|
+
if (!entry.speculative)
|
|
165
|
+
host.options.onSizeChange?.([...host.entries.values()].filter((item) => !item.speculative).length);
|
|
160
166
|
if (sessionPath)
|
|
161
167
|
await releaseWithinGrace(host.teardownHost, handle, sessionPath);
|
|
162
168
|
}
|
|
@@ -38,13 +38,14 @@ export function buildOpeningEntry(input) {
|
|
|
38
38
|
openCompletion.catch(() => { });
|
|
39
39
|
const entry = {
|
|
40
40
|
state: "opening",
|
|
41
|
+
...(options?.speculative ? { speculative: true } : {}),
|
|
41
42
|
scope: new ProviderScope(),
|
|
42
43
|
profile: storedProfile,
|
|
43
44
|
...sessionIdentity(storedProfile),
|
|
44
45
|
sessionPath,
|
|
45
46
|
reservationKey: sessionPath,
|
|
46
47
|
cwd: storedProfile.cwd,
|
|
47
|
-
attachments: 1,
|
|
48
|
+
attachments: options?.speculative ? 0 : 1,
|
|
48
49
|
retainOnDisconnect: options?.retainOnDisconnect === true,
|
|
49
50
|
lastCommandAt: input.now,
|
|
50
51
|
lifecycleMutex: Promise.resolve(),
|
|
@@ -24,6 +24,8 @@ export type SessionRuntime = AgentSessionRuntime;
|
|
|
24
24
|
export type RpcSessionState = "opening" | "open" | "closing" | "quarantined" | "closed";
|
|
25
25
|
export interface RpcSessionEntry {
|
|
26
26
|
state: RpcSessionState;
|
|
27
|
+
/** Built speculatively: no public identity, attachment, listing, or disk persistence until claimed. */
|
|
28
|
+
speculative?: boolean;
|
|
27
29
|
runtime?: SessionRuntime;
|
|
28
30
|
worker?: SessionWorkerClient;
|
|
29
31
|
/** The in-process session's ordered acceptance lane, present only while the compact-updates flag is on. */
|
|
@@ -116,6 +118,8 @@ export interface RpcSessionRegistryOptions {
|
|
|
116
118
|
}
|
|
117
119
|
/** Host-side lifecycle policy for one `open_session`, distinct from the session's launch profile. */
|
|
118
120
|
export interface RpcSessionOpenOptions {
|
|
121
|
+
/** Router-owned park construction; never accepted directly from the wire. */
|
|
122
|
+
speculative?: boolean;
|
|
119
123
|
/** Refuse unless the registry already owns the path; never prepare or construct a writer. */
|
|
120
124
|
attachOnly?: boolean;
|
|
121
125
|
/** Keep the session alive when its last client disconnects (`open_session.retain_on_disconnect`). */
|
|
@@ -4,6 +4,7 @@ export { frozenProfile, type OpenRpcSession, type RpcSessionEntry, type RpcSessi
|
|
|
4
4
|
/** Process-local lifecycle owner for multi-session RPC runtimes. */
|
|
5
5
|
export declare class RpcSessionRegistry {
|
|
6
6
|
readonly supportsCompact: (profile?: RpcSessionLaunchProfile) => boolean;
|
|
7
|
+
readonly supportsParkClaim = true;
|
|
7
8
|
private readonly entries;
|
|
8
9
|
private readonly reservations;
|
|
9
10
|
private readonly pendingClaims;
|
|
@@ -20,6 +21,8 @@ export declare class RpcSessionRegistry {
|
|
|
20
21
|
constructor(options: RpcSessionRegistryOptions);
|
|
21
22
|
/** Number of live entries, including ones still opening or closing. */
|
|
22
23
|
get size(): number;
|
|
24
|
+
/** Commit the ledger's exclusive claim after its existing runtime has acquired a persistent file. */
|
|
25
|
+
claimPark(handle: string, retainOnDisconnect: boolean): Promise<OpenRpcSession>;
|
|
23
26
|
/** Daemon-family identities come from the endpoint's claims and generation registry. */
|
|
24
27
|
holderPids(observedStarts?: ReadonlyMap<number, number | undefined>): Promise<readonly number[]>;
|
|
25
28
|
openSession(requested: RpcSessionLaunchProfile, options?: RpcSessionOpenOptions): Promise<OpenRpcSession>;
|
|
@@ -16,6 +16,7 @@ export class RpcSessionRegistry {
|
|
|
16
16
|
return this.options.agentDir;
|
|
17
17
|
}
|
|
18
18
|
constructor(options) {
|
|
19
|
+
this.supportsParkClaim = true;
|
|
19
20
|
this.entries = new Map();
|
|
20
21
|
this.reservations = new Set();
|
|
21
22
|
this.pendingClaims = new Map();
|
|
@@ -42,8 +43,10 @@ export class RpcSessionRegistry {
|
|
|
42
43
|
closeGraceMs: this.closeGraceMs,
|
|
43
44
|
get: (handle) => this.entries.get(handle),
|
|
44
45
|
delete: (handle) => {
|
|
46
|
+
const speculative = this.entries.get(handle)?.speculative;
|
|
45
47
|
const deleted = this.entries.delete(handle);
|
|
46
|
-
|
|
48
|
+
if (!speculative)
|
|
49
|
+
this.options.onSizeChange?.(this.size);
|
|
47
50
|
return deleted;
|
|
48
51
|
},
|
|
49
52
|
releaseReservation: async (key) => {
|
|
@@ -66,7 +69,26 @@ export class RpcSessionRegistry {
|
|
|
66
69
|
}
|
|
67
70
|
/** Number of live entries, including ones still opening or closing. */
|
|
68
71
|
get size() {
|
|
69
|
-
return this.entries.
|
|
72
|
+
return [...this.entries.values()].filter((entry) => !entry.speculative).length;
|
|
73
|
+
}
|
|
74
|
+
/** Commit the ledger's exclusive claim after its existing runtime has acquired a persistent file. */
|
|
75
|
+
async claimPark(handle, retainOnDisconnect) {
|
|
76
|
+
const entry = this.entries.get(handle);
|
|
77
|
+
if (!entry?.speculative || entry.state !== "open" || !entry.runtime)
|
|
78
|
+
throw new RpcSessionRegistryError("unknown_session");
|
|
79
|
+
await entry.runtime.persistSession();
|
|
80
|
+
// No await between the duplicate decision and publication: an ordinary open may have
|
|
81
|
+
// reserved this identity while persistence was being enabled.
|
|
82
|
+
for (const other of this.entries.values())
|
|
83
|
+
if (!other.speculative && other.durableSessionId === entry.runtime.session.sessionId)
|
|
84
|
+
throw new RpcSessionRegistryError("session_id_in_use");
|
|
85
|
+
entry.speculative = false;
|
|
86
|
+
entry.attachments = 1;
|
|
87
|
+
entry.retainOnDisconnect = retainOnDisconnect;
|
|
88
|
+
entry.lastCommandAt = this.now();
|
|
89
|
+
this.syncRuntimeMetadata();
|
|
90
|
+
this.options.onSizeChange?.(this.size);
|
|
91
|
+
return { sessionId: handle, durableSessionId: entry.runtime.session.sessionId, sessionPath: entry.sessionPath };
|
|
70
92
|
}
|
|
71
93
|
/** Daemon-family identities come from the endpoint's claims and generation registry. */
|
|
72
94
|
holderPids(observedStarts) {
|
|
@@ -143,7 +165,9 @@ export class RpcSessionRegistry {
|
|
|
143
165
|
list() {
|
|
144
166
|
this.syncRuntimeMetadata();
|
|
145
167
|
const now = this.now();
|
|
146
|
-
return [...this.entries]
|
|
168
|
+
return [...this.entries]
|
|
169
|
+
.filter(([, entry]) => !entry.speculative)
|
|
170
|
+
.map(([sessionId, entry]) => ({
|
|
147
171
|
sessionId,
|
|
148
172
|
durableSessionId: entry.durableSessionId,
|
|
149
173
|
sessionPath: entry.sessionPath,
|
|
@@ -208,6 +208,7 @@ export async function dispose(host) {
|
|
|
208
208
|
stopSweep(host);
|
|
209
209
|
if (host.drainTimer !== undefined)
|
|
210
210
|
clearInterval(host.drainTimer);
|
|
211
|
+
await host.parks.stop();
|
|
211
212
|
if (host.admissions)
|
|
212
213
|
await host.admissions.dispose();
|
|
213
214
|
await Promise.all([...host.bindings.entries()].map(async ([sessionId, binding]) => {
|
|
@@ -6,7 +6,9 @@ import type { SessionEventWriter } from "./session-event-writer.ts";
|
|
|
6
6
|
import type { RpcSessionRegistry } from "./session-registry.ts";
|
|
7
7
|
import { SessionAttachOrder } from "./session-attach-order.ts";
|
|
8
8
|
import type { RpcHostSessionDefaults, RpcSessionIdlePolicy } from "./session-router-types.ts";
|
|
9
|
+
import { HostParkLedger } from "./host-park-ledger.ts";
|
|
9
10
|
export declare class RouterContext {
|
|
11
|
+
readonly parks: HostParkLedger;
|
|
10
12
|
readonly admissions: CompactAdmissions | undefined;
|
|
11
13
|
readonly bindings: Map<string, RpcSessionBinding>;
|
|
12
14
|
readonly stopLimitBroadcast: () => void;
|
|
@@ -18,7 +20,7 @@ export declare class RouterContext {
|
|
|
18
20
|
readonly opensByConnection: Map<string, Set<Promise<void>>>;
|
|
19
21
|
/** Connections whose socket has already been detached. */
|
|
20
22
|
readonly releasedConnections: Set<string>;
|
|
21
|
-
readonly registry: Pick<RpcSessionRegistry, "openSession" | "peek" | "getForCommand" | "beginClose" | "close" | "closeMarked" | "list" | "size" | "warm" | "closeGraceMs"> & Partial<Pick<RpcSessionRegistry, "supportsCompact" | "holderPids" | "attachSession" | "agentDir">>;
|
|
23
|
+
readonly registry: Pick<RpcSessionRegistry, "openSession" | "peek" | "getForCommand" | "beginClose" | "close" | "closeMarked" | "list" | "size" | "warm" | "closeGraceMs"> & Partial<Pick<RpcSessionRegistry, "supportsCompact" | "supportsParkClaim" | "claimPark" | "holderPids" | "attachSession" | "agentDir">>;
|
|
22
24
|
readonly writer: SessionEventWriter;
|
|
23
25
|
readonly defaults: RpcHostSessionDefaults;
|
|
24
26
|
readonly createBinding: typeof createRpcSessionBinding;
|
|
@@ -59,6 +61,6 @@ export declare class RouterContext {
|
|
|
59
61
|
readonly mainHeapBytes: () => number;
|
|
60
62
|
readonly kernels: () => readonly RpcHostKernelMemory[];
|
|
61
63
|
} | undefined;
|
|
62
|
-
constructor(registry: Pick<RpcSessionRegistry, "openSession" | "peek" | "getForCommand" | "beginClose" | "close" | "closeMarked" | "list" | "size" | "warm" | "closeGraceMs"> & Partial<Pick<RpcSessionRegistry, "supportsCompact" | "holderPids" | "attachSession" | "agentDir">>, writer: SessionEventWriter, defaults: RpcHostSessionDefaults, createBinding?: typeof createRpcSessionBinding, connectionOptions?: Parameters<typeof createRpcSessionBinding>[4], idle?: RpcSessionIdlePolicy);
|
|
64
|
+
constructor(registry: Pick<RpcSessionRegistry, "openSession" | "peek" | "getForCommand" | "beginClose" | "close" | "closeMarked" | "list" | "size" | "warm" | "closeGraceMs"> & Partial<Pick<RpcSessionRegistry, "supportsCompact" | "supportsParkClaim" | "claimPark" | "holderPids" | "attachSession" | "agentDir">>, writer: SessionEventWriter, defaults: RpcHostSessionDefaults, createBinding?: typeof createRpcSessionBinding, connectionOptions?: Parameters<typeof createRpcSessionBinding>[4], idle?: RpcSessionIdlePolicy);
|
|
63
65
|
}
|
|
64
66
|
//# sourceMappingURL=session-router-context.d.ts.map
|
|
@@ -12,8 +12,11 @@ import { routingIdOf } from "./session-router-errors.js";
|
|
|
12
12
|
import { sweepIdleSessions } from "./session-router-occupancy.js";
|
|
13
13
|
import { followRetainedRecord } from "./session-router-residency.js";
|
|
14
14
|
import { registryStatusSource } from "./session-status-source.js";
|
|
15
|
+
import { HostParkLedger } from "./host-park-ledger.js";
|
|
16
|
+
import { closePark } from "./session-router-park.js";
|
|
15
17
|
export class RouterContext {
|
|
16
18
|
constructor(registry, writer, defaults, createBinding = createRpcSessionBinding, connectionOptions = {}, idle) {
|
|
19
|
+
this.parks = new HostParkLedger((opened) => closePark(this, opened));
|
|
17
20
|
this.admissions = compactUpdatesEnabled()
|
|
18
21
|
? new CompactAdmissions(ADMISSION_LIMITS, (socketEpoch, admissionId) => this.writer.routesAdmission(socketEpoch, admissionId))
|
|
19
22
|
: undefined;
|
|
@@ -14,6 +14,7 @@ import { sessionMemory } from "./session-router-observation.js";
|
|
|
14
14
|
export function answerProtocolInfo(host, command) {
|
|
15
15
|
const hostCapabilities = multiSessionHostCapabilities({
|
|
16
16
|
warm: host.registry.warm !== undefined,
|
|
17
|
+
parkClaim: host.registry.supportsParkClaim === true,
|
|
17
18
|
toolRuntime: host.defaults.toolRuntime ?? "in-process",
|
|
18
19
|
negotiated: host.connectionOptions?.capabilities ?? [],
|
|
19
20
|
mcpPresenters: host.writer.mcpPresenters !== undefined,
|
|
@@ -12,6 +12,15 @@ const DRAIN_SWEEP_MS = 50;
|
|
|
12
12
|
*/
|
|
13
13
|
export function beginDrain(host) {
|
|
14
14
|
host.draining = true;
|
|
15
|
+
void host.parks
|
|
16
|
+
.stop()
|
|
17
|
+
.catch((cause) => {
|
|
18
|
+
process.stderr.write(`senpi rpc park close failed during drain: ${String(cause)}\n`);
|
|
19
|
+
})
|
|
20
|
+
.then(() => sweepDrain(host))
|
|
21
|
+
.catch((cause) => {
|
|
22
|
+
process.stderr.write(`senpi rpc drain sweep failed after park close: ${String(cause)}\n`);
|
|
23
|
+
});
|
|
15
24
|
stopSweep(host);
|
|
16
25
|
// Armed BEFORE the first pass, so a pass that fails is retried instead of ending the drain
|
|
17
26
|
// (senpi#2285). Unref'd: a draining host must never be the reason the event loop stays open.
|
|
@@ -35,7 +44,7 @@ export function sweepDrain(host) {
|
|
|
35
44
|
parkForHandoff(host, sessionId, "session_dir_removed");
|
|
36
45
|
for (const sessionId of verdicts.park)
|
|
37
46
|
parkForHandoff(host, sessionId, "handoff_parked");
|
|
38
|
-
if (host.registry.size > 0 || host.handoffParks > 0 || host.finalizations.size > 0)
|
|
47
|
+
if (host.registry.size > 0 || host.parks.size > 0 || host.handoffParks > 0 || host.finalizations.size > 0)
|
|
39
48
|
return;
|
|
40
49
|
if (host.drainTimer !== undefined) {
|
|
41
50
|
clearInterval(host.drainTimer);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OpenRpcSession, RpcSessionEntry } from "./session-registry.ts";
|
|
2
|
+
import type { RouterContext } from "./session-router-context.ts";
|
|
3
|
+
/** A dropped opener waits until its open or claim has either transferred or released ownership. */
|
|
4
|
+
export declare function holdOpenConnection(host: RouterContext, owner: string): () => void;
|
|
5
|
+
/** One ownership transition for both a freshly built open and a claimed speculative open. */
|
|
6
|
+
export declare function ownOpen(host: RouterContext, opened: OpenRpcSession, owner: string | undefined, compact?: boolean): void;
|
|
7
|
+
export declare function publishOpen(host: RouterContext, opened: OpenRpcSession, entry: RpcSessionEntry): void;
|
|
8
|
+
export declare function replyOpen(host: RouterContext, opened: OpenRpcSession, entry: RpcSessionEntry, command: {
|
|
9
|
+
readonly type: "open_session" | "claim_session";
|
|
10
|
+
readonly id?: string;
|
|
11
|
+
}, reply?: boolean): void;
|
|
12
|
+
//# sourceMappingURL=session-router-open-result.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { openReplyPermissionMode } from "../../core/permission-mode.js";
|
|
2
|
+
import { buildRpcSessionState } from "./connection-handler.js";
|
|
3
|
+
/** A dropped opener waits until its open or claim has either transferred or released ownership. */
|
|
4
|
+
export function holdOpenConnection(host, owner) {
|
|
5
|
+
const barrier = Promise.withResolvers();
|
|
6
|
+
const opens = host.opensByConnection.get(owner) ?? new Set();
|
|
7
|
+
opens.add(barrier.promise);
|
|
8
|
+
host.opensByConnection.set(owner, opens);
|
|
9
|
+
return () => {
|
|
10
|
+
barrier.resolve();
|
|
11
|
+
opens.delete(barrier.promise);
|
|
12
|
+
if (opens.size === 0)
|
|
13
|
+
host.opensByConnection.delete(owner);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** One ownership transition for both a freshly built open and a claimed speculative open. */
|
|
17
|
+
export function ownOpen(host, opened, owner, compact = false) {
|
|
18
|
+
if (owner === undefined)
|
|
19
|
+
return;
|
|
20
|
+
if (!host.writer.hasRegisteredConnectionCapabilities(owner))
|
|
21
|
+
host.writer.setConnectionCapabilities(owner, host.pendingCapabilities.get(owner) ?? host.connectionOptions?.capabilities ?? []);
|
|
22
|
+
host.writer.attachConnectionToSession(owner, opened.sessionId, compact);
|
|
23
|
+
const owned = host.sessionsByConnection.get(owner) ?? new Map();
|
|
24
|
+
owned.set(opened.sessionId, (owned.get(opened.sessionId) ?? 0) + 1);
|
|
25
|
+
host.sessionsByConnection.set(owner, owned);
|
|
26
|
+
host.attachOrder.attached(opened.sessionId, owner);
|
|
27
|
+
}
|
|
28
|
+
export function publishOpen(host, opened, entry) {
|
|
29
|
+
if (opened.attached)
|
|
30
|
+
host.writer.status.publish(opened.sessionId, "attach", "client");
|
|
31
|
+
else {
|
|
32
|
+
host.writer.publishSessionOpened({
|
|
33
|
+
type: "session_opened",
|
|
34
|
+
sessionId: opened.sessionId,
|
|
35
|
+
durableSessionId: opened.durableSessionId,
|
|
36
|
+
...(opened.sessionPath !== undefined ? { sessionPath: opened.sessionPath } : {}),
|
|
37
|
+
kind: entry.kind,
|
|
38
|
+
cwd: entry.cwd,
|
|
39
|
+
context: entry.context,
|
|
40
|
+
});
|
|
41
|
+
host.writer.status.publish(opened.sessionId, "open", "client");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function replyOpen(host, opened, entry, command, reply = true) {
|
|
45
|
+
const state = entry.worker?.snapshot?.state ?? (entry.runtime ? buildRpcSessionState(entry.runtime.session) : undefined);
|
|
46
|
+
if (!state)
|
|
47
|
+
throw new Error("Session state was not created");
|
|
48
|
+
if (reply)
|
|
49
|
+
host.writer.enqueue(opened.sessionId, {
|
|
50
|
+
id: command.id,
|
|
51
|
+
type: "response",
|
|
52
|
+
command: command.type,
|
|
53
|
+
success: true,
|
|
54
|
+
data: {
|
|
55
|
+
sessionId: opened.sessionId,
|
|
56
|
+
state,
|
|
57
|
+
...openReplyPermissionMode(state),
|
|
58
|
+
...(opened.attached ? { attached: true } : {}),
|
|
59
|
+
...(host.defaults.sessionPresence ? { presence: host.defaults.sessionPresence.view(state.sessionId) } : {}),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=session-router-open-result.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type OpenAcceptance } from "./compact-open-admission.ts";
|
|
2
2
|
import type { HandoverParkedEntry } from "./host-handover-parked.ts";
|
|
3
3
|
import type { RpcCommand, RpcResponse } from "./rpc-types.ts";
|
|
4
|
+
import type { OpenRpcSession } from "./session-registry.ts";
|
|
4
5
|
import type { RouterContext } from "./session-router-context.ts";
|
|
5
6
|
export declare function openWithBarrier(host: RouterContext, command: Extract<RpcCommand, {
|
|
6
7
|
type: "open_session";
|
|
@@ -19,5 +20,7 @@ export declare function open(host: RouterContext, command: Extract<RpcCommand, {
|
|
|
19
20
|
readonly signal?: AbortSignal;
|
|
20
21
|
readonly defaults?: boolean;
|
|
21
22
|
readonly contextOverlay?: HandoverParkedEntry["contextOverlay"];
|
|
23
|
+
/** An invisible speculative open, published only when its ledger hands it to a caller. */
|
|
24
|
+
readonly park?: (opened: OpenRpcSession) => void;
|
|
22
25
|
}): Promise<RpcResponse | undefined>;
|
|
23
26
|
//# sourceMappingURL=session-router-open.d.ts.map
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { buildRpcSessionState } from "./connection-handler.js";
|
|
2
1
|
import { compactUpdatesEnabled } from "../../core/engine-socket-flag.js";
|
|
3
2
|
import { startOpenTimer } from "../../core/open-timing.js";
|
|
4
|
-
import { openReplyPermissionMode } from "../../core/permission-mode.js";
|
|
5
3
|
import { compactOpen } from "./compact-open-admission.js";
|
|
6
4
|
import { CompactAdmissionError } from "./compact-admission-error.js";
|
|
7
5
|
import { runAdmissionProducer } from "./compact-admission-producer.js";
|
|
@@ -12,6 +10,8 @@ import { close } from "./session-router-close.js";
|
|
|
12
10
|
import { code, detail, error } from "./session-router-errors.js";
|
|
13
11
|
import { sweepDrain } from "./session-router-occupancy.js";
|
|
14
12
|
import { invalidSpawnedBy, validatedOpenProfile } from "./session-router-open-profile.js";
|
|
13
|
+
import { holdOpenConnection, ownOpen, publishOpen, replyOpen } from "./session-router-open-result.js";
|
|
14
|
+
import { closePark } from "./session-router-park.js";
|
|
15
15
|
import { relocationConnection } from "./session-router-relocation.js";
|
|
16
16
|
import { syncRetainedRecord } from "./session-router-residency.js";
|
|
17
17
|
import { liveToolMediaScope, toolMediaPersister } from "./tool-media-store.js";
|
|
@@ -24,18 +24,12 @@ function openPhysicalWithBarrier(host, command, accept, signal) {
|
|
|
24
24
|
const owner = host.writer.currentConnection();
|
|
25
25
|
if (owner === undefined)
|
|
26
26
|
return open(host, command);
|
|
27
|
-
let resolveBarrier;
|
|
28
|
-
const barrier = new Promise((resolve) => {
|
|
29
|
-
resolveBarrier = resolve;
|
|
30
|
-
});
|
|
31
|
-
const opens = host.opensByConnection.get(owner) ?? new Set();
|
|
32
27
|
// Every open already accepted anywhere on this host, not just on this connection: the
|
|
33
28
|
// in-process runtime serves them on ONE loop, so the wait this caller faces is the total.
|
|
34
29
|
let inFlight = 0;
|
|
35
30
|
for (const pending of host.opensByConnection.values())
|
|
36
31
|
inFlight += pending.size;
|
|
37
|
-
|
|
38
|
-
host.opensByConnection.set(owner, opens);
|
|
32
|
+
const releaseBarrier = holdOpenConnection(host, owner);
|
|
39
33
|
// Before the open reaches the loop: a client that later hits its deadline can then say
|
|
40
34
|
// where it was queued instead of reporting a bare timeout (senpi#1844).
|
|
41
35
|
if (command.id !== undefined)
|
|
@@ -49,12 +43,7 @@ function openPhysicalWithBarrier(host, command, accept, signal) {
|
|
|
49
43
|
// A late open's physical session is released by its admission (the accept/rollback path, 4272-admission-
|
|
50
44
|
// abandonment), so the late response itself owns nothing to release here.
|
|
51
45
|
const opened = signal === undefined ? start() : runAdmissionProducer(signal, start, async () => { });
|
|
52
|
-
return opened.finally(
|
|
53
|
-
resolveBarrier();
|
|
54
|
-
opens.delete(barrier);
|
|
55
|
-
if (opens.size === 0)
|
|
56
|
-
host.opensByConnection.delete(owner);
|
|
57
|
-
});
|
|
46
|
+
return opened.finally(releaseBarrier);
|
|
58
47
|
}
|
|
59
48
|
/**
|
|
60
49
|
* The successor's reopen of a handoff-parked session: the same open as an `open_session`, with no
|
|
@@ -87,6 +76,7 @@ export async function open(host, command, owner, options) {
|
|
|
87
76
|
{
|
|
88
77
|
retainOnDisconnect: command.retain_on_disconnect === true,
|
|
89
78
|
attachOnly: command.expectedHostInstance !== undefined,
|
|
79
|
+
...(options?.park ? { speculative: true } : {}),
|
|
90
80
|
});
|
|
91
81
|
timer.mark("registry_open");
|
|
92
82
|
options?.signal?.throwIfAborted();
|
|
@@ -119,22 +109,11 @@ export async function open(host, command, owner, options) {
|
|
|
119
109
|
host.writer.registerAcceptance(openedSession.sessionId, lane);
|
|
120
110
|
if (options?.accept)
|
|
121
111
|
await options.accept(openedSession);
|
|
122
|
-
|
|
123
|
-
if (!host.writer.hasRegisteredConnectionCapabilities(owner))
|
|
124
|
-
host.writer.setConnectionCapabilities(owner, host.pendingCapabilities.get(owner) ?? host.connectionOptions?.capabilities ?? []);
|
|
125
|
-
host.writer.attachConnectionToSession(owner, openedSession.sessionId, options?.accept !== undefined);
|
|
126
|
-
}
|
|
127
|
-
// A client that dies without close_session (terminal closed, SIGKILL, dropped
|
|
128
|
-
// SSH) still holds this handle's attachment and its path reservation. Remember
|
|
129
|
-
// which connection owns it so releaseConnection() can close exactly that.
|
|
130
|
-
if (owner !== undefined) {
|
|
131
|
-
const owned = host.sessionsByConnection.get(owner) ?? new Map();
|
|
132
|
-
owned.set(openedSession.sessionId, (owned.get(openedSession.sessionId) ?? 0) + 1);
|
|
133
|
-
host.sessionsByConnection.set(owner, owned);
|
|
134
|
-
host.attachOrder.attached(openedSession.sessionId, owner);
|
|
135
|
-
}
|
|
112
|
+
ownOpen(host, openedSession, owner, options?.accept !== undefined);
|
|
136
113
|
if (!host.bindings.has(openedSession.sessionId)) {
|
|
137
|
-
host.bindings.set(openedSession.sessionId, await host.createBinding(openedSession.sessionId, entry, host.writer, () => void
|
|
114
|
+
host.bindings.set(openedSession.sessionId, await host.createBinding(openedSession.sessionId, entry, host.writer, () => void (entry.speculative
|
|
115
|
+
? closePark(host, openedSession)
|
|
116
|
+
: close(host, { type: "close_session", sessionId: openedSession.sessionId })), {
|
|
138
117
|
...host.connectionOptions,
|
|
139
118
|
capabilities: owner !== undefined
|
|
140
119
|
? (host.pendingCapabilities.get(owner) ?? host.connectionOptions?.capabilities ?? [])
|
|
@@ -181,20 +160,11 @@ export async function open(host, command, owner, options) {
|
|
|
181
160
|
});
|
|
182
161
|
}
|
|
183
162
|
options?.signal?.throwIfAborted();
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
host.writer.publishSessionOpened({
|
|
188
|
-
type: "session_opened",
|
|
189
|
-
sessionId: openedSession.sessionId,
|
|
190
|
-
durableSessionId: openedSession.durableSessionId,
|
|
191
|
-
...(openedSession.sessionPath !== undefined ? { sessionPath: openedSession.sessionPath } : {}),
|
|
192
|
-
kind: entry.kind,
|
|
193
|
-
cwd: entry.cwd,
|
|
194
|
-
context: entry.context,
|
|
195
|
-
});
|
|
196
|
-
host.writer.status.publish(openedSession.sessionId, "open", "client");
|
|
163
|
+
if (options?.park) {
|
|
164
|
+
options.park(openedSession);
|
|
165
|
+
return undefined;
|
|
197
166
|
}
|
|
167
|
+
publishOpen(host, openedSession, entry);
|
|
198
168
|
if (!options?.accept && !host.draining && owner !== undefined && host.releasedConnections.has(owner)) {
|
|
199
169
|
releaseOwnerAttachment(host, owner, openedSession.sessionId);
|
|
200
170
|
await releaseOwnedSession(host, openedSession.sessionId);
|
|
@@ -205,26 +175,8 @@ export async function open(host, command, owner, options) {
|
|
|
205
175
|
if (options?.accept) {
|
|
206
176
|
return undefined;
|
|
207
177
|
}
|
|
208
|
-
const replyState = entry.worker?.snapshot?.state ?? (entry.runtime ? buildRpcSessionState(entry.runtime.session) : undefined);
|
|
209
|
-
if (!replyState)
|
|
210
|
-
throw new Error("Session state was not created");
|
|
211
178
|
timer.mark("reply");
|
|
212
|
-
|
|
213
|
-
host.writer.enqueue(opened.sessionId, {
|
|
214
|
-
id: command.id,
|
|
215
|
-
type: "response",
|
|
216
|
-
command: "open_session",
|
|
217
|
-
success: true,
|
|
218
|
-
data: {
|
|
219
|
-
sessionId: opened.sessionId,
|
|
220
|
-
state: replyState,
|
|
221
|
-
...openReplyPermissionMode(replyState),
|
|
222
|
-
...(opened.attached ? { attached: true } : {}),
|
|
223
|
-
...(host.defaults.sessionPresence
|
|
224
|
-
? { presence: host.defaults.sessionPresence.view(replyState.sessionId) }
|
|
225
|
-
: {}),
|
|
226
|
-
},
|
|
227
|
-
});
|
|
179
|
+
replyOpen(host, opened, entry, command, options?.reply !== false);
|
|
228
180
|
return undefined;
|
|
229
181
|
}
|
|
230
182
|
catch (cause) {
|
|
@@ -233,7 +185,7 @@ export async function open(host, command, owner, options) {
|
|
|
233
185
|
// rollback, and only while the registry still holds the live entry, so only that
|
|
234
186
|
// rollback owes its subscribers the terminal record; an attach rollback, or one whose
|
|
235
187
|
// entry is already gone, never announced the session in the first place (#3198 review, L1).
|
|
236
|
-
if (!opened.attached && !options?.signal?.aborted) {
|
|
188
|
+
if (!options?.park && !opened.attached && !options?.signal?.aborted) {
|
|
237
189
|
const entry = host.registry.peek(opened.sessionId);
|
|
238
190
|
if (entry?.runtime !== undefined || entry?.worker !== undefined)
|
|
239
191
|
host.writer.sealRolledBackOpen(opened.sessionId, entry.sessionPath);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RpcCommand, RpcResponse } from "./rpc-types.ts";
|
|
2
|
+
import type { OpenRpcSession } from "./session-registry.ts";
|
|
3
|
+
import type { RouterContext } from "./session-router-context.ts";
|
|
4
|
+
type ParkCommand = Extract<RpcCommand, {
|
|
5
|
+
type: "park_session";
|
|
6
|
+
}>;
|
|
7
|
+
type ClaimCommand = Extract<RpcCommand, {
|
|
8
|
+
type: "claim_session";
|
|
9
|
+
}>;
|
|
10
|
+
/** Quiet teardown: speculation never owes a public terminal record or a handover park. */
|
|
11
|
+
export declare function closePark(host: RouterContext, opened: OpenRpcSession): Promise<void>;
|
|
12
|
+
export declare function parkSession(host: RouterContext, command: ParkCommand): Promise<RpcResponse>;
|
|
13
|
+
export declare function claimSession(host: RouterContext, command: ClaimCommand): Promise<RpcResponse | undefined>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=session-router-park.d.ts.map
|