@oh-my-pi/pi-coding-agent 13.19.0 → 14.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 +277 -2
- package/package.json +86 -20
- package/scripts/format-prompts.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +24 -0
- package/src/autoresearch/contract.ts +0 -44
- package/src/autoresearch/dashboard.ts +1 -2
- package/src/autoresearch/git.ts +91 -0
- package/src/autoresearch/helpers.ts +49 -0
- package/src/autoresearch/index.ts +28 -187
- package/src/autoresearch/prompt.md +26 -9
- package/src/autoresearch/state.ts +0 -6
- package/src/autoresearch/tools/init-experiment.ts +202 -117
- package/src/autoresearch/tools/log-experiment.ts +83 -125
- package/src/autoresearch/tools/run-experiment.ts +48 -10
- package/src/autoresearch/types.ts +2 -2
- package/src/capability/index.ts +4 -2
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/grep-cli.ts +8 -8
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/read-cli.ts +67 -0
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/update-cli.ts +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/grep.ts +6 -1
- package/src/commands/grievances.ts +20 -0
- package/src/commands/read.ts +33 -0
- package/src/commit/agentic/agent.ts +5 -5
- package/src/commit/agentic/index.ts +3 -4
- package/src/commit/agentic/tools/analyze-file.ts +3 -3
- package/src/commit/agentic/validation.ts +1 -1
- package/src/commit/analysis/conventional.ts +4 -4
- package/src/commit/analysis/summary.ts +3 -3
- package/src/commit/changelog/generate.ts +4 -4
- package/src/commit/map-reduce/map-phase.ts +4 -4
- package/src/commit/map-reduce/reduce-phase.ts +4 -4
- package/src/commit/pipeline.ts +3 -4
- package/src/config/model-registry.ts +17 -3
- package/src/config/prompt-templates.ts +44 -226
- package/src/config/resolve-config-value.ts +4 -2
- package/src/config/settings-schema.ts +54 -2
- package/src/config/settings.ts +25 -26
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/log-viewer.ts +3 -2
- package/src/discovery/builtin.ts +1 -2
- package/src/discovery/codex.ts +2 -2
- package/src/discovery/github.ts +2 -1
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/opencode.ts +2 -2
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +309 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +454 -0
- package/src/{patch → edit/modes}/hashline.ts +741 -361
- package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
- package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
- package/src/{patch → edit}/normalize.ts +97 -76
- package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
- package/src/exec/bash-executor.ts +4 -2
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +5 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +2 -2
- package/src/extensibility/custom-commands/bundled/review/index.ts +36 -15
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +34 -11
- package/src/extensibility/extensions/loader.ts +9 -4
- package/src/extensibility/extensions/runner.ts +24 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/loader.ts +5 -6
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +2 -1
- package/src/extensibility/slash-commands.ts +3 -7
- package/src/index.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/ipy/executor.ts +58 -17
- package/src/ipy/gateway-coordinator.ts +6 -4
- package/src/ipy/kernel.ts +45 -22
- package/src/ipy/runtime.ts +2 -2
- package/src/lsp/client.ts +7 -4
- package/src/lsp/clients/lsp-linter-client.ts +4 -4
- package/src/lsp/config.ts +20 -4
- package/src/lsp/defaults.json +688 -154
- package/src/lsp/index.ts +234 -45
- package/src/lsp/lspmux.ts +2 -2
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +12 -1
- package/src/lsp/utils.ts +8 -1
- package/src/main.ts +102 -46
- package/src/memories/index.ts +4 -5
- package/src/modes/acp/acp-agent.ts +563 -163
- package/src/modes/acp/acp-event-mapper.ts +9 -1
- package/src/modes/acp/acp-mode.ts +4 -2
- package/src/modes/components/agent-dashboard.ts +3 -4
- package/src/modes/components/diff.ts +6 -7
- package/src/modes/components/read-tool-group.ts +6 -12
- package/src/modes/components/session-observer-overlay.ts +21 -12
- package/src/modes/components/settings-defs.ts +5 -0
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/btw-controller.ts +2 -2
- package/src/modes/controllers/command-controller.ts +3 -2
- package/src/modes/controllers/input-controller.ts +12 -8
- package/src/modes/index.ts +20 -2
- package/src/modes/interactive-mode.ts +94 -37
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +178 -13
- package/src/modes/rpc/rpc-mode.ts +73 -3
- package/src/modes/rpc/rpc-types.ts +53 -1
- package/src/modes/theme/theme.ts +80 -8
- package/src/modes/types.ts +2 -2
- package/src/prompts/review-request.md +6 -0
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/chunk-edit.md +223 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/grep.md +3 -0
- package/src/prompts/tools/lsp.md +5 -5
- package/src/prompts/tools/read-chunk.md +17 -0
- package/src/prompts/tools/read.md +19 -5
- package/src/sdk.ts +190 -154
- package/src/secrets/obfuscator.ts +1 -1
- package/src/session/agent-session.ts +306 -256
- package/src/session/agent-storage.ts +12 -12
- package/src/session/compaction/branch-summarization.ts +3 -3
- package/src/session/compaction/compaction.ts +5 -6
- package/src/session/compaction/utils.ts +3 -3
- package/src/session/history-storage.ts +62 -19
- package/src/session/messages.ts +3 -3
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-storage.ts +4 -2
- package/src/session/streaming-output.ts +1 -1
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +56 -8
- package/src/ssh/connection-manager.ts +2 -2
- package/src/ssh/sshfs-mount.ts +5 -5
- package/src/stt/downloader.ts +4 -4
- package/src/stt/recorder.ts +4 -4
- package/src/stt/transcriber.ts +2 -2
- package/src/system-prompt.ts +21 -13
- package/src/task/agents.ts +5 -6
- package/src/task/commands.ts +2 -5
- package/src/task/executor.ts +4 -4
- package/src/task/index.ts +3 -4
- package/src/task/template.ts +2 -2
- package/src/task/worktree.ts +4 -4
- package/src/tools/ask.ts +2 -3
- package/src/tools/ast-edit.ts +7 -7
- package/src/tools/ast-grep.ts +7 -7
- package/src/tools/auto-generated-guard.ts +36 -41
- package/src/tools/await-tool.ts +2 -2
- package/src/tools/bash.ts +5 -23
- package/src/tools/browser.ts +4 -5
- package/src/tools/calculator.ts +2 -3
- package/src/tools/cancel-job.ts +2 -2
- package/src/tools/checkpoint.ts +3 -3
- package/src/tools/debug.ts +1007 -0
- package/src/tools/exit-plan-mode.ts +2 -3
- package/src/tools/fetch.ts +67 -3
- package/src/tools/find.ts +4 -5
- package/src/tools/fs-cache-invalidation.ts +5 -0
- package/src/tools/gemini-image.ts +13 -5
- package/src/tools/gh.ts +10 -11
- package/src/tools/grep.ts +57 -9
- package/src/tools/index.ts +44 -22
- package/src/tools/inspect-image.ts +4 -4
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/python.ts +19 -6
- package/src/tools/read.ts +198 -67
- package/src/tools/render-mermaid.ts +2 -3
- package/src/tools/render-utils.ts +20 -6
- package/src/tools/renderers.ts +3 -1
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +70 -39
- package/src/tools/search-tool-bm25.ts +2 -2
- package/src/tools/ssh.ts +2 -2
- package/src/tools/todo-write.ts +2 -2
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/write.ts +5 -6
- package/src/tui/tree-list.ts +3 -1
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/commit-message-generator.ts +2 -3
- package/src/utils/edit-mode.ts +49 -0
- package/src/utils/file-display-mode.ts +6 -5
- package/src/utils/file-mentions.ts +8 -7
- package/src/utils/git.ts +4 -4
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/title-generator.ts +2 -3
- package/src/utils/tools-manager.ts +6 -6
- package/src/web/scrapers/choosealicense.ts +1 -1
- package/src/web/search/index.ts +3 -3
- package/src/autoresearch/command-initialize.md +0 -34
- package/src/patch/diff.ts +0 -433
- package/src/patch/index.ts +0 -888
- package/src/patch/parser.ts +0 -532
- package/src/patch/types.ts +0 -292
- package/src/prompts/agents/oracle.md +0 -77
- package/src/tools/pending-action.ts +0 -49
- package/src/utils/child-process.ts +0 -88
- package/src/utils/frontmatter.ts +0 -117
- package/src/utils/image-input.ts +0 -274
- package/src/utils/mime.ts +0 -53
- package/src/utils/prompt-format.ts +0 -170
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentTool,
|
|
3
|
+
AgentToolContext,
|
|
4
|
+
AgentToolResult,
|
|
5
|
+
AgentToolUpdateCallback,
|
|
6
|
+
RenderResultOptions,
|
|
7
|
+
} from "@oh-my-pi/pi-agent-core";
|
|
8
|
+
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
9
|
+
import { type Component, Text } from "@oh-my-pi/pi-tui";
|
|
10
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
11
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
12
|
+
import {
|
|
13
|
+
type DapBreakpointRecord,
|
|
14
|
+
type DapCapabilities,
|
|
15
|
+
type DapContinueOutcome,
|
|
16
|
+
type DapDataBreakpointInfoResponse,
|
|
17
|
+
type DapDataBreakpointRecord,
|
|
18
|
+
type DapDisassembledInstruction,
|
|
19
|
+
type DapEvaluateArguments,
|
|
20
|
+
type DapEvaluateResponse,
|
|
21
|
+
type DapFunctionBreakpointRecord,
|
|
22
|
+
type DapInstructionBreakpointRecord,
|
|
23
|
+
type DapModule,
|
|
24
|
+
type DapScope,
|
|
25
|
+
type DapSessionSummary,
|
|
26
|
+
type DapSource,
|
|
27
|
+
type DapStackFrame,
|
|
28
|
+
type DapThread,
|
|
29
|
+
type DapVariable,
|
|
30
|
+
dapSessionManager,
|
|
31
|
+
getAvailableAdapters,
|
|
32
|
+
selectAttachAdapter,
|
|
33
|
+
selectLaunchAdapter,
|
|
34
|
+
} from "../dap";
|
|
35
|
+
import type { Theme } from "../modes/theme/theme";
|
|
36
|
+
import debugDescription from "../prompts/tools/debug.md" with { type: "text" };
|
|
37
|
+
import { renderStatusLine } from "../tui";
|
|
38
|
+
import { CachedOutputBlock } from "../tui/output-block";
|
|
39
|
+
import type { ToolSession } from ".";
|
|
40
|
+
import type { OutputMeta } from "./output-meta";
|
|
41
|
+
import { resolveToCwd } from "./path-utils";
|
|
42
|
+
import {
|
|
43
|
+
formatExpandHint,
|
|
44
|
+
formatStatusIcon,
|
|
45
|
+
PREVIEW_LIMITS,
|
|
46
|
+
replaceTabs,
|
|
47
|
+
TRUNCATE_LENGTHS,
|
|
48
|
+
truncateToWidth,
|
|
49
|
+
} from "./render-utils";
|
|
50
|
+
import { ToolError } from "./tool-errors";
|
|
51
|
+
import { toolResult } from "./tool-result";
|
|
52
|
+
import { clampTimeout } from "./tool-timeouts";
|
|
53
|
+
|
|
54
|
+
const debugSchema = Type.Object({
|
|
55
|
+
action: StringEnum(
|
|
56
|
+
[
|
|
57
|
+
"launch",
|
|
58
|
+
"attach",
|
|
59
|
+
"set_breakpoint",
|
|
60
|
+
"remove_breakpoint",
|
|
61
|
+
"set_instruction_breakpoint",
|
|
62
|
+
"remove_instruction_breakpoint",
|
|
63
|
+
"data_breakpoint_info",
|
|
64
|
+
"set_data_breakpoint",
|
|
65
|
+
"remove_data_breakpoint",
|
|
66
|
+
"continue",
|
|
67
|
+
"step_over",
|
|
68
|
+
"step_in",
|
|
69
|
+
"step_out",
|
|
70
|
+
"pause",
|
|
71
|
+
"evaluate",
|
|
72
|
+
"stack_trace",
|
|
73
|
+
"threads",
|
|
74
|
+
"scopes",
|
|
75
|
+
"variables",
|
|
76
|
+
"disassemble",
|
|
77
|
+
"read_memory",
|
|
78
|
+
"write_memory",
|
|
79
|
+
"modules",
|
|
80
|
+
"loaded_sources",
|
|
81
|
+
"custom_request",
|
|
82
|
+
"output",
|
|
83
|
+
"terminate",
|
|
84
|
+
"sessions",
|
|
85
|
+
],
|
|
86
|
+
{ description: "DAP debugger action" },
|
|
87
|
+
),
|
|
88
|
+
program: Type.Optional(Type.String({ description: "Program or script path for launch" })),
|
|
89
|
+
args: Type.Optional(Type.Array(Type.String(), { description: "Program arguments for launch" })),
|
|
90
|
+
adapter: Type.Optional(Type.String({ description: "Debugger adapter override (gdb, lldb-dap, debugpy, dlv)" })),
|
|
91
|
+
cwd: Type.Optional(Type.String({ description: "Working directory for launch or attach" })),
|
|
92
|
+
file: Type.Optional(Type.String({ description: "Source file for source breakpoints" })),
|
|
93
|
+
line: Type.Optional(Type.Number({ description: "1-indexed source line for source breakpoints" })),
|
|
94
|
+
function: Type.Optional(Type.String({ description: "Function name for function breakpoints" })),
|
|
95
|
+
name: Type.Optional(Type.String({ description: "Variable or data name for data breakpoint info" })),
|
|
96
|
+
condition: Type.Optional(Type.String({ description: "Breakpoint condition expression" })),
|
|
97
|
+
hit_condition: Type.Optional(Type.String({ description: "Breakpoint hit condition expression" })),
|
|
98
|
+
expression: Type.Optional(Type.String({ description: "Expression to evaluate in debugger context" })),
|
|
99
|
+
context: Type.Optional(Type.String({ description: "Evaluate context (watch, repl, hover, variables, clipboard)" })),
|
|
100
|
+
frame_id: Type.Optional(Type.Number({ description: "Stack frame ID for scopes/evaluate" })),
|
|
101
|
+
scope_id: Type.Optional(Type.Number({ description: "Scope variablesReference for variables requests" })),
|
|
102
|
+
variable_ref: Type.Optional(Type.Number({ description: "Variable reference for variables requests" })),
|
|
103
|
+
pid: Type.Optional(Type.Number({ description: "Process ID for attach" })),
|
|
104
|
+
port: Type.Optional(Type.Number({ description: "Port for remote attach when adapter supports it" })),
|
|
105
|
+
host: Type.Optional(Type.String({ description: "Host for remote attach when adapter supports it" })),
|
|
106
|
+
levels: Type.Optional(Type.Number({ description: "Maximum stack frames to fetch" })),
|
|
107
|
+
memory_reference: Type.Optional(Type.String({ description: "Memory reference or address" })),
|
|
108
|
+
instruction_reference: Type.Optional(
|
|
109
|
+
Type.String({ description: "Instruction address/reference for instruction breakpoints" }),
|
|
110
|
+
),
|
|
111
|
+
instruction_count: Type.Optional(Type.Number({ description: "Number of instructions to disassemble" })),
|
|
112
|
+
instruction_offset: Type.Optional(Type.Number({ description: "Instruction offset for disassembly" })),
|
|
113
|
+
count: Type.Optional(Type.Number({ description: "Number of bytes to read from memory" })),
|
|
114
|
+
data: Type.Optional(Type.String({ description: "Base64-encoded memory payload for write_memory" })),
|
|
115
|
+
data_id: Type.Optional(Type.String({ description: "DAP data breakpoint identifier" })),
|
|
116
|
+
access_type: Type.Optional(
|
|
117
|
+
StringEnum(["read", "write", "readWrite"], { description: "Data breakpoint access type" }),
|
|
118
|
+
),
|
|
119
|
+
command: Type.Optional(Type.String({ description: "Custom DAP request command" })),
|
|
120
|
+
arguments: Type.Optional(
|
|
121
|
+
Type.Record(Type.String(), Type.Any(), {
|
|
122
|
+
description: "Arguments object for custom_request",
|
|
123
|
+
}),
|
|
124
|
+
),
|
|
125
|
+
offset: Type.Optional(Type.Number({ description: "Memory or instruction offset" })),
|
|
126
|
+
resolve_symbols: Type.Optional(Type.Boolean({ description: "Resolve symbols during disassembly" })),
|
|
127
|
+
allow_partial: Type.Optional(Type.Boolean({ description: "Allow partial writes for write_memory" })),
|
|
128
|
+
start_module: Type.Optional(Type.Number({ description: "Modules request start index" })),
|
|
129
|
+
module_count: Type.Optional(Type.Number({ description: "Maximum modules to fetch" })),
|
|
130
|
+
timeout: Type.Optional(Type.Number({ description: "Per-request timeout in seconds" })),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export type DebugParams = Static<typeof debugSchema>;
|
|
134
|
+
export type DebugAction = DebugParams["action"];
|
|
135
|
+
|
|
136
|
+
interface DebugToolDetails {
|
|
137
|
+
action: DebugAction;
|
|
138
|
+
success: boolean;
|
|
139
|
+
snapshot?: DapSessionSummary;
|
|
140
|
+
sessions?: DapSessionSummary[];
|
|
141
|
+
stackFrames?: DapStackFrame[];
|
|
142
|
+
threads?: DapThread[];
|
|
143
|
+
scopes?: DapScope[];
|
|
144
|
+
variables?: DapVariable[];
|
|
145
|
+
sources?: DapSource[];
|
|
146
|
+
modules?: DapModule[];
|
|
147
|
+
evaluation?: DapEvaluateResponse;
|
|
148
|
+
breakpoints?: DapBreakpointRecord[];
|
|
149
|
+
functionBreakpoints?: DapFunctionBreakpointRecord[];
|
|
150
|
+
instructionBreakpoints?: DapInstructionBreakpointRecord[];
|
|
151
|
+
dataBreakpoints?: DapDataBreakpointRecord[];
|
|
152
|
+
dataBreakpointInfo?: DapDataBreakpointInfoResponse;
|
|
153
|
+
disassembly?: DapDisassembledInstruction[];
|
|
154
|
+
memoryAddress?: string;
|
|
155
|
+
memoryData?: string;
|
|
156
|
+
unreadableBytes?: number;
|
|
157
|
+
bytesWritten?: number;
|
|
158
|
+
customBody?: unknown;
|
|
159
|
+
output?: string;
|
|
160
|
+
adapter?: string;
|
|
161
|
+
state?: DapContinueOutcome["state"];
|
|
162
|
+
timedOut?: boolean;
|
|
163
|
+
meta?: OutputMeta;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function formatLocation(snapshot: DapSessionSummary | undefined): string | null {
|
|
167
|
+
if (!snapshot?.source?.path || snapshot.line === undefined) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
return `${snapshot.source.path}:${snapshot.line}${snapshot.column !== undefined ? `:${snapshot.column}` : ""}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function formatSessionSnapshot(snapshot: DapSessionSummary): string[] {
|
|
174
|
+
const lines = [
|
|
175
|
+
`Session ${snapshot.id}`,
|
|
176
|
+
`Adapter: ${snapshot.adapter}`,
|
|
177
|
+
`Status: ${snapshot.status}`,
|
|
178
|
+
`CWD: ${snapshot.cwd}`,
|
|
179
|
+
];
|
|
180
|
+
if (snapshot.program) lines.push(`Program: ${snapshot.program}`);
|
|
181
|
+
if (snapshot.stopReason) lines.push(`Stop reason: ${snapshot.stopReason}`);
|
|
182
|
+
if (snapshot.frameName) lines.push(`Frame: ${snapshot.frameName}`);
|
|
183
|
+
if (snapshot.instructionPointerReference) {
|
|
184
|
+
lines.push(`Instruction pointer: ${snapshot.instructionPointerReference}`);
|
|
185
|
+
}
|
|
186
|
+
const location = formatLocation(snapshot);
|
|
187
|
+
if (location) lines.push(`Location: ${location}`);
|
|
188
|
+
if (snapshot.needsConfigurationDone) {
|
|
189
|
+
lines.push("Configuration: pending configurationDone; set breakpoints, then continue.");
|
|
190
|
+
}
|
|
191
|
+
if (snapshot.exitCode !== undefined) lines.push(`Exit code: ${snapshot.exitCode}`);
|
|
192
|
+
return lines;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function formatBreakpoints(filePath: string, breakpoints: DapBreakpointRecord[]): string {
|
|
196
|
+
const lines = [`Breakpoints for ${filePath}:`];
|
|
197
|
+
if (breakpoints.length === 0) {
|
|
198
|
+
lines.push("(none)");
|
|
199
|
+
return lines.join("\n");
|
|
200
|
+
}
|
|
201
|
+
for (const breakpoint of breakpoints) {
|
|
202
|
+
lines.push(
|
|
203
|
+
`- line ${breakpoint.line}: ${breakpoint.verified ? "verified" : "pending"}${breakpoint.condition ? ` if ${breakpoint.condition}` : ""}${breakpoint.message ? ` (${breakpoint.message})` : ""}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
return lines.join("\n");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function formatFunctionBreakpoints(breakpoints: DapFunctionBreakpointRecord[]): string {
|
|
210
|
+
const lines = ["Function breakpoints:"];
|
|
211
|
+
if (breakpoints.length === 0) {
|
|
212
|
+
lines.push("(none)");
|
|
213
|
+
return lines.join("\n");
|
|
214
|
+
}
|
|
215
|
+
for (const breakpoint of breakpoints) {
|
|
216
|
+
lines.push(
|
|
217
|
+
`- ${breakpoint.name}: ${breakpoint.verified ? "verified" : "pending"}${breakpoint.condition ? ` if ${breakpoint.condition}` : ""}${breakpoint.message ? ` (${breakpoint.message})` : ""}`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
return lines.join("\n");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function formatStackFrames(frames: DapStackFrame[]): string {
|
|
224
|
+
const lines = ["Stack trace:"];
|
|
225
|
+
if (frames.length === 0) {
|
|
226
|
+
lines.push("(empty)");
|
|
227
|
+
return lines.join("\n");
|
|
228
|
+
}
|
|
229
|
+
for (const frame of frames) {
|
|
230
|
+
const location = frame.source?.path
|
|
231
|
+
? `${frame.source.path}:${frame.line}:${frame.column}`
|
|
232
|
+
: `<unknown>:${frame.line}:${frame.column}`;
|
|
233
|
+
lines.push(`- #${frame.id} ${frame.name} @ ${location}`);
|
|
234
|
+
}
|
|
235
|
+
return lines.join("\n");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function formatThreads(threads: DapThread[]): string {
|
|
239
|
+
const lines = ["Threads:"];
|
|
240
|
+
if (threads.length === 0) {
|
|
241
|
+
lines.push("(none)");
|
|
242
|
+
return lines.join("\n");
|
|
243
|
+
}
|
|
244
|
+
for (const thread of threads) {
|
|
245
|
+
lines.push(`- ${thread.id}: ${thread.name}`);
|
|
246
|
+
}
|
|
247
|
+
return lines.join("\n");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function formatScopes(scopes: DapScope[]): string {
|
|
251
|
+
const lines = ["Scopes:"];
|
|
252
|
+
if (scopes.length === 0) {
|
|
253
|
+
lines.push("(none)");
|
|
254
|
+
return lines.join("\n");
|
|
255
|
+
}
|
|
256
|
+
for (const scope of scopes) {
|
|
257
|
+
lines.push(
|
|
258
|
+
`- ${scope.name}: ref=${scope.variablesReference}, expensive=${scope.expensive ? "yes" : "no"}${scope.presentationHint ? `, hint=${scope.presentationHint}` : ""}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
return lines.join("\n");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function formatVariables(variables: DapVariable[]): string {
|
|
265
|
+
const lines = ["Variables:"];
|
|
266
|
+
if (variables.length === 0) {
|
|
267
|
+
lines.push("(none)");
|
|
268
|
+
return lines.join("\n");
|
|
269
|
+
}
|
|
270
|
+
for (const variable of variables) {
|
|
271
|
+
lines.push(
|
|
272
|
+
`- ${variable.name} = ${variable.value}${variable.type ? ` (${variable.type})` : ""}${variable.variablesReference > 0 ? ` [ref=${variable.variablesReference}]` : ""}`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return lines.join("\n");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function formatSourceLabel(source: DapSource | undefined, line?: number, column?: number): string | null {
|
|
279
|
+
if (!source?.path && !source?.name) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
const base = source.path ?? source.name ?? "<unknown>";
|
|
283
|
+
if (line === undefined) {
|
|
284
|
+
return base;
|
|
285
|
+
}
|
|
286
|
+
return `${base}:${line}${column !== undefined ? `:${column}` : ""}`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function formatDisassembly(instructions: DapDisassembledInstruction[]): string {
|
|
290
|
+
const lines = ["Disassembly:"];
|
|
291
|
+
if (instructions.length === 0) {
|
|
292
|
+
lines.push("(empty)");
|
|
293
|
+
return lines.join("\n");
|
|
294
|
+
}
|
|
295
|
+
const addressWidth = Math.max(...instructions.map(instruction => instruction.address.length));
|
|
296
|
+
const bytesWidth = Math.max(...instructions.map(instruction => instruction.instructionBytes?.length ?? 0), 2);
|
|
297
|
+
for (const instruction of instructions) {
|
|
298
|
+
const location = formatSourceLabel(instruction.location, instruction.line, instruction.column);
|
|
299
|
+
const parts = [
|
|
300
|
+
instruction.address.padEnd(addressWidth),
|
|
301
|
+
(instruction.instructionBytes ?? "").padEnd(bytesWidth),
|
|
302
|
+
instruction.instruction,
|
|
303
|
+
];
|
|
304
|
+
if (instruction.symbol) {
|
|
305
|
+
parts.push(`<${instruction.symbol}>`);
|
|
306
|
+
}
|
|
307
|
+
if (location) {
|
|
308
|
+
parts.push(`[${location}]`);
|
|
309
|
+
}
|
|
310
|
+
lines.push(
|
|
311
|
+
parts
|
|
312
|
+
.filter(part => part.length > 0)
|
|
313
|
+
.join(" ")
|
|
314
|
+
.trimEnd(),
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
return lines.join("\n");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function formatMemoryRead(address: string, data: string | undefined, unreadableBytes?: number): string {
|
|
321
|
+
const lines = [`Memory at ${address}:`];
|
|
322
|
+
const buffer = data ? Buffer.from(data, "base64") : Buffer.alloc(0);
|
|
323
|
+
if (buffer.length === 0) {
|
|
324
|
+
lines.push("(no readable bytes)");
|
|
325
|
+
} else {
|
|
326
|
+
for (let offset = 0; offset < buffer.length; offset += 16) {
|
|
327
|
+
const chunk = buffer.subarray(offset, offset + 16);
|
|
328
|
+
const hex = Array.from(chunk, byte => byte.toString(16).padStart(2, "0")).join(" ");
|
|
329
|
+
const ascii = Array.from(chunk, byte => (byte >= 32 && byte < 127 ? String.fromCharCode(byte) : ".")).join("");
|
|
330
|
+
lines.push(
|
|
331
|
+
`${(offset === 0 ? address : `+0x${offset.toString(16)}`).padEnd(18)} ${hex.padEnd(47)} |${ascii}|`,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (unreadableBytes !== undefined && unreadableBytes > 0) {
|
|
336
|
+
lines.push(`Unreadable bytes: ${unreadableBytes}`);
|
|
337
|
+
}
|
|
338
|
+
return lines.join("\n");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function formatTable(headers: string[], rows: string[][]): string {
|
|
342
|
+
const widths = headers.map((header, index) =>
|
|
343
|
+
Math.max(header.length, ...rows.map(row => (row[index] ?? "").length)),
|
|
344
|
+
);
|
|
345
|
+
const formatRow = (row: string[]) => row.map((cell, index) => (cell ?? "").padEnd(widths[index])).join(" ");
|
|
346
|
+
return [formatRow(headers), formatRow(widths.map(width => "-".repeat(width))), ...rows.map(formatRow)].join("\n");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function formatModules(modules: DapModule[]): string {
|
|
350
|
+
if (modules.length === 0) {
|
|
351
|
+
return "Modules:\n(none)";
|
|
352
|
+
}
|
|
353
|
+
return [
|
|
354
|
+
"Modules:",
|
|
355
|
+
formatTable(
|
|
356
|
+
["ID", "Name", "Path", "Symbols", "Range"],
|
|
357
|
+
modules.map(module => [
|
|
358
|
+
String(module.id),
|
|
359
|
+
module.name,
|
|
360
|
+
module.path ?? "",
|
|
361
|
+
module.symbolStatus ?? "",
|
|
362
|
+
module.addressRange ?? "",
|
|
363
|
+
]),
|
|
364
|
+
),
|
|
365
|
+
].join("\n");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function formatLoadedSources(sources: DapSource[]): string {
|
|
369
|
+
const lines = ["Loaded sources:"];
|
|
370
|
+
if (sources.length === 0) {
|
|
371
|
+
lines.push("(none)");
|
|
372
|
+
return lines.join("\n");
|
|
373
|
+
}
|
|
374
|
+
for (const source of sources) {
|
|
375
|
+
const label = source.path ?? source.name ?? "<unknown>";
|
|
376
|
+
lines.push(`- ${label}${source.sourceReference !== undefined ? ` [ref=${source.sourceReference}]` : ""}`);
|
|
377
|
+
}
|
|
378
|
+
return lines.join("\n");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function formatInstructionBreakpoints(breakpoints: DapInstructionBreakpointRecord[]): string {
|
|
382
|
+
const lines = ["Instruction breakpoints:"];
|
|
383
|
+
if (breakpoints.length === 0) {
|
|
384
|
+
lines.push("(none)");
|
|
385
|
+
return lines.join("\n");
|
|
386
|
+
}
|
|
387
|
+
for (const breakpoint of breakpoints) {
|
|
388
|
+
const location = `${breakpoint.instructionReference}${breakpoint.offset !== undefined ? `+${breakpoint.offset}` : ""}`;
|
|
389
|
+
lines.push(
|
|
390
|
+
`- ${location}: ${breakpoint.verified ? "verified" : "pending"}${breakpoint.condition ? ` if ${breakpoint.condition}` : ""}${breakpoint.hitCondition ? ` after ${breakpoint.hitCondition}` : ""}${breakpoint.message ? ` (${breakpoint.message})` : ""}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
return lines.join("\n");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function formatDataBreakpointInfo(info: DapDataBreakpointInfoResponse): string {
|
|
397
|
+
const lines = [`Data breakpoint info: ${info.description}`];
|
|
398
|
+
lines.push(`Data ID: ${info.dataId ?? "(not available)"}`);
|
|
399
|
+
if (info.accessTypes && info.accessTypes.length > 0) {
|
|
400
|
+
lines.push(`Access types: ${info.accessTypes.join(", ")}`);
|
|
401
|
+
}
|
|
402
|
+
if (info.canPersist !== undefined) {
|
|
403
|
+
lines.push(`Persistent: ${info.canPersist ? "yes" : "no"}`);
|
|
404
|
+
}
|
|
405
|
+
return lines.join("\n");
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function formatDataBreakpoints(breakpoints: DapDataBreakpointRecord[]): string {
|
|
409
|
+
const lines = ["Data breakpoints:"];
|
|
410
|
+
if (breakpoints.length === 0) {
|
|
411
|
+
lines.push("(none)");
|
|
412
|
+
return lines.join("\n");
|
|
413
|
+
}
|
|
414
|
+
for (const breakpoint of breakpoints) {
|
|
415
|
+
lines.push(
|
|
416
|
+
`- ${breakpoint.dataId}: ${breakpoint.verified ? "verified" : "pending"}${breakpoint.accessType ? ` (${breakpoint.accessType})` : ""}${breakpoint.condition ? ` if ${breakpoint.condition}` : ""}${breakpoint.hitCondition ? ` after ${breakpoint.hitCondition}` : ""}${breakpoint.message ? ` (${breakpoint.message})` : ""}`,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
return lines.join("\n");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function formatCustomResponse(command: string, body: unknown): string {
|
|
423
|
+
let serialized = "";
|
|
424
|
+
try {
|
|
425
|
+
serialized = JSON.stringify(body, null, 2) ?? "null";
|
|
426
|
+
} catch {
|
|
427
|
+
serialized = Bun.inspect(body);
|
|
428
|
+
}
|
|
429
|
+
return `${command} response:\n${serialized}`;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function formatSessions(sessions: DapSessionSummary[]): string {
|
|
433
|
+
if (sessions.length === 0) {
|
|
434
|
+
return "No debug sessions.";
|
|
435
|
+
}
|
|
436
|
+
return sessions
|
|
437
|
+
.map(session => {
|
|
438
|
+
const location = formatLocation(session);
|
|
439
|
+
return [
|
|
440
|
+
`${session.id}: ${session.status}`,
|
|
441
|
+
` adapter=${session.adapter}`,
|
|
442
|
+
` cwd=${session.cwd}`,
|
|
443
|
+
...(session.program ? [` program=${session.program}`] : []),
|
|
444
|
+
...(location ? [` location=${location}`] : []),
|
|
445
|
+
...(session.stopReason ? [` reason=${session.stopReason}`] : []),
|
|
446
|
+
].join("\n");
|
|
447
|
+
})
|
|
448
|
+
.join("\n\n");
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function formatEvaluation(evaluation: DapEvaluateResponse): string {
|
|
452
|
+
const lines = [`Result: ${evaluation.result}`];
|
|
453
|
+
if (evaluation.type) lines.push(`Type: ${evaluation.type}`);
|
|
454
|
+
if (evaluation.variablesReference > 0) {
|
|
455
|
+
lines.push(`Variables ref: ${evaluation.variablesReference}`);
|
|
456
|
+
}
|
|
457
|
+
return lines.join("\n");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function buildOutcomeText(outcome: DapContinueOutcome, timeoutSec: number, verb: string): string {
|
|
461
|
+
const lines = formatSessionSnapshot(outcome.snapshot);
|
|
462
|
+
if (outcome.timedOut) {
|
|
463
|
+
lines.push(`Program is still running after ${timeoutSec}s. Use pause to interrupt and inspect state.`);
|
|
464
|
+
return lines.join("\n");
|
|
465
|
+
}
|
|
466
|
+
if (outcome.state === "stopped") {
|
|
467
|
+
lines.push(`${verb} stopped at ${formatLocation(outcome.snapshot) ?? "unknown location"}.`);
|
|
468
|
+
return lines.join("\n");
|
|
469
|
+
}
|
|
470
|
+
if (outcome.state === "terminated") {
|
|
471
|
+
lines.push(
|
|
472
|
+
`Program terminated${outcome.snapshot.exitCode !== undefined ? ` with exit code ${outcome.snapshot.exitCode}` : ""}.`,
|
|
473
|
+
);
|
|
474
|
+
return lines.join("\n");
|
|
475
|
+
}
|
|
476
|
+
lines.push("Program is running.");
|
|
477
|
+
return lines.join("\n");
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function getConfiguredAdapters(cwd: string): string {
|
|
481
|
+
const adapters = getAvailableAdapters(cwd).map(adapter => adapter.name);
|
|
482
|
+
return adapters.length > 0 ? adapters.join(", ") : "none";
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
interface DebugRenderArgs extends Partial<DebugParams> {}
|
|
486
|
+
|
|
487
|
+
function getActiveSessionSnapshot(): DapSessionSummary {
|
|
488
|
+
const snapshot = dapSessionManager.getActiveSession();
|
|
489
|
+
if (!snapshot) {
|
|
490
|
+
throw new ToolError("No active debug session. Launch or attach first.");
|
|
491
|
+
}
|
|
492
|
+
return snapshot;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function requireCapability(capability: keyof DapCapabilities, description: string): DapSessionSummary {
|
|
496
|
+
const snapshot = getActiveSessionSnapshot();
|
|
497
|
+
if (dapSessionManager.getCapabilities()?.[capability] !== true) {
|
|
498
|
+
throw new ToolError(`Current adapter does not support ${description}`);
|
|
499
|
+
}
|
|
500
|
+
return snapshot;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function resolveDisassemblyReference(memoryReference: string | undefined): string {
|
|
504
|
+
if (memoryReference) {
|
|
505
|
+
return memoryReference;
|
|
506
|
+
}
|
|
507
|
+
const snapshot = getActiveSessionSnapshot();
|
|
508
|
+
if (snapshot.instructionPointerReference) {
|
|
509
|
+
return snapshot.instructionPointerReference;
|
|
510
|
+
}
|
|
511
|
+
throw new ToolError(
|
|
512
|
+
"disassemble requires memory_reference unless the current stop location has an instruction pointer reference",
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function summarizeDebugCall(args: DebugRenderArgs): string {
|
|
517
|
+
const action = args.action ? args.action.replaceAll("_", " ") : "request";
|
|
518
|
+
if (args.program) {
|
|
519
|
+
return `${action} ${truncateToWidth(args.program, TRUNCATE_LENGTHS.TITLE)}`;
|
|
520
|
+
}
|
|
521
|
+
if (args.file && args.line !== undefined) {
|
|
522
|
+
return `${action} ${truncateToWidth(`${args.file}:${args.line}`, TRUNCATE_LENGTHS.TITLE)}`;
|
|
523
|
+
}
|
|
524
|
+
if (args.function) {
|
|
525
|
+
return `${action} ${truncateToWidth(args.function, TRUNCATE_LENGTHS.TITLE)}`;
|
|
526
|
+
}
|
|
527
|
+
if (args.expression) {
|
|
528
|
+
return `${action} ${truncateToWidth(args.expression, TRUNCATE_LENGTHS.TITLE)}`;
|
|
529
|
+
}
|
|
530
|
+
if (args.command) {
|
|
531
|
+
return `${action} ${truncateToWidth(args.command, TRUNCATE_LENGTHS.TITLE)}`;
|
|
532
|
+
}
|
|
533
|
+
if (args.memory_reference) {
|
|
534
|
+
return `${action} ${truncateToWidth(args.memory_reference, TRUNCATE_LENGTHS.TITLE)}`;
|
|
535
|
+
}
|
|
536
|
+
if (args.instruction_reference) {
|
|
537
|
+
return `${action} ${truncateToWidth(args.instruction_reference, TRUNCATE_LENGTHS.TITLE)}`;
|
|
538
|
+
}
|
|
539
|
+
if (args.data_id) {
|
|
540
|
+
return `${action} ${truncateToWidth(args.data_id, TRUNCATE_LENGTHS.TITLE)}`;
|
|
541
|
+
}
|
|
542
|
+
if (args.name) {
|
|
543
|
+
return `${action} ${truncateToWidth(args.name, TRUNCATE_LENGTHS.TITLE)}`;
|
|
544
|
+
}
|
|
545
|
+
return action;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export const debugToolRenderer = {
|
|
549
|
+
renderCall(args: DebugRenderArgs, _options: RenderResultOptions, theme: Theme): Component {
|
|
550
|
+
const text = renderStatusLine({ icon: "pending", title: "Debug", description: summarizeDebugCall(args) }, theme);
|
|
551
|
+
return new Text(text, 0, 0);
|
|
552
|
+
},
|
|
553
|
+
|
|
554
|
+
renderResult(
|
|
555
|
+
result: { content: Array<{ type: string; text?: string }>; details?: DebugToolDetails; isError?: boolean },
|
|
556
|
+
options: RenderResultOptions,
|
|
557
|
+
theme: Theme,
|
|
558
|
+
args?: DebugRenderArgs,
|
|
559
|
+
): Component {
|
|
560
|
+
const outputBlock = new CachedOutputBlock();
|
|
561
|
+
return {
|
|
562
|
+
render(width: number): string[] {
|
|
563
|
+
const action = (args?.action ?? result.details?.action ?? "debug").replaceAll("_", " ");
|
|
564
|
+
const status = options.isPartial ? "running" : result.isError ? "error" : "success";
|
|
565
|
+
const header = `${formatStatusIcon(status, theme, options.spinnerFrame)} Debug ${action}`;
|
|
566
|
+
const summaryLines = result.details?.snapshot
|
|
567
|
+
? formatSessionSnapshot(result.details.snapshot).map(line => replaceTabs(line))
|
|
568
|
+
: [];
|
|
569
|
+
const text = result.content.find(block => block.type === "text")?.text ?? "No output";
|
|
570
|
+
const rawLines = replaceTabs(text).split("\n");
|
|
571
|
+
const previewLimit = options.expanded ? PREVIEW_LIMITS.EXPANDED_LINES : PREVIEW_LIMITS.COLLAPSED_LINES;
|
|
572
|
+
const displayedLines = rawLines
|
|
573
|
+
.slice(0, previewLimit)
|
|
574
|
+
.map(line => truncateToWidth(line, TRUNCATE_LENGTHS.LINE));
|
|
575
|
+
const remaining = rawLines.length - displayedLines.length;
|
|
576
|
+
if (remaining > 0) {
|
|
577
|
+
displayedLines.push(
|
|
578
|
+
theme.fg("muted", `… ${remaining} more lines ${formatExpandHint(theme, options.expanded, true)}`),
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
return outputBlock.render(
|
|
582
|
+
{
|
|
583
|
+
header,
|
|
584
|
+
state: result.isError ? "error" : "success",
|
|
585
|
+
sections: [
|
|
586
|
+
...(summaryLines.length > 0
|
|
587
|
+
? [{ label: theme.fg("toolTitle", "Session"), lines: summaryLines }]
|
|
588
|
+
: []),
|
|
589
|
+
{ label: theme.fg("toolTitle", "Output"), lines: displayedLines },
|
|
590
|
+
],
|
|
591
|
+
width,
|
|
592
|
+
applyBg: false,
|
|
593
|
+
},
|
|
594
|
+
theme,
|
|
595
|
+
);
|
|
596
|
+
},
|
|
597
|
+
invalidate() {
|
|
598
|
+
outputBlock.invalidate();
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
},
|
|
602
|
+
mergeCallAndResult: true,
|
|
603
|
+
inline: true,
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
export class DebugTool implements AgentTool<typeof debugSchema, DebugToolDetails> {
|
|
607
|
+
readonly name = "debug";
|
|
608
|
+
readonly label = "Debug";
|
|
609
|
+
readonly description: string;
|
|
610
|
+
readonly parameters = debugSchema;
|
|
611
|
+
readonly strict = true;
|
|
612
|
+
readonly concurrency = "exclusive";
|
|
613
|
+
|
|
614
|
+
constructor(private readonly session: ToolSession) {
|
|
615
|
+
this.description = prompt.render(debugDescription);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
static createIf(session: ToolSession): DebugTool | null {
|
|
619
|
+
return session.settings.get("debug.enabled") ? new DebugTool(session) : null;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
async execute(
|
|
623
|
+
_toolCallId: string,
|
|
624
|
+
params: DebugParams,
|
|
625
|
+
signal?: AbortSignal,
|
|
626
|
+
_onUpdate?: AgentToolUpdateCallback<DebugToolDetails>,
|
|
627
|
+
_context?: AgentToolContext,
|
|
628
|
+
): Promise<AgentToolResult<DebugToolDetails>> {
|
|
629
|
+
const timeoutSec = clampTimeout("debug", params.timeout);
|
|
630
|
+
const timeoutSignal = AbortSignal.timeout(timeoutSec * 1000);
|
|
631
|
+
const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
632
|
+
const details: DebugToolDetails = { action: params.action, success: true };
|
|
633
|
+
const result = toolResult(details);
|
|
634
|
+
switch (params.action) {
|
|
635
|
+
case "launch": {
|
|
636
|
+
if (!params.program) {
|
|
637
|
+
throw new ToolError("program is required for launch");
|
|
638
|
+
}
|
|
639
|
+
const commandCwd = params.cwd ? resolveToCwd(params.cwd, this.session.cwd) : this.session.cwd;
|
|
640
|
+
const program = resolveToCwd(params.program, commandCwd);
|
|
641
|
+
const adapter = selectLaunchAdapter(program, commandCwd, params.adapter);
|
|
642
|
+
if (!adapter) {
|
|
643
|
+
throw new ToolError(
|
|
644
|
+
`No debugger adapter available. Installed adapters: ${getConfiguredAdapters(commandCwd)}`,
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
const snapshot = await dapSessionManager.launch(
|
|
648
|
+
{ adapter, program, args: params.args, cwd: commandCwd },
|
|
649
|
+
combinedSignal,
|
|
650
|
+
timeoutSec * 1000,
|
|
651
|
+
);
|
|
652
|
+
details.snapshot = snapshot;
|
|
653
|
+
details.adapter = adapter.name;
|
|
654
|
+
return result.text(formatSessionSnapshot(snapshot).join("\n")).done();
|
|
655
|
+
}
|
|
656
|
+
case "attach": {
|
|
657
|
+
if (params.pid === undefined && params.port === undefined) {
|
|
658
|
+
throw new ToolError("attach requires pid or port");
|
|
659
|
+
}
|
|
660
|
+
const commandCwd = params.cwd ? resolveToCwd(params.cwd, this.session.cwd) : this.session.cwd;
|
|
661
|
+
const adapter = selectAttachAdapter(commandCwd, params.adapter, params.port);
|
|
662
|
+
if (!adapter) {
|
|
663
|
+
throw new ToolError(
|
|
664
|
+
`No debugger adapter available. Installed adapters: ${getConfiguredAdapters(commandCwd)}`,
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
const snapshot = await dapSessionManager.attach(
|
|
668
|
+
{ adapter, cwd: commandCwd, pid: params.pid, port: params.port, host: params.host },
|
|
669
|
+
combinedSignal,
|
|
670
|
+
timeoutSec * 1000,
|
|
671
|
+
);
|
|
672
|
+
details.snapshot = snapshot;
|
|
673
|
+
details.adapter = adapter.name;
|
|
674
|
+
return result.text(formatSessionSnapshot(snapshot).join("\n")).done();
|
|
675
|
+
}
|
|
676
|
+
case "set_breakpoint": {
|
|
677
|
+
if (params.function) {
|
|
678
|
+
const response = await dapSessionManager.setFunctionBreakpoint(
|
|
679
|
+
params.function,
|
|
680
|
+
params.condition,
|
|
681
|
+
combinedSignal,
|
|
682
|
+
timeoutSec * 1000,
|
|
683
|
+
);
|
|
684
|
+
details.snapshot = response.snapshot;
|
|
685
|
+
details.functionBreakpoints = response.breakpoints;
|
|
686
|
+
return result.text(formatFunctionBreakpoints(response.breakpoints)).done();
|
|
687
|
+
}
|
|
688
|
+
if (!params.file || params.line === undefined) {
|
|
689
|
+
throw new ToolError("set_breakpoint requires file+line or function");
|
|
690
|
+
}
|
|
691
|
+
const file = resolveToCwd(params.file, this.session.cwd);
|
|
692
|
+
const response = await dapSessionManager.setBreakpoint(
|
|
693
|
+
file,
|
|
694
|
+
params.line,
|
|
695
|
+
params.condition,
|
|
696
|
+
combinedSignal,
|
|
697
|
+
timeoutSec * 1000,
|
|
698
|
+
);
|
|
699
|
+
details.snapshot = response.snapshot;
|
|
700
|
+
details.breakpoints = response.breakpoints;
|
|
701
|
+
return result.text(formatBreakpoints(response.sourcePath, response.breakpoints)).done();
|
|
702
|
+
}
|
|
703
|
+
case "remove_breakpoint": {
|
|
704
|
+
if (params.function) {
|
|
705
|
+
const response = await dapSessionManager.removeFunctionBreakpoint(
|
|
706
|
+
params.function,
|
|
707
|
+
combinedSignal,
|
|
708
|
+
timeoutSec * 1000,
|
|
709
|
+
);
|
|
710
|
+
details.snapshot = response.snapshot;
|
|
711
|
+
details.functionBreakpoints = response.breakpoints;
|
|
712
|
+
return result.text(formatFunctionBreakpoints(response.breakpoints)).done();
|
|
713
|
+
}
|
|
714
|
+
if (!params.file || params.line === undefined) {
|
|
715
|
+
throw new ToolError("remove_breakpoint requires file+line or function");
|
|
716
|
+
}
|
|
717
|
+
const file = resolveToCwd(params.file, this.session.cwd);
|
|
718
|
+
const response = await dapSessionManager.removeBreakpoint(
|
|
719
|
+
file,
|
|
720
|
+
params.line,
|
|
721
|
+
combinedSignal,
|
|
722
|
+
timeoutSec * 1000,
|
|
723
|
+
);
|
|
724
|
+
details.snapshot = response.snapshot;
|
|
725
|
+
details.breakpoints = response.breakpoints;
|
|
726
|
+
return result.text(formatBreakpoints(response.sourcePath, response.breakpoints)).done();
|
|
727
|
+
}
|
|
728
|
+
case "set_instruction_breakpoint": {
|
|
729
|
+
requireCapability("supportsInstructionBreakpoints", "instruction breakpoints");
|
|
730
|
+
if (!params.instruction_reference) {
|
|
731
|
+
throw new ToolError("instruction_reference is required for set_instruction_breakpoint");
|
|
732
|
+
}
|
|
733
|
+
const response = await dapSessionManager.setInstructionBreakpoint(
|
|
734
|
+
params.instruction_reference,
|
|
735
|
+
params.offset,
|
|
736
|
+
params.condition,
|
|
737
|
+
params.hit_condition,
|
|
738
|
+
combinedSignal,
|
|
739
|
+
timeoutSec * 1000,
|
|
740
|
+
);
|
|
741
|
+
details.snapshot = response.snapshot;
|
|
742
|
+
details.instructionBreakpoints = response.breakpoints;
|
|
743
|
+
return result.text(formatInstructionBreakpoints(response.breakpoints)).done();
|
|
744
|
+
}
|
|
745
|
+
case "remove_instruction_breakpoint": {
|
|
746
|
+
requireCapability("supportsInstructionBreakpoints", "instruction breakpoints");
|
|
747
|
+
if (!params.instruction_reference) {
|
|
748
|
+
throw new ToolError("instruction_reference is required for remove_instruction_breakpoint");
|
|
749
|
+
}
|
|
750
|
+
const response = await dapSessionManager.removeInstructionBreakpoint(
|
|
751
|
+
params.instruction_reference,
|
|
752
|
+
params.offset,
|
|
753
|
+
combinedSignal,
|
|
754
|
+
timeoutSec * 1000,
|
|
755
|
+
);
|
|
756
|
+
details.snapshot = response.snapshot;
|
|
757
|
+
details.instructionBreakpoints = response.breakpoints;
|
|
758
|
+
return result.text(formatInstructionBreakpoints(response.breakpoints)).done();
|
|
759
|
+
}
|
|
760
|
+
case "data_breakpoint_info": {
|
|
761
|
+
requireCapability("supportsDataBreakpoints", "data breakpoints");
|
|
762
|
+
if (!params.name) {
|
|
763
|
+
throw new ToolError("name is required for data_breakpoint_info");
|
|
764
|
+
}
|
|
765
|
+
const response = await dapSessionManager.dataBreakpointInfo(
|
|
766
|
+
params.name,
|
|
767
|
+
params.variable_ref ?? params.scope_id,
|
|
768
|
+
params.frame_id,
|
|
769
|
+
combinedSignal,
|
|
770
|
+
timeoutSec * 1000,
|
|
771
|
+
);
|
|
772
|
+
details.snapshot = response.snapshot;
|
|
773
|
+
details.dataBreakpointInfo = response.info;
|
|
774
|
+
return result.text(formatDataBreakpointInfo(response.info)).done();
|
|
775
|
+
}
|
|
776
|
+
case "set_data_breakpoint": {
|
|
777
|
+
requireCapability("supportsDataBreakpoints", "data breakpoints");
|
|
778
|
+
if (!params.data_id) {
|
|
779
|
+
throw new ToolError("data_id is required for set_data_breakpoint");
|
|
780
|
+
}
|
|
781
|
+
const response = await dapSessionManager.setDataBreakpoint(
|
|
782
|
+
params.data_id,
|
|
783
|
+
params.access_type,
|
|
784
|
+
params.condition,
|
|
785
|
+
params.hit_condition,
|
|
786
|
+
combinedSignal,
|
|
787
|
+
timeoutSec * 1000,
|
|
788
|
+
);
|
|
789
|
+
details.snapshot = response.snapshot;
|
|
790
|
+
details.dataBreakpoints = response.breakpoints;
|
|
791
|
+
return result.text(formatDataBreakpoints(response.breakpoints)).done();
|
|
792
|
+
}
|
|
793
|
+
case "remove_data_breakpoint": {
|
|
794
|
+
requireCapability("supportsDataBreakpoints", "data breakpoints");
|
|
795
|
+
if (!params.data_id) {
|
|
796
|
+
throw new ToolError("data_id is required for remove_data_breakpoint");
|
|
797
|
+
}
|
|
798
|
+
const response = await dapSessionManager.removeDataBreakpoint(
|
|
799
|
+
params.data_id,
|
|
800
|
+
combinedSignal,
|
|
801
|
+
timeoutSec * 1000,
|
|
802
|
+
);
|
|
803
|
+
details.snapshot = response.snapshot;
|
|
804
|
+
details.dataBreakpoints = response.breakpoints;
|
|
805
|
+
return result.text(formatDataBreakpoints(response.breakpoints)).done();
|
|
806
|
+
}
|
|
807
|
+
case "continue": {
|
|
808
|
+
const outcome = await dapSessionManager.continue(combinedSignal, timeoutSec * 1000);
|
|
809
|
+
details.snapshot = outcome.snapshot;
|
|
810
|
+
details.state = outcome.state;
|
|
811
|
+
details.timedOut = outcome.timedOut;
|
|
812
|
+
return result.text(buildOutcomeText(outcome, timeoutSec, "Continue")).done();
|
|
813
|
+
}
|
|
814
|
+
case "step_over": {
|
|
815
|
+
const outcome = await dapSessionManager.stepOver(combinedSignal, timeoutSec * 1000);
|
|
816
|
+
details.snapshot = outcome.snapshot;
|
|
817
|
+
details.state = outcome.state;
|
|
818
|
+
details.timedOut = outcome.timedOut;
|
|
819
|
+
return result.text(buildOutcomeText(outcome, timeoutSec, "Step over")).done();
|
|
820
|
+
}
|
|
821
|
+
case "step_in": {
|
|
822
|
+
const outcome = await dapSessionManager.stepIn(combinedSignal, timeoutSec * 1000);
|
|
823
|
+
details.snapshot = outcome.snapshot;
|
|
824
|
+
details.state = outcome.state;
|
|
825
|
+
details.timedOut = outcome.timedOut;
|
|
826
|
+
return result.text(buildOutcomeText(outcome, timeoutSec, "Step in")).done();
|
|
827
|
+
}
|
|
828
|
+
case "step_out": {
|
|
829
|
+
const outcome = await dapSessionManager.stepOut(combinedSignal, timeoutSec * 1000);
|
|
830
|
+
details.snapshot = outcome.snapshot;
|
|
831
|
+
details.state = outcome.state;
|
|
832
|
+
details.timedOut = outcome.timedOut;
|
|
833
|
+
return result.text(buildOutcomeText(outcome, timeoutSec, "Step out")).done();
|
|
834
|
+
}
|
|
835
|
+
case "pause": {
|
|
836
|
+
const snapshot = await dapSessionManager.pause(combinedSignal, timeoutSec * 1000);
|
|
837
|
+
details.snapshot = snapshot;
|
|
838
|
+
return result.text(formatSessionSnapshot(snapshot).concat("Program paused.").join("\n")).done();
|
|
839
|
+
}
|
|
840
|
+
case "evaluate": {
|
|
841
|
+
if (!params.expression) {
|
|
842
|
+
throw new ToolError("expression is required for evaluate");
|
|
843
|
+
}
|
|
844
|
+
const evaluationContext = (params.context as DapEvaluateArguments["context"] | undefined) ?? "repl";
|
|
845
|
+
const response = await dapSessionManager.evaluate(
|
|
846
|
+
params.expression,
|
|
847
|
+
evaluationContext,
|
|
848
|
+
params.frame_id,
|
|
849
|
+
combinedSignal,
|
|
850
|
+
timeoutSec * 1000,
|
|
851
|
+
);
|
|
852
|
+
details.snapshot = response.snapshot;
|
|
853
|
+
details.evaluation = response.evaluation;
|
|
854
|
+
return result.text(formatEvaluation(response.evaluation)).done();
|
|
855
|
+
}
|
|
856
|
+
case "stack_trace": {
|
|
857
|
+
const response = await dapSessionManager.stackTrace(params.levels, combinedSignal, timeoutSec * 1000);
|
|
858
|
+
details.snapshot = response.snapshot;
|
|
859
|
+
details.stackFrames = response.stackFrames;
|
|
860
|
+
return result.text(formatStackFrames(response.stackFrames)).done();
|
|
861
|
+
}
|
|
862
|
+
case "threads": {
|
|
863
|
+
const response = await dapSessionManager.threads(combinedSignal, timeoutSec * 1000);
|
|
864
|
+
details.snapshot = response.snapshot;
|
|
865
|
+
details.threads = response.threads;
|
|
866
|
+
return result.text(formatThreads(response.threads)).done();
|
|
867
|
+
}
|
|
868
|
+
case "scopes": {
|
|
869
|
+
const response = await dapSessionManager.scopes(params.frame_id, combinedSignal, timeoutSec * 1000);
|
|
870
|
+
details.snapshot = response.snapshot;
|
|
871
|
+
details.scopes = response.scopes;
|
|
872
|
+
return result.text(formatScopes(response.scopes)).done();
|
|
873
|
+
}
|
|
874
|
+
case "variables": {
|
|
875
|
+
const variableReference = params.variable_ref ?? params.scope_id;
|
|
876
|
+
if (variableReference === undefined) {
|
|
877
|
+
throw new ToolError("variables requires variable_ref or scope_id");
|
|
878
|
+
}
|
|
879
|
+
const response = await dapSessionManager.variables(variableReference, combinedSignal, timeoutSec * 1000);
|
|
880
|
+
details.snapshot = response.snapshot;
|
|
881
|
+
details.variables = response.variables;
|
|
882
|
+
return result.text(formatVariables(response.variables)).done();
|
|
883
|
+
}
|
|
884
|
+
case "disassemble": {
|
|
885
|
+
requireCapability("supportsDisassembleRequest", "disassembly");
|
|
886
|
+
if (params.instruction_count === undefined) {
|
|
887
|
+
throw new ToolError("instruction_count is required for disassemble");
|
|
888
|
+
}
|
|
889
|
+
const response = await dapSessionManager.disassemble(
|
|
890
|
+
resolveDisassemblyReference(params.memory_reference),
|
|
891
|
+
params.instruction_count,
|
|
892
|
+
params.offset,
|
|
893
|
+
params.instruction_offset,
|
|
894
|
+
params.resolve_symbols,
|
|
895
|
+
combinedSignal,
|
|
896
|
+
timeoutSec * 1000,
|
|
897
|
+
);
|
|
898
|
+
details.snapshot = response.snapshot;
|
|
899
|
+
details.disassembly = response.instructions;
|
|
900
|
+
return result.text(formatDisassembly(response.instructions)).done();
|
|
901
|
+
}
|
|
902
|
+
case "read_memory": {
|
|
903
|
+
requireCapability("supportsReadMemoryRequest", "memory reads");
|
|
904
|
+
if (!params.memory_reference) {
|
|
905
|
+
throw new ToolError("memory_reference is required for read_memory");
|
|
906
|
+
}
|
|
907
|
+
if (params.count === undefined) {
|
|
908
|
+
throw new ToolError("count is required for read_memory");
|
|
909
|
+
}
|
|
910
|
+
const response = await dapSessionManager.readMemory(
|
|
911
|
+
params.memory_reference,
|
|
912
|
+
params.count,
|
|
913
|
+
params.offset,
|
|
914
|
+
combinedSignal,
|
|
915
|
+
timeoutSec * 1000,
|
|
916
|
+
);
|
|
917
|
+
details.snapshot = response.snapshot;
|
|
918
|
+
details.memoryAddress = response.address;
|
|
919
|
+
details.memoryData = response.data;
|
|
920
|
+
details.unreadableBytes = response.unreadableBytes;
|
|
921
|
+
return result.text(formatMemoryRead(response.address, response.data, response.unreadableBytes)).done();
|
|
922
|
+
}
|
|
923
|
+
case "write_memory": {
|
|
924
|
+
requireCapability("supportsWriteMemoryRequest", "memory writes");
|
|
925
|
+
if (!params.memory_reference) {
|
|
926
|
+
throw new ToolError("memory_reference is required for write_memory");
|
|
927
|
+
}
|
|
928
|
+
if (!params.data) {
|
|
929
|
+
throw new ToolError("data is required for write_memory");
|
|
930
|
+
}
|
|
931
|
+
const response = await dapSessionManager.writeMemory(
|
|
932
|
+
params.memory_reference,
|
|
933
|
+
params.data,
|
|
934
|
+
params.offset,
|
|
935
|
+
params.allow_partial,
|
|
936
|
+
combinedSignal,
|
|
937
|
+
timeoutSec * 1000,
|
|
938
|
+
);
|
|
939
|
+
details.snapshot = response.snapshot;
|
|
940
|
+
details.bytesWritten = response.bytesWritten;
|
|
941
|
+
return result
|
|
942
|
+
.text(
|
|
943
|
+
[
|
|
944
|
+
"Memory write completed.",
|
|
945
|
+
...(response.bytesWritten !== undefined ? [`Bytes written: ${response.bytesWritten}`] : []),
|
|
946
|
+
...(response.offset !== undefined ? [`Offset: ${response.offset}`] : []),
|
|
947
|
+
].join("\n"),
|
|
948
|
+
)
|
|
949
|
+
.done();
|
|
950
|
+
}
|
|
951
|
+
case "modules": {
|
|
952
|
+
requireCapability("supportsModulesRequest", "module introspection");
|
|
953
|
+
const response = await dapSessionManager.modules(
|
|
954
|
+
params.start_module,
|
|
955
|
+
params.module_count,
|
|
956
|
+
combinedSignal,
|
|
957
|
+
timeoutSec * 1000,
|
|
958
|
+
);
|
|
959
|
+
details.snapshot = response.snapshot;
|
|
960
|
+
details.modules = response.modules;
|
|
961
|
+
return result.text(formatModules(response.modules)).done();
|
|
962
|
+
}
|
|
963
|
+
case "loaded_sources": {
|
|
964
|
+
requireCapability("supportsLoadedSourcesRequest", "loaded sources");
|
|
965
|
+
const response = await dapSessionManager.loadedSources(combinedSignal, timeoutSec * 1000);
|
|
966
|
+
details.snapshot = response.snapshot;
|
|
967
|
+
details.sources = response.sources;
|
|
968
|
+
return result.text(formatLoadedSources(response.sources)).done();
|
|
969
|
+
}
|
|
970
|
+
case "custom_request": {
|
|
971
|
+
if (!params.command) {
|
|
972
|
+
throw new ToolError("command is required for custom_request");
|
|
973
|
+
}
|
|
974
|
+
const response = await dapSessionManager.customRequest(
|
|
975
|
+
params.command,
|
|
976
|
+
params.arguments,
|
|
977
|
+
combinedSignal,
|
|
978
|
+
timeoutSec * 1000,
|
|
979
|
+
);
|
|
980
|
+
details.snapshot = response.snapshot;
|
|
981
|
+
details.customBody = response.body;
|
|
982
|
+
return result.text(formatCustomResponse(params.command, response.body)).done();
|
|
983
|
+
}
|
|
984
|
+
case "output": {
|
|
985
|
+
const response = dapSessionManager.getOutput();
|
|
986
|
+
details.snapshot = response.snapshot;
|
|
987
|
+
details.output = response.output;
|
|
988
|
+
return result.text(response.output.length > 0 ? response.output : "(no output captured)").done();
|
|
989
|
+
}
|
|
990
|
+
case "terminate": {
|
|
991
|
+
const snapshot = await dapSessionManager.terminate(combinedSignal, timeoutSec * 1000);
|
|
992
|
+
if (!snapshot) {
|
|
993
|
+
return result.text("No debug session to terminate.").done();
|
|
994
|
+
}
|
|
995
|
+
details.snapshot = snapshot;
|
|
996
|
+
return result.text(formatSessionSnapshot(snapshot).concat("Debug session terminated.").join("\n")).done();
|
|
997
|
+
}
|
|
998
|
+
case "sessions": {
|
|
999
|
+
const sessions = dapSessionManager.listSessions();
|
|
1000
|
+
details.sessions = sessions;
|
|
1001
|
+
return result.text(formatSessions(sessions)).done();
|
|
1002
|
+
}
|
|
1003
|
+
default:
|
|
1004
|
+
throw new ToolError(`Unsupported debug action: ${params.action}`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|