@mariozechner/pi-coding-agent 0.55.2 → 0.55.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +1 -9
- package/dist/core/agent-session.d.ts +9 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +141 -44
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +6 -0
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +3 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +1 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +8 -2
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +1 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/system-prompt.d.ts +4 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +34 -12
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +2 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +28 -3
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +0 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/extensions.md +20 -1
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/dynamic-tools.ts +74 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -63,6 +63,7 @@ export class AgentSession {
|
|
|
63
63
|
// Event subscription state
|
|
64
64
|
_unsubscribeAgent;
|
|
65
65
|
_eventListeners = [];
|
|
66
|
+
_agentEventQueue = Promise.resolve();
|
|
66
67
|
/** Tracks pending steering messages for UI display. Removed when delivered. */
|
|
67
68
|
_steeringMessages = [];
|
|
68
69
|
/** Tracks pending follow-up messages for UI display. Removed when delivered. */
|
|
@@ -101,6 +102,8 @@ export class AgentSession {
|
|
|
101
102
|
_modelRegistry;
|
|
102
103
|
// Tool registry for extension getTools/setTools
|
|
103
104
|
_toolRegistry = new Map();
|
|
105
|
+
_toolPromptSnippets = new Map();
|
|
106
|
+
_toolPromptGuidelines = new Map();
|
|
104
107
|
// Base system prompt (without extension appends) - used to apply fresh appends each turn
|
|
105
108
|
_baseSystemPrompt = "";
|
|
106
109
|
constructor(config) {
|
|
@@ -139,7 +142,43 @@ export class AgentSession {
|
|
|
139
142
|
// Track last assistant message for auto-compaction check
|
|
140
143
|
_lastAssistantMessage = undefined;
|
|
141
144
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
142
|
-
_handleAgentEvent =
|
|
145
|
+
_handleAgentEvent = (event) => {
|
|
146
|
+
// Create retry promise synchronously before queueing async processing.
|
|
147
|
+
// Agent.emit() calls this handler synchronously, and prompt() calls waitForRetry()
|
|
148
|
+
// as soon as agent.prompt() resolves. If _retryPromise is created only inside
|
|
149
|
+
// _processAgentEvent, slow earlier queued events can delay agent_end processing
|
|
150
|
+
// and waitForRetry() can miss the in-flight retry.
|
|
151
|
+
this._createRetryPromiseForAgentEnd(event);
|
|
152
|
+
this._agentEventQueue = this._agentEventQueue.then(() => this._processAgentEvent(event), () => this._processAgentEvent(event));
|
|
153
|
+
// Keep queue alive if an event handler fails
|
|
154
|
+
this._agentEventQueue.catch(() => { });
|
|
155
|
+
};
|
|
156
|
+
_createRetryPromiseForAgentEnd(event) {
|
|
157
|
+
if (event.type !== "agent_end" || this._retryPromise) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const settings = this.settingsManager.getRetrySettings();
|
|
161
|
+
if (!settings.enabled) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const lastAssistant = this._findLastAssistantInMessages(event.messages);
|
|
165
|
+
if (!lastAssistant || !this._isRetryableError(lastAssistant)) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
this._retryPromise = new Promise((resolve) => {
|
|
169
|
+
this._retryResolve = resolve;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
_findLastAssistantInMessages(messages) {
|
|
173
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
174
|
+
const message = messages[i];
|
|
175
|
+
if (message.role === "assistant") {
|
|
176
|
+
return message;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
async _processAgentEvent(event) {
|
|
143
182
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
144
183
|
// This ensures the UI sees the updated queue state
|
|
145
184
|
if (event.type === "message_start" && event.message.role === "user") {
|
|
@@ -206,7 +245,7 @@ export class AgentSession {
|
|
|
206
245
|
}
|
|
207
246
|
await this._checkCompaction(msg);
|
|
208
247
|
}
|
|
209
|
-
}
|
|
248
|
+
}
|
|
210
249
|
/** Resolve the pending retry promise */
|
|
211
250
|
_resolveRetry() {
|
|
212
251
|
if (this._retryResolve) {
|
|
@@ -465,8 +504,42 @@ export class AgentSession {
|
|
|
465
504
|
get promptTemplates() {
|
|
466
505
|
return this._resourceLoader.getPrompts().prompts;
|
|
467
506
|
}
|
|
507
|
+
_normalizePromptSnippet(text) {
|
|
508
|
+
if (!text)
|
|
509
|
+
return undefined;
|
|
510
|
+
const oneLine = text
|
|
511
|
+
.replace(/[\r\n]+/g, " ")
|
|
512
|
+
.replace(/\s+/g, " ")
|
|
513
|
+
.trim();
|
|
514
|
+
return oneLine.length > 0 ? oneLine : undefined;
|
|
515
|
+
}
|
|
516
|
+
_normalizePromptGuidelines(guidelines) {
|
|
517
|
+
if (!guidelines || guidelines.length === 0) {
|
|
518
|
+
return [];
|
|
519
|
+
}
|
|
520
|
+
const unique = new Set();
|
|
521
|
+
for (const guideline of guidelines) {
|
|
522
|
+
const normalized = guideline.trim();
|
|
523
|
+
if (normalized.length > 0) {
|
|
524
|
+
unique.add(normalized);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return Array.from(unique);
|
|
528
|
+
}
|
|
468
529
|
_rebuildSystemPrompt(toolNames) {
|
|
469
|
-
const validToolNames = toolNames.filter((name) => this.
|
|
530
|
+
const validToolNames = toolNames.filter((name) => this._toolRegistry.has(name));
|
|
531
|
+
const toolSnippets = {};
|
|
532
|
+
const promptGuidelines = [];
|
|
533
|
+
for (const name of validToolNames) {
|
|
534
|
+
const snippet = this._toolPromptSnippets.get(name);
|
|
535
|
+
if (snippet) {
|
|
536
|
+
toolSnippets[name] = snippet;
|
|
537
|
+
}
|
|
538
|
+
const toolGuidelines = this._toolPromptGuidelines.get(name);
|
|
539
|
+
if (toolGuidelines) {
|
|
540
|
+
promptGuidelines.push(...toolGuidelines);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
470
543
|
const loaderSystemPrompt = this._resourceLoader.getSystemPrompt();
|
|
471
544
|
const loaderAppendSystemPrompt = this._resourceLoader.getAppendSystemPrompt();
|
|
472
545
|
const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : undefined;
|
|
@@ -479,6 +552,8 @@ export class AgentSession {
|
|
|
479
552
|
customPrompt: loaderSystemPrompt,
|
|
480
553
|
appendSystemPrompt,
|
|
481
554
|
selectedTools: validToolNames,
|
|
555
|
+
toolSnippets,
|
|
556
|
+
promptGuidelines,
|
|
482
557
|
});
|
|
483
558
|
}
|
|
484
559
|
// =========================================================================
|
|
@@ -1528,6 +1603,7 @@ export class AgentSession {
|
|
|
1528
1603
|
getActiveTools: () => this.getActiveToolNames(),
|
|
1529
1604
|
getAllTools: () => this.getAllTools(),
|
|
1530
1605
|
setActiveTools: (toolNames) => this.setActiveToolsByName(toolNames),
|
|
1606
|
+
refreshTools: () => this._refreshToolRegistry(),
|
|
1531
1607
|
getCommands,
|
|
1532
1608
|
setModel: async (model) => {
|
|
1533
1609
|
const key = await this.modelRegistry.getApiKey(model);
|
|
@@ -1562,6 +1638,57 @@ export class AgentSession {
|
|
|
1562
1638
|
getSystemPrompt: () => this.systemPrompt,
|
|
1563
1639
|
});
|
|
1564
1640
|
}
|
|
1641
|
+
_refreshToolRegistry(options) {
|
|
1642
|
+
const previousRegistryNames = new Set(this._toolRegistry.keys());
|
|
1643
|
+
const previousActiveToolNames = this.getActiveToolNames();
|
|
1644
|
+
const registeredTools = this._extensionRunner?.getAllRegisteredTools() ?? [];
|
|
1645
|
+
const allCustomTools = [
|
|
1646
|
+
...registeredTools,
|
|
1647
|
+
...this._customTools.map((def) => ({ definition: def, extensionPath: "<sdk>" })),
|
|
1648
|
+
];
|
|
1649
|
+
this._toolPromptSnippets = new Map(allCustomTools
|
|
1650
|
+
.map((registeredTool) => {
|
|
1651
|
+
const snippet = this._normalizePromptSnippet(registeredTool.definition.promptSnippet ?? registeredTool.definition.description);
|
|
1652
|
+
return snippet ? [registeredTool.definition.name, snippet] : undefined;
|
|
1653
|
+
})
|
|
1654
|
+
.filter((entry) => entry !== undefined));
|
|
1655
|
+
this._toolPromptGuidelines = new Map(allCustomTools
|
|
1656
|
+
.map((registeredTool) => {
|
|
1657
|
+
const guidelines = this._normalizePromptGuidelines(registeredTool.definition.promptGuidelines);
|
|
1658
|
+
return guidelines.length > 0 ? [registeredTool.definition.name, guidelines] : undefined;
|
|
1659
|
+
})
|
|
1660
|
+
.filter((entry) => entry !== undefined));
|
|
1661
|
+
const wrappedExtensionTools = this._extensionRunner
|
|
1662
|
+
? wrapRegisteredTools(allCustomTools, this._extensionRunner)
|
|
1663
|
+
: [];
|
|
1664
|
+
const toolRegistry = new Map(this._baseToolRegistry);
|
|
1665
|
+
for (const tool of wrappedExtensionTools) {
|
|
1666
|
+
toolRegistry.set(tool.name, tool);
|
|
1667
|
+
}
|
|
1668
|
+
if (this._extensionRunner) {
|
|
1669
|
+
const wrappedAllTools = wrapToolsWithExtensions(Array.from(toolRegistry.values()), this._extensionRunner);
|
|
1670
|
+
this._toolRegistry = new Map(wrappedAllTools.map((tool) => [tool.name, tool]));
|
|
1671
|
+
}
|
|
1672
|
+
else {
|
|
1673
|
+
this._toolRegistry = toolRegistry;
|
|
1674
|
+
}
|
|
1675
|
+
const nextActiveToolNames = options?.activeToolNames
|
|
1676
|
+
? [...options.activeToolNames]
|
|
1677
|
+
: [...previousActiveToolNames];
|
|
1678
|
+
if (options?.includeAllExtensionTools) {
|
|
1679
|
+
for (const tool of wrappedExtensionTools) {
|
|
1680
|
+
nextActiveToolNames.push(tool.name);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
else if (!options?.activeToolNames) {
|
|
1684
|
+
for (const toolName of this._toolRegistry.keys()) {
|
|
1685
|
+
if (!previousRegistryNames.has(toolName)) {
|
|
1686
|
+
nextActiveToolNames.push(toolName);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
this.setActiveToolsByName([...new Set(nextActiveToolNames)]);
|
|
1691
|
+
}
|
|
1565
1692
|
_buildRuntime(options) {
|
|
1566
1693
|
const autoResizeImages = this.settingsManager.getImageAutoResize();
|
|
1567
1694
|
const shellCommandPrefix = this.settingsManager.getShellCommandPrefix();
|
|
@@ -1591,47 +1718,14 @@ export class AgentSession {
|
|
|
1591
1718
|
this._bindExtensionCore(this._extensionRunner);
|
|
1592
1719
|
this._applyExtensionBindings(this._extensionRunner);
|
|
1593
1720
|
}
|
|
1594
|
-
const registeredTools = this._extensionRunner?.getAllRegisteredTools() ?? [];
|
|
1595
|
-
const allCustomTools = [
|
|
1596
|
-
...registeredTools,
|
|
1597
|
-
...this._customTools.map((def) => ({ definition: def, extensionPath: "<sdk>" })),
|
|
1598
|
-
];
|
|
1599
|
-
const wrappedExtensionTools = this._extensionRunner
|
|
1600
|
-
? wrapRegisteredTools(allCustomTools, this._extensionRunner)
|
|
1601
|
-
: [];
|
|
1602
|
-
const toolRegistry = new Map(this._baseToolRegistry);
|
|
1603
|
-
for (const tool of wrappedExtensionTools) {
|
|
1604
|
-
toolRegistry.set(tool.name, tool);
|
|
1605
|
-
}
|
|
1606
1721
|
const defaultActiveToolNames = this._baseToolsOverride
|
|
1607
1722
|
? Object.keys(this._baseToolsOverride)
|
|
1608
1723
|
: ["read", "bash", "edit", "write"];
|
|
1609
1724
|
const baseActiveToolNames = options.activeToolNames ?? defaultActiveToolNames;
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
const extensionToolNames = new Set(wrappedExtensionTools.map((tool) => tool.name));
|
|
1617
|
-
const activeBaseTools = Array.from(activeToolNameSet)
|
|
1618
|
-
.filter((name) => this._baseToolRegistry.has(name) && !extensionToolNames.has(name))
|
|
1619
|
-
.map((name) => this._baseToolRegistry.get(name));
|
|
1620
|
-
const activeExtensionTools = wrappedExtensionTools.filter((tool) => activeToolNameSet.has(tool.name));
|
|
1621
|
-
const activeToolsArray = [...activeBaseTools, ...activeExtensionTools];
|
|
1622
|
-
if (this._extensionRunner) {
|
|
1623
|
-
const wrappedActiveTools = wrapToolsWithExtensions(activeToolsArray, this._extensionRunner);
|
|
1624
|
-
this.agent.setTools(wrappedActiveTools);
|
|
1625
|
-
const wrappedAllTools = wrapToolsWithExtensions(Array.from(toolRegistry.values()), this._extensionRunner);
|
|
1626
|
-
this._toolRegistry = new Map(wrappedAllTools.map((tool) => [tool.name, tool]));
|
|
1627
|
-
}
|
|
1628
|
-
else {
|
|
1629
|
-
this.agent.setTools(activeToolsArray);
|
|
1630
|
-
this._toolRegistry = toolRegistry;
|
|
1631
|
-
}
|
|
1632
|
-
const systemPromptToolNames = Array.from(activeToolNameSet).filter((name) => this._baseToolRegistry.has(name));
|
|
1633
|
-
this._baseSystemPrompt = this._rebuildSystemPrompt(systemPromptToolNames);
|
|
1634
|
-
this.agent.setSystemPrompt(this._baseSystemPrompt);
|
|
1725
|
+
this._refreshToolRegistry({
|
|
1726
|
+
activeToolNames: baseActiveToolNames,
|
|
1727
|
+
includeAllExtensionTools: options.includeAllExtensionTools,
|
|
1728
|
+
});
|
|
1635
1729
|
}
|
|
1636
1730
|
async reload() {
|
|
1637
1731
|
const previousFlagValues = this._extensionRunner?.getFlagValues();
|
|
@@ -1677,15 +1771,18 @@ export class AgentSession {
|
|
|
1677
1771
|
*/
|
|
1678
1772
|
async _handleRetryableError(message) {
|
|
1679
1773
|
const settings = this.settingsManager.getRetrySettings();
|
|
1680
|
-
if (!settings.enabled)
|
|
1774
|
+
if (!settings.enabled) {
|
|
1775
|
+
this._resolveRetry();
|
|
1681
1776
|
return false;
|
|
1682
|
-
|
|
1683
|
-
//
|
|
1684
|
-
|
|
1777
|
+
}
|
|
1778
|
+
// Retry promise is created synchronously in _handleAgentEvent for agent_end.
|
|
1779
|
+
// Keep a defensive fallback here in case a future refactor bypasses that path.
|
|
1780
|
+
if (!this._retryPromise) {
|
|
1685
1781
|
this._retryPromise = new Promise((resolve) => {
|
|
1686
1782
|
this._retryResolve = resolve;
|
|
1687
1783
|
});
|
|
1688
1784
|
}
|
|
1785
|
+
this._retryAttempt++;
|
|
1689
1786
|
if (this._retryAttempt > settings.maxRetries) {
|
|
1690
1787
|
// Max retries exceeded, emit final failure and reset
|
|
1691
1788
|
this._emit({
|