@oh-my-pi/pi-coding-agent 1.337.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 +1228 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +637 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/reviewer.md +35 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +81 -0
- package/src/cli/args.ts +246 -0
- package/src/cli/file-processor.ts +72 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +650 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli.ts +10 -0
- package/src/commands/init.md +20 -0
- package/src/config.ts +159 -0
- package/src/core/agent-session.ts +1900 -0
- package/src/core/auth-storage.ts +236 -0
- package/src/core/bash-executor.ts +196 -0
- package/src/core/compaction/branch-summarization.ts +343 -0
- package/src/core/compaction/compaction.ts +742 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +154 -0
- package/src/core/custom-tools/index.ts +21 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +169 -0
- package/src/core/custom-tools/wrapper.ts +28 -0
- package/src/core/exec.ts +129 -0
- package/src/core/export-html/index.ts +211 -0
- package/src/core/export-html/template.css +781 -0
- package/src/core/export-html/template.html +54 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/vendor/highlight.min.js +1213 -0
- package/src/core/export-html/vendor/marked.min.js +6 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +312 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +99 -0
- package/src/core/hooks/types.ts +773 -0
- package/src/core/index.ts +52 -0
- package/src/core/mcp/client.ts +158 -0
- package/src/core/mcp/config.ts +154 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +68 -0
- package/src/core/mcp/manager.ts +181 -0
- package/src/core/mcp/tool-bridge.ts +148 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +220 -0
- package/src/core/messages.ts +189 -0
- package/src/core/model-registry.ts +317 -0
- package/src/core/model-resolver.ts +393 -0
- package/src/core/plugins/doctor.ts +59 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +338 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +32 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +760 -0
- package/src/core/session-manager.ts +1128 -0
- package/src/core/settings-manager.ts +443 -0
- package/src/core/skills.ts +437 -0
- package/src/core/slash-commands.ts +248 -0
- package/src/core/system-prompt.ts +439 -0
- package/src/core/timings.ts +25 -0
- package/src/core/tools/ask.ts +211 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +250 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +475 -0
- package/src/core/tools/edit.ts +208 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +64 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/logger.ts +56 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +196 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +337 -0
- package/src/core/tools/exa/types.ts +168 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +261 -0
- package/src/core/tools/grep.ts +555 -0
- package/src/core/tools/index.ts +202 -0
- package/src/core/tools/ls.ts +140 -0
- package/src/core/tools/lsp/client.ts +605 -0
- package/src/core/tools/lsp/config.ts +147 -0
- package/src/core/tools/lsp/edits.ts +101 -0
- package/src/core/tools/lsp/index.ts +804 -0
- package/src/core/tools/lsp/render.ts +447 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +463 -0
- package/src/core/tools/lsp/utils.ts +486 -0
- package/src/core/tools/notebook.ts +229 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +240 -0
- package/src/core/tools/renderers.ts +540 -0
- package/src/core/tools/task/agents.ts +153 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/bundled-agents/browser.md +71 -0
- package/src/core/tools/task/bundled-agents/explore.md +82 -0
- package/src/core/tools/task/bundled-agents/plan.md +54 -0
- package/src/core/tools/task/bundled-agents/reviewer.md +59 -0
- package/src/core/tools/task/bundled-agents/task.md +53 -0
- package/src/core/tools/task/bundled-commands/architect-plan.md +10 -0
- package/src/core/tools/task/bundled-commands/implement-with-critic.md +11 -0
- package/src/core/tools/task/bundled-commands/implement.md +11 -0
- package/src/core/tools/task/commands.ts +213 -0
- package/src/core/tools/task/discovery.ts +208 -0
- package/src/core/tools/task/executor.ts +367 -0
- package/src/core/tools/task/index.ts +388 -0
- package/src/core/tools/task/model-resolver.ts +115 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +232 -0
- package/src/core/tools/task/types.ts +99 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2370 -0
- package/src/core/tools/web-search/auth.ts +193 -0
- package/src/core/tools/web-search/index.ts +537 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +302 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +182 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +99 -0
- package/src/index.ts +176 -0
- package/src/main.ts +464 -0
- package/src/migrations.ts +135 -0
- package/src/modes/index.ts +43 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +196 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/footer.ts +381 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +247 -0
- package/src/modes/interactive/components/oauth-selector.ts +120 -0
- package/src/modes/interactive/components/plugin-settings.ts +479 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +204 -0
- package/src/modes/interactive/components/settings-selector.ts +453 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +675 -0
- package/src/modes/interactive/components/tree-selector.ts +866 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +183 -0
- package/src/modes/interactive/interactive-mode.ts +2516 -0
- package/src/modes/interactive/theme/dark.json +101 -0
- package/src/modes/interactive/theme/light.json +98 -0
- package/src/modes/interactive/theme/theme-schema.json +308 -0
- package/src/modes/interactive/theme/theme.ts +998 -0
- package/src/modes/print-mode.ts +128 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +483 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell.ts +276 -0
- package/src/utils/tools-manager.ts +274 -0
package/src/core/sdk.ts
ADDED
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK for programmatic usage of AgentSession.
|
|
3
|
+
*
|
|
4
|
+
* Provides a factory function and discovery helpers that allow full control
|
|
5
|
+
* over agent configuration, or sensible defaults that match CLI behavior.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // Minimal - everything auto-discovered
|
|
10
|
+
* const session = await createAgentSession();
|
|
11
|
+
*
|
|
12
|
+
* // With custom hooks
|
|
13
|
+
* const session = await createAgentSession({
|
|
14
|
+
* hooks: [
|
|
15
|
+
* ...await discoverHooks(),
|
|
16
|
+
* { factory: myHookFactory },
|
|
17
|
+
* ],
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Full control
|
|
21
|
+
* const session = await createAgentSession({
|
|
22
|
+
* model: myModel,
|
|
23
|
+
* getApiKey: async () => process.env.MY_KEY,
|
|
24
|
+
* tools: [readTool, bashTool],
|
|
25
|
+
* hooks: [],
|
|
26
|
+
* skills: [],
|
|
27
|
+
* sessionFile: false,
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { Agent, type ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
33
|
+
import type { Model } from "@oh-my-pi/pi-ai";
|
|
34
|
+
import { join } from "path";
|
|
35
|
+
import { getAgentDir } from "../config.js";
|
|
36
|
+
import { AgentSession } from "./agent-session.js";
|
|
37
|
+
import { AuthStorage } from "./auth-storage.js";
|
|
38
|
+
import {
|
|
39
|
+
type CustomToolsLoadResult,
|
|
40
|
+
discoverAndLoadCustomTools,
|
|
41
|
+
type LoadedCustomTool,
|
|
42
|
+
wrapCustomTools,
|
|
43
|
+
} from "./custom-tools/index.js";
|
|
44
|
+
import type { CustomTool } from "./custom-tools/types.js";
|
|
45
|
+
import { discoverAndLoadHooks, HookRunner, type LoadedHook, wrapToolsWithHooks } from "./hooks/index.js";
|
|
46
|
+
import type { HookFactory } from "./hooks/types.js";
|
|
47
|
+
import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp/index.js";
|
|
48
|
+
import { convertToLlm } from "./messages.js";
|
|
49
|
+
import { ModelRegistry } from "./model-registry.js";
|
|
50
|
+
import { SessionManager } from "./session-manager.js";
|
|
51
|
+
import { type Settings, SettingsManager, type SkillsSettings } from "./settings-manager.js";
|
|
52
|
+
import { loadSkills as loadSkillsInternal, type Skill } from "./skills.js";
|
|
53
|
+
import { type FileSlashCommand, loadSlashCommands as loadSlashCommandsInternal } from "./slash-commands.js";
|
|
54
|
+
import {
|
|
55
|
+
buildSystemPrompt as buildSystemPromptInternal,
|
|
56
|
+
loadProjectContextFiles as loadContextFilesInternal,
|
|
57
|
+
} from "./system-prompt.js";
|
|
58
|
+
import { time } from "./timings.js";
|
|
59
|
+
import { createToolContextStore } from "./tools/context.js";
|
|
60
|
+
import {
|
|
61
|
+
allTools,
|
|
62
|
+
applyBashInterception,
|
|
63
|
+
bashTool,
|
|
64
|
+
codingTools,
|
|
65
|
+
createBashTool,
|
|
66
|
+
createCodingTools,
|
|
67
|
+
createEditTool,
|
|
68
|
+
createFindTool,
|
|
69
|
+
createGrepTool,
|
|
70
|
+
createLsTool,
|
|
71
|
+
createReadOnlyTools,
|
|
72
|
+
createReadTool,
|
|
73
|
+
createWriteTool,
|
|
74
|
+
editTool,
|
|
75
|
+
findTool,
|
|
76
|
+
grepTool,
|
|
77
|
+
lsTool,
|
|
78
|
+
readOnlyTools,
|
|
79
|
+
readTool,
|
|
80
|
+
type Tool,
|
|
81
|
+
writeTool,
|
|
82
|
+
} from "./tools/index.js";
|
|
83
|
+
|
|
84
|
+
// Types
|
|
85
|
+
|
|
86
|
+
export interface CreateAgentSessionOptions {
|
|
87
|
+
/** Working directory for project-local discovery. Default: process.cwd() */
|
|
88
|
+
cwd?: string;
|
|
89
|
+
/** Global config directory. Default: ~/.pi/agent */
|
|
90
|
+
agentDir?: string;
|
|
91
|
+
|
|
92
|
+
/** Auth storage for credentials. Default: discoverAuthStorage(agentDir) */
|
|
93
|
+
authStorage?: AuthStorage;
|
|
94
|
+
/** Model registry. Default: discoverModels(authStorage, agentDir) */
|
|
95
|
+
modelRegistry?: ModelRegistry;
|
|
96
|
+
|
|
97
|
+
/** Model to use. Default: from settings, else first available */
|
|
98
|
+
model?: Model<any>;
|
|
99
|
+
/** Thinking level. Default: from settings, else 'off' (clamped to model capabilities) */
|
|
100
|
+
thinkingLevel?: ThinkingLevel;
|
|
101
|
+
/** Models available for cycling (Ctrl+P in interactive mode) */
|
|
102
|
+
scopedModels?: Array<{ model: Model<any>; thinkingLevel: ThinkingLevel }>;
|
|
103
|
+
|
|
104
|
+
/** System prompt. String replaces default, function receives default and returns final. */
|
|
105
|
+
systemPrompt?: string | ((defaultPrompt: string) => string);
|
|
106
|
+
|
|
107
|
+
/** Built-in tools to use. Default: codingTools [read, bash, edit, write] */
|
|
108
|
+
tools?: Tool[];
|
|
109
|
+
/** Custom tools (replaces discovery). */
|
|
110
|
+
customTools?: Array<{ path?: string; tool: CustomTool }>;
|
|
111
|
+
/** Additional custom tool paths to load (merged with discovery). */
|
|
112
|
+
additionalCustomToolPaths?: string[];
|
|
113
|
+
|
|
114
|
+
/** Hooks (replaces discovery). */
|
|
115
|
+
hooks?: Array<{ path?: string; factory: HookFactory }>;
|
|
116
|
+
/** Additional hook paths to load (merged with discovery). */
|
|
117
|
+
additionalHookPaths?: string[];
|
|
118
|
+
|
|
119
|
+
/** Skills. Default: discovered from multiple locations */
|
|
120
|
+
skills?: Skill[];
|
|
121
|
+
/** Context files (AGENTS.md content). Default: discovered walking up from cwd */
|
|
122
|
+
contextFiles?: Array<{ path: string; content: string }>;
|
|
123
|
+
/** Slash commands. Default: discovered from cwd/.pi/commands/ + agentDir/commands/ */
|
|
124
|
+
slashCommands?: FileSlashCommand[];
|
|
125
|
+
|
|
126
|
+
/** Enable MCP server discovery from .mcp.json files. Default: true */
|
|
127
|
+
enableMCP?: boolean;
|
|
128
|
+
|
|
129
|
+
/** Session manager. Default: SessionManager.create(cwd) */
|
|
130
|
+
sessionManager?: SessionManager;
|
|
131
|
+
|
|
132
|
+
/** Settings manager. Default: SettingsManager.create(cwd, agentDir) */
|
|
133
|
+
settingsManager?: SettingsManager;
|
|
134
|
+
|
|
135
|
+
/** Whether UI is available (enables interactive tools like ask). Default: false */
|
|
136
|
+
hasUI?: boolean;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Result from createAgentSession */
|
|
140
|
+
export interface CreateAgentSessionResult {
|
|
141
|
+
/** The created session */
|
|
142
|
+
session: AgentSession;
|
|
143
|
+
/** Custom tools result (for UI context setup in interactive mode) */
|
|
144
|
+
customToolsResult: CustomToolsLoadResult;
|
|
145
|
+
/** MCP manager for server lifecycle management (undefined if MCP disabled) */
|
|
146
|
+
mcpManager?: MCPManager;
|
|
147
|
+
/** Warning if session was restored with a different model than saved */
|
|
148
|
+
modelFallbackMessage?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Re-exports
|
|
152
|
+
|
|
153
|
+
export type { CustomTool } from "./custom-tools/types.js";
|
|
154
|
+
export type { HookAPI, HookCommandContext, HookContext, HookFactory } from "./hooks/types.js";
|
|
155
|
+
export type { MCPManager, MCPServerConfig, MCPServerConnection, MCPToolsLoadResult } from "./mcp/index.js";
|
|
156
|
+
export type { Settings, SkillsSettings } from "./settings-manager.js";
|
|
157
|
+
export type { Skill } from "./skills.js";
|
|
158
|
+
export type { FileSlashCommand } from "./slash-commands.js";
|
|
159
|
+
export type { Tool } from "./tools/index.js";
|
|
160
|
+
|
|
161
|
+
export {
|
|
162
|
+
// Pre-built tools (use process.cwd())
|
|
163
|
+
readTool,
|
|
164
|
+
bashTool,
|
|
165
|
+
editTool,
|
|
166
|
+
writeTool,
|
|
167
|
+
grepTool,
|
|
168
|
+
findTool,
|
|
169
|
+
lsTool,
|
|
170
|
+
codingTools,
|
|
171
|
+
readOnlyTools,
|
|
172
|
+
allTools as allBuiltInTools,
|
|
173
|
+
// Tool factories (for custom cwd)
|
|
174
|
+
createCodingTools,
|
|
175
|
+
createReadOnlyTools,
|
|
176
|
+
createReadTool,
|
|
177
|
+
createBashTool,
|
|
178
|
+
createEditTool,
|
|
179
|
+
createWriteTool,
|
|
180
|
+
createGrepTool,
|
|
181
|
+
createFindTool,
|
|
182
|
+
createLsTool,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Helper Functions
|
|
186
|
+
|
|
187
|
+
function getDefaultAgentDir(): string {
|
|
188
|
+
return getAgentDir();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Discovery Functions
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Create an AuthStorage instance for the given agent directory.
|
|
195
|
+
*/
|
|
196
|
+
export function discoverAuthStorage(agentDir: string = getDefaultAgentDir()): AuthStorage {
|
|
197
|
+
return new AuthStorage(join(agentDir, "auth.json"));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Create a ModelRegistry for the given agent directory.
|
|
202
|
+
*/
|
|
203
|
+
export function discoverModels(authStorage: AuthStorage, agentDir: string = getDefaultAgentDir()): ModelRegistry {
|
|
204
|
+
return new ModelRegistry(authStorage, join(agentDir, "models.json"));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Discover hooks from cwd and agentDir.
|
|
209
|
+
*/
|
|
210
|
+
export async function discoverHooks(
|
|
211
|
+
cwd?: string,
|
|
212
|
+
agentDir?: string,
|
|
213
|
+
): Promise<Array<{ path: string; factory: HookFactory }>> {
|
|
214
|
+
const resolvedCwd = cwd ?? process.cwd();
|
|
215
|
+
const resolvedAgentDir = agentDir ?? getDefaultAgentDir();
|
|
216
|
+
|
|
217
|
+
const { hooks, errors } = await discoverAndLoadHooks([], resolvedCwd, resolvedAgentDir);
|
|
218
|
+
|
|
219
|
+
// Log errors but don't fail
|
|
220
|
+
for (const { path, error } of errors) {
|
|
221
|
+
console.error(`Failed to load hook "${path}": ${error}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return hooks.map((h) => ({
|
|
225
|
+
path: h.path,
|
|
226
|
+
factory: createFactoryFromLoadedHook(h),
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Discover custom tools from cwd and agentDir.
|
|
232
|
+
*/
|
|
233
|
+
export async function discoverCustomTools(
|
|
234
|
+
cwd?: string,
|
|
235
|
+
agentDir?: string,
|
|
236
|
+
): Promise<Array<{ path: string; tool: CustomTool }>> {
|
|
237
|
+
const resolvedCwd = cwd ?? process.cwd();
|
|
238
|
+
const resolvedAgentDir = agentDir ?? getDefaultAgentDir();
|
|
239
|
+
|
|
240
|
+
const { tools, errors } = await discoverAndLoadCustomTools([], resolvedCwd, Object.keys(allTools), resolvedAgentDir);
|
|
241
|
+
|
|
242
|
+
// Log errors but don't fail
|
|
243
|
+
for (const { path, error } of errors) {
|
|
244
|
+
console.error(`Failed to load custom tool "${path}": ${error}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return tools.map((t) => ({
|
|
248
|
+
path: t.path,
|
|
249
|
+
tool: t.tool,
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Discover skills from cwd and agentDir.
|
|
255
|
+
*/
|
|
256
|
+
export function discoverSkills(cwd?: string, agentDir?: string, settings?: SkillsSettings): Skill[] {
|
|
257
|
+
const { skills } = loadSkillsInternal({
|
|
258
|
+
...settings,
|
|
259
|
+
cwd: cwd ?? process.cwd(),
|
|
260
|
+
agentDir: agentDir ?? getDefaultAgentDir(),
|
|
261
|
+
});
|
|
262
|
+
return skills;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Discover context files (AGENTS.md) walking up from cwd.
|
|
267
|
+
*/
|
|
268
|
+
export function discoverContextFiles(cwd?: string, agentDir?: string): Array<{ path: string; content: string }> {
|
|
269
|
+
return loadContextFilesInternal({
|
|
270
|
+
cwd: cwd ?? process.cwd(),
|
|
271
|
+
agentDir: agentDir ?? getDefaultAgentDir(),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Discover slash commands from cwd and agentDir.
|
|
277
|
+
*/
|
|
278
|
+
export function discoverSlashCommands(cwd?: string, agentDir?: string): FileSlashCommand[] {
|
|
279
|
+
return loadSlashCommandsInternal({
|
|
280
|
+
cwd: cwd ?? process.cwd(),
|
|
281
|
+
agentDir: agentDir ?? getDefaultAgentDir(),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Discover MCP servers from .mcp.json files.
|
|
287
|
+
* Returns the manager and loaded tools.
|
|
288
|
+
*/
|
|
289
|
+
export async function discoverMCPServers(cwd?: string): Promise<MCPToolsLoadResult> {
|
|
290
|
+
const resolvedCwd = cwd ?? process.cwd();
|
|
291
|
+
return discoverAndLoadMCPTools(resolvedCwd);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// API Key Helpers
|
|
295
|
+
|
|
296
|
+
// System Prompt
|
|
297
|
+
|
|
298
|
+
export interface BuildSystemPromptOptions {
|
|
299
|
+
tools?: Tool[];
|
|
300
|
+
skills?: Skill[];
|
|
301
|
+
contextFiles?: Array<{ path: string; content: string }>;
|
|
302
|
+
cwd?: string;
|
|
303
|
+
appendPrompt?: string;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Build the default system prompt.
|
|
308
|
+
*/
|
|
309
|
+
export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {
|
|
310
|
+
return buildSystemPromptInternal({
|
|
311
|
+
cwd: options.cwd,
|
|
312
|
+
skills: options.skills,
|
|
313
|
+
contextFiles: options.contextFiles,
|
|
314
|
+
appendSystemPrompt: options.appendPrompt,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Settings
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Load settings from agentDir/settings.json merged with cwd/.pi/settings.json.
|
|
322
|
+
*/
|
|
323
|
+
export function loadSettings(cwd?: string, agentDir?: string): Settings {
|
|
324
|
+
const manager = SettingsManager.create(cwd ?? process.cwd(), agentDir ?? getDefaultAgentDir());
|
|
325
|
+
return {
|
|
326
|
+
defaultProvider: manager.getDefaultProvider(),
|
|
327
|
+
defaultModel: manager.getDefaultModel(),
|
|
328
|
+
defaultThinkingLevel: manager.getDefaultThinkingLevel(),
|
|
329
|
+
queueMode: manager.getQueueMode(),
|
|
330
|
+
theme: manager.getTheme(),
|
|
331
|
+
compaction: manager.getCompactionSettings(),
|
|
332
|
+
retry: manager.getRetrySettings(),
|
|
333
|
+
hideThinkingBlock: manager.getHideThinkingBlock(),
|
|
334
|
+
shellPath: manager.getShellPath(),
|
|
335
|
+
collapseChangelog: manager.getCollapseChangelog(),
|
|
336
|
+
hooks: manager.getHookPaths(),
|
|
337
|
+
customTools: manager.getCustomToolPaths(),
|
|
338
|
+
skills: manager.getSkillsSettings(),
|
|
339
|
+
terminal: { showImages: manager.getShowImages() },
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Internal Helpers
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Create a HookFactory from a LoadedHook.
|
|
347
|
+
* This allows mixing discovered hooks with inline hooks.
|
|
348
|
+
*/
|
|
349
|
+
function createFactoryFromLoadedHook(loaded: LoadedHook): HookFactory {
|
|
350
|
+
return (api) => {
|
|
351
|
+
for (const [eventType, handlers] of loaded.handlers) {
|
|
352
|
+
for (const handler of handlers) {
|
|
353
|
+
api.on(eventType as any, handler as any);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Convert hook definitions to LoadedHooks for the HookRunner.
|
|
361
|
+
*/
|
|
362
|
+
function createLoadedHooksFromDefinitions(definitions: Array<{ path?: string; factory: HookFactory }>): LoadedHook[] {
|
|
363
|
+
return definitions.map((def) => {
|
|
364
|
+
const handlers = new Map<string, Array<(...args: unknown[]) => Promise<unknown>>>();
|
|
365
|
+
const messageRenderers = new Map<string, any>();
|
|
366
|
+
const commands = new Map<string, any>();
|
|
367
|
+
let sendMessageHandler: (message: any, triggerTurn?: boolean) => void = () => {};
|
|
368
|
+
let appendEntryHandler: (customType: string, data?: any) => void = () => {};
|
|
369
|
+
let newSessionHandler: (options?: any) => Promise<{ cancelled: boolean }> = async () => ({ cancelled: false });
|
|
370
|
+
let branchHandler: (entryId: string) => Promise<{ cancelled: boolean }> = async () => ({ cancelled: false });
|
|
371
|
+
let navigateTreeHandler: (targetId: string, options?: any) => Promise<{ cancelled: boolean }> = async () => ({
|
|
372
|
+
cancelled: false,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const api = {
|
|
376
|
+
on: (event: string, handler: (...args: unknown[]) => Promise<unknown>) => {
|
|
377
|
+
const list = handlers.get(event) ?? [];
|
|
378
|
+
list.push(handler);
|
|
379
|
+
handlers.set(event, list);
|
|
380
|
+
},
|
|
381
|
+
sendMessage: (message: any, triggerTurn?: boolean) => {
|
|
382
|
+
sendMessageHandler(message, triggerTurn);
|
|
383
|
+
},
|
|
384
|
+
appendEntry: (customType: string, data?: any) => {
|
|
385
|
+
appendEntryHandler(customType, data);
|
|
386
|
+
},
|
|
387
|
+
registerMessageRenderer: (customType: string, renderer: any) => {
|
|
388
|
+
messageRenderers.set(customType, renderer);
|
|
389
|
+
},
|
|
390
|
+
registerCommand: (name: string, options: any) => {
|
|
391
|
+
commands.set(name, { name, ...options });
|
|
392
|
+
},
|
|
393
|
+
newSession: (options?: any) => newSessionHandler(options),
|
|
394
|
+
branch: (entryId: string) => branchHandler(entryId),
|
|
395
|
+
navigateTree: (targetId: string, options?: any) => navigateTreeHandler(targetId, options),
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
def.factory(api as any);
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
path: def.path ?? "<inline>",
|
|
402
|
+
resolvedPath: def.path ?? "<inline>",
|
|
403
|
+
handlers,
|
|
404
|
+
messageRenderers,
|
|
405
|
+
commands,
|
|
406
|
+
setSendMessageHandler: (handler: (message: any, triggerTurn?: boolean) => void) => {
|
|
407
|
+
sendMessageHandler = handler;
|
|
408
|
+
},
|
|
409
|
+
setAppendEntryHandler: (handler: (customType: string, data?: any) => void) => {
|
|
410
|
+
appendEntryHandler = handler;
|
|
411
|
+
},
|
|
412
|
+
setNewSessionHandler: (handler: (options?: any) => Promise<{ cancelled: boolean }>) => {
|
|
413
|
+
newSessionHandler = handler;
|
|
414
|
+
},
|
|
415
|
+
setBranchHandler: (handler: (entryId: string) => Promise<{ cancelled: boolean }>) => {
|
|
416
|
+
branchHandler = handler;
|
|
417
|
+
},
|
|
418
|
+
setNavigateTreeHandler: (handler: (targetId: string, options?: any) => Promise<{ cancelled: boolean }>) => {
|
|
419
|
+
navigateTreeHandler = handler;
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Factory
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Create an AgentSession with the specified options.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```typescript
|
|
432
|
+
* // Minimal - uses defaults
|
|
433
|
+
* const { session } = await createAgentSession();
|
|
434
|
+
*
|
|
435
|
+
* // With explicit model
|
|
436
|
+
* import { getModel } from '@oh-my-pi/pi-ai';
|
|
437
|
+
* const { session } = await createAgentSession({
|
|
438
|
+
* model: getModel('anthropic', 'claude-opus-4-5'),
|
|
439
|
+
* thinkingLevel: 'high',
|
|
440
|
+
* });
|
|
441
|
+
*
|
|
442
|
+
* // Continue previous session
|
|
443
|
+
* const { session, modelFallbackMessage } = await createAgentSession({
|
|
444
|
+
* continueSession: true,
|
|
445
|
+
* });
|
|
446
|
+
*
|
|
447
|
+
* // Full control
|
|
448
|
+
* const { session } = await createAgentSession({
|
|
449
|
+
* model: myModel,
|
|
450
|
+
* getApiKey: async () => process.env.MY_KEY,
|
|
451
|
+
* systemPrompt: 'You are helpful.',
|
|
452
|
+
* tools: [readTool, bashTool],
|
|
453
|
+
* hooks: [],
|
|
454
|
+
* skills: [],
|
|
455
|
+
* sessionManager: SessionManager.inMemory(),
|
|
456
|
+
* });
|
|
457
|
+
* ```
|
|
458
|
+
*/
|
|
459
|
+
export async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {
|
|
460
|
+
const cwd = options.cwd ?? process.cwd();
|
|
461
|
+
const agentDir = options.agentDir ?? getDefaultAgentDir();
|
|
462
|
+
|
|
463
|
+
// Use provided or create AuthStorage and ModelRegistry
|
|
464
|
+
const authStorage = options.authStorage ?? discoverAuthStorage(agentDir);
|
|
465
|
+
const modelRegistry = options.modelRegistry ?? discoverModels(authStorage, agentDir);
|
|
466
|
+
time("discoverModels");
|
|
467
|
+
|
|
468
|
+
const settingsManager = options.settingsManager ?? SettingsManager.create(cwd, agentDir);
|
|
469
|
+
time("settingsManager");
|
|
470
|
+
const sessionManager = options.sessionManager ?? SessionManager.create(cwd);
|
|
471
|
+
time("sessionManager");
|
|
472
|
+
|
|
473
|
+
// Check if session has existing data to restore
|
|
474
|
+
const existingSession = sessionManager.buildSessionContext();
|
|
475
|
+
time("loadSession");
|
|
476
|
+
const hasExistingSession = existingSession.messages.length > 0;
|
|
477
|
+
|
|
478
|
+
let model = options.model;
|
|
479
|
+
let modelFallbackMessage: string | undefined;
|
|
480
|
+
|
|
481
|
+
// If session has data, try to restore model from it
|
|
482
|
+
if (!model && hasExistingSession && existingSession.model) {
|
|
483
|
+
const restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);
|
|
484
|
+
if (restoredModel && (await modelRegistry.getApiKey(restoredModel))) {
|
|
485
|
+
model = restoredModel;
|
|
486
|
+
}
|
|
487
|
+
if (!model) {
|
|
488
|
+
modelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// If still no model, try settings default
|
|
493
|
+
if (!model) {
|
|
494
|
+
const defaultProvider = settingsManager.getDefaultProvider();
|
|
495
|
+
const defaultModelId = settingsManager.getDefaultModel();
|
|
496
|
+
if (defaultProvider && defaultModelId) {
|
|
497
|
+
const settingsModel = modelRegistry.find(defaultProvider, defaultModelId);
|
|
498
|
+
if (settingsModel && (await modelRegistry.getApiKey(settingsModel))) {
|
|
499
|
+
model = settingsModel;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Fall back to first available model with a valid API key
|
|
505
|
+
if (!model) {
|
|
506
|
+
for (const m of modelRegistry.getAll()) {
|
|
507
|
+
if (await modelRegistry.getApiKey(m)) {
|
|
508
|
+
model = m;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
time("findAvailableModel");
|
|
513
|
+
if (model) {
|
|
514
|
+
if (modelFallbackMessage) {
|
|
515
|
+
modelFallbackMessage += `. Using ${model.provider}/${model.id}`;
|
|
516
|
+
}
|
|
517
|
+
} else {
|
|
518
|
+
// No models available - set message so user knows to /login or configure keys
|
|
519
|
+
modelFallbackMessage = "No models available. Use /login or set an API key environment variable.";
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
let thinkingLevel = options.thinkingLevel;
|
|
524
|
+
|
|
525
|
+
// If session has data, restore thinking level from it
|
|
526
|
+
if (thinkingLevel === undefined && hasExistingSession) {
|
|
527
|
+
thinkingLevel = existingSession.thinkingLevel as ThinkingLevel;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Fall back to settings default
|
|
531
|
+
if (thinkingLevel === undefined) {
|
|
532
|
+
thinkingLevel = settingsManager.getDefaultThinkingLevel() ?? "off";
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Clamp to model capabilities
|
|
536
|
+
if (!model || !model.reasoning) {
|
|
537
|
+
thinkingLevel = "off";
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const skills = options.skills ?? discoverSkills(cwd, agentDir, settingsManager.getSkillsSettings());
|
|
541
|
+
time("discoverSkills");
|
|
542
|
+
|
|
543
|
+
const contextFiles = options.contextFiles ?? discoverContextFiles(cwd, agentDir);
|
|
544
|
+
time("discoverContextFiles");
|
|
545
|
+
|
|
546
|
+
// Hook runner - created early for hooks
|
|
547
|
+
let hookRunner: HookRunner | undefined;
|
|
548
|
+
if (options.hooks !== undefined) {
|
|
549
|
+
if (options.hooks.length > 0) {
|
|
550
|
+
const loadedHooks = createLoadedHooksFromDefinitions(options.hooks);
|
|
551
|
+
hookRunner = new HookRunner(loadedHooks, cwd, sessionManager, modelRegistry);
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
// Discover hooks, merging with additional paths
|
|
555
|
+
const configuredPaths = [...settingsManager.getHookPaths(), ...(options.additionalHookPaths ?? [])];
|
|
556
|
+
const { hooks, errors } = await discoverAndLoadHooks(configuredPaths, cwd, agentDir);
|
|
557
|
+
time("discoverAndLoadHooks");
|
|
558
|
+
for (const { path, error } of errors) {
|
|
559
|
+
console.error(`Failed to load hook "${path}": ${error}`);
|
|
560
|
+
}
|
|
561
|
+
if (hooks.length > 0) {
|
|
562
|
+
hookRunner = new HookRunner(hooks, cwd, sessionManager, modelRegistry);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const sessionContext = {
|
|
567
|
+
getSessionFile: () => sessionManager.getSessionFile() ?? null,
|
|
568
|
+
};
|
|
569
|
+
const builtInTools = options.tools ?? createCodingTools(cwd, options.hasUI ?? false, sessionContext);
|
|
570
|
+
time("createCodingTools");
|
|
571
|
+
|
|
572
|
+
let customToolsResult: CustomToolsLoadResult;
|
|
573
|
+
if (options.customTools !== undefined) {
|
|
574
|
+
// Use provided custom tools
|
|
575
|
+
const loadedTools: LoadedCustomTool[] = options.customTools.map((ct) => ({
|
|
576
|
+
path: ct.path ?? "<inline>",
|
|
577
|
+
resolvedPath: ct.path ?? "<inline>",
|
|
578
|
+
tool: ct.tool,
|
|
579
|
+
}));
|
|
580
|
+
customToolsResult = {
|
|
581
|
+
tools: loadedTools,
|
|
582
|
+
errors: [],
|
|
583
|
+
setUIContext: () => {},
|
|
584
|
+
};
|
|
585
|
+
} else {
|
|
586
|
+
// Discover custom tools, merging with additional paths
|
|
587
|
+
const configuredPaths = [...settingsManager.getCustomToolPaths(), ...(options.additionalCustomToolPaths ?? [])];
|
|
588
|
+
customToolsResult = await discoverAndLoadCustomTools(configuredPaths, cwd, Object.keys(allTools), agentDir);
|
|
589
|
+
time("discoverAndLoadCustomTools");
|
|
590
|
+
for (const { path, error } of customToolsResult.errors) {
|
|
591
|
+
console.error(`Failed to load custom tool "${path}": ${error}`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Discover MCP tools from .mcp.json files
|
|
596
|
+
let mcpManager: MCPManager | undefined;
|
|
597
|
+
const enableMCP = options.enableMCP ?? true;
|
|
598
|
+
if (enableMCP) {
|
|
599
|
+
const mcpResult = await discoverAndLoadMCPTools(cwd);
|
|
600
|
+
time("discoverAndLoadMCPTools");
|
|
601
|
+
mcpManager = mcpResult.manager;
|
|
602
|
+
|
|
603
|
+
// Log MCP errors
|
|
604
|
+
for (const { path, error } of mcpResult.errors) {
|
|
605
|
+
console.error(`MCP "${path}": ${error}`);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Merge MCP tools into custom tools result
|
|
609
|
+
if (mcpResult.tools.length > 0) {
|
|
610
|
+
customToolsResult = {
|
|
611
|
+
...customToolsResult,
|
|
612
|
+
tools: [...customToolsResult.tools, ...mcpResult.tools],
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Add specialized Exa web search tools if EXA_API_KEY is available
|
|
618
|
+
const exaSettings = settingsManager.getExaSettings();
|
|
619
|
+
if (exaSettings.enabled && exaSettings.enableSearch) {
|
|
620
|
+
const { getWebSearchTools } = await import("./tools/web-search/index.js");
|
|
621
|
+
const exaWebSearchTools = await getWebSearchTools({
|
|
622
|
+
enableLinkedin: exaSettings.enableLinkedin,
|
|
623
|
+
enableCompany: exaSettings.enableCompany,
|
|
624
|
+
});
|
|
625
|
+
// Filter out the base web_search (already in built-in tools), add specialized Exa tools
|
|
626
|
+
const specializedTools = exaWebSearchTools.filter((t) => t.name !== "web_search");
|
|
627
|
+
if (specializedTools.length > 0) {
|
|
628
|
+
const loadedExaTools: LoadedCustomTool[] = specializedTools.map((tool) => ({
|
|
629
|
+
path: "<exa>",
|
|
630
|
+
resolvedPath: "<exa>",
|
|
631
|
+
tool,
|
|
632
|
+
}));
|
|
633
|
+
customToolsResult = {
|
|
634
|
+
...customToolsResult,
|
|
635
|
+
tools: [...customToolsResult.tools, ...loadedExaTools],
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
time("getWebSearchTools");
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
let agent: Agent;
|
|
642
|
+
let session: AgentSession;
|
|
643
|
+
const getSessionContext = () => ({
|
|
644
|
+
sessionManager,
|
|
645
|
+
modelRegistry,
|
|
646
|
+
model: agent.state.model,
|
|
647
|
+
isIdle: () => !session.isStreaming,
|
|
648
|
+
hasQueuedMessages: () => session.queuedMessageCount > 0,
|
|
649
|
+
abort: () => {
|
|
650
|
+
session.abort();
|
|
651
|
+
},
|
|
652
|
+
});
|
|
653
|
+
const toolContextStore = createToolContextStore(getSessionContext);
|
|
654
|
+
const wrappedCustomTools = wrapCustomTools(customToolsResult.tools, getSessionContext);
|
|
655
|
+
const baseSetUIContext = customToolsResult.setUIContext;
|
|
656
|
+
customToolsResult = {
|
|
657
|
+
...customToolsResult,
|
|
658
|
+
setUIContext: (uiContext, hasUI) => {
|
|
659
|
+
toolContextStore.setUIContext(uiContext, hasUI);
|
|
660
|
+
baseSetUIContext(uiContext, hasUI);
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
let allToolsArray: Tool[] = [...builtInTools, ...wrappedCustomTools];
|
|
665
|
+
time("combineTools");
|
|
666
|
+
|
|
667
|
+
// Apply bash interception to redirect common shell patterns to proper tools
|
|
668
|
+
allToolsArray = applyBashInterception(allToolsArray);
|
|
669
|
+
time("applyBashInterception");
|
|
670
|
+
|
|
671
|
+
if (hookRunner) {
|
|
672
|
+
allToolsArray = wrapToolsWithHooks(allToolsArray, hookRunner) as Tool[];
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
let systemPrompt: string;
|
|
676
|
+
const defaultPrompt = buildSystemPromptInternal({
|
|
677
|
+
cwd,
|
|
678
|
+
agentDir,
|
|
679
|
+
skills,
|
|
680
|
+
contextFiles,
|
|
681
|
+
});
|
|
682
|
+
time("buildSystemPrompt");
|
|
683
|
+
|
|
684
|
+
if (options.systemPrompt === undefined) {
|
|
685
|
+
systemPrompt = defaultPrompt;
|
|
686
|
+
} else if (typeof options.systemPrompt === "string") {
|
|
687
|
+
systemPrompt = buildSystemPromptInternal({
|
|
688
|
+
cwd,
|
|
689
|
+
agentDir,
|
|
690
|
+
skills,
|
|
691
|
+
contextFiles,
|
|
692
|
+
customPrompt: options.systemPrompt,
|
|
693
|
+
});
|
|
694
|
+
} else {
|
|
695
|
+
systemPrompt = options.systemPrompt(defaultPrompt);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const slashCommands = options.slashCommands ?? discoverSlashCommands(cwd, agentDir);
|
|
699
|
+
time("discoverSlashCommands");
|
|
700
|
+
|
|
701
|
+
agent = new Agent({
|
|
702
|
+
initialState: {
|
|
703
|
+
systemPrompt,
|
|
704
|
+
model,
|
|
705
|
+
thinkingLevel,
|
|
706
|
+
tools: allToolsArray,
|
|
707
|
+
},
|
|
708
|
+
convertToLlm,
|
|
709
|
+
transformContext: hookRunner
|
|
710
|
+
? async (messages) => {
|
|
711
|
+
return hookRunner.emitContext(messages);
|
|
712
|
+
}
|
|
713
|
+
: undefined,
|
|
714
|
+
queueMode: settingsManager.getQueueMode(),
|
|
715
|
+
getToolContext: toolContextStore.getContext,
|
|
716
|
+
getApiKey: async () => {
|
|
717
|
+
const currentModel = agent.state.model;
|
|
718
|
+
if (!currentModel) {
|
|
719
|
+
throw new Error("No model selected");
|
|
720
|
+
}
|
|
721
|
+
const key = await modelRegistry.getApiKey(currentModel);
|
|
722
|
+
if (!key) {
|
|
723
|
+
throw new Error(`No API key found for provider "${currentModel.provider}"`);
|
|
724
|
+
}
|
|
725
|
+
return key;
|
|
726
|
+
},
|
|
727
|
+
});
|
|
728
|
+
time("createAgent");
|
|
729
|
+
|
|
730
|
+
// Restore messages if session has existing data
|
|
731
|
+
if (hasExistingSession) {
|
|
732
|
+
agent.replaceMessages(existingSession.messages);
|
|
733
|
+
} else {
|
|
734
|
+
// Save initial model and thinking level for new sessions so they can be restored on resume
|
|
735
|
+
if (model) {
|
|
736
|
+
sessionManager.appendModelChange(model.provider, model.id);
|
|
737
|
+
}
|
|
738
|
+
sessionManager.appendThinkingLevelChange(thinkingLevel);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
session = new AgentSession({
|
|
742
|
+
agent,
|
|
743
|
+
sessionManager,
|
|
744
|
+
settingsManager,
|
|
745
|
+
scopedModels: options.scopedModels,
|
|
746
|
+
fileCommands: slashCommands,
|
|
747
|
+
hookRunner,
|
|
748
|
+
customTools: customToolsResult.tools,
|
|
749
|
+
skillsSettings: settingsManager.getSkillsSettings(),
|
|
750
|
+
modelRegistry,
|
|
751
|
+
});
|
|
752
|
+
time("createAgentSession");
|
|
753
|
+
|
|
754
|
+
return {
|
|
755
|
+
session,
|
|
756
|
+
customToolsResult,
|
|
757
|
+
mcpManager,
|
|
758
|
+
modelFallbackMessage,
|
|
759
|
+
};
|
|
760
|
+
}
|