@oh-my-pi/pi-coding-agent 17.2.10 → 17.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
- package/dist/cli.js +10775 -10762
- package/dist/types/advisor/runtime.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -1
- package/dist/types/config/custom-models.d.ts +31 -0
- package/dist/types/config/model-config-values.d.ts +19 -0
- package/dist/types/config/model-patch.d.ts +93 -0
- package/dist/types/config/model-provider-discovery.d.ts +51 -0
- package/dist/types/config/model-registry.d.ts +9 -52
- package/dist/types/config/settings-schema.d.ts +1 -34
- package/dist/types/config/settings.d.ts +5 -3
- package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
- package/dist/types/discovery/agent-plugin-format.d.ts +136 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +33 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/eval/executor-base.d.ts +8 -2
- package/dist/types/eval/kernel-session-registry.d.ts +60 -0
- package/dist/types/export/share.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- package/dist/types/lsp/diagnostics.d.ts +96 -0
- package/dist/types/lsp/index.d.ts +7 -128
- package/dist/types/lsp/servers.d.ts +72 -0
- package/dist/types/lsp/tool.d.ts +42 -0
- package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
- package/dist/types/lsp/writethrough.d.ts +33 -0
- package/dist/types/mcp/transports/header-policy.d.ts +46 -0
- package/dist/types/mcp/types.d.ts +15 -0
- package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
- package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
- package/dist/types/modes/components/agent-hub.d.ts +2 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/theme/color.d.ts +19 -0
- package/dist/types/modes/theme/loader.d.ts +19 -0
- package/dist/types/modes/theme/schema.d.ts +175 -0
- package/dist/types/modes/theme/symbols.d.ts +28 -0
- package/dist/types/modes/theme/theme-class.d.ts +244 -0
- package/dist/types/modes/theme/theme.d.ts +9 -280
- package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +1 -3
- package/dist/types/secrets/index.d.ts +26 -2
- package/dist/types/secrets/message-transform.d.ts +40 -0
- package/dist/types/secrets/obfuscator.d.ts +0 -108
- package/dist/types/secrets/placeholder-scan.d.ts +95 -0
- package/dist/types/secrets/placeholder.d.ts +94 -0
- package/dist/types/secrets/replacement.d.ts +82 -0
- package/dist/types/session/agent-session-events.d.ts +2 -2
- package/dist/types/session/agent-session-types.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -1
- package/dist/types/session/session-handoff.d.ts +3 -3
- package/dist/types/session/session-manager.d.ts +32 -0
- package/dist/types/session/session-provider-boundary.d.ts +1 -1
- package/dist/types/session/session-tools.d.ts +8 -0
- package/dist/types/session/turn-recovery.d.ts +9 -4
- package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
- package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
- package/dist/types/slash-commands/builtin-control.d.ts +2 -0
- package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
- package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
- package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/slash-commands/builtin-session.d.ts +2 -0
- package/dist/types/task/index.d.ts +2 -0
- package/dist/types/tools/gh-common.d.ts +69 -0
- package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
- package/dist/types/tools/gh-pr-diff.d.ts +102 -0
- package/dist/types/tools/gh-run-watch.d.ts +78 -0
- package/dist/types/tools/gh-search.d.ts +61 -0
- package/dist/types/tools/gh-types.d.ts +340 -0
- package/dist/types/tools/gh-view.d.ts +88 -0
- package/dist/types/tools/gh.d.ts +4 -201
- package/dist/types/tools/read-archive.d.ts +16 -0
- package/dist/types/tools/read-format.d.ts +104 -0
- package/dist/types/tools/read-path-resolution.d.ts +19 -0
- package/dist/types/tools/read-pdf-images.d.ts +12 -0
- package/dist/types/tools/read-renderer.d.ts +24 -0
- package/dist/types/tools/read-selector.d.ts +27 -0
- package/dist/types/tools/read-sqlite.d.ts +16 -0
- package/dist/types/tools/read-summary.d.ts +19 -0
- package/dist/types/tools/read.d.ts +1 -23
- package/dist/types/tools/shell-tokenize.d.ts +18 -1
- package/dist/types/utils/changelog.d.ts +0 -5
- package/package.json +13 -13
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +12 -7
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/command-help.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/share.ts +71 -0
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/custom-models.ts +188 -0
- package/src/config/model-config-values.ts +128 -0
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-patch.ts +252 -0
- package/src/config/model-provider-discovery.ts +132 -0
- package/src/config/model-registry.ts +84 -704
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +53 -3
- package/src/debug/raw-sse-buffer.ts +20 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +75 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- package/src/edit/hashline/diff.ts +5 -1
- package/src/eval/executor-base.ts +39 -6
- package/src/eval/jl/executor.ts +82 -371
- package/src/eval/kernel-session-registry.ts +398 -0
- package/src/eval/py/executor.ts +53 -261
- package/src/eval/rb/executor.ts +36 -279
- package/src/exec/non-interactive-env.ts +1 -0
- package/src/export/share.ts +2 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +78 -14
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +12 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- package/src/lsp/diagnostics.ts +516 -0
- package/src/lsp/index.ts +20 -2819
- package/src/lsp/servers.ts +296 -0
- package/src/lsp/tool.ts +1352 -0
- package/src/lsp/workspace-diagnostics.ts +170 -0
- package/src/lsp/writethrough.ts +561 -0
- package/src/main.ts +3 -2
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +15 -4
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +2 -2
- package/src/modes/components/agent-hub-renderer.ts +3 -7
- package/src/modes/components/agent-hub.ts +5 -0
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +6 -4
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +8 -2
- package/src/modes/controllers/event-controller.ts +15 -15
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/theme/color.ts +133 -0
- package/src/modes/theme/loader.ts +178 -0
- package/src/modes/theme/schema.ts +263 -0
- package/src/modes/theme/symbols.ts +1000 -0
- package/src/modes/theme/theme-class.ts +611 -0
- package/src/modes/theme/theme.ts +27 -2453
- package/src/modes/theme/tui-adapters.ts +279 -0
- package/src/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/registry/agent-registry.ts +2 -2
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +51 -6
- package/src/secrets/message-transform.ts +287 -0
- package/src/secrets/obfuscator.ts +39 -1303
- package/src/secrets/placeholder-scan.ts +506 -0
- package/src/secrets/placeholder.ts +309 -0
- package/src/secrets/replacement.ts +216 -0
- package/src/session/agent-session-events.ts +2 -2
- package/src/session/agent-session-types.ts +6 -0
- package/src/session/agent-session.ts +146 -21
- package/src/session/indexed-session-storage.ts +7 -2
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-advisors.ts +32 -34
- package/src/session/session-context.ts +3 -5
- package/src/session/session-handoff.ts +39 -16
- package/src/session/session-manager.ts +67 -3
- package/src/session/session-provider-boundary.ts +3 -6
- package/src/session/session-tools.ts +37 -3
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +115 -84
- package/src/slash-commands/builtin-collaboration.ts +504 -0
- package/src/slash-commands/builtin-completions.ts +291 -0
- package/src/slash-commands/builtin-control.ts +78 -0
- package/src/slash-commands/builtin-lifecycle.ts +506 -0
- package/src/slash-commands/builtin-marketplace.ts +567 -0
- package/src/slash-commands/builtin-modes.ts +518 -0
- package/src/slash-commands/builtin-registry.ts +21 -2996
- package/src/slash-commands/builtin-session.ts +592 -0
- package/src/task/executor.ts +17 -14
- package/src/task/index.ts +21 -6
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +10 -11
- package/src/tools/debug.ts +1 -1
- package/src/tools/gh-common.ts +288 -0
- package/src/tools/gh-pr-checkout.ts +679 -0
- package/src/tools/gh-pr-diff.ts +473 -0
- package/src/tools/gh-run-watch.ts +1015 -0
- package/src/tools/gh-search.ts +500 -0
- package/src/tools/gh-types.ts +379 -0
- package/src/tools/gh-view.ts +612 -0
- package/src/tools/gh.ts +109 -3821
- package/src/tools/jtd-to-json-schema.ts +123 -21
- package/src/tools/read-archive.ts +209 -0
- package/src/tools/read-format.ts +593 -0
- package/src/tools/read-path-resolution.ts +36 -0
- package/src/tools/read-pdf-images.ts +250 -0
- package/src/tools/read-renderer.ts +289 -0
- package/src/tools/read-selector.ts +84 -0
- package/src/tools/read-sqlite.ts +215 -0
- package/src/tools/read-summary.ts +199 -0
- package/src/tools/read.ts +82 -1824
- package/src/tools/shell-tokenize.ts +99 -1
- package/src/utils/changelog.ts +1 -1
- package/src/utils/title-generator.ts +3 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/zai.ts +12 -3
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type HighlightColors as NativeHighlightColors,
|
|
3
|
+
highlightCode as nativeHighlightCode,
|
|
4
|
+
supportsLanguage as nativeSupportsLanguage,
|
|
5
|
+
} from "@oh-my-pi/pi-natives";
|
|
6
|
+
import type { EditorTheme, MarkdownTheme, SelectListTheme, SettingsListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
|
|
7
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
8
|
+
import { LRUCache } from "@oh-my-pi/pi-utils/lru";
|
|
9
|
+
import { resolveMermaidAscii } from "./mermaid-cache";
|
|
10
|
+
import { theme } from "./theme";
|
|
11
|
+
import type { Theme } from "./theme-class";
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// TUI Helpers
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
let cachedHighlightColorsFor: Theme | undefined;
|
|
18
|
+
let cachedHighlightColors: NativeHighlightColors | undefined;
|
|
19
|
+
|
|
20
|
+
function getHighlightColors(t: Theme): NativeHighlightColors {
|
|
21
|
+
if (cachedHighlightColorsFor !== t || !cachedHighlightColors) {
|
|
22
|
+
cachedHighlightColorsFor = t;
|
|
23
|
+
cachedHighlightColors = {
|
|
24
|
+
comment: t.getFgAnsi("syntaxComment"),
|
|
25
|
+
keyword: t.getFgAnsi("syntaxKeyword"),
|
|
26
|
+
function: t.getFgAnsi("syntaxFunction"),
|
|
27
|
+
variable: t.getFgAnsi("syntaxVariable"),
|
|
28
|
+
string: t.getFgAnsi("syntaxString"),
|
|
29
|
+
number: t.getFgAnsi("syntaxNumber"),
|
|
30
|
+
type: t.getFgAnsi("syntaxType"),
|
|
31
|
+
operator: t.getFgAnsi("syntaxOperator"),
|
|
32
|
+
punctuation: t.getFgAnsi("syntaxPunctuation"),
|
|
33
|
+
inserted: t.getFgAnsi("toolDiffAdded"),
|
|
34
|
+
deleted: t.getFgAnsi("toolDiffRemoved"),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return cachedHighlightColors;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Memoized native syntax highlight. Returns the joined ANSI string, or `null`
|
|
42
|
+
* when the native tokenizer throws so callers can apply their own fallback.
|
|
43
|
+
*
|
|
44
|
+
* Keyed on `(lang, code)` and reset whenever the active `theme` instance
|
|
45
|
+
* changes — the ANSI colors are baked into the highlighted output, so a theme
|
|
46
|
+
* switch (which always reassigns `theme`) must invalidate every entry.
|
|
47
|
+
*
|
|
48
|
+
* Why this exists: animated tool blocks (eval/bash) repaint their box on every
|
|
49
|
+
* ~33ms border-shimmer frame, and markdown re-lexes on every streamed delta.
|
|
50
|
+
* Without memoization each frame can re-tokenize an unchanged code body through
|
|
51
|
+
* the Rust FFI — ~26ms for 100 lines, ~40ms for 150 — consuming or overrunning
|
|
52
|
+
* the 33ms frame budget and starving the spinner/render timers (the "TUI freeze").
|
|
53
|
+
*/
|
|
54
|
+
const HIGHLIGHT_CACHE_MAX = 256;
|
|
55
|
+
const highlightCache = new LRUCache<string, string>({ max: HIGHLIGHT_CACHE_MAX });
|
|
56
|
+
let highlightCacheTheme: Theme | undefined;
|
|
57
|
+
|
|
58
|
+
function highlightCached(code: string, validLang: string | undefined, highlightTheme: Theme): string | null {
|
|
59
|
+
if (highlightCacheTheme !== highlightTheme) {
|
|
60
|
+
highlightCache.clear();
|
|
61
|
+
highlightCacheTheme = highlightTheme;
|
|
62
|
+
}
|
|
63
|
+
const key = `${validLang ?? ""}\x00${code}`;
|
|
64
|
+
const hit = highlightCache.get(key);
|
|
65
|
+
if (hit !== undefined) {
|
|
66
|
+
return hit;
|
|
67
|
+
}
|
|
68
|
+
let highlighted: string;
|
|
69
|
+
try {
|
|
70
|
+
highlighted = nativeHighlightCode(code, validLang, getHighlightColors(highlightTheme));
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
highlightCache.set(key, highlighted);
|
|
75
|
+
return highlighted;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Highlight code with syntax coloring based on file extension or language.
|
|
80
|
+
* Returns array of highlighted lines.
|
|
81
|
+
*/
|
|
82
|
+
export function highlightCode(code: string, lang?: string, highlightTheme: Theme = theme): string[] {
|
|
83
|
+
const validLang = lang && nativeSupportsLanguage(lang) ? lang : undefined;
|
|
84
|
+
const highlighted = highlightCached(code, validLang, highlightTheme);
|
|
85
|
+
// Always return a fresh array: callers (e.g. renderCodeCell) push extra lines
|
|
86
|
+
// onto the result, which would corrupt the cached string otherwise.
|
|
87
|
+
return (highlighted ?? code).split("\n");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getSymbolTheme(): SymbolTheme {
|
|
91
|
+
// Guard against `theme` being undefined (pre-init or cross-module-instance
|
|
92
|
+
// plugin calls). Fall back to the ASCII preset so the returned symbols are
|
|
93
|
+
// usable instead of crashing. See #2998.
|
|
94
|
+
if (typeof theme === "undefined") {
|
|
95
|
+
const box = {
|
|
96
|
+
topLeft: "+",
|
|
97
|
+
topRight: "+",
|
|
98
|
+
bottomLeft: "+",
|
|
99
|
+
bottomRight: "+",
|
|
100
|
+
horizontal: "-",
|
|
101
|
+
vertical: "|",
|
|
102
|
+
cross: "+",
|
|
103
|
+
teeDown: "+",
|
|
104
|
+
teeUp: "+",
|
|
105
|
+
teeLeft: "+",
|
|
106
|
+
teeRight: "+",
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
cursor: ">",
|
|
110
|
+
inputCursor: "|",
|
|
111
|
+
boxRound: box,
|
|
112
|
+
boxSharp: box,
|
|
113
|
+
table: box,
|
|
114
|
+
quoteBorder: "|",
|
|
115
|
+
hrChar: "-",
|
|
116
|
+
colorSwatch: "[]",
|
|
117
|
+
spinnerFrames: ["-", "\\", "|", "/"],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const preset = theme.getSymbolPreset();
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
cursor: theme.nav.cursor,
|
|
124
|
+
inputCursor: preset === "ascii" ? "|" : "▏",
|
|
125
|
+
boxRound: theme.boxRound,
|
|
126
|
+
boxSharp: theme.boxSharp,
|
|
127
|
+
table: theme.boxSharp,
|
|
128
|
+
quoteBorder: theme.md.quoteBorder,
|
|
129
|
+
hrChar: theme.md.hrChar,
|
|
130
|
+
colorSwatch: theme.md.colorSwatch,
|
|
131
|
+
spinnerFrames: theme.getSpinnerFrames("activity"),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let cachedMarkdownTheme: MarkdownTheme | undefined;
|
|
136
|
+
let cachedMarkdownThemeRef: Theme | undefined;
|
|
137
|
+
let markdownMermaidRendering = true;
|
|
138
|
+
|
|
139
|
+
export function setMarkdownMermaidRendering(enabled: boolean): void {
|
|
140
|
+
if (markdownMermaidRendering === enabled) return;
|
|
141
|
+
markdownMermaidRendering = enabled;
|
|
142
|
+
cachedMarkdownTheme = undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function getMarkdownTheme(): MarkdownTheme {
|
|
146
|
+
if (cachedMarkdownTheme !== undefined && cachedMarkdownThemeRef === theme) {
|
|
147
|
+
return cachedMarkdownTheme;
|
|
148
|
+
}
|
|
149
|
+
const mermaid = markdownMermaidRendering
|
|
150
|
+
? (() => {
|
|
151
|
+
// Mermaid ASCII diagrams render with the active palette so they read as
|
|
152
|
+
// content rather than raw monochrome. Roles mirror the SVG renderer's
|
|
153
|
+
// mapping; `text`/`muted`/`border`/`borderMuted`/`accent` exist in every theme.
|
|
154
|
+
const mermaidColorMode =
|
|
155
|
+
theme.getColorMode() === "truecolor" ? ("truecolor" as const) : ("ansi256" as const);
|
|
156
|
+
const mermaidTheme = {
|
|
157
|
+
fg: theme.getColorHex("text"),
|
|
158
|
+
border: theme.getColorHex("border"),
|
|
159
|
+
line: theme.getColorHex("muted"),
|
|
160
|
+
arrow: theme.getColorHex("accent"),
|
|
161
|
+
corner: theme.getColorHex("muted"),
|
|
162
|
+
junction: theme.getColorHex("borderMuted"),
|
|
163
|
+
};
|
|
164
|
+
return { mermaidColorMode, mermaidTheme };
|
|
165
|
+
})()
|
|
166
|
+
: undefined;
|
|
167
|
+
const markdownTheme: MarkdownTheme = {
|
|
168
|
+
heading: (text: string) => theme.fg("mdHeading", text),
|
|
169
|
+
link: (text: string) => theme.fg("mdLink", text),
|
|
170
|
+
linkUrl: (text: string) => theme.fg("mdLinkUrl", text),
|
|
171
|
+
code: (text: string) => theme.fg("mdCode", text),
|
|
172
|
+
codeBlock: (text: string) => theme.fg("mdCodeBlock", text),
|
|
173
|
+
codeBlockBorder: (text: string) => theme.fg("mdCodeBlockBorder", text),
|
|
174
|
+
quote: (text: string) => theme.fg("mdQuote", text),
|
|
175
|
+
quoteBorder: (text: string) => theme.fg("mdQuoteBorder", text),
|
|
176
|
+
hr: (text: string) => theme.fg("mdHr", text),
|
|
177
|
+
listBullet: (text: string) => theme.fg("mdListBullet", text),
|
|
178
|
+
bold: (text: string) => theme.bold(text),
|
|
179
|
+
italic: (text: string) => theme.italic(text),
|
|
180
|
+
underline: (text: string) => theme.underline(text),
|
|
181
|
+
strikethrough: (text: string) => chalk.strikethrough(text),
|
|
182
|
+
symbols: getSymbolTheme(),
|
|
183
|
+
resolveMermaidAscii: mermaid
|
|
184
|
+
? (source, maxWidth) =>
|
|
185
|
+
resolveMermaidAscii(source, {
|
|
186
|
+
maxWidth,
|
|
187
|
+
theme: mermaid.mermaidTheme,
|
|
188
|
+
colorMode: mermaid.mermaidColorMode,
|
|
189
|
+
})
|
|
190
|
+
: undefined,
|
|
191
|
+
highlightCode: (code: string, lang?: string): string[] => {
|
|
192
|
+
const validLang = lang && nativeSupportsLanguage(lang) ? lang : undefined;
|
|
193
|
+
const highlighted = highlightCached(code, validLang, theme);
|
|
194
|
+
if (highlighted !== null) return highlighted.split("\n");
|
|
195
|
+
return code.split("\n").map(line => theme.fg("mdCodeBlock", line));
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
cachedMarkdownTheme = markdownTheme;
|
|
199
|
+
cachedMarkdownThemeRef = theme;
|
|
200
|
+
return markdownTheme;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function getSelectListTheme(): SelectListTheme {
|
|
204
|
+
// Guard against `theme` being undefined (pre-init or cross-module-instance
|
|
205
|
+
// plugin calls). See #2998.
|
|
206
|
+
if (typeof theme === "undefined") {
|
|
207
|
+
return {
|
|
208
|
+
selectedPrefix: (text: string) => text,
|
|
209
|
+
selectedText: (text: string) => text,
|
|
210
|
+
description: (text: string) => text,
|
|
211
|
+
scrollInfo: (text: string) => text,
|
|
212
|
+
noMatch: (text: string) => text,
|
|
213
|
+
symbols: getSymbolTheme(),
|
|
214
|
+
hovered: (text: string) => text,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
selectedPrefix: (text: string) => theme.fg("accent", text),
|
|
219
|
+
selectedText: (text: string) => theme.fg("accent", text),
|
|
220
|
+
description: (text: string) => theme.fg("muted", text),
|
|
221
|
+
scrollInfo: (text: string) => theme.fg("muted", text),
|
|
222
|
+
noMatch: (text: string) => theme.fg("muted", text),
|
|
223
|
+
symbols: getSymbolTheme(),
|
|
224
|
+
hovered: (text: string) => theme.bg("selectedBg", text),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function getEditorTheme(): EditorTheme {
|
|
229
|
+
// Guard against `theme` being undefined (pre-init or cross-module-instance
|
|
230
|
+
// plugin calls). See #2998.
|
|
231
|
+
if (typeof theme === "undefined") {
|
|
232
|
+
return {
|
|
233
|
+
borderColor: (text: string) => text,
|
|
234
|
+
selectList: getSelectListTheme(),
|
|
235
|
+
symbols: getSymbolTheme(),
|
|
236
|
+
hintStyle: (text: string) => text,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
borderColor: (text: string) => theme.fg("borderMuted", text),
|
|
241
|
+
selectList: getSelectListTheme(),
|
|
242
|
+
symbols: getSymbolTheme(),
|
|
243
|
+
hintStyle: (text: string) => theme.fg("dim", text),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function getSettingsListTheme(): SettingsListTheme {
|
|
248
|
+
// Plugins (e.g. pi-rtk-optimizer) may call this before `initTheme()` assigns
|
|
249
|
+
// the global `theme`, or from a separate module instance under npm-global
|
|
250
|
+
// installs where the live binding was never initialized. Fall back to plain
|
|
251
|
+
// text so the call returns a usable (unstyled) theme instead of crashing with
|
|
252
|
+
// "undefined is not an object (evaluating 'theme.fg')". See #2998.
|
|
253
|
+
if (typeof theme === "undefined") {
|
|
254
|
+
return {
|
|
255
|
+
label: (text: string) => text,
|
|
256
|
+
value: (text: string) => text,
|
|
257
|
+
description: (text: string) => text,
|
|
258
|
+
cursor: "> ",
|
|
259
|
+
hint: (text: string) => text,
|
|
260
|
+
heading: (text: string) => text,
|
|
261
|
+
section: (text: string) => text,
|
|
262
|
+
hovered: (text: string) => text,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
label: (text: string, selected: boolean, changed: boolean) =>
|
|
267
|
+
changed ? theme.fg("statusLineGitDirty", text) : selected ? theme.fg("accent", text) : text,
|
|
268
|
+
value: (text: string, selected: boolean, changed: boolean) =>
|
|
269
|
+
changed ? theme.fg("statusLineGitDirty", text) : selected ? theme.fg("accent", text) : theme.fg("muted", text),
|
|
270
|
+
description: (text: string) => theme.fg("dim", text),
|
|
271
|
+
cursor: theme.fg("accent", `${theme.nav.cursor} `),
|
|
272
|
+
hint: (text: string) => theme.fg("dim", text),
|
|
273
|
+
heading: (text: string, dimmed: boolean) =>
|
|
274
|
+
dimmed ? theme.fg("dim", theme.underline(text)) : theme.fg("muted", theme.bold(theme.underline(text))),
|
|
275
|
+
section: (text: string, active: boolean) =>
|
|
276
|
+
active ? theme.fg("accent", theme.bold(text)) : theme.fg("muted", text),
|
|
277
|
+
hovered: (text: string) => theme.bg("selectedBg", text),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
@@ -211,13 +211,15 @@ function sanitizeRecoveredRetryNote(note: string): string {
|
|
|
211
211
|
|
|
212
212
|
/**
|
|
213
213
|
* Resolve the turn-ending assistant error presentation, if any.
|
|
214
|
-
* Silent and user-interrupt aborts yield no label. Recovered
|
|
215
|
-
*
|
|
214
|
+
* Silent and user-interrupt aborts yield no label. Recovered retry attempts
|
|
215
|
+
* render a compact note; attempts superseded by an exhausted budget are hidden
|
|
216
|
+
* while the final terminal error keeps its full presentation.
|
|
216
217
|
*/
|
|
217
218
|
export function resolveAssistantErrorPresentation(
|
|
218
219
|
message: AssistantAgentMessage,
|
|
219
220
|
retryAttempt = 0,
|
|
220
221
|
): AssistantErrorPresentation {
|
|
222
|
+
if (message.retryRecovery?.status === "superseded") return { kind: "none" };
|
|
221
223
|
if (message.retryRecovery?.status === "recovered") {
|
|
222
224
|
return {
|
|
223
225
|
kind: "compact-recovered",
|
|
@@ -500,6 +500,7 @@ export class UiHelpers {
|
|
|
500
500
|
content.name,
|
|
501
501
|
renderArgs,
|
|
502
502
|
{
|
|
503
|
+
useBuiltInRenderer: this.ctx.viewSession.hasBuiltInTool(content.name),
|
|
503
504
|
snapshots: getFileSnapshotStore(this.ctx.viewSession),
|
|
504
505
|
clipboard: getEditClipboard(this.ctx.viewSession),
|
|
505
506
|
showImages: settings.get("terminal.showImages"),
|
|
@@ -15,7 +15,7 @@ import { oneLineLabel } from "../task/types";
|
|
|
15
15
|
export const MAIN_AGENT_ID = "Main";
|
|
16
16
|
|
|
17
17
|
/** Sidecar marker retained beside a child transcript after an explicit kill. */
|
|
18
|
-
|
|
18
|
+
const AGENT_TOMBSTONE_SUFFIX = ".tombstone";
|
|
19
19
|
|
|
20
20
|
export function getAgentTombstonePath(sessionFile: string): string {
|
|
21
21
|
return `${sessionFile}${AGENT_TOMBSTONE_SUFFIX}`;
|
|
@@ -30,7 +30,7 @@ export function getAgentTombstonePath(sessionFile: string): string {
|
|
|
30
30
|
*/
|
|
31
31
|
export type AgentStatus = "running" | "idle" | "parked" | "aborted";
|
|
32
32
|
/** Provenance of a displayed duration: active runtime, transcript span, or unavailable. */
|
|
33
|
-
|
|
33
|
+
type AgentDurationKind = "active" | "span" | "unknown";
|
|
34
34
|
/**
|
|
35
35
|
* - `main`/`sub`: the user-facing agent tree (driving agent + task subagents).
|
|
36
36
|
* - `advisor`: a passive review transcript persisted like a subagent for usage
|
package/src/sdk.ts
CHANGED
|
@@ -123,18 +123,12 @@ import lateDiagnosticTemplate from "./prompts/tools/lsp-late-diagnostic.md" with
|
|
|
123
123
|
import { AgentLifecycleManager } from "./registry/agent-lifecycle";
|
|
124
124
|
import { type AgentRef, AgentRegistry, MAIN_AGENT_ID } from "./registry/agent-registry";
|
|
125
125
|
import {
|
|
126
|
-
|
|
127
|
-
collectEnvSecrets,
|
|
126
|
+
buildSecretObfuscator,
|
|
128
127
|
deobfuscateSessionContext,
|
|
129
128
|
deobfuscateToolArguments,
|
|
130
|
-
getExistingSecretPlaceholderKey,
|
|
131
|
-
getSecretPlaceholderKey,
|
|
132
|
-
getSecretPlaceholderKeySync,
|
|
133
|
-
loadSecrets,
|
|
134
129
|
obfuscateMessages,
|
|
135
130
|
obfuscateProviderContext,
|
|
136
|
-
SecretObfuscator,
|
|
137
|
-
secretEntriesNeedPlaceholderKey,
|
|
131
|
+
type SecretObfuscator,
|
|
138
132
|
} from "./secrets";
|
|
139
133
|
import { AgentSession, type InitialRetryFallbackState, type PlanYolo, type Prewalk } from "./session/agent-session";
|
|
140
134
|
import { discoverAuthStorage as discoverAuthStorageFromConfig } from "./session/auth-broker-config";
|
|
@@ -1039,7 +1033,7 @@ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
|
|
|
1039
1033
|
success: event.success,
|
|
1040
1034
|
attempt: event.attempt,
|
|
1041
1035
|
finalError: event.finalError,
|
|
1042
|
-
|
|
1036
|
+
retryErrors: event.retryErrors,
|
|
1043
1037
|
},
|
|
1044
1038
|
ctx,
|
|
1045
1039
|
),
|
|
@@ -1377,46 +1371,9 @@ async function createAgentSessionScoped(options: CreateAgentSessionOptions): Pro
|
|
|
1377
1371
|
|
|
1378
1372
|
// Load and create secret obfuscator early so resumed session state and prompt warnings
|
|
1379
1373
|
// reflect actual loaded secrets, not just the setting toggle.
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
const envEntries = collectEnvSecrets();
|
|
1384
|
-
// Built-in credential-pattern entries come last so user-configured entries
|
|
1385
|
-
// (plain literals, custom regexes) take precedence in the scan order.
|
|
1386
|
-
const allEntries = [...envEntries, ...fileEntries, ...builtinCredentialSecretEntries()];
|
|
1387
|
-
// Only CONFIGURED entries force startup key creation: a configured
|
|
1388
|
-
// obfuscate-mode secret — or a default (no custom `replacement`)
|
|
1389
|
-
// replace-mode regex whose key-derived idempotent fallback marker needs a
|
|
1390
|
-
// stable key across restarts (see `secretEntryNeedsPlaceholderKey`) —
|
|
1391
|
-
// mints placeholders as soon as the obfuscator is built. The built-in
|
|
1392
|
-
// credential-pattern entry matches dynamically, so it resolves the
|
|
1393
|
-
// persisted key lazily on first match instead of creating the key file
|
|
1394
|
-
// for every secrets.enabled session; a session whose content never
|
|
1395
|
-
// contains a credential-shaped token must not require the key, otherwise a
|
|
1396
|
-
// headless run on an unwritable default config root pays for a feature it
|
|
1397
|
-
// does not use.
|
|
1398
|
-
const needsPlaceholderKey = secretEntriesNeedPlaceholderKey([...envEntries, ...fileEntries]);
|
|
1399
|
-
const explicitAgentDir = options.agentDir;
|
|
1400
|
-
const placeholderKey = needsPlaceholderKey
|
|
1401
|
-
? await getSecretPlaceholderKey(explicitAgentDir)
|
|
1402
|
-
: await getExistingSecretPlaceholderKey(explicitAgentDir);
|
|
1403
|
-
if (allEntries.length > 0) {
|
|
1404
|
-
obfuscator = new SecretObfuscator(
|
|
1405
|
-
allEntries,
|
|
1406
|
-
placeholderKey ?? (() => getSecretPlaceholderKeySync(explicitAgentDir)),
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
if (obfuscator?.hasSecrets() !== true && placeholderKey !== undefined) {
|
|
1410
|
-
// No configured entry produced an active secret (e.g. only ignored short
|
|
1411
|
-
// plain entries, or no entries at all), but a persisted key exists. Build a
|
|
1412
|
-
// redaction-only obfuscator so a tool read of the key file does not ship the
|
|
1413
|
-
// reusable HMAC key to the provider.
|
|
1414
|
-
obfuscator = new SecretObfuscator(
|
|
1415
|
-
[{ type: "plain", mode: "replace", content: placeholderKey }],
|
|
1416
|
-
placeholderKey,
|
|
1417
|
-
);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1374
|
+
const obfuscator: SecretObfuscator | undefined = settings.get("secrets.enabled")
|
|
1375
|
+
? await buildSecretObfuscator(cwd, agentDir, options.agentDir)
|
|
1376
|
+
: undefined;
|
|
1420
1377
|
const secretsEnabled = obfuscator?.hasSecrets() === true;
|
|
1421
1378
|
|
|
1422
1379
|
// An abnormal process exit after a non-terminal message tail is durable
|
package/src/secrets/index.ts
CHANGED
|
@@ -4,8 +4,10 @@ import * as path from "node:path";
|
|
|
4
4
|
import { SENSITIVE_TOKEN_RE } from "@oh-my-pi/pi-ai/providers/transform-messages";
|
|
5
5
|
import { getSecretPlaceholderKeyPath, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
6
6
|
import { YAML } from "bun";
|
|
7
|
-
import {
|
|
7
|
+
import { type SecretEntry, SecretObfuscator } from "./obfuscator";
|
|
8
|
+
import { sanitizeSecretFriendlyName, secretEntriesNeedPlaceholderKey } from "./placeholder";
|
|
8
9
|
import { compileSecretRegex } from "./regex";
|
|
10
|
+
import { regexHasUnresolvableShortMatchFallback } from "./replacement";
|
|
9
11
|
|
|
10
12
|
const PLACEHOLDER_KEY_RE = /^[A-Za-z0-9_-]{43}$/;
|
|
11
13
|
const cachedPlaceholderKeys = new Map<string, string>();
|
|
@@ -152,11 +154,9 @@ export {
|
|
|
152
154
|
deobfuscateToolArguments,
|
|
153
155
|
obfuscateMessages,
|
|
154
156
|
obfuscateProviderContext,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
secretEntryNeedsPlaceholderKey,
|
|
159
|
-
} from "./obfuscator";
|
|
157
|
+
} from "./message-transform";
|
|
158
|
+
export { type SecretEntry, SecretObfuscator } from "./obfuscator";
|
|
159
|
+
export { secretEntriesNeedPlaceholderKey, secretEntryNeedsPlaceholderKey } from "./placeholder";
|
|
160
160
|
|
|
161
161
|
/**
|
|
162
162
|
* Load secrets from project-local and global secrets.yml files.
|
|
@@ -223,6 +223,51 @@ export function builtinCredentialSecretEntries(): SecretEntry[] {
|
|
|
223
223
|
];
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Build the session secret obfuscator from every configured source: secrets.yml
|
|
228
|
+
* (project + global), secret-shaped environment variables, and the built-in
|
|
229
|
+
* credential patterns. Callers gate on `secrets.enabled`.
|
|
230
|
+
*
|
|
231
|
+
* Only CONFIGURED entries force startup key creation: a configured
|
|
232
|
+
* obfuscate-mode secret — or a default (no custom `replacement`) replace-mode
|
|
233
|
+
* regex whose key-derived idempotent fallback marker needs a stable key across
|
|
234
|
+
* restarts (see `secretEntryNeedsPlaceholderKey`) — mints placeholders as soon
|
|
235
|
+
* as the obfuscator is built. The built-in credential-pattern entry matches
|
|
236
|
+
* dynamically, so it resolves the persisted key lazily on first match instead
|
|
237
|
+
* of creating the key file for every secrets-enabled session.
|
|
238
|
+
*
|
|
239
|
+
* When no configured entry produced an active secret but a persisted key
|
|
240
|
+
* exists, returns a redaction-only obfuscator so a tool read of the key file
|
|
241
|
+
* does not ship the reusable HMAC key to the provider. Returns undefined when
|
|
242
|
+
* there is nothing to protect.
|
|
243
|
+
*
|
|
244
|
+
* `keyDir` is the explicit agent dir override for the placeholder-key file
|
|
245
|
+
* (default XDG/agent location when omitted).
|
|
246
|
+
*/
|
|
247
|
+
export async function buildSecretObfuscator(
|
|
248
|
+
cwd: string,
|
|
249
|
+
agentDir: string,
|
|
250
|
+
keyDir?: string,
|
|
251
|
+
): Promise<SecretObfuscator | undefined> {
|
|
252
|
+
const fileEntries = await logger.time("loadSecrets", loadSecrets, cwd, agentDir);
|
|
253
|
+
const envEntries = collectEnvSecrets();
|
|
254
|
+
// Built-in credential-pattern entries come last so user-configured entries
|
|
255
|
+
// (plain literals, custom regexes) take precedence in the scan order.
|
|
256
|
+
const allEntries = [...envEntries, ...fileEntries, ...builtinCredentialSecretEntries()];
|
|
257
|
+
const needsPlaceholderKey = secretEntriesNeedPlaceholderKey([...envEntries, ...fileEntries]);
|
|
258
|
+
const placeholderKey = needsPlaceholderKey
|
|
259
|
+
? await getSecretPlaceholderKey(keyDir)
|
|
260
|
+
: await getExistingSecretPlaceholderKey(keyDir);
|
|
261
|
+
let obfuscator: SecretObfuscator | undefined;
|
|
262
|
+
if (allEntries.length > 0) {
|
|
263
|
+
obfuscator = new SecretObfuscator(allEntries, placeholderKey ?? (() => getSecretPlaceholderKeySync(keyDir)));
|
|
264
|
+
}
|
|
265
|
+
if (obfuscator?.hasSecrets() !== true && placeholderKey !== undefined) {
|
|
266
|
+
obfuscator = new SecretObfuscator([{ type: "plain", mode: "replace", content: placeholderKey }], placeholderKey);
|
|
267
|
+
}
|
|
268
|
+
return obfuscator;
|
|
269
|
+
}
|
|
270
|
+
|
|
226
271
|
async function loadSecretsFile(filePath: string): Promise<SecretEntry[]> {
|
|
227
272
|
try {
|
|
228
273
|
const text = await Bun.file(filePath).text();
|