@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
|
@@ -2,8 +2,9 @@ import { loadMcpSdkAuth } from "../sdk.lazy.js";
|
|
|
2
2
|
import { safeDelay } from "../wrap.js";
|
|
3
3
|
import { isInvalidGrant, isTransientTokenError, OAuthFlowError } from "./oauth-errors.js";
|
|
4
4
|
import { oauthFetch } from "./oauth-fetch.js";
|
|
5
|
-
import { credentialsMatchIssuer, validatedAuthorizationServerIssuer } from "./oauth-issuer.js";
|
|
6
|
-
import { mergeTokensIntoStoredAuth,
|
|
5
|
+
import { canonicalAuthorizationServerIssuer, credentialsMatchIssuer, validatedAuthorizationServerIssuer, } from "./oauth-issuer.js";
|
|
6
|
+
import { mergeTokensIntoStoredAuth, storedAuthToTokens, storedGrantIssuer, validGrantProof, withGrantAuthorization, } from "./oauth-grant.js";
|
|
7
|
+
const REFRESH_LEEWAY_MS = 5 * 60 * 1000;
|
|
7
8
|
export function isTokenStale(record, now) {
|
|
8
9
|
const token = record?.accessToken;
|
|
9
10
|
if (token === undefined || token.length === 0)
|
|
@@ -82,6 +83,7 @@ export class McpRefreshManager {
|
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
85
|
const resource = new URL(current.resource ?? this.#provider.serverUrl);
|
|
86
|
+
const proof = validGrantProof(current, this.#provider.grantContext);
|
|
85
87
|
const { refreshAuthorization } = await loadMcpSdkAuth();
|
|
86
88
|
const maxRetries = this.#options.maxRetries ?? 2;
|
|
87
89
|
for (let attempt = 0;; attempt++) {
|
|
@@ -93,10 +95,29 @@ export class McpRefreshManager {
|
|
|
93
95
|
resource,
|
|
94
96
|
fetchFn: oauthFetch(this.#options.fetchFn),
|
|
95
97
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
if (tokens.issuer !== undefined &&
|
|
99
|
+
canonicalAuthorizationServerIssuer(tokens.issuer) !==
|
|
100
|
+
canonicalAuthorizationServerIssuer(authorizationServer)) {
|
|
101
|
+
this.#provider.store.writeUnlocked(undefined);
|
|
102
|
+
throw new OAuthFlowError("needs_auth", "MCP refresh response names a different issuer.", {
|
|
103
|
+
serverName: this.#provider.serverName,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const next = mergeTokensIntoStoredAuth({ ...current, issuer: authorizationServer, resource: resource.href, refreshScope: proof?.refreshScope }, {
|
|
107
|
+
...tokens,
|
|
108
|
+
issuer: authorizationServer,
|
|
109
|
+
scope: tokens.scope === undefined ? proof?.refreshScope : tokens.scope,
|
|
110
|
+
});
|
|
111
|
+
const committed = withGrantAuthorization(next, this.#provider.grantContext, current);
|
|
112
|
+
this.#provider.store.writeUnlocked(committed);
|
|
113
|
+
const result = storedAuthToTokens(committed);
|
|
114
|
+
if (result === undefined)
|
|
115
|
+
throw new OAuthFlowError("needs_auth", "MCP refresh returned no access token.");
|
|
116
|
+
return result;
|
|
98
117
|
}
|
|
99
118
|
catch (error) {
|
|
119
|
+
if (error instanceof OAuthFlowError)
|
|
120
|
+
throw error;
|
|
100
121
|
if (isInvalidGrant(error)) {
|
|
101
122
|
// Terminal: drop credentials so the next use forces a clean re-auth.
|
|
102
123
|
this.#provider.store.writeUnlocked(undefined);
|
|
@@ -34,11 +34,11 @@ export async function finishAuthorization(provider, code, options = {}, iss) {
|
|
|
34
34
|
let result;
|
|
35
35
|
const { auth } = await loadMcpSdkAuth();
|
|
36
36
|
try {
|
|
37
|
-
result = await auth(provider, {
|
|
37
|
+
result = await provider.withFreshAuthorization(() => auth(provider, {
|
|
38
38
|
serverUrl: provider.serverUrl,
|
|
39
39
|
authorizationCode: code,
|
|
40
40
|
fetchFn: oauthFetch(options.fetchFn),
|
|
41
|
-
});
|
|
41
|
+
}));
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
44
|
if (isInvalidGrant(error) || isRejectedAuthorizationCode(error)) {
|
|
@@ -111,12 +111,15 @@ export async function clientCredentialsGrant(provider, options = {}) {
|
|
|
111
111
|
},
|
|
112
112
|
};
|
|
113
113
|
const { fetchToken } = await loadMcpSdkAuth();
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
await provider.validateResourceURL(provider.serverUrl, provider.serverUrl);
|
|
115
|
+
await provider.withFreshAuthorization(async () => {
|
|
116
|
+
const tokens = await fetchToken(credentialsProvider, info.authorizationServerUrl, {
|
|
117
|
+
metadata: info.authorizationServerMetadata,
|
|
118
|
+
resource: new URL(provider.serverUrl),
|
|
119
|
+
fetchFn: oauthFetch(options.fetchFn),
|
|
120
|
+
});
|
|
121
|
+
await provider.saveTokens({ ...tokens, issuer: validatedAuthorizationServerIssuer(info, provider.serverName) });
|
|
118
122
|
});
|
|
119
|
-
await provider.saveTokens({ ...tokens, issuer: validatedAuthorizationServerIssuer(info, provider.serverName) });
|
|
120
123
|
}
|
|
121
124
|
export async function logout(provider) {
|
|
122
125
|
await provider.store.clear();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { McpClientRegistration, McpStoredAuth } from "./token-store.ts";
|
|
2
|
+
/** The provider options that decide which registration a flow uses. */
|
|
3
|
+
export interface RegistrationFlow {
|
|
4
|
+
readonly redirectUrl?: string | URL | undefined;
|
|
5
|
+
/** The flow sends an authorize request to `redirectUrl`. */
|
|
6
|
+
readonly requireRegisteredRedirect?: boolean | undefined;
|
|
7
|
+
}
|
|
8
|
+
/** The registration a flow uses: for an authorizing flow, the signed-in or staged client naming its redirect URI. */
|
|
9
|
+
export declare function registrationForFlow(record: McpStoredAuth | undefined, flow: RegistrationFlow): McpClientRegistration | undefined;
|
|
10
|
+
/** The stored grant as a flow may present it: a staged client has no grant, so the refresh token is withheld. */
|
|
11
|
+
export declare function grantForFlow(record: McpStoredAuth | undefined, flow: RegistrationFlow): McpStoredAuth | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Stores a registration. An authorizing flow stages a client that would replace the signed-in one; otherwise a
|
|
14
|
+
* replaced client's refresh token goes with it, because the SDK would present it under the new `client_id`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function withRegistration(current: McpStoredAuth | undefined, clientInfo: McpClientRegistration, flow: RegistrationFlow): McpStoredAuth;
|
|
17
|
+
/** After an authorizing flow's code exchange: its staged client replaces the signed-in one and that client's grant. */
|
|
18
|
+
export declare function withPromotedRegistration(current: McpStoredAuth | undefined, flow: RegistrationFlow): McpStoredAuth | undefined;
|
|
19
|
+
//# sourceMappingURL=registered-redirect.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Matched against the redirect URIs the client sent when it registered: an authorization server may echo them
|
|
2
|
+
// rewritten. A registration saved before that record existed falls back to the echo; one with neither is kept.
|
|
3
|
+
function registrationNamesRedirect(registration, redirectUrl) {
|
|
4
|
+
const registered = registration.redirectUrisSent ?? registration.redirect_uris;
|
|
5
|
+
if (registered === undefined || redirectUrl === undefined)
|
|
6
|
+
return true;
|
|
7
|
+
return registered.some((uri) => String(uri) === String(redirectUrl));
|
|
8
|
+
}
|
|
9
|
+
/** The registration a flow uses: for an authorizing flow, the signed-in or staged client naming its redirect URI. */
|
|
10
|
+
export function registrationForFlow(record, flow) {
|
|
11
|
+
if (flow.requireRegisteredRedirect !== true)
|
|
12
|
+
return record?.clientInfo;
|
|
13
|
+
return [record?.clientInfo, record?.pendingClientInfo].find((registration) => registration !== undefined && registrationNamesRedirect(registration, flow.redirectUrl));
|
|
14
|
+
}
|
|
15
|
+
function usesStagedRegistration(record, flow) {
|
|
16
|
+
const registration = registrationForFlow(record, flow);
|
|
17
|
+
return registration !== undefined && registration === record?.pendingClientInfo;
|
|
18
|
+
}
|
|
19
|
+
/** The stored grant as a flow may present it: a staged client has no grant, so the refresh token is withheld. */
|
|
20
|
+
export function grantForFlow(record, flow) {
|
|
21
|
+
if (record === undefined || !usesStagedRegistration(record, flow))
|
|
22
|
+
return record;
|
|
23
|
+
const grant = { ...record };
|
|
24
|
+
delete grant.refreshToken;
|
|
25
|
+
return grant;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Stores a registration. An authorizing flow stages a client that would replace the signed-in one; otherwise a
|
|
29
|
+
* replaced client's refresh token goes with it, because the SDK would present it under the new `client_id`.
|
|
30
|
+
*/
|
|
31
|
+
export function withRegistration(current, clientInfo, flow) {
|
|
32
|
+
const known = [current?.clientInfo, current?.pendingClientInfo].some((registration) => registration?.client_id === clientInfo.client_id);
|
|
33
|
+
// The SDK registers with `clientMetadata.redirect_uris`, which is exactly the flow's redirect URL.
|
|
34
|
+
const registration = known || flow.redirectUrl === undefined
|
|
35
|
+
? clientInfo
|
|
36
|
+
: { ...clientInfo, redirectUrisSent: [String(flow.redirectUrl)] };
|
|
37
|
+
const replaced = current?.clientInfo?.client_id;
|
|
38
|
+
if (replaced === undefined || replaced === registration.client_id)
|
|
39
|
+
return { ...(current ?? {}), clientInfo: registration };
|
|
40
|
+
if (flow.requireRegisteredRedirect === true)
|
|
41
|
+
return { ...current, pendingClientInfo: registration };
|
|
42
|
+
const next = { ...current, clientInfo: registration };
|
|
43
|
+
delete next.refreshToken;
|
|
44
|
+
return next;
|
|
45
|
+
}
|
|
46
|
+
/** After an authorizing flow's code exchange: its staged client replaces the signed-in one and that client's grant. */
|
|
47
|
+
export function withPromotedRegistration(current, flow) {
|
|
48
|
+
if (current?.pendingClientInfo === undefined || !usesStagedRegistration(current, flow))
|
|
49
|
+
return current;
|
|
50
|
+
const next = { ...current, clientInfo: current.pendingClientInfo };
|
|
51
|
+
delete next.pendingClientInfo;
|
|
52
|
+
delete next.refreshToken;
|
|
53
|
+
return next;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=registered-redirect.js.map
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
import type { OAuthDiscoveryState } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
2
2
|
import type { OAuthClientInformationFull } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
3
|
+
export type McpClientRegistration = OAuthClientInformationFull & {
|
|
4
|
+
redirectUrisSent?: string[];
|
|
5
|
+
};
|
|
6
|
+
export interface McpAuthorizationProof {
|
|
7
|
+
readonly credentialIdentity: string;
|
|
8
|
+
readonly previousCredentialIdentity?: string;
|
|
9
|
+
readonly issuer: string;
|
|
10
|
+
readonly resource: string;
|
|
11
|
+
readonly clientIdentity: string;
|
|
12
|
+
readonly scope: string;
|
|
13
|
+
readonly refreshScope?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface McpAuthorization {
|
|
16
|
+
readonly version: 1;
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly proof?: McpAuthorizationProof;
|
|
19
|
+
}
|
|
3
20
|
export interface McpStoredAuth {
|
|
21
|
+
readonly scope?: string;
|
|
22
|
+
readonly refreshScope?: string;
|
|
23
|
+
readonly authorization?: McpAuthorization;
|
|
4
24
|
accessToken?: string;
|
|
5
25
|
refreshToken?: string;
|
|
6
|
-
clientInfo?:
|
|
26
|
+
clientInfo?: McpClientRegistration;
|
|
27
|
+
pendingClientInfo?: McpClientRegistration;
|
|
7
28
|
codeVerifier?: string;
|
|
8
29
|
discoveryState?: OAuthDiscoveryState;
|
|
9
30
|
resource?: string;
|
|
@@ -33,6 +54,7 @@ export declare class McpTokenStore<TRecord extends McpStoredAuth = McpStoredAuth
|
|
|
33
54
|
readonly serverUrl: string;
|
|
34
55
|
constructor(options: TokenStoreOptions);
|
|
35
56
|
get rootDir(): string;
|
|
57
|
+
get agentDir(): string;
|
|
36
58
|
get dir(): string;
|
|
37
59
|
get tokensPath(): string;
|
|
38
60
|
get lockPath(): string;
|
|
@@ -39,24 +39,25 @@ export function cachedCatalogNeedsRefresh(cached, now = Date.now()) {
|
|
|
39
39
|
return now - cached.fetchedAt > CACHE_TTL_MS;
|
|
40
40
|
}
|
|
41
41
|
export async function collectServerCatalogForCache(connection, config, configHash, credentialIdentity) {
|
|
42
|
-
const
|
|
42
|
+
const client = connection.client;
|
|
43
|
+
const tools = await collectAllPages((cursor) => listMcpTools(client, cursor === undefined ? {} : { cursor }, {
|
|
43
44
|
timeout: config.requestTimeoutMs,
|
|
44
45
|
}));
|
|
45
|
-
const resources = await collectOptionalPages((cursor) =>
|
|
46
|
+
const resources = await collectOptionalPages((cursor) => client.listResources(cursor === undefined ? {} : { cursor }, {
|
|
46
47
|
timeout: config.requestTimeoutMs,
|
|
47
48
|
}));
|
|
48
|
-
const prompts = await collectOptionalPages((cursor) =>
|
|
49
|
+
const prompts = await collectOptionalPages((cursor) => client.listPrompts(cursor === undefined ? {} : { cursor }, {
|
|
49
50
|
timeout: config.requestTimeoutMs,
|
|
50
51
|
}));
|
|
51
|
-
const resourceTemplates = await collectOptionalPages((cursor) =>
|
|
52
|
+
const resourceTemplates = await collectOptionalPages((cursor) => client.listResourceTemplates(cursor === undefined ? {} : { cursor }, {
|
|
52
53
|
timeout: config.requestTimeoutMs,
|
|
53
54
|
}));
|
|
54
55
|
return {
|
|
55
56
|
configHash,
|
|
56
|
-
...(isModernMcpClient(
|
|
57
|
+
...(isModernMcpClient(client) ? { modern: true } : {}),
|
|
57
58
|
credentialIdentity,
|
|
58
59
|
fetchedAt: Date.now(),
|
|
59
|
-
instructions:
|
|
60
|
+
instructions: client.getInstructions(),
|
|
60
61
|
prompts,
|
|
61
62
|
resources,
|
|
62
63
|
resourceTemplates,
|
|
@@ -3,7 +3,8 @@ import { isModernMcpClient } from "./modern/request.js";
|
|
|
3
3
|
import { hasSupportedModernToolSchemas } from "./modern/tool-schema.js";
|
|
4
4
|
import { collectAllPages } from "./expose/pagination.js";
|
|
5
5
|
export async function collectToolCatalog(server, connection, config, options = {}) {
|
|
6
|
-
const
|
|
6
|
+
const client = connection.client;
|
|
7
|
+
const result = await collectAllPages((cursor) => listMcpTools(client, cursor === undefined ? {} : { cursor }, { timeout: config.requestTimeoutMs }));
|
|
7
8
|
return result.items.map((tool) => ({
|
|
8
9
|
requestOrigin: options.requestOrigin,
|
|
9
10
|
annotations: tool.annotations,
|
|
@@ -17,7 +18,7 @@ export async function collectToolCatalog(server, connection, config, options = {
|
|
|
17
18
|
requestTimeoutMs: config.requestTimeoutMs,
|
|
18
19
|
schema: tool.inputSchema,
|
|
19
20
|
outputSchema: tool.outputSchema,
|
|
20
|
-
modern: isModernMcpClient(
|
|
21
|
+
modern: isModernMcpClient(client),
|
|
21
22
|
server,
|
|
22
23
|
tool: tool.name,
|
|
23
24
|
}));
|
|
@@ -61,6 +61,12 @@ export function createMcpInvocationResolver(scope) {
|
|
|
61
61
|
try {
|
|
62
62
|
await Promise.race([ready, interrupted]);
|
|
63
63
|
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
readySignal.throwIfAborted();
|
|
66
|
+
if (!scope.isCurrent())
|
|
67
|
+
return refuse("unavailable", "MCP session or server configuration was replaced.");
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
64
70
|
finally {
|
|
65
71
|
clearTimeout(timeout);
|
|
66
72
|
readySignal.removeEventListener("abort", abort);
|
|
@@ -3,7 +3,7 @@ import { computeMcpExposurePolicy } from "./expose/policy.js";
|
|
|
3
3
|
import { registerDirectMcpTools } from "./expose/session.js";
|
|
4
4
|
import { ensureMcpToolCallConnection } from "./health.js";
|
|
5
5
|
import { createMcpInvocationResolver } from "./invocation.js";
|
|
6
|
-
import { connectAndRefreshMcpCatalog } from "./startup-race.js";
|
|
6
|
+
import { connectAndRefreshMcpCatalog, prepareMcpAuthentication } from "./startup-race.js";
|
|
7
7
|
export async function registerMcpServiceDirectTools(pi, config, entries, toolSearchService, options = {}) {
|
|
8
8
|
return await registerDirectMcpTools(pi, config, [...entries].map((entry) => {
|
|
9
9
|
const serverConfig = config.servers[entry.name]?.config;
|
|
@@ -18,7 +18,21 @@ export async function registerMcpServiceDirectTools(pi, config, entries, toolSea
|
|
|
18
18
|
servesSession: options.servesSession,
|
|
19
19
|
isCurrent: () => entry.isCurrent?.() !== false && options.isCurrent?.(entry) !== false,
|
|
20
20
|
ready: async () => {
|
|
21
|
-
|
|
21
|
+
if (!(await prepareMcpAuthentication(entry)))
|
|
22
|
+
return;
|
|
23
|
+
if (entry.isCurrent?.() === false || options.isCurrent?.(entry) === false)
|
|
24
|
+
return;
|
|
25
|
+
if (entry.credentialsCurrent?.() === false) {
|
|
26
|
+
await entry.onCredentialsChanged?.();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
await ensureMcpToolCallConnection(entry.connection);
|
|
30
|
+
if (entry.isCurrent?.() === false || options.isCurrent?.(entry) === false)
|
|
31
|
+
return;
|
|
32
|
+
if (entry.credentialsCurrent?.() === false) {
|
|
33
|
+
await entry.onCredentialsChanged?.();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
22
36
|
await connectAndRefreshMcpCatalog(entry, serverConfig);
|
|
23
37
|
await entry.startupCatalogClaim?.settled();
|
|
24
38
|
if (entry.isCurrent?.() === false || options.isCurrent?.(entry) === false)
|
|
@@ -54,71 +54,78 @@ export async function refreshMcpToolsOnListChanged(entry, targets, config, conne
|
|
|
54
54
|
// The startup connect that still owns registration registers its refreshed catalog itself.
|
|
55
55
|
if (connectOnly && entry.startupCatalogClaim?.ownsRegistration() === true)
|
|
56
56
|
return;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return;
|
|
62
|
-
entry.cachedCatalog = refreshed;
|
|
63
|
-
}
|
|
64
|
-
const catalog = await collectToolCatalog(entry.name, entry.connection, server.config, {
|
|
65
|
-
agentDir: entry.agentDir,
|
|
66
|
-
outputGuard: config.settings.outputGuard,
|
|
67
|
-
});
|
|
68
|
-
if (!ownsCatalog())
|
|
69
|
-
return;
|
|
70
|
-
const newNames = mapMcpCatalogNames(catalog).map(({ name }) => name);
|
|
71
|
-
// Before the first refresh, the registered names are the catalog the startup pass registered.
|
|
72
|
-
const knownNames = entry.knownToolNames ??
|
|
73
|
-
(registeredCatalog === undefined
|
|
74
|
-
? newNames
|
|
75
|
-
: mapMcpCatalogNames(cachedToolsToCatalogEntries(entry.name, registeredCatalog, entry.connection, server.config.requestTimeoutMs, async () => { })).map(({ name }) => name));
|
|
76
|
-
const diff = diffMcpToolNames(knownNames, newNames);
|
|
77
|
-
const identity = mcpRegistrationIdentity(catalog, entry.cachedCatalog);
|
|
78
|
-
const initial = targets();
|
|
79
|
-
const stale = connectOnly ? initial.filter((target) => target.registeredIdentity() !== identity) : initial;
|
|
80
|
-
if (stale.length > 0) {
|
|
81
|
-
// Registration reads entry.cachedCatalog. A shared lease refreshed it above; nothing else
|
|
82
|
-
// refreshes a non-shared connection's catalog after its startup connect (#2188).
|
|
83
|
-
if (!(entry.connection instanceof SharedMcpLease)) {
|
|
84
|
-
const refreshed = await collectServerCatalogForCache(entry.connection, server.config, entry.generationHash ?? entry.configHash, entry.credentialIdentity);
|
|
57
|
+
try {
|
|
58
|
+
const registeredCatalog = entry.cachedCatalog;
|
|
59
|
+
if (entry.connection instanceof SharedMcpLease) {
|
|
60
|
+
const refreshed = await entry.connection.catalog();
|
|
85
61
|
if (!ownsCatalog())
|
|
86
62
|
return;
|
|
87
63
|
entry.cachedCatalog = refreshed;
|
|
88
|
-
await writeMcpCachedServer(entry.agentDir, entry.name, refreshed, ownsCatalog);
|
|
89
64
|
}
|
|
65
|
+
const catalog = await collectToolCatalog(entry.name, entry.connection, server.config, {
|
|
66
|
+
agentDir: entry.agentDir,
|
|
67
|
+
outputGuard: config.settings.outputGuard,
|
|
68
|
+
});
|
|
90
69
|
if (!ownsCatalog())
|
|
91
70
|
return;
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
71
|
+
const newNames = mapMcpCatalogNames(catalog).map(({ name }) => name);
|
|
72
|
+
// Before the first refresh, the registered names are the catalog the startup pass registered.
|
|
73
|
+
const knownNames = entry.knownToolNames ??
|
|
74
|
+
(registeredCatalog === undefined
|
|
75
|
+
? newNames
|
|
76
|
+
: mapMcpCatalogNames(cachedToolsToCatalogEntries(entry.name, registeredCatalog, entry.connection, server.config.requestTimeoutMs, async () => { })).map(({ name }) => name));
|
|
77
|
+
const diff = diffMcpToolNames(knownNames, newNames);
|
|
78
|
+
const identity = mcpRegistrationIdentity(catalog, entry.cachedCatalog);
|
|
79
|
+
const initial = targets();
|
|
80
|
+
const stale = connectOnly ? initial.filter((target) => target.registeredIdentity() !== identity) : initial;
|
|
81
|
+
if (stale.length > 0) {
|
|
82
|
+
// Registration reads entry.cachedCatalog. A shared lease refreshed it above; nothing else
|
|
83
|
+
// refreshes a non-shared connection's catalog after its startup connect (#2188).
|
|
84
|
+
if (!(entry.connection instanceof SharedMcpLease)) {
|
|
85
|
+
const refreshed = await collectServerCatalogForCache(entry.connection, server.config, entry.generationHash ?? entry.configHash, entry.credentialIdentity);
|
|
99
86
|
if (!ownsCatalog())
|
|
100
87
|
return;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
88
|
+
entry.cachedCatalog = refreshed;
|
|
89
|
+
await writeMcpCachedServer(entry.agentDir, entry.name, refreshed, ownsCatalog);
|
|
90
|
+
}
|
|
91
|
+
if (!ownsCatalog())
|
|
92
|
+
return;
|
|
93
|
+
// Tombstone removed tools BEFORE re-registration so the subsequent
|
|
94
|
+
// setActiveTools (which excludes them) leaves the tombstones inactive.
|
|
95
|
+
const seen = new Set(initial.map((target) => target.pi));
|
|
96
|
+
const failures = [];
|
|
97
|
+
let pending = stale;
|
|
98
|
+
while (pending.length > 0) {
|
|
99
|
+
for (const target of pending) {
|
|
100
|
+
if (!ownsCatalog())
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
for (const removed of diff.removed)
|
|
104
|
+
target.pi.registerTool(buildMcpTombstoneDefinition(removed, entry.name));
|
|
105
|
+
await target.register();
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
failures.push(error);
|
|
109
|
+
}
|
|
108
110
|
}
|
|
111
|
+
// A session that attached during this refresh registered the catalog cached before it.
|
|
112
|
+
pending = targets().filter((target) => !seen.has(target.pi));
|
|
113
|
+
for (const target of pending)
|
|
114
|
+
seen.add(target.pi);
|
|
115
|
+
}
|
|
116
|
+
if (failures.length > 0) {
|
|
117
|
+
entry.knownToolNames = newNames;
|
|
118
|
+
entry.lastListChangedDelta = formatMcpListChangedDelta(diff);
|
|
119
|
+
throw new AggregateError(failures, `MCP ${entry.name} tool refresh failed in ${failures.length} of ${seen.size} session(s)`);
|
|
109
120
|
}
|
|
110
|
-
// A session that attached during this refresh registered the catalog cached before it.
|
|
111
|
-
pending = targets().filter((target) => !seen.has(target.pi));
|
|
112
|
-
for (const target of pending)
|
|
113
|
-
seen.add(target.pi);
|
|
114
|
-
}
|
|
115
|
-
if (failures.length > 0) {
|
|
116
|
-
entry.knownToolNames = newNames;
|
|
117
|
-
entry.lastListChangedDelta = formatMcpListChangedDelta(diff);
|
|
118
|
-
throw new AggregateError(failures, `MCP ${entry.name} tool refresh failed in ${failures.length} of ${seen.size} session(s)`);
|
|
119
121
|
}
|
|
122
|
+
entry.knownToolNames = newNames;
|
|
123
|
+
entry.lastListChangedDelta = formatMcpListChangedDelta(diff);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (!ownsCatalog())
|
|
127
|
+
return;
|
|
128
|
+
throw error;
|
|
120
129
|
}
|
|
121
|
-
entry.knownToolNames = newNames;
|
|
122
|
-
entry.lastListChangedDelta = formatMcpListChangedDelta(diff);
|
|
123
130
|
}
|
|
124
131
|
//# sourceMappingURL=service-tools-changed.js.map
|
|
@@ -43,6 +43,8 @@ export declare class McpDeferredAttach {
|
|
|
43
43
|
}
|
|
44
44
|
export declare function raceMcpStartupConnect(options: RaceMcpStartupConnectOptions): Promise<void>;
|
|
45
45
|
export declare function connectAndRefreshMcpCatalog(entry: McpConnectionEntry, serverConfig: ResolvedMcpServer["config"]): Promise<void>;
|
|
46
|
+
/** Reconcile the grant before health recovery without attempting a transport connection. */
|
|
47
|
+
export declare function prepareMcpAuthentication(entry: McpConnectionEntry): Promise<boolean>;
|
|
46
48
|
export declare function ignoreStartupNeedsAuth(entry: McpConnectionEntry, connect: Promise<void>): Promise<void>;
|
|
47
49
|
export declare function waitForMcpStartupRace(connect: Promise<void>, deadlineMs?: number): Promise<McpStartupRaceResult>;
|
|
48
50
|
export declare function shouldRaceMcpStartup(lifecycle: "lazy" | "eager" | "keep-alive"): boolean;
|
|
@@ -91,15 +91,16 @@ export function connectAndRefreshMcpCatalog(entry, serverConfig) {
|
|
|
91
91
|
void refresh.then(finish, finish);
|
|
92
92
|
return refresh;
|
|
93
93
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
/** Reconcile the grant before health recovery without attempting a transport connection. */
|
|
95
|
+
export async function prepareMcpAuthentication(entry) {
|
|
96
|
+
if (entry.isCurrent?.() === false)
|
|
97
|
+
return false;
|
|
97
98
|
try {
|
|
98
99
|
await entry.authPlan?.refresh?.ensureFresh();
|
|
99
100
|
}
|
|
100
101
|
catch (error) {
|
|
101
|
-
if (entry.connection.state === "disabled")
|
|
102
|
-
return;
|
|
102
|
+
if (entry.isCurrent?.() === false || entry.connection.state === "disabled")
|
|
103
|
+
return false;
|
|
103
104
|
const authError = markMcpConnectionNeedsAuth(entry.connection, error);
|
|
104
105
|
if (authError !== undefined) {
|
|
105
106
|
entry.logger.warn(authError.message);
|
|
@@ -107,10 +108,17 @@ async function refreshConnectedMcpCatalog(entry, serverConfig) {
|
|
|
107
108
|
}
|
|
108
109
|
throw error;
|
|
109
110
|
}
|
|
111
|
+
if (entry.isCurrent?.() === false)
|
|
112
|
+
return false;
|
|
110
113
|
if (entry.credentialsCurrent?.() === false) {
|
|
111
114
|
await entry.onCredentialsChanged?.();
|
|
112
|
-
return;
|
|
115
|
+
return false;
|
|
113
116
|
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
async function refreshConnectedMcpCatalog(entry, serverConfig) {
|
|
120
|
+
if (serverConfig === undefined || !(await prepareMcpAuthentication(entry)))
|
|
121
|
+
return;
|
|
114
122
|
await connectMcpServer(entry.connection, entry.logger);
|
|
115
123
|
if (entry.connection.state !== "connected" || entry.isCurrent?.() === false)
|
|
116
124
|
return;
|
|
@@ -120,7 +128,15 @@ async function refreshConnectedMcpCatalog(entry, serverConfig) {
|
|
|
120
128
|
entry.connection.state === "connected" &&
|
|
121
129
|
entry.connection.generation === generation;
|
|
122
130
|
if (entry.connection instanceof SharedMcpLease) {
|
|
123
|
-
|
|
131
|
+
let catalog;
|
|
132
|
+
try {
|
|
133
|
+
catalog = await entry.connection.catalog();
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (!ownsCatalog())
|
|
137
|
+
return;
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
124
140
|
if (!ownsCatalog()) {
|
|
125
141
|
if (entry.credentialsCurrent?.() === false)
|
|
126
142
|
await entry.onCredentialsChanged?.();
|
|
@@ -150,6 +166,8 @@ async function refreshConnectedMcpCatalog(entry, serverConfig) {
|
|
|
150
166
|
await ensureMcpResourceSubscriptions(entry.connection.client, catalog.resources ?? []);
|
|
151
167
|
}
|
|
152
168
|
catch (error) {
|
|
169
|
+
if (!ownsCatalog())
|
|
170
|
+
return;
|
|
153
171
|
entry.logger.warn("Failed to refresh MCP catalog cache", {
|
|
154
172
|
error: error instanceof Error ? error.message : String(error),
|
|
155
173
|
});
|
|
@@ -203,6 +203,7 @@ export function toolOwnedPermissionRequests(tools, toolName, input, cwd) {
|
|
|
203
203
|
permission: request.permission,
|
|
204
204
|
patterns: [...request.patterns],
|
|
205
205
|
always: [...request.always],
|
|
206
|
+
...(request.autoApproveAsk === true ? { autoApproveAsk: true } : {}),
|
|
206
207
|
}));
|
|
207
208
|
}
|
|
208
209
|
/** Registry for tool-specific permission parsers */
|
|
@@ -85,7 +85,7 @@ ${buildSearchLine(context)}
|
|
|
85
85
|
Route by true intent, not surface form:
|
|
86
86
|
- Information asks (explain, look into, investigate): read the code and report; no edits.
|
|
87
87
|
- Judgment asks (what do you think, review) and open-ended changes (refactor, improve, clean up): assess and propose, then wait for confirmation.
|
|
88
|
-
- Change asks (implement, add, fix this error): build, or diagnose and fix
|
|
88
|
+
- Change asks (implement, add, fix this error): build, or diagnose and fix at the cause.
|
|
89
89
|
|
|
90
90
|
Derive intent from the latest user turn alone: a new direction drops the stale plan, and queued steering messages outrank earlier intent.
|
|
91
91
|
|
|
@@ -93,7 +93,7 @@ Derive intent from the latest user turn alone: a new direction drops the stale p
|
|
|
93
93
|
|
|
94
94
|
The request sets the scope, and the scope is the deliverable: deliver all of it and only it. Make routine judgment calls yourself; ask only when different readings would lead to materially different work, and ask after doing everything that does not depend on the answer, through ask_user_question when it is available (waitForAnswer true when the next step depends on the answer). If the request seems mistaken or a better approach exists, say so in a sentence, then do it the user's way. If part of the task is blocked, finish every other part and say exactly what you left out and why.
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
This codebase is yours: what it's for and how it's built are your call, and you'll keep building on it. Change it the way its owner would: build on the code that already does the job instead of adding a parallel copy, fit new behavior into the structure that is there, and leave no duplicate or dead code behind. A defect in the code you are changing is yours to fix now; one elsewhere goes in your summary as a follow-up. Add no helpers or abstractions for hypothetical needs and no defensive checks inside trusted code; validate only at system boundaries. Scratch checks verify and get discarded; commit tests only where the task asks for them or the repository already keeps tests for that kind of change, sized like the neighboring test files. Prefer a surgical edit over rewriting a file when the result would be identical.
|
|
97
97
|
|
|
98
98
|
## Working the Task
|
|
99
99
|
|
|
@@ -107,7 +107,7 @@ Scale the checks to the change, never the rigor: diagnostics on every changed fi
|
|
|
107
107
|
|
|
108
108
|
${buildTestDisciplineSection(context.sharedSections)}
|
|
109
109
|
|
|
110
|
-
Before reporting progress, audit each claim against a tool result from this session; ${context.surface !== "terminal" ? `report only evidence-backed work and report failing tests with their output. ${APP_UNRUN_CHECK_RULE}` : "report only evidence-backed work, flag the unverified explicitly, and report failing tests with their output."} Fix
|
|
110
|
+
Before reporting progress, audit each claim against a tool result from this session; ${context.surface !== "terminal" ? `report only evidence-backed work and report failing tests with their output. ${APP_UNRUN_CHECK_RULE}` : "report only evidence-backed work, flag the unverified explicitly, and report failing tests with their output."} Fix failures your change caused and any that sit in the code you changed; note other pre-existing failures separately.
|
|
111
111
|
|
|
112
112
|
${context.toolSection}
|
|
113
113
|
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
// retries after a busy tool from nearly all tries to four in ten. "Blind
|
|
17
17
|
// retries" becomes the exact action it meant, repeating a failure that has not
|
|
18
18
|
// been diagnosed, so it no longer collides with "wait and retry".
|
|
19
|
-
// - Scope, structure ("Say who owns the code"):
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
19
|
+
// - Scope, structure ("Say who owns the code"): the guide's ownership sentence
|
|
20
|
+
// opens the structure paragraph and replaces the smallest-change sentence
|
|
21
|
+
// (owner order). Without it, 5.2 acts as a guest: parallel code, duplicates
|
|
22
|
+
// left in place, and nearby bugs only mentioned. senpi#6358 carries the same
|
|
23
|
+
// sentence, with what the owner does, into every core.
|
|
23
24
|
// - Working the Task, delegation ("Set expectations for subagents"): the 5.1
|
|
24
25
|
// sentence was a standing invitation to delegate, which the guide says to
|
|
25
26
|
// review first. It now states the cost before the invitation, what a brief
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
// mannered-prose sentence. 5.2 reports run dense, so the rule asks for plain
|
|
30
31
|
// words, mostly short one-idea sentences, one word per thing, and no names
|
|
31
32
|
// made up during the work, for a reader switching in from something else.
|
|
32
|
-
// Deliberately absent:
|
|
33
|
+
// Deliberately absent: "answer directly" or think-less
|
|
33
34
|
// lines (the guide: no effect at medium effort and above, lower the effort
|
|
34
35
|
// instead); a token budget; the "treat that answer as done" lines; hard
|
|
35
36
|
// sentence-length limits; any instruction to write reasoning in the reply (a
|
|
@@ -67,7 +68,7 @@ ${buildSearchLine(context)}
|
|
|
67
68
|
Route by true intent, not surface form:
|
|
68
69
|
- Information asks (explain, look into, investigate): read the code and report; no edits.
|
|
69
70
|
- Judgment asks (what do you think, review) and open-ended changes (refactor, improve, clean up): assess and propose, then wait for confirmation.
|
|
70
|
-
- Change asks (implement, add, fix this error): build, or diagnose and fix
|
|
71
|
+
- Change asks (implement, add, fix this error): build, or diagnose and fix at the cause.
|
|
71
72
|
|
|
72
73
|
Derive intent from the latest user turn alone: a new direction drops the stale plan, and queued steering messages outrank earlier intent.
|
|
73
74
|
|
|
@@ -77,7 +78,7 @@ The request sets the scope, and the scope is the deliverable: deliver all of it
|
|
|
77
78
|
|
|
78
79
|
Errors, timeouts, locked files, empty results, and failing tools are ordinary obstacles, not blockers: diagnose them, then work through them with the access you have (wait and retry a call that is safe to repeat, fix the request, use another tool or source) without asking first. A file marked not to be touched, withheld access, or a safety guardrail is a deliberate blocker: leave it alone and look for another safe way to finish. If part of the task stays blocked, finish every other part and say exactly what you left out and why.
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
This codebase is yours: what it's for and how it's built are your call, and you'll keep building on it. Change it the way its owner would: build on the code that already does the job instead of adding a parallel copy, fit new behavior into the structure that is there, and leave no duplicate or dead code behind. A defect in the code you are changing is yours to fix now; one elsewhere goes in your summary as a follow-up. Add no helpers or abstractions for hypothetical needs and no defensive checks inside trusted code; validate only at system boundaries. Scratch checks verify and get discarded; commit tests only where the task asks for them or the repository already keeps tests for that kind of change, sized like the neighboring test files. Prefer a surgical edit over rewriting a file when the result would be identical.
|
|
81
82
|
|
|
82
83
|
## Working the Task
|
|
83
84
|
|
|
@@ -91,7 +92,7 @@ Scale the checks to the change, never the rigor: diagnostics on every changed fi
|
|
|
91
92
|
|
|
92
93
|
${buildTestDisciplineSection(context.sharedSections)}
|
|
93
94
|
|
|
94
|
-
Before reporting progress, audit each claim against a tool result from this session; ${context.surface !== "terminal" ? `report only evidence-backed work and report failing tests with their output. ${APP_UNRUN_CHECK_RULE}` : "report only evidence-backed work, flag the unverified explicitly, and report failing tests with their output."} Fix
|
|
95
|
+
Before reporting progress, audit each claim against a tool result from this session; ${context.surface !== "terminal" ? `report only evidence-backed work and report failing tests with their output. ${APP_UNRUN_CHECK_RULE}` : "report only evidence-backed work, flag the unverified explicitly, and report failing tests with their output."} Fix failures your change caused and any that sit in the code you changed; note other pre-existing failures separately.
|
|
95
96
|
|
|
96
97
|
${context.toolSection}
|
|
97
98
|
|