pi2dsh 0.13.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +2 -0
  2. package/README.zh.md +2 -0
  3. package/dist/{analyzer-DBtUYKI1.mjs → analyzer-DPZ8LzSt.mjs} +2 -2
  4. package/dist/{analyzer-DBtUYKI1.mjs.map → analyzer-DPZ8LzSt.mjs.map} +1 -1
  5. package/dist/cli.mjs +2 -2
  6. package/dist/compat/pi-ai.d.mts +1 -1
  7. package/dist/compat/pi-ai.mjs +1 -1
  8. package/dist/compat/pi-coding-agent.mjs +1 -1
  9. package/dist/{compatibility-Bz6ZXA7F.mjs → compatibility-CP2tpyhg.mjs} +13 -1
  10. package/dist/compatibility-CP2tpyhg.mjs.map +1 -0
  11. package/dist/{credentials-oauth-CAohSLcB.mjs → credentials-oauth-Didpkkjx.mjs} +3 -3
  12. package/dist/{credentials-oauth-CAohSLcB.mjs.map → credentials-oauth-Didpkkjx.mjs.map} +1 -1
  13. package/dist/credentials-oauth.mjs +1 -1
  14. package/dist/host.d.mts +3 -1
  15. package/dist/host.d.mts.map +1 -1
  16. package/dist/host.mjs +4 -3
  17. package/dist/host.mjs.map +1 -1
  18. package/dist/index.d.mts +34 -4
  19. package/dist/index.d.mts.map +1 -1
  20. package/dist/index.mjs +117 -35
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{pi-ai-Dh7HUxbt.mjs → pi-ai-DpqiLdPA.mjs} +7 -2
  23. package/dist/{pi-ai-Dh7HUxbt.mjs.map → pi-ai-DpqiLdPA.mjs.map} +1 -1
  24. package/dist/{pi-ai-CfokFhIL.d.mts → pi-ai-ZU5MelXy.d.mts} +7 -2
  25. package/dist/{pi-ai-CfokFhIL.d.mts.map → pi-ai-ZU5MelXy.d.mts.map} +1 -1
  26. package/dist/{pi-coding-agent-Cvez0jkF.mjs → pi-coding-agent-DK8hbJP5.mjs} +0 -0
  27. package/dist/{pi-coding-agent-Cvez0jkF.mjs.map → pi-coding-agent-DK8hbJP5.mjs.map} +1 -1
  28. package/dist/{runtime-Dd2KhoX-.mjs → runtime-Bb9Bfk4M.mjs} +61 -10
  29. package/dist/runtime-Bb9Bfk4M.mjs.map +1 -0
  30. package/dist/runtime.d.mts +15 -1
  31. package/dist/runtime.d.mts.map +1 -1
  32. package/dist/runtime.mjs +1 -1
  33. package/package.json +8 -6
  34. package/dist/compatibility-Bz6ZXA7F.mjs.map +0 -1
  35. package/dist/runtime-Dd2KhoX-.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compatibility-CP2tpyhg.mjs","names":[],"sources":["../src/compatibility.ts"],"sourcesContent":["import type { CompatibilityLevel } from './types.js'\n\ninterface Rule {\n level: CompatibilityLevel\n detail: string\n /** How the mapping is built — the DSH seam or service that carries it. */\n design?: string\n}\n\n/**\n * A Pi extension surface. `design` is REQUIRED here and optional on Rule: an\n * imported symbol is served by a shim family the docs describe once, but every\n * surface a package can call has to name the DSH mechanism behind it, or the\n * documentation degrades into a list of verdicts nobody can act on. Adding a\n * surface without one is a type error, which is the point.\n */\ninterface SurfaceRule extends Rule {\n design: string\n}\n\nconst rule = (level: CompatibilityLevel, detail: string): Rule => ({ level, detail })\n\nconst surface = (level: CompatibilityLevel, detail: string, design: string): SurfaceRule =>\n ({ level, detail, design })\n\nexport const PI_CODING_AGENT_PACKAGES = Object.freeze([\n '@earendil-works/pi-coding-agent',\n '@mariozechner/pi-coding-agent',\n] as const)\n\nexport const PI_TUI_PACKAGES = Object.freeze([\n '@earendil-works/pi-tui',\n '@mariozechner/pi-tui',\n] as const)\n\nexport const PI_AI_PACKAGES = Object.freeze([\n '@earendil-works/pi-ai',\n '@mariozechner/pi-ai',\n] as const)\n\nconst VENDORED = 'Vendored byte-identical from Pi, so semantics match Pi exactly.'\nconst HEADLESS_COMPONENT = 'Constructible headless component with Pi-exact signatures; renders plain text, never a terminal.'\nconst VENDORED_TOOL = 'Pi\\'s built-in tool constructor, vendored byte-identical with its pure-logic closure.'\nconst EVENT_GUARD = 'Pi\\'s exact one-line tool-event guard, reimplemented verbatim.'\nconst VENDORED_SUMMARIZER = 'Vendored Pi logic; the model call fills Pi\\'s own streamFn injection point with the DSH llm bridge when the caller passes none, so summarization runs on the ONE model path. Without a mounted llm service it fails explicitly.'\nconst PI_PROVIDER_TRANSPORT_FACTORY = 'Pi\\'s real lazy protocol transport factory, exposed to transport-owning provider packages and then wrapped as one native DSH llm adapter.'\n\nexport const HOST_IMPORT_RULES: Readonly<Record<string, Readonly<Record<string, Rule>>>> = Object.freeze({\n 'pi-coding-agent': Object.freeze({\n defineTool: rule('full', 'Identity helper, preserved.'),\n CONFIG_DIR_NAME: rule('partial', 'The conventional config directory name is preserved, while DSH owns the actual profile layout.'),\n DEFAULT_MAX_LINES: rule('full', VENDORED),\n DEFAULT_MAX_BYTES: rule('full', VENDORED),\n VERSION: rule('partial', 'Reports a pi2dsh compatibility marker instead of a Pi release version.'),\n CURRENT_SESSION_VERSION: rule('full', VENDORED),\n getAgentDir: rule('partial', 'Redirected to an isolated DSH-owned pi2dsh directory instead of Pi global state.'),\n getPackageDir: rule('partial', 'Resolves inside the DSH-owned pi2dsh agent directory.'),\n formatSize: rule('full', VENDORED),\n truncateHead: rule('full', VENDORED),\n truncateTail: rule('full', VENDORED),\n truncateLine: rule('full', VENDORED),\n withFileMutationQueue: rule('full', VENDORED),\n SessionManager: rule('full', `${VENDORED} Sessions live under the DSH-owned pi2dsh agent directory.`),\n parseSessionEntries: rule('full', VENDORED),\n migrateSessionEntries: rule('full', VENDORED),\n getLatestCompactionEntry: rule('full', VENDORED),\n sessionEntryToContextMessages: rule('full', VENDORED),\n buildContextEntries: rule('full', VENDORED),\n buildSessionContext: rule('full', VENDORED),\n loadEntriesFromFile: rule('full', VENDORED),\n findMostRecentSession: rule('full', VENDORED),\n getDefaultSessionDir: rule('full', VENDORED),\n assertValidSessionId: rule('full', VENDORED),\n convertToLlm: rule('full', VENDORED),\n createCustomMessage: rule('full', VENDORED),\n createBranchSummaryMessage: rule('full', VENDORED),\n createCompactionSummaryMessage: rule('full', VENDORED),\n bashExecutionToText: rule('full', VENDORED),\n estimateTokens: rule('full', VENDORED),\n calculateContextTokens: rule('full', `${VENDORED} Pi\\'s Usage-based total (an earlier reimplementation summed message estimates — same name, different meaning — and was replaced).`),\n DEFAULT_COMPACTION_SETTINGS: rule('full', VENDORED),\n serializeConversation: rule('full', VENDORED),\n shouldCompact: rule('full', `${VENDORED} The pure threshold check; DSH still owns automatic compaction scheduling.`),\n compact: rule('partial', VENDORED_SUMMARIZER),\n findCutPoint: rule('full', VENDORED),\n generateSummary: rule('partial', VENDORED_SUMMARIZER),\n generateSummaryWithUsage: rule('partial', VENDORED_SUMMARIZER),\n generateBranchSummary: rule('partial', VENDORED_SUMMARIZER),\n parseFrontmatter: rule('full', `${VENDORED} Returns Pi\\'s public { frontmatter, body } shape with YAML-parsed values (an earlier reimplementation returned { attributes } with string values and was replaced).`),\n stripFrontmatter: rule('full', VENDORED),\n copyToClipboard: rule('partial', 'Attempts the platform clipboard command (pbcopy/clip/wl-copy/xclip); resolves false when none succeeds.'),\n resizeImage: rule('partial', 'Passes images through un-resized; Pi resizes only to save tokens, so content is preserved.'),\n convertToPng: rule('partial', 'PNG input passes through; other formats fail explicitly without Pi\\'s wasm codec.'),\n getShellConfig: rule('partial', 'Standard shell detection without Pi\\'s managed-bin PATH handling.'),\n getBinDir: rule('partial', 'Reports the first PATH segment instead of Pi\\'s managed bin directory.'),\n Theme: rule('partial', 'Headless theme: styling calls return their input text unstyled.'),\n theme: rule('partial', 'A headless theme singleton; jiti-loaded Pi extensions must not rely on Pi global theme state anyway.'),\n initTheme: rule('partial', 'Accepted as a no-op; DSH surfaces own presentation.'),\n getSettingsListTheme: rule('partial', 'Returns an unstyled theme with Pi\\'s exact field shape.'),\n getSelectListTheme: rule('partial', 'Returns an unstyled theme with Pi\\'s exact field shape.'),\n getMarkdownTheme: rule('partial', 'Returns a plain-text headless theme; Pi terminal styling is intentionally discarded.'),\n getLanguageFromPath: rule('partial', 'Extension-based language detection covering common languages.'),\n highlightCode: rule('partial', 'Splits lines without terminal syntax colors.'),\n DynamicBorder: rule('full', 'Headless implementation of Pi\\'s one-line border component; pass an explicit color function as Pi itself recommends.'),\n SettingsManager: rule('partial', 'In-memory settings with Pi\\'s getter/setter surface; DSH owns real persisted configuration.'),\n InMemorySettingsStorage: rule('partial', 'In-memory storage stub honoring the withLock contract.'),\n FileSettingsStorage: rule('partial', 'Alias of the in-memory storage; DSH owns persisted settings.'),\n ModelRegistry: rule('partial', 'A local registry container; DSH llm adapters own real model routing.'),\n createEventBus: rule('full', 'Pi event-bus semantics: async handler isolation and unsubscribe functions.'),\n readStoredCredential: rule('partial', 'Reads Pi-style auth.json from the pi2dsh-owned agent directory; DSH credentials stay authoritative for DSH model calls.'),\n parseSkillBlock: rule('full', 'Pi\\'s skill_content block parser, reimplemented over the same wire shape.'),\n wrapRegisteredTool: rule('full', `${VENDORED} The runner argument is used exactly as Pi uses it (createContext + getActiveTools), which the pi2dsh projection provides.`),\n ProjectTrustStore: rule('partial', `${VENDORED} The store operates on trust.json under the caller\\'s agentDir — with the redirected getAgentDir convention that is package-visible state inside the DSH-owned pi2dsh directory. The DSH host never consults it; host trust stays a DSH decision (ctx.isProjectTrusted fails closed).`),\n DefaultResourceLoader: rule('partial', 'A headless resource loader honoring overrides, with empty discovery sets.'),\n DefaultPackageManager: rule('unsupported', 'Installing packages is owned by the DSH host and its security gates (dsh plugin add/remove behind pnpm\\'s build-script approval). Importing it is flagged at mount time (startup check); constructing it throws a structured PiCapabilityError, and doing so during entry setup marks the package unusable.'),\n ModelRuntime: rule('unsupported', 'Standalone model stacks are owned by the DSH host llm configuration; packages read the ONE directory through ctx.modelRegistry. Importing it is flagged at mount time (startup check); constructing it throws a structured PiCapabilityError, and doing so during entry setup marks the package unusable.'),\n createAgentSession: rule('partial', 'Bridged to a genuine DSH child agent through ctx.agents (a Pi model on the options routes the child); compositions without a loop factory fail explicitly.'),\n createCodingTools: rule('full', 'Pi\\'s exact tool set composed from the vendored built-in constructors.'),\n createReadOnlyTools: rule('full', 'Pi\\'s exact read-only tool set composed from the vendored built-in constructors.'),\n createBashTool: rule('full', VENDORED_TOOL),\n createReadTool: rule('full', VENDORED_TOOL),\n createEditTool: rule('full', VENDORED_TOOL),\n createWriteTool: rule('full', VENDORED_TOOL),\n createGrepTool: rule('full', VENDORED_TOOL),\n createFindTool: rule('full', VENDORED_TOOL),\n createLsTool: rule('full', VENDORED_TOOL),\n createBashToolDefinition: rule('full', VENDORED_TOOL),\n createReadToolDefinition: rule('full', VENDORED_TOOL),\n createEditToolDefinition: rule('full', VENDORED_TOOL),\n createWriteToolDefinition: rule('full', VENDORED_TOOL),\n createGrepToolDefinition: rule('full', VENDORED_TOOL),\n createFindToolDefinition: rule('full', VENDORED_TOOL),\n createLsToolDefinition: rule('full', VENDORED_TOOL),\n isToolCallEventType: rule('full', EVENT_GUARD),\n isBashToolResult: rule('full', EVENT_GUARD),\n isReadToolResult: rule('full', EVENT_GUARD),\n isEditToolResult: rule('full', EVENT_GUARD),\n isWriteToolResult: rule('full', EVENT_GUARD),\n isGrepToolResult: rule('full', EVENT_GUARD),\n isFindToolResult: rule('full', EVENT_GUARD),\n isLsToolResult: rule('full', EVENT_GUARD),\n loadSkills: rule('partial', `${VENDORED} Real directory discovery with Pi\\'s exact rules; default locations resolve through the redirected getAgentDir, i.e. inside the DSH-owned pi2dsh directory.`),\n loadSkillsFromDir: rule('full', `${VENDORED} Real directory discovery: SKILL.md roots, direct .md children, ignore files, symlink dedup, and Pi\\'s name/description validation.`),\n formatSkillsForPrompt: rule('full', 'Pi\\'s skill prompt formatter, vendored with logic unchanged.'),\n CustomEditor: rule('partial', HEADLESS_COMPONENT),\n ToolExecutionComponent: rule('partial', HEADLESS_COMPONENT),\n FooterComponent: rule('partial', HEADLESS_COMPONENT),\n BorderedLoader: rule('partial', HEADLESS_COMPONENT),\n CustomMessageComponent: rule('partial', HEADLESS_COMPONENT),\n AssistantMessageComponent: rule('partial', HEADLESS_COMPONENT),\n UserMessageComponent: rule('partial', HEADLESS_COMPONENT),\n ExtensionSelectorComponent: rule('partial', HEADLESS_COMPONENT),\n ExtensionInputComponent: rule('partial', HEADLESS_COMPONENT),\n ExtensionEditorComponent: rule('partial', HEADLESS_COMPONENT),\n SettingsSelectorComponent: rule('partial', HEADLESS_COMPONENT),\n renderDiff: rule('partial', 'Plain unified-style diff lines without terminal colors.'),\n truncateToVisualLines: rule('partial', 'Visual-line truncation backed by Pi\\'s vendored width math.'),\n keyHint: rule('partial', 'Plain-text key hint without theme styling.'),\n keyText: rule('partial', 'Plain-text key name without theme styling.'),\n rawKeyHint: rule('partial', 'Plain-text key hint without theme styling.'),\n }),\n 'pi-tui': Object.freeze({\n visibleWidth: rule('full', VENDORED),\n truncateToWidth: rule('full', VENDORED),\n wrapTextWithAnsi: rule('full', VENDORED),\n sliceByColumn: rule('full', VENDORED),\n sliceWithWidth: rule('full', VENDORED),\n stripTerminalSequences: rule('full', VENDORED),\n getOsc8LinkAtColumn: rule('full', VENDORED),\n normalizeTerminalOutput: rule('full', VENDORED),\n extractAnsiCode: rule('full', VENDORED),\n getGraphemeCellRange: rule('full', VENDORED),\n getGraphemeSegmenter: rule('full', VENDORED),\n getWordSegmenter: rule('full', VENDORED),\n applyBackgroundToLine: rule('full', VENDORED),\n isWhitespaceChar: rule('full', VENDORED),\n isPunctuationChar: rule('full', VENDORED),\n cjkBreakRegex: rule('full', VENDORED),\n PUNCTUATION_REGEX: rule('full', VENDORED),\n fuzzyMatch: rule('full', VENDORED),\n fuzzyFilter: rule('full', VENDORED),\n parseKey: rule('full', VENDORED),\n matchesKey: rule('full', VENDORED),\n isKeyRelease: rule('full', VENDORED),\n isKeyRepeat: rule('full', VENDORED),\n decodeKittyPrintable: rule('full', VENDORED),\n isKittyProtocolActive: rule('full', VENDORED),\n setKittyProtocolActive: rule('full', VENDORED),\n Key: rule('full', VENDORED),\n getKeybindings: rule('full', `${VENDORED} Bindings only match when a surface feeds terminal input, which DSH does not.`),\n setKeybindings: rule('full', VENDORED),\n KeybindingsManager: rule('full', VENDORED),\n TUI_KEYBINDINGS: rule('full', VENDORED),\n parseOsc11BackgroundColor: rule('full', VENDORED),\n parseTerminalColorSchemeReport: rule('full', VENDORED),\n renderLatex: rule('full', VENDORED),\n getPngDimensions: rule('full', VENDORED),\n getJpegDimensions: rule('full', VENDORED),\n getGifDimensions: rule('full', VENDORED),\n getWebpDimensions: rule('full', VENDORED),\n getImageDimensions: rule('full', VENDORED),\n calculateImageRows: rule('full', VENDORED),\n allocateImageId: rule('full', VENDORED),\n encodeKitty: rule('partial', `${VENDORED} No DSH surface consumes the escape sequences.`),\n encodeITerm2: rule('partial', `${VENDORED} No DSH surface consumes the escape sequences.`),\n deleteKittyImage: rule('partial', `${VENDORED} No DSH surface consumes the escape sequences.`),\n deleteAllKittyImages: rule('partial', `${VENDORED} No DSH surface consumes the escape sequences.`),\n detectCapabilities: rule('partial', `${VENDORED} Headless environments report no image protocol.`),\n getCapabilities: rule('partial', VENDORED),\n setCapabilities: rule('partial', VENDORED),\n resetCapabilitiesCache: rule('partial', VENDORED),\n getCellDimensions: rule('partial', VENDORED),\n setCellDimensions: rule('partial', VENDORED),\n hyperlink: rule('full', VENDORED),\n imageFallback: rule('full', VENDORED),\n CombinedAutocompleteProvider: rule('partial', `${VENDORED} Suggestions surface only if a DSH UI asks for them.`),\n Marked: rule('full', 'Re-exported from the same marked dependency Pi uses.'),\n CURSOR_MARKER: rule('full', 'Pi\\'s exact APC marker; vendored width math treats it as zero-width.'),\n Text: rule('partial', HEADLESS_COMPONENT),\n Spacer: rule('partial', HEADLESS_COMPONENT),\n Container: rule('partial', HEADLESS_COMPONENT),\n Box: rule('partial', HEADLESS_COMPONENT),\n Markdown: rule('partial', HEADLESS_COMPONENT),\n TruncatedText: rule('partial', HEADLESS_COMPONENT),\n Editor: rule('partial', `${HEADLESS_COMPONENT} Text editing state works; interactive keyboard flows do not.`),\n Input: rule('partial', `${HEADLESS_COMPONENT} Value state and submit/escape callbacks work; kill-ring editing does not.`),\n SelectList: rule('partial', `${HEADLESS_COMPONENT} Filtering and selection state work; keyboard interaction does not.`),\n SettingsList: rule('partial', `${HEADLESS_COMPONENT} Value updates work; keyboard interaction does not.`),\n ScrollView: rule('partial', HEADLESS_COMPONENT),\n VStack: rule('partial', HEADLESS_COMPONENT),\n HStack: rule('partial', HEADLESS_COMPONENT),\n Loader: rule('partial', HEADLESS_COMPONENT),\n CancellableLoader: rule('partial', HEADLESS_COMPONENT),\n Image: rule('partial', `${HEADLESS_COMPONENT} Renders a text placeholder; image bytes flow through DSH attachments instead.`),\n isFocusable: rule('full', 'Structural check preserved.'),\n isViewportTUI: rule('partial', 'Always false: no viewport TUI exists in DSH surfaces.'),\n }),\n 'pi-ai': Object.freeze({\n StringEnum: rule('full', 'Preserves Pi flat string-enum JSON Schema generation without loading provider SDKs.'),\n envApiKeyAuth: rule('full', 'Pi 0.84.1 stored-key-first auth helper with ordered environment fallback and the original secret-prompt login contract.'),\n anthropicMessagesApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n openAICompletionsApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n openAIResponsesApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n openAICodexResponsesApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n azureOpenAIResponsesApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n googleGenerativeAIApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n googleVertexApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n mistralConversationsApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n bedrockConverseStreamApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n piMessagesApi: rule('full', PI_PROVIDER_TRANSPORT_FACTORY),\n streamSimpleOpenAIResponses: rule('full', 'Pi\\'s real OpenAI Responses simple transport, exported under the legacy symbol used by transport-owning provider packages.'),\n registerProvider: rule('partial', 'Recorded in the Pi-facing registry, then exposed as a DSH route: a transport-owning provider uses llm.registerAdapter; a catalog-only provider is translated into the official llm-pi-ai profile schema.'),\n getProviders: rule('partial', 'Returns the bridge-local registry contents.'),\n getProvider: rule('partial', 'Reads the bridge-local registry.'),\n getModel: rule('partial', 'Resolves no Pi model objects; DSH owns model routing.'),\n getModels: rule('partial', 'Returns an empty list; DSH owns model routing.'),\n complete: rule('partial', 'Real model calls through the DSH llm bridge (the host installs it at mount); without a mounted llm service the call fails explicitly. Provider SDKs are never loaded.'),\n stream: rule('partial', 'Real streaming model calls through the DSH llm bridge (the host installs it at mount); without a mounted llm service the call fails explicitly. Provider SDKs are never loaded.'),\n Type: rule('full', 'Re-exported from the same typebox dependency Pi resolves for extensions.'),\n uuidv7: rule('full', VENDORED),\n isContextOverflow: rule('full', VENDORED),\n isRecoverableLength: rule('full', VENDORED),\n isRetryableAssistantError: rule('full', VENDORED),\n contentText: rule('full', 'Pi\\'s text-block joiner, reimplemented with identical semantics.'),\n clampThinkingLevel: rule('full', 'Pi\\'s clamping walk over the extended thinking-level ladder.'),\n getSupportedThinkingLevels: rule('full', 'Pi\\'s thinkingLevelMap filter, preserved.'),\n modelsAreEqual: rule('full', 'Id+provider equality, preserved.'),\n }),\n})\n\nexport const CONTEXT_RULES: Readonly<Record<string, SurfaceRule>> = Object.freeze({\n cwd: surface('full', 'Mapped to the active DSH agent session working directory.', 'Read off the live DSH agent\\'s session; the bridge keeps no working directory of its own.'),\n signal: surface('full', 'Mapped to the active DSH cancellation signal when one is available.', 'The DSH cancellation signal belonging to the moment the context was built — a tool context carries its execution signal, a lifecycle context the agent\\'s.'),\n hasUI: surface('full', 'Reports whether a real interactive surface exists: true for dsh-TUI\\'s mounted scene service or for a DSH user-question provider; false for a genuinely headless composition and for child-agent questions that DSH refuses.', 'The terminal answer comes from the optional public tuiScenes service. The question answer is probed rather than guessed: the bridge registers a throwaway UserQuestion provider and reads the documented DUPLICATE_PROVIDER rejection as \"a real provider is already there\", then disposes it. Inside a child agent only the terminal scene can make this true, because DSH refuses child-agent questions.'),\n mode: surface('full', 'Reports tui when dsh-TUI\\'s public scene service is mounted, rpc in browser/headless compositions.', 'Derived from the live optional TuiSurfaceAdapter. This is the branch interactive Pi plugins use before calling ui.custom; it changes with the Cordis service lifecycle rather than being a constant.'),\n isIdle: surface('partial', 'Command contexts report idle; tool/lifecycle contexts conservatively report non-idle.', 'Derived from which context the call arrived through: a command context is outside a step, a tool or lifecycle context is inside one.'),\n isProjectTrusted: surface('partial', 'Fails closed as untrusted because DSH does not expose Pi project-trust state.', 'No DSH seam carries Pi\\'s project-trust state, so the bridge returns the safe constant instead of inventing one. Pi\\'s own ProjectTrustStore is vendored and available to packages that manage their own.'),\n hasPendingMessages: surface('full', 'Reads the DSH agent inbox — next-step plus next-turn input — which is exactly Pi\\'s steering plus follow-up queue.', 'Reads the DSH agent inbox — next-step plus next-turn queues — which is the same queue sendMessage/sendUserMessage write into.'),\n getContextUsage: surface('partial', 'Returns no Pi token-usage projection.', 'Not synthesized. DSH accounts tokens in its own token-meter service, and a guessed Pi projection would be read as measurement.'),\n getSystemPrompt: surface('full', 'Returns the system prompt currently assembled by the bridge.', 'Returns the string the bridge recorded during system-prompt/assemble for the current turn. It is recorded on every assembly, before any gate, so a package that only reads the prompt still sees it.'),\n getSystemPromptOptions: surface('partial', 'Returns an empty Pi option projection in command contexts.', 'Pi\\'s option object describes Pi\\'s own prompt builder, which never runs here; the projection stays empty rather than reconstructed from DSH\\'s sections.'),\n waitForIdle: surface('partial', 'Mapped to the DSH agent idle boundary when available.', 'Awaits the DSH agent\\'s own idle boundary when the agent exposes one.'),\n sessionManager: surface('partial', 'A real read-only projection: DSH durable messages plus pi2dsh sidecar entries, exposed through Pi\\'s exact 14-method surface as a single-branch tree. buildContextEntries is compaction-aware — entries a compaction summarized away are gone, exactly as they are for the model — while getEntries stays the append-only log, which is the same split Pi makes.', 'A read-only projection folded from two ordered sources — DSH\\'s durable session log and the pi2dsh sidecar — into the single chain Pi\\'s 14-method surface walks. Compaction awareness comes from the same fold: entries a compaction summarized away are dropped from the context view and kept in the log view.'),\n modelRegistry: surface('partial', 'A live registry over the ONE model directory — the DSH llm directory — projected exactly into Pi vocabulary; package-registered Pi-native routes keep api/baseUrl and the full Model shape through the round trip. DSH describes one model across two seams (a listing for directory membership, an exact per-route resolve for capacity) while Pi puts everything on one Model object read synchronously, so the projection joins them when the directory is read: entries carry contextWindow, maxTokens and reasoning, and a settings change re-reads them rather than serving the retired numbers. Custom gateways are HOST configuration (the official llm-pi-ai adapter\\'s settings), never a Pi-side file: Pi\\'s ~/.pi/agent/models.json is deliberately NOT read — user-facing configuration is DSH-shaped only. getProviderAuth/getApiKeyAndHeaders run Pi\\'s full credential chain for package-registered providers and the host\\'s configurable-provider + credentials seams for DSH routes. Host configuration may declare \"<route>-vision\" image-admission companions: real DSH routes that admit images, replace image blocks with explicit path-carrying notices (materialized attachment files any path-taking tool can read), and forward text-only to the original route; Pi\\'s ctx.model reports the original route for a companion selection.', 'A ModelCatalog over llm.listProviders() x listModels(), joined per route with llm.resolveModelInfo() for capacity, refreshed on the llm/adapters-updated notification and cached so reads stay synchronous — Pi\\'s getAll() is not async. Package-registered routes have their exact Pi Model restored from the registration on the way out.'),\n model: surface('partial', 'The agent\\'s real provider/model route (a setModel() override wins), enriched from the projected catalog. When the selected route is an image-admission companion, ctx.model reports the ORIGINAL route with its true modalities — the generating model is the original text-only one, which is the truth extensions branching on input modalities (a vision bridge\\'s activation check) need.', 'Reads the live agent\\'s own options.provider/model (a setModel override wins) and enriches it through the same catalog projection, so a package reads one Model shape everywhere.'),\n scopedModels: surface('full', 'Empty, carrying Pi\\'s own meaning for empty: no model scope is configured, so every available model is usable. DSH has no model-scope concept to narrow it.', 'Nothing to compute. DSH has no model-scope concept, and Pi\\'s empty array already carries exactly that meaning.'),\n hasConfiguredAuth: surface('partial', 'Configuration check on the projected registry: true when the model\\'s provider has a live route or package registration (not a key-liveness probe).', 'Answered from configuration alone — the package-provider map plus the live catalog — and never opens a connection, which is also what Pi\\'s own check does.'),\n thinkingLevel: surface('partial', 'Reflects the level recorded by setThinkingLevel(); applied as reasoningEffort on the next request.', 'Bridge state written by setThinkingLevel, applied to the request on the agent/request waterfall.'),\n abort: surface('partial', 'Mapped to agent.cancel({ kind: \"hook\" }) on the live DSH agent.', 'Calls agent.cancel({ kind: \\'hook\\' }) on the live DSH agent.'),\n shutdown: surface('partial', 'Pi defines shutdown behavior as host-provided (runner.ts bindExtensions); this host absorbs the request — the user owns DSH process exit — and informs the user once. The package keeps running.', 'Pi defines shutdown as host-provided, so this host absorbs it: the request is recorded in the capability ledger and surfaced to the user once. Nothing calls process.exit — the DSH process belongs to the user.'),\n compact: surface('partial', 'Pi\\'s fire-and-forget trigger, translated to DSH\\'s official manual compaction (ctx.compaction.compactNow on the live agent). onComplete receives the real summary text and the shadowed-content token estimate as tokensBefore; firstKeptEntryId is empty because the DSH log has no Pi entry ids. Without a compaction service the gap flows through Pi\\'s onError callback and the capability ledger.', 'Calls DSH\\'s official compaction.compactNow on the live agent and adapts the outcome onto Pi\\'s onComplete/onError callbacks. With no compaction service mounted the gap flows through onError rather than an exception.'),\n newSession: surface('partial', 'Really creates a DSH session (ctx.sessions.create) with parent lineage; withSession runs against a projection context bound to it, whose sendMessage/sendUserMessage/appendEntry write into THAT session rather than the one the call came from. DSH has no host-level \"current session pointer\" a plugin could move — which session the surface shows stays a host choice, announced once.', 'ctx.sessions.create with parent lineage, then a projection context bound to the new session so everything inside withSession (sendMessage, appendEntry, ...) writes into THAT session.'),\n fork: surface('partial', 'Really forks on DSH\\'s official prefix-fork surface (ctx.sessions.fork with lineage metadata). Anchors are durable-log entries (projected ids \"dsh-<seq>\"); Pi\\'s default position \"before\" is honored, and the boundary shrinks to the nearest completed-turn edge because DSH seeds must not split an open turn. Sidecar entries cannot anchor a fork.', 'ctx.sessions.fork at a durable sequence number decoded from the projected entry id (dsh-<seq>), snapped to the nearest completed-turn edge because a DSH seed must not split an open turn.'),\n navigateTree: surface('partial', 'Expressed through DSH\\'s session model: the DSH tree lives BETWEEN sessions (fork lineage), not inside one log, so navigation forks at the target boundary. summarize runs Pi\\'s vendored branch summarizer over the abandoned durable slice (model call on the DSH llm bridge) and lands the summary as a branch_summary entry in the new session\\'s projection; label lands as a label entry.', 'A fork at the target boundary, plus — when the caller asks for one — Pi\\'s vendored branch summarizer run over the abandoned slice with the DSH llm bridge filling Pi\\'s own streamFn injection point.'),\n switchSession: surface('partial', 'Targets a LIVE DSH session by id (or a Pi-style path whose basename is \"<id>.jsonl\"); withSession runs against its projection. Switching to persisted sessions is host-owned — resume them from the DSH surface first. Which session the surface shows stays a host choice.', 'Resolves the id (or the basename of a Pi-style <id>.jsonl path) against live DSH sessions and binds a projection context to it.'),\n reload: surface('partial', 'Really remounts every mounted package\\'s extension entries through a fresh loader (registrations replaced via the same-name path, event handlers reset), so edited plugin code takes effect. Skills, prompts, and themes are host-managed and reload with dsh itself.', 'Remounts every mounted package\\'s extension entries through a fresh jiti loader: registrations are replaced through the same-name path and handler lists reset, so edited plugin code takes effect without restarting DSH.'),\n})\n\nexport const UI_CONTEXT_RULES: Readonly<Record<string, SurfaceRule>> = Object.freeze({\n notify: surface('full', 'Captured as a command result when applicable and emitted through DSH logging at the severity the caller passed (warning and error log as warnings).', 'Written to the DSH logger at the severity the caller passed, and returned as the command result when the call happens inside one.'),\n setStatus: surface('full', 'Pi\\'s keyed status entries render in the active DSH front door: dsh-TUI\\'s native status line in terminal mode and package-keyed pills in the bridge\\'s browser half. setStatus(key, undefined) removes exactly one entry.', 'Terminal mode writes a package-namespaced key through dsh-TUI\\'s public tuiStatus service and owns the returned disposer. Browser mode writes BrowserSurfaces by (session, package, key); the client half draws the entries from the bridge route.'),\n setWidget: surface('partial', 'String-array widgets render as a strip in DSH\\'s conversation.input.dock seat (a full-width row of its own above the composer card). setWidget(key, undefined) removes one widget. Component factories are ignored, exactly like Pi\\'s own rpc mode, where widgets travel to a host as lines.', 'BrowserSurfaces.setWidget keeps the lines per widget key; the client half draws them in the conversation.input.dock slot. The factory branch is dropped at the ui seam, mirroring rpc-mode.ts\\'s \"Only support string arrays in RPC mode\".'),\n select: surface('full', 'Mapped to one native DSH userQuestions single-select request.', 'One native DSH UserQuestionService request carrying Pi\\'s options as the choices. The turn really blocks until a human answers.'),\n confirm: surface('full', 'Mapped to one native DSH userQuestions Yes/No request.', 'One native DSH UserQuestionService request with two choices.'),\n input: surface('full', 'Mapped to one native DSH userQuestions free-text request.', 'One native DSH UserQuestionService free-text request.'),\n editor: surface('partial', 'Mapped to one DSH userQuestions free-text request. The prefill is shown as context but is NOT editable text: the caller receives what the user typed fresh, not an edit of the prefill.', 'One native DSH UserQuestionService free-text request. DSH has no editable-prefill question type, so the prefill is shown in the question body and the answer comes back as fresh text.'),\n custom: surface('partial', 'In dsh-TUI, runs the real Pi component in a native full-screen scene with raw terminal input, render invalidation, result completion and disposal. In browser/headless compositions it resolves undefined, Pi\\'s own rpc-mode behavior.', 'The bridge adapts the public Pi component protocol (render(width), handleInput(raw bytes), requestRender, dispose) onto dsh-TUI\\'s public tuiScenes seam and host React/ANSI renderer; no Pi TUI owns the terminal. Browsers cannot execute terminal components, so their rpc behavior remains undefined and package-specific browser shapes continue through native client slots.'),\n onTerminalInput: surface('partial', 'Raw terminal input is absent; feature-detected listeners remain disabled.', 'Recorded; no DSH surface produces raw terminal input, and feature-detecting packages keep the listener disabled.'),\n setWorkingMessage: surface('partial', 'A live working message, drawn in DSH\\'s conversation.composer.dock band (under the composer card — the host\\'s ambient-readout seat, where its own stats line sits). Calling with no argument restores the default, i.e. clears it.', 'Recorded per session per package and drawn by the client half\\'s composer-dock seat. Pi\\'s rpc mode is a no-op here (it has no TUI loader); a browser host genuinely can show the text, so the bridge supersedes it.'),\n setWorkingVisible: surface('partial', 'Hides or shows the working chrome (message, indicator, hidden-thinking label) without clearing it — Pi\\'s exact semantics.', 'A flag on the per-package surface view; the client half skips working keys while it is false. Supersedes Pi\\'s rpc-mode no-op with a real visibility switch.'),\n setWorkingIndicator: surface('partial', 'WorkingIndicatorOptions ({frames?: string[]}) project to the frames\\' text in the same composer-dock working chrome. An empty array hides the indicator and frames: undefined restores the default (clears it); animated frames render as their static concatenation — the honest still of the package\\'s own frames.', 'surfaceText projects the frames object to text, recorded like the other working surfaces. Pi\\'s rpc mode ignores the call; the browser host draws the static projection instead.'),\n setHiddenThinkingLabel: surface('partial', 'The label shows in the same working chrome; calling with no argument restores the default, i.e. clears it. DSH owns the thinking block\\'s own presentation, so the label is informational chrome, not a re-render of the block.', 'Recorded per session per package like the other working surfaces. Pi\\'s rpc mode is a no-op here; the browser host shows the text.'),\n setFooter: surface('partial', 'A custom footer factory renders when it can build its component without Pi\\'s TUI, into the conversation.composer.dock band; otherwise the surface stays empty — the same shape as Pi\\'s rpc mode, where no footer factory runs at all. The factory receives the bridge\\'s headless theme.', 'surfaceText calls the factory with the headless theme (and no TUI) and renders the returned component\\'s render(80) output; a factory that needs the TUI throws and degrades to an empty surface.'),\n setHeader: surface('partial', 'A custom header factory renders when it can build its component without Pi\\'s TUI, into DSH\\'s conversation.session.header.utilities seat; otherwise the surface stays empty — the same shape as Pi\\'s rpc mode.', 'surfaceText calls the factory with the headless theme (and no TUI) and renders the component; failures degrade to empty. Drawn by the client half\\'s header-utilities seat.'),\n setTitle: surface('partial', 'Pi\\'s transient window title shows as a frame-wide pill in the bridge\\'s browser half. It deliberately does NOT rename the DSH session: a session title is durable, user-owned and shown in the session list, and quietly rewriting it would outlive the turn that asked.', 'Recorded per session per package and drawn as a pill in the shell.overlay seat, next to the status pills.'),\n pasteToEditor: surface('partial', 'Appends to a per-agent editor buffer readable through getEditorText().', 'Appends to the live composer text and writes it back through the same path as setEditorText — the append is against what the user actually has, not against the package\\'s last write.'),\n setEditorText: surface('partial', 'Stored in a per-agent editor buffer readable through getEditorText().', 'Writes the DSH composer for real. `inputActions.setDraft` is part of the session standard kit every session-scoped slot component receives, so the bridge\\'s own browser half performs the write; the request carries a revision so a repeated call is a new write rather than a no-op. Without a browser (the CLI profile) it falls back to the per-agent buffer, which is what Pi\\'s own non-interactive modes do.'),\n getEditorText: surface('partial', 'Reads the per-agent editor buffer maintained by the bridge.', 'Reads what the composer actually holds: the browser half reports the live draft back to the bridge, so a package sees the user\\'s typing and not only its own writes. With no browser watching, it answers with the last text this package wrote.'),\n addAutocompleteProvider: surface('partial', 'The provider really runs: its completions appear as rows in DSH\\'s own `@` trigger menu, and picking one inserts the value it chose. Providers anchored on a trigger character map exactly; one that completes bare words mid-sentence has no moment to fire in.', 'Pi asks a provider at the cursor on every edit; DSH asks a source after `/` or `@`. The two agree exactly for a provider ANCHORED on a trigger character — an @-mention provider, which is what the ecosystem\\'s provider is — so the bridge builds Pi\\'s provider chain, asks it with the token being typed, and offers the result as rows in DSH\\'s own trigger menu; picking one inserts the value the provider chose. A provider that completes bare words mid-sentence has no DSH moment to fire in and contributes nothing, rather than being given an interaction its author never designed.'),\n setEditorComponent: surface('partial', 'Registration is recorded; no DSH surface mounts a Pi editor component.', 'Kept in bridge state and readable back; no DSH surface consumes it.'),\n getEditorComponent: surface('partial', 'Returns the recorded factory.', 'Returns the factory setEditorComponent recorded.'),\n theme: surface('partial', 'A headless theme whose styling calls return unstyled text.', 'A single headless Theme object whose styling functions return their input unchanged; DSH does the rendering.'),\n getAllThemes: surface('partial', 'Lists the single headless theme.', 'Lists the one headless theme the bridge owns.'),\n getTheme: surface('partial', 'Resolves only the headless theme.', 'Resolves the one headless theme by name.'),\n setTheme: surface('partial', 'Accepts the headless theme; other names report an explicit error result.', 'Accepts the headless theme and returns Pi\\'s explicit error result for any other name, rather than pretending a switch happened.'),\n getToolsExpanded: surface('partial', 'A bridge-local presentation flag.', 'A bridge-local flag; nothing renders from it.'),\n setToolsExpanded: surface('partial', 'A bridge-local presentation flag.', 'A bridge-local flag; nothing renders from it.'),\n})\n\nexport const API_RULES: Readonly<Record<string, SurfaceRule>> = Object.freeze({\n registerTool: {\n level: 'partial',\n detail: 'Registered as a native DSH tool. Text and image results use native DSH content/attachments; unsupported JSON Schema constraints and Pi-only error details are explicitly degraded.',\n design: 'Registered on DSH\\'s own tool registry, so the model sees it in the same catalog and the loop runs it through the same permission and sandbox path. Arguments go through Pi\\'s vendored validateToolArguments inside DSH\\'s prepareArguments hook, which is what gives a Pi tool the coercions (\"7\" to 7) Pi\\'s own gate would have made before its handler ran.',\n },\n unregisterTool: {\n level: 'full',\n detail: 'Disposes the exact native DSH tool registration and removes it from the migrated package registry.',\n design: 'Disposes the exact registration handle kept when the tool was registered, and drops it from the package\\'s own registry.',\n },\n registerCommand: {\n level: 'partial',\n detail: 'Registered in ctx.commands with Pi\\'s never-throw collision semantics: a package re-registering its own name replaces it, and cross-source collisions mount under Pi\\'s numbered scheme (/name-2 — the earlier registration keeps the bare name, where Pi renumbers both). In dsh-TUI, its locally reserved /mcp remains native and the migrated command is reachable as /pi-mcp.',\n design: 'Registered on ctx.commands with an input descriptor. The descriptor is what makes /name <arguments> parse as a command instead of chat. dsh-TUI currently exposes no reserved-command registry; the one verified local interception needed by the MCP integration is translated before registration.',\n },\n registerShortcut: {\n level: 'partial',\n detail: 'Registration is recorded and introspectable; DSH surfaces feed no terminal key input, so handlers never fire — the same as Pi\\'s non-TUI modes.',\n design: 'Recorded in bridge state; no DSH surface feeds terminal key events, exactly as in Pi\\'s own non-TUI modes.',\n },\n registerFlag: {\n level: 'partial',\n detail: 'The declared default is available through getFlag; Pi process flags cannot be added to the DSH launcher.',\n design: 'The declared default goes into a bridge map. DSH\\'s launcher exposes no seam for adding process flags, so the flag exists for the package\\'s own reads only.',\n },\n getFlag: {\n level: 'partial',\n detail: 'Returns the migrated flag default because DSH cannot register the original Pi CLI flag.',\n design: 'Reads the bridge map registerFlag wrote.',\n },\n registerProvider: {\n level: 'partial',\n detail: 'Two outcomes, by whether the provider carries its own transport. '\n + 'WITH a transport (pi-ai createProvider and friends): it becomes a real DSH llm route through llm.registerAdapter, and from then on the package\\'s own HTTP client carries the turn — its API key or OAuth token is resolved by Pi\\'s credential chain and persisted in the bridge\\'s auth.json, not by DSH credentials. '\n + 'WITHOUT one (catalog-only): protocol, endpoint, credential reference, model capabilities, reasoning levels and the compat fields offered by DSH are translated into the official llm-pi-ai profile; no bridge transport is synthesized.',\n design: 'Two mechanisms behind one call. A provider carrying its own transport becomes a real DSH route through llm.registerAdapter, and the package\\'s own HTTP client then carries the turn with its key resolved by Pi\\'s credential chain into the bridge\\'s auth.json. A catalog-only declaration becomes configuration for DSH\\'s official llm-pi-ai adapter, which owns credentials and the real HTTP request. Both enter the one DSH model directory.',\n },\n unregisterProvider: {\n level: 'partial',\n detail: 'Removes the recorded provider declaration.',\n design: 'Disposes the route registration when one was made and drops the recorded declaration.',\n },\n registerMessageRenderer: {\n level: 'partial',\n detail: 'The renderer really runs: a custom message the package sent is drawn by the package\\'s own code and shown in the DSH web conversation. What reaches the browser is the component\\'s rendered text, not a mounted Pi component.',\n design: 'A custom message is a durable DSH log entry carrying Pi\\'s role:\"custom\" marker (source.piCustomType), so the projection reads it back from the session through ctx.sessions.get(id) rather than from a cache — the renderer keeps working after a restart. The returned component is projected through its own render(width), and the text takes a seat in the conversation flow.',\n },\n registerEntryRenderer: {\n level: 'partial',\n detail: 'The renderer really runs: entries the package appended are drawn by the package\\'s own code and shown in the DSH web conversation. What reaches the browser is the component\\'s rendered text, not a mounted Pi component.',\n design: 'Custom entries live in the pi2dsh sidecar (DSH\\'s durable log has no channel for event types declared outside the harness, so the host\\'s own conversation view can never show them). The registered EntryRenderer runs per entry, its component is projected through render(width), and the text is seated in the conversation flow. A renderer that throws contributes nothing and leaves every other package\\'s entries — and the request — intact.',\n },\n registerMarkdownTransformer: {\n level: 'partial',\n detail: 'Registration is accepted; DSH owns presentation, so the transformer is never invoked — matching Pi\\'s non-TUI surfaces.',\n design: 'Kept in bridge state and readable back; no DSH surface consumes it.',\n },\n sendMessage: {\n level: 'partial',\n detail: 'Durable by the time it returns, as in Pi: the no-turn call appends to the session log and announces its message events immediately, so the message IS in the conversation when the call resolves. Steering and follow-up drive a turn through the agent. Pi display/details metadata awaits the custom session-entry seam.',\n design: 'Two paths under one name. The no-turn call appends through Session.append(type, data, { surfaceOp: \\'append\\' }) — the public marker DSH requires for surface-eligible types — so the message is durable and visible by the time the call resolves, then announces its own message_start/message_end. Steering and follow-up go through the DSH agent inbox instead, which is what actually drives a turn.',\n },\n sendUserMessage: {\n level: 'full',\n detail: 'Mapped to native DSH steer/followup delivery with text and attachment-backed image content.',\n design: 'The DSH agent inbox (steer or follow-up by option), with image content materialized as DSH attachments first.',\n },\n appendEntry: {\n level: 'partial',\n detail: 'Persisted in a pi2dsh sidecar next to the DSH session and replayed on session start; DSH\\'s main log stays untouched because it has no out-of-repo plugin-event channel yet.',\n design: 'A pi2dsh sidecar file beside the DSH session, replayed at session start. DSH\\'s own log has no channel for event types declared outside the harness, and writing unknown types into it corrupts the session for every other reader.',\n },\n setSessionName: {\n level: 'full',\n detail: 'Renames the DSH session through ctx.sessionTitle, so every DSH surface shows it and the title is pinned against automatic regeneration, and announces it through session_info_changed. A composition that mounts no title service falls back to the pi2dsh sidecar, as does a blank name (DSH requires visible characters in a title; Pi does not).',\n design: 'ctx.sessionTitle.rename, which is also what pins the title against DSH\\'s automatic regeneration, followed by a session_info_changed dispatch. A composition with no title service — or a blank name, which DSH refuses and Pi allows — falls back to the sidecar.',\n },\n getSessionName: {\n level: 'full',\n detail: 'Reads DSH\\'s own session title, so it agrees with what DSH displays and sees titles DSH generated itself; falls back to the sidecar when no title service is mounted.',\n design: 'ctx.sessionTitle.get, so the answer agrees with what DSH displays and includes titles DSH generated itself; sidecar fallback when no title service is mounted.',\n },\n setLabel: {\n level: 'partial',\n detail: 'Persisted in the pi2dsh sidecar and reflected by the sessionManager projection.',\n design: 'Stored in the pi2dsh sidecar and read back through the sessionManager projection.',\n },\n exec: {\n level: 'partial',\n detail: 'Mapped to ctx.subprocess, so the selected local/E2B provider owns execution, isolation, cancellation, and tree cleanup; output is bounded to 64 MiB per stream.',\n design: 'Handed to ctx.subprocess, so the selected local or E2B provider owns execution, isolation, cancellation and process-tree cleanup — the bridge never spawns a child itself.',\n },\n getActiveTools: {\n level: 'partial',\n detail: 'Returns every tool visible in the current DSH agent scope, including native and migrated tools; scope-local tools follow DSH composition rules.',\n design: 'Lists the DSH tool scope the current context belongs to, native and migrated tools alike.',\n },\n getAllTools: {\n level: 'partial',\n detail: 'Returns metadata for all tools visible in the current DSH scope, without Pi-specific prompt guidelines unavailable from DSH schemas.',\n design: 'Reads schemas from the same DSH tool scope; Pi\\'s prompt-guideline fields have no DSH source and are left out rather than invented.',\n },\n setActiveTools: {\n level: 'partial',\n detail: 'Mapped to the active DSH agent scope through tools.restrict, per-agent and without mutating other agents. Names DSH does not know are skipped exactly as Pi skips them. A tool DSH does not permit restricting (a scope\\'s own registration, or a reserved transport name) cannot be deactivated at all and is reported once rather than silently left running.',\n design: 'DSH\\'s scoped tools.restrict, narrowed first. Pi silently skips names its registry does not know, while DSH fails the whole restrict call on a name it cannot restrict — so the list is filtered against what the scope reports as restrictable before it is applied, and a visible-but-unrestrictable tool is reported once instead of being silently left running. Called before an agent exists, the intent is remembered and applied when one starts.',\n },\n getCommands: {\n level: 'partial',\n detail: 'Returns commands registered by this migrated Pi package, not every command visible in the DSH scope.',\n design: 'Reads the bridge\\'s own command map, which holds this package\\'s registrations rather than the whole DSH scope.',\n },\n setModel: {\n level: 'partial',\n detail: 'Recorded as a per-agent override applied through the agent/request waterfall on the next model call; DSH remains authoritative for provider routing.',\n design: 'Recorded as a per-agent override and applied on the agent/request waterfall, so the next model call carries it while DSH stays authoritative for routing.',\n },\n getThinkingLevel: {\n level: 'partial',\n detail: 'Returns the level recorded by setThinkingLevel (default off).',\n design: 'Reads the per-agent level the bridge recorded.',\n },\n setThinkingLevel: {\n level: 'partial',\n detail: 'Recorded per agent and applied as reasoningEffort through the agent/request waterfall; DSH validates the effort id at the request boundary.',\n design: 'Recorded per agent and applied as reasoningEffort on the agent/request waterfall; DSH validates the effort id at the request boundary.',\n },\n events: {\n level: 'full',\n detail: 'Package-local Pi extension event-bus emit/on semantics are preserved for migrated extensions in the same bundle.',\n design: 'A package-local emitter inside the bridge, so extensions bundled together talk to each other exactly as they do under Pi.',\n },\n})\n\nconst OBSERVED_NEVER_FIRES = (moment: string): SurfaceRule => ({\n level: 'partial',\n detail: `Registration is accepted; ${moment} never occurs on DSH surfaces, so the handler never fires. Loading is unaffected.`,\n design: `The handler goes into the bridge's handler map like any other, and nothing dispatches it: no DSH seam produces ${moment}. Registration is kept rather than refused so a package that subscribes at load time still loads.`,\n})\n\nexport const EVENT_RULES: Readonly<Record<string, SurfaceRule>> = Object.freeze({\n session_start: { level: 'full', detail: 'Mapped to agent/session-start.', design: 'Dispatched from the cordis agent/session-start notification.' },\n session_shutdown: { level: 'full', detail: 'Mapped to agent disposal and plugin teardown with duplicate suppression.', design: 'Dispatched from agent/disposed and from plugin teardown, with duplicate suppression so a package that sees both gets one event.' },\n session_info_changed: { level: 'partial', detail: 'Fired by setSessionName() and projected from DSH session/title events.', design: 'Two sources, one event: the bridge\\'s own setSessionName, and DSH\\'s durable session title event projected into Pi\\'s shape.' },\n agent_start: { level: 'full', detail: 'Mapped to the DSH turn/start boundary.', design: 'The DSH turn/start boundary from the durable session/event stream. DSH\\'s turn is the whole prompt, which is what Pi calls an agent run.' },\n agent_settled: { level: 'full', detail: 'Mapped to the DSH turn/end boundary.', design: 'The DSH turn/end boundary.' },\n turn_start: { level: 'full', detail: 'Fires once per MODEL CALL, as in Pi — DSH calls that a step — with turnIndex counting from zero and resetting at each new prompt.', design: 'The DSH step/start boundary — one step is one model call, which is what Pi calls a turn. The index resets when a new prompt is claimed off the inbox, matching Pi\\'s reset at agent_start.' },\n tool_execution_start: { level: 'full', detail: 'Mapped from durable tool/call events.', design: 'Projected from the durable tool/call event, so a handler sees exactly what was written to the session log.' },\n tool_execution_end: { level: 'full', detail: 'Mapped from finalized tools/result events.', design: 'Dispatched on DSH\\'s tools/post-execute waterfall and awaited there. Riding the durable result emit instead let the handler land after turn_end, which is the opposite of Pi\\'s order; the waterfall is the moment that is guaranteed to run before the caller sees the result.' },\n tool_execution_update: {\n level: 'partial',\n detail: 'Fired from migrated Pi tools\\' own onUpdate callbacks; DSH-native tools expose no partial-result stream.',\n design: 'Fed by migrated Pi tools\\' own onUpdate callbacks. DSH-native tools expose no partial-result stream, so nothing is synthesized for them.',\n },\n tool_call: {\n level: 'partial',\n detail: 'Blocking is supported, in-place argument mutation reaches migrated Pi tools, and `terminate` follows Pi\\'s batch rule — the loop stops after a tool batch only when every call in it was blocked asking to stop. Mutating a DSH-native tool\\'s arguments is rejected because DSH logs arguments before policy.',\n design: 'DSH\\'s tools/pre-execute waterfall, whose decision type carries exactly the two outcomes Pi needs (proceed, deny with a reason). Pi\\'s in-place argument mutation is applied to migrated Pi tools; for a DSH-native tool it is refused, because DSH logs arguments before policy runs and the log would then disagree with what executed. Pi\\'s batch rule for terminate is reimplemented verbatim: the loop stops only when every finalized call in the batch asked to stop.',\n },\n tool_result: {\n level: 'partial',\n detail: 'Text replacement and success-to-error blocking are supported; arbitrary details and error recovery are not.',\n design: 'The same tools/post-execute waterfall, which is where a result can still be rewritten before the caller reads it.',\n },\n before_agent_start: {\n level: 'full',\n detail: 'Fires once per user prompt, inside the assembly of the turn it belongs to, with the real prompt text and image attachments; returned custom messages enter that same turn beside the user message, and a returned systemPrompt overrides that turn\\'s own assembly and resets at the next turn.',\n design: 'DSH\\'s system-prompt/assemble waterfall — an async waterfall that runs while the prompt is being assembled and whose return value is authoritative. That is why a returned systemPrompt reaches the very turn the handler fired for; the later agent/pre-step waterfall would have been one turn too late. Firing is gated on the inbox claim so it happens once per user prompt, and returned custom messages are held and injected into that same step.',\n },\n agent_end: {\n level: 'partial',\n detail: 'The lifecycle boundary is mapped, but the reconstructed Pi message history is intentionally minimal.',\n design: 'The DSH turn/end boundary. Pi\\'s message history on this event is reconstructed minimally rather than replayed, because the durable log is the honest source and packages that need it read the projection.',\n },\n turn_end: {\n level: 'full',\n detail: 'Fires once per MODEL CALL, as in Pi — DSH calls that a step — carrying that call\\'s own assistant message and its own tool results, with turnIndex counting model calls from zero and resetting each prompt.',\n design: 'The DSH step/end boundary, carrying that model call\\'s own assistant message and the tool results belonging to it.',\n },\n message_start: {\n level: 'partial',\n detail: 'Durable user, assistant, and tool-result messages are mapped without Pi-specific provider metadata.',\n design: 'Projected from durable message events in the session/event stream.',\n },\n message_end: {\n level: 'partial',\n detail: 'Durable messages are observed, but message replacement is not supported.',\n design: 'Projected from the same durable message events; DSH\\'s log is append-only, so Pi\\'s message replacement has nowhere to land.',\n },\n message_update: {\n level: 'partial',\n detail: 'Projected from DSH assistant/chunk events with accumulated text; Pi\\'s full AgentMessage accumulation state is approximated.',\n design: 'Projected from DSH assistant/chunk events with text accumulated by the bridge. Pi\\'s full AgentMessage accumulation state is approximated, not reconstructed.',\n },\n session_before_compact: {\n level: 'partial',\n detail: 'Projected from DSH compaction/start as a notification; cancel/replace cannot reach DSH\\'s compactor.',\n design: 'Projected from DSH\\'s compaction/start as a notification. It is an emit, not a waterfall, so a cancel or a replacement has no channel to reach DSH\\'s compactor and is not pretended.',\n },\n session_compact: {\n level: 'partial',\n detail: 'Fires once per SUCCESSFUL compaction, from DSH\\'s summary event, with the summary rendered to the string Pi\\'s CompactionEntry declares. A manual compaction is identified as manual; DSH does not record which automatic trigger fired, so automatic ones report \"threshold\" and willRetry is always false.',\n design: 'Projected from DSH\\'s compaction summary event, rendered to the exact string Pi\\'s CompactionEntry declares. Manual compactions are identifiable; DSH does not record which automatic trigger fired, so automatic ones report Pi\\'s threshold reason.',\n },\n model_select: {\n level: 'partial',\n detail: 'Fired by setModel() and projected from request/header model changes in the durable log.',\n design: 'Fired by the bridge\\'s own setModel, and projected from model changes in the durable request/header record — which is the call configuration DSH logs, not the HTTP body.',\n },\n thinking_level_select: {\n level: 'partial',\n detail: 'Fired by setThinkingLevel(); DSH-side reasoning changes surface through request/header projection.',\n design: 'Fired by the bridge\\'s own setThinkingLevel; host-side reasoning changes arrive through the same request/header projection.',\n },\n context: {\n level: 'partial',\n detail: 'Fires before each step with the full message projection; the transform applies to the step\\'s not-yet-entered messages (the slice packages rewrite), while already-entered history stays read-only under DSH\\'s append-only log.',\n design: 'DSH\\'s agent/pre-step waterfall, whose decision type distinguishes entering a step from rejecting it. The transform applies to the messages that have not entered the step yet — the slice Pi packages actually rewrite — while entered history stays read-only under DSH\\'s append-only log.',\n },\n before_provider_request: {\n level: 'partial',\n detail: 'Fires with the exact outgoing payload for Pi package-owned transports; native DSH adapters expose no body-builder hook and remain unavailable.',\n design: 'A transport-owning Pi provider is wrapped by pi2dsh as a DSH llm adapter and Pi\\'s standard stream helpers expose the exact pre-fetch body through onPayload, so the waterfall is real there. Native DSH adapters build their body behind their own boundary; without an upstream seam this event cannot honestly fire for them.',\n },\n before_provider_headers: {\n level: 'unsupported',\n detail: 'Provider header mutation belongs in a native DSH LLM adapter; the handler is accepted but never fires.',\n design: 'Deliberately not wired, for the same reason as before_provider_request: headers belong to the adapter that owns the transport.',\n },\n after_provider_response: {\n level: 'unsupported',\n detail: 'Provider response interception belongs in a native DSH LLM adapter; the handler is accepted but never fires.',\n design: 'Deliberately not wired: the response is consumed inside the adapter, and interception there is an adapter concern.',\n },\n user_bash: OBSERVED_NEVER_FIRES('Pi\\'s ! command surface'),\n input: OBSERVED_NEVER_FIRES('raw Pi terminal input'),\n project_trust: {\n level: 'unsupported',\n detail: 'Project trust must remain owned by the DSH host; the handler is accepted but never consulted.',\n design: 'Accepted and never consulted. Trust is a host decision in DSH, and letting a package answer it would move the decision to the code being trusted.',\n },\n resources_discover: {\n level: 'unsupported',\n detail: 'Dynamic resource discovery must be converted into DSH providers; the handler is accepted but never fires.',\n design: 'Accepted and never fired. Dynamic resource discovery in DSH is a provider registration, which is a different (and official) seam.',\n },\n session_before_switch: OBSERVED_NEVER_FIRES('Pi session switching'),\n session_before_fork: OBSERVED_NEVER_FIRES('Pi tree forking'),\n session_before_tree: OBSERVED_NEVER_FIRES('Pi session-tree navigation'),\n session_tree: OBSERVED_NEVER_FIRES('Pi session-tree navigation'),\n})\n\nexport function ruleForApi(method: string): Rule | undefined {\n return API_RULES[method]\n}\n\nexport function ruleForEvent(event: string): Rule {\n return EVENT_RULES[event] ?? {\n level: 'unsupported',\n detail: `Unknown Pi event ${JSON.stringify(event)} has no verified DSH mapping.`,\n }\n}\n\nexport function ruleForHostImport(packageName: string, importedName: string): Rule | undefined {\n const family = (PI_CODING_AGENT_PACKAGES as readonly string[]).includes(packageName)\n ? 'pi-coding-agent'\n : (PI_TUI_PACKAGES as readonly string[]).includes(packageName)\n ? 'pi-tui'\n : (PI_AI_PACKAGES as readonly string[]).includes(packageName) ? 'pi-ai' : undefined\n return family === undefined ? undefined : HOST_IMPORT_RULES[family]?.[importedName]\n}\n\nexport function ruleForContextProperty(property: string): Rule | undefined {\n return CONTEXT_RULES[property]\n}\n\nexport function ruleForUiContextProperty(property: string): Rule | undefined {\n return UI_CONTEXT_RULES[property]\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
 
2
- import "./pi-coding-agent-Cvez0jkF.mjs";
2
+ import "./pi-coding-agent-DK8hbJP5.mjs";
3
3
  import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
4
- import { c as builtinProviders } from "./pi-ai-Dh7HUxbt.mjs";
4
+ import { c as builtinProviders } from "./pi-ai-DpqiLdPA.mjs";
5
5
  import { mkdir, rename, writeFile } from "node:fs/promises";
6
6
  import { existsSync, readFileSync } from "node:fs";
7
7
  import { dirname, join } from "node:path";
@@ -538,4 +538,4 @@ function apply(ctx, config = {}) {
538
538
  //#endregion
539
539
  export { FileCredentialStore as a, providerSupportsOAuth as c, oauthCredentialRef as i, resolvePiProviderAuth as l, apply as n, joinSignals as o, name as r, loginPiProvider as s, PiOAuthCredentialProvider as t, storedOAuthCredential as u };
540
540
 
541
- //# sourceMappingURL=credentials-oauth-CAohSLcB.mjs.map
541
+ //# sourceMappingURL=credentials-oauth-Didpkkjx.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"credentials-oauth-CAohSLcB.mjs","names":["#path","#persist"],"sources":["../src/compat/vendor/pi-ai-abort.ts","../src/compat/vendor/pi-ai-credential-store.ts","../src/compat/vendor/pi-ai-diagnostics.ts","../src/compat/vendor/pi-ai-auth-resolve.ts","../src/compat/vendor/pi-oauth-adapt.ts","../src/oauth-bridge.ts","../src/credentials-oauth.ts"],"sourcesContent":["// @ts-nocheck — vendored Pi source (pi-ai utils/abort.js @0.84.1, MIT, see ./PI-LICENSE); unchanged.\nfunction abortReason(signal) {\n if (signal.reason !== undefined)\n return signal.reason;\n const error = new Error(\"The operation was aborted\");\n error.name = \"AbortError\";\n return error;\n}\n/** Create an operation-local signal for public APIs whose signal is optional. */\nexport function operationSignal(signal) {\n return signal ?? new AbortController().signal;\n}\n/**\n * Stop waiting for an operation when its signal aborts while continuing to\n * observe the abandoned promise so a later rejection is always handled.\n */\nexport function raceWithAbortSignal(operation, signal) {\n if (signal.aborted) {\n void operation.catch(() => { });\n return Promise.reject(abortReason(signal));\n }\n return new Promise((resolve, reject) => {\n let settled = false;\n const cleanup = () => signal.removeEventListener(\"abort\", onAbort);\n const onAbort = () => {\n if (settled)\n return;\n settled = true;\n cleanup();\n reject(abortReason(signal));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n void operation.then((value) => {\n if (settled)\n return;\n settled = true;\n cleanup();\n resolve(value);\n }, (error) => {\n if (settled)\n return;\n settled = true;\n cleanup();\n reject(error);\n });\n if (signal.aborted)\n onAbort();\n });\n}\n//# sourceMappingURL=abort.js.map","// @ts-nocheck — vendored Pi source (pi-ai auth/credential-store.js @0.84.1, MIT, see ./PI-LICENSE); import remapped, logic unchanged.\nimport { operationSignal, raceWithAbortSignal } from './pi-ai-abort.js';\n/**\n * Default in-memory credential store. Apps inject persistent stores.\n * Keyed by `Provider.id`, one credential per provider; see `CredentialStore`.\n * Writes are serialized per provider through a promise chain.\n */\nexport class InMemoryCredentialStore {\n credentials = new Map();\n chains = new Map();\n /** Serialize tasks per provider id without releasing the chain before active work settles. */\n enqueue(providerId, task, options) {\n const signal = operationSignal(options?.signal);\n const previous = this.chains.get(providerId) ?? Promise.resolve();\n const queued = (async () => {\n await previous.catch(() => { });\n signal.throwIfAborted();\n return task();\n })();\n const tail = queued.catch(() => { });\n this.chains.set(providerId, tail);\n void tail.then(() => {\n if (this.chains.get(providerId) === tail)\n this.chains.delete(providerId);\n });\n return raceWithAbortSignal(queued, signal);\n }\n async read(providerId, options) {\n options?.signal?.throwIfAborted();\n return this.credentials.get(providerId);\n }\n async list(options) {\n options?.signal?.throwIfAborted();\n return [...this.credentials].map(([providerId, credential]) => ({ providerId, type: credential.type }));\n }\n modify(providerId, fn, options) {\n return this.enqueue(providerId, async () => {\n const current = this.credentials.get(providerId);\n const next = await fn(current);\n options?.signal?.throwIfAborted();\n if (next !== undefined)\n this.credentials.set(providerId, next);\n return next ?? current;\n }, options);\n }\n delete(providerId, options) {\n return this.enqueue(providerId, async () => {\n this.credentials.delete(providerId);\n }, options);\n }\n}\n//# sourceMappingURL=credential-store.js.map","// @ts-nocheck — vendored Pi source (pi-ai utils/diagnostics.js @0.84.1, MIT, see ./PI-LICENSE); unchanged.\nexport function formatThrownValue(value) {\n if (value instanceof Error)\n return value.message || value.name;\n if (typeof value === \"string\")\n return value;\n return String(value);\n}\nexport function extractDiagnosticError(error) {\n if (!(error instanceof Error))\n return { name: \"ThrownValue\", message: formatThrownValue(error) };\n const code = error.code;\n return {\n name: error.name || undefined,\n message: error.message || error.name,\n stack: error.stack,\n code: typeof code === \"string\" || typeof code === \"number\" ? code : undefined,\n };\n}\nexport function createAssistantMessageDiagnostic(type, error, details) {\n return { type, timestamp: Date.now(), error: extractDiagnosticError(error), details };\n}\nexport function appendAssistantMessageDiagnostic(message, diagnostic) {\n message.diagnostics = [...(message.diagnostics ?? []), diagnostic];\n}\n//# sourceMappingURL=diagnostics.js.map","// @ts-nocheck — vendored Pi source (pi-ai auth/resolve.js @0.84.1, MIT, see ./PI-LICENSE); imports remapped, logic unchanged\n// (double-checked-lock OAuth refresh with persistence, no silent env fallback).\nimport { operationSignal, raceWithAbortSignal } from './pi-ai-abort.js';\nimport { formatThrownValue } from './pi-ai-diagnostics.js';\nexport class ModelsError extends Error {\n code;\n constructor(code, message, options) {\n super(withCauseDetail(message, options?.cause), options);\n this.name = \"ModelsError\";\n this.code = code;\n }\n}\n/** Callers surface `error.message` only, so keep the underlying reason in it. */\nfunction withCauseDetail(message, cause) {\n if (cause === undefined || cause === null)\n return message;\n const detail = formatThrownValue(cause).trim();\n if (!detail || message.includes(detail))\n return message;\n return `${message}: ${detail}`;\n}\n/**\n * Auth resolution shared by the `Models` and `ImagesModels` collections.\n * A stored credential owns the provider: ambient/env is consulted only when\n * nothing is stored. No silent env fallback after a failed refresh or for a\n * credential type without a matching handler.\n */\nexport function resolveProviderAuth(provider, credentials, authContext, overrides) {\n const signal = operationSignal(overrides?.signal);\n return raceWithAbortSignal(resolveProviderAuthWithSignal(provider, credentials, authContext, overrides, signal), signal);\n}\nasync function resolveProviderAuthWithSignal(provider, credentials, authContext, overrides, signal) {\n signal.throwIfAborted();\n const requestAuthContext = overrides?.env ? overlayEnvAuthContext(authContext, overrides.env) : authContext;\n if (overrides?.apiKey !== undefined && provider.auth.apiKey) {\n return resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, {\n type: \"api_key\",\n key: overrides.apiKey,\n env: overrides.env,\n }, signal);\n }\n const stored = await readCredential(credentials, provider.id, signal);\n if (stored) {\n if (stored.type === \"oauth\" && provider.auth.oauth) {\n return resolveStoredOAuth(credentials, provider.id, provider.auth.oauth, stored, signal, overrides?.minOAuthValidityMs);\n }\n if (stored.type === \"api_key\" && provider.auth.apiKey) {\n const credential = overrides?.env ? { ...stored, env: { ...stored.env, ...overrides.env } } : stored;\n return resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, credential, signal);\n }\n return undefined;\n }\n // Ambient (env vars, AWS profiles, ADC files).\n return provider.auth.apiKey\n ? resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, undefined, signal)\n : undefined;\n}\nfunction overlayEnvAuthContext(base, env) {\n return {\n env: async (name) => env[name] || (await base.env(name)),\n fileExists: (path) => base.fileExists(path),\n };\n}\nconst DEFAULT_OAUTH_MINIMUM_VALIDITY_MS = 5 * 60 * 1000;\nconst DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 15_000;\n/**\n * OAuth resolution with double-checked locking: tokens with less than five\n * minutes remaining lock, re-check expiry under the lock, refresh once\n * globally, and persist the rotated credential before release.\n */\nasync function resolveStoredOAuth(credentials, providerId, oauth, stored, signal, minOAuthValidityMs) {\n const minimumValidityMs = Math.max(DEFAULT_OAUTH_MINIMUM_VALIDITY_MS, minOAuthValidityMs ?? 0);\n const expiresSoon = (credential) => Date.now() + minimumValidityMs >= credential.expires;\n let credential = stored;\n if (expiresSoon(credential)) {\n // Optimistic check said expired; the authoritative check runs under the lock.\n let post;\n try {\n post = await credentials.modify(providerId, async (current) => {\n if (current?.type !== \"oauth\")\n return undefined; // logged out meanwhile\n if (!expiresSoon(current))\n return undefined; // another process/request refreshed\n try {\n const refreshSignal = AbortSignal.any([\n signal,\n AbortSignal.timeout(DEFAULT_OAUTH_REFRESH_TIMEOUT_MS),\n ]);\n return await oauth.refresh(current, refreshSignal);\n }\n catch (error) {\n throw new ModelsError(\"oauth\", `OAuth refresh failed for ${providerId}`, { cause: error });\n }\n }, { signal });\n }\n catch (error) {\n if (error instanceof ModelsError)\n throw error;\n throw new ModelsError(\"auth\", `Credential store modify failed for ${providerId}`, { cause: error });\n }\n if (post?.type !== \"oauth\")\n return undefined; // logged out meanwhile\n credential = post;\n // The normal five-minute window triggers a refresh but does not impose a\n // provider contract. Explicit callers (such as bearer-token export) do\n // require the requested minimum after the refresh.\n if (minOAuthValidityMs !== undefined && expiresSoon(credential)) {\n throw new ModelsError(\"oauth\", `OAuth refresh returned a token that expires too soon for ${providerId}`);\n }\n }\n try {\n return { auth: await oauth.toAuth(credential), source: \"OAuth\" };\n }\n catch (error) {\n throw new ModelsError(\"oauth\", `OAuth auth derivation failed for ${providerId}`, { cause: error });\n }\n}\nasync function resolveApiKey(authContext, apiKey, providerId, credential, signal) {\n try {\n return await apiKey.resolve({ ctx: authContext, credential, signal });\n }\n catch (error) {\n throw new ModelsError(\"auth\", `API key auth failed for provider ${providerId}`, { cause: error });\n }\n}\nasync function readCredential(credentials, providerId, signal) {\n try {\n return await credentials.read(providerId, { signal });\n }\n catch (error) {\n throw new ModelsError(\"auth\", `Credential store read failed for ${providerId}`, { cause: error });\n }\n}\n//# sourceMappingURL=resolve.js.map","// @ts-nocheck — vendored Pi source excerpt (pi-coding-agent core/provider-composer.js adaptOAuth @0.84.1, MIT, see ./PI-LICENSE); logic unchanged.\n// The official two-generation adapter: extension oauth callbacks (onAuth/onDeviceCode/onPrompt/onSelect)\n// over the pi-ai interaction surface (prompt/notify/signal), plus refresh and toAuth->getApiKey.\nexport function adaptOAuth(config) {\n return {\n name: config.name,\n isSubscription: config.isSubscription,\n login: async (callbacks) => {\n const credential = await config.login({\n onAuth: (info) => callbacks.notify({ type: \"auth_url\", ...info }),\n onDeviceCode: (info) => callbacks.notify({ type: \"device_code\", ...info }),\n onPrompt: (prompt) => callbacks.prompt({ type: \"text\", ...prompt }),\n onProgress: (message) => callbacks.notify({ type: \"progress\", message }),\n onManualCodeInput: () => callbacks.prompt({ type: \"manual_code\", message: \"Paste the authorization code\" }),\n onSelect: (prompt) => callbacks.prompt({ type: \"select\", ...prompt }),\n signal: callbacks.signal,\n });\n return { ...credential, type: \"oauth\" };\n },\n refresh: async (credential, signal) => ({ ...(await config.refreshToken(credential, signal)), type: \"oauth\" }),\n toAuth: async (credential) => ({ apiKey: config.getApiKey(credential) }),\n };\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { mkdir, rename, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport { InMemoryCredentialStore } from './compat/vendor/pi-ai-credential-store.js'\nimport { resolveProviderAuth } from './compat/vendor/pi-ai-auth-resolve.js'\nimport { adaptOAuth } from './compat/vendor/pi-oauth-adapt.js'\n\ntype UnknownRecord = Record<string, unknown>\n\n// The interactive OAuth host seam, four layers with one owner each:\n// L1 protocol — the package's own oauth.login/refreshToken/getApiKey code\n// runs verbatim (device-code, auth-code, rotation). The\n// bridge implements zero protocol.\n// L2 interaction— Pi's official adaptOAuth (vendored) turns the package's\n// callbacks into prompt/notify events; this file routes\n// those onto the Pi ui surface, which pi2dsh already maps\n// to DSH userQuestions with real waits.\n// L3 credentials— Pi's auth.json contract: one file keyed by provider id,\n// 0600, atomic replace, per-provider serialized writes\n// (the vendored InMemoryCredentialStore chain), refresh via\n// Pi's vendored double-checked-lock resolver.\n// L4 routing — resolveOAuthApiKey yields the request key for dsh-llm's\n// one-shot credential seam; tokens never enter DSH settings.\n\nexport class FileCredentialStore extends InMemoryCredentialStore {\n readonly #path: string\n\n constructor(path: string) {\n super()\n this.#path = path\n try {\n const data = JSON.parse(readFileSync(path, 'utf8')) as Record<string, UnknownRecord>\n for (const [providerId, credential] of Object.entries(data)) {\n ;(this as unknown as { credentials: Map<string, unknown> }).credentials.set(providerId, credential)\n }\n } catch {\n // Missing or unreadable auth.json starts empty, exactly like Pi.\n }\n }\n\n get path(): string {\n return this.#path\n }\n\n async #persist(): Promise<void> {\n const credentials = (this as unknown as { credentials: Map<string, unknown> }).credentials\n const data = Object.fromEntries(credentials)\n await mkdir(dirname(this.#path), { recursive: true })\n // Atomic replace so a crash mid-write never truncates the store; 0600 so\n // tokens stay owner-readable only. Same contract Pi's file store keeps.\n const temp = `${this.#path}.tmp-${process.pid}-${Math.floor(Math.random() * 1e9).toString(36)}`\n await writeFile(temp, `${JSON.stringify(data, null, 2)}\\n`, { mode: 0o600 })\n await rename(temp, this.#path)\n }\n\n override modify(providerId: string, fn: (current: unknown) => unknown, options?: UnknownRecord): Promise<unknown> {\n // The vendored per-provider chain already serializes writers; persisting\n // inside the chained task keeps disk order identical to memory order.\n return super.modify(providerId, async (current: unknown) => {\n const next = await fn(current)\n if (next !== undefined) {\n const credentials = (this as unknown as { credentials: Map<string, unknown> }).credentials\n credentials.set(providerId, next)\n await this.#persist()\n }\n return next\n }, options)\n }\n\n override delete(providerId: string, options?: UnknownRecord): Promise<void> {\n return super.delete(providerId, options).then(() => this.#persist()) as Promise<void>\n }\n}\n\n/**\n * One signal that fires when EITHER input does.\n *\n * A question on screen has two reasons to close: the flow's own race (the\n * browser callback beating the paste box) and the whole login being called off.\n * @param ambient - the login's signal, live for the whole flow.\n * @param perCall - the flow's signal for this one question, when it sent one.\n * @returns the joined signal, or whichever one exists.\n */\nexport function joinSignals(ambient: AbortSignal | undefined, perCall: unknown): AbortSignal | undefined {\n if (!(perCall instanceof AbortSignal)) return ambient\n if (ambient === undefined) return perCall\n const joined = new AbortController()\n const stop = () => joined.abort()\n if (ambient.aborted || perCall.aborted) joined.abort()\n else {\n ambient.addEventListener('abort', stop, { once: true })\n perCall.addEventListener('abort', stop, { once: true })\n }\n return joined.signal\n}\n\nexport interface OAuthUiSurface {\n /**\n * @param signal cancels THIS question when another path in the flow answers\n * first — the browser callback beating the paste box. Optional because\n * Pi's own ui.input takes two arguments; the bridge's accepts a third.\n */\n input(title: unknown, placeholder?: unknown, signal?: AbortSignal): Promise<string | undefined>\n select(title: unknown, options: unknown[], signal?: AbortSignal): Promise<string | undefined>\n notify(message: unknown): void\n /**\n * Keep a device-code flow visible while the package polls in the background.\n *\n * Unlike auth-code flows, device-code flows normally do not issue a later\n * prompt. A command notice therefore arrives only after the code has expired.\n * Hosts with a live question surface should implement this optional seam;\n * older/headless hosts still receive the plain notification below.\n */\n deviceCode?(title: unknown, detail: unknown, signal?: AbortSignal): Promise<void>\n}\n\ninterface OAuthPromptEvent {\n type: 'text' | 'manual_code' | 'select'\n message: string\n placeholder?: string\n options?: Array<{ id: string, label: string }>\n /** Cancels THIS question when another path in the flow answers first. */\n signal?: AbortSignal\n}\n\ninterface OAuthNotifyEvent {\n type: 'auth_url' | 'device_code' | 'progress'\n url?: string\n instructions?: string\n userCode?: string\n verificationUri?: string\n message?: string\n /** How long the device code stays valid. Every device-code flow sends it. */\n expiresInSeconds?: number\n /** How often the flow polls; sent alongside the expiry. */\n intervalSeconds?: number\n}\n\n// The pi-ai interaction surface ({prompt, notify, signal}) over the Pi ui\n// surface pi2dsh already maps to DSH userQuestions.\n/**\n * Hand a URL to the host's browser opener, when the host gave one.\n *\n * Opening a window is the HOST's action, not the translation layer's, so the\n * opener is injected rather than reached for. Wiring it in here meant every\n * caller opened a real browser tab — including the test suite, which spawned a\n * tab per assertion on the developer's own machine.\n * @param open - the host's opener, absent when the host does not open windows.\n * @param url - the address the flow announced, when it announced one.\n */\nfunction openIfPossible(open: ((url: string) => void) | undefined, url: string | undefined): void {\n if (open === undefined || typeof url !== 'string' || !/^https?:\\/\\//u.test(url)) return\n try {\n open(url)\n } catch {\n // Best effort by design: the URL is in the prompt either way, and a host\n // with no desktop session (a container, a remote box) must still be able to\n // finish the login by pasting the code.\n }\n}\n\nexport function oauthInteraction(\n ui: OAuthUiSurface,\n signal?: AbortSignal,\n shorten?: (url: string) => string | undefined,\n open?: (url: string) => void,\n cancel?: () => void,\n): {\n prompt(prompt: OAuthPromptEvent): Promise<string | undefined>\n notify(event: OAuthNotifyEvent): void\n signal: AbortSignal\n} {\n // Pi hosts always hand flows a live signal; flows attach abort listeners\n // unconditionally, so an absent caller signal becomes a never-aborting one.\n const effectiveSignal = signal ?? new AbortController().signal\n // Where the user has to GO to authorize, held until the next prompt.\n //\n // The flow announces the authorization URL through notify() and then blocks\n // on prompt() for the code. A notice is a command notice on DSH: it reaches\n // the user when the command ENDS — and this command cannot end until the user\n // acts on the notice. So the address was delivered after it was needed, and\n // the dialog on screen showed only the redirect URI (localhost/auth/callback),\n // which does nothing when clicked. Carrying it into the prompt puts it where\n // the user is already looking.\n let pending: string | undefined\n /**\n * Hand the announced address to the prompt as its DETAIL, not its title.\n *\n * An authorization URL is 400 characters of query string. Prepending it to\n * the question turned the dialog into a wall of wrapped text with the actual\n * question buried underneath; `detail` is the slot DSH renders supporting\n * text in.\n * @param placeholder - the flow's own placeholder, kept when it supplied one.\n */\n const takePending = (placeholder: unknown): string | undefined => {\n const carried = pending\n pending = undefined\n // A carried address takes the slot alone. Pi's placeholder is greyed hint\n // text INSIDE its input box; DSH's dialog has no such slot, so it lands in\n // the same body text — and codex's placeholder is the redirect URI\n // (localhost:1455/auth/callback), which GFM autolinks into a second,\n // clickable, useless address sitting right under the real one. That is the\n // trap the user already fell into. Where nothing was announced it is still\n // the only hint there is, so it stays.\n if (carried !== undefined) return carried\n return placeholder === undefined ? undefined : String(placeholder)\n }\n return {\n signal: effectiveSignal,\n async prompt(prompt) {\n if (prompt.type === 'select') {\n const options = prompt.options ?? []\n pending = undefined\n const picked = await ui.select(prompt.message, options.map(option => option.label), effectiveSignal)\n return options.find(option => option.label === picked)?.id ?? picked\n }\n // BOTH signals close this box: the flow's own (another path won the race\n // — the local callback got the code) and the login's (it was called off).\n //\n // The second one is not optional. Cancelling a flow only tells the flow;\n // the question is the HOST's, and Pi's host takes its dialog down, which\n // is how the flow's `await manualPromise` gets to settle. Pi's anthropic\n // flow deadlocks without that: cancelling makes its callback wait return\n // nothing, and it then waits on the paste box forever, because the abort\n // that would close the box sits in a `finally` this wait never reaches.\n return ui.input(prompt.message, takePending(prompt.placeholder), joinSignals(effectiveSignal, prompt.signal))\n },\n notify(event) {\n if (event.type === 'auth_url') {\n // An authorize URL is 400 characters of query string. Pasted whole it\n // filled the dialog with wrapped text and gave the user nothing to\n // click. The dialog's detail slot is rendered as markdown, so the\n // address goes in as a LINK — one short clickable line, opened in a new\n // tab by the host's own renderer. The short redirect on this app's\n // origin is what makes it copyable too (a notice is plain text).\n const shown = (event.url === undefined ? undefined : shorten?.(event.url)) ?? event.url\n const tail = event.instructions === undefined ? '' : `\\n\\n${event.instructions}`\n pending = `[Open the login page](${shown})${tail}`\n ui.notify(`Open this URL to authorize: ${shown}${tail}`)\n // Open it, the way Pi's own login dialog does. The flows themselves\n // print \"A browser window should open\" — that sentence is Pi telling\n // the user what the HOST is about to do, and a host that does not do it\n // leaves a wrapped, unclickable URL and no way to authorize.\n openIfPossible(open, event.url)\n } else if (event.type === 'device_code') {\n // A device code EXPIRES. Dropping that left the user staring at a code\n // with no idea it was on a clock — every device-code flow sends the\n // window, and Pi's own host shows it.\n const window = typeof event.expiresInSeconds === 'number' && event.expiresInSeconds > 0\n ? ` (valid for ${Math.round(event.expiresInSeconds / 60)} min)`\n : ''\n // Same as above: a clickable line in the dialog, plain text in the\n // notice. The code stays as code so it survives the markdown pass.\n const detail = `[Open the device login page](${event.verificationUri}) and enter code \\`${event.userCode}\\`${window}`\n pending = detail\n ui.notify(`Visit ${event.verificationUri} and enter code ${event.userCode}${window}`)\n // A device flow polls without asking a follow-up question. Put a live\n // host dialog on screen NOW; otherwise the only visible artifact is a\n // command notice released after the command ends, when the code is no\n // longer useful. Resolving this dialog means the user cancelled it.\n // Successful/failed login aborts the signal first and merely closes it.\n if (ui.deviceCode !== undefined) {\n pending = undefined\n void ui.deviceCode('Complete login in your browser', detail, effectiveSignal).then(\n () => { if (!effectiveSignal.aborted) cancel?.() },\n () => undefined,\n )\n }\n openIfPossible(open, event.verificationUri)\n } else if (event.message !== undefined) {\n ui.notify(event.message)\n }\n },\n }\n}\n\nfunction oauthConfigOf(providerConfig: UnknownRecord | undefined): UnknownRecord | undefined {\n // Extension-generation configs carry oauth at the top level; pi-ai\n // createProvider() objects carry it under auth.oauth. Pi accepts both.\n const oauth = providerConfig?.oauth\n ?? (providerConfig?.auth as UnknownRecord | undefined)?.oauth\n return typeof oauth === 'object' && oauth !== null && typeof (oauth as UnknownRecord).login === 'function'\n ? oauth as UnknownRecord\n : undefined\n}\n\n// Pi accepts both oauth generations: pi-ai native flows carry toAuth and take\n// the interaction surface directly; extension configs carry getApiKey and go\n// through Pi's adaptOAuth (vendored). Same acceptance here.\nfunction oauthAdapterOf(oauthConfig: UnknownRecord): {\n login(interaction: unknown): Promise<UnknownRecord>\n refresh(credential: unknown, signal?: AbortSignal): Promise<UnknownRecord>\n toAuth(credential: unknown): Promise<UnknownRecord>\n} {\n if (typeof oauthConfig.toAuth === 'function') {\n return oauthConfig as never\n }\n return adaptOAuth(oauthConfig) as never\n}\n\nexport function providerSupportsOAuth(providerConfig: UnknownRecord | undefined): boolean {\n return oauthConfigOf(providerConfig) !== undefined\n}\n\n// Pi models.login semantics: find the provider's oauth surface, run the\n// package's own login through the vendored adapter, persist through the\n// store's serialized modify.\nexport async function loginPiProvider(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n ui: OAuthUiSurface\n signal?: AbortSignal\n /** Turns a long authorize URL into a short one the user can actually click. */\n shorten?: (url: string) => string | undefined\n /** The host's browser opener. Absent = this host opens nothing. */\n open?: (url: string) => void\n}): Promise<UnknownRecord> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n if (oauthConfig === undefined) {\n throw new Error(`${options.providerName ?? options.providerId} does not support oauth login`)\n }\n const adapter = oauthAdapterOf(oauthConfig)\n // The flow's own signal, so a question still on screen when the login ends\n // gets cancelled. The browser half of an OAuth login finishes at the local\n // callback, not at the paste box — leaving that box open after the flow has\n // resolved (or failed, as a region-blocked token exchange does) strands the\n // user in front of a dialog that can no longer do anything.\n const done = new AbortController()\n if (options.signal !== undefined) {\n if (options.signal.aborted) done.abort()\n else options.signal.addEventListener('abort', () => done.abort(), { once: true })\n }\n try {\n const credential = await adapter.login(oauthInteraction(\n options.ui,\n done.signal,\n options.shorten,\n options.open,\n () => done.abort(),\n ))\n await options.store.modify(options.providerId, async () => credential)\n return credential\n } finally {\n done.abort()\n }\n}\n\n// Pi's resolveProviderAuth over the stored credential: five-minute expiry\n// window, double-checked-lock refresh through the package's own refreshToken,\n// rotated credential persisted before release, key derived by the package's\n// own getApiKey. Returns undefined when nothing is stored.\nexport async function resolveOAuthApiKey(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n signal?: AbortSignal\n}): Promise<string | undefined> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n if (oauthConfig === undefined) return undefined\n const provider = {\n id: options.providerId,\n name: options.providerName ?? options.providerId,\n auth: { oauth: oauthAdapterOf(oauthConfig) },\n }\n const resolved = await resolveProviderAuth(\n provider,\n options.store,\n { env: async () => undefined },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n ) as { auth?: { apiKey?: string } } | undefined\n return resolved?.auth?.apiKey\n}\n\nexport async function storedOAuthCredential(store: FileCredentialStore, providerId: string): Promise<UnknownRecord | undefined> {\n const stored = await store.read(providerId, undefined) as UnknownRecord | undefined\n return stored?.type === 'oauth' ? stored : undefined\n}\n\n// Pi's FULL getProviderAuth precedence over the vendored resolver — stored\n// OAuth (double-checked-lock refresh), then stored api_key, then the\n// provider's own auth.apiKey ambient resolution (its resolve() reads env vars\n// and probes credential files through the auth context). The package's\n// resolve code runs verbatim; the bridge supplies only the context.\nexport async function resolvePiProviderAuth(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n signal?: AbortSignal\n}): Promise<{ auth?: UnknownRecord, source?: string } | undefined> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n const declaredApiKey = (options.providerConfig.auth as UnknownRecord | undefined)?.apiKey\n if (oauthConfig === undefined && declaredApiKey === undefined) return undefined\n const auth: UnknownRecord = {}\n if (oauthConfig !== undefined) auth.oauth = oauthAdapterOf(oauthConfig)\n if (declaredApiKey !== undefined) auth.apiKey = declaredApiKey\n const provider = { id: options.providerId, name: options.providerName ?? options.providerId, auth }\n return await resolveProviderAuth(\n provider,\n options.store,\n {\n env: async (name: string) => process.env[name],\n fileExists: async (path: string) => existsSync(path),\n },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n ) as { auth?: UnknownRecord, source?: string } | undefined\n}\n","import { join } from 'node:path'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { CredentialProvider, type CredentialInfo, type CredentialRef, type ResolvedCredential } from '@deepseek-ai/dsh-credentials'\nimport { FileCredentialStore, providerSupportsOAuth, resolveOAuthApiKey, storedOAuthCredential } from './oauth-bridge.js'\nimport { builtinProviders } from './compat/pi-ai.js'\nimport { getAgentDir } from './compat/pi-coding-agent.js'\n\ntype UnknownRecord = Record<string, unknown>\n\n// L4 of the OAuth host seam: a standard dsh-credentials provider that serves\n// Pi OAuth tokens to DSH's native LLM request path. A route configured as\n// `apiKeyEnv: PI2DSH_OAUTH_OPENAI_CODEX` resolves per request through this\n// provider — Pi's double-checked-lock refresh runs on every resolution, so a\n// rotated token reaches the next model call with no restart, exactly the\n// per-operation semantics the credentials seam demands. Every other reference\n// falls through to the process environment, so one provider instance serves a\n// whole composition.\n//\n// Reference convention: PI2DSH_OAUTH_<PROVIDER_ID> where the provider id is\n// upper-cased with `-` as `_` (openai-codex → PI2DSH_OAUTH_OPENAI_CODEX).\n\nconst OAUTH_REF_PREFIX = 'PI2DSH_OAUTH_'\n\nexport function oauthCredentialRef(providerId: string): string {\n return `${OAUTH_REF_PREFIX}${providerId.toUpperCase().replaceAll('-', '_')}`\n}\n\nfunction providerIdOfRef(ref: string): string | undefined {\n if (!ref.startsWith(OAUTH_REF_PREFIX)) return undefined\n return ref.slice(OAUTH_REF_PREFIX.length).toLowerCase().replaceAll('_', '-')\n}\n\nexport interface PiOAuthCredentialProviderOptions {\n /** Path to the Pi-format auth.json; defaults to `$agentDir/auth.json`. */\n authPath?: string\n /** Additional provider configs (id → config with an oauth block), e.g. from packages that registered providers. */\n providers?: ReadonlyMap<string, UnknownRecord>\n}\n\nexport class PiOAuthCredentialProvider extends CredentialProvider {\n private readonly oauthStore: FileCredentialStore\n private readonly extraProviders: ReadonlyMap<string, UnknownRecord>\n\n constructor(ctx: Context, options: PiOAuthCredentialProviderOptions = {}) {\n super(ctx)\n this.oauthStore = new FileCredentialStore(options.authPath ?? join(getAgentDir(), 'auth.json'))\n this.extraProviders = options.providers ?? new Map()\n }\n\n private oauthConfigFor(providerId: string): UnknownRecord | undefined {\n const registered = this.extraProviders.get(providerId)\n if (providerSupportsOAuth(registered)) return registered\n const builtin = builtinProviders().find(provider => provider.id === providerId)\n if (builtin === undefined) return undefined\n return { name: builtin.name, baseUrl: builtin.baseUrl, oauth: builtin.auth.oauth }\n }\n\n async resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n const value = process.env[String(ref)]\n return value !== undefined && value.length > 0 ? { value, source: 'env' } : undefined\n }\n const config = this.oauthConfigFor(providerId)\n if (config === undefined) return undefined\n const value = await resolveOAuthApiKey({\n providerId,\n providerName: typeof config.name === 'string' ? config.name : providerId,\n providerConfig: config,\n store: this.oauthStore,\n })\n return value !== undefined && value.length > 0 ? { value, source: 'pi-oauth' } : undefined\n }\n\n async describe(ref: CredentialRef): Promise<CredentialInfo> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n const value = process.env[String(ref)]\n return { configured: value !== undefined && value.length > 0, ...(value ? { source: 'env' } : {}), writable: false }\n }\n const stored = await storedOAuthCredential(this.oauthStore, providerId)\n return { configured: stored !== undefined, ...(stored !== undefined ? { source: 'pi-oauth' } : {}), writable: false }\n }\n\n async set(ref: CredentialRef, _value: string): Promise<void> {\n throw new Error(`credential ${String(ref)} is read-only here: OAuth tokens are managed by /login, environment values by the shell`)\n }\n\n async unset(ref: CredentialRef): Promise<void> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n throw new Error(`credential ${String(ref)} is read-only here: environment values are managed by the shell`)\n }\n // Logging out is a legitimate unset: drop the stored token.\n await this.oauthStore.delete(providerId, undefined)\n }\n}\n\nexport const name = 'pi2dsh-credentials-oauth'\n\nexport function apply(ctx: Context, config: PiOAuthCredentialProviderOptions = {}): void {\n // The Service constructor registers itself as ctx.credentials.\n void new PiOAuthCredentialProvider(ctx, config)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"credentials-oauth-Didpkkjx.mjs","names":["#path","#persist"],"sources":["../src/compat/vendor/pi-ai-abort.ts","../src/compat/vendor/pi-ai-credential-store.ts","../src/compat/vendor/pi-ai-diagnostics.ts","../src/compat/vendor/pi-ai-auth-resolve.ts","../src/compat/vendor/pi-oauth-adapt.ts","../src/oauth-bridge.ts","../src/credentials-oauth.ts"],"sourcesContent":["// @ts-nocheck — vendored Pi source (pi-ai utils/abort.js @0.84.1, MIT, see ./PI-LICENSE); unchanged.\nfunction abortReason(signal) {\n if (signal.reason !== undefined)\n return signal.reason;\n const error = new Error(\"The operation was aborted\");\n error.name = \"AbortError\";\n return error;\n}\n/** Create an operation-local signal for public APIs whose signal is optional. */\nexport function operationSignal(signal) {\n return signal ?? new AbortController().signal;\n}\n/**\n * Stop waiting for an operation when its signal aborts while continuing to\n * observe the abandoned promise so a later rejection is always handled.\n */\nexport function raceWithAbortSignal(operation, signal) {\n if (signal.aborted) {\n void operation.catch(() => { });\n return Promise.reject(abortReason(signal));\n }\n return new Promise((resolve, reject) => {\n let settled = false;\n const cleanup = () => signal.removeEventListener(\"abort\", onAbort);\n const onAbort = () => {\n if (settled)\n return;\n settled = true;\n cleanup();\n reject(abortReason(signal));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n void operation.then((value) => {\n if (settled)\n return;\n settled = true;\n cleanup();\n resolve(value);\n }, (error) => {\n if (settled)\n return;\n settled = true;\n cleanup();\n reject(error);\n });\n if (signal.aborted)\n onAbort();\n });\n}\n//# sourceMappingURL=abort.js.map","// @ts-nocheck — vendored Pi source (pi-ai auth/credential-store.js @0.84.1, MIT, see ./PI-LICENSE); import remapped, logic unchanged.\nimport { operationSignal, raceWithAbortSignal } from './pi-ai-abort.js';\n/**\n * Default in-memory credential store. Apps inject persistent stores.\n * Keyed by `Provider.id`, one credential per provider; see `CredentialStore`.\n * Writes are serialized per provider through a promise chain.\n */\nexport class InMemoryCredentialStore {\n credentials = new Map();\n chains = new Map();\n /** Serialize tasks per provider id without releasing the chain before active work settles. */\n enqueue(providerId, task, options) {\n const signal = operationSignal(options?.signal);\n const previous = this.chains.get(providerId) ?? Promise.resolve();\n const queued = (async () => {\n await previous.catch(() => { });\n signal.throwIfAborted();\n return task();\n })();\n const tail = queued.catch(() => { });\n this.chains.set(providerId, tail);\n void tail.then(() => {\n if (this.chains.get(providerId) === tail)\n this.chains.delete(providerId);\n });\n return raceWithAbortSignal(queued, signal);\n }\n async read(providerId, options) {\n options?.signal?.throwIfAborted();\n return this.credentials.get(providerId);\n }\n async list(options) {\n options?.signal?.throwIfAborted();\n return [...this.credentials].map(([providerId, credential]) => ({ providerId, type: credential.type }));\n }\n modify(providerId, fn, options) {\n return this.enqueue(providerId, async () => {\n const current = this.credentials.get(providerId);\n const next = await fn(current);\n options?.signal?.throwIfAborted();\n if (next !== undefined)\n this.credentials.set(providerId, next);\n return next ?? current;\n }, options);\n }\n delete(providerId, options) {\n return this.enqueue(providerId, async () => {\n this.credentials.delete(providerId);\n }, options);\n }\n}\n//# sourceMappingURL=credential-store.js.map","// @ts-nocheck — vendored Pi source (pi-ai utils/diagnostics.js @0.84.1, MIT, see ./PI-LICENSE); unchanged.\nexport function formatThrownValue(value) {\n if (value instanceof Error)\n return value.message || value.name;\n if (typeof value === \"string\")\n return value;\n return String(value);\n}\nexport function extractDiagnosticError(error) {\n if (!(error instanceof Error))\n return { name: \"ThrownValue\", message: formatThrownValue(error) };\n const code = error.code;\n return {\n name: error.name || undefined,\n message: error.message || error.name,\n stack: error.stack,\n code: typeof code === \"string\" || typeof code === \"number\" ? code : undefined,\n };\n}\nexport function createAssistantMessageDiagnostic(type, error, details) {\n return { type, timestamp: Date.now(), error: extractDiagnosticError(error), details };\n}\nexport function appendAssistantMessageDiagnostic(message, diagnostic) {\n message.diagnostics = [...(message.diagnostics ?? []), diagnostic];\n}\n//# sourceMappingURL=diagnostics.js.map","// @ts-nocheck — vendored Pi source (pi-ai auth/resolve.js @0.84.1, MIT, see ./PI-LICENSE); imports remapped, logic unchanged\n// (double-checked-lock OAuth refresh with persistence, no silent env fallback).\nimport { operationSignal, raceWithAbortSignal } from './pi-ai-abort.js';\nimport { formatThrownValue } from './pi-ai-diagnostics.js';\nexport class ModelsError extends Error {\n code;\n constructor(code, message, options) {\n super(withCauseDetail(message, options?.cause), options);\n this.name = \"ModelsError\";\n this.code = code;\n }\n}\n/** Callers surface `error.message` only, so keep the underlying reason in it. */\nfunction withCauseDetail(message, cause) {\n if (cause === undefined || cause === null)\n return message;\n const detail = formatThrownValue(cause).trim();\n if (!detail || message.includes(detail))\n return message;\n return `${message}: ${detail}`;\n}\n/**\n * Auth resolution shared by the `Models` and `ImagesModels` collections.\n * A stored credential owns the provider: ambient/env is consulted only when\n * nothing is stored. No silent env fallback after a failed refresh or for a\n * credential type without a matching handler.\n */\nexport function resolveProviderAuth(provider, credentials, authContext, overrides) {\n const signal = operationSignal(overrides?.signal);\n return raceWithAbortSignal(resolveProviderAuthWithSignal(provider, credentials, authContext, overrides, signal), signal);\n}\nasync function resolveProviderAuthWithSignal(provider, credentials, authContext, overrides, signal) {\n signal.throwIfAborted();\n const requestAuthContext = overrides?.env ? overlayEnvAuthContext(authContext, overrides.env) : authContext;\n if (overrides?.apiKey !== undefined && provider.auth.apiKey) {\n return resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, {\n type: \"api_key\",\n key: overrides.apiKey,\n env: overrides.env,\n }, signal);\n }\n const stored = await readCredential(credentials, provider.id, signal);\n if (stored) {\n if (stored.type === \"oauth\" && provider.auth.oauth) {\n return resolveStoredOAuth(credentials, provider.id, provider.auth.oauth, stored, signal, overrides?.minOAuthValidityMs);\n }\n if (stored.type === \"api_key\" && provider.auth.apiKey) {\n const credential = overrides?.env ? { ...stored, env: { ...stored.env, ...overrides.env } } : stored;\n return resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, credential, signal);\n }\n return undefined;\n }\n // Ambient (env vars, AWS profiles, ADC files).\n return provider.auth.apiKey\n ? resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, undefined, signal)\n : undefined;\n}\nfunction overlayEnvAuthContext(base, env) {\n return {\n env: async (name) => env[name] || (await base.env(name)),\n fileExists: (path) => base.fileExists(path),\n };\n}\nconst DEFAULT_OAUTH_MINIMUM_VALIDITY_MS = 5 * 60 * 1000;\nconst DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 15_000;\n/**\n * OAuth resolution with double-checked locking: tokens with less than five\n * minutes remaining lock, re-check expiry under the lock, refresh once\n * globally, and persist the rotated credential before release.\n */\nasync function resolveStoredOAuth(credentials, providerId, oauth, stored, signal, minOAuthValidityMs) {\n const minimumValidityMs = Math.max(DEFAULT_OAUTH_MINIMUM_VALIDITY_MS, minOAuthValidityMs ?? 0);\n const expiresSoon = (credential) => Date.now() + minimumValidityMs >= credential.expires;\n let credential = stored;\n if (expiresSoon(credential)) {\n // Optimistic check said expired; the authoritative check runs under the lock.\n let post;\n try {\n post = await credentials.modify(providerId, async (current) => {\n if (current?.type !== \"oauth\")\n return undefined; // logged out meanwhile\n if (!expiresSoon(current))\n return undefined; // another process/request refreshed\n try {\n const refreshSignal = AbortSignal.any([\n signal,\n AbortSignal.timeout(DEFAULT_OAUTH_REFRESH_TIMEOUT_MS),\n ]);\n return await oauth.refresh(current, refreshSignal);\n }\n catch (error) {\n throw new ModelsError(\"oauth\", `OAuth refresh failed for ${providerId}`, { cause: error });\n }\n }, { signal });\n }\n catch (error) {\n if (error instanceof ModelsError)\n throw error;\n throw new ModelsError(\"auth\", `Credential store modify failed for ${providerId}`, { cause: error });\n }\n if (post?.type !== \"oauth\")\n return undefined; // logged out meanwhile\n credential = post;\n // The normal five-minute window triggers a refresh but does not impose a\n // provider contract. Explicit callers (such as bearer-token export) do\n // require the requested minimum after the refresh.\n if (minOAuthValidityMs !== undefined && expiresSoon(credential)) {\n throw new ModelsError(\"oauth\", `OAuth refresh returned a token that expires too soon for ${providerId}`);\n }\n }\n try {\n return { auth: await oauth.toAuth(credential), source: \"OAuth\" };\n }\n catch (error) {\n throw new ModelsError(\"oauth\", `OAuth auth derivation failed for ${providerId}`, { cause: error });\n }\n}\nasync function resolveApiKey(authContext, apiKey, providerId, credential, signal) {\n try {\n return await apiKey.resolve({ ctx: authContext, credential, signal });\n }\n catch (error) {\n throw new ModelsError(\"auth\", `API key auth failed for provider ${providerId}`, { cause: error });\n }\n}\nasync function readCredential(credentials, providerId, signal) {\n try {\n return await credentials.read(providerId, { signal });\n }\n catch (error) {\n throw new ModelsError(\"auth\", `Credential store read failed for ${providerId}`, { cause: error });\n }\n}\n//# sourceMappingURL=resolve.js.map","// @ts-nocheck — vendored Pi source excerpt (pi-coding-agent core/provider-composer.js adaptOAuth @0.84.1, MIT, see ./PI-LICENSE); logic unchanged.\n// The official two-generation adapter: extension oauth callbacks (onAuth/onDeviceCode/onPrompt/onSelect)\n// over the pi-ai interaction surface (prompt/notify/signal), plus refresh and toAuth->getApiKey.\nexport function adaptOAuth(config) {\n return {\n name: config.name,\n isSubscription: config.isSubscription,\n login: async (callbacks) => {\n const credential = await config.login({\n onAuth: (info) => callbacks.notify({ type: \"auth_url\", ...info }),\n onDeviceCode: (info) => callbacks.notify({ type: \"device_code\", ...info }),\n onPrompt: (prompt) => callbacks.prompt({ type: \"text\", ...prompt }),\n onProgress: (message) => callbacks.notify({ type: \"progress\", message }),\n onManualCodeInput: () => callbacks.prompt({ type: \"manual_code\", message: \"Paste the authorization code\" }),\n onSelect: (prompt) => callbacks.prompt({ type: \"select\", ...prompt }),\n signal: callbacks.signal,\n });\n return { ...credential, type: \"oauth\" };\n },\n refresh: async (credential, signal) => ({ ...(await config.refreshToken(credential, signal)), type: \"oauth\" }),\n toAuth: async (credential) => ({ apiKey: config.getApiKey(credential) }),\n };\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { mkdir, rename, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport { InMemoryCredentialStore } from './compat/vendor/pi-ai-credential-store.js'\nimport { resolveProviderAuth } from './compat/vendor/pi-ai-auth-resolve.js'\nimport { adaptOAuth } from './compat/vendor/pi-oauth-adapt.js'\n\ntype UnknownRecord = Record<string, unknown>\n\n// The interactive OAuth host seam, four layers with one owner each:\n// L1 protocol — the package's own oauth.login/refreshToken/getApiKey code\n// runs verbatim (device-code, auth-code, rotation). The\n// bridge implements zero protocol.\n// L2 interaction— Pi's official adaptOAuth (vendored) turns the package's\n// callbacks into prompt/notify events; this file routes\n// those onto the Pi ui surface, which pi2dsh already maps\n// to DSH userQuestions with real waits.\n// L3 credentials— Pi's auth.json contract: one file keyed by provider id,\n// 0600, atomic replace, per-provider serialized writes\n// (the vendored InMemoryCredentialStore chain), refresh via\n// Pi's vendored double-checked-lock resolver.\n// L4 routing — resolveOAuthApiKey yields the request key for dsh-llm's\n// one-shot credential seam; tokens never enter DSH settings.\n\nexport class FileCredentialStore extends InMemoryCredentialStore {\n readonly #path: string\n\n constructor(path: string) {\n super()\n this.#path = path\n try {\n const data = JSON.parse(readFileSync(path, 'utf8')) as Record<string, UnknownRecord>\n for (const [providerId, credential] of Object.entries(data)) {\n ;(this as unknown as { credentials: Map<string, unknown> }).credentials.set(providerId, credential)\n }\n } catch {\n // Missing or unreadable auth.json starts empty, exactly like Pi.\n }\n }\n\n get path(): string {\n return this.#path\n }\n\n async #persist(): Promise<void> {\n const credentials = (this as unknown as { credentials: Map<string, unknown> }).credentials\n const data = Object.fromEntries(credentials)\n await mkdir(dirname(this.#path), { recursive: true })\n // Atomic replace so a crash mid-write never truncates the store; 0600 so\n // tokens stay owner-readable only. Same contract Pi's file store keeps.\n const temp = `${this.#path}.tmp-${process.pid}-${Math.floor(Math.random() * 1e9).toString(36)}`\n await writeFile(temp, `${JSON.stringify(data, null, 2)}\\n`, { mode: 0o600 })\n await rename(temp, this.#path)\n }\n\n override modify(providerId: string, fn: (current: unknown) => unknown, options?: UnknownRecord): Promise<unknown> {\n // The vendored per-provider chain already serializes writers; persisting\n // inside the chained task keeps disk order identical to memory order.\n return super.modify(providerId, async (current: unknown) => {\n const next = await fn(current)\n if (next !== undefined) {\n const credentials = (this as unknown as { credentials: Map<string, unknown> }).credentials\n credentials.set(providerId, next)\n await this.#persist()\n }\n return next\n }, options)\n }\n\n override delete(providerId: string, options?: UnknownRecord): Promise<void> {\n return super.delete(providerId, options).then(() => this.#persist()) as Promise<void>\n }\n}\n\n/**\n * One signal that fires when EITHER input does.\n *\n * A question on screen has two reasons to close: the flow's own race (the\n * browser callback beating the paste box) and the whole login being called off.\n * @param ambient - the login's signal, live for the whole flow.\n * @param perCall - the flow's signal for this one question, when it sent one.\n * @returns the joined signal, or whichever one exists.\n */\nexport function joinSignals(ambient: AbortSignal | undefined, perCall: unknown): AbortSignal | undefined {\n if (!(perCall instanceof AbortSignal)) return ambient\n if (ambient === undefined) return perCall\n const joined = new AbortController()\n const stop = () => joined.abort()\n if (ambient.aborted || perCall.aborted) joined.abort()\n else {\n ambient.addEventListener('abort', stop, { once: true })\n perCall.addEventListener('abort', stop, { once: true })\n }\n return joined.signal\n}\n\nexport interface OAuthUiSurface {\n /**\n * @param signal cancels THIS question when another path in the flow answers\n * first — the browser callback beating the paste box. Optional because\n * Pi's own ui.input takes two arguments; the bridge's accepts a third.\n */\n input(title: unknown, placeholder?: unknown, signal?: AbortSignal): Promise<string | undefined>\n select(title: unknown, options: unknown[], signal?: AbortSignal): Promise<string | undefined>\n notify(message: unknown): void\n /**\n * Keep a device-code flow visible while the package polls in the background.\n *\n * Unlike auth-code flows, device-code flows normally do not issue a later\n * prompt. A command notice therefore arrives only after the code has expired.\n * Hosts with a live question surface should implement this optional seam;\n * older/headless hosts still receive the plain notification below.\n */\n deviceCode?(title: unknown, detail: unknown, signal?: AbortSignal): Promise<void>\n}\n\ninterface OAuthPromptEvent {\n type: 'text' | 'manual_code' | 'select'\n message: string\n placeholder?: string\n options?: Array<{ id: string, label: string }>\n /** Cancels THIS question when another path in the flow answers first. */\n signal?: AbortSignal\n}\n\ninterface OAuthNotifyEvent {\n type: 'auth_url' | 'device_code' | 'progress'\n url?: string\n instructions?: string\n userCode?: string\n verificationUri?: string\n message?: string\n /** How long the device code stays valid. Every device-code flow sends it. */\n expiresInSeconds?: number\n /** How often the flow polls; sent alongside the expiry. */\n intervalSeconds?: number\n}\n\n// The pi-ai interaction surface ({prompt, notify, signal}) over the Pi ui\n// surface pi2dsh already maps to DSH userQuestions.\n/**\n * Hand a URL to the host's browser opener, when the host gave one.\n *\n * Opening a window is the HOST's action, not the translation layer's, so the\n * opener is injected rather than reached for. Wiring it in here meant every\n * caller opened a real browser tab — including the test suite, which spawned a\n * tab per assertion on the developer's own machine.\n * @param open - the host's opener, absent when the host does not open windows.\n * @param url - the address the flow announced, when it announced one.\n */\nfunction openIfPossible(open: ((url: string) => void) | undefined, url: string | undefined): void {\n if (open === undefined || typeof url !== 'string' || !/^https?:\\/\\//u.test(url)) return\n try {\n open(url)\n } catch {\n // Best effort by design: the URL is in the prompt either way, and a host\n // with no desktop session (a container, a remote box) must still be able to\n // finish the login by pasting the code.\n }\n}\n\nexport function oauthInteraction(\n ui: OAuthUiSurface,\n signal?: AbortSignal,\n shorten?: (url: string) => string | undefined,\n open?: (url: string) => void,\n cancel?: () => void,\n): {\n prompt(prompt: OAuthPromptEvent): Promise<string | undefined>\n notify(event: OAuthNotifyEvent): void\n signal: AbortSignal\n} {\n // Pi hosts always hand flows a live signal; flows attach abort listeners\n // unconditionally, so an absent caller signal becomes a never-aborting one.\n const effectiveSignal = signal ?? new AbortController().signal\n // Where the user has to GO to authorize, held until the next prompt.\n //\n // The flow announces the authorization URL through notify() and then blocks\n // on prompt() for the code. A notice is a command notice on DSH: it reaches\n // the user when the command ENDS — and this command cannot end until the user\n // acts on the notice. So the address was delivered after it was needed, and\n // the dialog on screen showed only the redirect URI (localhost/auth/callback),\n // which does nothing when clicked. Carrying it into the prompt puts it where\n // the user is already looking.\n let pending: string | undefined\n /**\n * Hand the announced address to the prompt as its DETAIL, not its title.\n *\n * An authorization URL is 400 characters of query string. Prepending it to\n * the question turned the dialog into a wall of wrapped text with the actual\n * question buried underneath; `detail` is the slot DSH renders supporting\n * text in.\n * @param placeholder - the flow's own placeholder, kept when it supplied one.\n */\n const takePending = (placeholder: unknown): string | undefined => {\n const carried = pending\n pending = undefined\n // A carried address takes the slot alone. Pi's placeholder is greyed hint\n // text INSIDE its input box; DSH's dialog has no such slot, so it lands in\n // the same body text — and codex's placeholder is the redirect URI\n // (localhost:1455/auth/callback), which GFM autolinks into a second,\n // clickable, useless address sitting right under the real one. That is the\n // trap the user already fell into. Where nothing was announced it is still\n // the only hint there is, so it stays.\n if (carried !== undefined) return carried\n return placeholder === undefined ? undefined : String(placeholder)\n }\n return {\n signal: effectiveSignal,\n async prompt(prompt) {\n if (prompt.type === 'select') {\n const options = prompt.options ?? []\n pending = undefined\n const picked = await ui.select(prompt.message, options.map(option => option.label), effectiveSignal)\n return options.find(option => option.label === picked)?.id ?? picked\n }\n // BOTH signals close this box: the flow's own (another path won the race\n // — the local callback got the code) and the login's (it was called off).\n //\n // The second one is not optional. Cancelling a flow only tells the flow;\n // the question is the HOST's, and Pi's host takes its dialog down, which\n // is how the flow's `await manualPromise` gets to settle. Pi's anthropic\n // flow deadlocks without that: cancelling makes its callback wait return\n // nothing, and it then waits on the paste box forever, because the abort\n // that would close the box sits in a `finally` this wait never reaches.\n return ui.input(prompt.message, takePending(prompt.placeholder), joinSignals(effectiveSignal, prompt.signal))\n },\n notify(event) {\n if (event.type === 'auth_url') {\n // An authorize URL is 400 characters of query string. Pasted whole it\n // filled the dialog with wrapped text and gave the user nothing to\n // click. The dialog's detail slot is rendered as markdown, so the\n // address goes in as a LINK — one short clickable line, opened in a new\n // tab by the host's own renderer. The short redirect on this app's\n // origin is what makes it copyable too (a notice is plain text).\n const shown = (event.url === undefined ? undefined : shorten?.(event.url)) ?? event.url\n const tail = event.instructions === undefined ? '' : `\\n\\n${event.instructions}`\n pending = `[Open the login page](${shown})${tail}`\n ui.notify(`Open this URL to authorize: ${shown}${tail}`)\n // Open it, the way Pi's own login dialog does. The flows themselves\n // print \"A browser window should open\" — that sentence is Pi telling\n // the user what the HOST is about to do, and a host that does not do it\n // leaves a wrapped, unclickable URL and no way to authorize.\n openIfPossible(open, event.url)\n } else if (event.type === 'device_code') {\n // A device code EXPIRES. Dropping that left the user staring at a code\n // with no idea it was on a clock — every device-code flow sends the\n // window, and Pi's own host shows it.\n const window = typeof event.expiresInSeconds === 'number' && event.expiresInSeconds > 0\n ? ` (valid for ${Math.round(event.expiresInSeconds / 60)} min)`\n : ''\n // Same as above: a clickable line in the dialog, plain text in the\n // notice. The code stays as code so it survives the markdown pass.\n const detail = `[Open the device login page](${event.verificationUri}) and enter code \\`${event.userCode}\\`${window}`\n pending = detail\n ui.notify(`Visit ${event.verificationUri} and enter code ${event.userCode}${window}`)\n // A device flow polls without asking a follow-up question. Put a live\n // host dialog on screen NOW; otherwise the only visible artifact is a\n // command notice released after the command ends, when the code is no\n // longer useful. Resolving this dialog means the user cancelled it.\n // Successful/failed login aborts the signal first and merely closes it.\n if (ui.deviceCode !== undefined) {\n pending = undefined\n void ui.deviceCode('Complete login in your browser', detail, effectiveSignal).then(\n () => { if (!effectiveSignal.aborted) cancel?.() },\n () => undefined,\n )\n }\n openIfPossible(open, event.verificationUri)\n } else if (event.message !== undefined) {\n ui.notify(event.message)\n }\n },\n }\n}\n\nfunction oauthConfigOf(providerConfig: UnknownRecord | undefined): UnknownRecord | undefined {\n // Extension-generation configs carry oauth at the top level; pi-ai\n // createProvider() objects carry it under auth.oauth. Pi accepts both.\n const oauth = providerConfig?.oauth\n ?? (providerConfig?.auth as UnknownRecord | undefined)?.oauth\n return typeof oauth === 'object' && oauth !== null && typeof (oauth as UnknownRecord).login === 'function'\n ? oauth as UnknownRecord\n : undefined\n}\n\n// Pi accepts both oauth generations: pi-ai native flows carry toAuth and take\n// the interaction surface directly; extension configs carry getApiKey and go\n// through Pi's adaptOAuth (vendored). Same acceptance here.\nfunction oauthAdapterOf(oauthConfig: UnknownRecord): {\n login(interaction: unknown): Promise<UnknownRecord>\n refresh(credential: unknown, signal?: AbortSignal): Promise<UnknownRecord>\n toAuth(credential: unknown): Promise<UnknownRecord>\n} {\n if (typeof oauthConfig.toAuth === 'function') {\n return oauthConfig as never\n }\n return adaptOAuth(oauthConfig) as never\n}\n\nexport function providerSupportsOAuth(providerConfig: UnknownRecord | undefined): boolean {\n return oauthConfigOf(providerConfig) !== undefined\n}\n\n// Pi models.login semantics: find the provider's oauth surface, run the\n// package's own login through the vendored adapter, persist through the\n// store's serialized modify.\nexport async function loginPiProvider(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n ui: OAuthUiSurface\n signal?: AbortSignal\n /** Turns a long authorize URL into a short one the user can actually click. */\n shorten?: (url: string) => string | undefined\n /** The host's browser opener. Absent = this host opens nothing. */\n open?: (url: string) => void\n}): Promise<UnknownRecord> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n if (oauthConfig === undefined) {\n throw new Error(`${options.providerName ?? options.providerId} does not support oauth login`)\n }\n const adapter = oauthAdapterOf(oauthConfig)\n // The flow's own signal, so a question still on screen when the login ends\n // gets cancelled. The browser half of an OAuth login finishes at the local\n // callback, not at the paste box — leaving that box open after the flow has\n // resolved (or failed, as a region-blocked token exchange does) strands the\n // user in front of a dialog that can no longer do anything.\n const done = new AbortController()\n if (options.signal !== undefined) {\n if (options.signal.aborted) done.abort()\n else options.signal.addEventListener('abort', () => done.abort(), { once: true })\n }\n try {\n const credential = await adapter.login(oauthInteraction(\n options.ui,\n done.signal,\n options.shorten,\n options.open,\n () => done.abort(),\n ))\n await options.store.modify(options.providerId, async () => credential)\n return credential\n } finally {\n done.abort()\n }\n}\n\n// Pi's resolveProviderAuth over the stored credential: five-minute expiry\n// window, double-checked-lock refresh through the package's own refreshToken,\n// rotated credential persisted before release, key derived by the package's\n// own getApiKey. Returns undefined when nothing is stored.\nexport async function resolveOAuthApiKey(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n signal?: AbortSignal\n}): Promise<string | undefined> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n if (oauthConfig === undefined) return undefined\n const provider = {\n id: options.providerId,\n name: options.providerName ?? options.providerId,\n auth: { oauth: oauthAdapterOf(oauthConfig) },\n }\n const resolved = await resolveProviderAuth(\n provider,\n options.store,\n { env: async () => undefined },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n ) as { auth?: { apiKey?: string } } | undefined\n return resolved?.auth?.apiKey\n}\n\nexport async function storedOAuthCredential(store: FileCredentialStore, providerId: string): Promise<UnknownRecord | undefined> {\n const stored = await store.read(providerId, undefined) as UnknownRecord | undefined\n return stored?.type === 'oauth' ? stored : undefined\n}\n\n// Pi's FULL getProviderAuth precedence over the vendored resolver — stored\n// OAuth (double-checked-lock refresh), then stored api_key, then the\n// provider's own auth.apiKey ambient resolution (its resolve() reads env vars\n// and probes credential files through the auth context). The package's\n// resolve code runs verbatim; the bridge supplies only the context.\nexport async function resolvePiProviderAuth(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n signal?: AbortSignal\n}): Promise<{ auth?: UnknownRecord, source?: string } | undefined> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n const declaredApiKey = (options.providerConfig.auth as UnknownRecord | undefined)?.apiKey\n if (oauthConfig === undefined && declaredApiKey === undefined) return undefined\n const auth: UnknownRecord = {}\n if (oauthConfig !== undefined) auth.oauth = oauthAdapterOf(oauthConfig)\n if (declaredApiKey !== undefined) auth.apiKey = declaredApiKey\n const provider = { id: options.providerId, name: options.providerName ?? options.providerId, auth }\n return await resolveProviderAuth(\n provider,\n options.store,\n {\n env: async (name: string) => process.env[name],\n fileExists: async (path: string) => existsSync(path),\n },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n ) as { auth?: UnknownRecord, source?: string } | undefined\n}\n","import { join } from 'node:path'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { CredentialProvider, type CredentialInfo, type CredentialRef, type ResolvedCredential } from '@deepseek-ai/dsh-credentials'\nimport { FileCredentialStore, providerSupportsOAuth, resolveOAuthApiKey, storedOAuthCredential } from './oauth-bridge.js'\nimport { builtinProviders } from './compat/pi-ai.js'\nimport { getAgentDir } from './compat/pi-coding-agent.js'\n\ntype UnknownRecord = Record<string, unknown>\n\n// L4 of the OAuth host seam: a standard dsh-credentials provider that serves\n// Pi OAuth tokens to DSH's native LLM request path. A route configured as\n// `apiKeyEnv: PI2DSH_OAUTH_OPENAI_CODEX` resolves per request through this\n// provider — Pi's double-checked-lock refresh runs on every resolution, so a\n// rotated token reaches the next model call with no restart, exactly the\n// per-operation semantics the credentials seam demands. Every other reference\n// falls through to the process environment, so one provider instance serves a\n// whole composition.\n//\n// Reference convention: PI2DSH_OAUTH_<PROVIDER_ID> where the provider id is\n// upper-cased with `-` as `_` (openai-codex → PI2DSH_OAUTH_OPENAI_CODEX).\n\nconst OAUTH_REF_PREFIX = 'PI2DSH_OAUTH_'\n\nexport function oauthCredentialRef(providerId: string): string {\n return `${OAUTH_REF_PREFIX}${providerId.toUpperCase().replaceAll('-', '_')}`\n}\n\nfunction providerIdOfRef(ref: string): string | undefined {\n if (!ref.startsWith(OAUTH_REF_PREFIX)) return undefined\n return ref.slice(OAUTH_REF_PREFIX.length).toLowerCase().replaceAll('_', '-')\n}\n\nexport interface PiOAuthCredentialProviderOptions {\n /** Path to the Pi-format auth.json; defaults to `$agentDir/auth.json`. */\n authPath?: string\n /** Additional provider configs (id → config with an oauth block), e.g. from packages that registered providers. */\n providers?: ReadonlyMap<string, UnknownRecord>\n}\n\nexport class PiOAuthCredentialProvider extends CredentialProvider {\n private readonly oauthStore: FileCredentialStore\n private readonly extraProviders: ReadonlyMap<string, UnknownRecord>\n\n constructor(ctx: Context, options: PiOAuthCredentialProviderOptions = {}) {\n super(ctx)\n this.oauthStore = new FileCredentialStore(options.authPath ?? join(getAgentDir(), 'auth.json'))\n this.extraProviders = options.providers ?? new Map()\n }\n\n private oauthConfigFor(providerId: string): UnknownRecord | undefined {\n const registered = this.extraProviders.get(providerId)\n if (providerSupportsOAuth(registered)) return registered\n const builtin = builtinProviders().find(provider => provider.id === providerId)\n if (builtin === undefined) return undefined\n return { name: builtin.name, baseUrl: builtin.baseUrl, oauth: builtin.auth.oauth }\n }\n\n async resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n const value = process.env[String(ref)]\n return value !== undefined && value.length > 0 ? { value, source: 'env' } : undefined\n }\n const config = this.oauthConfigFor(providerId)\n if (config === undefined) return undefined\n const value = await resolveOAuthApiKey({\n providerId,\n providerName: typeof config.name === 'string' ? config.name : providerId,\n providerConfig: config,\n store: this.oauthStore,\n })\n return value !== undefined && value.length > 0 ? { value, source: 'pi-oauth' } : undefined\n }\n\n async describe(ref: CredentialRef): Promise<CredentialInfo> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n const value = process.env[String(ref)]\n return { configured: value !== undefined && value.length > 0, ...(value ? { source: 'env' } : {}), writable: false }\n }\n const stored = await storedOAuthCredential(this.oauthStore, providerId)\n return { configured: stored !== undefined, ...(stored !== undefined ? { source: 'pi-oauth' } : {}), writable: false }\n }\n\n async set(ref: CredentialRef, _value: string): Promise<void> {\n throw new Error(`credential ${String(ref)} is read-only here: OAuth tokens are managed by /login, environment values by the shell`)\n }\n\n async unset(ref: CredentialRef): Promise<void> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n throw new Error(`credential ${String(ref)} is read-only here: environment values are managed by the shell`)\n }\n // Logging out is a legitimate unset: drop the stored token.\n await this.oauthStore.delete(providerId, undefined)\n }\n}\n\nexport const name = 'pi2dsh-credentials-oauth'\n\nexport function apply(ctx: Context, config: PiOAuthCredentialProviderOptions = {}): void {\n // The Service constructor registers itself as ctx.credentials.\n void new PiOAuthCredentialProvider(ctx, config)\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
 
2
- import { i as oauthCredentialRef, n as apply, r as name, t as PiOAuthCredentialProvider } from "./credentials-oauth-CAohSLcB.mjs";
2
+ import { i as oauthCredentialRef, n as apply, r as name, t as PiOAuthCredentialProvider } from "./credentials-oauth-Didpkkjx.mjs";
3
3
  export { PiOAuthCredentialProvider, apply, name, oauthCredentialRef };
package/dist/host.d.mts CHANGED
@@ -31,7 +31,9 @@ declare function applyPiHost(ctx: Context, config: PiHostConfig, anchor?: string
31
31
  /** Resolve installed Pi packages and build immutable manifests without mounting their runtimes. */
32
32
  declare function preparePiHost(config: PiHostConfig, anchor?: string): Promise<PreparedPiHostPackage[]>;
33
33
  /** Mount prepared Pi package runtimes into one exact DSH context. */
34
- declare function applyPreparedPiHost(ctx: Context, prepared: readonly PreparedPiHostPackage[], ownerAgent?: UnknownRecord): Promise<void>;
34
+ declare function applyPreparedPiHost(ctx: Context, prepared: readonly PreparedPiHostPackage[], ownerAgent?: UnknownRecord, mode?: {
35
+ hostAnchor?: boolean;
36
+ }): Promise<void>;
35
37
  //#endregion
36
38
  export { PiHostConfig, PiHostPackageSpec, PreparedPiHostPackage, applyPiHost, applyPreparedPiHost, manifestForInstalled, preparePiHost };
37
39
  //# sourceMappingURL=host.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.mts","names":[],"sources":["../src/host.ts"],"mappings":";;;;KAkBK,gBAAgB;UAEJ;;EAEf;;EAEA,SAAS;;UAGM;EACf,UAAU,eAAe;;EAEzB,2BAA2B;;UAGZ;WACN;WACA,SAAS;WACT,UAAU;WACV,SAAS;;;iBAuBE,qBAAqB,KAAK,oBAAoB,QAAQ;;;;;;iBA8DtD,YAAY,KAAK,SAAS,QAAQ,cAAc,kBAAkB;;iBAOlE,cAAc,QAAQ,cAAc,kBAAkB,QAAQ;;iBAsC9D,oBACpB,KAAK,SACL,mBAAmB,yBACnB,aAAa,gBACZ"}
1
+ {"version":3,"file":"host.d.mts","names":[],"sources":["../src/host.ts"],"mappings":";;;;KAkBK,gBAAgB;UAEJ;;EAEf;;EAEA,SAAS;;UAGM;EACf,UAAU,eAAe;;EAEzB,2BAA2B;;UAGZ;WACN;WACA,SAAS;WACT,UAAU;WACV,SAAS;;;iBAuBE,qBAAqB,KAAK,oBAAoB,QAAQ;;;;;;iBA8DtD,YAAY,KAAK,SAAS,QAAQ,cAAc,kBAAkB;;iBAOlE,cAAc,QAAQ,cAAc,kBAAkB,QAAQ;;iBAsC9D,oBACpB,KAAK,SACL,mBAAmB,yBACnB,aAAa,eACb;EAAQ;IACP"}
package/dist/host.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as registerVisionCompanions, t as applyPiPackage } from "./runtime-Dd2KhoX-.mjs";
2
+ import { r as registerVisionCompanions, t as applyPiPackage } from "./runtime-Bb9Bfk4M.mjs";
3
3
  import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import { readFile } from "node:fs/promises";
@@ -117,7 +117,7 @@ async function preparePiHost(config, anchor) {
117
117
  return prepared;
118
118
  }
119
119
  /** Mount prepared Pi package runtimes into one exact DSH context. */
120
- async function applyPreparedPiHost(ctx, prepared, ownerAgent) {
120
+ async function applyPreparedPiHost(ctx, prepared, ownerAgent, mode = {}) {
121
121
  const errors = [];
122
122
  for (const pkg of prepared) try {
123
123
  await ctx.plugin(Object.assign(async (packageCtx) => {
@@ -128,7 +128,8 @@ async function applyPreparedPiHost(ctx, prepared, ownerAgent) {
128
128
  ...pkg.config ?? {},
129
129
  visionCompanions: false
130
130
  },
131
- ...ownerAgent === void 0 ? {} : { ownerAgent }
131
+ ...ownerAgent === void 0 ? {} : { ownerAgent },
132
+ ...mode.hostAnchor === true ? { hostAnchor: true } : {}
132
133
  });
133
134
  }, { inject: [
134
135
  "tools",
package/dist/host.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"host.mjs","names":[],"sources":["../src/host.ts"],"sourcesContent":["// PiHostOnDSH: one DSH plugin that hosts unmodified Pi packages.\n//\n// Pi packages are ordinary npm dependencies of the profile; DSH's plugin\n// manager (pnpm) installs them, and at load time this module resolves each\n// installed package, discovers its Pi entry points, and mounts it through\n// the same package-agnostic runtime. One host, any package — there is\n// deliberately no per-package branching here.\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { readFile, stat, writeFile, mkdir, cp } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, join, relative } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { applyPiPackage, registerVisionCompanions } from './runtime.js'\nimport { resolvePiPackage } from './source.js'\nimport type { GeneratedRuntimeManifest, ResolvedPiPackage } from './types.js'\n\ntype UnknownRecord = Record<string, unknown>\n\nexport interface PiHostPackageSpec {\n /** npm package name as installed in the host bundle's node_modules. */\n name: string\n /** Optional per-package config forwarded to the runtime. */\n config?: UnknownRecord\n}\n\nexport interface PiHostConfig {\n packages: Array<string | PiHostPackageSpec>\n /** Image-admission companions: default automatic; `false` off; explicit map narrows. */\n visionCompanions?: false | Record<string, readonly string[]>\n}\n\nexport interface PreparedPiHostPackage {\n readonly name: string\n readonly rootUrl: URL\n readonly manifest: GeneratedRuntimeManifest\n readonly config?: UnknownRecord\n}\n\nfunction parseFrontmatter(text: string): { attributes: Record<string, string>; body: string } {\n const normalized = text.replace(/\\r\\n?/gu, '\\n')\n if (!normalized.startsWith('---')) return { attributes: {}, body: normalized }\n const endIndex = normalized.indexOf('\\n---', 3)\n if (endIndex === -1) return { attributes: {}, body: normalized }\n const attributes: Record<string, string> = {}\n for (const line of normalized.slice(4, endIndex).split('\\n')) {\n const separator = line.indexOf(':')\n if (separator === -1) continue\n const key = line.slice(0, separator).trim()\n let value = line.slice(separator + 1).trim()\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1)\n }\n if (key.length > 0) attributes[key] = value\n }\n return { attributes, body: normalized.slice(endIndex + 4).trim() }\n}\n\n/** Build a runtime manifest in place over an installed Pi package directory. */\nexport async function manifestForInstalled(pkg: ResolvedPiPackage): Promise<GeneratedRuntimeManifest> {\n const relativeTo = (file: string): string => relative(pkg.rootDir, file).replaceAll('\\\\', '/')\n\n const skillDirs = new Set<string>()\n for (const file of pkg.resources.skills) {\n // <dir>/<name>/SKILL.md contributes <dir>; a flat <dir>/<name>.md contributes <dir>.\n skillDirs.add(relativeTo(basename(file) === 'SKILL.md' ? dirname(dirname(file)) : dirname(file)))\n }\n\n const prompts: GeneratedRuntimeManifest['prompts'] = []\n const promptNames = new Set<string>()\n for (const source of pkg.resources.prompts) {\n const name = basename(source, '.md').toLowerCase().replace(/[^a-z0-9_-]+/gu, '-')\n if (promptNames.has(name)) throw new Error(`prompt command name collision in ${pkg.identity.name}: ${name}`)\n promptNames.add(name)\n const { attributes, body } = parseFrontmatter(await readFile(source, 'utf8'))\n const firstLine = body.split(/\\r?\\n/u).map(line => line.trim()).find(Boolean)\n prompts.push({\n name,\n description: attributes.description ?? firstLine ?? `Run migrated Pi prompt ${name}`,\n ...(attributes['argument-hint'] !== undefined ? { argumentHint: attributes['argument-hint'] } : {}),\n path: relativeTo(source),\n })\n }\n\n return {\n schemaVersion: 1,\n package: pkg.identity,\n extensions: pkg.resources.extensions.map(relativeTo),\n skillDirs: [...skillDirs].sort(),\n prompts,\n }\n}\n\nfunction normalizeSpecs(config: PiHostConfig): PiHostPackageSpec[] {\n const packages = Array.isArray(config?.packages) ? config.packages : []\n return packages.map(spec => (typeof spec === 'string' ? { name: spec } : spec))\n .filter(spec => typeof spec?.name === 'string' && spec.name.length > 0)\n}\n\nfunction resolveInstalledDir(anchor: string, packageName: string): string {\n const require = createRequire(anchor)\n try {\n return dirname(require.resolve(`${packageName}/package.json`))\n } catch {\n // Modern strict `exports` maps refuse the package.json subpath, and pure\n // ESM packages (no \"require\" condition) refuse CJS entry resolution too.\n // Locate the installed directory on the filesystem instead: probe every\n // node_modules candidate on the resolution path — no exports involved.\n for (const candidate of require.resolve.paths(packageName) ?? []) {\n const dir = join(candidate, packageName)\n if (existsSync(join(dir, 'package.json'))) return dir\n }\n throw new Error(`cannot locate the installed package directory for ${JSON.stringify(packageName)} near ${JSON.stringify(anchor)}`)\n }\n}\n\n/**\n * Mount every configured Pi package from the host bundle's own node_modules.\n * Packages that fail to mount report their error and do not take down the\n * host or their siblings — matching Pi's own per-extension error isolation.\n */\nexport async function applyPiHost(ctx: Context, config: PiHostConfig, anchor?: string): Promise<void> {\n registerVisionCompanions(ctx, config.visionCompanions)\n const prepared = await preparePiHost(config, anchor)\n await applyPreparedPiHost(ctx, prepared)\n}\n\n/** Resolve installed Pi packages and build immutable manifests without mounting their runtimes. */\nexport async function preparePiHost(config: PiHostConfig, anchor?: string): Promise<PreparedPiHostPackage[]> {\n const anchorPath = anchor ?? fileURLToPath(import.meta.url)\n const errors: Array<{ name: string; error: string }> = []\n const prepared: PreparedPiHostPackage[] = []\n for (const spec of normalizeSpecs(config)) {\n try {\n const dir = resolveInstalledDir(anchorPath, spec.name)\n const pkg = await resolvePiPackage(dir)\n try {\n const manifest = await manifestForInstalled(pkg)\n prepared.push({\n name: spec.name,\n rootUrl: pathToFileURL(`${pkg.rootDir}/`),\n manifest,\n ...(spec.config === undefined ? {} : { config: spec.config }),\n })\n } finally {\n await pkg.dispose()\n }\n } catch (error) {\n errors.push({ name: spec.name, error: error instanceof Error ? error.message : String(error) })\n }\n }\n for (const failure of errors) {\n // Console AND logger, the same rule the engine states for its own mount\n // line: a profile's logger level must never be able to hide which packages\n // did not mount. It could, and it did — a package failed to mount and the\n // only symptom was its absence from a list nobody was diffing.\n const message = `[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`\n console.warn(message)\n }\n if (errors.length > 0 && errors.length === normalizeSpecs(config).length) {\n throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0]!.name}: ${errors[0]!.error}`)\n }\n return prepared\n}\n\n/** Mount prepared Pi package runtimes into one exact DSH context. */\nexport async function applyPreparedPiHost(\n ctx: Context,\n prepared: readonly PreparedPiHostPackage[],\n ownerAgent?: UnknownRecord,\n): Promise<void> {\n const errors: Array<{ name: string; error: string }> = []\n for (const pkg of prepared) {\n try {\n // One real Cordis activation per Pi package, owned by this Agent scope.\n // Besides giving teardown the correct owner, dsh-TUI's contribution\n // services bind registration and later use to this activation identity;\n // calling applyPiPackage() naked from the setup event has no live plugin\n // caller and cannot register or open a scene correctly.\n await ctx.plugin(Object.assign(\n async (packageCtx: Context) => {\n await applyPiPackage(packageCtx, {\n rootUrl: pkg.rootUrl,\n manifest: pkg.manifest,\n // The engine/host registers companion routes once on the host\n // before mounting prepared packages. Suppress applyPiPackage's\n // standalone default here so one package cannot undo an explicit\n // host-level `visionCompanions: false` or duplicate the catalog.\n config: { ...(pkg.config ?? {}), visionCompanions: false },\n ...(ownerAgent === undefined ? {} : { ownerAgent }),\n })\n },\n { inject: ['tools', 'systemPrompt', 'commands'] },\n ))\n } catch (error) {\n errors.push({ name: pkg.name, error: error instanceof Error ? error.message : String(error) })\n }\n }\n for (const failure of errors) {\n const log = (ctx as unknown as { logger?: { warn?(message: string): void } }).logger\n const message = `[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`\n log?.warn?.(message)\n console.warn(message)\n }\n if (errors.length > 0 && errors.length === prepared.length) {\n throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0]!.name}: ${errors[0]!.error}`)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"host.mjs","names":[],"sources":["../src/host.ts"],"sourcesContent":["// PiHostOnDSH: one DSH plugin that hosts unmodified Pi packages.\n//\n// Pi packages are ordinary npm dependencies of the profile; DSH's plugin\n// manager (pnpm) installs them, and at load time this module resolves each\n// installed package, discovers its Pi entry points, and mounts it through\n// the same package-agnostic runtime. One host, any package — there is\n// deliberately no per-package branching here.\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { readFile, stat, writeFile, mkdir, cp } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, join, relative } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { applyPiPackage, registerVisionCompanions } from './runtime.js'\nimport { resolvePiPackage } from './source.js'\nimport type { GeneratedRuntimeManifest, ResolvedPiPackage } from './types.js'\n\ntype UnknownRecord = Record<string, unknown>\n\nexport interface PiHostPackageSpec {\n /** npm package name as installed in the host bundle's node_modules. */\n name: string\n /** Optional per-package config forwarded to the runtime. */\n config?: UnknownRecord\n}\n\nexport interface PiHostConfig {\n packages: Array<string | PiHostPackageSpec>\n /** Image-admission companions: default automatic; `false` off; explicit map narrows. */\n visionCompanions?: false | Record<string, readonly string[]>\n}\n\nexport interface PreparedPiHostPackage {\n readonly name: string\n readonly rootUrl: URL\n readonly manifest: GeneratedRuntimeManifest\n readonly config?: UnknownRecord\n}\n\nfunction parseFrontmatter(text: string): { attributes: Record<string, string>; body: string } {\n const normalized = text.replace(/\\r\\n?/gu, '\\n')\n if (!normalized.startsWith('---')) return { attributes: {}, body: normalized }\n const endIndex = normalized.indexOf('\\n---', 3)\n if (endIndex === -1) return { attributes: {}, body: normalized }\n const attributes: Record<string, string> = {}\n for (const line of normalized.slice(4, endIndex).split('\\n')) {\n const separator = line.indexOf(':')\n if (separator === -1) continue\n const key = line.slice(0, separator).trim()\n let value = line.slice(separator + 1).trim()\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1)\n }\n if (key.length > 0) attributes[key] = value\n }\n return { attributes, body: normalized.slice(endIndex + 4).trim() }\n}\n\n/** Build a runtime manifest in place over an installed Pi package directory. */\nexport async function manifestForInstalled(pkg: ResolvedPiPackage): Promise<GeneratedRuntimeManifest> {\n const relativeTo = (file: string): string => relative(pkg.rootDir, file).replaceAll('\\\\', '/')\n\n const skillDirs = new Set<string>()\n for (const file of pkg.resources.skills) {\n // <dir>/<name>/SKILL.md contributes <dir>; a flat <dir>/<name>.md contributes <dir>.\n skillDirs.add(relativeTo(basename(file) === 'SKILL.md' ? dirname(dirname(file)) : dirname(file)))\n }\n\n const prompts: GeneratedRuntimeManifest['prompts'] = []\n const promptNames = new Set<string>()\n for (const source of pkg.resources.prompts) {\n const name = basename(source, '.md').toLowerCase().replace(/[^a-z0-9_-]+/gu, '-')\n if (promptNames.has(name)) throw new Error(`prompt command name collision in ${pkg.identity.name}: ${name}`)\n promptNames.add(name)\n const { attributes, body } = parseFrontmatter(await readFile(source, 'utf8'))\n const firstLine = body.split(/\\r?\\n/u).map(line => line.trim()).find(Boolean)\n prompts.push({\n name,\n description: attributes.description ?? firstLine ?? `Run migrated Pi prompt ${name}`,\n ...(attributes['argument-hint'] !== undefined ? { argumentHint: attributes['argument-hint'] } : {}),\n path: relativeTo(source),\n })\n }\n\n return {\n schemaVersion: 1,\n package: pkg.identity,\n extensions: pkg.resources.extensions.map(relativeTo),\n skillDirs: [...skillDirs].sort(),\n prompts,\n }\n}\n\nfunction normalizeSpecs(config: PiHostConfig): PiHostPackageSpec[] {\n const packages = Array.isArray(config?.packages) ? config.packages : []\n return packages.map(spec => (typeof spec === 'string' ? { name: spec } : spec))\n .filter(spec => typeof spec?.name === 'string' && spec.name.length > 0)\n}\n\nfunction resolveInstalledDir(anchor: string, packageName: string): string {\n const require = createRequire(anchor)\n try {\n return dirname(require.resolve(`${packageName}/package.json`))\n } catch {\n // Modern strict `exports` maps refuse the package.json subpath, and pure\n // ESM packages (no \"require\" condition) refuse CJS entry resolution too.\n // Locate the installed directory on the filesystem instead: probe every\n // node_modules candidate on the resolution path — no exports involved.\n for (const candidate of require.resolve.paths(packageName) ?? []) {\n const dir = join(candidate, packageName)\n if (existsSync(join(dir, 'package.json'))) return dir\n }\n throw new Error(`cannot locate the installed package directory for ${JSON.stringify(packageName)} near ${JSON.stringify(anchor)}`)\n }\n}\n\n/**\n * Mount every configured Pi package from the host bundle's own node_modules.\n * Packages that fail to mount report their error and do not take down the\n * host or their siblings — matching Pi's own per-extension error isolation.\n */\nexport async function applyPiHost(ctx: Context, config: PiHostConfig, anchor?: string): Promise<void> {\n registerVisionCompanions(ctx, config.visionCompanions)\n const prepared = await preparePiHost(config, anchor)\n await applyPreparedPiHost(ctx, prepared)\n}\n\n/** Resolve installed Pi packages and build immutable manifests without mounting their runtimes. */\nexport async function preparePiHost(config: PiHostConfig, anchor?: string): Promise<PreparedPiHostPackage[]> {\n const anchorPath = anchor ?? fileURLToPath(import.meta.url)\n const errors: Array<{ name: string; error: string }> = []\n const prepared: PreparedPiHostPackage[] = []\n for (const spec of normalizeSpecs(config)) {\n try {\n const dir = resolveInstalledDir(anchorPath, spec.name)\n const pkg = await resolvePiPackage(dir)\n try {\n const manifest = await manifestForInstalled(pkg)\n prepared.push({\n name: spec.name,\n rootUrl: pathToFileURL(`${pkg.rootDir}/`),\n manifest,\n ...(spec.config === undefined ? {} : { config: spec.config }),\n })\n } finally {\n await pkg.dispose()\n }\n } catch (error) {\n errors.push({ name: spec.name, error: error instanceof Error ? error.message : String(error) })\n }\n }\n for (const failure of errors) {\n // Console AND logger, the same rule the engine states for its own mount\n // line: a profile's logger level must never be able to hide which packages\n // did not mount. It could, and it did — a package failed to mount and the\n // only symptom was its absence from a list nobody was diffing.\n const message = `[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`\n console.warn(message)\n }\n if (errors.length > 0 && errors.length === normalizeSpecs(config).length) {\n throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0]!.name}: ${errors[0]!.error}`)\n }\n return prepared\n}\n\n/** Mount prepared Pi package runtimes into one exact DSH context. */\nexport async function applyPreparedPiHost(\n ctx: Context,\n prepared: readonly PreparedPiHostPackage[],\n ownerAgent?: UnknownRecord,\n mode: { hostAnchor?: boolean } = {},\n): Promise<void> {\n const errors: Array<{ name: string; error: string }> = []\n for (const pkg of prepared) {\n try {\n // One real Cordis activation per Pi package, owned by this Agent scope.\n // Besides giving teardown the correct owner, dsh-TUI's contribution\n // services bind registration and later use to this activation identity;\n // calling applyPiPackage() naked from the setup event has no live plugin\n // caller and cannot register or open a scene correctly.\n await ctx.plugin(Object.assign(\n async (packageCtx: Context) => {\n await applyPiPackage(packageCtx, {\n rootUrl: pkg.rootUrl,\n manifest: pkg.manifest,\n // The engine/host registers companion routes once on the host\n // before mounting prepared packages. Suppress applyPiPackage's\n // standalone default here so one package cannot undo an explicit\n // host-level `visionCompanions: false` or duplicate the catalog.\n config: { ...(pkg.config ?? {}), visionCompanions: false },\n ...(ownerAgent === undefined ? {} : { ownerAgent }),\n ...(mode.hostAnchor === true ? { hostAnchor: true } : {}),\n })\n },\n { inject: ['tools', 'systemPrompt', 'commands'] },\n ))\n } catch (error) {\n errors.push({ name: pkg.name, error: error instanceof Error ? error.message : String(error) })\n }\n }\n for (const failure of errors) {\n const log = (ctx as unknown as { logger?: { warn?(message: string): void } }).logger\n const message = `[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`\n log?.warn?.(message)\n console.warn(message)\n }\n if (errors.length > 0 && errors.length === prepared.length) {\n throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0]!.name}: ${errors[0]!.error}`)\n }\n}\n"],"mappings":"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"}