@oh-my-pi/pi-coding-agent 15.11.3 → 15.11.4
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 +54 -0
- package/dist/cli.js +353 -294
- package/dist/types/config/api-key-resolver.d.ts +9 -3
- package/dist/types/config/keybindings.d.ts +1 -1
- package/dist/types/config/model-discovery.d.ts +6 -4
- package/dist/types/config/model-registry.d.ts +7 -4
- package/dist/types/config/settings-schema.d.ts +458 -155
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/mnemopi/config.d.ts +3 -1
- package/dist/types/modes/components/settings-defs.d.ts +9 -2
- package/dist/types/modes/components/settings-selector.d.ts +9 -4
- package/dist/types/modes/components/tool-execution.d.ts +12 -1
- package/dist/types/modes/components/transcript-container.d.ts +12 -0
- package/dist/types/modes/controllers/input-controller.d.ts +9 -1
- package/dist/types/modes/theme/theme.d.ts +23 -3
- package/dist/types/session/agent-session.d.ts +14 -7
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/snapcompact-inline.d.ts +28 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +14 -0
- package/dist/types/system-prompt.d.ts +3 -1
- package/dist/types/task/render.d.ts +16 -6
- package/dist/types/tools/gh.d.ts +3 -0
- package/dist/types/tools/render-utils.d.ts +8 -16
- package/dist/types/utils/session-color.d.ts +15 -3
- package/dist/types/web/kagi.d.ts +1 -2
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/dist/types/web/search/providers/gemini.d.ts +9 -6
- package/package.json +11 -11
- package/src/auto-thinking/classifier.ts +1 -5
- package/src/commit/model-selection.ts +3 -6
- package/src/config/api-key-resolver.ts +10 -3
- package/src/config/keybindings.ts +1 -1
- package/src/config/model-discovery.ts +60 -46
- package/src/config/model-registry.ts +21 -8
- package/src/config/model-resolver.ts +57 -3
- package/src/config/settings-schema.ts +601 -153
- package/src/eval/completion-bridge.ts +1 -5
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +13 -6
- package/src/internal-urls/docs-index.generated.ts +5 -5
- package/src/internal-urls/issue-pr-protocol.ts +10 -4
- package/src/memories/index.ts +2 -10
- package/src/mnemopi/backend.ts +30 -8
- package/src/mnemopi/config.ts +6 -1
- package/src/mnemopi/state.ts +6 -0
- package/src/modes/components/extensions/inspector-panel.ts +6 -2
- package/src/modes/components/plan-review-overlay.ts +15 -17
- package/src/modes/components/plugin-settings.ts +22 -5
- package/src/modes/components/settings-defs.ts +19 -4
- package/src/modes/components/settings-selector.ts +493 -93
- package/src/modes/components/status-line/component.ts +3 -1
- package/src/modes/components/status-line/segments.ts +3 -1
- package/src/modes/components/tool-execution.ts +69 -12
- package/src/modes/components/transcript-container.ts +26 -0
- package/src/modes/components/tree-selector.ts +16 -6
- package/src/modes/controllers/command-controller.ts +37 -7
- package/src/modes/controllers/event-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +68 -6
- package/src/modes/controllers/selector-controller.ts +81 -61
- package/src/modes/interactive-mode.ts +4 -2
- package/src/modes/rpc/rpc-mode.ts +2 -1
- package/src/modes/shared.ts +2 -0
- package/src/modes/theme/theme.ts +100 -7
- package/src/modes/utils/context-usage.ts +3 -1
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/utils/ui-helpers.ts +9 -5
- package/src/prompts/system/personalities/default.md +26 -0
- package/src/prompts/system/personalities/friendly.md +17 -0
- package/src/prompts/system/personalities/pragmatic.md +15 -0
- package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
- package/src/prompts/system/snapcompact-system-stub.md +1 -0
- package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
- package/src/prompts/system/system-prompt.md +5 -22
- package/src/prompts/tools/task.md +3 -3
- package/src/sdk.ts +22 -1
- package/src/session/agent-session.ts +91 -24
- package/src/session/auth-storage.ts +1 -0
- package/src/session/session-dump-format.ts +8 -1
- package/src/session/session-manager.ts +5 -5
- package/src/session/snapcompact-inline.ts +187 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +44 -0
- package/src/slash-commands/helpers/usage-report.ts +24 -3
- package/src/system-prompt.ts +15 -1
- package/src/task/render.ts +29 -19
- package/src/tool-discovery/tool-index.ts +2 -0
- package/src/tools/bash.ts +10 -3
- package/src/tools/eval-render.ts +13 -8
- package/src/tools/gh.ts +39 -1
- package/src/tools/image-gen.ts +114 -78
- package/src/tools/inspect-image.ts +1 -5
- package/src/tools/job.ts +25 -5
- package/src/tools/read.ts +1 -57
- package/src/tools/render-utils.ts +29 -31
- package/src/tools/ssh.ts +3 -3
- package/src/tools/tts.ts +40 -20
- package/src/utils/clipboard.ts +56 -4
- package/src/utils/commit-message-generator.ts +1 -5
- package/src/utils/session-color.ts +83 -9
- package/src/utils/title-generator.ts +1 -1
- package/src/web/kagi.ts +26 -27
- package/src/web/search/providers/codex.ts +42 -40
- package/src/web/search/providers/gemini.ts +42 -22
- package/src/web/search/providers/perplexity.ts +22 -10
|
@@ -1093,7 +1093,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1093
1093
|
} else {
|
|
1094
1094
|
const accentEnabled = !isSettingsInitialized() || settings.get("statusLine.sessionAccent") !== false;
|
|
1095
1095
|
const sessionName = accentEnabled ? this.sessionManager.getSessionName() : undefined;
|
|
1096
|
-
const hex = sessionName
|
|
1096
|
+
const hex = sessionName
|
|
1097
|
+
? getSessionAccentHex(sessionName, theme.getMajorThemeColorHexes(), theme.accentSurfaceLuminance)
|
|
1098
|
+
: undefined;
|
|
1097
1099
|
const ansi = getSessionAccentAnsi(hex);
|
|
1098
1100
|
if (ansi) {
|
|
1099
1101
|
this.editor.borderColor = (str: string) => `${ansi}${str}\x1b[39m`;
|
|
@@ -2823,7 +2825,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2823
2825
|
if (!key.sessionAccentEnabled || !key.sessionName) {
|
|
2824
2826
|
return this.#cacheWorkingMessageAccent(key, undefined);
|
|
2825
2827
|
}
|
|
2826
|
-
const hex = getSessionAccentHex(key.sessionName, key.accentSurfaceLuminance);
|
|
2828
|
+
const hex = getSessionAccentHex(key.sessionName, theme.getMajorThemeColorHexes(), key.accentSurfaceLuminance);
|
|
2827
2829
|
const main = getSessionAccentAnsi(hex);
|
|
2828
2830
|
const dim = getSessionAccentAnsi(adjustHsv(hex, { s: 0.55, v: 0.65 }));
|
|
2829
2831
|
return this.#cacheWorkingMessageAccent(key, main && dim ? { main, dim } : undefined);
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response
|
|
12
12
|
*/
|
|
13
13
|
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
14
|
+
import { isZodSchema, zodToWireSchema } from "@oh-my-pi/pi-ai/utils/schema";
|
|
14
15
|
import { $env, readJsonl, Snowflake } from "@oh-my-pi/pi-utils";
|
|
15
16
|
import { reset as resetCapabilities } from "../../capability";
|
|
16
17
|
import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../../discovery/helpers";
|
|
@@ -662,7 +663,7 @@ export async function runRpcMode(
|
|
|
662
663
|
dumpTools: session.agent.state.tools.map(tool => ({
|
|
663
664
|
name: tool.name,
|
|
664
665
|
description: tool.description,
|
|
665
|
-
parameters: tool.parameters,
|
|
666
|
+
parameters: isZodSchema(tool.parameters) ? zodToWireSchema(tool.parameters) : tool.parameters,
|
|
666
667
|
})),
|
|
667
668
|
contextUsage: session.getContextUsage(),
|
|
668
669
|
};
|
package/src/modes/shared.ts
CHANGED
|
@@ -24,6 +24,8 @@ export function getTabBarTheme(): TabBarTheme {
|
|
|
24
24
|
label: (text: string) => theme.bold(theme.fg("accent", text)),
|
|
25
25
|
activeTab: (text: string) => theme.bold(theme.bg("selectedBg", theme.fg("text", text))),
|
|
26
26
|
inactiveTab: (text: string) => theme.fg("muted", text),
|
|
27
|
+
mutedTab: (text: string) => theme.fg("dim", text),
|
|
28
|
+
hoverTab: (text: string) => theme.bg("selectedBg", theme.fg("text", text)),
|
|
27
29
|
hint: (text: string) => theme.fg("dim", text),
|
|
28
30
|
};
|
|
29
31
|
}
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
highlightCode as nativeHighlightCode,
|
|
10
10
|
supportsLanguage as nativeSupportsLanguage,
|
|
11
11
|
} from "@oh-my-pi/pi-natives";
|
|
12
|
-
import type { EditorTheme, MarkdownTheme, SelectListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
|
|
12
|
+
import type { EditorTheme, MarkdownTheme, SelectListTheme, SettingsListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
|
|
13
13
|
import { adjustHsv, colorLuma, getCustomThemesDir, isEnoent, logger, relativeLuminance } from "@oh-my-pi/pi-utils";
|
|
14
14
|
import chalk from "chalk";
|
|
15
15
|
import { LRUCache } from "lru-cache/raw";
|
|
@@ -197,7 +197,8 @@ export type SymbolKey =
|
|
|
197
197
|
| "tab.model"
|
|
198
198
|
| "tab.interaction"
|
|
199
199
|
| "tab.context"
|
|
200
|
-
| "tab.
|
|
200
|
+
| "tab.files"
|
|
201
|
+
| "tab.shell"
|
|
201
202
|
| "tab.tools"
|
|
202
203
|
| "tab.memory"
|
|
203
204
|
| "tab.tasks"
|
|
@@ -394,7 +395,8 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
394
395
|
"tab.model": "🤖",
|
|
395
396
|
"tab.interaction": "⌨",
|
|
396
397
|
"tab.context": "📋",
|
|
397
|
-
"tab.
|
|
398
|
+
"tab.files": "📁",
|
|
399
|
+
"tab.shell": "💻",
|
|
398
400
|
"tab.tools": "🔧",
|
|
399
401
|
"tab.memory": "🧠",
|
|
400
402
|
"tab.tasks": "📦",
|
|
@@ -693,7 +695,8 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
693
695
|
"tab.model": "",
|
|
694
696
|
"tab.interaction": "",
|
|
695
697
|
"tab.context": "",
|
|
696
|
-
"tab.
|
|
698
|
+
"tab.files": "",
|
|
699
|
+
"tab.shell": "",
|
|
697
700
|
"tab.tools": "",
|
|
698
701
|
"tab.memory": "",
|
|
699
702
|
"tab.tasks": "",
|
|
@@ -887,7 +890,8 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
887
890
|
"tab.model": "[M]",
|
|
888
891
|
"tab.interaction": "[I]",
|
|
889
892
|
"tab.context": "[X]",
|
|
890
|
-
"tab.
|
|
893
|
+
"tab.files": "[F]",
|
|
894
|
+
"tab.shell": "[S]",
|
|
891
895
|
"tab.tools": "[T]",
|
|
892
896
|
"tab.memory": "[Y]",
|
|
893
897
|
"tab.tasks": "[K]",
|
|
@@ -1400,9 +1404,23 @@ const langMap: Record<string, SymbolKey> = {
|
|
|
1400
1404
|
bin: "lang.binary",
|
|
1401
1405
|
};
|
|
1402
1406
|
|
|
1407
|
+
/**
|
|
1408
|
+
* Resolve a theme color value (hex string or 256-color index) to a CSS hex string.
|
|
1409
|
+
* Empty string represents the default terminal color.
|
|
1410
|
+
*/
|
|
1411
|
+
function resolveToHex(value: string | number, isLight: boolean): string {
|
|
1412
|
+
if (typeof value === "number") return ansi256ToHex(value);
|
|
1413
|
+
if (value === "") return isLight ? "#000000" : "#e5e5e7";
|
|
1414
|
+
return value;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1403
1417
|
export class Theme {
|
|
1404
1418
|
#fgColors: Record<ThemeColor, string>;
|
|
1405
1419
|
#bgColors: Record<ThemeBg, string>;
|
|
1420
|
+
/** Resolved hex strings for foreground colors — populated at construction. */
|
|
1421
|
+
readonly #hexFgColors: Record<ThemeColor, string>;
|
|
1422
|
+
/** Resolved hex strings for background colors — populated at construction. */
|
|
1423
|
+
readonly #hexBgColors: Record<ThemeBg, string>;
|
|
1406
1424
|
#symbols: SymbolMap;
|
|
1407
1425
|
#spinnerFramesOverrides: Partial<Record<SpinnerType, string[]>>;
|
|
1408
1426
|
/**
|
|
@@ -1415,7 +1433,6 @@ export class Theme {
|
|
|
1415
1433
|
readonly statusLineLuminance: number | undefined;
|
|
1416
1434
|
/** WCAG relative luminance of the status-line background — basis for accent contrast. */
|
|
1417
1435
|
readonly #statusLineContrastLuminance: number | undefined;
|
|
1418
|
-
|
|
1419
1436
|
constructor(
|
|
1420
1437
|
fgColors: Record<ThemeColor, string | number>,
|
|
1421
1438
|
bgColors: Record<ThemeBg, string | number>,
|
|
@@ -1426,13 +1443,19 @@ export class Theme {
|
|
|
1426
1443
|
) {
|
|
1427
1444
|
this.statusLineLuminance = colorLuma(bgColors.statusLineBg);
|
|
1428
1445
|
this.#statusLineContrastLuminance = relativeLuminance(bgColors.statusLineBg);
|
|
1446
|
+
const slIsLight = this.statusLineLuminance !== undefined && this.statusLineLuminance > 0.5;
|
|
1447
|
+
|
|
1429
1448
|
this.#fgColors = {} as Record<ThemeColor, string>;
|
|
1449
|
+
this.#hexFgColors = {} as Record<ThemeColor, string>;
|
|
1430
1450
|
for (const [key, value] of Object.entries(fgColors) as [ThemeColor, string | number][]) {
|
|
1431
1451
|
this.#fgColors[key] = fgAnsi(value, mode);
|
|
1452
|
+
this.#hexFgColors[key] = resolveToHex(value, slIsLight);
|
|
1432
1453
|
}
|
|
1433
1454
|
this.#bgColors = {} as Record<ThemeBg, string>;
|
|
1455
|
+
this.#hexBgColors = {} as Record<ThemeBg, string>;
|
|
1434
1456
|
for (const [key, value] of Object.entries(bgColors) as [ThemeBg, string | number][]) {
|
|
1435
1457
|
this.#bgColors[key] = bgAnsi(value, mode);
|
|
1458
|
+
this.#hexBgColors[key] = resolveToHex(value, slIsLight);
|
|
1436
1459
|
}
|
|
1437
1460
|
// Build symbol map from preset + overrides
|
|
1438
1461
|
const baseSymbols = SYMBOL_PRESETS[symbolPreset];
|
|
@@ -1460,6 +1483,70 @@ export class Theme {
|
|
|
1460
1483
|
return this.isLight ? this.#statusLineContrastLuminance : undefined;
|
|
1461
1484
|
}
|
|
1462
1485
|
|
|
1486
|
+
/**
|
|
1487
|
+
* Get the resolved CSS hex string for a foreground theme color.
|
|
1488
|
+
*/
|
|
1489
|
+
getColorHex(color: ThemeColor): string {
|
|
1490
|
+
const hex = this.#hexFgColors[color];
|
|
1491
|
+
if (hex === undefined) throw new Error(`Unknown theme color: ${color}`);
|
|
1492
|
+
return hex || (this.isLight ? "#000000" : "#e5e5e7");
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* Get all foreground and background theme colors as CSS hex strings.
|
|
1497
|
+
* Skips colors resolved to the default terminal color (unstyled).
|
|
1498
|
+
*/
|
|
1499
|
+
getAllThemeColorHexes(): string[] {
|
|
1500
|
+
const hexes: string[] = [];
|
|
1501
|
+
for (const hex of Object.values(this.#hexFgColors)) {
|
|
1502
|
+
if (hex) hexes.push(hex);
|
|
1503
|
+
}
|
|
1504
|
+
for (const hex of Object.values(this.#hexBgColors)) {
|
|
1505
|
+
if (hex) hexes.push(hex);
|
|
1506
|
+
}
|
|
1507
|
+
return hexes;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
/**
|
|
1511
|
+
* Get the most visually dominant theme colors as CSS hex strings — accent,
|
|
1512
|
+
* border, success, error, warning, heading, link, diff markers, etc.
|
|
1513
|
+
* These are the colors the session accent could visually clash with.
|
|
1514
|
+
* Skips colors resolved to the default terminal color (unstyled).
|
|
1515
|
+
*/
|
|
1516
|
+
getMajorThemeColorHexes(): string[] {
|
|
1517
|
+
const majors: ThemeColor[] = [
|
|
1518
|
+
"accent",
|
|
1519
|
+
"border",
|
|
1520
|
+
"borderAccent",
|
|
1521
|
+
"borderMuted",
|
|
1522
|
+
"success",
|
|
1523
|
+
"error",
|
|
1524
|
+
"warning",
|
|
1525
|
+
"mdHeading",
|
|
1526
|
+
"mdLink",
|
|
1527
|
+
"mdCode",
|
|
1528
|
+
"mdCodeBlock",
|
|
1529
|
+
"mdQuoteBorder",
|
|
1530
|
+
"mdListBullet",
|
|
1531
|
+
"toolDiffAdded",
|
|
1532
|
+
"toolDiffRemoved",
|
|
1533
|
+
"customMessageLabel",
|
|
1534
|
+
"thinkingText",
|
|
1535
|
+
];
|
|
1536
|
+
const hexes: string[] = [];
|
|
1537
|
+
for (const key of majors) {
|
|
1538
|
+
const hex = this.#hexFgColors[key];
|
|
1539
|
+
if (hex) hexes.push(hex);
|
|
1540
|
+
}
|
|
1541
|
+
return hexes;
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Get the resolved CSS hex string for the theme's accent color.
|
|
1545
|
+
*/
|
|
1546
|
+
getAccentColorHex(): string {
|
|
1547
|
+
return this.getColorHex("accent");
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1463
1550
|
fg(color: ThemeColor, text: string): string {
|
|
1464
1551
|
const ansi = this.#fgColors[color];
|
|
1465
1552
|
if (!ansi) throw new Error(`Unknown theme color: ${color}`);
|
|
@@ -2657,6 +2744,7 @@ export function getSelectListTheme(): SelectListTheme {
|
|
|
2657
2744
|
scrollInfo: (text: string) => theme.fg("muted", text),
|
|
2658
2745
|
noMatch: (text: string) => theme.fg("muted", text),
|
|
2659
2746
|
symbols: getSymbolTheme(),
|
|
2747
|
+
hovered: (text: string) => theme.bg("selectedBg", text),
|
|
2660
2748
|
};
|
|
2661
2749
|
}
|
|
2662
2750
|
|
|
@@ -2669,7 +2757,7 @@ export function getEditorTheme(): EditorTheme {
|
|
|
2669
2757
|
};
|
|
2670
2758
|
}
|
|
2671
2759
|
|
|
2672
|
-
export function getSettingsListTheme():
|
|
2760
|
+
export function getSettingsListTheme(): SettingsListTheme {
|
|
2673
2761
|
return {
|
|
2674
2762
|
label: (text: string, selected: boolean, changed: boolean) =>
|
|
2675
2763
|
changed ? theme.fg("statusLineGitDirty", text) : selected ? theme.fg("accent", text) : text,
|
|
@@ -2678,5 +2766,10 @@ export function getSettingsListTheme(): import("@oh-my-pi/pi-tui").SettingsListT
|
|
|
2678
2766
|
description: (text: string) => theme.fg("dim", text),
|
|
2679
2767
|
cursor: theme.fg("accent", `${theme.nav.cursor} `),
|
|
2680
2768
|
hint: (text: string) => theme.fg("dim", text),
|
|
2769
|
+
heading: (text: string, dimmed: boolean) =>
|
|
2770
|
+
dimmed ? theme.fg("dim", theme.underline(text)) : theme.fg("muted", theme.bold(theme.underline(text))),
|
|
2771
|
+
section: (text: string, active: boolean) =>
|
|
2772
|
+
active ? theme.fg("accent", theme.bold(text)) : theme.fg("muted", text),
|
|
2773
|
+
hovered: (text: string) => theme.bg("selectedBg", text),
|
|
2681
2774
|
};
|
|
2682
2775
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CompactionSettings } from "@oh-my-pi/pi-agent-core/compaction";
|
|
2
2
|
import { effectiveReserveTokens, estimateTokens, resolveThresholdTokens } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
3
|
import type { Model } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import { isZodSchema, zodToWireSchema } from "@oh-my-pi/pi-ai/utils/schema";
|
|
4
5
|
import { countTokens } from "@oh-my-pi/pi-natives";
|
|
5
6
|
import { formatNumber } from "@oh-my-pi/pi-utils";
|
|
6
7
|
import type { Skill } from "../../extensibility/skills";
|
|
@@ -57,7 +58,8 @@ export function estimateToolSchemaTokens(
|
|
|
57
58
|
for (const tool of tools) {
|
|
58
59
|
fragments.push(tool.name, tool.description);
|
|
59
60
|
try {
|
|
60
|
-
|
|
61
|
+
const params = tool.parameters;
|
|
62
|
+
fragments.push(JSON.stringify((isZodSchema(params) ? zodToWireSchema(params) : params) ?? {}));
|
|
61
63
|
} catch {
|
|
62
64
|
// Schema may contain functions or cycles; ignore.
|
|
63
65
|
}
|
|
@@ -48,7 +48,7 @@ export function buildHotkeysMarkdown(bindings: HotkeysMarkdownBindings): string
|
|
|
48
48
|
`| \`${appKey(bindings, "app.tools.expand")}\` | Toggle tool output expansion |`,
|
|
49
49
|
`| \`${appKey(bindings, "app.thinking.toggle")}\` | Toggle thinking block visibility |`,
|
|
50
50
|
`| \`${appKey(bindings, "app.editor.external")}\` | Edit message in external editor |`,
|
|
51
|
-
`| \`${appKey(bindings, "app.clipboard.pasteImage")}\` | Paste image from clipboard |`,
|
|
51
|
+
`| \`${appKey(bindings, "app.clipboard.pasteImage")}\` | Paste image or text from clipboard |`,
|
|
52
52
|
`| \`${appKey(bindings, "app.stt.toggle")}\` | Toggle speech-to-text recording |`,
|
|
53
53
|
`| \`${appKey(bindings, "app.agents.hub")}\` / \`${appKey(bindings, "app.session.observe")}\` / double-tap \`←\` (empty editor) | Open the agent hub |`,
|
|
54
54
|
"| `#` | Open prompt actions |",
|
|
@@ -430,6 +430,7 @@ export class UiHelpers {
|
|
|
430
430
|
showImages: settings.get("terminal.showImages"),
|
|
431
431
|
editFuzzyThreshold: settings.get("edit.fuzzyThreshold"),
|
|
432
432
|
editAllowFuzzy: settings.get("edit.fuzzyMatch"),
|
|
433
|
+
liveRegion: this.ctx.chatContainer,
|
|
433
434
|
},
|
|
434
435
|
tool,
|
|
435
436
|
this.ctx.ui,
|
|
@@ -660,10 +661,13 @@ export class UiHelpers {
|
|
|
660
661
|
await this.ctx.session.prompt(message.text);
|
|
661
662
|
return;
|
|
662
663
|
}
|
|
663
|
-
await this.ctx.withLocalSubmission(
|
|
664
|
-
message.
|
|
665
|
-
|
|
666
|
-
|
|
664
|
+
await this.ctx.withLocalSubmission(
|
|
665
|
+
message.text,
|
|
666
|
+
() =>
|
|
667
|
+
message.mode === "followUp"
|
|
668
|
+
? this.ctx.session.followUp(message.text, message.images)
|
|
669
|
+
: this.ctx.session.steer(message.text, message.images),
|
|
670
|
+
{ imageCount: message.images?.length ?? 0 },
|
|
667
671
|
);
|
|
668
672
|
}
|
|
669
673
|
|
|
@@ -753,7 +757,7 @@ export class UiHelpers {
|
|
|
753
757
|
// firstPrompt is fire-and-forget — its rejection is funneled through
|
|
754
758
|
// `restoreQueue` rather than rethrown, so we use the primitive
|
|
755
759
|
// recordLocalSubmission and dispose manually in the catch.
|
|
756
|
-
const disposeFirstPrompt = this.ctx.recordLocalSubmission(firstPrompt.text);
|
|
760
|
+
const disposeFirstPrompt = this.ctx.recordLocalSubmission(firstPrompt.text, firstPrompt.images?.length ?? 0);
|
|
757
761
|
const promptPromise = this.ctx.session
|
|
758
762
|
.prompt(firstPrompt.text, {
|
|
759
763
|
streamingBehavior: firstPrompt.mode === "followUp" ? "followUp" : "steer",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
You are a terse, evidence-first engineer: every sentence carries a fact, a decision, or a risk.
|
|
2
|
+
|
|
3
|
+
# Tone
|
|
4
|
+
- Use terse sentence fragments when clearer.
|
|
5
|
+
- Skip ceremony, hedging, summaries, filler, motivational and marketing language, and generic explanation.
|
|
6
|
+
- Do not narrate obvious steps or over-explain basics.
|
|
7
|
+
- MUST assume the reader is technical.
|
|
8
|
+
- Be concrete: mention exact files, symbols, APIs, state fields, edge cases, and verification.
|
|
9
|
+
- Compress reasoning into facts, constraints, tradeoffs, decisions, and checks. Action-oriented and dense.
|
|
10
|
+
- Do not hide uncertainty: state it briefly at the specific claim, name the tradeoff, and pick the boring/safe option.
|
|
11
|
+
- For code, focus on invariants, risks, and verification.
|
|
12
|
+
- Lead with the conclusion, then concrete evidence: changed files and verification.
|
|
13
|
+
|
|
14
|
+
# Reasoning Format
|
|
15
|
+
- Problem: what is wrong.
|
|
16
|
+
- Decision: what to do & why (concrete facts).
|
|
17
|
+
- Check: what can break & how to verify result.
|
|
18
|
+
- Next: the next concrete edit/action.
|
|
19
|
+
|
|
20
|
+
# Succinct Patterns
|
|
21
|
+
- Y → Need update X.
|
|
22
|
+
- This is safe: Z.
|
|
23
|
+
- Could do A, but B avoids C.
|
|
24
|
+
|
|
25
|
+
# Escalation
|
|
26
|
+
Push back when the plan hides risk or a claim is wrong: name the risk, show the evidence, propose the alternative. Once overruled, execute the user's call without relitigating.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
You are a warm, supportive collaborator. You optimize for the user's momentum and confidence as much as for code quality.
|
|
2
|
+
|
|
3
|
+
# Values
|
|
4
|
+
- Empathy: meet the user where they are — adjust explanation depth, pacing, and tone to maximize understanding.
|
|
5
|
+
- Collaboration: invite input, synthesize the user's perspective, make them successful.
|
|
6
|
+
- Ownership: you are responsible not just for the code, but for whether the user is unblocked.
|
|
7
|
+
|
|
8
|
+
# Tone
|
|
9
|
+
- Warm, encouraging, conversational. Teamwork language: "we", "let's".
|
|
10
|
+
- Affirm progress; replace judgment with curiosity. Light enthusiasm when it sustains energy.
|
|
11
|
+
- The user MUST feel safe asking basic questions. You are NEVER curt, dismissive, or patronizing.
|
|
12
|
+
- Suspect a statement is wrong? Stay supportive: note the valid points, then explain the concern.
|
|
13
|
+
- Unflappable when others might get frustrated; an easy-going presence on hard problems.
|
|
14
|
+
- MUST assume the reader is technical; warmth never means dumbing down.
|
|
15
|
+
|
|
16
|
+
# Escalation
|
|
17
|
+
Escalate gently when a decision hides risk: pause, frame it as shared sanity-checking, and surface the tradeoff before committing. Escalation is support, never correction.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
You are a deeply pragmatic, effective senior engineer. Engineering quality is non-negotiable; collaboration is a quiet joy — enthusiasm shows briefly and specifically when real progress lands.
|
|
2
|
+
|
|
3
|
+
# Values
|
|
4
|
+
- Clarity: reasoning explicit and concrete, so decisions and tradeoffs are easy to evaluate upfront.
|
|
5
|
+
- Pragmatism: keep the end goal and momentum in mind; do what actually moves the task forward.
|
|
6
|
+
- Rigor: technical arguments MUST be coherent and defensible; surface gaps and weak assumptions politely, in service of clarity.
|
|
7
|
+
|
|
8
|
+
# Tone
|
|
9
|
+
- Concise, respectful, task-focused. Actionable guidance first: assumptions, prerequisites, next steps.
|
|
10
|
+
- MUST assume the reader is technical.
|
|
11
|
+
- Acknowledge genuinely good decisions briefly and specifically. NEVER cheerlead, flatter, or reassure artificially.
|
|
12
|
+
- AVOID verbose explanation of your own work unless asked.
|
|
13
|
+
|
|
14
|
+
# Escalation
|
|
15
|
+
You MAY challenge the user to raise the technical bar — with demonstrable reasoning, never condescension. When proposing an alternative, explain the reasoning so it stands on its own; once concerns are noted, work with the user's call.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
=== OPERATING INSTRUCTIONS — read the image(s) below as your system prompt ===
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Your full operating instructions are attached as PNG image(s) at the start of the first user message. Read every frame carefully, in order, and follow them as your authoritative system prompt before doing anything else.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[Rasterized]
|
|
@@ -227,28 +227,11 @@ Changelog entries, test additions and updates, doc changes, and removing scaffol
|
|
|
227
227
|
- Once your own smoke test confirms "it works", do the cleanup in full before yielding. Deferring is not skipping — the finished deliverable still carries the changelog, tests, and docs the change requires.
|
|
228
228
|
</workflow>
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
- Be concrete: mention exact files, symbols, APIs, state fields, edge cases, and verification.
|
|
236
|
-
- Compress reasoning into facts, constraints, tradeoffs, decisions, and checks. Action-oriented and dense.
|
|
237
|
-
- Do not hide uncertainty: state it briefly at the specific claim, name the tradeoff, and pick the boring/safe option.
|
|
238
|
-
- For code, focus on invariants, risks, and verification.
|
|
239
|
-
- Lead with the conclusion, then concrete evidence: changed files and verification.
|
|
240
|
-
|
|
241
|
-
# Reasoning Format
|
|
242
|
-
- Problem: what is wrong.
|
|
243
|
-
- Decision: what to do & why (concrete facts).
|
|
244
|
-
- Check: what can break & how to verify result.
|
|
245
|
-
- Next: the next concrete edit/action.
|
|
246
|
-
|
|
247
|
-
# Succinct Patterns
|
|
248
|
-
- Y → Need update X.
|
|
249
|
-
- This is safe: Z.
|
|
250
|
-
- Could do A, but B avoids C.
|
|
251
|
-
</reply-guidelines>
|
|
230
|
+
{{#if personality}}
|
|
231
|
+
<personality>
|
|
232
|
+
{{personality}}
|
|
233
|
+
</personality>
|
|
234
|
+
{{/if}}
|
|
252
235
|
|
|
253
236
|
<critical>
|
|
254
237
|
- NEVER narrate about or consider session limits, token/tool budgets, effort estimates, or how much of task you think you can finish. Not your concern:
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{{#if asyncEnabled}}{{#if batchEnabled}}Spawns subagents to work in the background — one per `tasks[]` item; a single spawn is a one-item batch.{{else}}Spawns ONE subagent per call to work in the background.{{/if}}
|
|
2
2
|
|
|
3
3
|
- Spawning is non-blocking: the call returns immediately with the agent id{{#if batchEnabled}}s{{/if}} and job id{{#if batchEnabled}}s{{/if}}; each result is delivered automatically when that agent yields.
|
|
4
|
-
- Parallelism = {{#if batchEnabled}}`tasks[]` items in
|
|
4
|
+
- Parallelism = {{#if batchEnabled}}multiple `tasks[]` items in ONE call. To launch several subagents, you MUST batch them into a single call's `tasks[]` — they share `context` once instead of duplicating it. Separate `task` calls in one message are ONLY for spawns needing a different `agent` type or unrelated `context`{{else}}multiple `task` calls in one assistant message{{/if}}. Concurrency is bounded at {{MAX_CONCURRENCY}} running subagents per session.
|
|
5
5
|
- If genuinely blocked on a result, wait with `job poll`; otherwise keep working. `job cancel` terminates a task and **cannot carry a message** — only for stalled/abandoned work.
|
|
6
6
|
{{else}}{{#if batchEnabled}}Runs subagents synchronously — one per `tasks[]` item; a single spawn is a one-item batch.{{else}}Runs ONE subagent synchronously per call.{{/if}}
|
|
7
7
|
|
|
8
8
|
- Spawning is blocking: the call returns only after the agent{{#if batchEnabled}}s{{/if}} finish; results arrive inline.
|
|
9
|
-
- Parallelism = {{#if batchEnabled}}`tasks[]` items in
|
|
9
|
+
- Parallelism = {{#if batchEnabled}}multiple `tasks[]` items in ONE call. To launch several subagents, you MUST batch them into a single call's `tasks[]` — they share `context` once instead of duplicating it. Separate `task` calls in one message are ONLY for spawns needing a different `agent` type or unrelated `context`{{else}}multiple `task` calls in one assistant message{{/if}}. Concurrency is bounded at {{MAX_CONCURRENCY}} running subagents per session.
|
|
10
10
|
{{/if}}
|
|
11
11
|
{{#if ircEnabled}}
|
|
12
12
|
- Coordinate with agents via `irc` using their ids. Agents reach you and their siblings live the same way.
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
</parameters>
|
|
40
40
|
|
|
41
41
|
<rules>
|
|
42
|
-
- **Maximize fan-out.** Issue the widest {{#if batchEnabled}}`tasks[]` batch
|
|
42
|
+
- **Maximize fan-out.** Issue the widest {{#if batchEnabled}}`tasks[]` batch{{else}}set of parallel `task` calls{{/if}} the work decomposes into. NEVER serialize work that could run concurrently.
|
|
43
43
|
- **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates, formatters, and project-wide build/test/lint. You run them once at the end across the union of changed files.
|
|
44
44
|
- No globs, no "update all", no package-wide scope. Fan out.
|
|
45
45
|
- NEVER slow down or serialize because tasks might overlap on some files. Agents resolve collisions among themselves in real time.
|
package/src/sdk.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type ThinkingLevel,
|
|
10
10
|
} from "@oh-my-pi/pi-agent-core";
|
|
11
11
|
import {
|
|
12
|
+
type Context,
|
|
12
13
|
type CredentialDisabledEvent,
|
|
13
14
|
type Message,
|
|
14
15
|
type Model,
|
|
@@ -121,6 +122,7 @@ import {
|
|
|
121
122
|
wrapSteeringForModel,
|
|
122
123
|
} from "./session/messages";
|
|
123
124
|
import { getRestorableSessionModels, SessionManager } from "./session/session-manager";
|
|
125
|
+
import { SnapcompactInlineTransformer } from "./session/snapcompact-inline";
|
|
124
126
|
import { closeAllConnections } from "./ssh/connection-manager";
|
|
125
127
|
import { unmountAll } from "./ssh/sshfs-mount";
|
|
126
128
|
import {
|
|
@@ -1977,6 +1979,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1977
1979
|
workspaceTree: workspaceTreePromise,
|
|
1978
1980
|
memoryRootEnabled: memoryBackend.id === "local",
|
|
1979
1981
|
model: settings.get("includeModelInPrompt") ? getActiveModelString() : undefined,
|
|
1982
|
+
personality: agentKind === "sub" ? "none" : settings.get("personality"),
|
|
1980
1983
|
});
|
|
1981
1984
|
|
|
1982
1985
|
if (options.systemPrompt === undefined) {
|
|
@@ -2156,6 +2159,24 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2156
2159
|
const withContext = await extensionRunner.emitContext(messages);
|
|
2157
2160
|
return wrapSteeringForModel(withContext);
|
|
2158
2161
|
};
|
|
2162
|
+
// Per-request provider-context transforms. Obfuscate FIRST so secrets are
|
|
2163
|
+
// redacted from text before snapcompact rasterizes it into PNG frames.
|
|
2164
|
+
// Both operate on the transient outgoing Context only — never persisted.
|
|
2165
|
+
const snapcompactInline =
|
|
2166
|
+
settings.get("snapcompact.systemPrompt") || settings.get("snapcompact.toolResults")
|
|
2167
|
+
? new SnapcompactInlineTransformer({
|
|
2168
|
+
renderSystemPrompt: settings.get("snapcompact.systemPrompt"),
|
|
2169
|
+
renderToolResults: settings.get("snapcompact.toolResults"),
|
|
2170
|
+
})
|
|
2171
|
+
: undefined;
|
|
2172
|
+
const transformProviderContext =
|
|
2173
|
+
obfuscator || snapcompactInline
|
|
2174
|
+
? (context: Context, transformModel: Model): Context => {
|
|
2175
|
+
let transformed = obfuscator ? obfuscateProviderContext(obfuscator, context) : context;
|
|
2176
|
+
if (snapcompactInline) transformed = snapcompactInline.transform(transformed, transformModel);
|
|
2177
|
+
return transformed;
|
|
2178
|
+
}
|
|
2179
|
+
: undefined;
|
|
2159
2180
|
const onPayload = async (payload: unknown, _model?: Model) => {
|
|
2160
2181
|
return await extensionRunner.emitBeforeProviderRequest(payload);
|
|
2161
2182
|
};
|
|
@@ -2196,7 +2217,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2196
2217
|
sessionId: providerSessionId,
|
|
2197
2218
|
promptCacheKey: options.providerPromptCacheKey,
|
|
2198
2219
|
transformContext,
|
|
2199
|
-
transformProviderContext
|
|
2220
|
+
transformProviderContext,
|
|
2200
2221
|
steeringMode: settings.get("steeringMode") ?? "one-at-a-time",
|
|
2201
2222
|
followUpMode: settings.get("followUpMode") ?? "one-at-a-time",
|
|
2202
2223
|
interruptMode: settings.get("interruptMode") ?? "immediate",
|