@mariozechner/pi-coding-agent 0.49.3 → 0.50.1
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 +110 -1
- package/README.md +310 -1230
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +57 -23
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts +14 -0
- package/dist/cli/config-selector.d.ts.map +1 -0
- package/dist/cli/config-selector.js +31 -0
- package/dist/cli/config-selector.js.map +1 -0
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/core/agent-session.d.ts +60 -37
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +272 -69
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +8 -18
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +39 -55
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/diagnostics.d.ts +15 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +2 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/export-html/template.css +9 -0
- package/dist/core/export-html/template.js +6 -4
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -1
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +9 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +39 -12
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +112 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +9 -2
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +13 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/model-registry.d.ts +42 -2
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +154 -44
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +3 -2
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +130 -0
- package/dist/core/package-manager.d.ts.map +1 -0
- package/dist/core/package-manager.js +1177 -0
- package/dist/core/package-manager.js.map +1 -0
- package/dist/core/prompt-templates.d.ts +6 -0
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +114 -54
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resource-loader.d.ts +160 -0
- package/dist/core/resource-loader.d.ts.map +1 -0
- package/dist/core/resource-loader.js +604 -0
- package/dist/core/resource-loader.js.map +1 -0
- package/dist/core/sdk.d.ts +14 -105
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +52 -304
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +45 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +34 -16
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +104 -25
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +18 -10
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +126 -93
- package/dist/core/skills.js.map +1 -1
- package/dist/core/system-prompt.d.ts +3 -27
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +16 -103
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +4 -4
- package/dist/core/tools/read.js.map +1 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +209 -97
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +5 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +29 -9
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts +71 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/config-selector.js +468 -0
- package/dist/modes/interactive/components/config-selector.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +4 -0
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +3 -4
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +18 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +195 -87
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +5 -5
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +42 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +538 -204
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +1 -1
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +8 -1
- package/dist/modes/interactive/theme/theme.d.ts +8 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +72 -25
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +7 -74
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +17 -82
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/git.d.ts +2 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +6 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/shell.d.ts +1 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +14 -1
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/sleep.d.ts +5 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +17 -0
- package/dist/utils/sleep.js.map +1 -0
- package/docs/compaction.md +23 -21
- package/docs/custom-provider.md +538 -0
- package/docs/development.md +69 -0
- package/docs/extensions.md +182 -118
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/json.md +79 -0
- package/docs/keybindings.md +162 -0
- package/docs/models.md +193 -0
- package/docs/packages.md +168 -0
- package/docs/prompt-templates.md +67 -0
- package/docs/providers.md +147 -0
- package/docs/sdk.md +111 -178
- package/docs/session.md +167 -16
- package/docs/settings.md +216 -0
- package/docs/shell-aliases.md +13 -0
- package/docs/skills.md +111 -202
- package/docs/terminal-setup.md +65 -0
- package/docs/themes.md +295 -0
- package/docs/tui.md +36 -5
- package/docs/windows.md +17 -0
- package/examples/README.md +1 -0
- package/examples/extensions/README.md +22 -2
- package/examples/extensions/bookmark.ts +50 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +349 -0
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
- package/examples/extensions/doom-overlay/doom/build.sh +1 -1
- package/examples/extensions/event-bus.ts +43 -0
- package/examples/extensions/message-renderer.ts +59 -0
- package/examples/extensions/session-name.ts +27 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/02-custom-model.ts +3 -3
- package/examples/sdk/03-custom-prompt.ts +20 -9
- package/examples/sdk/04-skills.ts +26 -27
- package/examples/sdk/06-extensions.ts +15 -6
- package/examples/sdk/07-context-files.ts +22 -18
- package/examples/sdk/08-prompt-templates.ts +19 -14
- package/examples/sdk/09-api-keys-and-oauth.ts +5 -12
- package/examples/sdk/10-settings.ts +3 -3
- package/examples/sdk/12-full-control.ts +16 -7
- package/examples/sdk/README.md +24 -30
- package/package.json +4 -4
- package/docs/theme.md +0 -617
- package/examples/extensions/chalk-logger.ts +0 -26
|
@@ -13,15 +13,35 @@
|
|
|
13
13
|
* Modes use this class and add their own I/O layer on top.
|
|
14
14
|
*/
|
|
15
15
|
import { readFileSync } from "node:fs";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { isContextOverflow, modelsAreEqual, resetApiProviders, supportsXhigh } from "@mariozechner/pi-ai";
|
|
18
|
+
import { getDocsPath } from "../config.js";
|
|
18
19
|
import { theme } from "../modes/interactive/theme/theme.js";
|
|
19
20
|
import { stripFrontmatter } from "../utils/frontmatter.js";
|
|
21
|
+
import { sleep } from "../utils/sleep.js";
|
|
20
22
|
import { executeBash as executeBashCommand, executeBashWithOperations } from "./bash-executor.js";
|
|
21
23
|
import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
|
|
22
24
|
import { exportSessionToHtml } from "./export-html/index.js";
|
|
23
25
|
import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
|
|
26
|
+
import { ExtensionRunner, wrapRegisteredTools, wrapToolsWithExtensions, } from "./extensions/index.js";
|
|
24
27
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
28
|
+
import { buildSystemPrompt } from "./system-prompt.js";
|
|
29
|
+
import { createAllTools } from "./tools/index.js";
|
|
30
|
+
/**
|
|
31
|
+
* Parse a skill block from message text.
|
|
32
|
+
* Returns null if the text doesn't contain a skill block.
|
|
33
|
+
*/
|
|
34
|
+
export function parseSkillBlock(text) {
|
|
35
|
+
const match = text.match(/^<skill name="([^"]+)" location="([^"]+)">\n([\s\S]*?)\n<\/skill>(?:\n\n([\s\S]+))?$/);
|
|
36
|
+
if (!match)
|
|
37
|
+
return null;
|
|
38
|
+
return {
|
|
39
|
+
name: match[1],
|
|
40
|
+
location: match[2],
|
|
41
|
+
content: match[3],
|
|
42
|
+
userMessage: match[4]?.trim() || undefined,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
25
45
|
// ============================================================================
|
|
26
46
|
// Constants
|
|
27
47
|
// ============================================================================
|
|
@@ -37,7 +57,6 @@ export class AgentSession {
|
|
|
37
57
|
sessionManager;
|
|
38
58
|
settingsManager;
|
|
39
59
|
_scopedModels;
|
|
40
|
-
_promptTemplates;
|
|
41
60
|
// Event subscription state
|
|
42
61
|
_unsubscribeAgent;
|
|
43
62
|
_eventListeners = [];
|
|
@@ -63,34 +82,43 @@ export class AgentSession {
|
|
|
63
82
|
// Extension system
|
|
64
83
|
_extensionRunner = undefined;
|
|
65
84
|
_turnIndex = 0;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
_resourceLoader;
|
|
86
|
+
_customTools;
|
|
87
|
+
_baseToolRegistry = new Map();
|
|
88
|
+
_cwd;
|
|
89
|
+
_extensionRunnerRef;
|
|
90
|
+
_initialActiveToolNames;
|
|
91
|
+
_baseToolsOverride;
|
|
92
|
+
_extensionUIContext;
|
|
93
|
+
_extensionCommandContextActions;
|
|
94
|
+
_extensionShutdownHandler;
|
|
95
|
+
_extensionErrorListener;
|
|
96
|
+
_extensionErrorUnsubscriber;
|
|
69
97
|
// Model registry for API key resolution
|
|
70
98
|
_modelRegistry;
|
|
71
99
|
// Tool registry for extension getTools/setTools
|
|
72
|
-
_toolRegistry;
|
|
73
|
-
// Function to rebuild system prompt when tools change
|
|
74
|
-
_rebuildSystemPrompt;
|
|
100
|
+
_toolRegistry = new Map();
|
|
75
101
|
// Base system prompt (without extension appends) - used to apply fresh appends each turn
|
|
76
|
-
_baseSystemPrompt;
|
|
102
|
+
_baseSystemPrompt = "";
|
|
77
103
|
constructor(config) {
|
|
78
104
|
this.agent = config.agent;
|
|
79
105
|
this.sessionManager = config.sessionManager;
|
|
80
106
|
this.settingsManager = config.settingsManager;
|
|
81
107
|
this._scopedModels = config.scopedModels ?? [];
|
|
82
|
-
this.
|
|
83
|
-
this.
|
|
84
|
-
this.
|
|
85
|
-
this._skillWarnings = config.skillWarnings ?? [];
|
|
86
|
-
this._skillsSettings = config.skillsSettings;
|
|
108
|
+
this._resourceLoader = config.resourceLoader;
|
|
109
|
+
this._customTools = config.customTools ?? [];
|
|
110
|
+
this._cwd = config.cwd;
|
|
87
111
|
this._modelRegistry = config.modelRegistry;
|
|
88
|
-
this.
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
112
|
+
this._extensionRunnerRef = config.extensionRunnerRef;
|
|
113
|
+
this._initialActiveToolNames = config.initialActiveToolNames;
|
|
114
|
+
this._baseToolsOverride = config.baseToolsOverride;
|
|
91
115
|
// Always subscribe to agent events for internal handling
|
|
92
116
|
// (session persistence, extensions, auto-compaction, retry logic)
|
|
93
117
|
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
|
|
118
|
+
this._buildRuntime({
|
|
119
|
+
activeToolNames: this._initialActiveToolNames,
|
|
120
|
+
includeAllExtensionTools: true,
|
|
121
|
+
});
|
|
94
122
|
}
|
|
95
123
|
/** Model registry for API key resolution and model discovery */
|
|
96
124
|
get modelRegistry() {
|
|
@@ -334,10 +362,8 @@ export class AgentSession {
|
|
|
334
362
|
}
|
|
335
363
|
this.agent.setTools(tools);
|
|
336
364
|
// Rebuild base system prompt with new tool set
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
this.agent.setSystemPrompt(this._baseSystemPrompt);
|
|
340
|
-
}
|
|
365
|
+
this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
|
|
366
|
+
this.agent.setSystemPrompt(this._baseSystemPrompt);
|
|
341
367
|
}
|
|
342
368
|
/** Whether auto-compaction is currently running */
|
|
343
369
|
get isCompacting() {
|
|
@@ -373,7 +399,23 @@ export class AgentSession {
|
|
|
373
399
|
}
|
|
374
400
|
/** File-based prompt templates */
|
|
375
401
|
get promptTemplates() {
|
|
376
|
-
return this.
|
|
402
|
+
return this._resourceLoader.getPrompts().prompts;
|
|
403
|
+
}
|
|
404
|
+
_rebuildSystemPrompt(toolNames) {
|
|
405
|
+
const validToolNames = toolNames.filter((name) => this._baseToolRegistry.has(name));
|
|
406
|
+
const loaderSystemPrompt = this._resourceLoader.getSystemPrompt();
|
|
407
|
+
const loaderAppendSystemPrompt = this._resourceLoader.getAppendSystemPrompt();
|
|
408
|
+
const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : undefined;
|
|
409
|
+
const loadedSkills = this._resourceLoader.getSkills().skills;
|
|
410
|
+
const loadedContextFiles = this._resourceLoader.getAgentsFiles().agentsFiles;
|
|
411
|
+
return buildSystemPrompt({
|
|
412
|
+
cwd: this._cwd,
|
|
413
|
+
skills: loadedSkills,
|
|
414
|
+
contextFiles: loadedContextFiles,
|
|
415
|
+
customPrompt: loaderSystemPrompt,
|
|
416
|
+
appendSystemPrompt,
|
|
417
|
+
selectedTools: validToolNames,
|
|
418
|
+
});
|
|
377
419
|
}
|
|
378
420
|
// =========================================================================
|
|
379
421
|
// Prompting
|
|
@@ -415,7 +457,7 @@ export class AgentSession {
|
|
|
415
457
|
let expandedText = currentText;
|
|
416
458
|
if (expandPromptTemplates) {
|
|
417
459
|
expandedText = this._expandSkillCommand(expandedText);
|
|
418
|
-
expandedText = expandPromptTemplate(expandedText, [...this.
|
|
460
|
+
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
419
461
|
}
|
|
420
462
|
// If streaming, queue via steer() or followUp() based on option
|
|
421
463
|
if (this.isStreaming) {
|
|
@@ -435,7 +477,7 @@ export class AgentSession {
|
|
|
435
477
|
// Validate model
|
|
436
478
|
if (!this.model) {
|
|
437
479
|
throw new Error("No model selected.\n\n" +
|
|
438
|
-
`Use /login
|
|
480
|
+
`Use /login or set an API key environment variable. See ${join(getDocsPath(), "authentication.md")}\n\n` +
|
|
439
481
|
"Then use /model to select a model.");
|
|
440
482
|
}
|
|
441
483
|
// Validate API key
|
|
@@ -448,7 +490,7 @@ export class AgentSession {
|
|
|
448
490
|
`Run '/login ${this.model.provider}' to re-authenticate.`);
|
|
449
491
|
}
|
|
450
492
|
throw new Error(`No API key found for ${this.model.provider}.\n\n` +
|
|
451
|
-
`Use /login
|
|
493
|
+
`Use /login or set an API key environment variable. See ${join(getDocsPath(), "authentication.md")}`);
|
|
452
494
|
}
|
|
453
495
|
// Check if we need to compact before sending (catches aborted responses)
|
|
454
496
|
const lastAssistant = this._findLastAssistantMessage();
|
|
@@ -540,15 +582,14 @@ export class AgentSession {
|
|
|
540
582
|
const spaceIndex = text.indexOf(" ");
|
|
541
583
|
const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
|
|
542
584
|
const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
|
543
|
-
const skill = this.
|
|
585
|
+
const skill = this.resourceLoader.getSkills().skills.find((s) => s.name === skillName);
|
|
544
586
|
if (!skill)
|
|
545
587
|
return text; // Unknown skill, pass through
|
|
546
588
|
try {
|
|
547
589
|
const content = readFileSync(skill.filePath, "utf-8");
|
|
548
590
|
const body = stripFrontmatter(content).trim();
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
return args ? `${skillMessage}\n\n---\n\nUser: ${args}` : skillMessage;
|
|
591
|
+
const skillBlock = `<skill name="${skill.name}" location="${skill.filePath}">\nReferences are relative to ${skill.baseDir}.\n\n${body}\n</skill>`;
|
|
592
|
+
return args ? `${skillBlock}\n\n${args}` : skillBlock;
|
|
552
593
|
}
|
|
553
594
|
catch (err) {
|
|
554
595
|
// Emit error like extension commands do
|
|
@@ -573,7 +614,7 @@ export class AgentSession {
|
|
|
573
614
|
}
|
|
574
615
|
// Expand skill commands and prompt templates
|
|
575
616
|
let expandedText = this._expandSkillCommand(text);
|
|
576
|
-
expandedText = expandPromptTemplate(expandedText, [...this.
|
|
617
|
+
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
577
618
|
await this._queueSteer(expandedText);
|
|
578
619
|
}
|
|
579
620
|
/**
|
|
@@ -589,7 +630,7 @@ export class AgentSession {
|
|
|
589
630
|
}
|
|
590
631
|
// Expand skill commands and prompt templates
|
|
591
632
|
let expandedText = this._expandSkillCommand(text);
|
|
592
|
-
expandedText = expandPromptTemplate(expandedText, [...this.
|
|
633
|
+
expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
|
|
593
634
|
await this._queueFollowUp(expandedText);
|
|
594
635
|
}
|
|
595
636
|
/**
|
|
@@ -665,6 +706,8 @@ export class AgentSession {
|
|
|
665
706
|
else {
|
|
666
707
|
this.agent.appendMessage(appMessage);
|
|
667
708
|
this.sessionManager.appendCustomMessageEntry(message.customType, message.content, message.display, message.details);
|
|
709
|
+
this._emit({ type: "message_start", message: appMessage });
|
|
710
|
+
this._emit({ type: "message_end", message: appMessage });
|
|
668
711
|
}
|
|
669
712
|
}
|
|
670
713
|
/**
|
|
@@ -729,16 +772,8 @@ export class AgentSession {
|
|
|
729
772
|
getFollowUpMessages() {
|
|
730
773
|
return this._followUpMessages;
|
|
731
774
|
}
|
|
732
|
-
get
|
|
733
|
-
return this.
|
|
734
|
-
}
|
|
735
|
-
/** Skills loaded by SDK (empty if --no-skills or skills: [] was passed) */
|
|
736
|
-
get skills() {
|
|
737
|
-
return this._skills;
|
|
738
|
-
}
|
|
739
|
-
/** Skill loading warnings captured by SDK */
|
|
740
|
-
get skillWarnings() {
|
|
741
|
-
return this._skillWarnings;
|
|
775
|
+
get resourceLoader() {
|
|
776
|
+
return this._resourceLoader;
|
|
742
777
|
}
|
|
743
778
|
/**
|
|
744
779
|
* Abort current operation and wait for agent to become idle.
|
|
@@ -752,7 +787,8 @@ export class AgentSession {
|
|
|
752
787
|
* Start a new session, optionally with initial messages and parent tracking.
|
|
753
788
|
* Clears all messages and starts a new session.
|
|
754
789
|
* Listeners are preserved and will continue receiving events.
|
|
755
|
-
* @param options - Optional
|
|
790
|
+
* @param options.parentSession - Optional parent session path for tracking
|
|
791
|
+
* @param options.setup - Optional callback to initialize session (e.g., append messages)
|
|
756
792
|
* @returns true if completed, false if cancelled by extension
|
|
757
793
|
*/
|
|
758
794
|
async newSession(options) {
|
|
@@ -770,11 +806,18 @@ export class AgentSession {
|
|
|
770
806
|
this._disconnectFromAgent();
|
|
771
807
|
await this.abort();
|
|
772
808
|
this.agent.reset();
|
|
773
|
-
this.sessionManager.newSession(options);
|
|
809
|
+
this.sessionManager.newSession({ parentSession: options?.parentSession });
|
|
774
810
|
this.agent.sessionId = this.sessionManager.getSessionId();
|
|
775
811
|
this._steeringMessages = [];
|
|
776
812
|
this._followUpMessages = [];
|
|
777
813
|
this._pendingNextTurnMessages = [];
|
|
814
|
+
// Run setup callback if provided (e.g., to append initial messages)
|
|
815
|
+
if (options?.setup) {
|
|
816
|
+
await options.setup(this.sessionManager);
|
|
817
|
+
// Sync agent state with session manager after setup
|
|
818
|
+
const sessionContext = this.sessionManager.buildSessionContext();
|
|
819
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
820
|
+
}
|
|
778
821
|
this._reconnectToAgent();
|
|
779
822
|
// Emit session_switch event with reason "new" to extensions
|
|
780
823
|
if (this._extensionRunner) {
|
|
@@ -879,12 +922,6 @@ export class AgentSession {
|
|
|
879
922
|
await this._emitModelSelect(nextModel, currentModel, "cycle");
|
|
880
923
|
return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
|
|
881
924
|
}
|
|
882
|
-
/**
|
|
883
|
-
* Get all available models with valid API keys.
|
|
884
|
-
*/
|
|
885
|
-
async getAvailableModels() {
|
|
886
|
-
return this._modelRegistry.getAvailable();
|
|
887
|
-
}
|
|
888
925
|
// =========================================================================
|
|
889
926
|
// Thinking Level Management
|
|
890
927
|
// =========================================================================
|
|
@@ -1255,6 +1292,188 @@ export class AgentSession {
|
|
|
1255
1292
|
get autoCompactionEnabled() {
|
|
1256
1293
|
return this.settingsManager.getCompactionEnabled();
|
|
1257
1294
|
}
|
|
1295
|
+
async bindExtensions(bindings) {
|
|
1296
|
+
if (bindings.uiContext !== undefined) {
|
|
1297
|
+
this._extensionUIContext = bindings.uiContext;
|
|
1298
|
+
}
|
|
1299
|
+
if (bindings.commandContextActions !== undefined) {
|
|
1300
|
+
this._extensionCommandContextActions = bindings.commandContextActions;
|
|
1301
|
+
}
|
|
1302
|
+
if (bindings.shutdownHandler !== undefined) {
|
|
1303
|
+
this._extensionShutdownHandler = bindings.shutdownHandler;
|
|
1304
|
+
}
|
|
1305
|
+
if (bindings.onError !== undefined) {
|
|
1306
|
+
this._extensionErrorListener = bindings.onError;
|
|
1307
|
+
}
|
|
1308
|
+
if (this._extensionRunner) {
|
|
1309
|
+
this._applyExtensionBindings(this._extensionRunner);
|
|
1310
|
+
await this._extensionRunner.emit({ type: "session_start" });
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
_applyExtensionBindings(runner) {
|
|
1314
|
+
runner.setUIContext(this._extensionUIContext);
|
|
1315
|
+
runner.bindCommandContext(this._extensionCommandContextActions);
|
|
1316
|
+
this._extensionErrorUnsubscriber?.();
|
|
1317
|
+
this._extensionErrorUnsubscriber = this._extensionErrorListener
|
|
1318
|
+
? runner.onError(this._extensionErrorListener)
|
|
1319
|
+
: undefined;
|
|
1320
|
+
}
|
|
1321
|
+
_bindExtensionCore(runner) {
|
|
1322
|
+
runner.bindCore({
|
|
1323
|
+
sendMessage: (message, options) => {
|
|
1324
|
+
this.sendCustomMessage(message, options).catch((err) => {
|
|
1325
|
+
runner.emitError({
|
|
1326
|
+
extensionPath: "<runtime>",
|
|
1327
|
+
event: "send_message",
|
|
1328
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1329
|
+
});
|
|
1330
|
+
});
|
|
1331
|
+
},
|
|
1332
|
+
sendUserMessage: (content, options) => {
|
|
1333
|
+
this.sendUserMessage(content, options).catch((err) => {
|
|
1334
|
+
runner.emitError({
|
|
1335
|
+
extensionPath: "<runtime>",
|
|
1336
|
+
event: "send_user_message",
|
|
1337
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1338
|
+
});
|
|
1339
|
+
});
|
|
1340
|
+
},
|
|
1341
|
+
appendEntry: (customType, data) => {
|
|
1342
|
+
this.sessionManager.appendCustomEntry(customType, data);
|
|
1343
|
+
},
|
|
1344
|
+
setSessionName: (name) => {
|
|
1345
|
+
this.sessionManager.appendSessionInfo(name);
|
|
1346
|
+
},
|
|
1347
|
+
getSessionName: () => {
|
|
1348
|
+
return this.sessionManager.getSessionName();
|
|
1349
|
+
},
|
|
1350
|
+
setLabel: (entryId, label) => {
|
|
1351
|
+
this.sessionManager.appendLabelChange(entryId, label);
|
|
1352
|
+
},
|
|
1353
|
+
getActiveTools: () => this.getActiveToolNames(),
|
|
1354
|
+
getAllTools: () => this.getAllTools(),
|
|
1355
|
+
setActiveTools: (toolNames) => this.setActiveToolsByName(toolNames),
|
|
1356
|
+
setModel: async (model) => {
|
|
1357
|
+
const key = await this.modelRegistry.getApiKey(model);
|
|
1358
|
+
if (!key)
|
|
1359
|
+
return false;
|
|
1360
|
+
await this.setModel(model);
|
|
1361
|
+
return true;
|
|
1362
|
+
},
|
|
1363
|
+
getThinkingLevel: () => this.thinkingLevel,
|
|
1364
|
+
setThinkingLevel: (level) => this.setThinkingLevel(level),
|
|
1365
|
+
}, {
|
|
1366
|
+
getModel: () => this.model,
|
|
1367
|
+
isIdle: () => !this.isStreaming,
|
|
1368
|
+
abort: () => this.abort(),
|
|
1369
|
+
hasPendingMessages: () => this.pendingMessageCount > 0,
|
|
1370
|
+
shutdown: () => {
|
|
1371
|
+
this._extensionShutdownHandler?.();
|
|
1372
|
+
},
|
|
1373
|
+
getContextUsage: () => this.getContextUsage(),
|
|
1374
|
+
compact: (options) => {
|
|
1375
|
+
void (async () => {
|
|
1376
|
+
try {
|
|
1377
|
+
const result = await this.compact(options?.customInstructions);
|
|
1378
|
+
options?.onComplete?.(result);
|
|
1379
|
+
}
|
|
1380
|
+
catch (error) {
|
|
1381
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1382
|
+
options?.onError?.(err);
|
|
1383
|
+
}
|
|
1384
|
+
})();
|
|
1385
|
+
},
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
_buildRuntime(options) {
|
|
1389
|
+
const autoResizeImages = this.settingsManager.getImageAutoResize();
|
|
1390
|
+
const shellCommandPrefix = this.settingsManager.getShellCommandPrefix();
|
|
1391
|
+
const baseTools = this._baseToolsOverride
|
|
1392
|
+
? this._baseToolsOverride
|
|
1393
|
+
: createAllTools(this._cwd, {
|
|
1394
|
+
read: { autoResizeImages },
|
|
1395
|
+
bash: { commandPrefix: shellCommandPrefix },
|
|
1396
|
+
});
|
|
1397
|
+
this._baseToolRegistry = new Map(Object.entries(baseTools).map(([name, tool]) => [name, tool]));
|
|
1398
|
+
const extensionsResult = this._resourceLoader.getExtensions();
|
|
1399
|
+
if (options.flagValues) {
|
|
1400
|
+
for (const [name, value] of options.flagValues) {
|
|
1401
|
+
extensionsResult.runtime.flagValues.set(name, value);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
const hasExtensions = extensionsResult.extensions.length > 0;
|
|
1405
|
+
const hasCustomTools = this._customTools.length > 0;
|
|
1406
|
+
this._extensionRunner =
|
|
1407
|
+
hasExtensions || hasCustomTools
|
|
1408
|
+
? new ExtensionRunner(extensionsResult.extensions, extensionsResult.runtime, this._cwd, this.sessionManager, this._modelRegistry)
|
|
1409
|
+
: undefined;
|
|
1410
|
+
if (this._extensionRunnerRef) {
|
|
1411
|
+
this._extensionRunnerRef.current = this._extensionRunner;
|
|
1412
|
+
}
|
|
1413
|
+
if (this._extensionRunner) {
|
|
1414
|
+
this._bindExtensionCore(this._extensionRunner);
|
|
1415
|
+
this._applyExtensionBindings(this._extensionRunner);
|
|
1416
|
+
}
|
|
1417
|
+
const registeredTools = this._extensionRunner?.getAllRegisteredTools() ?? [];
|
|
1418
|
+
const allCustomTools = [
|
|
1419
|
+
...registeredTools,
|
|
1420
|
+
...this._customTools.map((def) => ({ definition: def, extensionPath: "<sdk>" })),
|
|
1421
|
+
];
|
|
1422
|
+
const wrappedExtensionTools = this._extensionRunner
|
|
1423
|
+
? wrapRegisteredTools(allCustomTools, this._extensionRunner)
|
|
1424
|
+
: [];
|
|
1425
|
+
const toolRegistry = new Map(this._baseToolRegistry);
|
|
1426
|
+
for (const tool of wrappedExtensionTools) {
|
|
1427
|
+
toolRegistry.set(tool.name, tool);
|
|
1428
|
+
}
|
|
1429
|
+
const defaultActiveToolNames = this._baseToolsOverride
|
|
1430
|
+
? Object.keys(this._baseToolsOverride)
|
|
1431
|
+
: ["read", "bash", "edit", "write"];
|
|
1432
|
+
const baseActiveToolNames = options.activeToolNames ?? defaultActiveToolNames;
|
|
1433
|
+
const activeToolNameSet = new Set(baseActiveToolNames);
|
|
1434
|
+
if (options.includeAllExtensionTools) {
|
|
1435
|
+
for (const tool of wrappedExtensionTools) {
|
|
1436
|
+
activeToolNameSet.add(tool.name);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
const extensionToolNames = new Set(wrappedExtensionTools.map((tool) => tool.name));
|
|
1440
|
+
const activeBaseTools = Array.from(activeToolNameSet)
|
|
1441
|
+
.filter((name) => this._baseToolRegistry.has(name) && !extensionToolNames.has(name))
|
|
1442
|
+
.map((name) => this._baseToolRegistry.get(name));
|
|
1443
|
+
const activeExtensionTools = wrappedExtensionTools.filter((tool) => activeToolNameSet.has(tool.name));
|
|
1444
|
+
const activeToolsArray = [...activeBaseTools, ...activeExtensionTools];
|
|
1445
|
+
if (this._extensionRunner) {
|
|
1446
|
+
const wrappedActiveTools = wrapToolsWithExtensions(activeToolsArray, this._extensionRunner);
|
|
1447
|
+
this.agent.setTools(wrappedActiveTools);
|
|
1448
|
+
const wrappedAllTools = wrapToolsWithExtensions(Array.from(toolRegistry.values()), this._extensionRunner);
|
|
1449
|
+
this._toolRegistry = new Map(wrappedAllTools.map((tool) => [tool.name, tool]));
|
|
1450
|
+
}
|
|
1451
|
+
else {
|
|
1452
|
+
this.agent.setTools(activeToolsArray);
|
|
1453
|
+
this._toolRegistry = toolRegistry;
|
|
1454
|
+
}
|
|
1455
|
+
const systemPromptToolNames = Array.from(activeToolNameSet).filter((name) => this._baseToolRegistry.has(name));
|
|
1456
|
+
this._baseSystemPrompt = this._rebuildSystemPrompt(systemPromptToolNames);
|
|
1457
|
+
this.agent.setSystemPrompt(this._baseSystemPrompt);
|
|
1458
|
+
}
|
|
1459
|
+
async reload() {
|
|
1460
|
+
const previousFlagValues = this._extensionRunner?.getFlagValues();
|
|
1461
|
+
await this._extensionRunner?.emit({ type: "session_shutdown" });
|
|
1462
|
+
resetApiProviders();
|
|
1463
|
+
await this._resourceLoader.reload();
|
|
1464
|
+
this._buildRuntime({
|
|
1465
|
+
activeToolNames: this.getActiveToolNames(),
|
|
1466
|
+
flagValues: previousFlagValues,
|
|
1467
|
+
includeAllExtensionTools: true,
|
|
1468
|
+
});
|
|
1469
|
+
const hasBindings = this._extensionUIContext ||
|
|
1470
|
+
this._extensionCommandContextActions ||
|
|
1471
|
+
this._extensionShutdownHandler ||
|
|
1472
|
+
this._extensionErrorListener;
|
|
1473
|
+
if (this._extensionRunner && hasBindings) {
|
|
1474
|
+
await this._extensionRunner.emit({ type: "session_start" });
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1258
1477
|
// =========================================================================
|
|
1259
1478
|
// Auto-Retry
|
|
1260
1479
|
// =========================================================================
|
|
@@ -1270,8 +1489,8 @@ export class AgentSession {
|
|
|
1270
1489
|
if (isContextOverflow(message, contextWindow))
|
|
1271
1490
|
return false;
|
|
1272
1491
|
const err = message.errorMessage;
|
|
1273
|
-
// Match: overloaded_error, rate limit, 429, 500, 502, 503, 504, service unavailable, connection errors, fetch failed
|
|
1274
|
-
return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server error|internal error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers/i.test(err);
|
|
1492
|
+
// Match: overloaded_error, rate limit, 429, 500, 502, 503, 504, service unavailable, connection errors, fetch failed, terminated
|
|
1493
|
+
return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server error|internal error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated/i.test(err);
|
|
1275
1494
|
}
|
|
1276
1495
|
/**
|
|
1277
1496
|
* Handle retryable errors with exponential backoff.
|
|
@@ -1316,7 +1535,7 @@ export class AgentSession {
|
|
|
1316
1535
|
// Wait with exponential backoff (abortable)
|
|
1317
1536
|
this._retryAbortController = new AbortController();
|
|
1318
1537
|
try {
|
|
1319
|
-
await
|
|
1538
|
+
await sleep(delayMs, this._retryAbortController.signal);
|
|
1320
1539
|
}
|
|
1321
1540
|
catch {
|
|
1322
1541
|
// Aborted during sleep - emit end event so UI can clean up
|
|
@@ -1341,22 +1560,6 @@ export class AgentSession {
|
|
|
1341
1560
|
}, 0);
|
|
1342
1561
|
return true;
|
|
1343
1562
|
}
|
|
1344
|
-
/**
|
|
1345
|
-
* Sleep helper that respects abort signal.
|
|
1346
|
-
*/
|
|
1347
|
-
_sleep(ms, signal) {
|
|
1348
|
-
return new Promise((resolve, reject) => {
|
|
1349
|
-
if (signal?.aborted) {
|
|
1350
|
-
reject(new Error("Aborted"));
|
|
1351
|
-
return;
|
|
1352
|
-
}
|
|
1353
|
-
const timeout = setTimeout(resolve, ms);
|
|
1354
|
-
signal?.addEventListener("abort", () => {
|
|
1355
|
-
clearTimeout(timeout);
|
|
1356
|
-
reject(new Error("Aborted"));
|
|
1357
|
-
});
|
|
1358
|
-
});
|
|
1359
|
-
}
|
|
1360
1563
|
/**
|
|
1361
1564
|
* Cancel in-progress retry.
|
|
1362
1565
|
*/
|