@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
package/src/tools/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ import { TaskTool } from "../task";
|
|
|
32
32
|
import type { AgentOutputManager } from "../task/output-manager";
|
|
33
33
|
import { canSpawnAtDepth, type StructuredSubagentSchemaMode } from "../task/types";
|
|
34
34
|
import type { EventBus } from "../utils/event-bus";
|
|
35
|
+
import { type InspectImageMode, isInspectImageToolActive } from "../utils/inspect-image-mode";
|
|
35
36
|
import { WebSearchTool } from "../web/search";
|
|
36
37
|
import type { WorkspaceTree } from "../workspace-tree";
|
|
37
38
|
import { AskTool } from "./ask";
|
|
@@ -61,7 +62,7 @@ import { ReadTool } from "./read";
|
|
|
61
62
|
import type { PlanProposalHandler } from "./resolve";
|
|
62
63
|
import { type TodoPhase, TodoTool } from "./todo";
|
|
63
64
|
import { WriteTool } from "./write";
|
|
64
|
-
import { isMountableUnderXdev,
|
|
65
|
+
import { isMountableUnderXdev, type XdevState } from "./xdev";
|
|
65
66
|
import { YieldTool } from "./yield";
|
|
66
67
|
|
|
67
68
|
export * from "../edit";
|
|
@@ -243,8 +244,10 @@ export interface ToolSession {
|
|
|
243
244
|
isToolActive?: (name: string) => boolean;
|
|
244
245
|
/** Update the active built-in tool predicate when a session changes tools mid-run. */
|
|
245
246
|
setActiveToolNames?: (names: Iterable<string>) => void;
|
|
246
|
-
/**
|
|
247
|
-
|
|
247
|
+
/** Canonical map containing every registered tool exactly once. */
|
|
248
|
+
toolRegistry?: Map<string, Tool>;
|
|
249
|
+
/** `xd://` presentation state backed by {@link toolRegistry}. */
|
|
250
|
+
xdev?: XdevState;
|
|
248
251
|
/** Agent registry for IRC routing across live sessions. */
|
|
249
252
|
agentRegistry?: AgentRegistry;
|
|
250
253
|
/** Idle→parked→revive lifecycle owner; lets the hub kill a non-job-backed agent registration. Default: AgentLifecycleManager.global(). */
|
|
@@ -263,6 +266,8 @@ export interface ToolSession {
|
|
|
263
266
|
getActiveModelString?: () => string | undefined;
|
|
264
267
|
/** Get the current session model object (provider/api capabilities), regardless of how it was chosen. */
|
|
265
268
|
getActiveModel?: () => Model | undefined;
|
|
269
|
+
/** Session-scoped inspect_image mode override set by `/vision`; wins over the persisted setting. */
|
|
270
|
+
getInspectImageModeOverride?: () => InspectImageMode | undefined;
|
|
266
271
|
/** Get the session's live per-family service tiers (undefined = none). Source of truth for subagent `tier.subagent: inherit`. */
|
|
267
272
|
getServiceTierByFamily?: () => ServiceTierByFamily | undefined;
|
|
268
273
|
/** Auth storage for passing to subagents (avoids re-discovery) */
|
|
@@ -555,7 +560,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
555
560
|
if (name === "github") return session.settings.get("github.enabled");
|
|
556
561
|
if (name === "ast_grep") return session.settings.get("astGrep.enabled");
|
|
557
562
|
if (name === "ast_edit") return session.settings.get("astEdit.enabled");
|
|
558
|
-
if (name === "inspect_image") return session
|
|
563
|
+
if (name === "inspect_image") return isInspectImageToolActive(session);
|
|
559
564
|
if (name === "web_search") return session.settings.get("web_search.enabled");
|
|
560
565
|
if (name === "ask") return session.settings.get("ask.enabled");
|
|
561
566
|
if (name === "browser") return session.settings.get("browser.enabled");
|
|
@@ -613,6 +618,10 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
613
618
|
}),
|
|
614
619
|
);
|
|
615
620
|
let tools = baseResults.filter((r): r is Tool => r !== null);
|
|
621
|
+
const toolRegistry = session.toolRegistry ?? new Map<string, Tool>();
|
|
622
|
+
session.toolRegistry = toolRegistry;
|
|
623
|
+
const builtInNames = new Set(tools.map(tool => tool.name));
|
|
624
|
+
for (const tool of tools) toolRegistry.set(tool.name, tool);
|
|
616
625
|
|
|
617
626
|
// Ordinary sessions use xd:// for discoverable built-ins, custom tools, and
|
|
618
627
|
// MCP tools. Structured children must expose only their host-provided names,
|
|
@@ -621,26 +630,26 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
621
630
|
const xdevEnabled = !restrictToolNames && session.settings.get("tools.xdev");
|
|
622
631
|
const mountBuiltinTools = requestedTools === undefined;
|
|
623
632
|
if (xdevEnabled) {
|
|
624
|
-
const
|
|
633
|
+
const mountedNames = new Set<string>();
|
|
625
634
|
const kept: Tool[] = [];
|
|
626
635
|
for (const tool of tools) {
|
|
627
636
|
const mountable = mountBuiltinTools && isMountableUnderXdev(tool) && tool.name in BUILTIN_TOOLS;
|
|
628
|
-
(mountable
|
|
637
|
+
if (mountable) mountedNames.add(tool.name);
|
|
638
|
+
else kept.push(tool);
|
|
629
639
|
}
|
|
630
|
-
session.
|
|
640
|
+
session.xdev = {
|
|
641
|
+
tools: toolRegistry,
|
|
642
|
+
mountedNames,
|
|
643
|
+
builtInNames,
|
|
644
|
+
isActive: name => session.isToolActive?.(name) === true,
|
|
645
|
+
};
|
|
631
646
|
tools = kept;
|
|
632
|
-
const finalActiveNames = new Set(tools.map(tool => tool.name));
|
|
633
|
-
if (session.setActiveToolNames) {
|
|
634
|
-
session.setActiveToolNames(finalActiveNames);
|
|
635
|
-
} else {
|
|
636
|
-
session.isToolActive = name => finalActiveNames.has(name);
|
|
637
|
-
}
|
|
638
647
|
}
|
|
639
648
|
// The xd:// transport rides read/write: `read xd://` lists+documents devices,
|
|
640
649
|
// `write xd://<tool>` executes them. Staged previews from deferrable tools
|
|
641
650
|
// (e.g. ast_edit) also resolve through a `write` to xd://resolve/reject. Retain
|
|
642
651
|
// both whenever any device is mounted or a deferrable tool can stage one.
|
|
643
|
-
const xdevMounted = (session.
|
|
652
|
+
const xdevMounted = (session.xdev?.mountedNames.size ?? 0) > 0;
|
|
644
653
|
if (
|
|
645
654
|
!restrictToolNames &&
|
|
646
655
|
(tools.some(tool => tool.deferrable === true) || xdevMounted) &&
|
|
@@ -648,15 +657,26 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
648
657
|
) {
|
|
649
658
|
const writeTool = await logger.time("createTools:write", BUILTIN_TOOLS.write, session);
|
|
650
659
|
if (writeTool) {
|
|
651
|
-
|
|
660
|
+
const wrapped = wrapToolWithMetaNotice(writeTool);
|
|
661
|
+
tools.push(wrapped);
|
|
662
|
+
toolRegistry.set(wrapped.name, wrapped);
|
|
663
|
+
builtInNames.add(wrapped.name);
|
|
652
664
|
}
|
|
653
665
|
}
|
|
654
666
|
if (!restrictToolNames && xdevMounted && !tools.some(tool => tool.name === "read")) {
|
|
655
667
|
const readTool = await logger.time("createTools:read", BUILTIN_TOOLS.read, session);
|
|
656
668
|
if (readTool) {
|
|
657
|
-
|
|
669
|
+
const wrapped = wrapToolWithMetaNotice(readTool);
|
|
670
|
+
tools.push(wrapped);
|
|
671
|
+
toolRegistry.set(wrapped.name, wrapped);
|
|
672
|
+
builtInNames.add(wrapped.name);
|
|
658
673
|
}
|
|
659
674
|
}
|
|
675
|
+
if (xdevEnabled) {
|
|
676
|
+
const finalActiveNames = new Set(tools.map(tool => tool.name));
|
|
677
|
+
if (session.setActiveToolNames) session.setActiveToolNames(finalActiveNames);
|
|
678
|
+
else session.isToolActive = name => finalActiveNames.has(name);
|
|
679
|
+
}
|
|
660
680
|
|
|
661
681
|
return tools;
|
|
662
682
|
}
|
package/src/tools/output-meta.ts
CHANGED
|
@@ -631,6 +631,26 @@ export function resolveOutputSinkHeadBytes(s: Settings | undefined): number {
|
|
|
631
631
|
return getSpillConfig(s).headBytes;
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
+
/**
|
|
635
|
+
* Slack on top of the configured spill threshold before the final-defense
|
|
636
|
+
* inline byte cap fires. The OutputSink already bounds inline bodies to the
|
|
637
|
+
* threshold; only notice slop (wall time, exit code, elision marker,
|
|
638
|
+
* `[raw output: artifact://N]` footer) rides above it. The slack keeps the
|
|
639
|
+
* cap a genuine last resort for paths that bypass the sink (e.g. ACP
|
|
640
|
+
* client-bridge terminals) instead of re-truncating — and re-saving — every
|
|
641
|
+
* sink-elided result (the double-artifact `Artifact: N+1` vs `artifact://N`
|
|
642
|
+
* mismatch).
|
|
643
|
+
*/
|
|
644
|
+
const INLINE_CAP_SLACK_BYTES = 2 * 1024;
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Resolve the `enforceInlineByteCap` budget for streaming tools (bash/ssh)
|
|
648
|
+
* from session settings: the user's spill threshold plus notice slack.
|
|
649
|
+
*/
|
|
650
|
+
export function resolveInlineByteCapBudget(s: Settings | undefined): number {
|
|
651
|
+
return getSpillConfig(s).threshold + INLINE_CAP_SLACK_BYTES;
|
|
652
|
+
}
|
|
653
|
+
|
|
634
654
|
/**
|
|
635
655
|
* Resolve the per-line column cap from session settings. Shared by streaming
|
|
636
656
|
* executors (bash/python/ssh/eval via OutputSink) and the `read` tool's
|
package/src/tools/read.ts
CHANGED
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
import { fileHyperlink, renderCodeCell, renderMarkdownCell, renderStatusLine, tryResolveInternalUrlSync } from "../tui";
|
|
58
58
|
import { CachedOutputBlock, markFramedBlockComponent } from "../tui/output-block";
|
|
59
59
|
import { buildLineEntriesWithBlockContext, type LineEntry, lineEntriesToPlainText } from "../utils/block-context";
|
|
60
|
+
import { isCpuProfilePath, renderCpuProfile } from "../utils/cpuprofile";
|
|
60
61
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
61
62
|
import {
|
|
62
63
|
ImageInputTooLargeError,
|
|
@@ -64,7 +65,9 @@ import {
|
|
|
64
65
|
MAX_IMAGE_INPUT_BYTES,
|
|
65
66
|
webpExclusionForModel,
|
|
66
67
|
} from "../utils/image-loading";
|
|
68
|
+
import { isInspectImageToolActive } from "../utils/inspect-image-mode";
|
|
67
69
|
import { CONVERTIBLE_EXTENSIONS, convertFileWithMarkit } from "../utils/markit";
|
|
70
|
+
import { isSampleProfilePath, renderSampleProfile } from "../utils/sample-profile";
|
|
68
71
|
import { type ArchiveReader, formatArchiveEntryLines, openArchive, parseArchivePathCandidates } from "../utils/zip";
|
|
69
72
|
import { buildDirectoryTree, type DirectoryTree } from "../workspace-tree";
|
|
70
73
|
import {
|
|
@@ -131,6 +134,7 @@ import {
|
|
|
131
134
|
} from "./sqlite-reader";
|
|
132
135
|
import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
|
|
133
136
|
import { toolResult } from "./tool-result";
|
|
137
|
+
import { xdevDocs, xdevListing } from "./xdev";
|
|
134
138
|
|
|
135
139
|
// Per-session memo for tree-sitter summaries. `summarizeCode` is a pure function
|
|
136
140
|
// of (code, path, fold settings) but costs ~12-18ms for a ~1500-line file, and a
|
|
@@ -155,6 +159,8 @@ function getSummaryParseCache(session: object): LRUCache<string, SummaryResult |
|
|
|
155
159
|
}
|
|
156
160
|
|
|
157
161
|
const MAX_SUMMARY_BYTES = 2 * 1024 * 1024;
|
|
162
|
+
/** Largest profile (`*.sample.txt`, `*.cpuprofile`) converted to a bottleneck summary; bigger files read as plain text. */
|
|
163
|
+
const MAX_PROFILE_SUMMARY_BYTES = 32 * 1024 * 1024;
|
|
158
164
|
const MAX_SUMMARY_LINES = 20_000;
|
|
159
165
|
const MAX_ARTIFACT_RAW_INLINE_BYTES = DEFAULT_MAX_BYTES;
|
|
160
166
|
/**
|
|
@@ -855,31 +861,74 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
855
861
|
pathTargetsSsh(String((args as { path?: unknown }).path ?? "")) ? "exec" : "read";
|
|
856
862
|
readonly label = "Read";
|
|
857
863
|
readonly loadMode = "essential";
|
|
858
|
-
|
|
864
|
+
description: string;
|
|
859
865
|
readonly parameters = readSchema;
|
|
860
866
|
readonly strict = true;
|
|
861
867
|
|
|
862
868
|
readonly #autoResizeImages: boolean;
|
|
863
869
|
readonly #defaultLimit: number;
|
|
864
|
-
|
|
870
|
+
#inspectImageActive: boolean;
|
|
865
871
|
|
|
866
872
|
constructor(private readonly session: ToolSession) {
|
|
867
|
-
const displayMode = resolveFileDisplayMode(session);
|
|
868
873
|
this.#autoResizeImages = session.settings.get("images.autoResize");
|
|
869
874
|
this.#defaultLimit = Math.max(
|
|
870
875
|
1,
|
|
871
876
|
Math.min(session.settings.get("read.defaultLimit") ?? DEFAULT_MAX_LINES, DEFAULT_MAX_LINES),
|
|
872
877
|
);
|
|
873
|
-
this.#
|
|
874
|
-
this.description =
|
|
878
|
+
this.#inspectImageActive = this.#resolveInspectImageAvailability();
|
|
879
|
+
this.description = this.#renderDescription();
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Re-render the tool description for the current display mode and the
|
|
884
|
+
* effective inspect_image state (mode setting, `/vision` override, and
|
|
885
|
+
* active-model image capability all feed it, so it can change at runtime).
|
|
886
|
+
*/
|
|
887
|
+
#renderDescription(): string {
|
|
888
|
+
const displayMode = resolveFileDisplayMode(this.session);
|
|
889
|
+
return prompt.render(readDescription, {
|
|
875
890
|
DEFAULT_LIMIT: String(this.#defaultLimit),
|
|
876
891
|
DEFAULT_MAX_LINES: String(DEFAULT_MAX_LINES),
|
|
877
892
|
IS_HL_MODE: displayMode.hashLines,
|
|
878
893
|
IS_LINE_NUMBER_MODE: !displayMode.hashLines && displayMode.lineNumbers,
|
|
879
|
-
INSPECT_IMAGE_ENABLED: this.#
|
|
894
|
+
INSPECT_IMAGE_ENABLED: this.#inspectImageActive,
|
|
880
895
|
});
|
|
881
896
|
}
|
|
882
897
|
|
|
898
|
+
/**
|
|
899
|
+
* Whether the agent can actually reach `inspect_image` right now: exposed
|
|
900
|
+
* top-level, or mounted as an `xd://` device while the effective mode wants
|
|
901
|
+
* it (mounted devices stay executable via `write xd://inspect_image`, so a
|
|
902
|
+
* metadata-only read remains actionable). Sessions with neither
|
|
903
|
+
* availability signal (tests, embedded use) fall back to the mode
|
|
904
|
+
* computation alone. Restricted slates (subagents without the tool and
|
|
905
|
+
* without xdev) resolve to unavailable, so those sessions get inline image
|
|
906
|
+
* blocks instead of guidance pointing at an absent tool.
|
|
907
|
+
*/
|
|
908
|
+
#resolveInspectImageAvailability(): boolean {
|
|
909
|
+
const topLevel = this.session.isToolActive?.("inspect_image");
|
|
910
|
+
const xdev = this.session.xdev;
|
|
911
|
+
if (topLevel === undefined && xdev === undefined) return isInspectImageToolActive(this.session);
|
|
912
|
+
if (topLevel === true) return true;
|
|
913
|
+
return xdev?.mountedNames.has("inspect_image") === true && isInspectImageToolActive(this.session);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Re-evaluate the effective inspect_image state; it can flip when the model
|
|
918
|
+
* or the `/vision` override changes after this tool was constructed. Keeps
|
|
919
|
+
* the behavior branch and the advertised description in lockstep. Called
|
|
920
|
+
* per image read and by tool reconciliation before prompt rebuilds (which
|
|
921
|
+
* passes the post-change availability as `availableOverride`).
|
|
922
|
+
*/
|
|
923
|
+
syncInspectImageState(availableOverride?: boolean): boolean {
|
|
924
|
+
const active = availableOverride ?? this.#resolveInspectImageAvailability();
|
|
925
|
+
if (active !== this.#inspectImageActive) {
|
|
926
|
+
this.#inspectImageActive = active;
|
|
927
|
+
this.description = this.#renderDescription();
|
|
928
|
+
}
|
|
929
|
+
return active;
|
|
930
|
+
}
|
|
931
|
+
|
|
883
932
|
/**
|
|
884
933
|
* Recover the active approved plan when a model rewrites its `local://` URL
|
|
885
934
|
* as a same-basename path in the working-directory root.
|
|
@@ -1256,10 +1305,10 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1256
1305
|
|
|
1257
1306
|
/**
|
|
1258
1307
|
* Build content blocks for an on-disk image file: an `inspect_image`
|
|
1259
|
-
* metadata note when inspection is
|
|
1308
|
+
* metadata note when inspection is active, otherwise the decoded image
|
|
1260
1309
|
* block. Shared by the plain-file read path and the `local://` image fast
|
|
1261
|
-
* path so both honor
|
|
1262
|
-
* identically. Too-large / unsupported images surface as {@link ToolError}.
|
|
1310
|
+
* path so both honor the effective inspect_image state, the size cap, and
|
|
1311
|
+
* auto-resize identically. Too-large / unsupported images surface as {@link ToolError}.
|
|
1263
1312
|
*/
|
|
1264
1313
|
async #loadImageContent(options: {
|
|
1265
1314
|
readPath: string;
|
|
@@ -1269,7 +1318,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1269
1318
|
fileSize: number;
|
|
1270
1319
|
}): Promise<{ content: Array<TextContent | ImageContent>; details: ReadToolDetails; sourcePath: string }> {
|
|
1271
1320
|
const { readPath, absolutePath, mimeType, imageMetadata, fileSize } = options;
|
|
1272
|
-
if (this
|
|
1321
|
+
if (this.syncInspectImageState()) {
|
|
1273
1322
|
const outputMime = imageMetadata?.mimeType ?? mimeType;
|
|
1274
1323
|
const metadataLines = [
|
|
1275
1324
|
"Image metadata:",
|
|
@@ -2432,6 +2481,31 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2432
2481
|
const mimeType = imageMetadata?.mimeType;
|
|
2433
2482
|
const ext = path.extname(absolutePath).toLowerCase();
|
|
2434
2483
|
const shouldConvertWithMarkit = CONVERTIBLE_EXTENSIONS.has(ext);
|
|
2484
|
+
|
|
2485
|
+
// Profiler reports (macOS `sample` call trees, V8 `.cpuprofile` JSON):
|
|
2486
|
+
// replace the raw dump with a bottleneck summary (hot paths, top self
|
|
2487
|
+
// time/samples). `:raw` reads the original bytes; text that merely wears
|
|
2488
|
+
// the extension falls through to the plain-text path.
|
|
2489
|
+
if (!mimeType && !isRawSelector(parsed) && fileSize <= MAX_PROFILE_SUMMARY_BYTES) {
|
|
2490
|
+
let rendered: string | null = null;
|
|
2491
|
+
if (isSampleProfilePath(absolutePath)) rendered = renderSampleProfile(await Bun.file(absolutePath).text());
|
|
2492
|
+
else if (isCpuProfilePath(absolutePath)) rendered = renderCpuProfile(await Bun.file(absolutePath).text());
|
|
2493
|
+
if (rendered) {
|
|
2494
|
+
if (isMultiRange(parsed) && parsed.kind === "lines") {
|
|
2495
|
+
return this.#buildInMemoryMultiRangeResult(rendered, parsed.ranges, {
|
|
2496
|
+
details: { resolvedPath: absolutePath },
|
|
2497
|
+
sourcePath: absolutePath,
|
|
2498
|
+
entityLabel: "profile summary",
|
|
2499
|
+
});
|
|
2500
|
+
}
|
|
2501
|
+
const { offset, limit } = selToOffsetLimit(parsed);
|
|
2502
|
+
return this.#buildInMemoryTextResult(rendered, offset, limit, {
|
|
2503
|
+
details: { resolvedPath: absolutePath },
|
|
2504
|
+
sourcePath: absolutePath,
|
|
2505
|
+
entityLabel: "profile summary",
|
|
2506
|
+
});
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2435
2509
|
// Read the file based on type
|
|
2436
2510
|
let content: Array<TextContent | ImageContent> | undefined;
|
|
2437
2511
|
let details: ReadToolDetails = {};
|
|
@@ -3230,9 +3304,9 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
3230
3304
|
read: async name => {
|
|
3231
3305
|
if (name === REPORT_ISSUE_DEVICE_NAME) return reportIssueDeviceUsage();
|
|
3232
3306
|
if (name && isResolutionDeviceName(name)) return resolutionDeviceUsage(name);
|
|
3233
|
-
const
|
|
3234
|
-
if (!
|
|
3235
|
-
return name === null ?
|
|
3307
|
+
const xdev = this.session.xdev;
|
|
3308
|
+
if (!xdev) throw new ToolError("xd:// is not mounted in this session.");
|
|
3309
|
+
return name === null ? xdevListing(xdev) : xdevDocs(xdev, name);
|
|
3236
3310
|
},
|
|
3237
3311
|
},
|
|
3238
3312
|
});
|
package/src/tools/write.ts
CHANGED
|
@@ -88,7 +88,14 @@ import {
|
|
|
88
88
|
} from "./sqlite-reader";
|
|
89
89
|
import { ToolError } from "./tool-errors";
|
|
90
90
|
import { toolResult } from "./tool-result";
|
|
91
|
-
import {
|
|
91
|
+
import {
|
|
92
|
+
dispatchXdevTool,
|
|
93
|
+
renderXdevCall,
|
|
94
|
+
renderXdevResult,
|
|
95
|
+
resolveXdevTool,
|
|
96
|
+
type XdevDispatch,
|
|
97
|
+
xdevListing,
|
|
98
|
+
} from "./xdev";
|
|
92
99
|
|
|
93
100
|
const LOOSE_HASHLINE_HEADER_RE = /^\s*\[[^#\r\n]+#[^ \t\r\n]*\]\s*$/;
|
|
94
101
|
const EXECUTABLE_NOTICE = "[Notice: Made executable via chmod +x]";
|
|
@@ -471,7 +478,8 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
471
478
|
if (xdevTarget) {
|
|
472
479
|
if (xdevTarget.name === REPORT_ISSUE_DEVICE_NAME) return "write";
|
|
473
480
|
if (xdevTarget.name && isResolutionDeviceName(xdevTarget.name)) return "read";
|
|
474
|
-
const inst =
|
|
481
|
+
const inst =
|
|
482
|
+
xdevTarget.name && this.session.xdev ? resolveXdevTool(this.session.xdev, xdevTarget.name) : undefined;
|
|
475
483
|
if (!inst) return "exec";
|
|
476
484
|
// Decode the device JSON payload and evaluate the mounted tool's own
|
|
477
485
|
// approval (which may be argument-dependent, e.g. ast_edit is read-tier
|
|
@@ -1104,14 +1112,15 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
1104
1112
|
};
|
|
1105
1113
|
return;
|
|
1106
1114
|
}
|
|
1107
|
-
const
|
|
1108
|
-
if (!
|
|
1115
|
+
const xdev = this.session.xdev;
|
|
1116
|
+
if (!xdev) {
|
|
1109
1117
|
throw new ToolError("xd:// is not mounted in this session.");
|
|
1110
1118
|
}
|
|
1111
1119
|
if (!name) {
|
|
1112
|
-
throw new ToolError(`Cannot write to xd:// itself — pick a device:\n${
|
|
1120
|
+
throw new ToolError(`Cannot write to xd:// itself — pick a device:\n${xdevListing(xdev)}`);
|
|
1113
1121
|
}
|
|
1114
|
-
const { result, xdev } = await
|
|
1122
|
+
const { result, xdev: dispatch } = await dispatchXdevTool(
|
|
1123
|
+
xdev,
|
|
1115
1124
|
name,
|
|
1116
1125
|
deviceContent,
|
|
1117
1126
|
_toolCallId,
|
|
@@ -1124,7 +1133,7 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
1124
1133
|
);
|
|
1125
1134
|
xdResult = {
|
|
1126
1135
|
content: result.content,
|
|
1127
|
-
details: { xdev },
|
|
1136
|
+
details: { xdev: dispatch },
|
|
1128
1137
|
isError: result.isError,
|
|
1129
1138
|
useless: result.useless,
|
|
1130
1139
|
};
|