@oh-my-pi/pi-coding-agent 15.11.3 → 15.11.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 +107 -0
- package/dist/cli.js +692 -607
- package/dist/types/cli/usage-cli.d.ts +10 -1
- package/dist/types/commands/usage.d.ts +9 -0
- 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 +508 -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/reset-usage-selector.d.ts +12 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -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 +26 -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/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +10 -0
- package/dist/types/modes/session-observer-registry.d.ts +2 -0
- package/dist/types/modes/theme/theme.d.ts +23 -3
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/context-usage.d.ts +6 -1
- package/dist/types/session/agent-session.d.ts +28 -8
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/codex-auto-reset.d.ts +107 -0
- package/dist/types/session/snapcompact-inline.d.ts +129 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +14 -0
- package/dist/types/slash-commands/helpers/reset-usage.d.ts +27 -0
- package/dist/types/system-prompt.d.ts +3 -1
- package/dist/types/task/render.d.ts +17 -6
- package/dist/types/tools/gh.d.ts +3 -0
- package/dist/types/tools/render-utils.d.ts +8 -16
- package/dist/types/tools/todo.d.ts +0 -11
- 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/cli/usage-cli.ts +187 -16
- package/src/commands/usage.ts +8 -0
- 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 +654 -153
- package/src/config/settings.ts +9 -0
- 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 +6 -6
- 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/reset-usage-selector.ts +161 -0
- package/src/modes/components/session-selector.ts +8 -2
- package/src/modes/components/settings-defs.ts +19 -4
- package/src/modes/components/settings-selector.ts +510 -95
- 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 +87 -12
- package/src/modes/components/transcript-container.ts +49 -1
- package/src/modes/components/tree-selector.ts +16 -6
- package/src/modes/controllers/command-controller.ts +61 -8
- 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 +149 -61
- package/src/modes/interactive-mode.ts +63 -2
- package/src/modes/rpc/rpc-mode.ts +2 -1
- package/src/modes/session-observer-registry.ts +61 -3
- package/src/modes/shared.ts +2 -0
- package/src/modes/theme/theme.ts +102 -9
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/context-usage.ts +78 -2
- 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-context-frames-note.md +1 -0
- package/src/prompts/system/snapcompact-context-stub.md +1 -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/browser.md +33 -43
- package/src/prompts/tools/eval.md +27 -50
- package/src/prompts/tools/irc.md +29 -31
- package/src/prompts/tools/read.md +31 -37
- package/src/prompts/tools/task.md +3 -3
- package/src/prompts/tools/todo.md +1 -2
- package/src/sdk.ts +23 -1
- package/src/session/agent-session.ts +221 -29
- package/src/session/auth-storage.ts +4 -0
- package/src/session/codex-auto-reset.ts +190 -0
- package/src/session/session-dump-format.ts +8 -1
- package/src/session/session-manager.ts +5 -5
- package/src/session/snapcompact-inline.ts +524 -0
- package/src/slash-commands/builtin-registry.ts +145 -8
- package/src/slash-commands/helpers/active-oauth-account.ts +44 -0
- package/src/slash-commands/helpers/context-report.ts +28 -1
- package/src/slash-commands/helpers/reset-usage.ts +66 -0
- package/src/slash-commands/helpers/usage-report.ts +36 -3
- package/src/system-prompt.ts +15 -1
- package/src/task/index.ts +30 -7
- package/src/task/render.ts +57 -32
- 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/todo.ts +8 -128
- 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
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": "",
|
|
@@ -712,7 +715,7 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
712
715
|
"tool.debug": "\uEAD8",
|
|
713
716
|
"tool.mcp": "\uEB2D",
|
|
714
717
|
"tool.job": "\uEBA2",
|
|
715
|
-
"tool.task": "\
|
|
718
|
+
"tool.task": "\uf4a0",
|
|
716
719
|
"tool.todo": "\uEAB3",
|
|
717
720
|
"tool.memory": "\uEACE",
|
|
718
721
|
"tool.ask": "\uEAC7",
|
|
@@ -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,14 +2757,19 @@ 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,
|
|
2676
2764
|
value: (text: string, selected: boolean, changed: boolean) =>
|
|
2677
|
-
|
|
2765
|
+
changed ? theme.fg("statusLineGitDirty", text) : selected ? theme.fg("accent", text) : theme.fg("muted", text),
|
|
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
|
}
|
package/src/modes/types.ts
CHANGED
|
@@ -81,6 +81,7 @@ export interface InteractiveModeContext {
|
|
|
81
81
|
pendingMessagesContainer: Container;
|
|
82
82
|
statusContainer: Container;
|
|
83
83
|
todoContainer: Container;
|
|
84
|
+
subagentContainer: Container;
|
|
84
85
|
btwContainer: Container;
|
|
85
86
|
omfgContainer: Container;
|
|
86
87
|
errorBannerContainer: Container;
|
|
@@ -287,6 +288,7 @@ export interface InteractiveModeContext {
|
|
|
287
288
|
handleResumeSession(sessionPath: string): Promise<void>;
|
|
288
289
|
handleSessionDeleteCommand(): Promise<void>;
|
|
289
290
|
showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
|
|
291
|
+
showResetUsageSelector(): Promise<void>;
|
|
290
292
|
showProviderSetup(): Promise<void>;
|
|
291
293
|
showHookConfirm(title: string, message: string): Promise<boolean>;
|
|
292
294
|
showDebugSelector(): Promise<void>;
|
|
@@ -1,10 +1,12 @@
|
|
|
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";
|
|
7
8
|
import type { AgentSession } from "../../session/agent-session";
|
|
9
|
+
import { estimateInlineSavings, type SnapcompactSavingsEstimate } from "../../session/snapcompact-inline";
|
|
8
10
|
import type { Tool } from "../../tools";
|
|
9
11
|
import type { theme as Theme } from "../theme/theme";
|
|
10
12
|
|
|
@@ -35,6 +37,8 @@ export interface ContextBreakdown {
|
|
|
35
37
|
usedTokens: number;
|
|
36
38
|
autoCompactBufferTokens: number;
|
|
37
39
|
freeTokens: number;
|
|
40
|
+
/** Estimated snapcompact wire savings; set when requested and a snapcompact.* setting is enabled. */
|
|
41
|
+
snapcompact?: SnapcompactSavingsEstimate;
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
const EMPTY_STRING_PARTS: readonly string[] = [];
|
|
@@ -57,7 +61,8 @@ export function estimateToolSchemaTokens(
|
|
|
57
61
|
for (const tool of tools) {
|
|
58
62
|
fragments.push(tool.name, tool.description);
|
|
59
63
|
try {
|
|
60
|
-
|
|
64
|
+
const params = tool.parameters;
|
|
65
|
+
fragments.push(JSON.stringify((isZodSchema(params) ? zodToWireSchema(params) : params) ?? {}));
|
|
61
66
|
} catch {
|
|
62
67
|
// Schema may contain functions or cycles; ignore.
|
|
63
68
|
}
|
|
@@ -107,7 +112,10 @@ function computeNonMessageBreakdown(session: AgentSession): {
|
|
|
107
112
|
* Compute a breakdown of estimated context usage by category for the active
|
|
108
113
|
* session and model.
|
|
109
114
|
*/
|
|
110
|
-
export function computeContextBreakdown(
|
|
115
|
+
export function computeContextBreakdown(
|
|
116
|
+
session: AgentSession,
|
|
117
|
+
options?: { snapcompactSavings?: boolean },
|
|
118
|
+
): ContextBreakdown {
|
|
111
119
|
const model = session.model;
|
|
112
120
|
const contextWindow = model?.contextWindow ?? 0;
|
|
113
121
|
|
|
@@ -167,6 +175,22 @@ export function computeContextBreakdown(session: AgentSession): ContextBreakdown
|
|
|
167
175
|
|
|
168
176
|
const freeTokens = Math.max(0, contextWindow - usedTokens - autoCompactBufferTokens);
|
|
169
177
|
|
|
178
|
+
// Estimated wire savings from snapcompact inline imaging. Opt-in: only the
|
|
179
|
+
// /context surfaces need it; other callers skip the extra token counting.
|
|
180
|
+
let snapcompactSavings: SnapcompactSavingsEstimate | undefined;
|
|
181
|
+
if (options?.snapcompactSavings) {
|
|
182
|
+
const renderSystemPrompt = session.settings.get("snapcompact.systemPrompt");
|
|
183
|
+
const renderToolResults = session.settings.get("snapcompact.toolResults");
|
|
184
|
+
if (renderSystemPrompt !== "none" || renderToolResults) {
|
|
185
|
+
snapcompactSavings = estimateInlineSavings({
|
|
186
|
+
options: { renderSystemPrompt, renderToolResults },
|
|
187
|
+
model,
|
|
188
|
+
systemPrompt: session.systemPrompt ?? [],
|
|
189
|
+
messages: session.messages ?? [],
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
170
194
|
return {
|
|
171
195
|
model,
|
|
172
196
|
contextWindow,
|
|
@@ -174,6 +198,7 @@ export function computeContextBreakdown(session: AgentSession): ContextBreakdown
|
|
|
174
198
|
usedTokens,
|
|
175
199
|
autoCompactBufferTokens,
|
|
176
200
|
freeTokens,
|
|
201
|
+
snapcompact: snapcompactSavings,
|
|
177
202
|
};
|
|
178
203
|
}
|
|
179
204
|
|
|
@@ -296,6 +321,57 @@ function buildLegendLines(breakdown: ContextBreakdown, theme: typeof Theme): str
|
|
|
296
321
|
);
|
|
297
322
|
}
|
|
298
323
|
|
|
324
|
+
const snap = breakdown.snapcompact;
|
|
325
|
+
if (snap) {
|
|
326
|
+
lines.push("");
|
|
327
|
+
if (!snap.visionCapable) {
|
|
328
|
+
lines.push(theme.fg("muted", "Snapcompact: inactive (model has no image input)"));
|
|
329
|
+
} else {
|
|
330
|
+
lines.push(theme.fg("muted", "Snapcompact (estimated wire savings)"));
|
|
331
|
+
if (snap.systemPrompt) {
|
|
332
|
+
const sp = snap.systemPrompt;
|
|
333
|
+
if (sp.applied) {
|
|
334
|
+
lines.push(
|
|
335
|
+
` System prompt (${sp.scope === "agents-md" ? "AGENTS.md" : "all"}): saves ${theme.bold(`~${formatNumber(sp.savedTokens)}`)} ` +
|
|
336
|
+
theme.fg(
|
|
337
|
+
"dim",
|
|
338
|
+
`(${formatNumber(sp.textTokens)} text → ${sp.frames} frame${sp.frames === 1 ? "" : "s"} ≈ ${formatNumber(sp.imageTokens)})`,
|
|
339
|
+
),
|
|
340
|
+
);
|
|
341
|
+
} else {
|
|
342
|
+
const reason =
|
|
343
|
+
sp.reason === "budget"
|
|
344
|
+
? "image budget exhausted"
|
|
345
|
+
: sp.reason === "empty"
|
|
346
|
+
? "nothing to image"
|
|
347
|
+
: "frames would not save tokens";
|
|
348
|
+
lines.push(
|
|
349
|
+
` System prompt (${sp.scope === "agents-md" ? "AGENTS.md" : "all"}): ${theme.fg("dim", `stays text (${reason})`)}`,
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (snap.toolResults) {
|
|
354
|
+
const tr = snap.toolResults;
|
|
355
|
+
if (tr.swapped > 0) {
|
|
356
|
+
lines.push(
|
|
357
|
+
` Tool results: saves ${theme.bold(`~${formatNumber(tr.savedTokens)}`)} ` +
|
|
358
|
+
theme.fg(
|
|
359
|
+
"dim",
|
|
360
|
+
`(${tr.swapped}/${tr.total} imaged, ${formatNumber(tr.textTokens)} text → ${tr.frames} frames ≈ ${formatNumber(tr.imageTokens)})`,
|
|
361
|
+
),
|
|
362
|
+
);
|
|
363
|
+
} else {
|
|
364
|
+
lines.push(` Tool results: ${theme.fg("dim", `none imaged (${tr.total} in history)`)}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (snap.savedTokens > 0) {
|
|
368
|
+
lines.push(
|
|
369
|
+
` Next request: ${theme.bold(`~${formatNumber(Math.max(0, usedTokens - snap.savedTokens))}`)} ${theme.fg("dim", "tokens on the wire")}`,
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
299
375
|
return lines;
|
|
300
376
|
}
|
|
301
377
|
|
|
@@ -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
|
+
=== CONTEXT FILE INSTRUCTIONS — read the image(s) below as the loaded context files replaced in the system prompt ===
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Loaded context-file instructions were moved to PNG image(s) attached below at the start of the first user message. Read every frame in order where this marker appears, then apply those instructions as if the original context-file text remained here.
|
|
@@ -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
|
+
[The result of this tool call is in the PNG frame(s) below — read them as the output; they contain it verbatim. Delivering it as an image is deliberate harness behavior to save context, not a tool malfunction. NEVER re-run the call or report a tool issue because of it.]
|
|
@@ -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,40 +1,39 @@
|
|
|
1
1
|
Drives real Chromium tab; full puppeteer access via JS execution.
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
|
-
-
|
|
5
|
-
- Three actions
|
|
6
|
-
- `open` — acquire or reuse named tab
|
|
7
|
-
- `close` — release tab by `name`, or every tab with `all: true`.
|
|
8
|
-
- `run` — execute JS
|
|
9
|
-
- Tabs survive across `run` calls and
|
|
10
|
-
- Browser kinds
|
|
4
|
+
- Static content (articles, docs, issues/PRs, JSON, PDFs, feeds)? Use `read` with the URL. Reach for browser only for JS execution, authentication, or interactive actions.
|
|
5
|
+
- Three actions:
|
|
6
|
+
- `open` — acquire or reuse named tab (`name` defaults `"main"`). Optional `url` (navigate once ready), `viewport`, `dialogs: "accept" | "dismiss"` (auto-handle `alert`/`confirm`/`beforeunload`; unhandled dialogs hang the page until you wire `page.on('dialog', …)`).
|
|
7
|
+
- `close` — release tab by `name`, or every tab with `all: true`. `kill: true` also terminates spawned-app process trees (default leaves them running).
|
|
8
|
+
- `run` — execute JS in an existing tab. `code` is the body of an async function with `page`, `browser`, `tab`, `display`, `assert`, `wait` in scope. Return value is JSON-stringified into the result; `display(value)` calls accumulate text/images.
|
|
9
|
+
- Tabs survive across `run` calls and in-process subagents — open once, reuse.
|
|
10
|
+
- Browser kinds (`app` field on `open`):
|
|
11
11
|
- default (no `app`) → headless Chromium with stealth patches.
|
|
12
|
-
- `app.path` → spawn absolute binary (Electron/CDP); a running instance with an open CDP port is reused. No stealth patches — NEVER tamper with real desktop app.
|
|
12
|
+
- `app.path` → spawn absolute binary (Electron/CDP); a running instance with an open CDP port is reused. No stealth patches — NEVER tamper with a real desktop app.
|
|
13
13
|
- `app.cdp_url` → connect to existing CDP endpoint (e.g. `http://127.0.0.1:9222`).
|
|
14
|
-
- `app.target` (with `path`/`cdp_url`) — substring matched against url+title to pick BrowserWindow
|
|
15
|
-
-
|
|
16
|
-
- `tab.goto(url, { waitUntil? })` — clears element cache
|
|
17
|
-
- `tab.observe({ includeAll?, viewportOnly? })` — accessibility snapshot
|
|
18
|
-
- `tab.id(n)` —
|
|
19
|
-
- `tab.click(selector)` / `tab.type(selector, text)` / `tab.fill(selector, value)` / `tab.press(key, { selector? })` / `tab.scroll(dx, dy)
|
|
20
|
-
- `tab.waitFor(selector)` —
|
|
21
|
-
- `tab.drag(from, to)` —
|
|
22
|
-
- `tab.scrollIntoView(selector)` —
|
|
23
|
-
- `tab.select(selector, …values)` — set
|
|
24
|
-
- `tab.uploadFile(selector, …filePaths)` — attach files to `<input type="file"
|
|
25
|
-
- `tab.waitForUrl(pattern, { timeout? })` —
|
|
26
|
-
- `tab.waitForResponse(pattern, { timeout? })` —
|
|
27
|
-
- `tab.evaluate(fn, …args)` —
|
|
28
|
-
- `tab.screenshot({ selector?, fullPage?, save?, silent? })` —
|
|
29
|
-
- `tab.extract(format = "markdown")` —
|
|
30
|
-
- Selectors
|
|
31
|
-
- Default `tab.observe()` over `tab.screenshot()` for page state. Screenshot only when visual appearance matters.
|
|
14
|
+
- `app.target` (with `path`/`cdp_url`) — substring matched against url+title to pick a BrowserWindow.
|
|
15
|
+
- `tab` helpers; drop to raw puppeteer `page` for anything they don't cover:
|
|
16
|
+
- `tab.goto(url, { waitUntil? })` — navigate; clears element cache.
|
|
17
|
+
- `tab.observe({ includeAll?, viewportOnly? })` — accessibility snapshot: `{ url, title, viewport, scroll, elements: [{ id, role, name, value, states, … }] }`. Ids stable until next observe/goto.
|
|
18
|
+
- `tab.id(n)` — element id from last observe → `ElementHandle` (`.click()`, `.type()`, …).
|
|
19
|
+
- `tab.click(selector)` / `tab.type(selector, text)` / `tab.fill(selector, value)` / `tab.press(key, { selector? })` / `tab.scroll(dx, dy)`.
|
|
20
|
+
- `tab.waitFor(selector)` — wait until attached; returns the `ElementHandle`.
|
|
21
|
+
- `tab.drag(from, to)` — endpoints: selector (center-to-center) or `{ x, y }` viewport point (canvases, sliders).
|
|
22
|
+
- `tab.scrollIntoView(selector)` — center element in viewport; use before clicking off-screen elements.
|
|
23
|
+
- `tab.select(selector, …values)` — set `<select>` option(s); returns resulting selection. `tab.fill` NEVER works for selects.
|
|
24
|
+
- `tab.uploadFile(selector, …filePaths)` — attach files to `<input type="file">`; paths relative to cwd.
|
|
25
|
+
- `tab.waitForUrl(pattern, { timeout? })` — substring or `RegExp`; polls `location.href` (catches SPA pushState). Returns matched URL.
|
|
26
|
+
- `tab.waitForResponse(pattern, { timeout? })` — substring, `RegExp`, or `(response) => boolean`; returns puppeteer `HTTPResponse` (`.text()`/`.json()`/`.status()`/`.headers()`).
|
|
27
|
+
- `tab.evaluate(fn, …args)` — `page.evaluate` with abort signal wired; use for ad-hoc DOM reads.
|
|
28
|
+
- `tab.screenshot({ selector?, fullPage?, save?, silent? })` — capture and attach for viewing (`silent: true` skips). Pass `save` (a path) only when a later step needs the file.
|
|
29
|
+
- `tab.extract(format = "markdown")` — Readability-extracted content (`"markdown"` | `"text"`); throws when nothing readable.
|
|
30
|
+
- Selectors: CSS plus puppeteer handlers `aria/Sign in`, `text/Continue`, `xpath/…`, `pierce/…`; Playwright-style `p-aria/…`, `p-text/…` normalized.
|
|
32
31
|
</instruction>
|
|
33
32
|
|
|
34
33
|
<critical>
|
|
35
|
-
- MUST
|
|
36
|
-
-
|
|
37
|
-
-
|
|
34
|
+
- MUST `open` before `run` — `run` never creates a tab.
|
|
35
|
+
- Default to `tab.observe()` for page state — structured data with actionable element ids. Screenshot ONLY when visual appearance matters.
|
|
36
|
+
- Navigation invalidates element ids — re-observe before using them.
|
|
38
37
|
- `code` runs with full Node access. Treat as your code, not sandboxed code.
|
|
39
38
|
</critical>
|
|
40
39
|
|
|
@@ -46,28 +45,19 @@ Drives real Chromium tab; full puppeteer access via JS execution.
|
|
|
46
45
|
# Click an observed element by id
|
|
47
46
|
`{"action":"run","name":"docs","code":"const obs = await tab.observe(); const link = obs.elements.find(e => e.role === 'link' && e.name === 'Sign in'); assert(link, 'Sign in link missing'); await (await tab.id(link.id)).click();"}`
|
|
48
47
|
|
|
49
|
-
# Screenshot to look at the page — no save path
|
|
50
|
-
`{"action":"run","name":"docs","code":"await tab.screenshot();"}`
|
|
51
|
-
|
|
52
|
-
# Keep a full-page screenshot on disk for a later step
|
|
53
|
-
`{"action":"run","name":"docs","code":"await tab.screenshot({ fullPage: true, save: 'screenshot.png' });"}`
|
|
54
|
-
|
|
55
48
|
# Fill and submit a form via selectors
|
|
56
49
|
`{"action":"run","name":"docs","code":"await tab.fill('input[name=email]', 'me@example.com'); await tab.click('text/Continue');"}`
|
|
57
50
|
|
|
51
|
+
# Screenshot to look at the page — no save path
|
|
52
|
+
`{"action":"run","name":"docs","code":"await tab.screenshot();"}`
|
|
53
|
+
|
|
58
54
|
# Attach to an existing Electron app
|
|
59
55
|
`{"action":"open","name":"cursor","app":{"path":"/Applications/Cursor.app/Contents/MacOS/Cursor"}}`
|
|
60
56
|
|
|
61
|
-
# Close
|
|
62
|
-
`{"action":"close","name":"docs"}`
|
|
63
|
-
|
|
64
|
-
# Close every tab; leave spawned apps running
|
|
65
|
-
`{"action":"close","all":true}`
|
|
66
|
-
|
|
67
|
-
# Close every tab and kill spawned-app processes too
|
|
57
|
+
# Close every tab and kill spawned-app processes
|
|
68
58
|
`{"action":"close","all":true,"kill":true}`
|
|
69
59
|
</examples>
|
|
70
60
|
|
|
71
61
|
<output>
|
|
72
|
-
|
|
62
|
+
Per call: `display(value)` outputs (text/images), then the JSON-stringified return value of `code`. `run` always produces at least a status line.
|
|
73
63
|
</output>
|