pi2dsh 0.7.0 → 0.9.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.
- package/README.md +5 -2
- package/README.zh.md +4 -2
- package/dist/analyzer-C86ba-27.mjs +626 -0
- package/dist/analyzer-C86ba-27.mjs.map +1 -0
- package/dist/cli.mjs +3 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/compat/pi-ai.d.mts +1 -11
- package/dist/compat/pi-ai.d.mts.map +1 -1
- package/dist/compat/pi-ai.mjs +2 -2
- package/dist/credentials-oauth.mjs +1 -1
- package/dist/generator-C0jNM-yu.mjs +298 -0
- package/dist/generator-C0jNM-yu.mjs.map +1 -0
- package/dist/host.d.mts +2 -0
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +2 -1
- package/dist/host.mjs.map +1 -1
- package/dist/index.d.mts +29 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +27 -4
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-config-Dlou-ZA-.mjs +707 -0
- package/dist/mcp-config-Dlou-ZA-.mjs.map +1 -0
- package/dist/{pi-ai-B4hucafU.mjs → pi-ai-BA3h0Dwv.mjs} +2 -84
- package/dist/pi-ai-BA3h0Dwv.mjs.map +1 -0
- package/dist/{runtime-k3IlCgjQ.mjs → runtime-DMEJknhK.mjs} +115 -960
- package/dist/runtime-DMEJknhK.mjs.map +1 -0
- package/dist/runtime.d.mts +19 -6
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/package.json +6 -3
- package/dist/all-BChhwMVE.mjs +0 -28512
- package/dist/all-BChhwMVE.mjs.map +0 -1
- package/dist/anthropic-messages-Dlw4T0S4.mjs +0 -6438
- package/dist/anthropic-messages-Dlw4T0S4.mjs.map +0 -1
- package/dist/azure-openai-responses-vGUNukZ7.mjs +0 -209
- package/dist/azure-openai-responses-vGUNukZ7.mjs.map +0 -1
- package/dist/deferred-tools-BD4g5QUx.mjs +0 -32
- package/dist/deferred-tools-BD4g5QUx.mjs.map +0 -1
- package/dist/diagnostics-CJye1UFh.mjs +0 -99
- package/dist/diagnostics-CJye1UFh.mjs.map +0 -1
- package/dist/dist-DY2GeEQ6.mjs +0 -1089
- package/dist/dist-DY2GeEQ6.mjs.map +0 -1
- package/dist/dist-DYfTHu0C.mjs +0 -1246
- package/dist/dist-DYfTHu0C.mjs.map +0 -1
- package/dist/error-body-DKA3lknR.mjs +0 -104
- package/dist/error-body-DKA3lknR.mjs.map +0 -1
- package/dist/github-copilot-headers-CZo2QHrt.mjs +0 -25
- package/dist/github-copilot-headers-CZo2QHrt.mjs.map +0 -1
- package/dist/google-generative-ai-C30gRjRi.mjs +0 -358
- package/dist/google-generative-ai-C30gRjRi.mjs.map +0 -1
- package/dist/google-shared-W8g3Esep.mjs +0 -29042
- package/dist/google-shared-W8g3Esep.mjs.map +0 -1
- package/dist/google-vertex--O_gyVYA.mjs +0 -402
- package/dist/google-vertex--O_gyVYA.mjs.map +0 -1
- package/dist/hash-Dbggamac.mjs +0 -19
- package/dist/hash-Dbggamac.mjs.map +0 -1
- package/dist/headers-CPzZhJQn.mjs +0 -17
- package/dist/headers-CPzZhJQn.mjs.map +0 -1
- package/dist/images-models-DJtHo9Rp.mjs +0 -1068
- package/dist/images-models-DJtHo9Rp.mjs.map +0 -1
- package/dist/json-parse-C-vG4_qc.mjs +0 -384
- package/dist/json-parse-C-vG4_qc.mjs.map +0 -1
- package/dist/mcp-config-Cful6uUS.mjs +0 -1618
- package/dist/mcp-config-Cful6uUS.mjs.map +0 -1
- package/dist/mistral-conversations-LSDG9sjq.mjs +0 -42007
- package/dist/mistral-conversations-LSDG9sjq.mjs.map +0 -1
- package/dist/multipart-parser-CoM3rwav.mjs +0 -301
- package/dist/multipart-parser-CoM3rwav.mjs.map +0 -1
- package/dist/openai-codex-responses-JxII_OUX.mjs +0 -1169
- package/dist/openai-codex-responses-JxII_OUX.mjs.map +0 -1
- package/dist/openai-completions-Dp-VwLzJ.mjs +0 -947
- package/dist/openai-completions-Dp-VwLzJ.mjs.map +0 -1
- package/dist/openai-completions-lite-BrM7gJzs.mjs +0 -316
- package/dist/openai-completions-lite-BrM7gJzs.mjs.map +0 -1
- package/dist/openai-prompt-cache-BDWtZRKh.mjs +0 -11
- package/dist/openai-prompt-cache-BDWtZRKh.mjs.map +0 -1
- package/dist/openai-responses-CWmNks8K.mjs +0 -247
- package/dist/openai-responses-CWmNks8K.mjs.map +0 -1
- package/dist/openai-responses-shared-CYgIDTf3.mjs +0 -572
- package/dist/openai-responses-shared-CYgIDTf3.mjs.map +0 -1
- package/dist/openrouter-images-DfAULV7l.mjs +0 -127
- package/dist/openrouter-images-DfAULV7l.mjs.map +0 -1
- package/dist/otel-Boafyju5.mjs +0 -24935
- package/dist/otel-Boafyju5.mjs.map +0 -1
- package/dist/pi-ai-B4hucafU.mjs.map +0 -1
- package/dist/pi-messages-HzGtvc5_.mjs +0 -306
- package/dist/pi-messages-HzGtvc5_.mjs.map +0 -1
- package/dist/provider-env-C9gOBFJi.mjs +0 -40
- package/dist/provider-env-C9gOBFJi.mjs.map +0 -1
- package/dist/provider-retry-Cw6YJ4cr.mjs +0 -80
- package/dist/provider-retry-Cw6YJ4cr.mjs.map +0 -1
- package/dist/runtime-k3IlCgjQ.mjs.map +0 -1
- package/dist/sanitize-unicode-wLA11KvZ.mjs +0 -29
- package/dist/sanitize-unicode-wLA11KvZ.mjs.map +0 -1
- package/dist/src-D0GRWCE8.mjs +0 -5031
- package/dist/src-D0GRWCE8.mjs.map +0 -1
- package/dist/transform-messages-DGq9mvhA.mjs +0 -379
- package/dist/transform-messages-DGq9mvhA.mjs.map +0 -1
- package/dist/uuid-DtFT7tOE.mjs +0 -65
- package/dist/uuid-DtFT7tOE.mjs.map +0 -1
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
|
|
2
|
+
import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
//#region src/compatibility.ts
|
|
7
|
+
const rule = (level, detail) => ({
|
|
8
|
+
level,
|
|
9
|
+
detail
|
|
10
|
+
});
|
|
11
|
+
const PI_CODING_AGENT_PACKAGES = Object.freeze(["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"]);
|
|
12
|
+
const PI_TUI_PACKAGES = Object.freeze(["@earendil-works/pi-tui", "@mariozechner/pi-tui"]);
|
|
13
|
+
const PI_AI_PACKAGES = Object.freeze(["@earendil-works/pi-ai", "@mariozechner/pi-ai"]);
|
|
14
|
+
const VENDORED = "Vendored byte-identical from Pi, so semantics match Pi exactly.";
|
|
15
|
+
const HEADLESS_COMPONENT = "Constructible headless component with Pi-exact signatures; renders plain text, never a terminal.";
|
|
16
|
+
const RUNTIME_STUB = "Importable, but calling it fails explicitly: it belongs to Pi's internal agent runtime and needs a native DSH port.";
|
|
17
|
+
const VENDORED_TOOL = "Pi's built-in tool constructor, vendored byte-identical with its pure-logic closure.";
|
|
18
|
+
const EVENT_GUARD = "Pi's exact one-line tool-event guard, reimplemented verbatim.";
|
|
19
|
+
const HOST_IMPORT_RULES = Object.freeze({
|
|
20
|
+
"pi-coding-agent": Object.freeze({
|
|
21
|
+
defineTool: rule("full", "Identity helper, preserved."),
|
|
22
|
+
CONFIG_DIR_NAME: rule("partial", "The conventional config directory name is preserved, while DSH owns the actual profile layout."),
|
|
23
|
+
DEFAULT_MAX_LINES: rule("full", VENDORED),
|
|
24
|
+
DEFAULT_MAX_BYTES: rule("full", VENDORED),
|
|
25
|
+
VERSION: rule("partial", "Reports a pi2dsh compatibility marker instead of a Pi release version."),
|
|
26
|
+
CURRENT_SESSION_VERSION: rule("full", VENDORED),
|
|
27
|
+
getAgentDir: rule("partial", "Redirected to an isolated DSH-owned pi2dsh directory instead of Pi global state."),
|
|
28
|
+
getPackageDir: rule("partial", "Resolves inside the DSH-owned pi2dsh agent directory."),
|
|
29
|
+
formatSize: rule("full", VENDORED),
|
|
30
|
+
truncateHead: rule("full", VENDORED),
|
|
31
|
+
truncateTail: rule("full", VENDORED),
|
|
32
|
+
truncateLine: rule("full", VENDORED),
|
|
33
|
+
withFileMutationQueue: rule("full", VENDORED),
|
|
34
|
+
SessionManager: rule("full", `${VENDORED} Sessions live under the DSH-owned pi2dsh agent directory.`),
|
|
35
|
+
parseSessionEntries: rule("full", VENDORED),
|
|
36
|
+
migrateSessionEntries: rule("full", VENDORED),
|
|
37
|
+
getLatestCompactionEntry: rule("full", VENDORED),
|
|
38
|
+
sessionEntryToContextMessages: rule("full", VENDORED),
|
|
39
|
+
buildContextEntries: rule("full", VENDORED),
|
|
40
|
+
buildSessionContext: rule("full", VENDORED),
|
|
41
|
+
loadEntriesFromFile: rule("full", VENDORED),
|
|
42
|
+
findMostRecentSession: rule("full", VENDORED),
|
|
43
|
+
getDefaultSessionDir: rule("full", VENDORED),
|
|
44
|
+
assertValidSessionId: rule("full", VENDORED),
|
|
45
|
+
convertToLlm: rule("full", VENDORED),
|
|
46
|
+
createCustomMessage: rule("full", VENDORED),
|
|
47
|
+
createBranchSummaryMessage: rule("full", VENDORED),
|
|
48
|
+
createCompactionSummaryMessage: rule("full", VENDORED),
|
|
49
|
+
bashExecutionToText: rule("full", VENDORED),
|
|
50
|
+
estimateTokens: rule("full", "Reimplements Pi's chars/4 heuristic over the same message roles."),
|
|
51
|
+
calculateContextTokens: rule("full", "Sums the Pi chars/4 heuristic across messages."),
|
|
52
|
+
DEFAULT_COMPACTION_SETTINGS: rule("full", "Pi's default compaction thresholds, preserved as constants."),
|
|
53
|
+
serializeConversation: rule("partial", "JSON serialization without Pi's prompt-oriented formatting."),
|
|
54
|
+
shouldCompact: rule("partial", "Always reports false: DSH owns compaction scheduling."),
|
|
55
|
+
compact: rule("unsupported", "Pi compaction execution belongs to DSH's native compaction plugin; calling fails explicitly."),
|
|
56
|
+
findCutPoint: rule("unsupported", RUNTIME_STUB),
|
|
57
|
+
generateSummary: rule("unsupported", RUNTIME_STUB),
|
|
58
|
+
generateSummaryWithUsage: rule("unsupported", RUNTIME_STUB),
|
|
59
|
+
generateBranchSummary: rule("unsupported", RUNTIME_STUB),
|
|
60
|
+
parseFrontmatter: rule("full", "Frontmatter parsing with Pi-compatible semantics."),
|
|
61
|
+
stripFrontmatter: rule("full", "Frontmatter stripping with Pi-compatible semantics."),
|
|
62
|
+
copyToClipboard: rule("partial", "Attempts the platform clipboard command (pbcopy/clip/wl-copy/xclip); resolves false when none succeeds."),
|
|
63
|
+
resizeImage: rule("partial", "Passes images through un-resized; Pi resizes only to save tokens, so content is preserved."),
|
|
64
|
+
convertToPng: rule("partial", "PNG input passes through; other formats fail explicitly without Pi's wasm codec."),
|
|
65
|
+
getShellConfig: rule("partial", "Standard shell detection without Pi's managed-bin PATH handling."),
|
|
66
|
+
getBinDir: rule("partial", "Reports the first PATH segment instead of Pi's managed bin directory."),
|
|
67
|
+
Theme: rule("partial", "Headless theme: styling calls return their input text unstyled."),
|
|
68
|
+
theme: rule("partial", "A headless theme singleton; jiti-loaded Pi extensions must not rely on Pi global theme state anyway."),
|
|
69
|
+
initTheme: rule("partial", "Accepted as a no-op; DSH surfaces own presentation."),
|
|
70
|
+
getSettingsListTheme: rule("partial", "Returns an unstyled theme with Pi's exact field shape."),
|
|
71
|
+
getSelectListTheme: rule("partial", "Returns an unstyled theme with Pi's exact field shape."),
|
|
72
|
+
getMarkdownTheme: rule("partial", "Returns a plain-text headless theme; Pi terminal styling is intentionally discarded."),
|
|
73
|
+
getLanguageFromPath: rule("partial", "Extension-based language detection covering common languages."),
|
|
74
|
+
highlightCode: rule("partial", "Splits lines without terminal syntax colors."),
|
|
75
|
+
DynamicBorder: rule("full", "Headless implementation of Pi's one-line border component; pass an explicit color function as Pi itself recommends."),
|
|
76
|
+
SettingsManager: rule("partial", "In-memory settings with Pi's getter/setter surface; DSH owns real persisted configuration."),
|
|
77
|
+
InMemorySettingsStorage: rule("partial", "In-memory storage stub honoring the withLock contract."),
|
|
78
|
+
FileSettingsStorage: rule("partial", "Alias of the in-memory storage; DSH owns persisted settings."),
|
|
79
|
+
ModelRegistry: rule("partial", "A local registry container; DSH llm adapters own real model routing."),
|
|
80
|
+
createEventBus: rule("full", "Pi event-bus semantics: async handler isolation and unsubscribe functions."),
|
|
81
|
+
readStoredCredential: rule("partial", "Reads Pi-style auth.json from the pi2dsh-owned agent directory; DSH credentials stay authoritative for DSH model calls."),
|
|
82
|
+
parseSkillBlock: rule("full", "Pi's skill_content block parser, reimplemented over the same wire shape."),
|
|
83
|
+
wrapRegisteredTool: rule("unsupported", RUNTIME_STUB),
|
|
84
|
+
ProjectTrustStore: rule("unsupported", RUNTIME_STUB),
|
|
85
|
+
DefaultResourceLoader: rule("partial", "A headless resource loader honoring overrides, with empty discovery sets."),
|
|
86
|
+
DefaultPackageManager: rule("unsupported", RUNTIME_STUB),
|
|
87
|
+
ModelRuntime: rule("unsupported", RUNTIME_STUB),
|
|
88
|
+
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."),
|
|
89
|
+
createCodingTools: rule("full", "Pi's exact tool set composed from the vendored built-in constructors."),
|
|
90
|
+
createReadOnlyTools: rule("full", "Pi's exact read-only tool set composed from the vendored built-in constructors."),
|
|
91
|
+
createBashTool: rule("full", VENDORED_TOOL),
|
|
92
|
+
createReadTool: rule("full", VENDORED_TOOL),
|
|
93
|
+
createEditTool: rule("full", VENDORED_TOOL),
|
|
94
|
+
createWriteTool: rule("full", VENDORED_TOOL),
|
|
95
|
+
createGrepTool: rule("full", VENDORED_TOOL),
|
|
96
|
+
createFindTool: rule("full", VENDORED_TOOL),
|
|
97
|
+
createLsTool: rule("full", VENDORED_TOOL),
|
|
98
|
+
createBashToolDefinition: rule("full", VENDORED_TOOL),
|
|
99
|
+
createReadToolDefinition: rule("full", VENDORED_TOOL),
|
|
100
|
+
createEditToolDefinition: rule("full", VENDORED_TOOL),
|
|
101
|
+
createWriteToolDefinition: rule("full", VENDORED_TOOL),
|
|
102
|
+
createGrepToolDefinition: rule("full", VENDORED_TOOL),
|
|
103
|
+
createFindToolDefinition: rule("full", VENDORED_TOOL),
|
|
104
|
+
createLsToolDefinition: rule("full", VENDORED_TOOL),
|
|
105
|
+
isToolCallEventType: rule("full", EVENT_GUARD),
|
|
106
|
+
isBashToolResult: rule("full", EVENT_GUARD),
|
|
107
|
+
isReadToolResult: rule("full", EVENT_GUARD),
|
|
108
|
+
isEditToolResult: rule("full", EVENT_GUARD),
|
|
109
|
+
isWriteToolResult: rule("full", EVENT_GUARD),
|
|
110
|
+
isGrepToolResult: rule("full", EVENT_GUARD),
|
|
111
|
+
isFindToolResult: rule("full", EVENT_GUARD),
|
|
112
|
+
isLsToolResult: rule("full", EVENT_GUARD),
|
|
113
|
+
loadSkills: rule("unsupported", RUNTIME_STUB),
|
|
114
|
+
loadSkillsFromDir: rule("unsupported", RUNTIME_STUB),
|
|
115
|
+
formatSkillsForPrompt: rule("full", "Pi's skill prompt formatter, vendored with logic unchanged."),
|
|
116
|
+
CustomEditor: rule("partial", HEADLESS_COMPONENT),
|
|
117
|
+
ToolExecutionComponent: rule("partial", HEADLESS_COMPONENT),
|
|
118
|
+
FooterComponent: rule("partial", HEADLESS_COMPONENT),
|
|
119
|
+
BorderedLoader: rule("partial", HEADLESS_COMPONENT),
|
|
120
|
+
CustomMessageComponent: rule("partial", HEADLESS_COMPONENT),
|
|
121
|
+
AssistantMessageComponent: rule("partial", HEADLESS_COMPONENT),
|
|
122
|
+
UserMessageComponent: rule("partial", HEADLESS_COMPONENT),
|
|
123
|
+
ExtensionSelectorComponent: rule("partial", HEADLESS_COMPONENT),
|
|
124
|
+
ExtensionInputComponent: rule("partial", HEADLESS_COMPONENT),
|
|
125
|
+
ExtensionEditorComponent: rule("partial", HEADLESS_COMPONENT),
|
|
126
|
+
SettingsSelectorComponent: rule("partial", HEADLESS_COMPONENT),
|
|
127
|
+
renderDiff: rule("partial", "Plain unified-style diff lines without terminal colors."),
|
|
128
|
+
truncateToVisualLines: rule("partial", "Visual-line truncation backed by Pi's vendored width math."),
|
|
129
|
+
keyHint: rule("partial", "Plain-text key hint without theme styling."),
|
|
130
|
+
keyText: rule("partial", "Plain-text key name without theme styling."),
|
|
131
|
+
rawKeyHint: rule("partial", "Plain-text key hint without theme styling.")
|
|
132
|
+
}),
|
|
133
|
+
"pi-tui": Object.freeze({
|
|
134
|
+
visibleWidth: rule("full", VENDORED),
|
|
135
|
+
truncateToWidth: rule("full", VENDORED),
|
|
136
|
+
wrapTextWithAnsi: rule("full", VENDORED),
|
|
137
|
+
sliceByColumn: rule("full", VENDORED),
|
|
138
|
+
sliceWithWidth: rule("full", VENDORED),
|
|
139
|
+
stripTerminalSequences: rule("full", VENDORED),
|
|
140
|
+
getOsc8LinkAtColumn: rule("full", VENDORED),
|
|
141
|
+
normalizeTerminalOutput: rule("full", VENDORED),
|
|
142
|
+
extractAnsiCode: rule("full", VENDORED),
|
|
143
|
+
getGraphemeCellRange: rule("full", VENDORED),
|
|
144
|
+
getGraphemeSegmenter: rule("full", VENDORED),
|
|
145
|
+
getWordSegmenter: rule("full", VENDORED),
|
|
146
|
+
applyBackgroundToLine: rule("full", VENDORED),
|
|
147
|
+
isWhitespaceChar: rule("full", VENDORED),
|
|
148
|
+
isPunctuationChar: rule("full", VENDORED),
|
|
149
|
+
cjkBreakRegex: rule("full", VENDORED),
|
|
150
|
+
PUNCTUATION_REGEX: rule("full", VENDORED),
|
|
151
|
+
fuzzyMatch: rule("full", VENDORED),
|
|
152
|
+
fuzzyFilter: rule("full", VENDORED),
|
|
153
|
+
parseKey: rule("full", VENDORED),
|
|
154
|
+
matchesKey: rule("full", VENDORED),
|
|
155
|
+
isKeyRelease: rule("full", VENDORED),
|
|
156
|
+
isKeyRepeat: rule("full", VENDORED),
|
|
157
|
+
decodeKittyPrintable: rule("full", VENDORED),
|
|
158
|
+
isKittyProtocolActive: rule("full", VENDORED),
|
|
159
|
+
setKittyProtocolActive: rule("full", VENDORED),
|
|
160
|
+
Key: rule("full", VENDORED),
|
|
161
|
+
getKeybindings: rule("full", `${VENDORED} Bindings only match when a surface feeds terminal input, which DSH does not.`),
|
|
162
|
+
setKeybindings: rule("full", VENDORED),
|
|
163
|
+
KeybindingsManager: rule("full", VENDORED),
|
|
164
|
+
TUI_KEYBINDINGS: rule("full", VENDORED),
|
|
165
|
+
parseOsc11BackgroundColor: rule("full", VENDORED),
|
|
166
|
+
parseTerminalColorSchemeReport: rule("full", VENDORED),
|
|
167
|
+
renderLatex: rule("full", VENDORED),
|
|
168
|
+
getPngDimensions: rule("full", VENDORED),
|
|
169
|
+
getJpegDimensions: rule("full", VENDORED),
|
|
170
|
+
getGifDimensions: rule("full", VENDORED),
|
|
171
|
+
getWebpDimensions: rule("full", VENDORED),
|
|
172
|
+
getImageDimensions: rule("full", VENDORED),
|
|
173
|
+
calculateImageRows: rule("full", VENDORED),
|
|
174
|
+
allocateImageId: rule("full", VENDORED),
|
|
175
|
+
encodeKitty: rule("partial", `${VENDORED} No DSH surface consumes the escape sequences.`),
|
|
176
|
+
encodeITerm2: rule("partial", `${VENDORED} No DSH surface consumes the escape sequences.`),
|
|
177
|
+
deleteKittyImage: rule("partial", `${VENDORED} No DSH surface consumes the escape sequences.`),
|
|
178
|
+
deleteAllKittyImages: rule("partial", `${VENDORED} No DSH surface consumes the escape sequences.`),
|
|
179
|
+
detectCapabilities: rule("partial", `${VENDORED} Headless environments report no image protocol.`),
|
|
180
|
+
getCapabilities: rule("partial", VENDORED),
|
|
181
|
+
setCapabilities: rule("partial", VENDORED),
|
|
182
|
+
resetCapabilitiesCache: rule("partial", VENDORED),
|
|
183
|
+
getCellDimensions: rule("partial", VENDORED),
|
|
184
|
+
setCellDimensions: rule("partial", VENDORED),
|
|
185
|
+
hyperlink: rule("full", VENDORED),
|
|
186
|
+
imageFallback: rule("full", VENDORED),
|
|
187
|
+
CombinedAutocompleteProvider: rule("partial", `${VENDORED} Suggestions surface only if a DSH UI asks for them.`),
|
|
188
|
+
Marked: rule("full", "Re-exported from the same marked dependency Pi uses."),
|
|
189
|
+
CURSOR_MARKER: rule("full", "Pi's exact APC marker; vendored width math treats it as zero-width."),
|
|
190
|
+
Text: rule("partial", HEADLESS_COMPONENT),
|
|
191
|
+
Spacer: rule("partial", HEADLESS_COMPONENT),
|
|
192
|
+
Container: rule("partial", HEADLESS_COMPONENT),
|
|
193
|
+
Box: rule("partial", HEADLESS_COMPONENT),
|
|
194
|
+
Markdown: rule("partial", HEADLESS_COMPONENT),
|
|
195
|
+
TruncatedText: rule("partial", HEADLESS_COMPONENT),
|
|
196
|
+
Editor: rule("partial", `${HEADLESS_COMPONENT} Text editing state works; interactive keyboard flows do not.`),
|
|
197
|
+
Input: rule("partial", `${HEADLESS_COMPONENT} Value state and submit/escape callbacks work; kill-ring editing does not.`),
|
|
198
|
+
SelectList: rule("partial", `${HEADLESS_COMPONENT} Filtering and selection state work; keyboard interaction does not.`),
|
|
199
|
+
SettingsList: rule("partial", `${HEADLESS_COMPONENT} Value updates work; keyboard interaction does not.`),
|
|
200
|
+
ScrollView: rule("partial", HEADLESS_COMPONENT),
|
|
201
|
+
VStack: rule("partial", HEADLESS_COMPONENT),
|
|
202
|
+
HStack: rule("partial", HEADLESS_COMPONENT),
|
|
203
|
+
Loader: rule("partial", HEADLESS_COMPONENT),
|
|
204
|
+
CancellableLoader: rule("partial", HEADLESS_COMPONENT),
|
|
205
|
+
Image: rule("partial", `${HEADLESS_COMPONENT} Renders a text placeholder; image bytes flow through DSH attachments instead.`),
|
|
206
|
+
isFocusable: rule("full", "Structural check preserved."),
|
|
207
|
+
isViewportTUI: rule("partial", "Always false: no viewport TUI exists in DSH surfaces.")
|
|
208
|
+
}),
|
|
209
|
+
"pi-ai": Object.freeze({
|
|
210
|
+
StringEnum: rule("full", "Preserves Pi flat string-enum JSON Schema generation without loading provider SDKs."),
|
|
211
|
+
registerProvider: rule("partial", "Recorded in a bridge-local registry; DSH llm adapters own real routing."),
|
|
212
|
+
getProviders: rule("partial", "Returns the bridge-local registry contents."),
|
|
213
|
+
getProvider: rule("partial", "Reads the bridge-local registry."),
|
|
214
|
+
getModel: rule("partial", "Resolves no Pi model objects; DSH owns model routing."),
|
|
215
|
+
getModels: rule("partial", "Returns an empty list; DSH owns model routing."),
|
|
216
|
+
complete: rule("unsupported", "Pi provider SDK calls have no DSH mapping; calling fails explicitly."),
|
|
217
|
+
stream: rule("unsupported", "Pi provider SDK calls have no DSH mapping; calling fails explicitly."),
|
|
218
|
+
Type: rule("full", "Re-exported from the same typebox dependency Pi resolves for extensions."),
|
|
219
|
+
uuidv7: rule("full", VENDORED),
|
|
220
|
+
isContextOverflow: rule("full", VENDORED),
|
|
221
|
+
isRecoverableLength: rule("full", VENDORED),
|
|
222
|
+
isRetryableAssistantError: rule("full", VENDORED),
|
|
223
|
+
contentText: rule("full", "Pi's text-block joiner, reimplemented with identical semantics."),
|
|
224
|
+
clampThinkingLevel: rule("full", "Pi's clamping walk over the extended thinking-level ladder."),
|
|
225
|
+
getSupportedThinkingLevels: rule("full", "Pi's thinkingLevelMap filter, preserved."),
|
|
226
|
+
modelsAreEqual: rule("full", "Id+provider equality, preserved.")
|
|
227
|
+
})
|
|
228
|
+
});
|
|
229
|
+
const CONTEXT_RULES = Object.freeze({
|
|
230
|
+
cwd: rule("full", "Mapped to the active DSH agent session working directory."),
|
|
231
|
+
signal: rule("full", "Mapped to the active DSH cancellation signal when one is available."),
|
|
232
|
+
hasUI: rule("full", "Reports whether the native DSH userQuestions service is available to back Pi dialogs."),
|
|
233
|
+
mode: rule("partial", "Reports rpc mode so Pi extensions can choose their documented headless fallback."),
|
|
234
|
+
isIdle: rule("partial", "Command contexts report idle; tool/lifecycle contexts conservatively report non-idle."),
|
|
235
|
+
isProjectTrusted: rule("partial", "Fails closed as untrusted because DSH does not expose Pi project-trust state."),
|
|
236
|
+
hasPendingMessages: rule("partial", "Conservatively reports no Pi-specific pending-message queue."),
|
|
237
|
+
getContextUsage: rule("partial", "Returns no Pi token-usage projection."),
|
|
238
|
+
getSystemPrompt: rule("full", "Returns the system prompt currently assembled by the bridge."),
|
|
239
|
+
getSystemPromptOptions: rule("partial", "Returns an empty Pi option projection in command contexts."),
|
|
240
|
+
waitForIdle: rule("partial", "Mapped to the DSH agent idle boundary when available."),
|
|
241
|
+
sessionManager: rule("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."),
|
|
242
|
+
modelRegistry: rule("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. 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."),
|
|
243
|
+
model: rule("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."),
|
|
244
|
+
scopedModels: rule("partial", "The projected model catalog (DSH llm directory plus package-registered providers)."),
|
|
245
|
+
hasConfiguredAuth: rule("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)."),
|
|
246
|
+
thinkingLevel: rule("partial", "Reflects the level recorded by setThinkingLevel(); applied as reasoningEffort on the next request."),
|
|
247
|
+
abort: rule("partial", "Mapped to agent.cancel({ kind: \"hook\" }) on the live DSH agent."),
|
|
248
|
+
shutdown: rule("unsupported", "A migrated package may not shut down the DSH host; calling fails explicitly."),
|
|
249
|
+
compact: rule("unsupported", "Pi compaction control requires a native DSH compaction integration; calling fails explicitly."),
|
|
250
|
+
newSession: rule("unsupported", "Session replacement belongs to the DSH host; calling fails explicitly."),
|
|
251
|
+
fork: rule("unsupported", "Pi entry-tree forking has no DSH equivalent (DSH fork is boundary-based); calling fails explicitly."),
|
|
252
|
+
navigateTree: rule("unsupported", "Pi tree navigation has no DSH equivalent; calling fails explicitly."),
|
|
253
|
+
switchSession: rule("unsupported", "Session switching belongs to the DSH host; calling fails explicitly."),
|
|
254
|
+
reload: rule("unsupported", "Extension reload belongs to the DSH host (HMR); calling fails explicitly.")
|
|
255
|
+
});
|
|
256
|
+
const UI_CONTEXT_RULES = Object.freeze({
|
|
257
|
+
notify: rule("full", "Captured as a command result when applicable and emitted through DSH logging."),
|
|
258
|
+
setStatus: rule("partial", "Accepted as a no-op because DSH owns status presentation."),
|
|
259
|
+
setWidget: rule("partial", "Accepted as a no-op because Pi terminal widgets cannot render in DSH."),
|
|
260
|
+
select: rule("full", "Mapped to one native DSH userQuestions single-select request."),
|
|
261
|
+
confirm: rule("full", "Mapped to one native DSH userQuestions Yes/No request."),
|
|
262
|
+
input: rule("full", "Mapped to one native DSH userQuestions free-text request."),
|
|
263
|
+
editor: rule("partial", "Mapped to one DSH userQuestions free-text request; multi-line editing UX is not emulated."),
|
|
264
|
+
custom: rule("partial", "Resolves undefined, exactly like Pi's own rpc mode; guarded fallbacks keep working."),
|
|
265
|
+
onTerminalInput: rule("partial", "Raw terminal input is absent; feature-detected listeners remain disabled."),
|
|
266
|
+
setWorkingMessage: rule("partial", "Accepted as a no-op; DSH owns progress presentation."),
|
|
267
|
+
setWorkingVisible: rule("partial", "Accepted as a no-op; DSH owns progress presentation."),
|
|
268
|
+
setWorkingIndicator: rule("partial", "Accepted as a no-op; DSH owns progress presentation."),
|
|
269
|
+
setHiddenThinkingLabel: rule("partial", "Accepted as a no-op; DSH owns thinking presentation."),
|
|
270
|
+
setFooter: rule("partial", "Accepted as a no-op; DSH owns footer presentation."),
|
|
271
|
+
setHeader: rule("partial", "Accepted as a no-op; DSH owns header presentation."),
|
|
272
|
+
setTitle: rule("partial", "Accepted as a no-op; DSH owns window titles."),
|
|
273
|
+
pasteToEditor: rule("partial", "Appends to a per-agent editor buffer readable through getEditorText()."),
|
|
274
|
+
setEditorText: rule("partial", "Stored in a per-agent editor buffer readable through getEditorText()."),
|
|
275
|
+
getEditorText: rule("partial", "Reads the per-agent editor buffer maintained by the bridge."),
|
|
276
|
+
addAutocompleteProvider: rule("partial", "Registration is recorded; no DSH surface requests suggestions."),
|
|
277
|
+
setEditorComponent: rule("partial", "Registration is recorded; no DSH surface mounts a Pi editor component."),
|
|
278
|
+
getEditorComponent: rule("partial", "Returns the recorded factory."),
|
|
279
|
+
theme: rule("partial", "A headless theme whose styling calls return unstyled text."),
|
|
280
|
+
getAllThemes: rule("partial", "Lists the single headless theme."),
|
|
281
|
+
getTheme: rule("partial", "Resolves only the headless theme."),
|
|
282
|
+
setTheme: rule("partial", "Accepts the headless theme; other names report an explicit error result."),
|
|
283
|
+
getToolsExpanded: rule("partial", "A bridge-local presentation flag."),
|
|
284
|
+
setToolsExpanded: rule("partial", "A bridge-local presentation flag.")
|
|
285
|
+
});
|
|
286
|
+
const API_RULES = Object.freeze({
|
|
287
|
+
registerTool: {
|
|
288
|
+
level: "partial",
|
|
289
|
+
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."
|
|
290
|
+
},
|
|
291
|
+
unregisterTool: {
|
|
292
|
+
level: "full",
|
|
293
|
+
detail: "Disposes the exact native DSH tool registration and removes it from the migrated package registry."
|
|
294
|
+
},
|
|
295
|
+
registerCommand: {
|
|
296
|
+
level: "partial",
|
|
297
|
+
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). ui.notify becomes the result, while interactive Pi TUI methods fail explicitly in headless DSH."
|
|
298
|
+
},
|
|
299
|
+
registerShortcut: {
|
|
300
|
+
level: "partial",
|
|
301
|
+
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."
|
|
302
|
+
},
|
|
303
|
+
registerFlag: {
|
|
304
|
+
level: "partial",
|
|
305
|
+
detail: "The declared default is available through getFlag; Pi process flags cannot be added to the DSH launcher."
|
|
306
|
+
},
|
|
307
|
+
getFlag: {
|
|
308
|
+
level: "partial",
|
|
309
|
+
detail: "Returns the migrated flag default because DSH cannot register the original Pi CLI flag."
|
|
310
|
+
},
|
|
311
|
+
registerProvider: {
|
|
312
|
+
level: "partial",
|
|
313
|
+
detail: "The provider declaration is recorded and introspectable; model calls stay on native DSH llm adapters and credentials, which own transports and secrets."
|
|
314
|
+
},
|
|
315
|
+
unregisterProvider: {
|
|
316
|
+
level: "partial",
|
|
317
|
+
detail: "Removes the recorded provider declaration."
|
|
318
|
+
},
|
|
319
|
+
registerMessageRenderer: {
|
|
320
|
+
level: "partial",
|
|
321
|
+
detail: "Registration is accepted; DSH owns presentation, so the renderer is never invoked — matching Pi's non-TUI surfaces."
|
|
322
|
+
},
|
|
323
|
+
registerEntryRenderer: {
|
|
324
|
+
level: "partial",
|
|
325
|
+
detail: "Registration is accepted; DSH owns presentation, so the renderer is never invoked — matching Pi's non-TUI surfaces."
|
|
326
|
+
},
|
|
327
|
+
registerMarkdownTransformer: {
|
|
328
|
+
level: "partial",
|
|
329
|
+
detail: "Registration is accepted; DSH owns presentation, so the transformer is never invoked — matching Pi's non-TUI surfaces."
|
|
330
|
+
},
|
|
331
|
+
sendMessage: {
|
|
332
|
+
level: "partial",
|
|
333
|
+
detail: "Mapped to native DSH inject/steer/followup delivery with honest plugin provenance; Pi display/details metadata awaits the custom session-entry seam."
|
|
334
|
+
},
|
|
335
|
+
sendUserMessage: {
|
|
336
|
+
level: "full",
|
|
337
|
+
detail: "Mapped to native DSH steer/followup delivery with text and attachment-backed image content."
|
|
338
|
+
},
|
|
339
|
+
appendEntry: {
|
|
340
|
+
level: "partial",
|
|
341
|
+
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."
|
|
342
|
+
},
|
|
343
|
+
setSessionName: {
|
|
344
|
+
level: "partial",
|
|
345
|
+
detail: "Persisted in the pi2dsh sidecar and announced through session_info_changed; DSH's own title events are also projected when present."
|
|
346
|
+
},
|
|
347
|
+
getSessionName: {
|
|
348
|
+
level: "partial",
|
|
349
|
+
detail: "Reads the sidecar-persisted session name."
|
|
350
|
+
},
|
|
351
|
+
setLabel: {
|
|
352
|
+
level: "partial",
|
|
353
|
+
detail: "Persisted in the pi2dsh sidecar and reflected by the sessionManager projection."
|
|
354
|
+
},
|
|
355
|
+
exec: {
|
|
356
|
+
level: "partial",
|
|
357
|
+
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."
|
|
358
|
+
},
|
|
359
|
+
getActiveTools: {
|
|
360
|
+
level: "partial",
|
|
361
|
+
detail: "Returns every tool visible in the current DSH agent scope, including native and migrated tools; scope-local tools follow DSH composition rules."
|
|
362
|
+
},
|
|
363
|
+
getAllTools: {
|
|
364
|
+
level: "partial",
|
|
365
|
+
detail: "Returns metadata for all tools visible in the current DSH scope, without Pi-specific prompt guidelines unavailable from DSH schemas."
|
|
366
|
+
},
|
|
367
|
+
setActiveTools: {
|
|
368
|
+
level: "partial",
|
|
369
|
+
detail: "Mapped to the active DSH agent scope through tools.restrict({ allow }), preserving per-agent global-tool visibility without mutating other agents; DSH scope-local tools remain visible by design."
|
|
370
|
+
},
|
|
371
|
+
getCommands: {
|
|
372
|
+
level: "partial",
|
|
373
|
+
detail: "Returns commands registered by this migrated Pi package, not every command visible in the DSH scope."
|
|
374
|
+
},
|
|
375
|
+
setModel: {
|
|
376
|
+
level: "partial",
|
|
377
|
+
detail: "Recorded as a per-agent override applied through the agent/request waterfall on the next model call; DSH remains authoritative for provider routing."
|
|
378
|
+
},
|
|
379
|
+
getThinkingLevel: {
|
|
380
|
+
level: "partial",
|
|
381
|
+
detail: "Returns the level recorded by setThinkingLevel (default off)."
|
|
382
|
+
},
|
|
383
|
+
setThinkingLevel: {
|
|
384
|
+
level: "partial",
|
|
385
|
+
detail: "Recorded per agent and applied as reasoningEffort through the agent/request waterfall; DSH validates the effort id at the request boundary."
|
|
386
|
+
},
|
|
387
|
+
events: {
|
|
388
|
+
level: "full",
|
|
389
|
+
detail: "Package-local Pi extension event-bus emit/on semantics are preserved for migrated extensions in the same bundle."
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
const OBSERVED_NEVER_FIRES = (moment) => ({
|
|
393
|
+
level: "partial",
|
|
394
|
+
detail: `Registration is accepted; ${moment} never occurs on DSH surfaces, so the handler never fires. Loading is unaffected.`
|
|
395
|
+
});
|
|
396
|
+
const EVENT_RULES = Object.freeze({
|
|
397
|
+
session_start: {
|
|
398
|
+
level: "full",
|
|
399
|
+
detail: "Mapped to agent/session-start."
|
|
400
|
+
},
|
|
401
|
+
session_shutdown: {
|
|
402
|
+
level: "full",
|
|
403
|
+
detail: "Mapped to agent disposal and plugin teardown with duplicate suppression."
|
|
404
|
+
},
|
|
405
|
+
session_info_changed: {
|
|
406
|
+
level: "partial",
|
|
407
|
+
detail: "Fired by setSessionName() and projected from DSH session/title events."
|
|
408
|
+
},
|
|
409
|
+
agent_start: {
|
|
410
|
+
level: "full",
|
|
411
|
+
detail: "Mapped to the DSH turn/start boundary."
|
|
412
|
+
},
|
|
413
|
+
agent_settled: {
|
|
414
|
+
level: "full",
|
|
415
|
+
detail: "Mapped to the DSH turn/end boundary."
|
|
416
|
+
},
|
|
417
|
+
turn_start: {
|
|
418
|
+
level: "full",
|
|
419
|
+
detail: "Mapped from durable turn/start events."
|
|
420
|
+
},
|
|
421
|
+
tool_execution_start: {
|
|
422
|
+
level: "full",
|
|
423
|
+
detail: "Mapped from durable tool/call events."
|
|
424
|
+
},
|
|
425
|
+
tool_execution_end: {
|
|
426
|
+
level: "full",
|
|
427
|
+
detail: "Mapped from finalized tools/result events."
|
|
428
|
+
},
|
|
429
|
+
tool_execution_update: {
|
|
430
|
+
level: "partial",
|
|
431
|
+
detail: "Fired from migrated Pi tools' own onUpdate callbacks; DSH-native tools expose no partial-result stream."
|
|
432
|
+
},
|
|
433
|
+
tool_call: {
|
|
434
|
+
level: "partial",
|
|
435
|
+
detail: "Blocking is supported, and in-place argument mutation reaches migrated Pi tools; mutating a DSH-native tool's arguments is rejected because DSH logs arguments before policy."
|
|
436
|
+
},
|
|
437
|
+
tool_result: {
|
|
438
|
+
level: "partial",
|
|
439
|
+
detail: "Text replacement and success-to-error blocking are supported; arbitrary details and error recovery are not."
|
|
440
|
+
},
|
|
441
|
+
before_agent_start: {
|
|
442
|
+
level: "full",
|
|
443
|
+
detail: "Fires at the turn's first pre-step with the real prompt text and image attachments; returned custom messages enter the turn beside the user message, and a returned systemPrompt overrides this turn's assembly."
|
|
444
|
+
},
|
|
445
|
+
agent_end: {
|
|
446
|
+
level: "partial",
|
|
447
|
+
detail: "The lifecycle boundary is mapped, but the reconstructed Pi message history is intentionally minimal."
|
|
448
|
+
},
|
|
449
|
+
turn_end: {
|
|
450
|
+
level: "partial",
|
|
451
|
+
detail: "The lifecycle boundary and tool results are mapped; the exact Pi final-message shape is not guaranteed."
|
|
452
|
+
},
|
|
453
|
+
message_start: {
|
|
454
|
+
level: "partial",
|
|
455
|
+
detail: "Durable user, assistant, and tool-result messages are mapped without Pi-specific provider metadata."
|
|
456
|
+
},
|
|
457
|
+
message_end: {
|
|
458
|
+
level: "partial",
|
|
459
|
+
detail: "Durable messages are observed, but message replacement is not supported."
|
|
460
|
+
},
|
|
461
|
+
message_update: {
|
|
462
|
+
level: "partial",
|
|
463
|
+
detail: "Projected from DSH assistant/chunk events with accumulated text; Pi's full AgentMessage accumulation state is approximated."
|
|
464
|
+
},
|
|
465
|
+
session_before_compact: {
|
|
466
|
+
level: "partial",
|
|
467
|
+
detail: "Projected from DSH compaction/start as a notification; cancel/replace cannot reach DSH's compactor."
|
|
468
|
+
},
|
|
469
|
+
session_compact: {
|
|
470
|
+
level: "partial",
|
|
471
|
+
detail: "Projected from DSH compaction summary/end events."
|
|
472
|
+
},
|
|
473
|
+
model_select: {
|
|
474
|
+
level: "partial",
|
|
475
|
+
detail: "Fired by setModel() and projected from request/header model changes in the durable log."
|
|
476
|
+
},
|
|
477
|
+
thinking_level_select: {
|
|
478
|
+
level: "partial",
|
|
479
|
+
detail: "Fired by setThinkingLevel(); DSH-side reasoning changes surface through request/header projection."
|
|
480
|
+
},
|
|
481
|
+
context: {
|
|
482
|
+
level: "partial",
|
|
483
|
+
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."
|
|
484
|
+
},
|
|
485
|
+
before_provider_request: {
|
|
486
|
+
level: "unsupported",
|
|
487
|
+
detail: "Provider payload mutation belongs in a native DSH LLM adapter; the handler is accepted but never fires."
|
|
488
|
+
},
|
|
489
|
+
before_provider_headers: {
|
|
490
|
+
level: "unsupported",
|
|
491
|
+
detail: "Provider header mutation belongs in a native DSH LLM adapter; the handler is accepted but never fires."
|
|
492
|
+
},
|
|
493
|
+
after_provider_response: {
|
|
494
|
+
level: "unsupported",
|
|
495
|
+
detail: "Provider response interception belongs in a native DSH LLM adapter; the handler is accepted but never fires."
|
|
496
|
+
},
|
|
497
|
+
user_bash: OBSERVED_NEVER_FIRES("Pi's ! command surface"),
|
|
498
|
+
input: OBSERVED_NEVER_FIRES("raw Pi terminal input"),
|
|
499
|
+
project_trust: {
|
|
500
|
+
level: "unsupported",
|
|
501
|
+
detail: "Project trust must remain owned by the DSH host; the handler is accepted but never consulted."
|
|
502
|
+
},
|
|
503
|
+
resources_discover: {
|
|
504
|
+
level: "unsupported",
|
|
505
|
+
detail: "Dynamic resource discovery must be converted into DSH providers; the handler is accepted but never fires."
|
|
506
|
+
},
|
|
507
|
+
session_before_switch: OBSERVED_NEVER_FIRES("Pi session switching"),
|
|
508
|
+
session_before_fork: OBSERVED_NEVER_FIRES("Pi tree forking"),
|
|
509
|
+
session_before_tree: OBSERVED_NEVER_FIRES("Pi session-tree navigation"),
|
|
510
|
+
session_tree: OBSERVED_NEVER_FIRES("Pi session-tree navigation")
|
|
511
|
+
});
|
|
512
|
+
function ruleForApi(method) {
|
|
513
|
+
return API_RULES[method];
|
|
514
|
+
}
|
|
515
|
+
function ruleForEvent(event) {
|
|
516
|
+
return EVENT_RULES[event] ?? {
|
|
517
|
+
level: "unsupported",
|
|
518
|
+
detail: `Unknown Pi event ${JSON.stringify(event)} has no verified DSH mapping.`
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function ruleForHostImport(packageName, importedName) {
|
|
522
|
+
const family = PI_CODING_AGENT_PACKAGES.includes(packageName) ? "pi-coding-agent" : PI_TUI_PACKAGES.includes(packageName) ? "pi-tui" : PI_AI_PACKAGES.includes(packageName) ? "pi-ai" : void 0;
|
|
523
|
+
return family === void 0 ? void 0 : HOST_IMPORT_RULES[family]?.[importedName];
|
|
524
|
+
}
|
|
525
|
+
function ruleForContextProperty(property) {
|
|
526
|
+
return CONTEXT_RULES[property];
|
|
527
|
+
}
|
|
528
|
+
function ruleForUiContextProperty(property) {
|
|
529
|
+
return UI_CONTEXT_RULES[property];
|
|
530
|
+
}
|
|
531
|
+
//#endregion
|
|
532
|
+
//#region src/mcp-config.ts
|
|
533
|
+
/** Pi's documented config precedence, lowest to highest. */
|
|
534
|
+
function piMcpConfigPaths(cwd) {
|
|
535
|
+
const home = homedir();
|
|
536
|
+
return [
|
|
537
|
+
join(home, ".config", "mcp", "mcp.json"),
|
|
538
|
+
join(home, ".agents", "mcp.json"),
|
|
539
|
+
join(home, ".agents", "mcp", "mcp.json"),
|
|
540
|
+
join(getAgentDir(), "mcp.json"),
|
|
541
|
+
join(cwd, ".mcp.json"),
|
|
542
|
+
join(cwd, ".pi", "mcp.json")
|
|
543
|
+
];
|
|
544
|
+
}
|
|
545
|
+
function stringRecord(value) {
|
|
546
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
547
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
548
|
+
return entries.length === 0 ? void 0 : Object.fromEntries(entries);
|
|
549
|
+
}
|
|
550
|
+
function parseServer(name, raw, sourcePath) {
|
|
551
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
552
|
+
const record = raw;
|
|
553
|
+
const disabled = record.disabled === true;
|
|
554
|
+
if (typeof record.url === "string") return {
|
|
555
|
+
name,
|
|
556
|
+
sourcePath,
|
|
557
|
+
transport: "streamable-http",
|
|
558
|
+
url: record.url,
|
|
559
|
+
...stringRecord(record.headers) === void 0 ? {} : { headers: stringRecord(record.headers) },
|
|
560
|
+
disabled
|
|
561
|
+
};
|
|
562
|
+
if (typeof record.command === "string") return {
|
|
563
|
+
name,
|
|
564
|
+
sourcePath,
|
|
565
|
+
transport: "stdio",
|
|
566
|
+
command: record.command,
|
|
567
|
+
...Array.isArray(record.args) ? { args: record.args.map(String) } : {},
|
|
568
|
+
...stringRecord(record.env) === void 0 ? {} : { env: stringRecord(record.env) },
|
|
569
|
+
...typeof record.cwd === "string" ? { cwd: record.cwd } : {},
|
|
570
|
+
disabled
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function collectPiMcpServers(cwd, extraPaths = []) {
|
|
574
|
+
const servers = /* @__PURE__ */ new Map();
|
|
575
|
+
const sources = [];
|
|
576
|
+
for (const path of [...piMcpConfigPaths(cwd), ...extraPaths]) {
|
|
577
|
+
if (!existsSync(path)) continue;
|
|
578
|
+
let parsed;
|
|
579
|
+
try {
|
|
580
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
581
|
+
} catch {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const mcpServers = parsed?.mcpServers;
|
|
585
|
+
if (typeof mcpServers !== "object" || mcpServers === null) continue;
|
|
586
|
+
sources.push(path);
|
|
587
|
+
for (const [name, raw] of Object.entries(mcpServers)) {
|
|
588
|
+
const record = raw;
|
|
589
|
+
const existing = servers.get(name);
|
|
590
|
+
if (existing !== void 0 && typeof record === "object" && record !== null && record.command === void 0 && record.url === void 0 && typeof record.disabled === "boolean") {
|
|
591
|
+
servers.set(name, {
|
|
592
|
+
...existing,
|
|
593
|
+
disabled: record.disabled,
|
|
594
|
+
sourcePath: path
|
|
595
|
+
});
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
const server = parseServer(name, raw, path);
|
|
599
|
+
if (server !== void 0) servers.set(name, server);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
servers,
|
|
604
|
+
sources
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
const SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]{1,32}$/u;
|
|
608
|
+
const ENV_REFERENCE_PATTERN = /^\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?$/u;
|
|
609
|
+
const SECRETISH_KEY_PATTERN = /token|secret|key|password|credential/iu;
|
|
610
|
+
function js(expression) {
|
|
611
|
+
return { __pi2dshJs: expression };
|
|
612
|
+
}
|
|
613
|
+
function convertValueMap(server, kind, values, warnings) {
|
|
614
|
+
if (values === void 0) return void 0;
|
|
615
|
+
const output = {};
|
|
616
|
+
for (const [key, value] of Object.entries(values)) {
|
|
617
|
+
const reference = ENV_REFERENCE_PATTERN.exec(value);
|
|
618
|
+
if (reference !== null) {
|
|
619
|
+
output[key] = js(`process.env.${reference[1]}`);
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (SECRETISH_KEY_PATTERN.test(key)) warnings.push({
|
|
623
|
+
server,
|
|
624
|
+
message: `${kind}.${key} carries a literal value; move the secret to an environment variable and reference it as $NAME so the patch stays credential-free`
|
|
625
|
+
});
|
|
626
|
+
output[key] = value;
|
|
627
|
+
}
|
|
628
|
+
return output;
|
|
629
|
+
}
|
|
630
|
+
function convertPiMcpConfig(cwd, extraPaths = []) {
|
|
631
|
+
const { servers, sources } = collectPiMcpServers(cwd, extraPaths);
|
|
632
|
+
const warnings = [];
|
|
633
|
+
const entries = [];
|
|
634
|
+
for (const server of servers.values()) {
|
|
635
|
+
if (server.disabled) {
|
|
636
|
+
warnings.push({
|
|
637
|
+
server: server.name,
|
|
638
|
+
message: "skipped: disabled in Pi configuration"
|
|
639
|
+
});
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
if (!SERVER_NAME_PATTERN.test(server.name)) {
|
|
643
|
+
warnings.push({
|
|
644
|
+
server: server.name,
|
|
645
|
+
message: "skipped: DSH serverName must match [A-Za-z0-9_-]{1,32}; rename the server in the Pi config"
|
|
646
|
+
});
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
const config = {
|
|
650
|
+
serverName: server.name,
|
|
651
|
+
transport: server.transport
|
|
652
|
+
};
|
|
653
|
+
if (server.transport === "stdio") {
|
|
654
|
+
config.command = server.command;
|
|
655
|
+
if (server.args !== void 0) config.args = server.args;
|
|
656
|
+
const env = convertValueMap(server.name, "env", server.env, warnings);
|
|
657
|
+
if (env !== void 0) config.env = env;
|
|
658
|
+
if (server.cwd !== void 0) config.cwd = server.cwd;
|
|
659
|
+
} else {
|
|
660
|
+
config.url = server.url;
|
|
661
|
+
const headers = convertValueMap(server.name, "headers", server.headers, warnings);
|
|
662
|
+
if (headers !== void 0) config.headers = headers;
|
|
663
|
+
}
|
|
664
|
+
entries.push({
|
|
665
|
+
id: `mcp-${server.name}`,
|
|
666
|
+
name: "@deepseek-ai/dsh-mcp-client",
|
|
667
|
+
config
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
servers: [...servers.values()],
|
|
672
|
+
entries,
|
|
673
|
+
warnings,
|
|
674
|
+
sources
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
function yamlScalar(value, indent) {
|
|
678
|
+
if (typeof value === "object" && value !== null && "__pi2dshJs" in value) return `!!js ${value.__pi2dshJs}`;
|
|
679
|
+
if (typeof value === "string") return /^[A-Za-z0-9@._\/:-]+$/u.test(value) ? value : JSON.stringify(value);
|
|
680
|
+
if (Array.isArray(value)) {
|
|
681
|
+
if (value.length === 0) return "[]";
|
|
682
|
+
return `\n${value.map((item) => `${indent} - ${yamlScalar(item, `${indent} `)}`).join("\n")}`;
|
|
683
|
+
}
|
|
684
|
+
if (typeof value === "object" && value !== null) {
|
|
685
|
+
const record = value;
|
|
686
|
+
const keys = Object.keys(record);
|
|
687
|
+
if (keys.length === 0) return "{}";
|
|
688
|
+
return `\n${keys.map((key) => `${indent} ${key}: ${yamlScalar(record[key], `${indent} `)}`).join("\n")}`;
|
|
689
|
+
}
|
|
690
|
+
return String(value);
|
|
691
|
+
}
|
|
692
|
+
/** Render the converted entries as a cordis.patch.yml `insert` block. */
|
|
693
|
+
function renderMcpPatch(result) {
|
|
694
|
+
if (result.entries.length === 0) return "# no enabled MCP servers found in Pi configuration\n";
|
|
695
|
+
const lines = ["- insert:"];
|
|
696
|
+
for (const entry of result.entries) {
|
|
697
|
+
lines.push(` - id: ${String(entry.id)}`);
|
|
698
|
+
lines.push(` name: '@deepseek-ai/dsh-mcp-client'`);
|
|
699
|
+
lines.push(` inject: [tools]`);
|
|
700
|
+
lines.push(` config:${yamlScalar(entry.config, " ")}`);
|
|
701
|
+
}
|
|
702
|
+
return `${lines.join("\n")}\n`;
|
|
703
|
+
}
|
|
704
|
+
//#endregion
|
|
705
|
+
export { CONTEXT_RULES as a, PI_AI_PACKAGES as c, UI_CONTEXT_RULES as d, ruleForApi as f, ruleForUiContextProperty as g, ruleForHostImport as h, API_RULES as i, PI_CODING_AGENT_PACKAGES as l, ruleForEvent as m, convertPiMcpConfig as n, EVENT_RULES as o, ruleForContextProperty as p, renderMcpPatch as r, HOST_IMPORT_RULES as s, collectPiMcpServers as t, PI_TUI_PACKAGES as u };
|
|
706
|
+
|
|
707
|
+
//# sourceMappingURL=mcp-config-Dlou-ZA-.mjs.map
|