@oh-my-pi/pi-coding-agent 15.10.4 → 15.10.6
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 +74 -0
- package/dist/types/capability/rule-buckets.d.ts +1 -1
- package/dist/types/capability/rule.d.ts +6 -1
- package/dist/types/cli/update-cli.d.ts +11 -1
- package/dist/types/config/model-registry.d.ts +18 -1
- package/dist/types/discovery/at-imports.d.ts +15 -0
- package/dist/types/edit/diff.d.ts +3 -2
- package/dist/types/eval/__tests__/helpers-local-roots.test.d.ts +1 -0
- package/dist/types/eval/backend.d.ts +7 -0
- package/dist/types/eval/js/context-manager.d.ts +1 -0
- package/dist/types/eval/js/executor.d.ts +2 -0
- package/dist/types/eval/js/index.d.ts +1 -1
- package/dist/types/eval/js/shared/helpers.d.ts +6 -0
- package/dist/types/eval/js/shared/runtime.d.ts +5 -0
- package/dist/types/eval/js/worker-protocol.d.ts +6 -0
- package/dist/types/eval/py/executor.d.ts +7 -0
- package/dist/types/eval/py/index.d.ts +1 -1
- package/dist/types/exa/index.d.ts +1 -19
- package/dist/types/exa/mcp-client.d.ts +10 -3
- package/dist/types/exa/types.d.ts +0 -83
- package/dist/types/export/ttsr.d.ts +14 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +1 -1
- package/dist/types/internal-urls/local-protocol.d.ts +10 -0
- package/dist/types/mcp/oauth-flow.d.ts +2 -2
- package/dist/types/modes/components/custom-editor.d.ts +3 -0
- package/dist/types/modes/components/{status-line.d.ts → status-line/component.d.ts} +2 -32
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +31 -2
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +8 -0
- package/dist/types/modes/image-references.d.ts +8 -3
- package/dist/types/modes/interactive-mode.d.ts +9 -1
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +0 -2
- package/dist/types/task/render.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +15 -0
- package/dist/types/tools/index.d.ts +17 -2
- package/dist/types/tools/render-utils.d.ts +1 -1
- package/dist/types/tools/tool-timeouts.d.ts +1 -1
- package/dist/types/utils/block-context.d.ts +35 -0
- package/dist/types/utils/git.d.ts +6 -0
- package/dist/types/utils/image-loading.d.ts +12 -0
- package/package.json +29 -9
- package/src/capability/rule-buckets.ts +4 -2
- package/src/capability/rule.ts +10 -1
- package/src/cli/auth-broker-cli.ts +6 -7
- package/src/cli/auth-gateway-cli.ts +4 -3
- package/src/cli/list-models.ts +5 -0
- package/src/cli/update-cli.ts +138 -16
- package/src/commit/agentic/tools/split-commit.ts +8 -1
- package/src/config/model-provider-priority.ts +1 -0
- package/src/config/model-registry.ts +81 -2
- package/src/debug/index.ts +4 -8
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-rules/index.ts +4 -0
- package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
- package/src/discovery/helpers.ts +2 -1
- package/src/edit/diff.ts +114 -4
- package/src/edit/hashline/diff.ts +1 -1
- package/src/edit/hashline/execute.ts +1 -1
- package/src/edit/modes/patch.ts +6 -2
- package/src/edit/modes/replace.ts +1 -1
- package/src/edit/renderer.ts +12 -2
- package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
- package/src/eval/backend.ts +15 -0
- package/src/eval/js/context-manager.ts +4 -2
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/index.ts +7 -1
- package/src/eval/js/shared/helpers.ts +53 -6
- package/src/eval/js/shared/runtime.ts +8 -0
- package/src/eval/js/worker-core.ts +1 -0
- package/src/eval/js/worker-protocol.ts +6 -0
- package/src/eval/py/executor.ts +12 -0
- package/src/eval/py/index.ts +7 -1
- package/src/eval/py/prelude.py +43 -4
- package/src/eval/py/runner.py +1 -0
- package/src/exa/index.ts +1 -26
- package/src/exa/mcp-client.ts +10 -10
- package/src/exa/types.ts +0 -97
- package/src/export/ttsr.ts +122 -1
- package/src/extensibility/extensions/types.ts +8 -1
- package/src/extensibility/legacy-pi-ai-shim.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +6 -5
- package/src/goals/tools/goal-tool.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/internal-urls/local-protocol.ts +13 -0
- package/src/lsp/render.ts +8 -6
- package/src/mcp/oauth-flow.ts +3 -3
- package/src/mcp/render.ts +7 -1
- package/src/modes/components/agent-dashboard.ts +6 -4
- package/src/modes/components/custom-editor.ts +12 -6
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/oauth-selector.ts +4 -4
- package/src/modes/components/read-tool-group.ts +10 -3
- package/src/modes/components/{status-line.ts → status-line/component.ts} +18 -40
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/types.ts +23 -8
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/transcript-container.ts +17 -10
- package/src/modes/components/user-message.ts +6 -3
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/event-controller.ts +8 -0
- package/src/modes/controllers/extension-ui-controller.ts +143 -127
- package/src/modes/controllers/input-controller.ts +60 -11
- package/src/modes/controllers/mcp-command-controller.ts +52 -17
- package/src/modes/controllers/selector-controller.ts +4 -11
- package/src/modes/controllers/ssh-command-controller.ts +2 -2
- package/src/modes/image-references.ts +13 -7
- package/src/modes/interactive-mode.ts +35 -3
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +3 -11
- package/src/modes/theme/theme.ts +95 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/ui-helpers.ts +14 -5
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/eval.md +4 -4
- package/src/sdk.ts +31 -14
- package/src/session/agent-session.ts +290 -196
- package/src/session/session-manager.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +9 -1
- package/src/system-prompt.ts +15 -9
- package/src/task/index.ts +9 -1
- package/src/task/render.ts +36 -14
- package/src/tools/ask.ts +14 -5
- package/src/tools/bash-interactive.ts +1 -1
- package/src/tools/bash.ts +14 -2
- package/src/tools/browser/render.ts +5 -2
- package/src/tools/browser/tab-worker.ts +211 -91
- package/src/tools/debug.ts +5 -2
- package/src/tools/eval-render.ts +6 -3
- package/src/tools/eval.ts +1 -1
- package/src/tools/gh-renderer.ts +29 -15
- package/src/tools/index.ts +32 -4
- package/src/tools/inspect-image-renderer.ts +12 -5
- package/src/tools/job.ts +9 -6
- package/src/tools/memory-render.ts +19 -5
- package/src/tools/read.ts +165 -18
- package/src/tools/render-utils.ts +3 -1
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +1 -1
- package/src/tools/ssh.ts +4 -1
- package/src/tools/todo.ts +8 -1
- package/src/tools/tool-timeouts.ts +1 -1
- package/src/tools/write.ts +1 -1
- package/src/tui/code-cell.ts +1 -1
- package/src/utils/block-context.ts +312 -0
- package/src/utils/git.ts +41 -0
- package/src/utils/image-loading.ts +31 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/render.ts +14 -6
- package/dist/types/exa/factory.d.ts +0 -13
- package/dist/types/exa/render.d.ts +0 -19
- package/dist/types/exa/researcher.d.ts +0 -9
- package/dist/types/exa/search.d.ts +0 -9
- package/dist/types/exa/websets.d.ts +0 -9
- package/src/exa/factory.ts +0 -60
- package/src/exa/render.ts +0 -244
- package/src/exa/researcher.ts +0 -36
- package/src/exa/search.ts +0 -47
- package/src/exa/websets.ts +0 -248
|
@@ -133,6 +133,19 @@ export function resolveLocalUrlToPath(input: string | InternalUrl, options: Loca
|
|
|
133
133
|
return resolved;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* On-disk roots the eval helpers (`read`/`write`/`append`) substitute for
|
|
138
|
+
* internal-URL schemes so e.g. `write("local://x.md")` lands where a later
|
|
139
|
+
* `read local://x.md` resolves — instead of a literal `local:/` directory under
|
|
140
|
+
* the cwd (a stdlib `pathlib.Path`/`path.resolve` collapses `local://` to
|
|
141
|
+
* `local:/`). Keyed by scheme without the `://`. Currently only `local`, but the
|
|
142
|
+
* shape is a map so additional file-backed schemes can be added without
|
|
143
|
+
* re-plumbing the worker boundary.
|
|
144
|
+
*/
|
|
145
|
+
export function buildEvalUrlRoots(options: LocalProtocolOptions): Record<string, string> {
|
|
146
|
+
return { local: resolveLocalRoot(options) };
|
|
147
|
+
}
|
|
148
|
+
|
|
136
149
|
/**
|
|
137
150
|
* Protocol handler for local:// URLs.
|
|
138
151
|
*
|
package/src/lsp/render.ts
CHANGED
|
@@ -166,15 +166,17 @@ export function renderResult(
|
|
|
166
166
|
} else if (result.details?.action === "diagnostics" && text === "OK") {
|
|
167
167
|
label = "Diagnostics";
|
|
168
168
|
state = "success";
|
|
169
|
-
bodyLines = [`${theme.styledSymbol("
|
|
169
|
+
bodyLines = [`${theme.styledSymbol("tool.lsp", "accent")} ${theme.fg("dim", "OK")}`];
|
|
170
170
|
} else {
|
|
171
171
|
label = "Response";
|
|
172
172
|
bodyLines = renderGeneric(text, lines, expanded, theme);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
const actionLabel = (request?.action ?? result.details?.action ?? label.toLowerCase()).replace(/_/g, " ");
|
|
176
|
-
const
|
|
177
|
-
const icon =
|
|
176
|
+
const isSuccess = !isPartial && !result.isError;
|
|
177
|
+
const icon = isSuccess
|
|
178
|
+
? theme.styledSymbol("tool.lsp", "accent")
|
|
179
|
+
: formatStatusIcon(isPartial ? "running" : "error", theme, spinnerFrame);
|
|
178
180
|
const header = `${icon} LSP ${actionLabel}`;
|
|
179
181
|
|
|
180
182
|
return outputBlock.render(
|
|
@@ -325,7 +327,7 @@ function renderDiagnostics(
|
|
|
325
327
|
? theme.styledSymbol("status.error", "error")
|
|
326
328
|
: warnCount > 0
|
|
327
329
|
? theme.styledSymbol("status.warning", "warning")
|
|
328
|
-
: theme.styledSymbol("
|
|
330
|
+
: theme.styledSymbol("tool.lsp", "accent");
|
|
329
331
|
|
|
330
332
|
const meta: string[] = [];
|
|
331
333
|
if (errorCount > 0) meta.push(`${errorCount} error${errorCount !== 1 ? "s" : ""}`);
|
|
@@ -407,7 +409,7 @@ function renderDiagnostics(
|
|
|
407
409
|
function renderReferences(refMatch: RegExpMatchArray, lines: string[], expanded: boolean, theme: Theme): string[] {
|
|
408
410
|
const refCount = Number.parseInt(refMatch[1], 10);
|
|
409
411
|
const icon =
|
|
410
|
-
refCount > 0 ? theme.styledSymbol("
|
|
412
|
+
refCount > 0 ? theme.styledSymbol("tool.lsp", "accent") : theme.styledSymbol("status.warning", "warning");
|
|
411
413
|
|
|
412
414
|
const locLines = lines.filter(l => /^\s*\S+:\d+:\d+/.test(l));
|
|
413
415
|
|
|
@@ -598,7 +600,7 @@ function renderGeneric(text: string, lines: string[], expanded: boolean, theme:
|
|
|
598
600
|
hasError && !hasSuccess
|
|
599
601
|
? theme.styledSymbol("status.error", "error")
|
|
600
602
|
: hasSuccess && !hasError
|
|
601
|
-
? theme.styledSymbol("
|
|
603
|
+
? theme.styledSymbol("tool.lsp", "accent")
|
|
602
604
|
: theme.styledSymbol("status.info", "accent");
|
|
603
605
|
|
|
604
606
|
if (expanded) {
|
package/src/mcp/oauth-flow.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* by providing authorization URL, token URL, and client credentials.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { OAuthCallbackFlowOptions } from "@oh-my-pi/pi-ai/
|
|
9
|
-
import { OAuthCallbackFlow } from "@oh-my-pi/pi-ai/
|
|
10
|
-
import type { OAuthController, OAuthCredentials } from "@oh-my-pi/pi-ai/
|
|
8
|
+
import type { OAuthCallbackFlowOptions } from "@oh-my-pi/pi-ai/oauth/callback-server";
|
|
9
|
+
import { OAuthCallbackFlow } from "@oh-my-pi/pi-ai/oauth/callback-server";
|
|
10
|
+
import type { OAuthController, OAuthCredentials } from "@oh-my-pi/pi-ai/oauth/types";
|
|
11
11
|
|
|
12
12
|
const DEFAULT_PORT = 3000;
|
|
13
13
|
const CALLBACK_PATH = "/callback";
|
package/src/mcp/render.ts
CHANGED
|
@@ -53,7 +53,13 @@ export function renderMCPResult(
|
|
|
53
53
|
const lines: string[] = [];
|
|
54
54
|
const isError = result.isError ?? result.details?.isError ?? false;
|
|
55
55
|
const title = result.details ? `${result.details.serverName}/${result.details.mcpToolName}` : "MCP";
|
|
56
|
-
|
|
56
|
+
const success = !isError;
|
|
57
|
+
lines.push(
|
|
58
|
+
renderStatusLine(
|
|
59
|
+
success ? { iconOverride: theme.styledSymbol("tool.mcp", "accent"), title } : { icon: "error", title },
|
|
60
|
+
theme,
|
|
61
|
+
),
|
|
62
|
+
);
|
|
57
63
|
|
|
58
64
|
// Args section (when expanded)
|
|
59
65
|
if (expanded && args && typeof args === "object" && Object.keys(args).length > 0) {
|
|
@@ -649,6 +649,11 @@ export class AgentDashboard extends Container {
|
|
|
649
649
|
this.#buildLayout();
|
|
650
650
|
}
|
|
651
651
|
|
|
652
|
+
#shouldSubmitCreateDescription(data: string): boolean {
|
|
653
|
+
if (matchesKey(data, "ctrl+enter")) return true;
|
|
654
|
+
return process.platform === "win32" && data === "\n" && this.#createDescription.trim().length > 0;
|
|
655
|
+
}
|
|
656
|
+
|
|
652
657
|
async #generateAgentFromDescription(rawDescription: string): Promise<void> {
|
|
653
658
|
const description = rawDescription.trim();
|
|
654
659
|
this.#createDescription = description;
|
|
@@ -1094,10 +1099,7 @@ export class AgentDashboard extends Container {
|
|
|
1094
1099
|
}
|
|
1095
1100
|
return;
|
|
1096
1101
|
}
|
|
1097
|
-
if (
|
|
1098
|
-
!this.#createGenerating &&
|
|
1099
|
-
(matchesKey(data, "ctrl+enter") || (data.charCodeAt(0) === 10 && data.length > 1))
|
|
1100
|
-
) {
|
|
1102
|
+
if (!this.#createGenerating && this.#shouldSubmitCreateDescription(data)) {
|
|
1101
1103
|
this.#submitCreateDescription();
|
|
1102
1104
|
return;
|
|
1103
1105
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { addKeyAliases, canonicalKeyId, Editor, type KeyId, parseKey, parseKittySequence } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import type { AppKeybinding } from "../../config/keybindings";
|
|
3
|
-
import { imageReferenceHyperlink,
|
|
3
|
+
import { imageReferenceHyperlink, PLACEHOLDER_REGEX, renderPlaceholders } from "../image-references";
|
|
4
4
|
import { highlightMagicKeywords } from "../magic-keywords";
|
|
5
5
|
import { theme } from "../theme/theme";
|
|
6
6
|
|
|
@@ -76,16 +76,22 @@ export function extractBracketedImagePastePath(data: string): string | undefined
|
|
|
76
76
|
export class CustomEditor extends Editor {
|
|
77
77
|
imageLinks?: readonly (string | undefined)[];
|
|
78
78
|
|
|
79
|
+
/** Treat image/paste markers as indivisible: a stray backspace deletes the whole token
|
|
80
|
+
* instead of corrupting `[Paste #1, +30 lines]` into plain text. */
|
|
81
|
+
override atomicTokenPattern = PLACEHOLDER_REGEX;
|
|
82
|
+
|
|
79
83
|
/** Gradient-highlight the "ultrathink" / "orchestrate" / "workflowz" keywords as the user types
|
|
80
84
|
* them, skipping any occurrence inside code spans, fenced blocks, or XML sections. Also make
|
|
81
85
|
* pasted image placeholders visually distinct and hyperlink them once their blob file exists. */
|
|
82
86
|
decorateText = (text: string): string =>
|
|
83
|
-
|
|
87
|
+
renderPlaceholders(text, {
|
|
84
88
|
renderText: value => highlightMagicKeywords(value),
|
|
85
|
-
renderReference: (value, index) =>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
renderReference: (value, kind, index) =>
|
|
90
|
+
kind === "image"
|
|
91
|
+
? imageReferenceHyperlink(value, index, this.imageLinks, label =>
|
|
92
|
+
theme.fg("accent", `\x1b[1m\x1b[4m${label}\x1b[24m\x1b[22m`),
|
|
93
|
+
)
|
|
94
|
+
: theme.fg("accent", `\x1b[1m${value}\x1b[22m`),
|
|
89
95
|
});
|
|
90
96
|
onEscape?: () => void;
|
|
91
97
|
onClear?: () => void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getOAuthProviders } from "@oh-my-pi/pi-ai/
|
|
1
|
+
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
2
2
|
import { Container, getKeybindings, Input, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { theme } from "../../modes/theme/theme";
|
|
4
4
|
import { openPath } from "../../utils/open";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getOAuthProviders } from "@oh-my-pi/pi-ai/
|
|
2
|
-
import type { OAuthProviderInfo } from "@oh-my-pi/pi-ai/
|
|
1
|
+
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
2
|
+
import type { OAuthProviderInfo } from "@oh-my-pi/pi-ai/oauth/types";
|
|
3
3
|
import {
|
|
4
4
|
Container,
|
|
5
5
|
extractPrintableText,
|
|
@@ -179,10 +179,10 @@ export class OAuthSelectorComponent extends Container {
|
|
|
179
179
|
return theme.fg("error", ` ${theme.status.error} invalid`) + source;
|
|
180
180
|
}
|
|
181
181
|
if (state === "valid") {
|
|
182
|
-
return theme.fg("success", ` ${theme.status.
|
|
182
|
+
return theme.fg("success", ` ${theme.status.enabled} logged in`) + source;
|
|
183
183
|
}
|
|
184
184
|
return this.#hasSelectableAuth(providerId)
|
|
185
|
-
? theme.fg("success", ` ${theme.status.
|
|
185
|
+
? theme.fg("success", ` ${theme.status.enabled} logged in`) + source
|
|
186
186
|
: "";
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -54,6 +54,10 @@ type ReadToolResultDetails = {
|
|
|
54
54
|
};
|
|
55
55
|
conflictCount?: number;
|
|
56
56
|
displayReadTargets?: unknown;
|
|
57
|
+
displayContent?: {
|
|
58
|
+
text?: string;
|
|
59
|
+
startLine?: number;
|
|
60
|
+
};
|
|
57
61
|
meta?: {
|
|
58
62
|
source?: {
|
|
59
63
|
type?: string;
|
|
@@ -373,10 +377,13 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa
|
|
|
373
377
|
typeof details?.conflictCount === "number" && details.conflictCount > 0 ? details.conflictCount : undefined;
|
|
374
378
|
entry.conflictCount = conflictCount;
|
|
375
379
|
entry.status = result.isError ? "error" : suffixResolution ? "warning" : "success";
|
|
376
|
-
// Store
|
|
380
|
+
// Store clean display content for preview/expanded display when the read
|
|
381
|
+
// tool provides it; fall back to model-facing text for legacy results.
|
|
382
|
+
const displayContent =
|
|
383
|
+
typeof details?.displayContent?.text === "string" ? details.displayContent.text : undefined;
|
|
377
384
|
const textContent = result.content?.find(c => c.type === "text")?.text;
|
|
378
|
-
if (textContent !== undefined) {
|
|
379
|
-
entry.contentText = textContent;
|
|
385
|
+
if (displayContent !== undefined || textContent !== undefined) {
|
|
386
|
+
entry.contentText = displayContent ?? textContent;
|
|
380
387
|
}
|
|
381
388
|
this.#updateDisplay();
|
|
382
389
|
}
|
|
@@ -4,46 +4,24 @@ import { estimateTokens } from "@oh-my-pi/pi-agent-core/compaction";
|
|
|
4
4
|
import { type Component, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import { formatCount, getProjectDir } from "@oh-my-pi/pi-utils";
|
|
6
6
|
import { $ } from "bun";
|
|
7
|
-
import { settings } from "
|
|
8
|
-
import type {
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export interface StatusLineSegmentOptions {
|
|
27
|
-
model?: { showThinkingLevel?: boolean };
|
|
28
|
-
path?: { abbreviate?: boolean; maxLength?: number; stripWorkPrefix?: boolean };
|
|
29
|
-
git?: { showBranch?: boolean; showStaged?: boolean; showUnstaged?: boolean; showUntracked?: boolean };
|
|
30
|
-
time?: { format?: "12h" | "24h"; showSeconds?: boolean };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface StatusLineSettings {
|
|
34
|
-
preset?: StatusLinePreset;
|
|
35
|
-
leftSegments?: StatusLineSegmentId[];
|
|
36
|
-
rightSegments?: StatusLineSegmentId[];
|
|
37
|
-
separator?: StatusLineSeparatorStyle;
|
|
38
|
-
segmentOptions?: StatusLineSegmentOptions;
|
|
39
|
-
showHookStatus?: boolean;
|
|
40
|
-
sessionAccent?: boolean;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export type EffectiveStatusLineSettings = Required<
|
|
44
|
-
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
45
|
-
> &
|
|
46
|
-
StatusLineSettings;
|
|
7
|
+
import { settings } from "../../../config/settings";
|
|
8
|
+
import type { AgentSession } from "../../../session/agent-session";
|
|
9
|
+
import * as git from "../../../utils/git";
|
|
10
|
+
import { getSessionAccentAnsi, getSessionAccentHex } from "../../../utils/session-color";
|
|
11
|
+
import { sanitizeStatusText } from "../../shared";
|
|
12
|
+
import { theme } from "../../theme/theme";
|
|
13
|
+
import { computeNonMessageTokens } from "../../utils/context-usage";
|
|
14
|
+
import { canReuseCachedPr, createPrCacheContext, isSamePrCacheContext, type PrCacheContext } from "./git-utils";
|
|
15
|
+
import { getPreset } from "./presets";
|
|
16
|
+
import { renderSegment, type SegmentContext } from "./segments";
|
|
17
|
+
import { getSeparator } from "./separators";
|
|
18
|
+
import { calculateTokensPerSecond } from "./token-rate";
|
|
19
|
+
import type {
|
|
20
|
+
EffectiveStatusLineSettings,
|
|
21
|
+
StatusLineSegmentId,
|
|
22
|
+
StatusLineSegmentOptions,
|
|
23
|
+
StatusLineSettings,
|
|
24
|
+
} from "./types";
|
|
47
25
|
|
|
48
26
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
49
27
|
// Per-message token cache
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
import type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle } from "../../../config/settings-schema";
|
|
2
2
|
import type { AgentSession } from "../../../session/agent-session";
|
|
3
|
-
import type { StatusLineSegmentOptions, StatusLineSettings } from "../status-line";
|
|
4
3
|
|
|
5
|
-
export type {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
4
|
+
export type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle };
|
|
5
|
+
|
|
6
|
+
export interface StatusLineSegmentOptions {
|
|
7
|
+
model?: { showThinkingLevel?: boolean };
|
|
8
|
+
path?: { abbreviate?: boolean; maxLength?: number; stripWorkPrefix?: boolean };
|
|
9
|
+
git?: { showBranch?: boolean; showStaged?: boolean; showUnstaged?: boolean; showUntracked?: boolean };
|
|
10
|
+
time?: { format?: "12h" | "24h"; showSeconds?: boolean };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface StatusLineSettings {
|
|
14
|
+
preset?: StatusLinePreset;
|
|
15
|
+
leftSegments?: StatusLineSegmentId[];
|
|
16
|
+
rightSegments?: StatusLineSegmentId[];
|
|
17
|
+
separator?: StatusLineSeparatorStyle;
|
|
18
|
+
segmentOptions?: StatusLineSegmentOptions;
|
|
19
|
+
showHookStatus?: boolean;
|
|
20
|
+
sessionAccent?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type EffectiveStatusLineSettings = Required<
|
|
24
|
+
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
25
|
+
> &
|
|
26
|
+
StatusLineSettings;
|
|
12
27
|
|
|
13
28
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
14
29
|
// Segment Rendering
|
|
@@ -914,7 +914,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
914
914
|
*/
|
|
915
915
|
#formatToolExecution(): string {
|
|
916
916
|
const lines: string[] = [];
|
|
917
|
-
const icon = this.#isPartial ? "pending" : this.#result?.isError ? "error" : "
|
|
917
|
+
const icon = this.#isPartial ? "pending" : this.#result?.isError ? "error" : "done";
|
|
918
918
|
lines.push(renderStatusLine({ icon, title: this.#toolLabel }, theme));
|
|
919
919
|
|
|
920
920
|
const argsObject = this.#args && typeof this.#args === "object" ? (this.#args as Record<string, unknown>) : null;
|
|
@@ -71,9 +71,7 @@ function commonPrefixLength(prev: string[], cur: string[]): number {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function commonSuffixLength(prev: string[], cur: string[], prefixLength: number): number {
|
|
74
|
-
const
|
|
75
|
-
const curLimit = cur.length - prefixLength;
|
|
76
|
-
const limit = Math.min(prevLimit, curLimit);
|
|
74
|
+
const limit = Math.min(prev.length - prefixLength, cur.length - prefixLength);
|
|
77
75
|
let i = 0;
|
|
78
76
|
while (i < limit && prev[prev.length - 1 - i] === cur[cur.length - 1 - i]) i++;
|
|
79
77
|
return i;
|
|
@@ -95,14 +93,23 @@ function deriveLiveCommitState(
|
|
|
95
93
|
const staticRender = prefixLength === previous.lines.length && prefixLength === current.length;
|
|
96
94
|
if (!staticRender) {
|
|
97
95
|
const suffixLength = commonSuffixLength(previous.lines, current, prefixLength);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
96
|
+
// Append-only growth never rewrites a row that may already have scrolled
|
|
97
|
+
// into native scrollback; it only grows the block at/near its tail. Three
|
|
98
|
+
// shapes qualify: a pure bottom append, an insertion above stable trailing
|
|
99
|
+
// chrome (a streaming tool's footer/border), and an in-place extension of
|
|
100
|
+
// the current line by one streamed token (line count unchanged). The first
|
|
101
|
+
// two preserve every previous row across a matching prefix + suffix; the
|
|
102
|
+
// last leaves a single divergent previous row that the current row merely
|
|
103
|
+
// lengthens. A divergent interior row that is genuinely rewritten means the
|
|
104
|
+
// block re-laid-out committed content — volatile, and never committed.
|
|
105
|
+
const preservedEveryRow = prefixLength + suffixLength >= previous.lines.length;
|
|
106
|
+
const tailExtendedInPlace =
|
|
107
|
+
prefixLength + suffixLength === previous.lines.length - 1 &&
|
|
108
|
+
prefixLength < current.length &&
|
|
109
|
+
current[prefixLength]!.startsWith(previous.lines[prefixLength]!);
|
|
110
|
+
if ((preservedEveryRow || tailExtendedInPlace) && current.length >= previous.lines.length && !volatile) {
|
|
104
111
|
appendOnly = true;
|
|
105
|
-
} else if (
|
|
112
|
+
} else if (!preservedEveryRow && !tailExtendedInPlace) {
|
|
106
113
|
volatile = true;
|
|
107
114
|
appendOnly = false;
|
|
108
115
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Container, Markdown } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
3
|
-
import { imageReferenceHyperlink,
|
|
3
|
+
import { imageReferenceHyperlink, renderPlaceholders } from "../image-references";
|
|
4
4
|
import { highlightMagicKeywords } from "../magic-keywords";
|
|
5
5
|
|
|
6
6
|
// OSC 133 shell integration: marks prompt zones for terminal multiplexers
|
|
@@ -26,9 +26,12 @@ export class UserMessageComponent extends Container {
|
|
|
26
26
|
: (value: string) => theme.fg("userMessageText", highlightMagicKeywords(value, keywordReset));
|
|
27
27
|
const imageLabel = (value: string) => theme.fg("accent", `\x1b[1m\x1b[4m${value}\x1b[24m\x1b[22m`);
|
|
28
28
|
const color = (value: string) =>
|
|
29
|
-
|
|
29
|
+
renderPlaceholders(value, {
|
|
30
30
|
renderText: baseText,
|
|
31
|
-
renderReference: (label, index) =>
|
|
31
|
+
renderReference: (label, kind, index) =>
|
|
32
|
+
kind === "image"
|
|
33
|
+
? imageReferenceHyperlink(label, index, imageLinks, imageLabel)
|
|
34
|
+
: theme.fg("accent", `\x1b[1m${label}\x1b[22m`),
|
|
32
35
|
});
|
|
33
36
|
this.addChild(
|
|
34
37
|
new Markdown(text, 1, 1, getMarkdownTheme(), {
|
|
@@ -185,7 +185,7 @@ export class WelcomeComponent implements Component {
|
|
|
185
185
|
for (const server of this.lspServers) {
|
|
186
186
|
const icon =
|
|
187
187
|
server.status === "ready"
|
|
188
|
-
? theme.styledSymbol("status.
|
|
188
|
+
? theme.styledSymbol("status.enabled", "success")
|
|
189
189
|
: server.status === "connecting"
|
|
190
190
|
? theme.styledSymbol("status.pending", "muted")
|
|
191
191
|
: theme.styledSymbol("status.error", "error");
|
|
@@ -798,7 +798,15 @@ export class EventController {
|
|
|
798
798
|
);
|
|
799
799
|
} else if (isShakeAction) {
|
|
800
800
|
// Shake produces no CompactionResult; rebuild on success, suppress benign skips.
|
|
801
|
+
// The fallback path (`errorMessage` set, `skipped` false) means shake reclaimed
|
|
802
|
+
// some tokens before deciding the threshold still wasn't cleared — rebuild so
|
|
803
|
+
// the chat reflects the dropped regions even though a context-full pass follows.
|
|
801
804
|
if (event.errorMessage) {
|
|
805
|
+
if (!event.skipped) {
|
|
806
|
+
this.ctx.rebuildChatFromMessages();
|
|
807
|
+
this.ctx.statusLine.invalidate();
|
|
808
|
+
this.ctx.updateEditorTopBorder();
|
|
809
|
+
}
|
|
802
810
|
this.ctx.showWarning(event.errorMessage);
|
|
803
811
|
} else if (!event.skipped) {
|
|
804
812
|
this.ctx.rebuildChatFromMessages();
|