@code-yeongyu/senpi 2026.10.25-4 → 2026.10.26-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/dist/bundle/chunks/{anthropic-messages-WBASBTIW.js → anthropic-messages-ODSFJCXR.js} +3 -3
- package/dist/bundle/chunks/{args-B444OQBK.js → args-SWKJAYQD.js} +1 -1
- package/dist/bundle/chunks/azure-openai-responses-REGXS4XO.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +3 -3
- package/dist/bundle/chunks/chunk-2XF6W6F6.js +410 -0
- package/dist/bundle/chunks/{chunk-WDPQ6CZJ.js → chunk-4QVORFQ2.js} +1 -1
- package/dist/bundle/chunks/{chunk-6AOVUTUR.js → chunk-5W3DTM2J.js} +2 -2
- package/dist/bundle/chunks/{chunk-E6WYH3ZL.js → chunk-6GAMRAVM.js} +9 -7
- package/dist/bundle/chunks/{chunk-N7IN46Q5.js → chunk-7IZH2KQX.js} +9 -7
- package/dist/bundle/chunks/{chunk-7GZUFOM3.js → chunk-ARSENVDY.js} +1 -1
- package/dist/bundle/chunks/{chunk-STMXIBXF.js → chunk-CNDPCYNT.js} +1 -1
- package/dist/bundle/chunks/{chunk-XF3LJC3O.js → chunk-CYOHFREV.js} +1 -1
- package/dist/bundle/chunks/{chunk-E6D4RIT7.js → chunk-DNDZPKIQ.js} +2 -2
- package/dist/bundle/chunks/{chunk-E4LBKOZG.js → chunk-EQNUL2WB.js} +4 -4
- package/dist/bundle/chunks/{chunk-2RB5EL4X.js → chunk-ESNSRUW4.js} +1 -1
- package/dist/bundle/chunks/{chunk-4AOMNOGY.js → chunk-FQDARFHT.js} +1 -1
- package/dist/bundle/chunks/{chunk-BWA4FVFW.js → chunk-GPPOPK76.js} +3 -3
- package/dist/bundle/chunks/{chunk-SBGIXMX3.js → chunk-GWGNVYPB.js} +1 -1
- package/dist/bundle/chunks/{chunk-KKD4CYCI.js → chunk-H4WJ4LY3.js} +1 -1
- package/dist/bundle/chunks/{chunk-V2UYXTRR.js → chunk-HWTD4QF3.js} +1 -1
- package/dist/bundle/chunks/chunk-I2WFLKZ2.js +5 -0
- package/dist/bundle/chunks/{chunk-WD6WBYC5.js → chunk-IHKHCWWG.js} +1 -1
- package/dist/bundle/chunks/{chunk-T36LJ6MW.js → chunk-IY4G44MN.js} +1 -1
- package/dist/bundle/chunks/{chunk-BZ7726QO.js → chunk-J2YAYTB2.js} +1 -1
- package/dist/bundle/chunks/{chunk-UFJ2K7Q7.js → chunk-JH7MVRNJ.js} +1 -1
- package/dist/bundle/chunks/chunk-KBSFTXAT.js +2 -0
- package/dist/bundle/chunks/{chunk-OLCDIITS.js → chunk-KRO63T4Q.js} +1 -1
- package/dist/bundle/chunks/{chunk-TTU2RNVX.js → chunk-KTDIIF2F.js} +1 -1
- package/dist/bundle/chunks/{chunk-KNVNKDER.js → chunk-L2O6K3SJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-3BTN337S.js → chunk-NDNIMC5W.js} +1 -1
- package/dist/bundle/chunks/{chunk-ITGYN22E.js → chunk-ODRGPC5S.js} +1 -1
- package/dist/bundle/chunks/{chunk-4MOZXOJX.js → chunk-PDG4XWO2.js} +1 -1
- package/dist/bundle/chunks/{chunk-3QCYBOKN.js → chunk-PPECSSWX.js} +1 -1
- package/dist/bundle/chunks/{chunk-4P7WARQZ.js → chunk-PY4ZQHTN.js} +1 -1
- package/dist/bundle/chunks/{chunk-C2MQ4QXN.js → chunk-RPW3XQW3.js} +48 -42
- package/dist/bundle/chunks/{chunk-JMOUU75H.js → chunk-RQ5JTXUC.js} +1 -1
- package/dist/bundle/chunks/chunk-TQ67PB7U.js +13 -0
- package/dist/bundle/chunks/{chunk-5JJDR6XI.js → chunk-UJPG4ROT.js} +1 -1
- package/dist/bundle/chunks/{chunk-FHXR4ZAC.js → chunk-XM36IFP3.js} +1 -1
- package/dist/bundle/chunks/{chunk-PT6JJUN2.js → chunk-XMNPK472.js} +1 -1
- package/dist/bundle/chunks/{client-WTYEICLH.js → chunk-Y3TMJV3J.js} +3 -3
- package/dist/bundle/chunks/{chunk-GSSD3R5X.js → chunk-Z6BREXV3.js} +1 -1
- package/dist/bundle/chunks/{chunk-KXB5WIS5.js → chunk-Z6SAQ5O3.js} +1 -1
- package/dist/bundle/chunks/{chunk-LOIF6PHI.js → chunk-ZAUVY6ZC.js} +3 -3
- package/dist/bundle/chunks/{chunk-7CNOCJCN.js → chunk-ZP6JJDYV.js} +1 -1
- package/dist/bundle/chunks/cli-main-Q2GA53XM.js +3 -0
- package/dist/bundle/chunks/client-LOS6GSEW.js +2 -0
- package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-K5H6BJP3.js → cloudflare-workers-ai-system-one-WDHT7BBV.js} +1 -1
- package/dist/bundle/chunks/cursor-agent.js +3 -3
- package/dist/bundle/chunks/devin-agent.js +3 -3
- package/dist/bundle/chunks/dispatch-5R3TYAYB.js +9 -0
- package/dist/bundle/chunks/google-generative-ai-FHYFXKLA.js +2 -0
- package/dist/bundle/chunks/google-vertex-5DWE5U5K.js +2 -0
- package/dist/bundle/chunks/{help-fast-path-3Z26DQSV.js → help-fast-path-FETLIF3W.js} +1 -1
- package/dist/bundle/chunks/host-command-XEB7SHT3.js +2 -0
- package/dist/bundle/chunks/{host-lifecycle-3VHHIGDL.js → host-lifecycle-HX3EGRRW.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +2 -2
- package/dist/bundle/chunks/{host-session-command-56XF6ACS.js → host-session-command-FFN52LXJ.js} +1 -1
- package/dist/bundle/chunks/{host-socket-request-QL2WEZTN.js → host-socket-request-K4LCUOUD.js} +1 -1
- package/dist/bundle/chunks/interactive-mode-5DEKY3YW.js +2 -0
- package/dist/bundle/chunks/{mcp-presenter-bootstrap-XUSCQVRC.js → mcp-presenter-bootstrap-FOMKF4TU.js} +1 -1
- package/dist/bundle/chunks/{mistral-conversations-DH44NOYV.js → mistral-conversations-I2PI7DDF.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-Z2CW4SDM.js → multi-session-host-OPR5VBFO.js} +6 -7
- package/dist/bundle/chunks/{openai-codex-responses-SERAFKAS.js → openai-codex-responses-WSEX6SH7.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-BZB5O45A.js → openai-completions-YCBHTBSS.js} +1 -1
- package/dist/bundle/chunks/openai-responses-GXNSQF4A.js +3 -0
- package/dist/bundle/chunks/{package-manager-cli-N55U6PEO.js → package-manager-cli-W55E3LHP.js} +1 -1
- package/dist/bundle/chunks/{pi-messages-VXATX45B.js → pi-messages-O6GISIRW.js} +2 -2
- package/dist/bundle/chunks/{role-JVSBNKGE.js → role-SHIZQKK4.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-CBGCBYHV.js → rotation-stream-2PCZWNKF.js} +1 -1
- package/dist/bundle/chunks/rpc-mode-IDUJTSYS.js +2 -0
- package/dist/bundle/chunks/{rpc-monitor-source-AE3C6EQV.js → rpc-monitor-source-DRZXJHTL.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-VS7CZC2J.js → schedule-command-B7RRSANU.js} +1 -1
- package/dist/bundle/chunks/session-control-endpoint-LMZH5JLH.js +3 -0
- package/dist/bundle/chunks/{session-picker-E4KUJDLO.js → session-picker-DTGBOXDR.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +214 -205
- package/dist/bundle/chunks/{typesafe-system-one-P75WJ767.js → typesafe-system-one-XGYJJVSE.js} +1 -1
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +2 -2
- 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/host-command-args.d.ts +1 -0
- package/dist/cli/host-command-args.js +5 -0
- package/dist/cli/host-command.js +7 -1
- package/dist/cli.js +21 -3
- package/dist/core/agent-session.d.ts +11 -1
- package/dist/core/agent-session.js +19 -2
- package/dist/core/extensions/builtin/compaction/index.js +7 -1
- package/dist/core/extensions/builtin/herdr/index.js +9 -5
- package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.d.ts +6 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +25 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +3 -3
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +50 -19
- package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +4 -3
- package/dist/core/extensions/builtin/mcp/auth/oauth.js +19 -12
- package/dist/core/extensions/builtin/mcp/catalog-cache.d.ts +2 -1
- package/dist/core/extensions/builtin/mcp/catalog-cache.js +19 -6
- package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -2
- package/dist/core/extensions/builtin/mcp/catalog.js +20 -3
- package/dist/core/extensions/builtin/mcp/expose/call.d.ts +2 -1
- package/dist/core/extensions/builtin/mcp/expose/call.js +2 -1
- package/dist/core/extensions/builtin/mcp/expose/register.js +1 -1
- package/dist/core/extensions/builtin/mcp/expose/schema-compat.d.ts +2 -2
- package/dist/core/extensions/builtin/mcp/expose/schema-compat.js +11 -1
- package/dist/core/extensions/builtin/mcp/invocation.js +16 -2
- package/dist/core/extensions/builtin/mcp/modern/dispatch.js +1 -0
- package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +4 -1
- package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +33 -35
- package/dist/core/extensions/builtin/mcp/modern/request.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/modern/request.js +4 -1
- package/dist/core/extensions/builtin/mcp/modern/sdk-bridge.js +18 -3
- package/dist/core/extensions/builtin/mcp/modern/stdio-probe.js +23 -0
- package/dist/core/extensions/builtin/mcp/modern/subscriptions.d.ts +14 -0
- package/dist/core/extensions/builtin/mcp/modern/subscriptions.js +226 -0
- package/dist/core/extensions/builtin/mcp/modern/tool-schema.d.ts +13 -0
- package/dist/core/extensions/builtin/mcp/modern/tool-schema.js +75 -0
- package/dist/core/extensions/builtin/mcp/modern/tools.d.ts +107 -0
- package/dist/core/extensions/builtin/mcp/modern/tools.js +53 -0
- package/dist/core/extensions/builtin/mcp/modern/transport.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/modern/transport.js +4 -2
- package/dist/core/extensions/builtin/mcp/resources.js +4 -0
- package/dist/core/extensions/builtin/mcp/tool-client.d.ts +13 -0
- package/dist/core/extensions/builtin/mcp/tool-client.js +17 -0
- package/dist/core/extensions/builtin/mcp/tool-contracts.d.ts +19 -0
- package/dist/core/extensions/builtin/mcp/tool-contracts.js +2 -0
- package/dist/core/extensions/builtin/mcp/wrap.js +2 -2
- package/dist/core/extensions/builtin/permission-system/bash-command-segments.d.ts +37 -0
- package/dist/core/extensions/builtin/permission-system/bash-command-segments.js +899 -0
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts +2 -0
- package/dist/core/extensions/builtin/permission-system/parsers.js +44 -10
- package/dist/core/extensions/builtin/terminal/extension-state.js +2 -1
- package/dist/core/extensions/builtin/vertex-model-garden/auth.d.ts +9 -0
- package/dist/core/extensions/builtin/vertex-model-garden/auth.js +17 -1
- package/dist/core/extensions/builtin/websearch/websearch/providers/google.d.ts +6 -0
- package/dist/core/extensions/builtin/websearch/websearch/providers/google.js +6 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/lifecycle-children.d.ts +3 -1
- package/dist/core/lifecycle-children.js +79 -15
- package/dist/core/lifecycle-record.js +1 -24
- package/dist/core/lifecycle-session-binding.js +2 -1
- package/dist/core/lifecycle-wait-items.d.ts +17 -0
- package/dist/core/lifecycle-wait-items.js +72 -0
- package/dist/core/model-runtime.js +6 -1
- package/dist/core/provider-api-key-auth.d.ts +6 -1
- package/dist/core/provider-api-key-auth.js +11 -4
- package/dist/core/provider-composer.d.ts +8 -1
- package/dist/core/provider-composer.js +7 -3
- package/dist/core/sdk.js +4 -1
- package/dist/core/session-activity.js +2 -0
- package/dist/core/session-lifecycle-types.d.ts +20 -4
- package/dist/core/session-lifecycle.js +35 -4
- package/dist/core/structured-session-lane.d.ts +33 -0
- package/dist/core/structured-session-lane.js +62 -0
- package/dist/core/work-custody-record.d.ts +2 -1
- package/dist/core/work-custody-record.js +2 -0
- package/dist/core/work-obligations.d.ts +1 -1
- package/dist/core/work-obligations.js +8 -2
- package/dist/modes/interactive/session-control-commands.js +21 -4
- package/dist/modes/interactive/session-control-rpc-commands.d.ts +1 -1
- package/dist/modes/interactive/session-control-rpc-commands.js +19 -0
- package/dist/modes/json-event.d.ts +79 -1
- package/dist/modes/json-event.js +79 -1
- package/dist/modes/rpc/compact-content-index.d.ts +85 -0
- package/dist/modes/rpc/compact-content-index.js +254 -0
- package/dist/modes/rpc/compact-content-store.d.ts +59 -0
- package/dist/modes/rpc/compact-content-store.js +134 -0
- package/dist/modes/rpc/compact-index-pages.d.ts +12 -0
- package/dist/modes/rpc/compact-index-pages.js +31 -0
- package/dist/modes/rpc/compact-session-state.d.ts +83 -0
- package/dist/modes/rpc/compact-session-state.js +197 -0
- package/dist/modes/rpc/compact-spool-dir.d.ts +8 -0
- package/dist/modes/rpc/compact-spool-dir.js +94 -0
- package/dist/modes/rpc/compact-spool-storage.d.ts +53 -0
- package/dist/modes/rpc/compact-spool-storage.js +162 -0
- package/dist/modes/rpc/compact-storage-io.d.ts +28 -0
- package/dist/modes/rpc/compact-storage-io.js +25 -0
- package/dist/modes/rpc/compact-transition.d.ts +41 -0
- package/dist/modes/rpc/compact-transition.js +12 -0
- package/dist/modes/rpc/connection-handler.d.ts +4 -0
- package/dist/modes/rpc/connection-handler.js +51 -13
- package/dist/modes/rpc/connection-question-bridge.d.ts +2 -1
- package/dist/modes/rpc/connection-question-bridge.js +6 -2
- package/dist/modes/rpc/extension-ui-response.d.ts +7 -2
- package/dist/modes/rpc/extension-ui-response.js +27 -7
- package/dist/modes/rpc/get-snapshot.d.ts +18 -0
- package/dist/modes/rpc/get-snapshot.js +43 -0
- package/dist/modes/rpc/host-capabilities.js +3 -0
- package/dist/modes/rpc/host-ensure-start.js +6 -3
- package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
- package/dist/modes/rpc/host-ensure.js +1 -0
- package/dist/modes/rpc/host-handoff.d.ts +2 -0
- package/dist/modes/rpc/host-handover-request.d.ts +1 -1
- package/dist/modes/rpc/host-handover-request.js +4 -3
- package/dist/modes/rpc/host-handover-wire.js +6 -0
- package/dist/modes/rpc/host-idle-handover.d.ts +2 -0
- package/dist/modes/rpc/host-idle-handover.js +3 -1
- package/dist/modes/rpc/host-observe-request.js +1 -0
- package/dist/modes/rpc/host-runner.d.ts +1 -0
- package/dist/modes/rpc/host-runner.js +9 -7
- package/dist/modes/rpc/host-successor.js +4 -0
- package/dist/modes/rpc/json-normal-value.d.ts +6 -0
- package/dist/modes/rpc/json-normal-value.js +100 -0
- package/dist/modes/rpc/mcp-presenter-bootstrap.js +5 -0
- package/dist/modes/rpc/media-placeholders.d.ts +9 -3
- package/dist/modes/rpc/media-placeholders.js +12 -4
- package/dist/modes/rpc/rpc-host-command-types.d.ts +2 -0
- package/dist/modes/rpc/rpc-question-types.d.ts +1 -0
- package/dist/modes/rpc/rpc-read-response-types.d.ts +7 -0
- package/dist/modes/rpc/rpc-session-command-types.d.ts +6 -0
- package/dist/modes/rpc/rpc-session-state-types.d.ts +3 -0
- package/dist/modes/rpc/rpc-session-state.js +2 -0
- package/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/modes/rpc/security-presentations.js +2 -1
- package/dist/modes/rpc/session-attachment-gate.js +1 -0
- package/dist/modes/rpc/session-binding.js +30 -17
- package/dist/modes/rpc/session-command-activity.js +1 -0
- package/dist/modes/rpc/session-event-fanout.d.ts +1 -1
- package/dist/modes/rpc/session-event-fanout.js +17 -9
- package/dist/modes/rpc/session-event-writer.d.ts +2 -0
- package/dist/modes/rpc/session-event-writer.js +24 -5
- package/dist/modes/rpc/session-extension-ui-requests.d.ts +2 -1
- package/dist/modes/rpc/session-extension-ui-requests.js +15 -10
- package/dist/modes/rpc/session-held.js +1 -0
- package/dist/modes/rpc/session-record-stream.d.ts +3 -3
- package/dist/modes/rpc/session-record-stream.js +3 -3
- package/dist/modes/rpc/session-status-stream.d.ts +2 -1
- package/dist/modes/rpc/session-status-stream.js +8 -2
- package/dist/modes/rpc/session-status-subscriptions.d.ts +2 -1
- package/dist/modes/rpc/session-status-subscriptions.js +4 -0
- package/dist/modes/rpc/session-structured-lanes.d.ts +12 -0
- package/dist/modes/rpc/session-structured-lanes.js +24 -0
- package/dist/modes/rpc/session-worker-client.js +6 -5
- package/dist/modes/rpc/session-worker.js +5 -2
- package/dist/modes/rpc/snapshot-types.d.ts +29 -0
- package/dist/modes/rpc/snapshot-types.js +2 -0
- package/dist/modes/rpc/structured-session-records.d.ts +12 -0
- package/dist/modes/rpc/structured-session-records.js +88 -0
- package/dist/modes/rpc/ui-answer-ledger.d.ts +13 -0
- package/dist/modes/rpc/ui-answer-ledger.js +34 -0
- package/dist/senpi +21 -3
- package/docs/extensions.md +7 -1
- package/docs/mcp.md +20 -0
- package/docs/rpc.md +61 -2
- package/package.json +7 -6
- package/dist/bundle/chunks/azure-openai-responses-7I45DI72.js +0 -2
- package/dist/bundle/chunks/chunk-DMI55M2N.js +0 -2
- package/dist/bundle/chunks/chunk-DODH3N6U.js +0 -410
- package/dist/bundle/chunks/chunk-J37GRED2.js +0 -5
- package/dist/bundle/chunks/chunk-TBU6VYHN.js +0 -13
- package/dist/bundle/chunks/cli-main-YYH3IBC6.js +0 -3
- package/dist/bundle/chunks/dispatch-MXG5YWLV.js +0 -9
- package/dist/bundle/chunks/google-generative-ai-UM5Y7T6B.js +0 -2
- package/dist/bundle/chunks/google-vertex-OKU2VQHL.js +0 -2
- package/dist/bundle/chunks/host-command-TDLLHXVI.js +0 -2
- package/dist/bundle/chunks/interactive-mode-OEPULLZK.js +0 -2
- package/dist/bundle/chunks/openai-responses-TNHQ6SJS.js +0 -3
- package/dist/bundle/chunks/rpc-mode-ETXQWEB2.js +0 -2
- package/dist/bundle/chunks/session-control-endpoint-4LEIDIVI.js +0 -3
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Modes use this class and add their own I/O layer on top.
|
|
14
14
|
*/
|
|
15
|
-
import type { Agent, AgentEvent, AgentMessage, AgentState, AgentTool, AgentToolCall, AgentToolResult, ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
15
|
+
import type { Agent, AgentEvent, AgentMessage, AgentState, AgentTool, AgentToolCall, AgentToolResult, InternalAgentChannel, ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
16
16
|
import { type ThinkingSelection } from "@earendil-works/pi-ai";
|
|
17
17
|
import type { Api, ImageContent, Model, TextContent } from "@earendil-works/pi-ai/compat";
|
|
18
18
|
import { type ToolRuntimeKind } from "../cli/tool-runtime.ts";
|
|
@@ -60,6 +60,7 @@ import type { SessionRelocationHost } from "./session-relocation.ts";
|
|
|
60
60
|
import { type SettingsManager, type SettingsSourceSelection } from "./settings-manager.ts";
|
|
61
61
|
import { type SkillInvocationSyntax } from "./skill-invocation.ts";
|
|
62
62
|
import { type SourceInfo } from "./source-info.ts";
|
|
63
|
+
import { type AgentSessionStructuredRecord } from "./structured-session-lane.ts";
|
|
63
64
|
import { type ThinkingClampNotice } from "./thinking-levels.ts";
|
|
64
65
|
import { type ToolBarrierSpec } from "./tool-barrier.ts";
|
|
65
66
|
import { type SessionMessageUpdateEvent } from "./tool-call-display-name.ts";
|
|
@@ -372,6 +373,8 @@ export type AgentSessionEvent = WithParentToolCallId<Exclude<AgentEvent, {
|
|
|
372
373
|
export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
|
|
373
374
|
export interface AgentSessionConfig {
|
|
374
375
|
agent: Agent;
|
|
376
|
+
/** @internal Construction channel for senpi's session layer only; absent for externally supplied Agents. */
|
|
377
|
+
internalChannel?: InternalAgentChannel;
|
|
375
378
|
/** Explicit service tier for the initial model. */
|
|
376
379
|
serviceTier?: ServiceTier;
|
|
377
380
|
sessionManager: SessionManager;
|
|
@@ -650,6 +653,7 @@ export declare class AgentSession {
|
|
|
650
653
|
private _unsubscribeSettingsSource?;
|
|
651
654
|
private _unregisterMemoryReport;
|
|
652
655
|
private _eventListeners;
|
|
656
|
+
private readonly _structuredLane;
|
|
653
657
|
private _agentEventQueue;
|
|
654
658
|
/**
|
|
655
659
|
* Exact message objects whose message_end persistence is still queued.
|
|
@@ -1211,6 +1215,12 @@ export declare class AgentSession {
|
|
|
1211
1215
|
* Multiple listeners can be added. Returns unsubscribe function for this listener.
|
|
1212
1216
|
*/
|
|
1213
1217
|
subscribe(listener: AgentSessionEventListener): () => void;
|
|
1218
|
+
/**
|
|
1219
|
+
* The internal structured lane (senpi#4272): every session event in `_emit` order with its provider capture, plus
|
|
1220
|
+
* block controls at their position. Attach before a turn's first provider push; a stream already pushing keeps
|
|
1221
|
+
* its decision and yields no capture.
|
|
1222
|
+
*/
|
|
1223
|
+
subscribeStructured(listener: (record: AgentSessionStructuredRecord) => void): () => void;
|
|
1214
1224
|
/**
|
|
1215
1225
|
* Temporarily disconnect from agent events.
|
|
1216
1226
|
* User listeners are preserved and will receive events again after resubscribe().
|
|
@@ -139,6 +139,7 @@ import { formatSkillInvocationPrompt, parseSkillInvocationTokens, removeSkillInv
|
|
|
139
139
|
import { BUILTIN_SLASH_COMMANDS } from "./slash-commands.js";
|
|
140
140
|
import { BUILTIN_PATH_PREFIX, createSyntheticSourceInfo } from "./source-info.js";
|
|
141
141
|
import { stopResumeDirective } from "./stop-resume-directive.js";
|
|
142
|
+
import { StructuredSessionLane } from "./structured-session-lane.js";
|
|
142
143
|
import { clampThinkingSelection, getSupportedThinkingLevels, getThinkingClampNotice, supportsMax, supportsXhigh, } from "./thinking-levels.js";
|
|
143
144
|
import { resetTimings, time } from "./timings.js";
|
|
144
145
|
import { ToolBarrier, toolBarrierSpecError } from "./tool-barrier.js";
|
|
@@ -701,6 +702,11 @@ export class AgentSession {
|
|
|
701
702
|
this._promptSurface = config.promptSurface;
|
|
702
703
|
this._browserEngine = config.browserEngine;
|
|
703
704
|
this.agent = config.agent;
|
|
705
|
+
this._structuredLane = new StructuredSessionLane(config.internalChannel, (deliver) => {
|
|
706
|
+
if (!this._unsubscribeAgent)
|
|
707
|
+
return;
|
|
708
|
+
this._agentEventQueue = this._agentEventQueue.then(deliver, deliver);
|
|
709
|
+
});
|
|
704
710
|
this.sessionManager = config.sessionManager;
|
|
705
711
|
this._sessionThinkingMemory = new SessionThinkingMemory(config.sessionManager);
|
|
706
712
|
this._thinkingLevelSource = config.initialThinkingLevelSource ?? "default";
|
|
@@ -1701,7 +1707,7 @@ export class AgentSession {
|
|
|
1701
1707
|
this.agent.state.messages = this.sessionManager.buildSessionContext().messages;
|
|
1702
1708
|
this._emitEntryAppended(entry.id);
|
|
1703
1709
|
}
|
|
1704
|
-
_emit(event) {
|
|
1710
|
+
_emit(event, captureSource = event) {
|
|
1705
1711
|
if (event.type === "session_paused" ||
|
|
1706
1712
|
event.type === "session_resumed" ||
|
|
1707
1713
|
event.type === "compaction_start" ||
|
|
@@ -1744,6 +1750,8 @@ export class AgentSession {
|
|
|
1744
1750
|
for (const l of [...this._eventListeners]) {
|
|
1745
1751
|
l(event);
|
|
1746
1752
|
}
|
|
1753
|
+
if (this._structuredLane.active)
|
|
1754
|
+
this._structuredLane.emitEvent(event, captureSource);
|
|
1747
1755
|
}
|
|
1748
1756
|
async _emitSessionCompactFailed(event) {
|
|
1749
1757
|
if (this._extensionRunner.hasHandlers("session_compact_failed")) {
|
|
@@ -2567,7 +2575,7 @@ export class AgentSession {
|
|
|
2567
2575
|
: event.type === "message_update"
|
|
2568
2576
|
? withResolvedToolName(event, (name) => this.resolveToolCallName(name))
|
|
2569
2577
|
: event;
|
|
2570
|
-
this._emit(sessionEvent);
|
|
2578
|
+
this._emit(sessionEvent, event);
|
|
2571
2579
|
if (event.type === "agent_end") {
|
|
2572
2580
|
if (this._abortProvenance.takeLateUserJoin())
|
|
2573
2581
|
await this._emitSessionAbort();
|
|
@@ -3234,6 +3242,14 @@ export class AgentSession {
|
|
|
3234
3242
|
}
|
|
3235
3243
|
};
|
|
3236
3244
|
}
|
|
3245
|
+
/**
|
|
3246
|
+
* The internal structured lane (senpi#4272): every session event in `_emit` order with its provider capture, plus
|
|
3247
|
+
* block controls at their position. Attach before a turn's first provider push; a stream already pushing keeps
|
|
3248
|
+
* its decision and yields no capture.
|
|
3249
|
+
*/
|
|
3250
|
+
subscribeStructured(listener) {
|
|
3251
|
+
return this._structuredLane.subscribe(listener);
|
|
3252
|
+
}
|
|
3237
3253
|
/**
|
|
3238
3254
|
* Temporarily disconnect from agent events.
|
|
3239
3255
|
* User listeners are preserved and will receive events again after resubscribe().
|
|
@@ -3302,6 +3318,7 @@ export class AgentSession {
|
|
|
3302
3318
|
this._unsubscribeWakeSources = undefined;
|
|
3303
3319
|
this._lifecycleBinding.dispose();
|
|
3304
3320
|
this._eventListeners = [];
|
|
3321
|
+
this._structuredLane.dispose();
|
|
3305
3322
|
cleanupSessionResources(this.sessionId);
|
|
3306
3323
|
// Nothing reads or writes this manager once its session is gone: it releases
|
|
3307
3324
|
// its writer grant and its disposable blob directory here.
|
|
@@ -648,8 +648,14 @@ export default function compactionExtension(pi, remoteCompactionDependencies = {
|
|
|
648
648
|
catch (error) {
|
|
649
649
|
const message = error instanceof Error ? error.message : String(error);
|
|
650
650
|
const failureKind = classifyRequiredCompactionFallbackFailure(error);
|
|
651
|
+
// A terminal provider failure (dead model, dead credential) has no cheaper
|
|
652
|
+
// recovery on any compaction kind: without this, a soft-threshold pre_prompt
|
|
653
|
+
// compaction cancelled instead, the session rejected it, and the pending
|
|
654
|
+
// prompt failed with RequiredCompactionError while shouldCompact still said
|
|
655
|
+
// compact (the "Compaction rejected: compaction generator failed" wedge).
|
|
651
656
|
if ((requiresDeterministicCompactionFallback(event, ctx.getContextUsage()) ||
|
|
652
|
-
failureKind === "unsafe-retained-content"
|
|
657
|
+
failureKind === "unsafe-retained-content" ||
|
|
658
|
+
failureKind === "summarization-provider-failure") &&
|
|
653
659
|
failureKind !== undefined &&
|
|
654
660
|
!event.signal.aborted) {
|
|
655
661
|
const recovery = recoverRequiredCompaction(ctx, snapshot, failureKind, error);
|
|
@@ -2,6 +2,7 @@ import { closeSync, openSync, readdirSync, readFileSync, readSync } from "node:f
|
|
|
2
2
|
import { createConnection } from "node:net";
|
|
3
3
|
import { join, win32 } from "node:path";
|
|
4
4
|
import { debuglog } from "node:util";
|
|
5
|
+
import { APP_COMMAND } from "../../../../config.js";
|
|
5
6
|
import { isTerminalMonitorStateEvent, isWakeSourceStateEvent, TERMINAL_MONITOR_STATE_EVENT, WAKE_SOURCE_STATE_EVENT, } from "../monitor-state-event.js";
|
|
6
7
|
import { HerdrClient } from "./herdr-client.js";
|
|
7
8
|
import { initialHerdrState, isHerdrBlockedEvent, reduceHerdrState, selectHerdrReport } from "./herdr-state.js";
|
|
@@ -115,11 +116,14 @@ export function createHerdrExtension(deps) {
|
|
|
115
116
|
void publish();
|
|
116
117
|
}, 4000);
|
|
117
118
|
poll.unref();
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
// Herdr accepts a resume command only after this reporter holds the pane.
|
|
120
|
+
await Promise.all([reportTitle(ctx), publish()]);
|
|
121
|
+
await send("pane.report_agent_session", {
|
|
122
|
+
agent: "pi",
|
|
123
|
+
...sessionRef(),
|
|
124
|
+
resume_argv: [APP_COMMAND, "--session", bound.getSessionFile() ?? bound.getSessionId()],
|
|
125
|
+
session_start_source: _event.reason,
|
|
126
|
+
});
|
|
123
127
|
});
|
|
124
128
|
pi.on("session_info_changed", (_event, ctx) => {
|
|
125
129
|
if (owns(ctx))
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { OAuthServerInfo } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
2
|
+
import type { McpStoredAuth } from "./token-store.ts";
|
|
3
|
+
export declare function canonicalAuthorizationServerIssuer(issuer: string): string;
|
|
4
|
+
export declare function validatedAuthorizationServerIssuer(info: OAuthServerInfo, serverName: string): string;
|
|
5
|
+
export declare function credentialsMatchIssuer(record: McpStoredAuth | undefined, issuer: string): boolean;
|
|
6
|
+
//# sourceMappingURL=oauth-issuer.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OAuthFlowError } from "./oauth-errors.js";
|
|
2
|
+
// Credential stamps tolerate only the slash spelling of an origin-only URL, not URL normalization.
|
|
3
|
+
export function canonicalAuthorizationServerIssuer(issuer) {
|
|
4
|
+
return issuer.replace(/^(https?:\/\/[^/?#]+)\/$/, "$1");
|
|
5
|
+
}
|
|
6
|
+
// RFC 8414 section 3.3: identifiers from metadata are compared verbatim, not as normalized URLs.
|
|
7
|
+
export function validatedAuthorizationServerIssuer(info, serverName) {
|
|
8
|
+
// The SDK's legacy fallback discovers root metadata and spells the origin with a trailing slash.
|
|
9
|
+
// Only that configured-resource fallback is normalized; PRM-provided identifiers remain exact.
|
|
10
|
+
const origin = new URL(info.authorizationServerUrl).origin;
|
|
11
|
+
const issuer = !info.resourceMetadata?.authorization_servers?.length && info.authorizationServerUrl === `${origin}/`
|
|
12
|
+
? canonicalAuthorizationServerIssuer(info.authorizationServerUrl)
|
|
13
|
+
: info.authorizationServerUrl;
|
|
14
|
+
if (info.authorizationServerMetadata !== undefined && info.authorizationServerMetadata.issuer !== issuer) {
|
|
15
|
+
throw new OAuthFlowError("needs_auth", `MCP server ${serverName} authorization server metadata issuer mismatch; restart authorization after correcting the server metadata.`, { serverName });
|
|
16
|
+
}
|
|
17
|
+
return issuer;
|
|
18
|
+
}
|
|
19
|
+
export function credentialsMatchIssuer(record, issuer) {
|
|
20
|
+
const grantIssuer = record?.issuer ?? record?.discoveryState?.authorizationServerUrl;
|
|
21
|
+
const clientIssuer = record?.clientInfo?.issuer ?? grantIssuer;
|
|
22
|
+
return [grantIssuer, clientIssuer].every((stamp) => stamp === undefined ||
|
|
23
|
+
canonicalAuthorizationServerIssuer(stamp) === canonicalAuthorizationServerIssuer(issuer));
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=oauth-issuer.js.map
|
|
@@ -15,8 +15,6 @@ export interface McpOAuthProviderOptions {
|
|
|
15
15
|
onRedirect?: (url: URL) => void | Promise<void>;
|
|
16
16
|
}
|
|
17
17
|
export declare function tokenExpiresAt(tokens: OAuthTokens, now?: number): number | undefined;
|
|
18
|
-
/** Compares authorization server identifiers the way the SDK does: parsed, tolerating one trailing `/`. */
|
|
19
|
-
export declare function sameAuthorizationServer(a: string, b: string): boolean;
|
|
20
18
|
/**
|
|
21
19
|
* The authorization server a stored grant belongs to (senpi#2940, GHSA-6qxp-vccf-f47h): its `issuer` stamp, or
|
|
22
20
|
* for a record saved before the stamp existed, the authorization server its sign-in discovered. A record with
|
|
@@ -37,7 +35,9 @@ export declare class McpOAuthProvider implements OAuthClientProvider {
|
|
|
37
35
|
get lastAuthorizationUrl(): URL | undefined;
|
|
38
36
|
get redirectUrl(): string | URL | undefined;
|
|
39
37
|
get clientMetadataUrl(): string | undefined;
|
|
40
|
-
get clientMetadata(): OAuthClientMetadata
|
|
38
|
+
get clientMetadata(): OAuthClientMetadata & {
|
|
39
|
+
application_type?: "native" | "web";
|
|
40
|
+
};
|
|
41
41
|
state(): string;
|
|
42
42
|
consumeState(candidate: string | undefined): boolean;
|
|
43
43
|
clientInformation(): OAuthClientInformationMixed | undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { fingerprintSecret } from "../log.js";
|
|
3
3
|
import { OAuthFlowError } from "./oauth-errors.js";
|
|
4
|
+
import { canonicalAuthorizationServerIssuer, credentialsMatchIssuer, validatedAuthorizationServerIssuer, } from "./oauth-issuer.js";
|
|
4
5
|
// Buffer before absolute token expiry at which a refresh is considered due.
|
|
5
6
|
export const REFRESH_LEEWAY_MS = 5 * 60 * 1000;
|
|
6
7
|
export function tokenExpiresAt(tokens, now = Date.now()) {
|
|
@@ -8,20 +9,6 @@ export function tokenExpiresAt(tokens, now = Date.now()) {
|
|
|
8
9
|
return undefined;
|
|
9
10
|
return now + tokens.expires_in * 1000;
|
|
10
11
|
}
|
|
11
|
-
/** Compares authorization server identifiers the way the SDK does: parsed, tolerating one trailing `/`. */
|
|
12
|
-
export function sameAuthorizationServer(a, b) {
|
|
13
|
-
const normalize = (value) => {
|
|
14
|
-
try {
|
|
15
|
-
return new URL(value).href.replace(/\/$/, "");
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
if (error instanceof TypeError)
|
|
19
|
-
return value.replace(/\/$/, "");
|
|
20
|
-
throw error;
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
return normalize(a) === normalize(b);
|
|
24
|
-
}
|
|
25
12
|
/**
|
|
26
13
|
* The authorization server a stored grant belongs to (senpi#2940, GHSA-6qxp-vccf-f47h): its `issuer` stamp, or
|
|
27
14
|
* for a record saved before the stamp existed, the authorization server its sign-in discovered. A record with
|
|
@@ -101,6 +88,14 @@ export class McpOAuthProvider {
|
|
|
101
88
|
get clientMetadata() {
|
|
102
89
|
const redirect = this.#options.redirectUrl;
|
|
103
90
|
return {
|
|
91
|
+
...(redirect === undefined
|
|
92
|
+
? {}
|
|
93
|
+
: {
|
|
94
|
+
application_type: !["http:", "https:"].includes(new URL(redirect).protocol) ||
|
|
95
|
+
["127.0.0.1", "localhost", "[::1]"].includes(new URL(redirect).hostname)
|
|
96
|
+
? "native"
|
|
97
|
+
: "web",
|
|
98
|
+
}),
|
|
104
99
|
redirect_uris: redirect === undefined ? [] : [String(redirect)],
|
|
105
100
|
grant_types: ["authorization_code", "refresh_token"],
|
|
106
101
|
response_types: ["code"],
|
|
@@ -127,20 +122,56 @@ export class McpOAuthProvider {
|
|
|
127
122
|
return this.#store.read()?.clientInfo;
|
|
128
123
|
}
|
|
129
124
|
async saveClientInformation(info) {
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
const discovery = this.discoveryState();
|
|
126
|
+
const issuer = discovery === undefined
|
|
127
|
+
? info.issuer && canonicalAuthorizationServerIssuer(info.issuer)
|
|
128
|
+
: validatedAuthorizationServerIssuer(discovery, this.serverName);
|
|
129
|
+
await this.#store.update((current) => ({ ...(current ?? {}), clientInfo: { ...info, issuer } }));
|
|
132
130
|
}
|
|
133
131
|
async saveDiscoveryState(state) {
|
|
134
|
-
|
|
132
|
+
const issuer = validatedAuthorizationServerIssuer(state, this.serverName);
|
|
133
|
+
const discoveryState = { ...state, authorizationServerUrl: issuer };
|
|
134
|
+
await this.#store.update((current) => {
|
|
135
|
+
const attributed = freezeGrantAttribution(current);
|
|
136
|
+
if (!credentialsMatchIssuer(attributed, issuer))
|
|
137
|
+
return { discoveryState };
|
|
138
|
+
const clientIssuer = attributed.clientInfo?.issuer ?? storedGrantIssuer(attributed);
|
|
139
|
+
return {
|
|
140
|
+
...attributed,
|
|
141
|
+
// Unattributed legacy registrations cannot be sent to the server discovered now.
|
|
142
|
+
clientInfo: clientIssuer === undefined || attributed.clientInfo === undefined
|
|
143
|
+
? undefined
|
|
144
|
+
: { ...attributed.clientInfo, issuer },
|
|
145
|
+
discoveryState,
|
|
146
|
+
};
|
|
147
|
+
});
|
|
135
148
|
}
|
|
136
149
|
discoveryState() {
|
|
137
|
-
|
|
150
|
+
const current = this.#store.read();
|
|
151
|
+
const state = current?.discoveryState;
|
|
152
|
+
if (state !== undefined) {
|
|
153
|
+
try {
|
|
154
|
+
const issuer = validatedAuthorizationServerIssuer(state, this.serverName);
|
|
155
|
+
if (!credentialsMatchIssuer(current, issuer))
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (error instanceof OAuthFlowError)
|
|
160
|
+
return undefined;
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return state;
|
|
138
165
|
}
|
|
139
166
|
tokens() {
|
|
140
167
|
return storedAuthToTokens(this.#store.read());
|
|
141
168
|
}
|
|
142
169
|
async saveTokens(tokens) {
|
|
143
|
-
|
|
170
|
+
const discovery = this.discoveryState();
|
|
171
|
+
const issuer = discovery === undefined
|
|
172
|
+
? tokens.issuer && canonicalAuthorizationServerIssuer(tokens.issuer)
|
|
173
|
+
: validatedAuthorizationServerIssuer(discovery, this.serverName);
|
|
174
|
+
await this.#store.update((current) => mergeTokensIntoStoredAuth(current, { ...tokens, issuer }, this.serverUrl));
|
|
144
175
|
this.#logFingerprint("saveTokens", tokens.access_token);
|
|
145
176
|
}
|
|
146
177
|
async saveCodeVerifier(codeVerifier) {
|
|
@@ -2,7 +2,8 @@ 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 {
|
|
5
|
+
import { credentialsMatchIssuer, validatedAuthorizationServerIssuer } from "./oauth-issuer.js";
|
|
6
|
+
import { mergeTokensIntoStoredAuth, REFRESH_LEEWAY_MS, storedAuthToTokens, storedGrantIssuer, } from "./oauth-provider.js";
|
|
6
7
|
export function isTokenStale(record, now) {
|
|
7
8
|
const token = record?.accessToken;
|
|
8
9
|
if (token === undefined || token.length === 0)
|
|
@@ -67,8 +68,8 @@ export class McpRefreshManager {
|
|
|
67
68
|
// elsewhere, or one that cannot be attributed at all, is a continuity break: drop it and require a fresh
|
|
68
69
|
// sign-in rather than refreshing silently against whatever server discovery now names.
|
|
69
70
|
const issuer = storedGrantIssuer(current);
|
|
70
|
-
const authorizationServer =
|
|
71
|
-
if (issuer === undefined || !
|
|
71
|
+
const authorizationServer = validatedAuthorizationServerIssuer(info, this.#provider.serverName);
|
|
72
|
+
if (issuer === undefined || !credentialsMatchIssuer(current, authorizationServer)) {
|
|
72
73
|
this.#provider.store.writeUnlocked(undefined);
|
|
73
74
|
throw new OAuthFlowError("needs_auth", issuer === undefined
|
|
74
75
|
? `MCP server ${this.#provider.serverName} has a saved sign-in with no record of the authorization server that issued it; credentials cleared, re-authentication required.`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { loadMcpSdkAuth } from "../sdk.lazy.js";
|
|
2
2
|
import { isInvalidGrant, OAuthFlowError } from "./oauth-errors.js";
|
|
3
3
|
import { oauthFetch } from "./oauth-fetch.js";
|
|
4
|
+
import { validatedAuthorizationServerIssuer } from "./oauth-issuer.js";
|
|
4
5
|
import { assertS256Supported } from "./oauth-refresh.js";
|
|
5
6
|
// Pre-flight: discover the authorization server and REFUSE (typed) if it does
|
|
6
7
|
// not advertise PKCE S256. Runs before any authorize redirect.
|
|
@@ -20,7 +21,7 @@ export async function beginAuthorization(provider, options = {}) {
|
|
|
20
21
|
}
|
|
21
22
|
// Paste flow: validates single-use state itself, then exchanges the code.
|
|
22
23
|
export async function completeAuthorization(provider, redirectInput, options = {}) {
|
|
23
|
-
const { code, state, iss } = parseRedirect(redirectInput, provider
|
|
24
|
+
const { code, state, iss } = parseRedirect(redirectInput, provider);
|
|
24
25
|
if (!provider.consumeState(state)) {
|
|
25
26
|
throw new OAuthFlowError("state_mismatch", `MCP server ${provider.serverName} authorization state did not match (possible CSRF or a stale/replayed link); restart with /mcp auth-start.`, { serverName: provider.serverName });
|
|
26
27
|
}
|
|
@@ -56,15 +57,19 @@ export async function finishAuthorization(provider, code, options = {}, iss) {
|
|
|
56
57
|
// RFC 9207: an authorization response that names another authorization server, or that omits `iss`
|
|
57
58
|
// although this server promises to send it, may have been injected by a mix-up attack; never exchange its code.
|
|
58
59
|
function assertAuthorizationResponseIssuer(provider, iss) {
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
|
|
60
|
+
const discovery = provider.discoveryState();
|
|
61
|
+
if (discovery === undefined) {
|
|
62
|
+
throw new OAuthFlowError("needs_auth", `MCP server ${provider.serverName} has no trusted authorization discovery; restart with /mcp auth-start ${provider.serverName}.`, { serverName: provider.serverName });
|
|
63
|
+
}
|
|
64
|
+
const metadata = discovery.authorizationServerMetadata;
|
|
65
|
+
const issuer = validatedAuthorizationServerIssuer(discovery, provider.serverName);
|
|
62
66
|
// The SDK keeps unknown metadata fields (loose schema) but does not type this RFC 9207 one.
|
|
63
|
-
const promised = metadata
|
|
64
|
-
|
|
67
|
+
const promised = metadata !== undefined &&
|
|
68
|
+
"authorization_response_iss_parameter_supported" in metadata &&
|
|
69
|
+
metadata.authorization_response_iss_parameter_supported === true;
|
|
65
70
|
if (iss === undefined && !promised)
|
|
66
71
|
return;
|
|
67
|
-
if (iss !== undefined && iss ===
|
|
72
|
+
if (iss !== undefined && iss === issuer)
|
|
68
73
|
return;
|
|
69
74
|
throw new OAuthFlowError("needs_auth", iss === undefined
|
|
70
75
|
? `MCP server ${provider.serverName} authorization response has no issuer although its authorization server promises one; restart with /mcp auth-start ${provider.serverName}.`
|
|
@@ -111,19 +116,19 @@ export async function clientCredentialsGrant(provider, options = {}) {
|
|
|
111
116
|
resource: new URL(provider.serverUrl),
|
|
112
117
|
fetchFn: oauthFetch(options.fetchFn),
|
|
113
118
|
});
|
|
114
|
-
await provider.saveTokens({ ...tokens, issuer:
|
|
119
|
+
await provider.saveTokens({ ...tokens, issuer: validatedAuthorizationServerIssuer(info, provider.serverName) });
|
|
115
120
|
}
|
|
116
121
|
export async function logout(provider) {
|
|
117
122
|
await provider.store.clear();
|
|
118
123
|
}
|
|
119
124
|
async function discover(provider, options) {
|
|
120
|
-
if (options.discover !== undefined)
|
|
121
|
-
return options.discover(provider.serverUrl);
|
|
122
125
|
const cached = provider.discoveryState();
|
|
123
126
|
if (cached !== undefined)
|
|
124
127
|
return cached;
|
|
125
128
|
const { discoverOAuthServerInfo } = await loadMcpSdkAuth();
|
|
126
|
-
const info =
|
|
129
|
+
const info = options.discover !== undefined
|
|
130
|
+
? await options.discover(provider.serverUrl)
|
|
131
|
+
: await discoverOAuthServerInfo(provider.serverUrl, { fetchFn: oauthFetch(options.fetchFn) });
|
|
127
132
|
await provider.saveDiscoveryState(toDiscoveryState(info));
|
|
128
133
|
return info;
|
|
129
134
|
}
|
|
@@ -134,7 +139,8 @@ function toDiscoveryState(info) {
|
|
|
134
139
|
resourceMetadata: info.resourceMetadata,
|
|
135
140
|
};
|
|
136
141
|
}
|
|
137
|
-
function parseRedirect(input,
|
|
142
|
+
function parseRedirect(input, provider) {
|
|
143
|
+
const serverName = provider.serverName;
|
|
138
144
|
let url;
|
|
139
145
|
try {
|
|
140
146
|
url = new URL(input.trim());
|
|
@@ -142,6 +148,7 @@ function parseRedirect(input, serverName) {
|
|
|
142
148
|
catch {
|
|
143
149
|
throw new OAuthFlowError("needs_auth", `MCP server ${serverName} received a malformed redirect URL; paste the full http://127.0.0.1/... address from your browser.`, { serverName });
|
|
144
150
|
}
|
|
151
|
+
assertAuthorizationResponseIssuer(provider, url.searchParams.get("iss") ?? undefined);
|
|
145
152
|
const error = url.searchParams.get("error");
|
|
146
153
|
if (error !== null) {
|
|
147
154
|
throw new OAuthFlowError("needs_auth", `MCP server ${serverName} authorization failed: ${error}`, { serverName });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
2
|
import type { McpServerDeclaredConfig } from "./config-schema.ts";
|
|
3
3
|
import type { ServerConnection } from "./connection.ts";
|
|
4
|
-
type ListedTool
|
|
4
|
+
import type { McpListedTool as ListedTool } from "./tool-contracts.ts";
|
|
5
5
|
type ListedResource = Awaited<ReturnType<Client["listResources"]>>["resources"][number];
|
|
6
6
|
type ListedResourceTemplate = Awaited<ReturnType<Client["listResourceTemplates"]>>["resourceTemplates"][number];
|
|
7
7
|
type ListedPrompt = Awaited<ReturnType<Client["listPrompts"]>>["prompts"][number];
|
|
@@ -10,6 +10,7 @@ export interface McpCatalogCacheFile {
|
|
|
10
10
|
readonly servers: Record<string, McpCachedServerCatalog>;
|
|
11
11
|
}
|
|
12
12
|
export interface McpCachedServerCatalog {
|
|
13
|
+
readonly modern?: boolean;
|
|
13
14
|
readonly configHash: string;
|
|
14
15
|
readonly credentialIdentity?: string;
|
|
15
16
|
readonly fetchedAt: number;
|
|
@@ -6,6 +6,9 @@ import { redactResolvedReferenceData } from "../../../config-reference-redaction
|
|
|
6
6
|
import { lockWithLossHandling } from "../../../file-lock.js";
|
|
7
7
|
import { isEphemeralIdentity } from "./config-identity-key.js";
|
|
8
8
|
import { collectAllPages } from "./expose/pagination.js";
|
|
9
|
+
import { listMcpTools } from "./tool-client.js";
|
|
10
|
+
import { isModernMcpClient } from "./modern/request.js";
|
|
11
|
+
import { hasSupportedModernToolSchemas } from "./modern/tool-schema.js";
|
|
9
12
|
const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
10
13
|
const EMPTY_CACHE = { version: 1, servers: {} };
|
|
11
14
|
export function getMcpCatalogCachePath(agentDir = getAgentDir()) {
|
|
@@ -36,7 +39,7 @@ export function cachedCatalogNeedsRefresh(cached, now = Date.now()) {
|
|
|
36
39
|
return now - cached.fetchedAt > CACHE_TTL_MS;
|
|
37
40
|
}
|
|
38
41
|
export async function collectServerCatalogForCache(connection, config, configHash, credentialIdentity) {
|
|
39
|
-
const tools = await collectAllPages((cursor) => connection.client
|
|
42
|
+
const tools = await collectAllPages((cursor) => listMcpTools(connection.client, cursor === undefined ? {} : { cursor }, {
|
|
40
43
|
timeout: config.requestTimeoutMs,
|
|
41
44
|
}));
|
|
42
45
|
const resources = await collectOptionalPages((cursor) => connection.client.listResources(cursor === undefined ? {} : { cursor }, {
|
|
@@ -50,6 +53,7 @@ export async function collectServerCatalogForCache(connection, config, configHas
|
|
|
50
53
|
}));
|
|
51
54
|
return {
|
|
52
55
|
configHash,
|
|
56
|
+
...(isModernMcpClient(connection.client) ? { modern: true } : {}),
|
|
53
57
|
credentialIdentity,
|
|
54
58
|
fetchedAt: Date.now(),
|
|
55
59
|
instructions: connection.client.getInstructions(),
|
|
@@ -128,7 +132,7 @@ function normalizeCachedServer(value) {
|
|
|
128
132
|
if (!isRecord(value) || typeof value.configHash !== "string" || typeof value.fetchedAt !== "number") {
|
|
129
133
|
return undefined;
|
|
130
134
|
}
|
|
131
|
-
const tools = normalizeTools(value.tools);
|
|
135
|
+
const tools = normalizeTools(value.tools, value.modern === true);
|
|
132
136
|
if (tools === undefined)
|
|
133
137
|
return undefined;
|
|
134
138
|
const resources = Array.isArray(value.resources) ? value.resources : [];
|
|
@@ -139,6 +143,7 @@ function normalizeCachedServer(value) {
|
|
|
139
143
|
const instructions = typeof value.instructions === "string" ? value.instructions : undefined;
|
|
140
144
|
return {
|
|
141
145
|
configHash: value.configHash,
|
|
146
|
+
...(value.modern === true ? { modern: true } : {}),
|
|
142
147
|
credentialIdentity: typeof value.credentialIdentity === "string" ? value.credentialIdentity : undefined,
|
|
143
148
|
fetchedAt: value.fetchedAt,
|
|
144
149
|
instructions,
|
|
@@ -148,19 +153,27 @@ function normalizeCachedServer(value) {
|
|
|
148
153
|
tools,
|
|
149
154
|
};
|
|
150
155
|
}
|
|
151
|
-
function normalizeTools(value) {
|
|
156
|
+
function normalizeTools(value, modern) {
|
|
152
157
|
if (!Array.isArray(value))
|
|
153
158
|
return undefined;
|
|
154
159
|
const tools = [];
|
|
155
160
|
for (const item of value) {
|
|
156
|
-
if (!isRecord(item) || typeof item.name !== "string"
|
|
161
|
+
if (!isRecord(item) || typeof item.name !== "string")
|
|
157
162
|
return undefined;
|
|
158
|
-
|
|
163
|
+
if (!isRecord(item.inputSchema) && !(modern && typeof item.inputSchema === "boolean"))
|
|
164
|
+
return undefined;
|
|
165
|
+
const tool = {
|
|
166
|
+
...(modern ? item : {}),
|
|
159
167
|
annotations: isRecord(item.annotations) ? item.annotations : undefined,
|
|
160
168
|
description: typeof item.description === "string" ? item.description : undefined,
|
|
161
169
|
inputSchema: item.inputSchema,
|
|
170
|
+
...(modern && (isRecord(item.outputSchema) || typeof item.outputSchema === "boolean")
|
|
171
|
+
? { outputSchema: item.outputSchema }
|
|
172
|
+
: {}),
|
|
162
173
|
name: item.name,
|
|
163
|
-
}
|
|
174
|
+
};
|
|
175
|
+
if (!modern || hasSupportedModernToolSchemas(tool))
|
|
176
|
+
tools.push(tool);
|
|
164
177
|
}
|
|
165
178
|
return tools;
|
|
166
179
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { McpListedTool as ListedTool } from "./tool-contracts.ts";
|
|
2
2
|
import type { McpCachedServerCatalog } from "./catalog-cache.ts";
|
|
3
3
|
import type { McpServerDeclaredConfig, McpSettings } from "./config-schema.ts";
|
|
4
4
|
import type { ServerConnection } from "./connection.ts";
|
|
@@ -6,12 +6,13 @@ import type { McpOutputArtifacts } from "./guard/output-guard.ts";
|
|
|
6
6
|
import type { McpEnsureFreshAuth } from "./health.ts";
|
|
7
7
|
import type { McpInvocationResolver } from "./invocation.ts";
|
|
8
8
|
import type { McpRequestOrigin } from "./request-origin.ts";
|
|
9
|
-
type ListedTool = Awaited<ReturnType<Client["listTools"]>>["tools"][number];
|
|
10
9
|
export interface McpToolCatalogEntry {
|
|
11
10
|
requestOrigin?: () => McpRequestOrigin | undefined;
|
|
12
11
|
server: string;
|
|
13
12
|
tool: string;
|
|
14
13
|
schema: ListedTool["inputSchema"];
|
|
14
|
+
outputSchema?: ListedTool["outputSchema"];
|
|
15
|
+
modern?: boolean;
|
|
15
16
|
description?: string;
|
|
16
17
|
annotations?: ListedTool["annotations"];
|
|
17
18
|
requestTimeoutMs: number;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { listMcpTools } from "./tool-client.js";
|
|
2
|
+
import { isModernMcpClient } from "./modern/request.js";
|
|
3
|
+
import { hasSupportedModernToolSchemas } from "./modern/tool-schema.js";
|
|
1
4
|
import { collectAllPages } from "./expose/pagination.js";
|
|
2
5
|
export async function collectToolCatalog(server, connection, config, options = {}) {
|
|
3
|
-
const result = await collectAllPages((cursor) => connection.client
|
|
6
|
+
const result = await collectAllPages((cursor) => listMcpTools(connection.client, cursor === undefined ? {} : { cursor }, { timeout: config.requestTimeoutMs }));
|
|
4
7
|
return result.items.map((tool) => ({
|
|
5
8
|
requestOrigin: options.requestOrigin,
|
|
6
9
|
annotations: tool.annotations,
|
|
@@ -13,6 +16,8 @@ export async function collectToolCatalog(server, connection, config, options = {
|
|
|
13
16
|
outputGuard: options.outputGuard,
|
|
14
17
|
requestTimeoutMs: config.requestTimeoutMs,
|
|
15
18
|
schema: tool.inputSchema,
|
|
19
|
+
outputSchema: tool.outputSchema,
|
|
20
|
+
modern: isModernMcpClient(connection.client),
|
|
16
21
|
server,
|
|
17
22
|
tool: tool.name,
|
|
18
23
|
}));
|
|
@@ -21,13 +26,23 @@ export async function collectToolCatalog(server, connection, config, options = {
|
|
|
21
26
|
* equal identities register identical definitions. */
|
|
22
27
|
export function mcpRegistrationIdentity(tools, catalog) {
|
|
23
28
|
return JSON.stringify([
|
|
24
|
-
tools.map((entry) => [
|
|
29
|
+
tools.map((entry) => [
|
|
30
|
+
entry.tool,
|
|
31
|
+
entry.description,
|
|
32
|
+
entry.schema,
|
|
33
|
+
entry.outputSchema,
|
|
34
|
+
entry.modern,
|
|
35
|
+
entry.annotations,
|
|
36
|
+
]),
|
|
25
37
|
catalog?.resources ?? [],
|
|
26
38
|
catalog?.prompts ?? [],
|
|
27
39
|
]);
|
|
28
40
|
}
|
|
29
41
|
export function cachedToolsToCatalogEntries(server, catalog, connection, requestTimeoutMs, ensureConnected, options = {}) {
|
|
30
|
-
|
|
42
|
+
const modern = connection.state === "connected" ? isModernMcpClient(connection.client) || undefined : catalog.modern;
|
|
43
|
+
return catalog.tools
|
|
44
|
+
.filter((tool) => !modern || hasSupportedModernToolSchemas(tool))
|
|
45
|
+
.map((tool) => ({
|
|
31
46
|
requestOrigin: options.requestOrigin,
|
|
32
47
|
annotations: tool.annotations,
|
|
33
48
|
agentDir: options.agentDir,
|
|
@@ -40,6 +55,8 @@ export function cachedToolsToCatalogEntries(server, catalog, connection, request
|
|
|
40
55
|
outputGuard: options.outputGuard,
|
|
41
56
|
requestTimeoutMs,
|
|
42
57
|
schema: tool.inputSchema,
|
|
58
|
+
outputSchema: tool.outputSchema,
|
|
59
|
+
modern,
|
|
43
60
|
server,
|
|
44
61
|
tool: tool.name,
|
|
45
62
|
}));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { McpToolCatalogEntry } from "../catalog.ts";
|
|
2
2
|
import type { McpInvocation, McpInvocationError } from "../invocation.ts";
|
|
3
3
|
import type { McpToolDefinition } from "./register.ts";
|
|
4
|
+
import type { McpToolResult } from "../tool-contracts.ts";
|
|
4
5
|
export declare function callMcpTool(entry: McpToolCatalogEntry, args: Record<string, unknown>, signal: AbortSignal | undefined, onUpdate: Parameters<McpToolDefinition["execute"]>[3], label: string, invocation: McpInvocation | undefined): Promise<{
|
|
5
6
|
kind: "called";
|
|
6
7
|
entry: McpToolCatalogEntry;
|
|
7
|
-
result:
|
|
8
|
+
result: McpToolResult;
|
|
8
9
|
} | {
|
|
9
10
|
kind: "refused";
|
|
10
11
|
error: McpInvocationError;
|
|
@@ -4,6 +4,7 @@ import { ensureMcpToolCallConnection, withMcpRetriableFailedSendRetry, withMcpSe
|
|
|
4
4
|
import { runMcpConnectionLifecycleCall } from "../idle.js";
|
|
5
5
|
import { mcpRequestOptions } from "../request-origin.js";
|
|
6
6
|
import { McpMrtrError } from "../modern/errors.js";
|
|
7
|
+
import { callMcpToolRequest } from "../tool-client.js";
|
|
7
8
|
export async function callMcpTool(entry, args, signal, onUpdate, label, invocation) {
|
|
8
9
|
try {
|
|
9
10
|
return await runMcpConnectionLifecycleCall(entry.connection, () => withMcpSessionExpiryRetry(entry.connection, async () => {
|
|
@@ -31,7 +32,7 @@ export async function callMcpTool(entry, args, signal, onUpdate, label, invocati
|
|
|
31
32
|
if (prepared !== undefined && !prepared.isCurrent())
|
|
32
33
|
continue;
|
|
33
34
|
const current = prepared?.entry ?? entry;
|
|
34
|
-
const result = await current.connection.client
|
|
35
|
+
const result = await callMcpToolRequest(current.connection.client, { name: current.tool, arguments: args }, current.outputSchema, mcpRequestOptions(current.requestOrigin?.(), {
|
|
35
36
|
onprogress: (progress) => {
|
|
36
37
|
onUpdate?.(redactResolvedReferenceData({
|
|
37
38
|
content: [{ type: "text", text: formatProgress(label, progress) }],
|
|
@@ -18,7 +18,7 @@ export function buildMcpToolDefinitions(entries, warn) {
|
|
|
18
18
|
return mapMcpCatalogNames(entries, warn).map(({ entry, name }) => createMcpToolDefinition(entry, name));
|
|
19
19
|
}
|
|
20
20
|
function createMcpToolDefinition(entry, name) {
|
|
21
|
-
const converted = convertJsonSchemaToTypeBox(entry.schema);
|
|
21
|
+
const converted = convertJsonSchemaToTypeBox(entry.schema, entry.modern);
|
|
22
22
|
const label = `${entry.server}/${entry.tool}`;
|
|
23
23
|
registerDispatchIdentity(converted.schema, () => entry.invocation?.identity(entry));
|
|
24
24
|
return {
|