@mrclrchtr/supi-debug 4.10.0 → 6.0.0
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/README.md +15 -7
- package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -6
- package/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/package.json +3 -2
- package/prompts/supi-tooling-retro.md +77 -0
- package/src/command.ts +132 -0
- package/src/config.ts +83 -0
- package/src/debug.ts +19 -309
- package/src/format-events.ts +101 -0
- package/src/query.ts +45 -0
- package/src/render-details.ts +336 -0
- package/src/renderer.ts +134 -59
- package/src/session-events.ts +88 -14
- package/src/tool/debug/execute.ts +20 -0
- package/src/tool/debug/guidance.ts +11 -0
- package/src/tool/debug/register.ts +16 -0
- package/src/tool/debug/render.ts +105 -0
- package/src/tool/debug/result.ts +113 -0
- package/src/tool/debug/spec.ts +35 -0
- package/src/tool/guidance.ts +0 -11
package/src/session-events.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type DebugEventQuery,
|
|
5
5
|
type DebugEventView,
|
|
6
6
|
isDebugLevel,
|
|
7
|
+
isDebugOperationId,
|
|
7
8
|
matchesDebugEventQuery,
|
|
8
9
|
redactDebugData,
|
|
9
10
|
} from "@mrclrchtr/supi-core/debug";
|
|
@@ -11,7 +12,10 @@ import {
|
|
|
11
12
|
/** Custom session-entry type used for sanitized debug-event persistence. */
|
|
12
13
|
export const DEBUG_EVENT_ENTRY_TYPE = "supi-debug-event";
|
|
13
14
|
|
|
14
|
-
type PersistedDebugEventQuery = Pick<
|
|
15
|
+
type PersistedDebugEventQuery = Pick<
|
|
16
|
+
DebugEventQuery,
|
|
17
|
+
"operationId" | "source" | "level" | "category" | "limit"
|
|
18
|
+
>;
|
|
15
19
|
|
|
16
20
|
/** Sanitized events and total persisted entries found in one PI session file. */
|
|
17
21
|
export interface SessionDebugEvents {
|
|
@@ -19,6 +23,19 @@ export interface SessionDebugEvents {
|
|
|
19
23
|
persistedEventCount: number;
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
/** Small progress facts emitted while a persisted session file is scanned. */
|
|
27
|
+
export interface SessionDebugReadProgress {
|
|
28
|
+
scannedLines: number;
|
|
29
|
+
persistedEventCount: number;
|
|
30
|
+
matchedEvents: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Optional cancellation and progress controls for persisted-session reads. */
|
|
34
|
+
export interface SessionDebugReadOptions {
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
onProgress?: (progress: SessionDebugReadProgress) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
22
39
|
function parsePersistedEvent(data: unknown): DebugEventView | undefined {
|
|
23
40
|
if (typeof data !== "object" || data === null) return undefined;
|
|
24
41
|
const event = data as Record<string, unknown>;
|
|
@@ -31,6 +48,7 @@ function parsePersistedEvent(data: unknown): DebugEventView | undefined {
|
|
|
31
48
|
!isDebugLevel(event.level) ||
|
|
32
49
|
typeof event.category !== "string" ||
|
|
33
50
|
typeof event.message !== "string" ||
|
|
51
|
+
(event.operationId !== undefined && !isDebugOperationId(event.operationId)) ||
|
|
34
52
|
(event.cwd !== undefined && typeof event.cwd !== "string")
|
|
35
53
|
) {
|
|
36
54
|
return undefined;
|
|
@@ -39,6 +57,7 @@ function parsePersistedEvent(data: unknown): DebugEventView | undefined {
|
|
|
39
57
|
return {
|
|
40
58
|
id: event.id,
|
|
41
59
|
timestamp: event.timestamp,
|
|
60
|
+
operationId: event.operationId,
|
|
42
61
|
source: event.source,
|
|
43
62
|
level: event.level,
|
|
44
63
|
category: event.category,
|
|
@@ -57,32 +76,87 @@ function parseDebugEntry(line: string): unknown {
|
|
|
57
76
|
}
|
|
58
77
|
}
|
|
59
78
|
|
|
79
|
+
function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
80
|
+
if (!signal?.aborted) return;
|
|
81
|
+
const error = new Error("Persisted debug-event scan was canceled.");
|
|
82
|
+
error.name = "AbortError";
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function reportProgress(
|
|
87
|
+
onProgress: SessionDebugReadOptions["onProgress"],
|
|
88
|
+
progress: SessionDebugReadProgress,
|
|
89
|
+
): void {
|
|
90
|
+
onProgress?.(progress);
|
|
91
|
+
}
|
|
92
|
+
|
|
60
93
|
/** Read sanitized debug events persisted by SuPi Debug from a PI session file. */
|
|
61
94
|
export async function readSessionDebugEvents(
|
|
62
95
|
sessionFile: string,
|
|
63
96
|
query: PersistedDebugEventQuery = {},
|
|
97
|
+
options: SessionDebugReadOptions = {},
|
|
64
98
|
): Promise<SessionDebugEvents> {
|
|
65
99
|
const events: DebugEventView[] = [];
|
|
66
100
|
let persistedEventCount = 0;
|
|
101
|
+
let scannedLines = 0;
|
|
102
|
+
throwIfAborted(options.signal);
|
|
103
|
+
|
|
104
|
+
const input = createReadStream(sessionFile, { encoding: "utf8" });
|
|
105
|
+
const abortHandler = () => input.destroy();
|
|
106
|
+
options.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
67
107
|
const lines = createInterface({
|
|
68
|
-
input
|
|
108
|
+
input,
|
|
69
109
|
crlfDelay: Number.POSITIVE_INFINITY,
|
|
70
110
|
});
|
|
71
111
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
(
|
|
79
|
-
|
|
80
|
-
|
|
112
|
+
try {
|
|
113
|
+
throwIfAborted(options.signal);
|
|
114
|
+
reportProgress(options.onProgress, { scannedLines, persistedEventCount, matchedEvents: 0 });
|
|
115
|
+
|
|
116
|
+
for await (const line of lines) {
|
|
117
|
+
scannedLines++;
|
|
118
|
+
throwIfAborted(options.signal);
|
|
119
|
+
const entry = parseDebugEntry(line);
|
|
120
|
+
if (
|
|
121
|
+
typeof entry !== "object" ||
|
|
122
|
+
entry === null ||
|
|
123
|
+
(entry as Record<string, unknown>).type !== "custom" ||
|
|
124
|
+
(entry as Record<string, unknown>).customType !== DEBUG_EVENT_ENTRY_TYPE
|
|
125
|
+
) {
|
|
126
|
+
if (scannedLines % 250 === 0) {
|
|
127
|
+
reportProgress(options.onProgress, {
|
|
128
|
+
scannedLines,
|
|
129
|
+
persistedEventCount,
|
|
130
|
+
matchedEvents: events.length,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
persistedEventCount++;
|
|
137
|
+
const event = parsePersistedEvent((entry as Record<string, unknown>).data);
|
|
138
|
+
if (event && matchesDebugEventQuery(event, query)) events.push(event);
|
|
139
|
+
|
|
140
|
+
if (scannedLines % 250 === 0) {
|
|
141
|
+
reportProgress(options.onProgress, {
|
|
142
|
+
scannedLines,
|
|
143
|
+
persistedEventCount,
|
|
144
|
+
matchedEvents: events.length,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
81
147
|
}
|
|
82
148
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
149
|
+
throwIfAborted(options.signal);
|
|
150
|
+
reportProgress(options.onProgress, {
|
|
151
|
+
scannedLines,
|
|
152
|
+
persistedEventCount,
|
|
153
|
+
matchedEvents: events.length,
|
|
154
|
+
});
|
|
155
|
+
throwIfAborted(options.signal);
|
|
156
|
+
} finally {
|
|
157
|
+
options.signal?.removeEventListener("abort", abortHandler);
|
|
158
|
+
lines.close();
|
|
159
|
+
input.destroy();
|
|
86
160
|
}
|
|
87
161
|
|
|
88
162
|
const limit = query.limit && query.limit > 0 ? Math.floor(query.limit) : Number.POSITIVE_INFINITY;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { isDebugOperationId } from "@mrclrchtr/supi-core/debug";
|
|
3
|
+
import { applyDebugConfig } from "../../config.ts";
|
|
4
|
+
import type { DebugToolParams } from "../../query.ts";
|
|
5
|
+
import { buildToolResult } from "./result.ts";
|
|
6
|
+
|
|
7
|
+
type DebugExecute = NonNullable<Parameters<ExtensionAPI["registerTool"]>[0]["execute"]>;
|
|
8
|
+
|
|
9
|
+
/** Build the debug tool execute function. */
|
|
10
|
+
export function makeDebugExecute(): DebugExecute {
|
|
11
|
+
// biome-ignore lint/complexity/useMaxParams: pi ToolDefinition.execute signature
|
|
12
|
+
return async (_toolCallId, params, signal, onUpdate, ctx) => {
|
|
13
|
+
const query = params as DebugToolParams;
|
|
14
|
+
if (query.operationId !== undefined && !isDebugOperationId(query.operationId)) {
|
|
15
|
+
throw new Error("Invalid Debug Operation ID");
|
|
16
|
+
}
|
|
17
|
+
const config = applyDebugConfig(ctx.cwd);
|
|
18
|
+
return buildToolResult(query, { config, cwd: ctx.cwd, signal, onUpdate });
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Prompt guidance and tool description for the debug tool.
|
|
2
|
+
|
|
3
|
+
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
|
|
5
|
+
export const toolDescription = `Fetch recent SuPi debug events, or sanitized persisted events from a PI session JSONL via sessionFile, with optional exact Debug Operation ID and event filters. Raw data is available only for the live session when allowed. Output is truncated to ${DEFAULT_MAX_LINES} lines or ${formatSize(DEFAULT_MAX_BYTES)} (whichever is hit first).`;
|
|
6
|
+
|
|
7
|
+
export const promptSnippet = "debug — fetch live or persisted SuPi debug events";
|
|
8
|
+
|
|
9
|
+
export const promptGuidelines = [
|
|
10
|
+
"Use debug for SuPi failures, fallback reasons, or session debug events; pass sessionFile to inspect a prior session and request raw data only when explicitly asked and settings allow it.",
|
|
11
|
+
];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { promptGuidelines, promptSnippet, toolDescription } from "./guidance.ts";
|
|
3
|
+
import { renderDebugToolCall, renderDebugToolResult } from "./render.ts";
|
|
4
|
+
import { debugSpec } from "./spec.ts";
|
|
5
|
+
|
|
6
|
+
/** Register the debug agent tool. */
|
|
7
|
+
export function registerDebugTool(pi: ExtensionAPI): void {
|
|
8
|
+
pi.registerTool({
|
|
9
|
+
...debugSpec,
|
|
10
|
+
description: toolDescription,
|
|
11
|
+
promptSnippet,
|
|
12
|
+
promptGuidelines,
|
|
13
|
+
renderCall: renderDebugToolCall,
|
|
14
|
+
renderResult: renderDebugToolResult,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** Transcript renderers for the debug tool. */
|
|
2
|
+
|
|
3
|
+
import { keyHint, type Theme, type ToolRenderResultOptions } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
5
|
+
import { type DebugRenderDetails, readDebugRenderDetails } from "../../render-details.ts";
|
|
6
|
+
import { renderExpandedReport } from "../../renderer.ts";
|
|
7
|
+
|
|
8
|
+
interface DebugToolResult {
|
|
9
|
+
content?: Array<{ type: string; text?: string }>;
|
|
10
|
+
details?: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface DebugCallArgs {
|
|
14
|
+
operationId?: unknown;
|
|
15
|
+
source?: unknown;
|
|
16
|
+
level?: unknown;
|
|
17
|
+
category?: unknown;
|
|
18
|
+
limit?: unknown;
|
|
19
|
+
sessionFile?: unknown;
|
|
20
|
+
includeRaw?: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function boundedCallValue(value: unknown, maxLength = 80): string | undefined {
|
|
24
|
+
if (typeof value !== "string" && typeof value !== "number") return undefined;
|
|
25
|
+
const text = String(value);
|
|
26
|
+
return text.length > maxLength ? `${text.slice(0, maxLength - 1)}…` : text;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function formatCallArgs(args: unknown): string {
|
|
30
|
+
const input = (args ?? {}) as DebugCallArgs;
|
|
31
|
+
const filters: string[] = [];
|
|
32
|
+
const operationId = boundedCallValue(input.operationId);
|
|
33
|
+
const source = boundedCallValue(input.source);
|
|
34
|
+
const level = boundedCallValue(input.level);
|
|
35
|
+
const category = boundedCallValue(input.category);
|
|
36
|
+
const limit = boundedCallValue(input.limit);
|
|
37
|
+
const sessionFile = boundedCallValue(input.sessionFile);
|
|
38
|
+
|
|
39
|
+
if (operationId) filters.push(`operationId=${operationId}`);
|
|
40
|
+
if (source) filters.push(`source=${source}`);
|
|
41
|
+
if (level) filters.push(`level=${level}`);
|
|
42
|
+
if (category) filters.push(`category=${category}`);
|
|
43
|
+
if (limit) filters.push(`limit=${limit}`);
|
|
44
|
+
if (sessionFile) filters.push(`sessionFile=${sessionFile}`);
|
|
45
|
+
if (input.includeRaw === true) filters.push("raw");
|
|
46
|
+
return filters.join(" ");
|
|
47
|
+
}
|
|
48
|
+
/** Render the compact human-facing call header for `debug`. */
|
|
49
|
+
export function renderDebugToolCall(args: unknown, theme: Theme): Text {
|
|
50
|
+
const callArgs = formatCallArgs(args);
|
|
51
|
+
let content = theme.fg("toolTitle", "debug");
|
|
52
|
+
if (callArgs) content += ` ${theme.fg("dim", callArgs)}`;
|
|
53
|
+
return new Text(content, 0, 0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function renderToolSummary(details: DebugRenderDetails, theme: Theme): string {
|
|
57
|
+
if (details.eventCount === 0) {
|
|
58
|
+
return details.emptyReason === "no-persisted-events"
|
|
59
|
+
? theme.fg("muted", "No persisted debug events")
|
|
60
|
+
: theme.fg("muted", "No matching debug events");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let summary = `${details.eventCount} event${details.eventCount === 1 ? "" : "s"}`;
|
|
64
|
+
if (details.omittedEventCount > 0) summary += ` · ${details.omittedEventCount} omitted`;
|
|
65
|
+
if (details.eventDataTruncated) summary += " · data bounded";
|
|
66
|
+
if (details.truncation?.truncated) summary += " · output truncated";
|
|
67
|
+
if (details.rawDataUnavailable || details.rawAccessDenied) summary += " · raw unavailable";
|
|
68
|
+
return theme.fg("muted", summary);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function renderProgress(details: unknown, expanded: boolean, theme: Theme): Text {
|
|
72
|
+
const progress =
|
|
73
|
+
typeof details === "object" && details !== null ? (details as Record<string, unknown>) : {};
|
|
74
|
+
const scannedLines =
|
|
75
|
+
typeof progress.scannedLines === "number" ? progress.scannedLines.toLocaleString("en-US") : "?";
|
|
76
|
+
const matchedEvents =
|
|
77
|
+
typeof progress.matchedEvents === "number"
|
|
78
|
+
? progress.matchedEvents.toLocaleString("en-US")
|
|
79
|
+
: "?";
|
|
80
|
+
const line = `Reading persisted debug events… ${scannedLines} lines scanned · ${matchedEvents} matches`;
|
|
81
|
+
return new Text(theme.fg("warning", expanded ? line : "Reading persisted debug events…"), 0, 0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Render the compact or expanded result for the `debug` tool. */
|
|
85
|
+
|
|
86
|
+
/** Render the compact or expanded result for the `debug` tool. */
|
|
87
|
+
export function renderDebugToolResult(
|
|
88
|
+
result: DebugToolResult,
|
|
89
|
+
options: ToolRenderResultOptions,
|
|
90
|
+
theme: Theme,
|
|
91
|
+
context: { isError: boolean },
|
|
92
|
+
): Text {
|
|
93
|
+
if (options.isPartial) return renderProgress(result.details, options.expanded, theme);
|
|
94
|
+
if (context.isError) return new Text(theme.fg("error", "debug failed"), 0, 0);
|
|
95
|
+
|
|
96
|
+
const details = readDebugRenderDetails(result.details);
|
|
97
|
+
if (!options.expanded) {
|
|
98
|
+
let summary = renderToolSummary(details, theme);
|
|
99
|
+
if (details.eventCount > 0)
|
|
100
|
+
summary += theme.fg("dim", ` ${keyHint("app.tools.expand", "to expand")}`);
|
|
101
|
+
return new Text(summary, 0, 0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return new Text(renderExpandedReport(details, theme), 0, 0);
|
|
105
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { AgentToolUpdateCallback } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
type DebugEventQuery,
|
|
4
|
+
type DebugEventView,
|
|
5
|
+
getDebugEvents,
|
|
6
|
+
} from "@mrclrchtr/supi-core/debug";
|
|
7
|
+
import { resolveToolPath } from "@mrclrchtr/supi-core/path";
|
|
8
|
+
import type { DebugConfig } from "../../config.ts";
|
|
9
|
+
import { formatDebugEvents, truncateDebugOutput } from "../../format-events.ts";
|
|
10
|
+
import type { DebugToolParams } from "../../query.ts";
|
|
11
|
+
import { createDebugRenderDetails } from "../../render-details.ts";
|
|
12
|
+
import { readSessionDebugEvents } from "../../session-events.ts";
|
|
13
|
+
|
|
14
|
+
interface DebugProgressDetails {
|
|
15
|
+
scannedLines: number;
|
|
16
|
+
persistedEventCount: number;
|
|
17
|
+
matchedEvents: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function reportDebugProgress(
|
|
21
|
+
onUpdate: AgentToolUpdateCallback<unknown> | undefined,
|
|
22
|
+
progress: DebugProgressDetails,
|
|
23
|
+
): void {
|
|
24
|
+
onUpdate?.({
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: `Reading persisted debug events: ${progress.matchedEvents} matching events found.`,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
details: progress,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface DebugToolExecutionOptions {
|
|
36
|
+
config: DebugConfig;
|
|
37
|
+
cwd: string;
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
onUpdate?: AgentToolUpdateCallback<unknown>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Assemble the model-facing debug tool result for one query. */
|
|
43
|
+
export async function buildToolResult(params: DebugToolParams, options: DebugToolExecutionOptions) {
|
|
44
|
+
const { config, cwd, signal, onUpdate } = options;
|
|
45
|
+
if (!config.enabled && !params.sessionFile) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"SuPi debug event capture is disabled. Enable Debug in /supi-settings to retain events.",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (config.agentAccess === "off") {
|
|
52
|
+
throw new Error("Agent access to SuPi debug events is disabled.");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const filters = {
|
|
56
|
+
operationId: params.operationId,
|
|
57
|
+
source: params.source,
|
|
58
|
+
level: params.level,
|
|
59
|
+
category: params.category,
|
|
60
|
+
limit: params.limit,
|
|
61
|
+
};
|
|
62
|
+
const query: DebugEventQuery = {
|
|
63
|
+
...filters,
|
|
64
|
+
includeRaw: params.includeRaw,
|
|
65
|
+
allowRaw: config.agentAccess === "raw",
|
|
66
|
+
};
|
|
67
|
+
let events: DebugEventView[];
|
|
68
|
+
let rawAccessDenied: boolean;
|
|
69
|
+
let rawDataUnavailable = false;
|
|
70
|
+
let persistedEventCount: number | undefined;
|
|
71
|
+
if (params.sessionFile) {
|
|
72
|
+
const sessionFile = resolveToolPath(cwd, params.sessionFile);
|
|
73
|
+
const persisted =
|
|
74
|
+
signal || onUpdate
|
|
75
|
+
? await readSessionDebugEvents(sessionFile, filters, {
|
|
76
|
+
signal,
|
|
77
|
+
onProgress: (progress) => reportDebugProgress(onUpdate, progress),
|
|
78
|
+
})
|
|
79
|
+
: await readSessionDebugEvents(sessionFile, filters);
|
|
80
|
+
events = persisted.events;
|
|
81
|
+
persistedEventCount = persisted.persistedEventCount;
|
|
82
|
+
rawAccessDenied = Boolean(params.includeRaw);
|
|
83
|
+
rawDataUnavailable = rawAccessDenied;
|
|
84
|
+
} else {
|
|
85
|
+
const result = getDebugEvents(query);
|
|
86
|
+
events = result.events;
|
|
87
|
+
rawAccessDenied = result.rawAccessDenied;
|
|
88
|
+
}
|
|
89
|
+
const output = truncateDebugOutput(
|
|
90
|
+
formatDebugEvents(events, rawAccessDenied, rawDataUnavailable, persistedEventCount).join("\n"),
|
|
91
|
+
);
|
|
92
|
+
const details = createDebugRenderDetails(events, {
|
|
93
|
+
enabled: config.enabled,
|
|
94
|
+
agentAccess: config.agentAccess,
|
|
95
|
+
sessionFile: params.sessionFile,
|
|
96
|
+
rawAccessDenied,
|
|
97
|
+
rawDataUnavailable,
|
|
98
|
+
persistedEventCount,
|
|
99
|
+
eventCount: events.length,
|
|
100
|
+
emptyReason:
|
|
101
|
+
events.length === 0
|
|
102
|
+
? persistedEventCount === 0
|
|
103
|
+
? "no-persisted-events"
|
|
104
|
+
: "no-matches"
|
|
105
|
+
: undefined,
|
|
106
|
+
truncation: output.truncation,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: "text" as const, text: output.text }],
|
|
111
|
+
details,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { makeDebugExecute } from "./execute.ts";
|
|
4
|
+
|
|
5
|
+
export const DEBUG_TOOL_NAME = "debug";
|
|
6
|
+
export const DEBUG_TOOL_LABEL = "SuPi Debug";
|
|
7
|
+
|
|
8
|
+
/** Canonical provider-facing metadata for the debug tool. */
|
|
9
|
+
export const debugSpec = {
|
|
10
|
+
name: DEBUG_TOOL_NAME,
|
|
11
|
+
label: DEBUG_TOOL_LABEL,
|
|
12
|
+
parameters: Type.Object({
|
|
13
|
+
operationId: Type.Optional(
|
|
14
|
+
Type.String({
|
|
15
|
+
description: "Filter by exact Debug Operation ID",
|
|
16
|
+
pattern: "^op-[A-Za-z0-9_-]{21}[AQgw]$",
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
source: Type.Optional(Type.String({ description: "Filter by extension source, e.g. lsp" })),
|
|
20
|
+
level: Type.Optional(
|
|
21
|
+
StringEnum(["debug", "info", "warning", "error"], {
|
|
22
|
+
description: "Filter by debug level",
|
|
23
|
+
}),
|
|
24
|
+
),
|
|
25
|
+
category: Type.Optional(Type.String({ description: "Filter by event category" })),
|
|
26
|
+
limit: Type.Optional(Type.Number({ description: "Maximum number of events to return" })),
|
|
27
|
+
sessionFile: Type.Optional(
|
|
28
|
+
Type.String({ description: "PI session JSONL file containing persisted debug events" }),
|
|
29
|
+
),
|
|
30
|
+
includeRaw: Type.Optional(
|
|
31
|
+
Type.Boolean({ description: "Request raw event data when settings permit it" }),
|
|
32
|
+
),
|
|
33
|
+
}),
|
|
34
|
+
execute: makeDebugExecute(),
|
|
35
|
+
} as const;
|
package/src/tool/guidance.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Prompt guidance and tool description for the supi_debug tool.
|
|
2
|
-
|
|
3
|
-
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
|
|
5
|
-
export const toolDescription = `Fetch recent SuPi debug events, or sanitized persisted events from a PI session JSONL via sessionFile, with optional filters. Raw data is available only for the live session when allowed. Output is truncated to ${DEFAULT_MAX_LINES} lines or ${formatSize(DEFAULT_MAX_BYTES)} (whichever is hit first).`;
|
|
6
|
-
|
|
7
|
-
export const promptSnippet = "supi_debug — fetch live or persisted SuPi debug events";
|
|
8
|
-
|
|
9
|
-
export const promptGuidelines = [
|
|
10
|
-
"Use supi_debug for SuPi failures, fallback reasons, or session debug events; pass sessionFile to inspect a prior session and request raw data only when explicitly asked and settings allow it.",
|
|
11
|
-
];
|