@oh-my-pi/pi-coding-agent 3.37.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +89 -0
- package/README.md +44 -3
- package/docs/extensions.md +29 -4
- package/docs/sdk.md +3 -3
- package/package.json +5 -5
- package/src/cli/args.ts +8 -0
- package/src/config.ts +5 -15
- package/src/core/agent-session.ts +193 -47
- package/src/core/auth-storage.ts +16 -3
- package/src/core/bash-executor.ts +79 -14
- package/src/core/custom-commands/types.ts +1 -1
- package/src/core/custom-tools/types.ts +1 -1
- package/src/core/export-html/index.ts +33 -1
- package/src/core/export-html/template.css +99 -0
- package/src/core/export-html/template.generated.ts +1 -1
- package/src/core/export-html/template.js +133 -8
- package/src/core/extensions/index.ts +22 -4
- package/src/core/extensions/loader.ts +152 -214
- package/src/core/extensions/runner.ts +139 -79
- package/src/core/extensions/types.ts +143 -19
- package/src/core/extensions/wrapper.ts +5 -8
- package/src/core/hooks/types.ts +1 -1
- package/src/core/index.ts +2 -1
- package/src/core/keybindings.ts +4 -1
- package/src/core/model-registry.ts +1 -1
- package/src/core/model-resolver.ts +35 -26
- package/src/core/sdk.ts +96 -76
- package/src/core/settings-manager.ts +45 -14
- package/src/core/system-prompt.ts +5 -15
- package/src/core/tools/bash.ts +115 -54
- package/src/core/tools/find.ts +86 -7
- package/src/core/tools/grep.ts +27 -6
- package/src/core/tools/index.ts +15 -6
- package/src/core/tools/ls.ts +49 -18
- package/src/core/tools/render-utils.ts +2 -1
- package/src/core/tools/task/worker.ts +35 -12
- package/src/core/tools/web-search/auth.ts +37 -32
- package/src/core/tools/web-search/providers/anthropic.ts +35 -22
- package/src/index.ts +101 -9
- package/src/main.ts +60 -20
- package/src/migrations.ts +47 -2
- package/src/modes/index.ts +2 -2
- package/src/modes/interactive/components/assistant-message.ts +25 -7
- package/src/modes/interactive/components/bash-execution.ts +5 -0
- package/src/modes/interactive/components/branch-summary-message.ts +5 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +5 -0
- package/src/modes/interactive/components/countdown-timer.ts +38 -0
- package/src/modes/interactive/components/custom-editor.ts +8 -0
- package/src/modes/interactive/components/custom-message.ts +5 -0
- package/src/modes/interactive/components/footer.ts +2 -5
- package/src/modes/interactive/components/hook-input.ts +29 -20
- package/src/modes/interactive/components/hook-selector.ts +52 -38
- package/src/modes/interactive/components/index.ts +39 -0
- package/src/modes/interactive/components/login-dialog.ts +160 -0
- package/src/modes/interactive/components/model-selector.ts +10 -2
- package/src/modes/interactive/components/session-selector.ts +5 -1
- package/src/modes/interactive/components/settings-defs.ts +9 -0
- package/src/modes/interactive/components/status-line/segments.ts +3 -3
- package/src/modes/interactive/components/tool-execution.ts +9 -16
- package/src/modes/interactive/components/tree-selector.ts +1 -6
- package/src/modes/interactive/interactive-mode.ts +466 -215
- package/src/modes/interactive/theme/theme.ts +50 -2
- package/src/modes/print-mode.ts +78 -31
- package/src/modes/rpc/rpc-mode.ts +186 -78
- package/src/modes/rpc/rpc-types.ts +10 -3
- package/src/prompts/system-prompt.md +36 -28
- package/src/utils/clipboard.ts +90 -50
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +1 -1
- package/src/utils/tools-manager.ts +2 -2
|
@@ -8,14 +8,17 @@
|
|
|
8
8
|
* - Interact with the user via UI primitives
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { AgentMessage, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
11
|
+
import type { AgentMessage, AgentToolResult, AgentToolUpdateCallback, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
12
12
|
import type { ImageContent, Model, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
|
|
13
|
-
import type { Component, KeyId, TUI } from "@oh-my-pi/pi-tui";
|
|
13
|
+
import type { Component, EditorComponent, EditorTheme, KeyId, TUI } from "@oh-my-pi/pi-tui";
|
|
14
14
|
import type { Static, TSchema } from "@sinclair/typebox";
|
|
15
|
+
import type * as piCodingAgent from "../../index";
|
|
15
16
|
import type { Theme } from "../../modes/interactive/theme/theme";
|
|
17
|
+
import type { BashResult } from "../bash-executor";
|
|
16
18
|
import type { CompactionPreparation, CompactionResult } from "../compaction";
|
|
17
19
|
import type { EventBus } from "../event-bus";
|
|
18
20
|
import type { ExecOptions, ExecResult } from "../exec";
|
|
21
|
+
import type { KeybindingsManager } from "../keybindings";
|
|
19
22
|
import type { CustomMessage } from "../messages";
|
|
20
23
|
import type { ModelRegistry } from "../model-registry";
|
|
21
24
|
import type {
|
|
@@ -26,28 +29,38 @@ import type {
|
|
|
26
29
|
SessionManager,
|
|
27
30
|
} from "../session-manager";
|
|
28
31
|
import type { BashToolDetails, FindToolDetails, GrepToolDetails, LsToolDetails, ReadToolDetails } from "../tools";
|
|
32
|
+
import type { BashOperations } from "../tools/bash";
|
|
29
33
|
import type { EditToolDetails } from "../tools/edit";
|
|
30
34
|
|
|
31
35
|
export type { ExecOptions, ExecResult } from "../exec";
|
|
32
36
|
export type { AgentToolResult, AgentToolUpdateCallback };
|
|
37
|
+
export type { AppAction, KeybindingsManager } from "../keybindings";
|
|
33
38
|
|
|
34
39
|
// ============================================================================
|
|
35
40
|
// UI Context
|
|
36
41
|
// ============================================================================
|
|
37
42
|
|
|
43
|
+
/**
|
|
44
|
+
* UI dialog options for extensions.
|
|
45
|
+
*/
|
|
46
|
+
export interface ExtensionUIDialogOptions {
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
timeout?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
/**
|
|
39
52
|
* UI context for extensions to request interactive UI.
|
|
40
53
|
* Each mode (interactive, RPC, print) provides its own implementation.
|
|
41
54
|
*/
|
|
42
55
|
export interface ExtensionUIContext {
|
|
43
56
|
/** Show a selector and return the user's choice. */
|
|
44
|
-
select(title: string, options: string[]): Promise<string | undefined>;
|
|
57
|
+
select(title: string, options: string[], dialogOptions?: ExtensionUIDialogOptions): Promise<string | undefined>;
|
|
45
58
|
|
|
46
59
|
/** Show a confirmation dialog. */
|
|
47
|
-
confirm(title: string, message: string): Promise<boolean>;
|
|
60
|
+
confirm(title: string, message: string, dialogOptions?: ExtensionUIDialogOptions): Promise<boolean>;
|
|
48
61
|
|
|
49
62
|
/** Show a text input dialog. */
|
|
50
|
-
input(title: string, placeholder?: string): Promise<string | undefined>;
|
|
63
|
+
input(title: string, placeholder?: string, dialogOptions?: ExtensionUIDialogOptions): Promise<string | undefined>;
|
|
51
64
|
|
|
52
65
|
/** Show a notification to the user. */
|
|
53
66
|
notify(message: string, type?: "info" | "warning" | "error"): void;
|
|
@@ -59,6 +72,12 @@ export interface ExtensionUIContext {
|
|
|
59
72
|
setWidget(key: string, content: string[] | undefined): void;
|
|
60
73
|
setWidget(key: string, content: ((tui: TUI, theme: Theme) => Component & { dispose?(): void }) | undefined): void;
|
|
61
74
|
|
|
75
|
+
/** Set a custom footer component, or undefined to restore the built-in footer. */
|
|
76
|
+
setFooter(factory: ((tui: TUI, theme: Theme) => Component & { dispose?(): void }) | undefined): void;
|
|
77
|
+
|
|
78
|
+
/** Set a custom header component, or undefined to restore the built-in header. */
|
|
79
|
+
setHeader(factory: ((tui: TUI, theme: Theme) => Component & { dispose?(): void }) | undefined): void;
|
|
80
|
+
|
|
62
81
|
/** Set the terminal window/tab title. */
|
|
63
82
|
setTitle(title: string): void;
|
|
64
83
|
|
|
@@ -67,8 +86,10 @@ export interface ExtensionUIContext {
|
|
|
67
86
|
factory: (
|
|
68
87
|
tui: TUI,
|
|
69
88
|
theme: Theme,
|
|
89
|
+
keybindings: KeybindingsManager,
|
|
70
90
|
done: (result: T) => void,
|
|
71
91
|
) => (Component & { dispose?(): void }) | Promise<Component & { dispose?(): void }>,
|
|
92
|
+
options?: { overlay?: boolean },
|
|
72
93
|
): Promise<T>;
|
|
73
94
|
|
|
74
95
|
/** Set the text in the core input editor. */
|
|
@@ -80,8 +101,22 @@ export interface ExtensionUIContext {
|
|
|
80
101
|
/** Show a multi-line editor for text editing. */
|
|
81
102
|
editor(title: string, prefill?: string): Promise<string | undefined>;
|
|
82
103
|
|
|
104
|
+
/** Set a custom editor component via factory function, or undefined to restore the default editor. */
|
|
105
|
+
setEditorComponent(
|
|
106
|
+
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => EditorComponent) | undefined,
|
|
107
|
+
): void;
|
|
108
|
+
|
|
83
109
|
/** Get the current theme for styling. */
|
|
84
110
|
readonly theme: Theme;
|
|
111
|
+
|
|
112
|
+
/** Get all available themes with names and paths. */
|
|
113
|
+
getAllThemes(): { name: string; path: string | undefined }[];
|
|
114
|
+
|
|
115
|
+
/** Load a theme by name without switching to it. */
|
|
116
|
+
getTheme(name: string): Theme | undefined;
|
|
117
|
+
|
|
118
|
+
/** Set the current theme by name or Theme object. */
|
|
119
|
+
setTheme(theme: string | Theme): { success: boolean; error?: string };
|
|
85
120
|
}
|
|
86
121
|
|
|
87
122
|
// ============================================================================
|
|
@@ -110,6 +145,8 @@ export interface ExtensionContext {
|
|
|
110
145
|
abort(): void;
|
|
111
146
|
/** Whether there are queued messages waiting */
|
|
112
147
|
hasPendingMessages(): boolean;
|
|
148
|
+
/** Gracefully shutdown and exit. */
|
|
149
|
+
shutdown(): void;
|
|
113
150
|
/** @deprecated Use hasPendingMessages() instead */
|
|
114
151
|
hasQueuedMessages(): boolean;
|
|
115
152
|
}
|
|
@@ -299,6 +336,7 @@ export interface BeforeAgentStartEvent {
|
|
|
299
336
|
type: "before_agent_start";
|
|
300
337
|
prompt: string;
|
|
301
338
|
images?: ImageContent[];
|
|
339
|
+
systemPrompt: string;
|
|
302
340
|
}
|
|
303
341
|
|
|
304
342
|
/** Fired when an agent loop starts */
|
|
@@ -327,6 +365,21 @@ export interface TurnEndEvent {
|
|
|
327
365
|
toolResults: ToolResultMessage[];
|
|
328
366
|
}
|
|
329
367
|
|
|
368
|
+
// ============================================================================
|
|
369
|
+
// User Bash Events
|
|
370
|
+
// ============================================================================
|
|
371
|
+
|
|
372
|
+
/** Fired when user executes a bash command via ! or !! prefix */
|
|
373
|
+
export interface UserBashEvent {
|
|
374
|
+
type: "user_bash";
|
|
375
|
+
/** The command to execute */
|
|
376
|
+
command: string;
|
|
377
|
+
/** True if !! prefix was used (excluded from LLM context) */
|
|
378
|
+
excludeFromContext: boolean;
|
|
379
|
+
/** Current working directory */
|
|
380
|
+
cwd: string;
|
|
381
|
+
}
|
|
382
|
+
|
|
330
383
|
// ============================================================================
|
|
331
384
|
// Tool Events
|
|
332
385
|
// ============================================================================
|
|
@@ -430,6 +483,7 @@ export type ExtensionEvent =
|
|
|
430
483
|
| AgentEndEvent
|
|
431
484
|
| TurnStartEvent
|
|
432
485
|
| TurnEndEvent
|
|
486
|
+
| UserBashEvent
|
|
433
487
|
| ToolCallEvent
|
|
434
488
|
| ToolResultEvent;
|
|
435
489
|
|
|
@@ -446,6 +500,14 @@ export interface ToolCallEventResult {
|
|
|
446
500
|
reason?: string;
|
|
447
501
|
}
|
|
448
502
|
|
|
503
|
+
/** Result from user_bash event handler */
|
|
504
|
+
export interface UserBashEventResult {
|
|
505
|
+
/** Custom operations to use for execution */
|
|
506
|
+
operations?: BashOperations;
|
|
507
|
+
/** Full replacement: extension handled execution, use this result */
|
|
508
|
+
result?: BashResult;
|
|
509
|
+
}
|
|
510
|
+
|
|
449
511
|
export interface ToolResultEventResult {
|
|
450
512
|
content?: (TextContent | ImageContent)[];
|
|
451
513
|
details?: unknown;
|
|
@@ -454,7 +516,8 @@ export interface ToolResultEventResult {
|
|
|
454
516
|
|
|
455
517
|
export interface BeforeAgentStartEventResult {
|
|
456
518
|
message?: Pick<CustomMessage, "customType" | "content" | "display" | "details">;
|
|
457
|
-
|
|
519
|
+
/** Replace the system prompt for this turn. If multiple extensions return this, they are chained. */
|
|
520
|
+
systemPrompt?: string;
|
|
458
521
|
}
|
|
459
522
|
|
|
460
523
|
export interface SessionBeforeSwitchResult {
|
|
@@ -526,7 +589,7 @@ export interface ExtensionAPI {
|
|
|
526
589
|
typebox: typeof import("@sinclair/typebox");
|
|
527
590
|
|
|
528
591
|
/** Injected pi-coding-agent exports for accessing SDK utilities */
|
|
529
|
-
pi: typeof
|
|
592
|
+
pi: typeof piCodingAgent;
|
|
530
593
|
|
|
531
594
|
// =========================================================================
|
|
532
595
|
// Event Subscription
|
|
@@ -559,6 +622,7 @@ export interface ExtensionAPI {
|
|
|
559
622
|
on(event: "turn_end", handler: ExtensionHandler<TurnEndEvent>): void;
|
|
560
623
|
on(event: "tool_call", handler: ExtensionHandler<ToolCallEvent, ToolCallEventResult>): void;
|
|
561
624
|
on(event: "tool_result", handler: ExtensionHandler<ToolResultEvent, ToolResultEventResult>): void;
|
|
625
|
+
on(event: "user_bash", handler: ExtensionHandler<UserBashEvent, UserBashEventResult>): void;
|
|
562
626
|
|
|
563
627
|
// =========================================================================
|
|
564
628
|
// Tool Registration
|
|
@@ -613,6 +677,12 @@ export interface ExtensionAPI {
|
|
|
613
677
|
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
|
614
678
|
): void;
|
|
615
679
|
|
|
680
|
+
/** Send a user message to the agent. Always triggers a turn. */
|
|
681
|
+
sendUserMessage(
|
|
682
|
+
content: string | (TextContent | ImageContent)[],
|
|
683
|
+
options?: { deliverAs?: "steer" | "followUp" },
|
|
684
|
+
): void;
|
|
685
|
+
|
|
616
686
|
/** Append a custom entry to the session for state persistence (not sent to LLM). */
|
|
617
687
|
appendEntry<T = unknown>(customType: string, data?: T): void;
|
|
618
688
|
|
|
@@ -628,12 +698,21 @@ export interface ExtensionAPI {
|
|
|
628
698
|
/** Set the active tools by name. */
|
|
629
699
|
setActiveTools(toolNames: string[]): void;
|
|
630
700
|
|
|
701
|
+
/** Set the current model. Returns false if no API key available. */
|
|
702
|
+
setModel(model: Model<any>): Promise<boolean>;
|
|
703
|
+
|
|
704
|
+
/** Get current thinking level. */
|
|
705
|
+
getThinkingLevel(): ThinkingLevel;
|
|
706
|
+
|
|
707
|
+
/** Set thinking level (clamped to model capabilities). */
|
|
708
|
+
setThinkingLevel(level: ThinkingLevel): void;
|
|
709
|
+
|
|
631
710
|
/** Shared event bus for extension communication. */
|
|
632
711
|
events: EventBus;
|
|
633
712
|
}
|
|
634
713
|
|
|
635
|
-
/** Extension factory function type. */
|
|
636
|
-
export type ExtensionFactory = (pi: ExtensionAPI) => void
|
|
714
|
+
/** Extension factory function type. Supports both sync and async initialization. */
|
|
715
|
+
export type ExtensionFactory = (pi: ExtensionAPI) => void | Promise<void>;
|
|
637
716
|
|
|
638
717
|
// ============================================================================
|
|
639
718
|
// Loaded Extension Types
|
|
@@ -666,6 +745,11 @@ export type SendMessageHandler = <T = unknown>(
|
|
|
666
745
|
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
|
667
746
|
) => void;
|
|
668
747
|
|
|
748
|
+
export type SendUserMessageHandler = (
|
|
749
|
+
content: string | (TextContent | ImageContent)[],
|
|
750
|
+
options?: { deliverAs?: "steer" | "followUp" },
|
|
751
|
+
) => void;
|
|
752
|
+
|
|
669
753
|
export type AppendEntryHandler = <T = unknown>(customType: string, data?: T) => void;
|
|
670
754
|
|
|
671
755
|
export type GetActiveToolsHandler = () => string[];
|
|
@@ -674,8 +758,55 @@ export type GetAllToolsHandler = () => string[];
|
|
|
674
758
|
|
|
675
759
|
export type SetActiveToolsHandler = (toolNames: string[]) => void;
|
|
676
760
|
|
|
761
|
+
export type SetModelHandler = (model: Model<any>) => Promise<boolean>;
|
|
762
|
+
|
|
763
|
+
export type GetThinkingLevelHandler = () => ThinkingLevel;
|
|
764
|
+
|
|
765
|
+
export type SetThinkingLevelHandler = (level: ThinkingLevel) => void;
|
|
766
|
+
|
|
767
|
+
/** Shared state created by loader, used during registration and runtime. */
|
|
768
|
+
export interface ExtensionRuntimeState {
|
|
769
|
+
flagValues: Map<string, boolean | string>;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/** Action implementations for ExtensionAPI methods. */
|
|
773
|
+
export interface ExtensionActions {
|
|
774
|
+
sendMessage: SendMessageHandler;
|
|
775
|
+
sendUserMessage: SendUserMessageHandler;
|
|
776
|
+
appendEntry: AppendEntryHandler;
|
|
777
|
+
getActiveTools: GetActiveToolsHandler;
|
|
778
|
+
getAllTools: GetAllToolsHandler;
|
|
779
|
+
setActiveTools: SetActiveToolsHandler;
|
|
780
|
+
setModel: SetModelHandler;
|
|
781
|
+
getThinkingLevel: GetThinkingLevelHandler;
|
|
782
|
+
setThinkingLevel: SetThinkingLevelHandler;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/** Actions for ExtensionContext (ctx.* in event handlers). */
|
|
786
|
+
export interface ExtensionContextActions {
|
|
787
|
+
getModel: () => Model<any> | undefined;
|
|
788
|
+
isIdle: () => boolean;
|
|
789
|
+
abort: () => void;
|
|
790
|
+
hasPendingMessages: () => boolean;
|
|
791
|
+
shutdown: () => void;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/** Actions for ExtensionCommandContext (ctx.* in command handlers). */
|
|
795
|
+
export interface ExtensionCommandContextActions {
|
|
796
|
+
waitForIdle: () => Promise<void>;
|
|
797
|
+
newSession: (options?: {
|
|
798
|
+
parentSession?: string;
|
|
799
|
+
setup?: (sessionManager: SessionManager) => Promise<void>;
|
|
800
|
+
}) => Promise<{ cancelled: boolean }>;
|
|
801
|
+
branch: (entryId: string) => Promise<{ cancelled: boolean }>;
|
|
802
|
+
navigateTree: (targetId: string, options?: { summarize?: boolean }) => Promise<{ cancelled: boolean }>;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/** Full runtime = state + actions. */
|
|
806
|
+
export interface ExtensionRuntime extends ExtensionRuntimeState, ExtensionActions {}
|
|
807
|
+
|
|
677
808
|
/** Loaded extension with all registered items. */
|
|
678
|
-
export interface
|
|
809
|
+
export interface Extension {
|
|
679
810
|
path: string;
|
|
680
811
|
resolvedPath: string;
|
|
681
812
|
handlers: Map<string, HandlerFn[]>;
|
|
@@ -683,21 +814,14 @@ export interface LoadedExtension {
|
|
|
683
814
|
messageRenderers: Map<string, MessageRenderer>;
|
|
684
815
|
commands: Map<string, RegisteredCommand>;
|
|
685
816
|
flags: Map<string, ExtensionFlag>;
|
|
686
|
-
flagValues: Map<string, boolean | string>;
|
|
687
817
|
shortcuts: Map<KeyId, ExtensionShortcut>;
|
|
688
|
-
setSendMessageHandler: (handler: SendMessageHandler) => void;
|
|
689
|
-
setAppendEntryHandler: (handler: AppendEntryHandler) => void;
|
|
690
|
-
setGetActiveToolsHandler: (handler: GetActiveToolsHandler) => void;
|
|
691
|
-
setGetAllToolsHandler: (handler: GetAllToolsHandler) => void;
|
|
692
|
-
setSetActiveToolsHandler: (handler: SetActiveToolsHandler) => void;
|
|
693
|
-
setFlagValue: (name: string, value: boolean | string) => void;
|
|
694
818
|
}
|
|
695
819
|
|
|
696
820
|
/** Result of loading extensions. */
|
|
697
821
|
export interface LoadExtensionsResult {
|
|
698
|
-
extensions:
|
|
822
|
+
extensions: Extension[];
|
|
699
823
|
errors: Array<{ path: string; error: string }>;
|
|
700
|
-
|
|
824
|
+
runtime: ExtensionRuntime;
|
|
701
825
|
}
|
|
702
826
|
|
|
703
827
|
// ============================================================================
|
|
@@ -6,12 +6,12 @@ import type { AgentTool, AgentToolContext, AgentToolUpdateCallback } from "@oh-m
|
|
|
6
6
|
import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
7
7
|
import type { Theme } from "../../modes/interactive/theme/theme";
|
|
8
8
|
import type { ExtensionRunner } from "./runner";
|
|
9
|
-
import type {
|
|
9
|
+
import type { RegisteredTool, ToolCallEventResult, ToolResultEventResult } from "./types";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Wrap a RegisteredTool into an AgentTool.
|
|
13
13
|
*/
|
|
14
|
-
export function wrapRegisteredTool(registeredTool: RegisteredTool,
|
|
14
|
+
export function wrapRegisteredTool(registeredTool: RegisteredTool, runner: ExtensionRunner): AgentTool {
|
|
15
15
|
const { definition } = registeredTool;
|
|
16
16
|
return {
|
|
17
17
|
name: definition.name,
|
|
@@ -19,7 +19,7 @@ export function wrapRegisteredTool(registeredTool: RegisteredTool, getContext: (
|
|
|
19
19
|
description: definition.description,
|
|
20
20
|
parameters: definition.parameters,
|
|
21
21
|
execute: (toolCallId, params, signal, onUpdate) =>
|
|
22
|
-
definition.execute(toolCallId, params, onUpdate,
|
|
22
|
+
definition.execute(toolCallId, params, onUpdate, runner.createContext(), signal),
|
|
23
23
|
renderCall: definition.renderCall ? (args, theme) => definition.renderCall?.(args, theme as Theme) : undefined,
|
|
24
24
|
renderResult: definition.renderResult
|
|
25
25
|
? (result, options, theme) =>
|
|
@@ -35,11 +35,8 @@ export function wrapRegisteredTool(registeredTool: RegisteredTool, getContext: (
|
|
|
35
35
|
/**
|
|
36
36
|
* Wrap all registered tools into AgentTools.
|
|
37
37
|
*/
|
|
38
|
-
export function wrapRegisteredTools(
|
|
39
|
-
registeredTools
|
|
40
|
-
getContext: () => ExtensionContext,
|
|
41
|
-
): AgentTool[] {
|
|
42
|
-
return registeredTools.map((rt) => wrapRegisteredTool(rt, getContext));
|
|
38
|
+
export function wrapRegisteredTools(registeredTools: RegisteredTool[], runner: ExtensionRunner): AgentTool[] {
|
|
39
|
+
return registeredTools.map((rt) => wrapRegisteredTool(rt, runner));
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
/**
|
package/src/core/hooks/types.ts
CHANGED
|
@@ -747,7 +747,7 @@ export interface HookAPI {
|
|
|
747
747
|
/** Injected @sinclair/typebox module */
|
|
748
748
|
typebox: typeof import("@sinclair/typebox");
|
|
749
749
|
/** Injected pi-coding-agent exports */
|
|
750
|
-
pi: typeof import("../../index
|
|
750
|
+
pi: typeof import("../../index");
|
|
751
751
|
}
|
|
752
752
|
|
|
753
753
|
/**
|
package/src/core/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export {
|
|
|
11
11
|
type PromptOptions,
|
|
12
12
|
type SessionStats,
|
|
13
13
|
} from "./agent-session";
|
|
14
|
-
export { type BashExecutorOptions, type BashResult, executeBash } from "./bash-executor";
|
|
14
|
+
export { type BashExecutorOptions, type BashResult, executeBash, executeBashWithOperations } from "./bash-executor";
|
|
15
15
|
export type { CompactionResult } from "./compaction/index";
|
|
16
16
|
export {
|
|
17
17
|
discoverAndLoadExtensions,
|
|
@@ -21,6 +21,7 @@ export {
|
|
|
21
21
|
type ExtensionFactory,
|
|
22
22
|
ExtensionRunner,
|
|
23
23
|
type ExtensionUIContext,
|
|
24
|
+
type ExtensionUIDialogOptions,
|
|
24
25
|
loadExtensionFromFactory,
|
|
25
26
|
type ToolDefinition,
|
|
26
27
|
} from "./extensions/index";
|
package/src/core/keybindings.ts
CHANGED
|
@@ -26,7 +26,8 @@ export type AppAction =
|
|
|
26
26
|
| "expandTools"
|
|
27
27
|
| "toggleThinking"
|
|
28
28
|
| "externalEditor"
|
|
29
|
-
| "followUp"
|
|
29
|
+
| "followUp"
|
|
30
|
+
| "dequeue";
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* All configurable actions.
|
|
@@ -56,6 +57,7 @@ export const DEFAULT_APP_KEYBINDINGS: Record<AppAction, KeyId | KeyId[]> = {
|
|
|
56
57
|
toggleThinking: "ctrl+t",
|
|
57
58
|
externalEditor: "ctrl+g",
|
|
58
59
|
followUp: "alt+enter",
|
|
60
|
+
dequeue: "alt+up",
|
|
59
61
|
};
|
|
60
62
|
|
|
61
63
|
/**
|
|
@@ -80,6 +82,7 @@ const APP_ACTIONS: AppAction[] = [
|
|
|
80
82
|
"toggleThinking",
|
|
81
83
|
"externalEditor",
|
|
82
84
|
"followUp",
|
|
85
|
+
"dequeue",
|
|
83
86
|
];
|
|
84
87
|
|
|
85
88
|
function isAppAction(action: string): action is AppAction {
|
|
@@ -384,7 +384,7 @@ export class ModelRegistry {
|
|
|
384
384
|
* Find a model by provider and ID.
|
|
385
385
|
*/
|
|
386
386
|
find(provider: string, modelId: string): Model<Api> | undefined {
|
|
387
|
-
return this.models.find((m) => m.provider === provider && m.id === modelId)
|
|
387
|
+
return this.models.find((m) => m.provider === provider && m.id === modelId);
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
/**
|
|
@@ -25,12 +25,13 @@ export const defaultModelPerProvider: Record<KnownProvider, string> = {
|
|
|
25
25
|
cerebras: "zai-glm-4.6",
|
|
26
26
|
zai: "glm-4.6",
|
|
27
27
|
mistral: "devstral-medium-latest",
|
|
28
|
-
opencode: "claude-
|
|
28
|
+
opencode: "claude-opus-4-5",
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
export interface ScopedModel {
|
|
32
32
|
model: Model<Api>;
|
|
33
|
-
thinkingLevel
|
|
33
|
+
thinkingLevel?: ThinkingLevel;
|
|
34
|
+
explicitThinkingLevel: boolean;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/** Priority chain for auto-discovering smol/fast models */
|
|
@@ -122,8 +123,10 @@ function tryMatchModel(modelPattern: string, availableModels: Model<Api>[]): Mod
|
|
|
122
123
|
|
|
123
124
|
export interface ParsedModelResult {
|
|
124
125
|
model: Model<Api> | undefined;
|
|
125
|
-
|
|
126
|
+
/** Thinking level if explicitly specified in pattern, undefined otherwise */
|
|
127
|
+
thinkingLevel?: ThinkingLevel;
|
|
126
128
|
warning: string | undefined;
|
|
129
|
+
explicitThinkingLevel: boolean;
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
/**
|
|
@@ -132,10 +135,10 @@ export interface ParsedModelResult {
|
|
|
132
135
|
*
|
|
133
136
|
* Algorithm:
|
|
134
137
|
* 1. Try to match full pattern as a model
|
|
135
|
-
* 2. If found, return it with
|
|
138
|
+
* 2. If found, return it with undefined thinking level
|
|
136
139
|
* 3. If not found and has colons, split on last colon:
|
|
137
140
|
* - If suffix is valid thinking level, use it and recurse on prefix
|
|
138
|
-
* - If suffix is invalid, warn and recurse on prefix
|
|
141
|
+
* - If suffix is invalid, warn and recurse on prefix
|
|
139
142
|
*
|
|
140
143
|
* @internal Exported for testing
|
|
141
144
|
*/
|
|
@@ -143,14 +146,14 @@ export function parseModelPattern(pattern: string, availableModels: Model<Api>[]
|
|
|
143
146
|
// Try exact match first
|
|
144
147
|
const exactMatch = tryMatchModel(pattern, availableModels);
|
|
145
148
|
if (exactMatch) {
|
|
146
|
-
return { model: exactMatch, thinkingLevel:
|
|
149
|
+
return { model: exactMatch, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
// No match - try splitting on last colon if present
|
|
150
153
|
const lastColonIndex = pattern.lastIndexOf(":");
|
|
151
154
|
if (lastColonIndex === -1) {
|
|
152
155
|
// No colons, pattern simply doesn't match any model
|
|
153
|
-
return { model: undefined, thinkingLevel:
|
|
156
|
+
return { model: undefined, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
const prefix = pattern.substring(0, lastColonIndex);
|
|
@@ -161,26 +164,28 @@ export function parseModelPattern(pattern: string, availableModels: Model<Api>[]
|
|
|
161
164
|
const result = parseModelPattern(prefix, availableModels);
|
|
162
165
|
if (result.model) {
|
|
163
166
|
// Only use this thinking level if no warning from inner recursion
|
|
164
|
-
|
|
167
|
+
const explicitThinkingLevel = !result.warning;
|
|
165
168
|
return {
|
|
166
169
|
model: result.model,
|
|
167
|
-
thinkingLevel:
|
|
170
|
+
thinkingLevel: explicitThinkingLevel ? suffix : undefined,
|
|
168
171
|
warning: result.warning,
|
|
172
|
+
explicitThinkingLevel,
|
|
169
173
|
};
|
|
170
174
|
}
|
|
171
175
|
return result;
|
|
172
|
-
} else {
|
|
173
|
-
// Invalid suffix - recurse on prefix with "off" and warn
|
|
174
|
-
const result = parseModelPattern(prefix, availableModels);
|
|
175
|
-
if (result.model) {
|
|
176
|
-
return {
|
|
177
|
-
model: result.model,
|
|
178
|
-
thinkingLevel: "off",
|
|
179
|
-
warning: `Invalid thinking level "${suffix}" in pattern "${pattern}". Using "off" instead.`,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
return result;
|
|
183
176
|
}
|
|
177
|
+
|
|
178
|
+
// Invalid suffix - recurse on prefix and warn
|
|
179
|
+
const result = parseModelPattern(prefix, availableModels);
|
|
180
|
+
if (result.model) {
|
|
181
|
+
return {
|
|
182
|
+
model: result.model,
|
|
183
|
+
thinkingLevel: undefined,
|
|
184
|
+
warning: `Invalid thinking level "${suffix}" in pattern "${pattern}". Using default instead.`,
|
|
185
|
+
explicitThinkingLevel: false,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
184
189
|
}
|
|
185
190
|
|
|
186
191
|
/**
|
|
@@ -204,12 +209,14 @@ export async function resolveModelScope(patterns: string[], modelRegistry: Model
|
|
|
204
209
|
// Extract optional thinking level suffix (e.g., "provider/*:high")
|
|
205
210
|
const colonIdx = pattern.lastIndexOf(":");
|
|
206
211
|
let globPattern = pattern;
|
|
207
|
-
let thinkingLevel: ThinkingLevel
|
|
212
|
+
let thinkingLevel: ThinkingLevel | undefined;
|
|
213
|
+
let explicitThinkingLevel = false;
|
|
208
214
|
|
|
209
215
|
if (colonIdx !== -1) {
|
|
210
216
|
const suffix = pattern.substring(colonIdx + 1);
|
|
211
217
|
if (isValidThinkingLevel(suffix)) {
|
|
212
218
|
thinkingLevel = suffix;
|
|
219
|
+
explicitThinkingLevel = true;
|
|
213
220
|
globPattern = pattern.substring(0, colonIdx);
|
|
214
221
|
}
|
|
215
222
|
}
|
|
@@ -228,13 +235,13 @@ export async function resolveModelScope(patterns: string[], modelRegistry: Model
|
|
|
228
235
|
|
|
229
236
|
for (const model of matchingModels) {
|
|
230
237
|
if (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) {
|
|
231
|
-
scopedModels.push({ model, thinkingLevel });
|
|
238
|
+
scopedModels.push({ model, thinkingLevel, explicitThinkingLevel });
|
|
232
239
|
}
|
|
233
240
|
}
|
|
234
241
|
continue;
|
|
235
242
|
}
|
|
236
243
|
|
|
237
|
-
const { model, thinkingLevel, warning } = parseModelPattern(pattern, availableModels);
|
|
244
|
+
const { model, thinkingLevel, warning, explicitThinkingLevel } = parseModelPattern(pattern, availableModels);
|
|
238
245
|
|
|
239
246
|
if (warning) {
|
|
240
247
|
console.warn(chalk.yellow(`Warning: ${warning}`));
|
|
@@ -247,7 +254,7 @@ export async function resolveModelScope(patterns: string[], modelRegistry: Model
|
|
|
247
254
|
|
|
248
255
|
// Avoid duplicates
|
|
249
256
|
if (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) {
|
|
250
|
-
scopedModels.push({ model, thinkingLevel });
|
|
257
|
+
scopedModels.push({ model, thinkingLevel, explicitThinkingLevel });
|
|
251
258
|
}
|
|
252
259
|
}
|
|
253
260
|
|
|
@@ -304,9 +311,11 @@ export async function findInitialModel(options: {
|
|
|
304
311
|
|
|
305
312
|
// 2. Use first model from scoped models (skip if continuing/resuming)
|
|
306
313
|
if (scopedModels.length > 0 && !isContinuing) {
|
|
314
|
+
const scoped = scopedModels[0];
|
|
315
|
+
const scopedThinkingLevel = scoped.thinkingLevel ?? defaultThinkingLevel ?? "off";
|
|
307
316
|
return {
|
|
308
|
-
model:
|
|
309
|
-
thinkingLevel:
|
|
317
|
+
model: scoped.model,
|
|
318
|
+
thinkingLevel: scopedThinkingLevel,
|
|
310
319
|
fallbackMessage: undefined,
|
|
311
320
|
};
|
|
312
321
|
}
|