@oh-my-pi/pi-coding-agent 15.3.1 → 15.4.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 +119 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +1 -1
- package/dist/types/cli/file-processor.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +45 -3
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +2 -0
- package/dist/types/edit/file-read-cache.d.ts +15 -4
- package/dist/types/edit/index.d.ts +3 -8
- package/dist/types/edit/renderer.d.ts +1 -2
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +1 -0
- package/dist/types/eval/js/shared/local-module-loader.d.ts +16 -0
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +4 -0
- package/dist/types/eval/js/shared/runtime.d.ts +14 -8
- package/dist/types/eval/py/executor.d.ts +1 -2
- package/dist/types/eval/py/kernel.d.ts +6 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -5
- package/dist/types/eval/session-id.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +1 -3
- package/dist/types/hashline/anchors.d.ts +15 -9
- package/dist/types/hashline/constants.d.ts +0 -2
- package/dist/types/hashline/diff.d.ts +1 -2
- package/dist/types/hashline/executor.d.ts +52 -0
- package/dist/types/hashline/hash.d.ts +44 -93
- package/dist/types/hashline/index.d.ts +2 -1
- package/dist/types/hashline/input.d.ts +2 -9
- package/dist/types/hashline/recovery.d.ts +3 -9
- package/dist/types/hashline/tokenizer.d.ts +91 -0
- package/dist/types/hashline/types.d.ts +5 -7
- package/dist/types/modes/components/extensions/types.d.ts +0 -4
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +11 -15
- package/dist/types/session/agent-storage.d.ts +11 -10
- package/dist/types/slash-commands/acp-builtins.d.ts +3 -3
- package/dist/types/slash-commands/types.d.ts +0 -5
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/tool-discovery/tool-index.d.ts +0 -50
- package/dist/types/tools/index.d.ts +2 -8
- package/dist/types/tools/match-line-format.d.ts +4 -4
- package/dist/types/tools/output-schema-validator.d.ts +64 -0
- package/dist/types/tools/review.d.ts +13 -0
- package/dist/types/tools/search-tool-bm25.d.ts +1 -1
- package/dist/types/tools/search.d.ts +4 -3
- package/dist/types/utils/edit-mode.d.ts +1 -1
- package/dist/types/web/kagi.d.ts +4 -2
- package/dist/types/web/parallel.d.ts +4 -3
- package/dist/types/web/scrapers/types.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +12 -4
- package/dist/types/web/search/provider.d.ts +2 -1
- package/dist/types/web/search/providers/anthropic.d.ts +9 -4
- package/dist/types/web/search/providers/base.d.ts +34 -2
- package/dist/types/web/search/providers/brave.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +13 -9
- package/dist/types/web/search/providers/exa.d.ts +10 -1
- package/dist/types/web/search/providers/gemini.d.ts +20 -23
- package/dist/types/web/search/providers/jina.d.ts +2 -1
- package/dist/types/web/search/providers/kagi.d.ts +4 -1
- package/dist/types/web/search/providers/kimi.d.ts +10 -1
- package/dist/types/web/search/providers/parallel.d.ts +3 -2
- package/dist/types/web/search/providers/perplexity.d.ts +5 -2
- package/dist/types/web/search/providers/searxng.d.ts +2 -1
- package/dist/types/web/search/providers/synthetic.d.ts +5 -8
- package/dist/types/web/search/providers/tavily.d.ts +11 -4
- package/dist/types/web/search/providers/utils.d.ts +8 -6
- package/dist/types/web/search/providers/zai.d.ts +12 -3
- package/package.json +7 -7
- package/src/cli/auth-gateway-cli.ts +71 -2
- package/src/cli/file-processor.ts +12 -2
- package/src/cli.ts +0 -8
- package/src/commands/auth-gateway.ts +2 -0
- package/src/commands/commit.ts +8 -8
- package/src/config/prompt-templates.ts +6 -6
- package/src/config/settings-schema.ts +47 -3
- package/src/config/settings.ts +5 -5
- package/src/debug/raw-sse.ts +68 -3
- package/src/edit/file-read-cache.ts +68 -25
- package/src/edit/index.ts +6 -37
- package/src/edit/renderer.ts +9 -47
- package/src/edit/streaming.ts +43 -56
- package/src/eval/__tests__/shared-executors.test.ts +520 -0
- package/src/eval/js/context-manager.ts +64 -53
- package/src/eval/js/shared/local-module-loader.ts +265 -0
- package/src/eval/js/shared/prelude.txt +4 -0
- package/src/eval/js/shared/rewrite-imports.ts +85 -0
- package/src/eval/js/shared/runtime.ts +129 -86
- package/src/eval/js/worker-core.ts +23 -38
- package/src/eval/py/executor.ts +155 -84
- package/src/eval/py/kernel.ts +10 -1
- package/src/eval/py/prelude.py +22 -24
- package/src/eval/py/runner.py +203 -85
- package/src/eval/py/tool-bridge.ts +17 -10
- package/src/eval/session-id.ts +8 -0
- package/src/exec/bash-executor.ts +27 -16
- package/src/extensibility/extensions/runner.ts +0 -1
- package/src/extensibility/extensions/types.ts +1 -3
- package/src/extensibility/plugins/marketplace/manager.ts +20 -1
- package/src/hashline/anchors.ts +56 -65
- package/src/hashline/apply.ts +29 -31
- package/src/hashline/constants.ts +0 -3
- package/src/hashline/diff-preview.ts +4 -5
- package/src/hashline/diff.ts +30 -4
- package/src/hashline/execute.ts +91 -26
- package/src/hashline/executor.ts +239 -0
- package/src/hashline/grammar.lark +12 -10
- package/src/hashline/hash.ts +69 -114
- package/src/hashline/index.ts +2 -1
- package/src/hashline/input.ts +48 -41
- package/src/hashline/prefixes.ts +21 -11
- package/src/hashline/recovery.ts +63 -71
- package/src/hashline/stream.ts +2 -2
- package/src/hashline/tokenizer.ts +467 -0
- package/src/hashline/types.ts +6 -8
- package/src/internal-urls/docs-index.generated.ts +9 -8
- package/src/lsp/config.ts +87 -22
- package/src/modes/components/extensions/types.ts +0 -5
- package/src/modes/components/session-observer-overlay.ts +11 -2
- package/src/modes/components/tree-selector.ts +10 -2
- package/src/modes/controllers/command-controller.ts +1 -3
- package/src/modes/controllers/extension-ui-controller.ts +10 -11
- package/src/modes/controllers/selector-controller.ts +5 -5
- package/src/modes/types.ts +4 -1
- package/src/modes/utils/ui-helpers.ts +4 -0
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/eval.md +1 -1
- package/src/prompts/tools/hashline.md +73 -94
- package/src/prompts/tools/read.md +4 -4
- package/src/prompts/tools/search.md +3 -3
- package/src/sdk.ts +21 -24
- package/src/session/agent-session.ts +59 -66
- package/src/session/agent-storage.ts +13 -14
- package/src/slash-commands/acp-builtins.ts +3 -3
- package/src/slash-commands/types.ts +0 -6
- package/src/task/executor.ts +55 -57
- package/src/task/index.ts +8 -4
- package/src/task/render.ts +53 -1
- package/src/task/types.ts +8 -0
- package/src/tool-discovery/tool-index.ts +0 -134
- package/src/tools/ast-edit.ts +36 -13
- package/src/tools/ast-grep.ts +45 -4
- package/src/tools/browser/tab-worker.ts +3 -2
- package/src/tools/eval.ts +2 -1
- package/src/tools/fetch.ts +23 -14
- package/src/tools/index.ts +2 -8
- package/src/tools/irc.ts +59 -5
- package/src/tools/jtd-to-json-schema.ts +5 -1
- package/src/tools/match-line-format.ts +5 -7
- package/src/tools/output-schema-validator.ts +132 -0
- package/src/tools/read.ts +142 -63
- package/src/tools/review.ts +23 -0
- package/src/tools/search-tool-bm25.ts +3 -30
- package/src/tools/search.ts +48 -16
- package/src/tools/write.ts +3 -3
- package/src/tools/yield.ts +32 -41
- package/src/utils/edit-mode.ts +1 -2
- package/src/utils/file-mentions.ts +2 -2
- package/src/web/kagi.ts +15 -6
- package/src/web/parallel.ts +9 -6
- package/src/web/scrapers/types.ts +7 -1
- package/src/web/scrapers/youtube.ts +13 -7
- package/src/web/search/index.ts +37 -11
- package/src/web/search/provider.ts +5 -3
- package/src/web/search/providers/anthropic.ts +30 -21
- package/src/web/search/providers/base.ts +35 -2
- package/src/web/search/providers/brave.ts +4 -4
- package/src/web/search/providers/codex.ts +118 -89
- package/src/web/search/providers/exa.ts +3 -2
- package/src/web/search/providers/gemini.ts +58 -155
- package/src/web/search/providers/jina.ts +4 -4
- package/src/web/search/providers/kagi.ts +17 -11
- package/src/web/search/providers/kimi.ts +29 -13
- package/src/web/search/providers/parallel.ts +171 -23
- package/src/web/search/providers/perplexity.ts +38 -37
- package/src/web/search/providers/searxng.ts +3 -1
- package/src/web/search/providers/synthetic.ts +16 -19
- package/src/web/search/providers/tavily.ts +23 -18
- package/src/web/search/providers/utils.ts +11 -17
- package/src/web/search/providers/zai.ts +16 -8
- package/dist/types/hashline/parser.d.ts +0 -7
- package/dist/types/mcp/discoverable-tool-metadata.d.ts +0 -7
- package/dist/types/tools/vim.d.ts +0 -58
- package/dist/types/vim/buffer.d.ts +0 -41
- package/dist/types/vim/commands.d.ts +0 -6
- package/dist/types/vim/engine.d.ts +0 -47
- package/dist/types/vim/parser.d.ts +0 -3
- package/dist/types/vim/render.d.ts +0 -25
- package/dist/types/vim/types.d.ts +0 -182
- package/src/hashline/parser.ts +0 -212
- package/src/mcp/discoverable-tool-metadata.ts +0 -24
- package/src/prompts/tools/vim.md +0 -98
- package/src/tools/vim.ts +0 -949
- package/src/vim/buffer.ts +0 -309
- package/src/vim/commands.ts +0 -382
- package/src/vim/engine.ts +0 -2409
- package/src/vim/parser.ts +0 -134
- package/src/vim/render.ts +0 -252
- package/src/vim/types.ts +0 -197
|
@@ -108,6 +108,7 @@ import {
|
|
|
108
108
|
executePython as executePythonCommand,
|
|
109
109
|
type PythonResult,
|
|
110
110
|
} from "../eval/py/executor";
|
|
111
|
+
import { defaultEvalSessionId } from "../eval/session-id";
|
|
111
112
|
import { type BashResult, executeBash as executeBashCommand } from "../exec/bash-executor";
|
|
112
113
|
import { exportSessionToHtml } from "../export/html";
|
|
113
114
|
import type { TtsrManager, TtsrMatchContext } from "../export/ttsr";
|
|
@@ -141,14 +142,6 @@ import { GoalRuntime } from "../goals/runtime";
|
|
|
141
142
|
import type { Goal, GoalModeState } from "../goals/state";
|
|
142
143
|
import type { HindsightSessionState } from "../hindsight/state";
|
|
143
144
|
import { type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
144
|
-
import {
|
|
145
|
-
buildDiscoverableMCPSearchIndex,
|
|
146
|
-
collectDiscoverableMCPTools,
|
|
147
|
-
type DiscoverableMCPSearchIndex,
|
|
148
|
-
type DiscoverableMCPTool,
|
|
149
|
-
isMCPToolName,
|
|
150
|
-
selectDiscoverableMCPToolNamesByServer,
|
|
151
|
-
} from "../mcp/discoverable-tool-metadata";
|
|
152
145
|
import { resolveMemoryBackend } from "../memory-backend";
|
|
153
146
|
import { getCurrentThemeName, theme } from "../modes/theme/theme";
|
|
154
147
|
import type { PlanModeState } from "../plan-mode/state";
|
|
@@ -171,6 +164,9 @@ import {
|
|
|
171
164
|
collectDiscoverableTools,
|
|
172
165
|
type DiscoverableTool,
|
|
173
166
|
type DiscoverableToolSearchIndex,
|
|
167
|
+
filterBySource,
|
|
168
|
+
isMCPToolName,
|
|
169
|
+
selectDiscoverableToolNamesByServer,
|
|
174
170
|
} from "../tool-discovery/tool-index";
|
|
175
171
|
import { assertEditableFile } from "../tools/auto-generated-guard";
|
|
176
172
|
import type { CheckpointState } from "../tools/checkpoint";
|
|
@@ -312,6 +308,8 @@ export interface AgentSessionConfig {
|
|
|
312
308
|
ttsrManager?: TtsrManager;
|
|
313
309
|
/** Secret obfuscator for deobfuscating streaming edit content */
|
|
314
310
|
obfuscator?: SecretObfuscator;
|
|
311
|
+
/** Inherited eval executor session id from a parent agent. */
|
|
312
|
+
parentEvalSessionId?: string;
|
|
315
313
|
/** Logical owner for retained Python kernels created by this session. */
|
|
316
314
|
evalKernelOwnerId?: string;
|
|
317
315
|
/**
|
|
@@ -808,6 +806,7 @@ export class AgentSession {
|
|
|
808
806
|
// Python execution state
|
|
809
807
|
#evalAbortControllers = new Set<AbortController>();
|
|
810
808
|
#evalKernelOwnerId: string;
|
|
809
|
+
#parentEvalSessionId: string | undefined;
|
|
811
810
|
/**
|
|
812
811
|
* AsyncJobManager owned by this session (top-level only). Subagents leave
|
|
813
812
|
* this undefined and **MUST NOT** dispose the global instance on teardown.
|
|
@@ -865,8 +864,7 @@ export class AgentSession {
|
|
|
865
864
|
*/
|
|
866
865
|
#lastAppliedToolSignature: string | undefined;
|
|
867
866
|
#mcpDiscoveryEnabled = false;
|
|
868
|
-
#discoverableMCPTools = new Map<string,
|
|
869
|
-
#discoverableMCPSearchIndex: DiscoverableMCPSearchIndex | null = null;
|
|
867
|
+
#discoverableMCPTools = new Map<string, DiscoverableTool>();
|
|
870
868
|
#selectedMCPToolNames = new Set<string>();
|
|
871
869
|
// Generic tool discovery (covers built-in + MCP + extension when tools.discoveryMode === "all")
|
|
872
870
|
#discoverableToolSearchIndex: DiscoverableToolSearchIndex | null = null;
|
|
@@ -997,6 +995,7 @@ export class AgentSession {
|
|
|
997
995
|
this.settings = config.settings;
|
|
998
996
|
// Power assertions are taken per turn (see #beginInFlight); nothing acquired here.
|
|
999
997
|
this.#evalKernelOwnerId = config.evalKernelOwnerId ?? `agent-session:${Snowflake.next()}`;
|
|
998
|
+
this.#parentEvalSessionId = config.parentEvalSessionId;
|
|
1000
999
|
this.#ownedAsyncJobManager = config.ownedAsyncJobManager;
|
|
1001
1000
|
this.#scopedModels = config.scopedModels ?? [];
|
|
1002
1001
|
this.#thinkingLevel = config.thinkingLevel;
|
|
@@ -2876,11 +2875,12 @@ export class AgentSession {
|
|
|
2876
2875
|
return this.#retryAttempt;
|
|
2877
2876
|
}
|
|
2878
2877
|
|
|
2879
|
-
#collectDiscoverableMCPToolsFromRegistry(): Map<string,
|
|
2880
|
-
|
|
2878
|
+
#collectDiscoverableMCPToolsFromRegistry(): Map<string, DiscoverableTool> {
|
|
2879
|
+
const mcpTools = filterBySource(collectDiscoverableTools(this.#toolRegistry.values()), "mcp");
|
|
2880
|
+
return new Map(mcpTools.map(tool => [tool.name, tool] as const));
|
|
2881
2881
|
}
|
|
2882
2882
|
|
|
2883
|
-
#setDiscoverableMCPTools(discoverableMCPTools: Map<string,
|
|
2883
|
+
#setDiscoverableMCPTools(discoverableMCPTools: Map<string, DiscoverableTool>): void {
|
|
2884
2884
|
this.#discoverableMCPTools = discoverableMCPTools;
|
|
2885
2885
|
this.#invalidateDiscoveryCaches();
|
|
2886
2886
|
}
|
|
@@ -2889,7 +2889,6 @@ export class AgentSession {
|
|
|
2889
2889
|
* affect which tools should be discoverable: registry mutations (refreshMCPTools,
|
|
2890
2890
|
* refreshRpcHostTools) or active-tool mutations (#applyActiveToolsByName). */
|
|
2891
2891
|
#invalidateDiscoveryCaches(): void {
|
|
2892
|
-
this.#discoverableMCPSearchIndex = null;
|
|
2893
2892
|
this.#discoverableToolSearchIndex = null;
|
|
2894
2893
|
}
|
|
2895
2894
|
|
|
@@ -2900,7 +2899,7 @@ export class AgentSession {
|
|
|
2900
2899
|
#getConfiguredDefaultSelectedMCPToolNames(): string[] {
|
|
2901
2900
|
return this.#filterSelectableMCPToolNames([
|
|
2902
2901
|
...this.#defaultSelectedMCPToolNames,
|
|
2903
|
-
...
|
|
2902
|
+
...selectDiscoverableToolNamesByServer(
|
|
2904
2903
|
this.#discoverableMCPTools.values(),
|
|
2905
2904
|
this.#defaultSelectedMCPServerNames,
|
|
2906
2905
|
),
|
|
@@ -2993,28 +2992,6 @@ export class AgentSession {
|
|
|
2993
2992
|
return this.#mcpDiscoveryEnabled;
|
|
2994
2993
|
}
|
|
2995
2994
|
|
|
2996
|
-
/** @deprecated Use {@link getDiscoverableTools} with `{ source: "mcp" }` instead.
|
|
2997
|
-
* Preserves the legacy `description`-bearing MCP shape for back-compat callers. */
|
|
2998
|
-
getDiscoverableMCPTools(): DiscoverableMCPTool[] {
|
|
2999
|
-
return Array.from(this.#discoverableMCPTools.values()).map(t => ({
|
|
3000
|
-
name: t.name,
|
|
3001
|
-
label: t.label,
|
|
3002
|
-
description: t.description,
|
|
3003
|
-
serverName: t.serverName,
|
|
3004
|
-
mcpToolName: t.mcpToolName,
|
|
3005
|
-
schemaKeys: t.schemaKeys,
|
|
3006
|
-
}));
|
|
3007
|
-
}
|
|
3008
|
-
|
|
3009
|
-
/** @deprecated Use {@link getDiscoverableToolSearchIndex} instead.
|
|
3010
|
-
* Returns the legacy MCP search index whose documents expose `tool.description`. */
|
|
3011
|
-
getDiscoverableMCPSearchIndex(): DiscoverableMCPSearchIndex {
|
|
3012
|
-
if (!this.#discoverableMCPSearchIndex) {
|
|
3013
|
-
this.#discoverableMCPSearchIndex = buildDiscoverableMCPSearchIndex(this.#discoverableMCPTools.values());
|
|
3014
|
-
}
|
|
3015
|
-
return this.#discoverableMCPSearchIndex;
|
|
3016
|
-
}
|
|
3017
|
-
|
|
3018
2995
|
getSelectedMCPToolNames(): string[] {
|
|
3019
2996
|
if (!this.#mcpDiscoveryEnabled) {
|
|
3020
2997
|
return this.getActiveToolNames().filter(name => isMCPToolName(name) && this.#toolRegistry.has(name));
|
|
@@ -3062,17 +3039,7 @@ export class AgentSession {
|
|
|
3062
3039
|
// For "mcp-only" mode we only return MCP tools.
|
|
3063
3040
|
const mode = this.#resolveEffectiveDiscoveryMode();
|
|
3064
3041
|
const activeNames = new Set(this.getActiveToolNames());
|
|
3065
|
-
const mcpTools
|
|
3066
|
-
.filter(t => !activeNames.has(t.name))
|
|
3067
|
-
.map(t => ({
|
|
3068
|
-
name: t.name,
|
|
3069
|
-
label: t.label,
|
|
3070
|
-
summary: t.description,
|
|
3071
|
-
source: "mcp" as const,
|
|
3072
|
-
serverName: t.serverName,
|
|
3073
|
-
mcpToolName: t.mcpToolName,
|
|
3074
|
-
schemaKeys: t.schemaKeys,
|
|
3075
|
-
}));
|
|
3042
|
+
const mcpTools = Array.from(this.#discoverableMCPTools.values()).filter(t => !activeNames.has(t.name));
|
|
3076
3043
|
const builtinTools: DiscoverableTool[] = mode === "all" ? this.#collectDiscoverableBuiltinTools() : [];
|
|
3077
3044
|
const allTools = [...builtinTools, ...mcpTools];
|
|
3078
3045
|
return filter?.source ? allTools.filter(t => t.source === filter.source) : allTools;
|
|
@@ -3693,6 +3660,13 @@ export class AgentSession {
|
|
|
3693
3660
|
get sessionId(): string {
|
|
3694
3661
|
return this.#providerSessionId ?? this.sessionManager.getSessionId();
|
|
3695
3662
|
}
|
|
3663
|
+
getEvalSessionId(): string | null {
|
|
3664
|
+
if (this.#parentEvalSessionId !== undefined) return this.#parentEvalSessionId;
|
|
3665
|
+
return defaultEvalSessionId({
|
|
3666
|
+
cwd: this.sessionManager.getCwd(),
|
|
3667
|
+
getSessionFile: () => this.sessionManager.getSessionFile() ?? null,
|
|
3668
|
+
});
|
|
3669
|
+
}
|
|
3696
3670
|
|
|
3697
3671
|
/** Current session display name, if set */
|
|
3698
3672
|
get sessionName(): string | undefined {
|
|
@@ -4225,7 +4199,6 @@ export class AgentSession {
|
|
|
4225
4199
|
void this.dispose();
|
|
4226
4200
|
process.exit(0);
|
|
4227
4201
|
},
|
|
4228
|
-
hasQueuedMessages: () => this.queuedMessageCount > 0,
|
|
4229
4202
|
getContextUsage: () => this.getContextUsage(),
|
|
4230
4203
|
waitForIdle: () => this.waitForIdle(),
|
|
4231
4204
|
newSession: async options => {
|
|
@@ -4558,11 +4531,11 @@ export class AgentSession {
|
|
|
4558
4531
|
}
|
|
4559
4532
|
|
|
4560
4533
|
/**
|
|
4561
|
-
* Send a user message to the agent.
|
|
4562
|
-
* When
|
|
4534
|
+
* Send a user message to the agent.
|
|
4535
|
+
* When deliverAs is set, queue the message instead of starting a new turn.
|
|
4563
4536
|
*
|
|
4564
4537
|
* @param content User message content (string or content array)
|
|
4565
|
-
* @param options.deliverAs Delivery mode
|
|
4538
|
+
* @param options.deliverAs Delivery mode: "steer" or "followUp"
|
|
4566
4539
|
*/
|
|
4567
4540
|
async sendUserMessage(
|
|
4568
4541
|
content: string | (TextContent | ImageContent)[],
|
|
@@ -4588,10 +4561,18 @@ export class AgentSession {
|
|
|
4588
4561
|
if (images.length === 0) images = undefined;
|
|
4589
4562
|
}
|
|
4590
4563
|
|
|
4564
|
+
if (options?.deliverAs === "followUp") {
|
|
4565
|
+
await this.#queueFollowUp(text, images);
|
|
4566
|
+
return;
|
|
4567
|
+
}
|
|
4568
|
+
if (options?.deliverAs === "steer") {
|
|
4569
|
+
await this.#queueSteer(text, images);
|
|
4570
|
+
return;
|
|
4571
|
+
}
|
|
4572
|
+
|
|
4591
4573
|
// Use prompt() with expandPromptTemplates: false to skip command handling and template expansion
|
|
4592
4574
|
await this.prompt(text, {
|
|
4593
4575
|
expandPromptTemplates: false,
|
|
4594
|
-
streamingBehavior: options?.deliverAs,
|
|
4595
4576
|
images,
|
|
4596
4577
|
});
|
|
4597
4578
|
}
|
|
@@ -6781,10 +6762,11 @@ export class AgentSession {
|
|
|
6781
6762
|
#isTransientTransportErrorMessage(errorMessage: string): boolean {
|
|
6782
6763
|
// Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504,
|
|
6783
6764
|
// service unavailable, provider-suggested retry, network/connection/socket errors, fetch failed,
|
|
6784
|
-
// terminated, retry delay exceeded
|
|
6765
|
+
// terminated, retry delay exceeded, Bun HTTP/2 stream resets (RST_STREAM / REFUSED_STREAM /
|
|
6766
|
+
// ENHANCE_YOUR_CALM, surfaced verbatim from src/http/h2_client/dispatch.zig)
|
|
6785
6767
|
return (
|
|
6786
6768
|
isUnexpectedSocketCloseMessage(errorMessage) ||
|
|
6787
|
-
/overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|retry your request|network.?error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|timed? out|timeout|terminated|retry delay|stream stall|no error details in response/i.test(
|
|
6769
|
+
/overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|retry your request|network.?error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|timed? out|timeout|terminated|retry delay|stream stall|no error details in response|HTTP2(?:StreamReset|RefusedStream|EnhanceYourCalm)/i.test(
|
|
6788
6770
|
errorMessage,
|
|
6789
6771
|
)
|
|
6790
6772
|
);
|
|
@@ -7421,9 +7403,13 @@ export class AgentSession {
|
|
|
7421
7403
|
}
|
|
7422
7404
|
}
|
|
7423
7405
|
|
|
7424
|
-
// Use the same session ID as eval's Python backend for kernel sharing
|
|
7425
|
-
const
|
|
7426
|
-
|
|
7406
|
+
// Use the same session ID as eval's Python backend for kernel sharing.
|
|
7407
|
+
const sessionId =
|
|
7408
|
+
this.getEvalSessionId() ??
|
|
7409
|
+
defaultEvalSessionId({
|
|
7410
|
+
cwd,
|
|
7411
|
+
getSessionFile: () => this.sessionManager.getSessionFile() ?? null,
|
|
7412
|
+
});
|
|
7427
7413
|
const result = await executePythonCommand(code, {
|
|
7428
7414
|
cwd,
|
|
7429
7415
|
sessionId,
|
|
@@ -7562,11 +7548,11 @@ export class AgentSession {
|
|
|
7562
7548
|
* Generate an ephemeral reply to a background message (e.g. an IRC ping from
|
|
7563
7549
|
* another agent) using this session's current model + system prompt + history.
|
|
7564
7550
|
*
|
|
7565
|
-
* The
|
|
7566
|
-
*
|
|
7567
|
-
*
|
|
7568
|
-
*
|
|
7569
|
-
*
|
|
7551
|
+
* The incoming message is queued for injection into the recipient's persisted
|
|
7552
|
+
* history immediately so timeouts/abort still preserve delivery. The reply is
|
|
7553
|
+
* computed via a side-channel `streamSimple` call (analogous to `/btw`) so it
|
|
7554
|
+
* never blocks on the recipient's in-flight tool calls. When a reply is
|
|
7555
|
+
* generated, it is queued separately. Injection happens immediately when the
|
|
7570
7556
|
* session is idle, otherwise it is deferred until streaming ends.
|
|
7571
7557
|
*/
|
|
7572
7558
|
async respondAsBackground(args: {
|
|
@@ -7595,8 +7581,8 @@ export class AgentSession {
|
|
|
7595
7581
|
timestamp: incomingTimestamp,
|
|
7596
7582
|
});
|
|
7597
7583
|
|
|
7584
|
+
this.#queueBackgroundExchangeInjection([incomingRecord]);
|
|
7598
7585
|
if (!awaitReply) {
|
|
7599
|
-
this.#queueBackgroundExchangeInjection([incomingRecord]);
|
|
7600
7586
|
return { replyText: null };
|
|
7601
7587
|
}
|
|
7602
7588
|
|
|
@@ -7626,7 +7612,7 @@ export class AgentSession {
|
|
|
7626
7612
|
kind: "reply",
|
|
7627
7613
|
timestamp: replyRecord.timestamp,
|
|
7628
7614
|
});
|
|
7629
|
-
this.#queueBackgroundExchangeInjection([
|
|
7615
|
+
this.#queueBackgroundExchangeInjection([replyRecord]);
|
|
7630
7616
|
|
|
7631
7617
|
return { replyText };
|
|
7632
7618
|
}
|
|
@@ -7708,10 +7694,17 @@ export class AgentSession {
|
|
|
7708
7694
|
// removes the surface entirely.
|
|
7709
7695
|
tools: [],
|
|
7710
7696
|
};
|
|
7697
|
+
const cacheSessionId = this.sessionId;
|
|
7711
7698
|
const options = this.prepareSimpleStreamOptions(
|
|
7712
7699
|
{
|
|
7713
7700
|
apiKey,
|
|
7714
|
-
|
|
7701
|
+
// Side-channel turns must not share OpenAI/Codex append-only
|
|
7702
|
+
// conversation state with the main agent turn: IRC and /btw can run
|
|
7703
|
+
// while the main turn is mid-tool-call. Keep the prompt-cache key
|
|
7704
|
+
// stable, but give provider routing a unique request lineage.
|
|
7705
|
+
sessionId: `${cacheSessionId}:side:${Snowflake.next()}`,
|
|
7706
|
+
promptCacheKey: cacheSessionId,
|
|
7707
|
+
preferWebsockets: false,
|
|
7715
7708
|
reasoning: toReasoningEffort(this.thinkingLevel),
|
|
7716
7709
|
hideThinkingSummary: this.agent.hideThinkingSummary,
|
|
7717
7710
|
serviceTier: this.serviceTier,
|
|
@@ -240,11 +240,10 @@ FROM model_usage_legacy
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
243
|
+
* Reads legacy settings persisted in the agent.db `settings` table.
|
|
244
|
+
* The canonical settings store is `config.yml`; this accessor only
|
|
245
|
+
* exists so the config loader can migrate values from older installs.
|
|
246
246
|
* @returns Settings object, or null if no settings are stored
|
|
247
|
-
* @deprecated Use config.yml instead. This is only for migration.
|
|
248
247
|
*/
|
|
249
248
|
getSettings(): Settings | null {
|
|
250
249
|
const rows = (this.#listSettingsStmt.all() as SettingsRow[]) ?? [];
|
|
@@ -263,16 +262,6 @@ FROM model_usage_legacy
|
|
|
263
262
|
return settings as Settings;
|
|
264
263
|
}
|
|
265
264
|
|
|
266
|
-
/**
|
|
267
|
-
* @deprecated Settings are now stored in config.yml, not agent.db.
|
|
268
|
-
* This method is kept for backward compatibility but does nothing.
|
|
269
|
-
*/
|
|
270
|
-
saveSettings(settings: Settings): void {
|
|
271
|
-
logger.warn("AgentStorage.saveSettings is deprecated - settings are now stored in config.yml", {
|
|
272
|
-
keys: Object.keys(settings),
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
265
|
/**
|
|
277
266
|
* Records model usage, updating the last-used timestamp.
|
|
278
267
|
* @param modelKey - Model key in "provider/modelId" format
|
|
@@ -313,6 +302,16 @@ FROM model_usage_legacy
|
|
|
313
302
|
return this.#authStore.listAuthCredentials().length > 0;
|
|
314
303
|
}
|
|
315
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Returns the underlying {@link AuthCredentialStore} so callers that need
|
|
307
|
+
* the lower-level pi-ai abstraction (e.g. `findAnthropicAuth(store)`) can
|
|
308
|
+
* reuse this storage's open database connection instead of opening their
|
|
309
|
+
* own.
|
|
310
|
+
*/
|
|
311
|
+
get authStore(): AuthCredentialStore {
|
|
312
|
+
return this.#authStore;
|
|
313
|
+
}
|
|
314
|
+
|
|
316
315
|
/**
|
|
317
316
|
* Lists auth credentials, optionally filtered by provider.
|
|
318
317
|
* Only returns active (non-disabled) credentials by default.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AvailableCommand } from "@agentclientprotocol/sdk";
|
|
2
2
|
import { BUILTIN_SLASH_COMMANDS_INTERNAL, lookupBuiltinSlashCommand } from "./builtin-registry";
|
|
3
3
|
import { parseSlashCommand } from "./helpers/parse";
|
|
4
|
-
import type {
|
|
4
|
+
import type { AcpBuiltinSlashCommandResult, SlashCommandRuntime } from "./types";
|
|
5
5
|
|
|
6
|
-
export type {
|
|
6
|
+
export type { AcpBuiltinSlashCommandResult } from "./types";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Commands advertised to ACP clients. Entries without a text-mode `handle`
|
|
@@ -34,7 +34,7 @@ export const ACP_BUILTIN_SLASH_COMMANDS: AvailableCommand[] = BUILTIN_SLASH_COMM
|
|
|
34
34
|
*/
|
|
35
35
|
export async function executeAcpBuiltinSlashCommand(
|
|
36
36
|
text: string,
|
|
37
|
-
runtime:
|
|
37
|
+
runtime: SlashCommandRuntime,
|
|
38
38
|
): Promise<AcpBuiltinSlashCommandResult> {
|
|
39
39
|
const parsed = parseSlashCommand(text);
|
|
40
40
|
if (!parsed) return false;
|
|
@@ -116,11 +116,5 @@ export interface SlashCommandSpec extends BuiltinSlashCommand {
|
|
|
116
116
|
) => Promise<SlashCommandResult> | SlashCommandResult;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
/**
|
|
120
|
-
* @deprecated Use `SlashCommandRuntime` directly. Retained as an alias so
|
|
121
|
-
* downstream code that imported the ACP-specific name keeps compiling.
|
|
122
|
-
*/
|
|
123
|
-
export type AcpBuiltinCommandRuntime = SlashCommandRuntime;
|
|
124
|
-
|
|
125
119
|
/** Result returned by `executeAcpBuiltinSlashCommand`. */
|
|
126
120
|
export type AcpBuiltinSlashCommandResult = false | { consumed: true } | { prompt: string };
|
package/src/task/executor.ts
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import type { AgentEvent, AgentIdentity, AgentTelemetryConfig, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
9
9
|
import { recordHandoff, resolveTelemetry } from "@oh-my-pi/pi-agent-core";
|
|
10
|
-
import { type JsonSchemaValidationIssue, validateJsonSchemaValue } from "@oh-my-pi/pi-ai/utils/schema";
|
|
11
10
|
import { logger, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
12
11
|
import { ModelRegistry } from "../config/model-registry";
|
|
13
12
|
import { resolveModelOverrideWithAuthFallback } from "../config/model-resolver";
|
|
@@ -33,7 +32,10 @@ import { SKILL_PROMPT_MESSAGE_TYPE } from "../session/messages";
|
|
|
33
32
|
import { SessionManager } from "../session/session-manager";
|
|
34
33
|
import { truncateTail } from "../session/streaming-output";
|
|
35
34
|
import type { ContextFileEntry } from "../tools";
|
|
36
|
-
import {
|
|
35
|
+
import { normalizeSchema } from "../tools/jtd-to-json-schema";
|
|
36
|
+
import { buildOutputValidator, summarizeValidationFailure } from "../tools/output-schema-validator";
|
|
37
|
+
|
|
38
|
+
import { type ReportFindingDetails, toReviewFinding } from "../tools/review";
|
|
37
39
|
import { ToolAbortError } from "../tools/tool-errors";
|
|
38
40
|
import type { EventBus } from "../utils/event-bus";
|
|
39
41
|
import { buildNamedToolChoice } from "../utils/tool-choice";
|
|
@@ -49,6 +51,7 @@ import {
|
|
|
49
51
|
TASK_SUBAGENT_EVENT_CHANNEL,
|
|
50
52
|
TASK_SUBAGENT_LIFECYCLE_CHANNEL,
|
|
51
53
|
TASK_SUBAGENT_PROGRESS_CHANNEL,
|
|
54
|
+
type TaskToolDetails,
|
|
52
55
|
} from "./types";
|
|
53
56
|
|
|
54
57
|
const MCP_CALL_TIMEOUT_MS = 60_000;
|
|
@@ -182,6 +185,8 @@ export interface ExecutorOptions {
|
|
|
182
185
|
*/
|
|
183
186
|
parentArtifactManager?: ArtifactManager;
|
|
184
187
|
parentHindsightSessionState?: HindsightSessionState;
|
|
188
|
+
/** Parent agent's eval executor session id. Subagents reuse it so eval state is shared. */
|
|
189
|
+
parentEvalSessionId?: string;
|
|
185
190
|
/**
|
|
186
191
|
* Parent agent's OpenTelemetry configuration. When defined, the subagent's
|
|
187
192
|
* loop is started with the same tracer/hooks but its own agent identity
|
|
@@ -207,51 +212,6 @@ function parseStringifiedJson(value: unknown): unknown {
|
|
|
207
212
|
}
|
|
208
213
|
}
|
|
209
214
|
|
|
210
|
-
interface OutputValidator {
|
|
211
|
-
validate: (value: unknown) => { ok: true } | { ok: false; message: string; missingRequired: string[] };
|
|
212
|
-
requiredFields: string[];
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function buildOutputValidator(schema: unknown): { validator?: OutputValidator; error?: string } {
|
|
216
|
-
const { normalized, error } = normalizeSchema(schema);
|
|
217
|
-
if (error) return { error };
|
|
218
|
-
if (normalized === undefined) return {};
|
|
219
|
-
const jsonSchema = jtdToJsonSchema(normalized);
|
|
220
|
-
const required = extractRequiredFields(jsonSchema);
|
|
221
|
-
return {
|
|
222
|
-
validator: {
|
|
223
|
-
requiredFields: required,
|
|
224
|
-
validate: value => {
|
|
225
|
-
const result = validateJsonSchemaValue(jsonSchema, value);
|
|
226
|
-
if (result.success) return { ok: true };
|
|
227
|
-
const missing = computeMissingRequired(required, value);
|
|
228
|
-
const message = formatValidationIssue(result.issues[0]) ?? "schema validation failed";
|
|
229
|
-
return { ok: false, message, missingRequired: missing };
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function extractRequiredFields(jsonSchema: unknown): string[] {
|
|
236
|
-
if (!jsonSchema || typeof jsonSchema !== "object") return [];
|
|
237
|
-
const required = (jsonSchema as { required?: unknown }).required;
|
|
238
|
-
return Array.isArray(required) ? required.filter((k): k is string => typeof k === "string") : [];
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function computeMissingRequired(required: readonly string[], value: unknown): string[] {
|
|
242
|
-
if (required.length === 0) return [];
|
|
243
|
-
if (value === null || value === undefined) return [...required];
|
|
244
|
-
if (typeof value !== "object" || Array.isArray(value)) return [];
|
|
245
|
-
const record = value as Record<string, unknown>;
|
|
246
|
-
return required.filter(key => !(key in record) || record[key] === undefined);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function formatValidationIssue(issue: JsonSchemaValidationIssue | undefined): string | undefined {
|
|
250
|
-
if (!issue) return undefined;
|
|
251
|
-
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "(root)";
|
|
252
|
-
return `${path}: ${issue.message}`;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
215
|
function previewOffendingData(value: unknown, maxLength = 500): string {
|
|
256
216
|
let serialized: string;
|
|
257
217
|
try {
|
|
@@ -305,7 +265,7 @@ function resolveFallbackCompletion(rawOutput: string, outputSchema: unknown): {
|
|
|
305
265
|
if (candidate === undefined) return null;
|
|
306
266
|
const { validator, error } = buildOutputValidator(outputSchema);
|
|
307
267
|
if (error) return null;
|
|
308
|
-
if (validator && !validator.validate(candidate).
|
|
268
|
+
if (validator && !validator.validate(candidate).success) return null;
|
|
309
269
|
return { data: candidate };
|
|
310
270
|
}
|
|
311
271
|
|
|
@@ -392,9 +352,10 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
392
352
|
stderr = `schema_violation: invalid output schema: ${schemaError}`;
|
|
393
353
|
exitCode = 1;
|
|
394
354
|
} else {
|
|
395
|
-
const
|
|
396
|
-
if (!
|
|
397
|
-
const
|
|
355
|
+
const result = validator?.validate(completeData) ?? { success: true as const };
|
|
356
|
+
if (!result.success) {
|
|
357
|
+
const summary = summarizeValidationFailure(result, completeData, validator?.requiredFields ?? []);
|
|
358
|
+
const outcome = buildSchemaViolationOutcome(summary, completeData);
|
|
398
359
|
rawOutput = outcome.rawOutput;
|
|
399
360
|
stderr = outcome.stderr;
|
|
400
361
|
exitCode = outcome.exitCode;
|
|
@@ -419,9 +380,10 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
419
380
|
if (fallback) {
|
|
420
381
|
const completeData = normalizeCompleteData(fallback.data, reportFindings);
|
|
421
382
|
const { validator } = buildOutputValidator(outputSchema);
|
|
422
|
-
const
|
|
423
|
-
if (!
|
|
424
|
-
const
|
|
383
|
+
const result = validator?.validate(completeData) ?? { success: true as const };
|
|
384
|
+
if (!result.success) {
|
|
385
|
+
const summary = summarizeValidationFailure(result, completeData, validator?.requiredFields ?? []);
|
|
386
|
+
const outcome = buildSchemaViolationOutcome(summary, completeData);
|
|
425
387
|
rawOutput = outcome.rawOutput;
|
|
426
388
|
stderr = outcome.stderr;
|
|
427
389
|
exitCode = outcome.exitCode;
|
|
@@ -909,6 +871,11 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
909
871
|
if (intent) {
|
|
910
872
|
progress.lastIntent = intent;
|
|
911
873
|
}
|
|
874
|
+
// Reset any prior in-flight task snapshot so we don't show stale
|
|
875
|
+
// nested progress when the agent enters a fresh `task` call.
|
|
876
|
+
if (event.toolName === "task") {
|
|
877
|
+
progress.inflightTaskDetails = undefined;
|
|
878
|
+
}
|
|
912
879
|
break;
|
|
913
880
|
}
|
|
914
881
|
|
|
@@ -927,6 +894,12 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
927
894
|
progress.currentTool = undefined;
|
|
928
895
|
progress.currentToolArgs = undefined;
|
|
929
896
|
progress.currentToolStartMs = undefined;
|
|
897
|
+
// The finalized TaskToolDetails will be captured below into
|
|
898
|
+
// `extractedToolData.task`; drop the in-flight snapshot so the
|
|
899
|
+
// renderer doesn't double-count it against the final entry.
|
|
900
|
+
if (event.toolName === "task") {
|
|
901
|
+
progress.inflightTaskDetails = undefined;
|
|
902
|
+
}
|
|
930
903
|
|
|
931
904
|
// Check for registered subagent tool handler
|
|
932
905
|
const handler = subprocessToolRegistry.getHandler(event.toolName);
|
|
@@ -979,6 +952,23 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
979
952
|
break;
|
|
980
953
|
}
|
|
981
954
|
|
|
955
|
+
case "tool_execution_update": {
|
|
956
|
+
// Surface nested-subagent progress mid-flight. The child task
|
|
957
|
+
// tool emits incremental `onUpdate` calls carrying its current
|
|
958
|
+
// `TaskToolDetails` (results + progress); we stash the latest
|
|
959
|
+
// snapshot so the parent UI can render the in-flight subtree
|
|
960
|
+
// without waiting for the call to finish.
|
|
961
|
+
if (event.toolName === "task") {
|
|
962
|
+
const partial = (event as { partialResult?: { details?: unknown } }).partialResult;
|
|
963
|
+
const details = partial && typeof partial === "object" ? partial.details : undefined;
|
|
964
|
+
if (details && typeof details === "object" && "results" in (details as TaskToolDetails)) {
|
|
965
|
+
progress.inflightTaskDetails = details as TaskToolDetails;
|
|
966
|
+
flushProgress = true;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
break;
|
|
970
|
+
}
|
|
971
|
+
|
|
982
972
|
case "message_update": {
|
|
983
973
|
if (event.message?.role !== "assistant") break;
|
|
984
974
|
const assistantEvent = (
|
|
@@ -1248,6 +1238,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1248
1238
|
customTools: mcpProxyTools.length > 0 ? mcpProxyTools : undefined,
|
|
1249
1239
|
localProtocolOptions: options.localProtocolOptions,
|
|
1250
1240
|
telemetry: subagentTelemetry,
|
|
1241
|
+
parentEvalSessionId: options.parentEvalSessionId,
|
|
1251
1242
|
}),
|
|
1252
1243
|
);
|
|
1253
1244
|
|
|
@@ -1295,22 +1286,25 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1295
1286
|
}
|
|
1296
1287
|
|
|
1297
1288
|
const extensionRunner = session.extensionRunner;
|
|
1289
|
+
const pendingExtensionMessages: Promise<void>[] = [];
|
|
1298
1290
|
if (extensionRunner) {
|
|
1299
1291
|
extensionRunner.initialize(
|
|
1300
1292
|
{
|
|
1301
1293
|
sendMessage: (message, options) => {
|
|
1302
|
-
session.sendCustomMessage(message, options).catch(e => {
|
|
1294
|
+
const sendPromise = session.sendCustomMessage(message, options).catch(e => {
|
|
1303
1295
|
logger.error("Extension sendMessage failed", {
|
|
1304
1296
|
error: e instanceof Error ? e.message : String(e),
|
|
1305
1297
|
});
|
|
1306
1298
|
});
|
|
1299
|
+
pendingExtensionMessages.push(sendPromise);
|
|
1307
1300
|
},
|
|
1308
1301
|
sendUserMessage: (content, options) => {
|
|
1309
|
-
session.sendUserMessage(content, options).catch(e => {
|
|
1302
|
+
const sendPromise = session.sendUserMessage(content, options).catch(e => {
|
|
1310
1303
|
logger.error("Extension sendUserMessage failed", {
|
|
1311
1304
|
error: e instanceof Error ? e.message : String(e),
|
|
1312
1305
|
});
|
|
1313
1306
|
});
|
|
1307
|
+
pendingExtensionMessages.push(sendPromise);
|
|
1314
1308
|
},
|
|
1315
1309
|
appendEntry: (customType, data) => {
|
|
1316
1310
|
session.sessionManager.appendCustomEntry(customType, data);
|
|
@@ -1346,6 +1340,9 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1346
1340
|
logger.error("Extension error", { path: err.extensionPath, error: err.error });
|
|
1347
1341
|
});
|
|
1348
1342
|
await awaitAbortable(extensionRunner.emit({ type: "session_start" }));
|
|
1343
|
+
while (pendingExtensionMessages.length > 0) {
|
|
1344
|
+
await awaitAbortable(Promise.all(pendingExtensionMessages.splice(0)));
|
|
1345
|
+
}
|
|
1349
1346
|
}
|
|
1350
1347
|
|
|
1351
1348
|
const MAX_YIELD_RETRIES = 3;
|
|
@@ -1518,7 +1515,8 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1518
1515
|
// Use final output if available, otherwise accumulated output
|
|
1519
1516
|
let rawOutput = finalOutputChunks.length > 0 ? finalOutputChunks.join("") : outputChunks.join("");
|
|
1520
1517
|
const yieldItems = progress.extractedToolData?.yield as YieldItem[] | undefined;
|
|
1521
|
-
const
|
|
1518
|
+
const reportFindingDetails = progress.extractedToolData?.report_finding as ReportFindingDetails[] | undefined;
|
|
1519
|
+
const reportFindings: ReviewFinding[] | undefined = reportFindingDetails?.map(toReviewFinding);
|
|
1522
1520
|
const finalized = finalizeSubprocessOutput({
|
|
1523
1521
|
rawOutput,
|
|
1524
1522
|
exitCode,
|
package/src/task/index.ts
CHANGED
|
@@ -558,6 +558,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
558
558
|
const commitStyle = this.session.settings.get("task.isolation.commits");
|
|
559
559
|
const maxConcurrency = this.session.settings.get("task.maxConcurrency");
|
|
560
560
|
const taskDepth = this.session.taskDepth ?? 0;
|
|
561
|
+
const subagentLspEnabled = (this.session.enableLsp ?? true) && this.session.settings.get("task.enableLsp");
|
|
561
562
|
|
|
562
563
|
if (isolationMode === "none" && "isolated" in params) {
|
|
563
564
|
return {
|
|
@@ -843,6 +844,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
843
844
|
file => path.basename(file.path).toLowerCase() !== "agents.md",
|
|
844
845
|
);
|
|
845
846
|
const promptTemplates = this.session.promptTemplates;
|
|
847
|
+
const parentEvalSessionId = this.session.getEvalSessionId?.() ?? undefined;
|
|
846
848
|
|
|
847
849
|
// Initialize progress for all tasks
|
|
848
850
|
for (let i = 0; i < tasksWithUniqueIds.length; i++) {
|
|
@@ -872,7 +874,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
872
874
|
if (!isIsolated) {
|
|
873
875
|
return runSubprocess({
|
|
874
876
|
cwd: this.session.cwd,
|
|
875
|
-
agent,
|
|
877
|
+
agent: effectiveAgent,
|
|
876
878
|
task: renderSubagentUserPrompt(task.assignment, simpleMode),
|
|
877
879
|
assignment: task.assignment.trim(),
|
|
878
880
|
context: sharedContext,
|
|
@@ -888,7 +890,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
888
890
|
persistArtifacts: !!artifactsDir,
|
|
889
891
|
artifactsDir: effectiveArtifactsDir,
|
|
890
892
|
contextFile: contextFilePath,
|
|
891
|
-
enableLsp:
|
|
893
|
+
enableLsp: subagentLspEnabled,
|
|
892
894
|
signal,
|
|
893
895
|
eventBus: this.session.eventBus,
|
|
894
896
|
onProgress: progress => {
|
|
@@ -910,6 +912,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
910
912
|
parentArtifactManager,
|
|
911
913
|
parentHindsightSessionState: this.session.getHindsightSessionState?.(),
|
|
912
914
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
915
|
+
parentEvalSessionId,
|
|
913
916
|
});
|
|
914
917
|
}
|
|
915
918
|
|
|
@@ -927,7 +930,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
927
930
|
const result = await runSubprocess({
|
|
928
931
|
cwd: this.session.cwd,
|
|
929
932
|
worktree: isolationDir,
|
|
930
|
-
agent,
|
|
933
|
+
agent: effectiveAgent,
|
|
931
934
|
task: renderSubagentUserPrompt(task.assignment, simpleMode),
|
|
932
935
|
assignment: task.assignment.trim(),
|
|
933
936
|
context: sharedContext,
|
|
@@ -943,7 +946,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
943
946
|
persistArtifacts: !!artifactsDir,
|
|
944
947
|
artifactsDir: effectiveArtifactsDir,
|
|
945
948
|
contextFile: contextFilePath,
|
|
946
|
-
enableLsp:
|
|
949
|
+
enableLsp: subagentLspEnabled,
|
|
947
950
|
signal,
|
|
948
951
|
eventBus: this.session.eventBus,
|
|
949
952
|
onProgress: progress => {
|
|
@@ -965,6 +968,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
965
968
|
parentArtifactManager,
|
|
966
969
|
parentHindsightSessionState: this.session.getHindsightSessionState?.(),
|
|
967
970
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
971
|
+
parentEvalSessionId,
|
|
968
972
|
});
|
|
969
973
|
if (mergeMode === "branch" && result.exitCode === 0) {
|
|
970
974
|
try {
|