@oh-my-pi/pi-coding-agent 17.1.6 → 17.1.7
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 +23 -0
- package/dist/{CHANGELOG-x9zt79k8.md → CHANGELOG-5k4dq4g6.md} +23 -0
- package/dist/cli.js +3149 -3092
- package/dist/types/config/settings-schema.d.ts +16 -1
- package/dist/types/cursor.d.ts +2 -1
- package/dist/types/extensibility/extensions/runner.d.ts +4 -0
- package/dist/types/extensibility/shared-events.d.ts +18 -1
- package/dist/types/modes/components/custom-editor.d.ts +5 -0
- package/dist/types/session/agent-session-types.d.ts +5 -5
- package/dist/types/session/agent-session.d.ts +26 -1
- package/dist/types/session/model-controls.d.ts +1 -1
- package/dist/types/session/session-tools.d.ts +44 -6
- package/dist/types/session/streaming-output.d.ts +7 -2
- package/dist/types/session/turn-recovery.d.ts +1 -1
- package/dist/types/tools/index.d.ts +8 -3
- package/dist/types/tools/output-meta.d.ts +5 -0
- package/dist/types/tools/read.d.ts +9 -1
- package/dist/types/tools/xdev.d.ts +53 -67
- package/dist/types/utils/cpuprofile.d.ts +51 -0
- package/dist/types/utils/inspect-image-mode.d.ts +29 -0
- package/dist/types/utils/profile-tree.d.ts +47 -0
- package/dist/types/utils/sample-profile.d.ts +67 -0
- package/package.json +12 -12
- package/src/config/settings-schema.ts +15 -1
- package/src/config/settings.ts +35 -0
- package/src/cursor.ts +4 -3
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
- package/src/extensibility/extensions/runner.ts +26 -0
- package/src/extensibility/extensions/wrapper.ts +74 -42
- package/src/extensibility/hooks/tool-wrapper.ts +11 -4
- package/src/extensibility/shared-events.ts +18 -1
- package/src/modes/components/custom-editor.ts +39 -16
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +8 -7
- package/src/modes/controllers/extension-ui-controller.ts +4 -4
- package/src/modes/controllers/selector-controller.ts +7 -2
- package/src/sdk.ts +47 -50
- package/src/session/agent-session-types.ts +5 -5
- package/src/session/agent-session.ts +123 -7
- package/src/session/model-controls.ts +5 -5
- package/src/session/session-listing.ts +66 -4
- package/src/session/session-tools.ts +158 -52
- package/src/session/streaming-output.ts +18 -6
- package/src/session/turn-recovery.ts +4 -4
- package/src/slash-commands/builtin-registry.ts +69 -0
- package/src/tools/bash.ts +16 -9
- package/src/tools/index.ts +36 -16
- package/src/tools/output-meta.ts +20 -0
- package/src/tools/read.ts +87 -13
- package/src/tools/write.ts +16 -7
- package/src/tools/xdev.ts +198 -210
- package/src/utils/cpuprofile.ts +235 -0
- package/src/utils/inspect-image-mode.ts +39 -0
- package/src/utils/profile-tree.ts +111 -0
- package/src/utils/sample-profile.ts +437 -0
|
@@ -71,6 +71,14 @@ function buildMatchKeys(keys: readonly KeyId[]): Set<string> {
|
|
|
71
71
|
return matchKeys;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function unionOfMatchKeys(matchKeys: ReadonlyMap<ConfigurableEditorAction, ReadonlySet<string>>): Set<string> {
|
|
75
|
+
const union = new Set<string>();
|
|
76
|
+
for (const keys of matchKeys.values()) {
|
|
77
|
+
for (const key of keys) union.add(key);
|
|
78
|
+
}
|
|
79
|
+
return union;
|
|
80
|
+
}
|
|
81
|
+
|
|
74
82
|
const BRACKETED_PASTE_START = "\x1b[200~";
|
|
75
83
|
const BRACKETED_PASTE_END = "\x1b[201~";
|
|
76
84
|
const BRACKETED_IMAGE_PATH_REGEX = /\.(?:png|jpe?g|gif|webp)$/i;
|
|
@@ -419,6 +427,17 @@ export class CustomEditor extends Editor {
|
|
|
419
427
|
this.pendingImageLinks = [];
|
|
420
428
|
}
|
|
421
429
|
|
|
430
|
+
/** Replace the composer draft with a restored historical prompt: sets the text and
|
|
431
|
+
* re-attaches the message's images so positional `[Image #N]` markers resolve on
|
|
432
|
+
* resubmit instead of degrading to literal text (esc-esc branch, `/tree`). Source
|
|
433
|
+
* links are unknown for restored drafts, so every link slot is `undefined`. */
|
|
434
|
+
setDraft(text: string, images?: readonly ImageContent[]): void {
|
|
435
|
+
this.setText(text);
|
|
436
|
+
this.imageLinks = undefined;
|
|
437
|
+
this.pendingImages = images ? [...images] : [];
|
|
438
|
+
this.pendingImageLinks = images ? images.map(() => undefined) : [];
|
|
439
|
+
}
|
|
440
|
+
|
|
422
441
|
/** Treat image/paste markers as indivisible: a stray backspace deletes the whole token
|
|
423
442
|
* instead of corrupting `[Paste #1, +30 lines]` into plain text. */
|
|
424
443
|
override atomicTokenPattern = PLACEHOLDER_REGEX;
|
|
@@ -600,14 +619,14 @@ export class CustomEditor extends Editor {
|
|
|
600
619
|
buildMatchKeys(keys),
|
|
601
620
|
]),
|
|
602
621
|
);
|
|
622
|
+
/** Union of every action's match keys: one probe in `handleInput` decides
|
|
623
|
+
* whether the per-action interception chain can match at all. */
|
|
624
|
+
#actionMatchKeyUnion = unionOfMatchKeys(this.#actionMatchKeys);
|
|
603
625
|
|
|
604
626
|
setActionKeys(action: ConfigurableEditorAction, keys: KeyId[]): void {
|
|
605
627
|
this.#actionKeys.set(action, [...keys]);
|
|
606
|
-
this.#
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
#rebuildActionMatchKeys(action: ConfigurableEditorAction): void {
|
|
610
|
-
this.#actionMatchKeys.set(action, buildMatchKeys(this.#actionKeys.get(action) ?? []));
|
|
628
|
+
this.#actionMatchKeys.set(action, buildMatchKeys(keys));
|
|
629
|
+
this.#actionMatchKeyUnion = unionOfMatchKeys(this.#actionMatchKeys);
|
|
611
630
|
}
|
|
612
631
|
|
|
613
632
|
#rebuildCustomMatchKeys(): void {
|
|
@@ -760,8 +779,10 @@ export class CustomEditor extends Editor {
|
|
|
760
779
|
this.#pendingInput.push(data);
|
|
761
780
|
return;
|
|
762
781
|
}
|
|
763
|
-
|
|
764
|
-
|
|
782
|
+
// textEquals avoids getText()'s O(buffer) join on every keystroke; kitty
|
|
783
|
+
// sequences always start with ESC, so plain bytes skip the native parse.
|
|
784
|
+
const hadBareQueuePrefix = this.textEquals("->") || this.textEquals("=>");
|
|
785
|
+
const kittyParsed = data.charCodeAt(0) === 0x1b ? parseKittySequence(data) : null;
|
|
765
786
|
if (kittyParsed && (kittyParsed.modifier & 64) !== 0 && this.onCapsLock) {
|
|
766
787
|
// Caps Lock is modifier bit 64
|
|
767
788
|
this.onCapsLock();
|
|
@@ -823,7 +844,12 @@ export class CustomEditor extends Editor {
|
|
|
823
844
|
// Space-hold push-to-talk: a sustained space bar starts/stops STT instead of typing spaces.
|
|
824
845
|
if (this.#handleSpaceHold(data, canonical)) return;
|
|
825
846
|
|
|
826
|
-
|
|
847
|
+
// One union probe decides whether any per-action interception below can
|
|
848
|
+
// match — plain typing then skips the ~20 per-action set lookups per key.
|
|
849
|
+
if (
|
|
850
|
+
canonical !== undefined &&
|
|
851
|
+
(this.#actionMatchKeyUnion.has(canonical) || this.#customMatchKeys.has(canonical))
|
|
852
|
+
) {
|
|
827
853
|
// Intercept configured image paste (async - fires and handles result)
|
|
828
854
|
if (this.#matchesAction(canonical, "app.clipboard.pasteImage") && this.onPasteImage) {
|
|
829
855
|
void this.onPasteImage();
|
|
@@ -964,14 +990,11 @@ export class CustomEditor extends Editor {
|
|
|
964
990
|
|
|
965
991
|
// Pass to parent for normal handling
|
|
966
992
|
super.handleInput(data);
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
cursor.col === 2
|
|
973
|
-
) {
|
|
974
|
-
this.insertText("\n");
|
|
993
|
+
if (!hadBareQueuePrefix && (this.textEquals("->") || this.textEquals("=>"))) {
|
|
994
|
+
const cursor = this.getCursor();
|
|
995
|
+
if (cursor.line === 0 && cursor.col === 2) {
|
|
996
|
+
this.insertText("\n");
|
|
997
|
+
}
|
|
975
998
|
}
|
|
976
999
|
}
|
|
977
1000
|
|
|
@@ -22,4 +22,5 @@ Press ← ← to drill into a running or finished agent and inspect its tool cal
|
|
|
22
22
|
Hit a Codex rate limit? `/usage reset` spends a saved reset credit to immediately restore your quota
|
|
23
23
|
No native tool_calling? Inference provider botches parsing them? `PI_DIALECT=glm|kimi|anthropic…` rolls it locally for them!
|
|
24
24
|
Turn on `/advisor` to attach a second model that reviews every turn and quietly injects advice
|
|
25
|
-
Try starting your prompt with a ->, and writing a list (1. Do X, 2. Do Y)
|
|
25
|
+
Try starting your prompt with a ->, and writing a list (1. Do X, 2. Do Y)
|
|
26
|
+
Press shift+tab to cycle through reasoning effort levels
|
|
@@ -25,6 +25,7 @@ import { isWaitingPollDetails } from "../../tools/hub";
|
|
|
25
25
|
import { formatStatusIcon, replaceTabs, resolveImageOptions } from "../../tools/render-utils";
|
|
26
26
|
import { type FirstResultViewportRepaint, toolRenderers } from "../../tools/renderers";
|
|
27
27
|
import { TODO_STRIKE_TOTAL_FRAMES, type TodoToolDetails } from "../../tools/todo";
|
|
28
|
+
import type { XdevState } from "../../tools/xdev";
|
|
28
29
|
import { isFramedBlockComponent, markFramedBlockComponent, renderStatusLine, WidthAwareText } from "../../tui";
|
|
29
30
|
import { sanitizeWithOptionalSixelPassthrough } from "../../utils/sixel";
|
|
30
31
|
import { renderDiff } from "./diff";
|
|
@@ -1316,13 +1317,13 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
1316
1317
|
}
|
|
1317
1318
|
context.renderDiff = renderDiff;
|
|
1318
1319
|
} else if (this.#toolName === "write") {
|
|
1319
|
-
// Device-dispatch previews
|
|
1320
|
-
|
|
1321
|
-
const
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1320
|
+
// Device-dispatch previews resolve renderers from the canonical tool map.
|
|
1321
|
+
const writeTool = this.#tool as { session?: { xdev?: XdevState } } | undefined;
|
|
1322
|
+
const xdev = writeTool?.session?.xdev;
|
|
1323
|
+
if (xdev) {
|
|
1324
|
+
context.resolveXdevMounted = (name: string) =>
|
|
1325
|
+
xdev.mountedNames.has(name) ? xdev.tools.get(name) : undefined;
|
|
1326
|
+
}
|
|
1326
1327
|
}
|
|
1327
1328
|
|
|
1328
1329
|
return context;
|
|
@@ -246,7 +246,7 @@ export class ExtensionUiController {
|
|
|
246
246
|
// Update UI
|
|
247
247
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
248
248
|
await this.ctx.reloadTodos();
|
|
249
|
-
this.ctx.editor.
|
|
249
|
+
this.ctx.editor.setDraft(result.selectedText, result.selectedImages);
|
|
250
250
|
this.ctx.showStatus("Branched to new session");
|
|
251
251
|
|
|
252
252
|
return { cancelled: false };
|
|
@@ -261,7 +261,7 @@ export class ExtensionUiController {
|
|
|
261
261
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
262
262
|
await this.ctx.reloadTodos();
|
|
263
263
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
264
|
-
this.ctx.editor.
|
|
264
|
+
this.ctx.editor.setDraft(result.editorText, result.editorImages);
|
|
265
265
|
}
|
|
266
266
|
this.ctx.showStatus("Navigated to selected point");
|
|
267
267
|
|
|
@@ -476,7 +476,7 @@ export class ExtensionUiController {
|
|
|
476
476
|
// Update UI
|
|
477
477
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
478
478
|
await this.ctx.reloadTodos();
|
|
479
|
-
this.ctx.editor.
|
|
479
|
+
this.ctx.editor.setDraft(result.selectedText, result.selectedImages);
|
|
480
480
|
this.ctx.showStatus("Branched to new session");
|
|
481
481
|
|
|
482
482
|
return { cancelled: false };
|
|
@@ -491,7 +491,7 @@ export class ExtensionUiController {
|
|
|
491
491
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
492
492
|
await this.ctx.reloadTodos();
|
|
493
493
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
494
|
-
this.ctx.editor.
|
|
494
|
+
this.ctx.editor.setDraft(result.editorText, result.editorImages);
|
|
495
495
|
}
|
|
496
496
|
this.ctx.showStatus("Navigated to selected point");
|
|
497
497
|
|
|
@@ -455,6 +455,11 @@ export class SelectorController {
|
|
|
455
455
|
this.ctx.showError(`Failed to apply memory backend: ${err}`);
|
|
456
456
|
});
|
|
457
457
|
break;
|
|
458
|
+
case "inspect_image.mode":
|
|
459
|
+
void this.ctx.session.applyInspectImageModeChange().catch(err => {
|
|
460
|
+
this.ctx.showError(`Failed to apply vision mode: ${err}`);
|
|
461
|
+
});
|
|
462
|
+
break;
|
|
458
463
|
|
|
459
464
|
case "autocompleteMaxVisible":
|
|
460
465
|
this.ctx.editor.setAutocompleteMaxVisible(typeof value === "number" ? value : Number(value));
|
|
@@ -1097,7 +1102,7 @@ export class SelectorController {
|
|
|
1097
1102
|
}
|
|
1098
1103
|
|
|
1099
1104
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
1100
|
-
this.ctx.editor.
|
|
1105
|
+
this.ctx.editor.setDraft(result.selectedText, result.selectedImages);
|
|
1101
1106
|
done();
|
|
1102
1107
|
this.ctx.showStatus("Branched to new session");
|
|
1103
1108
|
},
|
|
@@ -1272,7 +1277,7 @@ export class SelectorController {
|
|
|
1272
1277
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
1273
1278
|
await this.ctx.reloadTodos();
|
|
1274
1279
|
if (result.editorText && !this.ctx.editor.getText().trim()) {
|
|
1275
|
-
this.ctx.editor.
|
|
1280
|
+
this.ctx.editor.setDraft(result.editorText, result.editorImages);
|
|
1276
1281
|
}
|
|
1277
1282
|
this.ctx.showStatus("Navigated to selected point");
|
|
1278
1283
|
|
package/src/sdk.ts
CHANGED
|
@@ -190,13 +190,17 @@ import {
|
|
|
190
190
|
HIDDEN_TOOLS,
|
|
191
191
|
isMountableUnderXdev,
|
|
192
192
|
type LspStartupServerInfo,
|
|
193
|
+
listXdevTools,
|
|
193
194
|
ReadTool,
|
|
194
195
|
releaseComputerSessionsForOwner,
|
|
196
|
+
resolveMountedXdevExecutable,
|
|
195
197
|
type Tool,
|
|
196
198
|
type ToolSession,
|
|
197
199
|
WebSearchTool,
|
|
198
200
|
WriteTool,
|
|
199
201
|
warmupLspServers,
|
|
202
|
+
xdevDocsAll,
|
|
203
|
+
xdevEntries,
|
|
200
204
|
} from "./tools";
|
|
201
205
|
import { isMCPToolName, normalizeToolNames } from "./tools/builtin-names";
|
|
202
206
|
import { ToolContextStore } from "./tools/context";
|
|
@@ -1620,6 +1624,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1620
1624
|
// mutation (any tool) bumped it in the meantime.
|
|
1621
1625
|
const fileMutationVersions = new Map<string, number>();
|
|
1622
1626
|
const activeToolNames = new Set<string>();
|
|
1627
|
+
const toolRegistry = new Map<string, Tool>();
|
|
1623
1628
|
const setActiveToolNames = (names: Iterable<string>): void => {
|
|
1624
1629
|
activeToolNames.clear();
|
|
1625
1630
|
for (const name of names) {
|
|
@@ -1632,6 +1637,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1632
1637
|
},
|
|
1633
1638
|
isToolActive: name => activeToolNames.has(name),
|
|
1634
1639
|
setActiveToolNames,
|
|
1640
|
+
toolRegistry,
|
|
1635
1641
|
hasUI: options.hasUI ?? false,
|
|
1636
1642
|
get additionalDirectories() {
|
|
1637
1643
|
return sessionManager.getAdditionalDirectories();
|
|
@@ -1682,6 +1688,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1682
1688
|
getModelString: () => (hasExplicitModel && model ? formatModelString(model) : undefined),
|
|
1683
1689
|
getActiveModelString,
|
|
1684
1690
|
getActiveModel: () => agent?.state.model ?? model,
|
|
1691
|
+
getInspectImageModeOverride: () => session?.getInspectImageModeOverride(),
|
|
1685
1692
|
getServiceTierByFamily: () => session?.serviceTierByFamily,
|
|
1686
1693
|
getImageAttachments: () => session?.getImageAttachments() ?? [],
|
|
1687
1694
|
getPlanModeState: () => session?.getPlanModeState(),
|
|
@@ -1776,7 +1783,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1776
1783
|
);
|
|
1777
1784
|
|
|
1778
1785
|
// Create built-in tools (already wrapped with meta notice formatting)
|
|
1779
|
-
|
|
1786
|
+
await logger.time("createAllTools", createTools, toolSession, options.toolNames);
|
|
1780
1787
|
|
|
1781
1788
|
// Restricted sessions cannot inherit or discover MCP capabilities.
|
|
1782
1789
|
const enableMCP = !restrictToolNames && (options.enableMCP ?? true);
|
|
@@ -1868,7 +1875,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1868
1875
|
// to mirror the AsyncJobManager ownership rule.
|
|
1869
1876
|
if (mcpManager && !options.parentTaskPrefix) MCPManager.setInstance(mcpManager);
|
|
1870
1877
|
|
|
1871
|
-
const builtInToolNames =
|
|
1878
|
+
const builtInToolNames = [...toolRegistry.keys()];
|
|
1872
1879
|
let customToolPaths: ToolPathWithSource[] = [];
|
|
1873
1880
|
const inlineExtensions: ExtensionFactory[] = [];
|
|
1874
1881
|
if (!restrictToolNames) {
|
|
@@ -2549,12 +2556,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2549
2556
|
);
|
|
2550
2557
|
|
|
2551
2558
|
// All built-in tools are active (conditional tools like git/ask return null from factory if disabled)
|
|
2552
|
-
const builtInRegistryToolNames = new Set
|
|
2553
|
-
const toolRegistry = new Map<string, Tool>();
|
|
2554
|
-
for (const tool of builtinTools) {
|
|
2555
|
-
toolRegistry.set(tool.name, tool);
|
|
2556
|
-
builtInRegistryToolNames.add(tool.name);
|
|
2557
|
-
}
|
|
2559
|
+
const builtInRegistryToolNames = toolSession.xdev?.builtInNames ?? new Set(toolRegistry.keys());
|
|
2558
2560
|
if (!restrictToolNames && !toolRegistry.has("goal") && settings.get("goal.enabled")) {
|
|
2559
2561
|
const goalTool = await logger.time("createTools:goal:session", HIDDEN_TOOLS.goal, toolSession);
|
|
2560
2562
|
if (goalTool) {
|
|
@@ -2606,31 +2608,26 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2606
2608
|
// Existing staged/device paths need write registered before active-set assembly.
|
|
2607
2609
|
// Deferred MCP also registers it now, but refresh activates it only after a server connects.
|
|
2608
2610
|
const hasDeferrableTools = Array.from(toolRegistry.values()).some(tool => tool.deferrable === true);
|
|
2609
|
-
const hasXdevTools = (toolSession.
|
|
2611
|
+
const hasXdevTools = (toolSession.xdev?.mountedNames.size ?? 0) > 0;
|
|
2610
2612
|
const planModeAvailable = settings.get("plan.enabled");
|
|
2611
2613
|
if (!restrictToolNames && (hasDeferrableTools || hasXdevTools || planModeAvailable || deferMCPDiscoveryForUI)) {
|
|
2612
2614
|
await ensureWriteRegistered();
|
|
2613
2615
|
}
|
|
2614
2616
|
|
|
2615
2617
|
let cursorEventEmitter: ((event: AgentEvent) => void) | undefined;
|
|
2616
|
-
//
|
|
2617
|
-
//
|
|
2618
|
-
//
|
|
2619
|
-
//
|
|
2620
|
-
// tier gate, but Cursor invokes advertised devices via `tool.execute()`
|
|
2621
|
-
// directly, and the agent loop's fallback resolver executes mounted
|
|
2622
|
-
// devices the model called by their top-level name — so wrap unwrapped
|
|
2623
|
-
// devices here to keep the approval/deny/prompt gate. Dynamic mounts
|
|
2624
|
-
// (custom/MCP) already come from the wrapped registry.
|
|
2618
|
+
// Cursor and the agent loop may call a mounted device by its top-level
|
|
2619
|
+
// name. Resolve that name from the canonical map and apply the same
|
|
2620
|
+
// execution-only ACP decorator used by `write xd://<tool>`; docs and
|
|
2621
|
+
// renderer lookup continue to use the undecorated canonical instance.
|
|
2625
2622
|
const resolveDeviceTool = (name: string): AgentTool | undefined => {
|
|
2626
|
-
const
|
|
2627
|
-
if (!
|
|
2628
|
-
return
|
|
2623
|
+
const state = toolSession.xdev;
|
|
2624
|
+
if (!state) return undefined;
|
|
2625
|
+
return resolveMountedXdevExecutable(state, name);
|
|
2629
2626
|
};
|
|
2630
2627
|
const cursorExecHandlers = new CursorExecHandlers({
|
|
2631
2628
|
cwd,
|
|
2632
2629
|
tools: toolRegistry,
|
|
2633
|
-
|
|
2630
|
+
getExecutableTool: resolveDeviceTool,
|
|
2634
2631
|
getToolContext: () => toolContextStore.getContext(),
|
|
2635
2632
|
emitEvent: event => cursorEventEmitter?.(event),
|
|
2636
2633
|
getTodoPhases: () => session.getTodoPhases(),
|
|
@@ -2680,7 +2677,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2680
2677
|
if (memoryInstructions) appendParts.push(memoryInstructions);
|
|
2681
2678
|
if (autoLearnInstructions) appendParts.push(autoLearnInstructions);
|
|
2682
2679
|
const projection = projectMountedMCPXdevGuidance(
|
|
2683
|
-
collectMountedMCPToolRoutes(toolSession.
|
|
2680
|
+
collectMountedMCPToolRoutes(toolSession.xdev ? listXdevTools(toolSession.xdev) : []),
|
|
2684
2681
|
);
|
|
2685
2682
|
if (projection.mappings.length > 0 || projection.hasOmittedMappings) {
|
|
2686
2683
|
appendParts.push(
|
|
@@ -2723,13 +2720,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2723
2720
|
const defaultPrompt = await buildSystemPromptInternal({
|
|
2724
2721
|
cwd,
|
|
2725
2722
|
additionalWorkspaceRoots: sessionManager.getAdditionalDirectories(),
|
|
2726
|
-
xdevTools: toolSession.
|
|
2727
|
-
xdevDocs:
|
|
2728
|
-
toolSession.
|
|
2729
|
-
|
|
2730
|
-
settings.get("tools.xdevInlineDevices"),
|
|
2731
|
-
) ?? "",
|
|
2732
|
-
autoQaEnabled: !restrictToolNames && isAutoQaEnabled(settings),
|
|
2723
|
+
xdevTools: toolSession.xdev ? xdevEntries(toolSession.xdev) : [],
|
|
2724
|
+
xdevDocs: toolSession.xdev
|
|
2725
|
+
? xdevDocsAll(toolSession.xdev, settings.get("tools.xdevDocs"), settings.get("tools.xdevInlineDevices"))
|
|
2726
|
+
: "",
|
|
2733
2727
|
resolvedCustomPrompt: options.customSystemPrompt,
|
|
2734
2728
|
skills: session?.skills ?? skills,
|
|
2735
2729
|
contextFiles,
|
|
@@ -2747,6 +2741,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2747
2741
|
taskBatch: settings.get("task.batch"),
|
|
2748
2742
|
taskMaxConcurrency: settings.get("task.maxConcurrency"),
|
|
2749
2743
|
taskIrcEnabled: !restrictToolNames && isIrcEnabled(settings, options.taskDepth ?? 0),
|
|
2744
|
+
autoQaEnabled: !restrictToolNames && isAutoQaEnabled(settings),
|
|
2750
2745
|
secretsEnabled,
|
|
2751
2746
|
workspaceTree: workspaceTreePromise,
|
|
2752
2747
|
includeWorkspaceTree,
|
|
@@ -2853,31 +2848,24 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2853
2848
|
// constructed and attached. Startup failure therefore leaves it revivable.
|
|
2854
2849
|
hasRegistered = options.expectedAgentRef === undefined || options.expectedAgentRef === null;
|
|
2855
2850
|
|
|
2856
|
-
// Partition the initial enabled set for the xd:// transport
|
|
2857
|
-
//
|
|
2858
|
-
|
|
2859
|
-
// default-set built-in devices from createTools; this reconciles dynamic
|
|
2860
|
-
// mounts (image-gen, TTS, startup MCP, active extension tools).
|
|
2861
|
-
let initialMountedXdevToolNames: string[] = [];
|
|
2862
|
-
if (toolSession.xdevRegistry) {
|
|
2851
|
+
// Partition the initial enabled set for the xd:// transport. Tool instances
|
|
2852
|
+
// remain in the canonical map; only presentation names move between layers.
|
|
2853
|
+
if (toolSession.xdev) {
|
|
2863
2854
|
const topLevelToolNames: string[] = [];
|
|
2864
|
-
const
|
|
2855
|
+
const mountedNames: string[] = [];
|
|
2865
2856
|
for (const name of initialToolNames) {
|
|
2866
2857
|
const tool = toolRegistry.get(name);
|
|
2867
2858
|
const explicitlyRequested = explicitlyRequestedToolNameSet?.has(name) === true;
|
|
2868
2859
|
if (tool && xdevReadAvailable && !explicitlyRequested && isMountableUnderXdev(tool))
|
|
2869
|
-
|
|
2860
|
+
mountedNames.push(name);
|
|
2870
2861
|
else topLevelToolNames.push(name);
|
|
2871
2862
|
}
|
|
2872
|
-
const writeTransportAvailable =
|
|
2863
|
+
const writeTransportAvailable = mountedNames.length === 0 || (await ensureWriteRegistered());
|
|
2864
|
+
toolSession.xdev.mountedNames.clear();
|
|
2873
2865
|
if (writeTransportAvailable) {
|
|
2874
|
-
toolSession.
|
|
2875
|
-
initialMountedXdevToolNames = mountedTools.map(tool => tool.name);
|
|
2866
|
+
for (const name of mountedNames) toolSession.xdev.mountedNames.add(name);
|
|
2876
2867
|
initialToolNames = topLevelToolNames;
|
|
2877
|
-
if (
|
|
2878
|
-
initialToolNames.push("write");
|
|
2879
|
-
} else {
|
|
2880
|
-
toolSession.xdevRegistry.reconcile([]);
|
|
2868
|
+
if (mountedNames.length > 0 && !initialToolNames.includes("write")) initialToolNames.push("write");
|
|
2881
2869
|
}
|
|
2882
2870
|
}
|
|
2883
2871
|
|
|
@@ -3055,7 +3043,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3055
3043
|
return settingsAwareStreamFn(streamModel, context, streamOptions);
|
|
3056
3044
|
},
|
|
3057
3045
|
cursorExecHandlers,
|
|
3058
|
-
getCursorTools: () =>
|
|
3046
|
+
getCursorTools: () => (toolSession.xdev ? listXdevTools(toolSession.xdev) : []),
|
|
3059
3047
|
transformToolCallArguments,
|
|
3060
3048
|
resolveFallbackTool: resolveDeviceTool,
|
|
3061
3049
|
intentTracing: !!intentField,
|
|
@@ -3111,6 +3099,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3111
3099
|
return id ? `${id}-advisor` : null;
|
|
3112
3100
|
},
|
|
3113
3101
|
getAgentId: () => "advisor",
|
|
3102
|
+
// The primary's availability signals are wrong for advisors: their tool
|
|
3103
|
+
// slate is filtered separately at runtime (default read/grep/glob, no
|
|
3104
|
+
// write transport), so xd:// devices are unreachable and read must never
|
|
3105
|
+
// advertise inspect_image — images are inlined, and the provider
|
|
3106
|
+
// boundary handles text-only advisor models.
|
|
3107
|
+
xdev: undefined,
|
|
3108
|
+
isToolActive: name => name !== "inspect_image" && toolSession.isToolActive?.(name) === true,
|
|
3114
3109
|
};
|
|
3115
3110
|
const advisorToolBuilds: Array<Tool | null | Promise<Tool | null>> = [];
|
|
3116
3111
|
for (const name in BUILTIN_TOOLS) {
|
|
@@ -3178,6 +3173,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3178
3173
|
createComputerTool: restrictToolNames
|
|
3179
3174
|
? undefined
|
|
3180
3175
|
: async () => (await BUILTIN_TOOLS.computer(toolSession)) ?? null,
|
|
3176
|
+
createInspectImageTool: restrictToolNames
|
|
3177
|
+
? undefined
|
|
3178
|
+
: async () => (await BUILTIN_TOOLS.inspect_image(toolSession)) ?? null,
|
|
3181
3179
|
createVibeTools:
|
|
3182
3180
|
(options.taskDepth ?? 0) === 0 && !options.parentTaskPrefix
|
|
3183
3181
|
? () => createVibeTools(toolSession)
|
|
@@ -3192,9 +3190,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3192
3190
|
preferWebsockets: preferOpenAICodexWebsockets,
|
|
3193
3191
|
convertToLlm: convertToLlmFinal,
|
|
3194
3192
|
rebuildSystemPrompt,
|
|
3195
|
-
getXdevToolEntries: () => toolSession.
|
|
3196
|
-
|
|
3197
|
-
initialMountedXdevToolNames,
|
|
3193
|
+
getXdevToolEntries: () => (toolSession.xdev ? xdevEntries(toolSession.xdev) : []),
|
|
3194
|
+
xdev: toolSession.xdev,
|
|
3198
3195
|
presentationPinnedToolNames: explicitlyRequestedToolNameSet,
|
|
3199
3196
|
setActiveToolNames,
|
|
3200
3197
|
ensureWriteRegistered,
|
|
@@ -26,7 +26,7 @@ import type { Skill, SkillWarning } from "../extensibility/skills";
|
|
|
26
26
|
import type { FileSlashCommand } from "../extensibility/slash-commands";
|
|
27
27
|
import type { SecretObfuscator } from "../secrets/obfuscator";
|
|
28
28
|
import type { ConfiguredThinkingLevel } from "../thinking";
|
|
29
|
-
import type {
|
|
29
|
+
import type { XdevState } from "../tools/xdev";
|
|
30
30
|
import type { SessionManager } from "./session-manager";
|
|
31
31
|
|
|
32
32
|
/** Maximum time the interactive shutdown path waits for Mnemopi consolidation. */
|
|
@@ -139,6 +139,8 @@ export interface AgentSessionConfig {
|
|
|
139
139
|
createMemoryTools?: () => Promise<AgentTool[]>;
|
|
140
140
|
/** Creates the built-in `computer` tool for session-scoped runtime enablement (see {@link AgentSession.setComputerToolEnabled}). */
|
|
141
141
|
createComputerTool?: () => Promise<AgentTool | null>;
|
|
142
|
+
/** Creates the built-in `inspect_image` tool for session-scoped runtime enablement (see {@link AgentSession.setInspectImageMode}). */
|
|
143
|
+
createInspectImageTool?: () => Promise<AgentTool | null>;
|
|
142
144
|
/** Model registry for API key resolution and model discovery. */
|
|
143
145
|
modelRegistry: ModelRegistry;
|
|
144
146
|
/** Tool registry for LSP and settings. */
|
|
@@ -177,10 +179,8 @@ export interface AgentSessionConfig {
|
|
|
177
179
|
getLocalCalendarDate?: () => string;
|
|
178
180
|
/** Tools mounted under `xd://`, for `/tools` display. */
|
|
179
181
|
getXdevToolEntries?: () => Array<{ name: string; summary: string }>;
|
|
180
|
-
/**
|
|
181
|
-
|
|
182
|
-
/** Discoverable tools mounted under `xd://` in the initial enabled set. */
|
|
183
|
-
initialMountedXdevToolNames?: string[];
|
|
182
|
+
/** `xd://` presentation state backed by the canonical tool map. */
|
|
183
|
+
xdev?: XdevState;
|
|
184
184
|
/** Names pinned top-level during runtime repartitioning. */
|
|
185
185
|
presentationPinnedToolNames?: ReadonlySet<string>;
|
|
186
186
|
/** Accessor for live MCP server instructions. */
|