@oh-my-pi/pi-coding-agent 17.0.1 → 17.0.3
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 +162 -40
- package/dist/cli.js +4559 -4543
- package/dist/types/advisor/config.d.ts +14 -6
- package/dist/types/advisor/runtime.d.ts +20 -11
- package/dist/types/autolearn/controller.d.ts +1 -0
- package/dist/types/config/model-discovery.d.ts +17 -0
- package/dist/types/config/model-resolver.d.ts +6 -2
- package/dist/types/config/settings-schema.d.ts +39 -10
- package/dist/types/config/settings.d.ts +50 -0
- package/dist/types/cursor.d.ts +11 -0
- package/dist/types/discovery/helpers.d.ts +5 -2
- package/dist/types/eval/agent-bridge.d.ts +7 -19
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/managed-timers.d.ts +15 -0
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +18 -0
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +55 -1
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/extensibility/utils.d.ts +2 -2
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/lsp/types.d.ts +3 -0
- package/dist/types/mcp/transports/stdio.d.ts +42 -1
- package/dist/types/modes/components/advisor-config.d.ts +8 -1
- package/dist/types/modes/components/agent-hub.d.ts +15 -0
- package/dist/types/modes/components/hook-editor.d.ts +7 -0
- package/dist/types/modes/components/model-hub.d.ts +5 -2
- package/dist/types/modes/components/session-selector.d.ts +2 -0
- package/dist/types/modes/controllers/command-controller.d.ts +8 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/print-mode.d.ts +1 -1
- package/dist/types/modes/warp-events.d.ts +24 -0
- package/dist/types/modes/warp-events.test.d.ts +1 -0
- package/dist/types/plan-mode/model-transition.d.ts +47 -0
- package/dist/types/plan-mode/model-transition.test.d.ts +1 -0
- package/dist/types/registry/agent-lifecycle.d.ts +26 -1
- package/dist/types/registry/persisted-agents.d.ts +3 -0
- package/dist/types/sdk.d.ts +27 -5
- package/dist/types/session/agent-session.d.ts +34 -10
- package/dist/types/session/session-entries.d.ts +6 -1
- package/dist/types/session/session-history-format.d.ts +10 -0
- package/dist/types/session/session-manager.d.ts +19 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
- package/dist/types/task/executor.d.ts +26 -1
- package/dist/types/task/index.d.ts +1 -1
- package/dist/types/task/label.d.ts +1 -1
- package/dist/types/task/parallel.d.ts +14 -0
- package/dist/types/task/structured-subagent.d.ts +111 -0
- package/dist/types/task/types.d.ts +51 -0
- package/dist/types/telemetry-export.d.ts +34 -9
- package/dist/types/tools/approval.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/essential-tools.d.ts +29 -0
- package/dist/types/tools/fetch.d.ts +4 -5
- package/dist/types/tools/hub/messaging.d.ts +5 -1
- package/dist/types/tools/image-gen.d.ts +2 -1
- package/dist/types/tools/index.d.ts +17 -1
- package/dist/types/tools/todo.d.ts +31 -0
- package/dist/types/tools/xdev.d.ts +11 -2
- package/dist/types/tui/tree-list.d.ts +7 -0
- package/dist/types/utils/markit.d.ts +11 -0
- package/dist/types/utils/title-generator.d.ts +2 -1
- package/dist/types/web/search/providers/kimi.d.ts +4 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +21 -16
- package/src/advisor/__tests__/advisor.test.ts +1304 -42
- package/src/advisor/__tests__/config.test.ts +58 -2
- package/src/advisor/config.ts +76 -24
- package/src/advisor/runtime.ts +445 -92
- package/src/autolearn/controller.ts +23 -28
- package/src/cli/file-processor.ts +1 -2
- package/src/cli.ts +5 -1
- package/src/config/model-discovery.ts +81 -21
- package/src/config/model-registry.ts +25 -6
- package/src/config/model-resolver.ts +14 -7
- package/src/config/settings-schema.ts +46 -9
- package/src/config/settings.ts +405 -25
- package/src/cursor.ts +20 -3
- package/src/debug/report-bundle.ts +40 -4
- package/src/discovery/helpers.ts +28 -5
- package/src/eval/__tests__/agent-bridge.test.ts +133 -47
- package/src/eval/__tests__/prelude-agent.test.ts +29 -0
- package/src/eval/agent-bridge.ts +104 -477
- package/src/eval/jl/prelude.jl +7 -6
- package/src/eval/js/shared/prelude.txt +5 -4
- package/src/eval/py/prelude.py +11 -39
- package/src/eval/rb/prelude.rb +5 -6
- package/src/exec/bash-executor.ts +14 -5
- package/src/extensibility/custom-tools/loader.ts +3 -3
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/extensibility/extensions/loader.ts +3 -3
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/runner.ts +26 -0
- package/src/extensibility/extensions/types.ts +18 -0
- package/src/extensibility/extensions/wrapper.ts +2 -1
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +149 -8
- package/src/extensibility/plugins/legacy-pi-compat.ts +225 -22
- package/src/extensibility/plugins/manager.ts +2 -2
- package/src/extensibility/shared-events.ts +6 -0
- package/src/extensibility/utils.ts +91 -25
- package/src/irc/bus.ts +22 -3
- package/src/launch/broker.ts +3 -2
- package/src/launch/client.ts +2 -2
- package/src/launch/presence.ts +2 -2
- package/src/lsp/client.ts +58 -1
- package/src/lsp/index.ts +62 -6
- package/src/lsp/types.ts +3 -0
- package/src/main.ts +11 -8
- package/src/mcp/manager.ts +9 -3
- package/src/mcp/oauth-flow.ts +20 -0
- package/src/mcp/transports/stdio.test.ts +269 -1
- package/src/mcp/transports/stdio.ts +255 -24
- package/src/modes/components/advisor-config.ts +65 -3
- package/src/modes/components/agent-hub.ts +1 -72
- package/src/modes/components/ask-dialog.ts +1 -1
- package/src/modes/components/bash-execution.ts +7 -3
- package/src/modes/components/eval-execution.ts +3 -1
- package/src/modes/components/hook-editor.ts +18 -3
- package/src/modes/components/model-hub.ts +138 -42
- package/src/modes/components/session-selector.ts +4 -0
- package/src/modes/components/status-line/component.test.ts +1 -0
- package/src/modes/components/status-line/segments.ts +21 -6
- package/src/modes/controllers/command-controller.ts +167 -47
- package/src/modes/controllers/event-controller.ts +5 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -22
- package/src/modes/controllers/input-controller.ts +12 -12
- package/src/modes/controllers/selector-controller.ts +191 -31
- package/src/modes/interactive-mode.ts +169 -62
- package/src/modes/print-mode.ts +3 -3
- package/src/modes/rpc/host-tools.ts +2 -1
- package/src/modes/rpc/rpc-mode.ts +19 -4
- package/src/modes/warp-events.test.ts +794 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/plan-mode/model-transition.test.ts +60 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/tools/eval.md +5 -2
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/task.md +12 -8
- package/src/prompts/tools/write.md +1 -1
- package/src/registry/agent-lifecycle.ts +133 -18
- package/src/registry/persisted-agents.ts +74 -0
- package/src/sdk.ts +343 -122
- package/src/session/agent-session.ts +1359 -368
- package/src/session/session-entries.ts +6 -1
- package/src/session/session-history-format.ts +20 -5
- package/src/session/session-manager.ts +57 -0
- package/src/session/streaming-output.ts +41 -1
- package/src/slash-commands/builtin-registry.ts +7 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
- package/src/system-prompt.ts +7 -2
- package/src/task/executor.ts +100 -22
- package/src/task/index.ts +258 -429
- package/src/task/label.ts +2 -0
- package/src/task/parallel.ts +43 -0
- package/src/task/persisted-revive.ts +19 -7
- package/src/task/structured-subagent.ts +642 -0
- package/src/task/types.ts +58 -0
- package/src/telemetry-export.ts +453 -97
- package/src/tools/__tests__/eval-description.test.ts +1 -1
- package/src/tools/approval.ts +11 -0
- package/src/tools/bash.ts +71 -38
- package/src/tools/essential-tools.ts +45 -0
- package/src/tools/fetch.ts +28 -105
- package/src/tools/gh.ts +169 -2
- package/src/tools/hub/messaging.ts +16 -3
- package/src/tools/image-gen.ts +69 -7
- package/src/tools/index.ts +50 -15
- package/src/tools/path-utils.ts +1 -0
- package/src/tools/read.ts +62 -29
- package/src/tools/todo.ts +126 -13
- package/src/tools/write.ts +22 -4
- package/src/tools/xdev.ts +14 -3
- package/src/tui/tree-list.ts +39 -0
- package/src/utils/markit.ts +12 -0
- package/src/utils/title-generator.ts +15 -4
- package/src/utils/zip.ts +16 -1
- package/src/web/search/providers/kimi.ts +18 -12
- package/src/web/search/types.ts +6 -1
package/src/sdk.ts
CHANGED
|
@@ -2,13 +2,21 @@ import {
|
|
|
2
2
|
Agent,
|
|
3
3
|
type AgentEvent,
|
|
4
4
|
type AgentMessage,
|
|
5
|
+
type AgentOptions,
|
|
5
6
|
type AgentTelemetryConfig,
|
|
6
7
|
type AgentTool,
|
|
7
8
|
AppendOnlyContextManager,
|
|
8
9
|
filterProviderReplayMessages,
|
|
9
10
|
type ThinkingLevel,
|
|
10
11
|
} from "@oh-my-pi/pi-agent-core";
|
|
11
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
Context,
|
|
14
|
+
CredentialDisabledEvent,
|
|
15
|
+
Message,
|
|
16
|
+
Model,
|
|
17
|
+
ProviderSessionState,
|
|
18
|
+
SimpleStreamOptions,
|
|
19
|
+
} from "@oh-my-pi/pi-ai";
|
|
12
20
|
import type { Dialect } from "@oh-my-pi/pi-ai/dialect";
|
|
13
21
|
import {
|
|
14
22
|
getOpenAICodexTransportDetails,
|
|
@@ -26,6 +34,7 @@ import {
|
|
|
26
34
|
} from "./advisor";
|
|
27
35
|
import { type AsyncJob, AsyncJobManager } from "./async";
|
|
28
36
|
import { AutoLearnController, buildAutoLearnInstructions } from "./autolearn/controller";
|
|
37
|
+
import { createAutoresearchExtension } from "./autoresearch";
|
|
29
38
|
import { loadCapability } from "./capability";
|
|
30
39
|
import { type Rule, ruleCapability, setActiveRules } from "./capability/rule";
|
|
31
40
|
import { bucketRules } from "./capability/rule-buckets";
|
|
@@ -137,6 +146,7 @@ import {
|
|
|
137
146
|
} from "./system-prompt";
|
|
138
147
|
import { AgentOutputManager } from "./task/output-manager";
|
|
139
148
|
import { wrapStreamFnWithProviderConcurrency } from "./task/provider-concurrency";
|
|
149
|
+
import type { StructuredSubagentSchemaMode } from "./task/types";
|
|
140
150
|
import {
|
|
141
151
|
AUTO_THINKING,
|
|
142
152
|
type ConfiguredThinkingLevel,
|
|
@@ -154,6 +164,7 @@ import {
|
|
|
154
164
|
createTools,
|
|
155
165
|
createVibeTools,
|
|
156
166
|
type DeferredDiagnosticsEntry,
|
|
167
|
+
defaultLoadModeForToolName,
|
|
157
168
|
discoverStartupLspServers,
|
|
158
169
|
EditTool,
|
|
159
170
|
EvalTool,
|
|
@@ -473,20 +484,30 @@ export interface CreateAgentSessionOptions {
|
|
|
473
484
|
/** File-based slash commands. Default: discovered from commands/ directories */
|
|
474
485
|
slashCommands?: FileSlashCommand[];
|
|
475
486
|
|
|
476
|
-
/**
|
|
487
|
+
/**
|
|
488
|
+
* Enable MCP capabilities. `false` skips MCP discovery and ignores
|
|
489
|
+
* `mcpManager`, preventing process-global or inherited MCP access. Default:
|
|
490
|
+
* true.
|
|
491
|
+
*/
|
|
477
492
|
enableMCP?: boolean;
|
|
478
|
-
/** Existing MCP manager to reuse (skips discovery, propagates to toolSession). */
|
|
493
|
+
/** Existing MCP manager to reuse when MCP is enabled (skips discovery, propagates to toolSession). */
|
|
479
494
|
mcpManager?: MCPManager;
|
|
480
495
|
|
|
481
496
|
/** Enable LSP integration (tool, formatting, diagnostics, warmup). Default: true */
|
|
482
497
|
enableLsp?: boolean;
|
|
498
|
+
/** Whether this invocation may expose IRC. `false` removes it even for subagents. */
|
|
499
|
+
enableIrc?: boolean;
|
|
483
500
|
/** Skip subprocess-kernel availability checks and prelude warmup */
|
|
484
501
|
skipPythonPreflight?: boolean;
|
|
485
502
|
/** Tool names explicitly requested (enables disabled-by-default tools) */
|
|
486
503
|
toolNames?: string[];
|
|
504
|
+
/** Limit the session to explicitly supplied tool names, without discovered extras. */
|
|
505
|
+
restrictToolNames?: boolean;
|
|
487
506
|
|
|
488
|
-
/** Output schema for structured completion (subagents) */
|
|
507
|
+
/** Output schema for structured completion (subagents). */
|
|
489
508
|
outputSchema?: unknown;
|
|
509
|
+
/** Enforcement policy for {@link outputSchema}; defaults to legacy permissive behavior. */
|
|
510
|
+
outputSchemaMode?: StructuredSubagentSchemaMode;
|
|
490
511
|
/** Whether to include the yield tool by default */
|
|
491
512
|
requireYieldTool?: boolean;
|
|
492
513
|
/** Task recursion depth (for subagent sessions). Default: 0 */
|
|
@@ -890,7 +911,7 @@ function customToolToDefinition(tool: CustomTool): ToolDefinition {
|
|
|
890
911
|
description: tool.description,
|
|
891
912
|
parameters: tool.parameters,
|
|
892
913
|
hidden: tool.hidden,
|
|
893
|
-
loadMode: tool.loadMode
|
|
914
|
+
loadMode: defaultLoadModeForToolName(tool.name, tool.loadMode),
|
|
894
915
|
deferrable: tool.deferrable,
|
|
895
916
|
approval: typeof tool.approval === "function" ? tool.approval.bind(tool) : tool.approval,
|
|
896
917
|
mcpServerName: tool.mcpServerName,
|
|
@@ -1055,6 +1076,88 @@ function buildMCPPromptCommands(manager: MCPManager): LoadedCustomCommand[] {
|
|
|
1055
1076
|
}
|
|
1056
1077
|
return commands;
|
|
1057
1078
|
}
|
|
1079
|
+
|
|
1080
|
+
/** Dependencies used to construct an isolated auto-learn capture agent. */
|
|
1081
|
+
export interface AutoLearnCaptureRunnerOptions {
|
|
1082
|
+
sourceAgent: Agent;
|
|
1083
|
+
captureTools: AgentTool[];
|
|
1084
|
+
createAgent: (options: AgentOptions) => Agent;
|
|
1085
|
+
onPayload?: SimpleStreamOptions["onPayload"];
|
|
1086
|
+
onResponse?: SimpleStreamOptions["onResponse"];
|
|
1087
|
+
createSessionId?: () => string;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/** Build a private capture runner over a detached message snapshot and provider session. */
|
|
1091
|
+
export function createAutoLearnCaptureRunner(
|
|
1092
|
+
options: AutoLearnCaptureRunnerOptions,
|
|
1093
|
+
): (content: string, signal?: AbortSignal) => Promise<void> {
|
|
1094
|
+
return async (content, signal) => {
|
|
1095
|
+
if (options.captureTools.length === 0 || signal?.aborted) return;
|
|
1096
|
+
const captureModel = options.sourceAgent.state.model;
|
|
1097
|
+
if (!captureModel) return;
|
|
1098
|
+
|
|
1099
|
+
const captureSessionId = options.createSessionId?.() ?? Bun.randomUUIDv7();
|
|
1100
|
+
const captureProviderSessionState = new Map<string, ProviderSessionState>();
|
|
1101
|
+
const captureMessages = options.sourceAgent.state.messages.map((message): AgentMessage => {
|
|
1102
|
+
if (message.role === "assistant") {
|
|
1103
|
+
return { ...message, responseId: undefined, providerPayload: undefined };
|
|
1104
|
+
}
|
|
1105
|
+
if (message.role === "user" || message.role === "developer") {
|
|
1106
|
+
return { ...message, providerPayload: undefined };
|
|
1107
|
+
}
|
|
1108
|
+
return message;
|
|
1109
|
+
});
|
|
1110
|
+
const captureAgent = options.createAgent({
|
|
1111
|
+
initialState: {
|
|
1112
|
+
systemPrompt: [...options.sourceAgent.state.systemPrompt],
|
|
1113
|
+
model: captureModel,
|
|
1114
|
+
thinkingLevel: options.sourceAgent.state.thinkingLevel,
|
|
1115
|
+
disableReasoning: options.sourceAgent.state.disableReasoning,
|
|
1116
|
+
tools: options.captureTools,
|
|
1117
|
+
messages: captureMessages,
|
|
1118
|
+
},
|
|
1119
|
+
sessionId: captureSessionId,
|
|
1120
|
+
promptCacheKey: captureSessionId,
|
|
1121
|
+
providerSessionState: captureProviderSessionState,
|
|
1122
|
+
getApiKey: requestModel => options.sourceAgent.getApiKey?.(requestModel),
|
|
1123
|
+
onPayload: options.onPayload,
|
|
1124
|
+
onResponse: options.onResponse,
|
|
1125
|
+
});
|
|
1126
|
+
captureAgent.setMetadataResolver(provider => options.sourceAgent.metadataForProvider(provider));
|
|
1127
|
+
const captureMessage: CustomMessage = {
|
|
1128
|
+
role: "custom",
|
|
1129
|
+
customType: "autolearn-nudge",
|
|
1130
|
+
content,
|
|
1131
|
+
display: false,
|
|
1132
|
+
attribution: "agent",
|
|
1133
|
+
timestamp: Date.now(),
|
|
1134
|
+
};
|
|
1135
|
+
const abortCapture = () => captureAgent.abort(signal?.reason);
|
|
1136
|
+
signal?.addEventListener("abort", abortCapture, { once: true });
|
|
1137
|
+
try {
|
|
1138
|
+
if (signal?.aborted) {
|
|
1139
|
+
abortCapture();
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
await captureAgent.prompt(captureMessage);
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
if (!signal?.aborted) throw error;
|
|
1145
|
+
} finally {
|
|
1146
|
+
signal?.removeEventListener("abort", abortCapture);
|
|
1147
|
+
for (const [providerKey, state] of captureProviderSessionState) {
|
|
1148
|
+
try {
|
|
1149
|
+
state.close();
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
logger.warn("Failed to close auto-learn capture provider state", {
|
|
1152
|
+
providerKey,
|
|
1153
|
+
error: String(error),
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
captureProviderSessionState.clear();
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1058
1161
|
/**
|
|
1059
1162
|
* Create an AgentSession with the specified options.
|
|
1060
1163
|
*
|
|
@@ -1453,7 +1556,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1453
1556
|
let session!: AgentSession;
|
|
1454
1557
|
let hasSession = false;
|
|
1455
1558
|
let hasRegistered = false;
|
|
1456
|
-
const
|
|
1559
|
+
const restrictToolNames = options.restrictToolNames === true;
|
|
1560
|
+
const enableLsp = !restrictToolNames && (options.enableLsp ?? true);
|
|
1457
1561
|
const asyncMaxJobs = Math.min(100, Math.max(1, settings.get("async.maxJobs") ?? 100));
|
|
1458
1562
|
const ASYNC_INLINE_RESULT_MAX_CHARS = 12_000;
|
|
1459
1563
|
const ASYNC_PREVIEW_MAX_CHARS = 4_000;
|
|
@@ -1550,9 +1654,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1550
1654
|
setActiveToolNames,
|
|
1551
1655
|
hasUI: options.hasUI ?? false,
|
|
1552
1656
|
enableLsp,
|
|
1657
|
+
enableIrc: restrictToolNames ? false : options.enableIrc,
|
|
1658
|
+
restrictToolNames,
|
|
1553
1659
|
get hasEditTool() {
|
|
1554
1660
|
const requestedToolNames = options.toolNames ? normalizeToolNames(options.toolNames) : undefined;
|
|
1555
|
-
return
|
|
1661
|
+
return restrictToolNames
|
|
1662
|
+
? requestedToolNames?.includes("edit") === true
|
|
1663
|
+
: !requestedToolNames || requestedToolNames.includes("edit");
|
|
1556
1664
|
},
|
|
1557
1665
|
skipPythonPreflight: options.skipPythonPreflight,
|
|
1558
1666
|
contextFiles,
|
|
@@ -1564,6 +1672,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1564
1672
|
rules: allRules,
|
|
1565
1673
|
eventBus,
|
|
1566
1674
|
outputSchema: options.outputSchema,
|
|
1675
|
+
outputSchemaMode: options.outputSchemaMode,
|
|
1567
1676
|
requireYieldTool: options.requireYieldTool,
|
|
1568
1677
|
prewalkArmed: options.prewalk !== undefined,
|
|
1569
1678
|
taskDepth: options.taskDepth ?? 0,
|
|
@@ -1680,10 +1789,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1680
1789
|
// Create built-in tools (already wrapped with meta notice formatting)
|
|
1681
1790
|
const builtinTools = await logger.time("createAllTools", createTools, toolSession, options.toolNames);
|
|
1682
1791
|
|
|
1683
|
-
//
|
|
1684
|
-
|
|
1792
|
+
// Restricted sessions cannot inherit or discover MCP capabilities.
|
|
1793
|
+
const enableMCP = !restrictToolNames && (options.enableMCP ?? true);
|
|
1794
|
+
let mcpManager: MCPManager | undefined = enableMCP ? options.mcpManager : undefined;
|
|
1685
1795
|
toolSession.mcpManager = mcpManager;
|
|
1686
|
-
|
|
1796
|
+
toolSession.enableMCP = enableMCP;
|
|
1687
1797
|
const deferMCPDiscoveryForUI = enableMCP && !mcpManager && options.hasUI === true;
|
|
1688
1798
|
const customTools: CustomTool[] = [];
|
|
1689
1799
|
let startDeferredMCPDiscovery: ((liveSession: AgentSession) => void) | undefined;
|
|
@@ -1769,58 +1879,62 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1769
1879
|
// to mirror the AsyncJobManager ownership rule.
|
|
1770
1880
|
if (mcpManager && !options.parentTaskPrefix) MCPManager.setInstance(mcpManager);
|
|
1771
1881
|
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1882
|
+
const builtInToolNames = builtinTools.map(t => t.name);
|
|
1883
|
+
let customToolPaths: ToolPathWithSource[] = [];
|
|
1884
|
+
const inlineExtensions: ExtensionFactory[] = [];
|
|
1885
|
+
if (!restrictToolNames) {
|
|
1886
|
+
// Add image tools when generation is enabled and either no explicit tool
|
|
1887
|
+
// whitelist was given or it names `generate_image`. Unlike built-in tools
|
|
1888
|
+
// (filtered in `createTools`), custom tools are force-activated via
|
|
1889
|
+
// `alwaysInclude` below, so an explicit `--no-tools`/whitelist must be
|
|
1890
|
+
// honored here or image-gen would leak past every filter (issue #5305).
|
|
1891
|
+
const imageGenRequested = !options.toolNames || options.toolNames.includes("generate_image");
|
|
1892
|
+
if (settings.get("generate_image.enabled") && imageGenRequested) {
|
|
1893
|
+
const imageGenTools = await logger.time("getImageGenTools", () => getImageGenTools(modelRegistry, model));
|
|
1894
|
+
if (imageGenTools.length > 0) {
|
|
1895
|
+
customTools.push(...(imageGenTools as unknown as CustomTool[]));
|
|
1896
|
+
}
|
|
1782
1897
|
}
|
|
1783
|
-
}
|
|
1784
1898
|
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1899
|
+
if (settings.get("speechgen.enabled")) {
|
|
1900
|
+
customTools.push(ttsTool as unknown as CustomTool);
|
|
1901
|
+
}
|
|
1788
1902
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1903
|
+
// Add web search tools
|
|
1904
|
+
if (options.toolNames?.includes("web_search")) {
|
|
1905
|
+
customTools.push(...getSearchTools());
|
|
1906
|
+
}
|
|
1793
1907
|
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1908
|
+
// Discover custom tools from `.omp/tools/`, `.claude/tools/`, plugins, etc.
|
|
1909
|
+
// Subagents reuse the parent's scan via `preloadedCustomToolPaths` to skip
|
|
1910
|
+
// the FS walk, but ALWAYS re-call `loadCustomTools` here so factories bind
|
|
1911
|
+
// to THIS session's `CustomToolAPI` (cwd, exec, pushPendingAction, UI).
|
|
1912
|
+
// Forwarding the parent's `LoadedCustomTool[]` directly would route tool
|
|
1913
|
+
// execution back through the parent — wrong for isolated tasks and for
|
|
1914
|
+
// pending-action queueing.
|
|
1915
|
+
customToolPaths =
|
|
1916
|
+
options.preloadedCustomToolPaths ??
|
|
1917
|
+
(await logger.time("discoverCustomToolPaths", () => discoverCustomToolPaths([], cwd)));
|
|
1918
|
+
const customToolsLoadResult = await logger.time("loadCustomTools", () =>
|
|
1919
|
+
loadCustomTools(customToolPaths, cwd, builtInToolNames, action => queueResolveHandler(toolSession, action)),
|
|
1920
|
+
);
|
|
1921
|
+
for (const { path, error } of customToolsLoadResult.errors) {
|
|
1922
|
+
logger.error("Custom tool load failed", { path, error });
|
|
1923
|
+
}
|
|
1924
|
+
if (customToolsLoadResult.tools.length > 0) {
|
|
1925
|
+
customTools.push(...customToolsLoadResult.tools.map(loaded => loaded.tool));
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
inlineExtensions.push(...(options.extensions ?? []));
|
|
1929
|
+
inlineExtensions.push(createAutoresearchExtension);
|
|
1930
|
+
if (customTools.length > 0) {
|
|
1931
|
+
inlineExtensions.push(createCustomToolsExtension(customTools));
|
|
1932
|
+
}
|
|
1813
1933
|
}
|
|
1814
1934
|
// Forward the path list (NOT the loaded tools) to subagents so they
|
|
1815
1935
|
// re-bind under their own `CustomToolAPI` while skipping the FS scan.
|
|
1816
1936
|
toolSession.customToolPaths = customToolPaths;
|
|
1817
1937
|
|
|
1818
|
-
const inlineExtensions: ExtensionFactory[] = options.extensions ? [...options.extensions] : [];
|
|
1819
|
-
inlineExtensions.push((await import("./autoresearch")).createAutoresearchExtension);
|
|
1820
|
-
if (customTools.length > 0) {
|
|
1821
|
-
inlineExtensions.push(createCustomToolsExtension(customTools));
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
1938
|
// Load extensions. Three paths:
|
|
1825
1939
|
// 1. `preloadedExtensions` (CLI): caller already loaded — reuse the
|
|
1826
1940
|
// Extension instances. Shallow-clone `extensions` so the inline
|
|
@@ -1835,7 +1949,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1835
1949
|
// the flag and pre-resolved the result already reflects that choice.
|
|
1836
1950
|
let extensionPaths: string[];
|
|
1837
1951
|
let extensionsResult: LoadExtensionsResult;
|
|
1838
|
-
if (
|
|
1952
|
+
if (restrictToolNames) {
|
|
1953
|
+
// Allocate a session runtime without evaluating caller-provided extension
|
|
1954
|
+
// instances, paths, or factories.
|
|
1955
|
+
extensionPaths = [];
|
|
1956
|
+
extensionsResult = await loadExtensions([], cwd, eventBus);
|
|
1957
|
+
} else if (options.preloadedExtensions) {
|
|
1839
1958
|
extensionsResult = {
|
|
1840
1959
|
...options.preloadedExtensions,
|
|
1841
1960
|
extensions: [...options.preloadedExtensions.extensions],
|
|
@@ -2144,11 +2263,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2144
2263
|
}
|
|
2145
2264
|
}
|
|
2146
2265
|
|
|
2147
|
-
//
|
|
2148
|
-
const customCommandsResult: CustomCommandsLoadResult =
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2266
|
+
// Restricted sessions do not discover or evaluate custom command modules.
|
|
2267
|
+
const customCommandsResult: CustomCommandsLoadResult =
|
|
2268
|
+
options.disableExtensionDiscovery || restrictToolNames
|
|
2269
|
+
? { commands: [], errors: [] }
|
|
2270
|
+
: await logger.time("discoverCustomCommands", loadCustomCommandsInternal, { cwd, agentDir });
|
|
2271
|
+
if (!options.disableExtensionDiscovery && !restrictToolNames) {
|
|
2152
2272
|
for (const { path, error } of customCommandsResult.errors) {
|
|
2153
2273
|
logger.error("Failed to load custom command", { path, error });
|
|
2154
2274
|
}
|
|
@@ -2193,8 +2313,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2193
2313
|
});
|
|
2194
2314
|
const toolContextStore = new ToolContextStore(getSessionContext);
|
|
2195
2315
|
|
|
2196
|
-
const registeredTools = extensionRunner.getAllRegisteredTools();
|
|
2197
|
-
const sdkCustomTools =
|
|
2316
|
+
const registeredTools = restrictToolNames ? [] : extensionRunner.getAllRegisteredTools();
|
|
2317
|
+
const sdkCustomTools = restrictToolNames
|
|
2318
|
+
? []
|
|
2319
|
+
: (options.customTools?.filter(tool => !isLegacyBuiltinToolDefinition(tool)) ?? []);
|
|
2198
2320
|
const allCustomTools = [
|
|
2199
2321
|
...registeredTools,
|
|
2200
2322
|
...sdkCustomTools.map(tool => {
|
|
@@ -2217,7 +2339,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2217
2339
|
toolRegistry.set(tool.name, tool);
|
|
2218
2340
|
builtInRegistryToolNames.add(tool.name);
|
|
2219
2341
|
}
|
|
2220
|
-
if (!toolRegistry.has("goal") && settings.get("goal.enabled")) {
|
|
2342
|
+
if (!restrictToolNames && !toolRegistry.has("goal") && settings.get("goal.enabled")) {
|
|
2221
2343
|
const goalTool = await logger.time("createTools:goal:session", HIDDEN_TOOLS.goal, toolSession);
|
|
2222
2344
|
if (goalTool) {
|
|
2223
2345
|
toolRegistry.set(goalTool.name, wrapToolWithMetaNotice(goalTool));
|
|
@@ -2247,31 +2369,50 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2247
2369
|
builtInRegistryToolNames.delete("edit");
|
|
2248
2370
|
}
|
|
2249
2371
|
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
// plan mode unable to exit through the intended path.
|
|
2257
|
-
const hasDeferrableTools = Array.from(toolRegistry.values()).some(tool => tool.deferrable === true);
|
|
2258
|
-
const hasXdevTools = (toolSession.xdevRegistry?.size ?? 0) > 0;
|
|
2259
|
-
const planModeAvailable = settings.get("plan.enabled");
|
|
2260
|
-
if ((hasDeferrableTools || hasXdevTools || planModeAvailable) && !toolRegistry.has("write")) {
|
|
2261
|
-
const writeTool = await logger.time("createTools:write:session", BUILTIN_TOOLS.write, toolSession);
|
|
2262
|
-
if (writeTool) {
|
|
2372
|
+
let writeRegistration: Promise<boolean> | undefined;
|
|
2373
|
+
const ensureWriteRegistered = (): Promise<boolean> => {
|
|
2374
|
+
if (toolRegistry.has("write")) return Promise.resolve(builtInRegistryToolNames.has("write"));
|
|
2375
|
+
writeRegistration ??= (async () => {
|
|
2376
|
+
const writeTool = await logger.time("createTools:write:session", BUILTIN_TOOLS.write, toolSession);
|
|
2377
|
+
if (!writeTool || toolRegistry.has("write")) return builtInRegistryToolNames.has("write");
|
|
2263
2378
|
toolRegistry.set(
|
|
2264
2379
|
writeTool.name,
|
|
2265
2380
|
new ExtensionToolWrapper(wrapToolWithMetaNotice(writeTool), extensionRunner) as Tool,
|
|
2266
2381
|
);
|
|
2267
2382
|
builtInRegistryToolNames.add(writeTool.name);
|
|
2268
|
-
|
|
2383
|
+
return true;
|
|
2384
|
+
})().finally(() => {
|
|
2385
|
+
writeRegistration = undefined;
|
|
2386
|
+
});
|
|
2387
|
+
return writeRegistration;
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
// Existing staged/device paths need write registered before active-set assembly.
|
|
2391
|
+
// Deferred MCP also registers it now, but refresh activates it only after a server connects.
|
|
2392
|
+
const hasDeferrableTools = Array.from(toolRegistry.values()).some(tool => tool.deferrable === true);
|
|
2393
|
+
const hasXdevTools = (toolSession.xdevRegistry?.size ?? 0) > 0;
|
|
2394
|
+
const planModeAvailable = settings.get("plan.enabled");
|
|
2395
|
+
if (!restrictToolNames && (hasDeferrableTools || hasXdevTools || planModeAvailable || deferMCPDiscoveryForUI)) {
|
|
2396
|
+
await ensureWriteRegistered();
|
|
2269
2397
|
}
|
|
2270
2398
|
|
|
2271
2399
|
let cursorEventEmitter: ((event: AgentEvent) => void) | undefined;
|
|
2400
|
+
// Built-in xd:// devices (ast_edit, debug, browser, lsp, web_search) are
|
|
2401
|
+
// mounted in createTools BEFORE this loop wraps registry entries in
|
|
2402
|
+
// ExtensionToolWrapper, so the registry holds them unwrapped. The normal
|
|
2403
|
+
// `write xd://<tool>` path runs approval through the wrapped `write` tool's
|
|
2404
|
+
// tier gate, but Cursor invokes advertised devices via `tool.execute()`
|
|
2405
|
+
// directly — so wrap unwrapped devices here to keep the approval/deny/prompt
|
|
2406
|
+
// gate. Dynamic mounts (custom/MCP) already come from the wrapped registry.
|
|
2407
|
+
const resolveCursorDevice = (name: string): AgentTool | undefined => {
|
|
2408
|
+
const device = toolSession.xdevRegistry?.get(name);
|
|
2409
|
+
if (!device) return undefined;
|
|
2410
|
+
return device instanceof ExtensionToolWrapper ? device : new ExtensionToolWrapper(device, extensionRunner);
|
|
2411
|
+
};
|
|
2272
2412
|
const cursorExecHandlers = new CursorExecHandlers({
|
|
2273
2413
|
cwd,
|
|
2274
2414
|
tools: toolRegistry,
|
|
2415
|
+
getTool: resolveCursorDevice,
|
|
2275
2416
|
getToolContext: () => toolContextStore.getContext(),
|
|
2276
2417
|
emitEvent: event => cursorEventEmitter?.(event),
|
|
2277
2418
|
});
|
|
@@ -2291,8 +2432,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2291
2432
|
): Promise<BuildSystemPromptResult> => {
|
|
2292
2433
|
toolContextStore.setToolNames(toolNames);
|
|
2293
2434
|
const promptTools = buildSystemPromptToolMetadata(tools);
|
|
2294
|
-
const memoryBackend = await resolveMemoryBackend(settings);
|
|
2295
|
-
const memoryInstructions =
|
|
2435
|
+
const memoryBackend = restrictToolNames ? undefined : await resolveMemoryBackend(settings);
|
|
2436
|
+
const memoryInstructions = memoryBackend
|
|
2437
|
+
? await memoryBackend.buildDeveloperInstructions(agentDir, settings, session)
|
|
2438
|
+
: undefined;
|
|
2296
2439
|
|
|
2297
2440
|
// Build combined append prompt: memory instructions + auto-learn guidance
|
|
2298
2441
|
// + MCP server instructions. For UI sessions MCP discovery is deferred, so
|
|
@@ -2307,10 +2450,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2307
2450
|
// session-start build — so a subagent that filtered them out, a mid-session
|
|
2308
2451
|
// enable that never built them, or a same-named custom tool while auto-learn
|
|
2309
2452
|
// is off all get no guidance.
|
|
2310
|
-
const autoLearnInstructions =
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2453
|
+
const autoLearnInstructions = restrictToolNames
|
|
2454
|
+
? undefined
|
|
2455
|
+
: buildAutoLearnInstructions({
|
|
2456
|
+
manageSkill: builtInToolNames.includes("manage_skill"),
|
|
2457
|
+
learn: builtInToolNames.includes("learn"),
|
|
2458
|
+
});
|
|
2314
2459
|
const appendParts: string[] = [];
|
|
2315
2460
|
if (memoryInstructions) appendParts.push(memoryInstructions);
|
|
2316
2461
|
if (autoLearnInstructions) appendParts.push(autoLearnInstructions);
|
|
@@ -2342,7 +2487,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2342
2487
|
cwd,
|
|
2343
2488
|
xdevTools: toolSession.xdevRegistry?.entries() ?? [],
|
|
2344
2489
|
xdevDocs: toolSession.xdevRegistry?.docsAll() ?? "",
|
|
2345
|
-
autoQaEnabled: isAutoQaEnabled(settings),
|
|
2490
|
+
autoQaEnabled: !restrictToolNames && isAutoQaEnabled(settings),
|
|
2346
2491
|
resolvedCustomPrompt: options.customSystemPrompt,
|
|
2347
2492
|
skills: session?.skills ?? skills,
|
|
2348
2493
|
contextFiles,
|
|
@@ -2359,11 +2504,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2359
2504
|
eagerTasksAlways,
|
|
2360
2505
|
taskBatch: settings.get("task.batch"),
|
|
2361
2506
|
taskMaxConcurrency: settings.get("task.maxConcurrency"),
|
|
2362
|
-
taskIrcEnabled: isIrcEnabled(settings, options.taskDepth ?? 0),
|
|
2507
|
+
taskIrcEnabled: !restrictToolNames && isIrcEnabled(settings, options.taskDepth ?? 0),
|
|
2363
2508
|
secretsEnabled,
|
|
2364
2509
|
workspaceTree: workspaceTreePromise,
|
|
2365
2510
|
includeWorkspaceTree,
|
|
2366
|
-
memoryRootEnabled: memoryBackend
|
|
2511
|
+
memoryRootEnabled: memoryBackend?.id === "local",
|
|
2367
2512
|
model: getActiveModelString(),
|
|
2368
2513
|
includeModelInPrompt: settings.get("includeModelInPrompt"),
|
|
2369
2514
|
personality: agentKind === "sub" ? "none" : settings.get("personality"),
|
|
@@ -2404,7 +2549,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2404
2549
|
// exactly the builtins createTools built (`builtInToolNames` — provenance, so a
|
|
2405
2550
|
// same-named custom/extension tool is never force-activated when auto-learn is
|
|
2406
2551
|
// off) to keep guidance, controller, and the active set consistent.
|
|
2407
|
-
if (explicitlyRequestedToolNames) {
|
|
2552
|
+
if (!restrictToolNames && explicitlyRequestedToolNames) {
|
|
2408
2553
|
for (const name of ["manage_skill", "learn"]) {
|
|
2409
2554
|
if (builtInToolNames.includes(name) && !explicitlyRequestedToolNames.includes(name)) {
|
|
2410
2555
|
explicitlyRequestedToolNames.push(name);
|
|
@@ -2413,21 +2558,29 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2413
2558
|
}
|
|
2414
2559
|
const requestedToolNames = explicitlyRequestedToolNames ?? toolNamesFromRegistry;
|
|
2415
2560
|
const normalizedRequested = requestedToolNames.filter(name => toolRegistry.has(name));
|
|
2416
|
-
const requestedToolNameSet = new Set(normalizedRequested);
|
|
2417
2561
|
const defaultInactiveToolNames = new Set(
|
|
2418
2562
|
registeredTools.filter(tool => tool.definition.defaultInactive).map(tool => tool.definition.name),
|
|
2419
2563
|
);
|
|
2420
2564
|
const requestedActiveToolNames = normalizedRequested.filter(name => name !== "goal");
|
|
2565
|
+
const explicitlyRequestedToolNameSet = explicitlyRequestedToolNames
|
|
2566
|
+
? new Set(explicitlyRequestedToolNames)
|
|
2567
|
+
: undefined;
|
|
2568
|
+
const xdevReadAvailable =
|
|
2569
|
+
builtInRegistryToolNames.has("read") &&
|
|
2570
|
+
(explicitlyRequestedToolNameSet === undefined || explicitlyRequestedToolNameSet.has("read"));
|
|
2421
2571
|
const initialRequestedActiveToolNames = options.toolNames
|
|
2422
2572
|
? requestedActiveToolNames
|
|
2423
2573
|
: requestedActiveToolNames.filter(name => !defaultInactiveToolNames.has(name));
|
|
2424
2574
|
let initialToolNames = [...initialRequestedActiveToolNames];
|
|
2425
2575
|
|
|
2426
|
-
// Custom tools and extension-registered tools are always included regardless of toolNames filter
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2576
|
+
// Custom tools and extension-registered tools are always included regardless of toolNames filter.
|
|
2577
|
+
// Restricted callers own the list, so never widen it with registered tools.
|
|
2578
|
+
const alwaysInclude: string[] = restrictToolNames
|
|
2579
|
+
? []
|
|
2580
|
+
: [
|
|
2581
|
+
...sdkCustomTools.map(t => (isCustomTool(t) ? t.name : t.name)),
|
|
2582
|
+
...registeredTools.filter(t => !t.definition.defaultInactive).map(t => t.definition.name),
|
|
2583
|
+
];
|
|
2431
2584
|
for (const name of alwaysInclude) {
|
|
2432
2585
|
if (toolRegistry.has(name) && !initialToolNames.includes(name)) {
|
|
2433
2586
|
initialToolNames.push(name);
|
|
@@ -2449,24 +2602,32 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2449
2602
|
});
|
|
2450
2603
|
hasRegistered = true;
|
|
2451
2604
|
|
|
2452
|
-
// Partition the initial enabled set for the xd:// transport:
|
|
2453
|
-
// tools become mounted devices
|
|
2454
|
-
//
|
|
2455
|
-
//
|
|
2456
|
-
//
|
|
2457
|
-
// never ship a schema. Presentation only — selection already happened.
|
|
2605
|
+
// Partition the initial enabled set for the xd:// transport: ambient
|
|
2606
|
+
// discoverable tools become mounted devices, while explicitly requested
|
|
2607
|
+
// tools keep their top-level presentation. The registry already holds the
|
|
2608
|
+
// default-set built-in devices from createTools; this reconciles dynamic
|
|
2609
|
+
// mounts (image-gen, TTS, startup MCP, active extension tools).
|
|
2458
2610
|
let initialMountedXdevToolNames: string[] = [];
|
|
2459
2611
|
if (toolSession.xdevRegistry) {
|
|
2460
2612
|
const topLevelToolNames: string[] = [];
|
|
2461
2613
|
const mountedTools: Tool[] = [];
|
|
2462
2614
|
for (const name of initialToolNames) {
|
|
2463
2615
|
const tool = toolRegistry.get(name);
|
|
2464
|
-
|
|
2616
|
+
const explicitlyRequested = explicitlyRequestedToolNameSet?.has(name) === true;
|
|
2617
|
+
if (tool && xdevReadAvailable && !explicitlyRequested && isMountableUnderXdev(tool))
|
|
2618
|
+
mountedTools.push(tool);
|
|
2465
2619
|
else topLevelToolNames.push(name);
|
|
2466
2620
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2621
|
+
const writeTransportAvailable = mountedTools.length === 0 || (await ensureWriteRegistered());
|
|
2622
|
+
if (writeTransportAvailable) {
|
|
2623
|
+
toolSession.xdevRegistry.reconcile(mountedTools);
|
|
2624
|
+
initialMountedXdevToolNames = mountedTools.map(tool => tool.name);
|
|
2625
|
+
initialToolNames = topLevelToolNames;
|
|
2626
|
+
if (initialMountedXdevToolNames.length > 0 && !initialToolNames.includes("write"))
|
|
2627
|
+
initialToolNames.push("write");
|
|
2628
|
+
} else {
|
|
2629
|
+
toolSession.xdevRegistry.reconcile([]);
|
|
2630
|
+
}
|
|
2470
2631
|
}
|
|
2471
2632
|
|
|
2472
2633
|
setActiveToolNames(initialToolNames);
|
|
@@ -2551,6 +2712,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2551
2712
|
const initialTools = initialToolNames
|
|
2552
2713
|
.map(name => toolRegistry.get(name))
|
|
2553
2714
|
.filter((tool): tool is AgentTool => tool !== undefined);
|
|
2715
|
+
const autoLearnCaptureTools = initialTools.filter(tool => tool.name === "manage_skill" || tool.name === "learn");
|
|
2554
2716
|
|
|
2555
2717
|
const openaiWebsocketSetting = settings.get("providers.openaiWebsockets") ?? "off";
|
|
2556
2718
|
const preferOpenAICodexWebsockets =
|
|
@@ -2577,6 +2739,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2577
2739
|
settings,
|
|
2578
2740
|
createSettingsAwareStreamFn(settings),
|
|
2579
2741
|
);
|
|
2742
|
+
const transformToolCallArguments = (args: Record<string, unknown>): Record<string, unknown> => {
|
|
2743
|
+
let result = args;
|
|
2744
|
+
const maxTimeout = settings.get("tools.maxTimeout");
|
|
2745
|
+
if (maxTimeout > 0 && typeof result.timeout === "number") {
|
|
2746
|
+
result = { ...result, timeout: Math.min(result.timeout, maxTimeout) };
|
|
2747
|
+
}
|
|
2748
|
+
if (obfuscator?.hasSecrets()) {
|
|
2749
|
+
result = deobfuscateToolArguments(obfuscator, result);
|
|
2750
|
+
}
|
|
2751
|
+
return result;
|
|
2752
|
+
};
|
|
2753
|
+
const kimiApiFormatSetting = settings.get("providers.kimiApiFormat");
|
|
2754
|
+
const kimiApiFormat = kimiApiFormatSetting === "auto" ? undefined : kimiApiFormatSetting;
|
|
2580
2755
|
agent = new Agent({
|
|
2581
2756
|
initialState: {
|
|
2582
2757
|
systemPrompt,
|
|
@@ -2610,7 +2785,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2610
2785
|
presencePenalty: settings.get("presencePenalty") >= 0 ? settings.get("presencePenalty") : undefined,
|
|
2611
2786
|
repetitionPenalty: settings.get("repetitionPenalty") >= 0 ? settings.get("repetitionPenalty") : undefined,
|
|
2612
2787
|
hideThinkingSummary: settings.get("omitThinking"),
|
|
2613
|
-
kimiApiFormat
|
|
2788
|
+
kimiApiFormat,
|
|
2614
2789
|
preferWebsockets: preferOpenAICodexWebsockets,
|
|
2615
2790
|
getToolContext: tc => toolContextStore.getContext(tc),
|
|
2616
2791
|
getApiKey: requestModel => modelRegistry.resolver(requestModel, agent.sessionId),
|
|
@@ -2629,17 +2804,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2629
2804
|
return settingsAwareStreamFn(streamModel, context, streamOptions);
|
|
2630
2805
|
},
|
|
2631
2806
|
cursorExecHandlers,
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
const maxTimeout = settings.get("tools.maxTimeout");
|
|
2635
|
-
if (maxTimeout > 0 && typeof result.timeout === "number") {
|
|
2636
|
-
result = { ...result, timeout: Math.min(result.timeout, maxTimeout) };
|
|
2637
|
-
}
|
|
2638
|
-
if (obfuscator?.hasSecrets()) {
|
|
2639
|
-
result = deobfuscateToolArguments(obfuscator, result);
|
|
2640
|
-
}
|
|
2641
|
-
return result;
|
|
2642
|
-
},
|
|
2807
|
+
getCursorTools: () => [...(toolSession.xdevRegistry?.list() ?? [])],
|
|
2808
|
+
transformToolCallArguments,
|
|
2643
2809
|
intentTracing: !!intentField,
|
|
2644
2810
|
pruneToolDescriptions: inlineToolDescriptors,
|
|
2645
2811
|
dialect: resolveDialect(settings.get("tools.format"), model),
|
|
@@ -2761,8 +2927,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2761
2927
|
getXdevToolEntries: () => toolSession.xdevRegistry?.entries() ?? [],
|
|
2762
2928
|
xdevRegistry: toolSession.xdevRegistry,
|
|
2763
2929
|
initialMountedXdevToolNames,
|
|
2764
|
-
|
|
2930
|
+
presentationPinnedToolNames: explicitlyRequestedToolNameSet,
|
|
2765
2931
|
setActiveToolNames,
|
|
2932
|
+
ensureWriteRegistered,
|
|
2766
2933
|
getMcpServerInstructions: mcpManager
|
|
2767
2934
|
? () => {
|
|
2768
2935
|
const raw = mcpManager.getServerInstructions();
|
|
@@ -2922,8 +3089,56 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2922
3089
|
});
|
|
2923
3090
|
};
|
|
2924
3091
|
|
|
2925
|
-
|
|
2926
|
-
|
|
3092
|
+
const runAutoLearnCapture = createAutoLearnCaptureRunner({
|
|
3093
|
+
sourceAgent: agent,
|
|
3094
|
+
captureTools: autoLearnCaptureTools,
|
|
3095
|
+
onPayload,
|
|
3096
|
+
onResponse,
|
|
3097
|
+
createAgent: captureOptions => {
|
|
3098
|
+
const captureModel = captureOptions.initialState?.model;
|
|
3099
|
+
const captureSessionId = captureOptions.sessionId;
|
|
3100
|
+
if (!captureModel || !captureSessionId) throw new Error("Auto-learn capture identity is incomplete");
|
|
3101
|
+
return new Agent({
|
|
3102
|
+
...captureOptions,
|
|
3103
|
+
cwd: sessionManager.getCwd(),
|
|
3104
|
+
cwdResolver: () => sessionManager.getCwd(),
|
|
3105
|
+
convertToLlm: convertToLlmFinal,
|
|
3106
|
+
transformContext: async messages => wrapSteeringForModel(messages),
|
|
3107
|
+
transformProviderContext: async (context, transformModel) => {
|
|
3108
|
+
const transformed = obfuscator ? obfuscateProviderContext(obfuscator, context) : context;
|
|
3109
|
+
return clampProviderContextImages(transformed, transformModel);
|
|
3110
|
+
},
|
|
3111
|
+
thinkingBudgets: agent.thinkingBudgets,
|
|
3112
|
+
temperature: agent.temperature,
|
|
3113
|
+
topP: agent.topP,
|
|
3114
|
+
topK: agent.topK,
|
|
3115
|
+
minP: agent.minP,
|
|
3116
|
+
presencePenalty: agent.presencePenalty,
|
|
3117
|
+
repetitionPenalty: agent.repetitionPenalty,
|
|
3118
|
+
serviceTierResolver: agent.serviceTierResolver,
|
|
3119
|
+
hideThinkingSummary: agent.hideThinkingSummary,
|
|
3120
|
+
maxRetryDelayMs: agent.maxRetryDelayMs,
|
|
3121
|
+
kimiApiFormat,
|
|
3122
|
+
preferWebsockets: preferOpenAICodexWebsockets,
|
|
3123
|
+
getToolContext: toolCall => toolContextStore.getContext(toolCall),
|
|
3124
|
+
streamFn: settingsAwareStreamFn,
|
|
3125
|
+
transformToolCallArguments,
|
|
3126
|
+
intentTracing: !!intentField,
|
|
3127
|
+
pruneToolDescriptions: inlineToolDescriptors,
|
|
3128
|
+
dialect: resolveDialect(settings.get("tools.format"), captureModel),
|
|
3129
|
+
abortOnFabricatedToolResult: settings.get("tools.abortOnFabricatedResult"),
|
|
3130
|
+
appendOnlyContext: shouldEnableAppendOnlyContext(
|
|
3131
|
+
settings.get("provider.appendOnlyContext"),
|
|
3132
|
+
captureModel,
|
|
3133
|
+
)
|
|
3134
|
+
? new AppendOnlyContextManager()
|
|
3135
|
+
: undefined,
|
|
3136
|
+
});
|
|
3137
|
+
},
|
|
3138
|
+
});
|
|
3139
|
+
|
|
3140
|
+
// Auto-learn can immediately trigger a private capture after the first real
|
|
3141
|
+
// stop. When a memory backend is selected, install that backend's
|
|
2927
3142
|
// per-session state first so the capture turn's `learn` tool observes the
|
|
2928
3143
|
// same initialized state as normal memory tools. Other sessions keep memory
|
|
2929
3144
|
// startup in the background to preserve the existing startup profile.
|
|
@@ -2936,11 +3151,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2936
3151
|
// and the tools; the fire-time re-check in `#onAgentEnd` still handles a
|
|
2937
3152
|
// mid-session DISABLE. The subscription lives for the session's lifetime; the
|
|
2938
3153
|
// reference is intentionally discarded (the listener retains it).
|
|
2939
|
-
if (
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
3154
|
+
if (!restrictToolNames) {
|
|
3155
|
+
if (settings.get("autolearn.enabled") && taskDepth === 0) {
|
|
3156
|
+
await logger.time("startMemoryStartupTask", startMemoryBackend);
|
|
3157
|
+
new AutoLearnController({
|
|
3158
|
+
session,
|
|
3159
|
+
settings,
|
|
3160
|
+
capture: content => session.runAutolearnCapture(signal => runAutoLearnCapture(content, signal)),
|
|
3161
|
+
});
|
|
3162
|
+
} else {
|
|
3163
|
+
void logger.time("startMemoryStartupTask", startMemoryBackend);
|
|
3164
|
+
}
|
|
2944
3165
|
}
|
|
2945
3166
|
|
|
2946
3167
|
// Wire MCP manager callbacks to session for reactive tool updates.
|