@mrclrchtr/supi-debug 5.0.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 CHANGED
@@ -27,7 +27,7 @@ pi install ./packages/supi-debug
27
27
  After install, this package wires the shared debug registry into three user-facing surfaces:
28
28
 
29
29
  - `/supi-debug` — show recent debug events in a readable TUI report
30
- - `supi_debug` — let the model query recent debug events during troubleshooting
30
+ - `debug` — let the model query recent debug events during troubleshooting
31
31
  - `/supi-settings` integration — configure whether events are captured and how much data is exposed
32
32
 
33
33
  It also registers a **Debug** provider section for `/supi-context`, and ships a `/supi-tooling-retro` prompt template for post-task retrospective feedback on the SuPi tooling used in the completed task.
@@ -78,14 +78,14 @@ keeps the conversation focused; expand only when you need the details.
78
78
 
79
79
  ### Seeing full details without expanding
80
80
 
81
- The agent-facing `supi_debug` tool returns the expanded plain-text
81
+ The agent-facing `debug` tool returns the expanded plain-text
82
82
  representation, subject to PI's standard tool-output truncation limits. This is
83
83
  useful for automated troubleshooting flows while protecting the model context
84
84
  from very large event payloads.
85
85
 
86
86
  ## Filters
87
87
 
88
- Both `/supi-debug` and `supi_debug` support the same basic filters:
88
+ Both `/supi-debug` and `debug` support the same basic filters:
89
89
 
90
90
  - `source`
91
91
  - `level`
@@ -93,7 +93,7 @@ Both `/supi-debug` and `supi_debug` support the same basic filters:
93
93
  - exact `operationId`
94
94
  - `limit`
95
95
 
96
- For historical sessions, pass `sessionFile` to `supi_debug`, or
96
+ For historical sessions, pass `sessionFile` to `debug`, or
97
97
  `sessionFile=<path>` to `/supi-debug`. Historical sessions never retain raw data.
98
98
  The tool also accepts `includeRaw` for live-session data when settings allow it. A Debug Operation ID groups direct request ownership only. It is not a security identity, distributed trace, raw Pi Tool-call identity, or time-window correlation.
99
99
 
@@ -108,7 +108,7 @@ Available settings:
108
108
  - `maxEvents` — maximum retained events in memory
109
109
 
110
110
  Historical inspection works for events captured after this version is loaded. For example, an
111
- agent can call `supi_debug` with `sessionFile` set to a PI session JSONL path.
111
+ agent can call `debug` with `sessionFile` set to a PI session JSONL path.
112
112
 
113
113
  Defaults come from the shared debug registry:
114
114
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "5.0.0",
3
+ "version": "6.0.0",
4
4
  "description": "Shared settings, configuration, reporting, and session infrastructure",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -53,7 +53,7 @@
53
53
  "./api": "./src/api.ts",
54
54
  "./config": "./src/config.ts",
55
55
  "./context": "./src/context.ts",
56
- "./debug": "./src/debug-registry.ts",
56
+ "./debug": "./src/debug.ts",
57
57
  "./evidence-badge": "./src/evidence-badge.ts",
58
58
  "./footer-registry": "./src/footer-registry.ts",
59
59
  "./llm": "./src/llm.ts",
@@ -11,7 +11,7 @@ export * from "./config.ts";
11
11
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
12
12
  export * from "./context.ts";
13
13
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
14
- export * from "./debug-registry.ts";
14
+ export * from "./debug.ts";
15
15
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
16
16
  export * from "./evidence-badge.ts";
17
17
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
@@ -4,9 +4,6 @@
4
4
  // supi-debug extension owns policy/configuration and exposes events through a
5
5
  // command/tool while this module stays dependency-free for producers.
6
6
 
7
- // biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
8
- export * from "./debug-timing.ts";
9
-
10
7
  export type DebugLevel = "debug" | "info" | "warning" | "error";
11
8
  export type DebugAgentAccess = "off" | "sanitized" | "raw";
12
9
  export interface DebugRegistryConfig {
@@ -0,0 +1,9 @@
1
+ // Debug domain entry for `@mrclrchtr/supi-core/debug`.
2
+ //
3
+ // Kept separate from debug-registry.ts so debug-timing.ts can import the
4
+ // registry without creating an import cycle through the barrel re-export.
5
+
6
+ // biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
7
+ export * from "./debug-registry.ts";
8
+ // biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
9
+ export * from "./debug-timing.ts";
@@ -11,7 +11,7 @@ export * from "./config.ts";
11
11
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
12
12
  export * from "./context.ts";
13
13
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
14
- export * from "./debug-registry.ts";
14
+ export * from "./debug.ts";
15
15
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
16
16
  export * from "./footer-registry.ts";
17
17
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-debug",
3
- "version": "5.0.0",
3
+ "version": "6.0.0",
4
4
  "description": "Capture and inspect SuPi debug events",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  "README.md"
33
33
  ],
34
34
  "dependencies": {
35
- "@mrclrchtr/supi-core": "5.0.0"
35
+ "@mrclrchtr/supi-core": "6.0.0"
36
36
  },
37
37
  "bundledDependencies": [
38
38
  "@mrclrchtr/supi-core"
@@ -2,30 +2,35 @@
2
2
  description: Retrospect on SuPi tooling used during the completed task
3
3
  ---
4
4
 
5
- Produce a compact, evidence-based retrospective of the task that just ended. Evaluate SuPi-provided tools, skills, prompts, extensions, and injected context—not the entire suite in the abstract.
5
+ Produce a compact, evidence-based retrospective of the task that just ended. Evaluate the SuPi surfaces that were available and relevant: tools, skills, prompts, extensions, injected context or guidance, documentation, settings, and human-only TUI commands. Do not evaluate the entire suite in the abstract. Do not evaluate pi-core behavior.
6
6
 
7
7
  ## Evidence rules
8
8
 
9
9
  - Reconstruct the retrospective from this session only. Do not invent tool calls, outcomes, failures, token costs, or user impact.
10
- - Tag claims as `[observed]`, `[inferred]`, or `[unknown]`. An observed claim is supported by the transcript or a tool result; an inferred claim is plausible but untested.
11
- - Use the exact surface name shown in the session. Do not treat a tool being available but unused as evidence that it failed.
12
- - If no SuPi surface was used or relevant, say so explicitly. Do not manufacture a wishlist; list only task-specific, plausible missed help.
13
- - Do not make tool calls or perform follow-up investigation. Stop after writing the retrospective.
10
+ - You may query the `debug` tool to verify event-level claims. You may query `cache_forensics` when it is available to verify cache-related claims. Use such queries only for verification, and keep them few. Do not perform any other tool calls or follow-up investigation. Stop after you write the retrospective.
11
+ - Tag every claim as `[observed]`, `[inferred]`, or `[unknown]`. An observed claim is supported by the transcript, a tool result, or an evidence query. An inferred claim is plausible but untested.
12
+ - Rate the impact of each surfaced item as `high`, `medium`, or `low`. The impact is the degree to which the item changed the task outcome or its cost.
13
+ - Use the exact surface name that appears in the session. Do not treat an available but unused tool as evidence of failure.
14
+ - If no SuPi surface was used or relevant, say so explicitly. Do not build a wishlist. List only task-specific, plausible missed help.
14
15
 
15
16
  ## Evaluate
16
17
 
17
- - Actual SuPi surfaces used: what they enabled, where they fell short, and any failure or friction.
18
- - Missed opportunities: a relevant unused tool (like supi-code-intelligence tools) or context source, and why it was missed—discoverability, guidance, timing, capability, or not applicable.
18
+ - SuPi surfaces that were actually used: what they enabled, where they fell short, and any failure or friction.
19
+ - Failed or mis-parameterized tool calls: SuPi surface calls that failed or were rejected for wrong, missing, or invalid parameters, including calls corrected on retry. Name the cause: agent misuse, ambiguous tool description, missing guidance (promptSnippet or promptGuidelines), a schema gap, or a tool bug.
20
+ - Recognized problems or bugs: concrete defects in SuPi surfaces, such as crashes, wrong output, failed calls for valid usage, regressions, inconsistent behavior, or guidance that led to a wrong action. Do not report defects in the project's own code.
21
+ - Missed opportunities: a relevant unused tool (for example a supi-code-intelligence tool) or context source, and why it was missed. Name the cause: discoverability, guidance, timing, capability, or not applicable.
19
22
  - Missing pieces: a concrete utility, capability, documentation page, example, or output improvement that would have changed this task.
20
23
  - Noise: redundant instructions, repeated advice without added value, stale or irrelevant context, excessive output, unnecessary long paths, poor timing, or misleading guidance. Do not criticize necessary context without naming the avoidable cost.
21
- - Keep SuPi/tooling recommendations separate from general code or project recommendations.
24
+ - Keep SuPi or tooling recommendations separate from general code or project recommendations.
22
25
 
23
26
  ## Output rules
24
27
 
25
- - Keep the result under about 500 words and specific to this task.
26
- - Prefer concrete evidence and observed friction over generic praise or a general feature wishlist.
27
- - If a section has no supported item, write `None identified` and explain the evidence limit briefly.
28
- - Include at most three recommendations. Each must name a changeable surface (tool, prompt, skill, docs, guidance, or feature), the proposed change, and the expected benefit. Do not recommend “use the tool more” unless discoverability is the identified cause.
28
+ - Keep the result under about 600 words and specific to this task.
29
+ - Open with a one-line overall verdict on SuPi tooling for this task.
30
+ - Give each item a confidence tag and an impact rating. `None identified` lines need neither.
31
+ - Prefer concrete evidence and observed friction over generic praise or a feature wishlist.
32
+ - If a section has no supported item, write `None identified` and briefly explain the evidence limit.
33
+ - Include at most three recommendations. Each must name a changeable surface (tool, prompt, skill, docs, guidance, or feature), the proposed change, and the expected benefit. Recommended changes may target tool descriptions, parameter schemas, or guidance, especially when they address the Failed or mis-parameterized tool calls listed above. Do not recommend "use the tool more" unless discoverability is the identified cause.
29
34
  - Do not edit files, open issues, update OpenSpec artifacts, or take any other follow-up action.
30
35
 
31
36
  ## Required output
@@ -34,29 +39,39 @@ Produce a compact, evidence-based retrospective of the task that just ended. Eva
34
39
 
35
40
  **Task completed**: <1–2 sentence summary>
36
41
 
42
+ **Verdict**: <one line: the overall state of SuPi tooling for this task and its biggest lever>
43
+
37
44
  ### Tools used
38
- - `[observed]` **`<tool, skill, prompt, extension, or context>`** — concrete help, friction, or failure.
45
+ - `[observed · impact: high|medium|low]` **`<surface>`** — concrete help, friction, or failure.
39
46
  - If none: `None identified — no SuPi surface materially participated in this task.`
40
47
 
48
+ ### Failed or mis-parameterized tool calls
49
+ - `[observed|inferred · impact: high|medium|low]` **`<surface>`** — the call, the invalid or missing parameter, the outcome, and the cause: agent misuse, ambiguous description, missing guidance, schema gap, or tool bug. Include near misses: a call rejected once and corrected on retry is stronger evidence than a clean pass.
50
+ - If none: `None identified — every SuPi call used valid parameters.`
51
+
52
+ ### Recognized problems or bugs
53
+ - `[observed|inferred · impact: high|medium|low]` **`<surface>`** — the concrete defect (crash, wrong behavior, regression, inconsistency) and how it affected the task. Do not repeat ordinary friction that belongs under Tools used, and do not repeat parameter-caused call failures that belong under Failed or mis-parameterized tool calls.
54
+ - If none: `None identified — no defect surfaced in this task.`
55
+
41
56
  ### Missed opportunities
42
- - `[inferred]` **`<surface>`** — task-specific help it might have provided; cause: discoverability, guidance, timing, capability, or not applicable.
57
+ - `[inferred · impact: high|medium|low]` **`<surface>`** — task-specific help it might have provided. Name the cause: discoverability, guidance, timing, capability, or not applicable.
43
58
  - If none: `None identified — do not infer a gap from non-use alone.`
44
59
 
45
60
  ### Missing pieces
46
- - `[observed|inferred]` **`<utility, feature, docs, example, or output change>`** — the concrete gap and how it affected this task.
61
+ - `[observed|inferred · impact: high|medium|low]` **`<utility, feature, docs, example, or output change>`** — the concrete gap and how it affected this task.
47
62
  - If none: `None identified.`
48
63
 
49
64
  ### Unhelpful or noisy context
50
- - `[observed]` **`<instruction, context, or output>`** — what was unnecessary or costly and how it could be reduced or better timed.
65
+ - `[observed|inferred · impact: high|medium|low]` **`<instruction, context, or output>`** — what was unnecessary or costly, and how it could be reduced or better timed.
51
66
  - If none: `None identified.`
52
67
 
53
68
  ### Prioritized recommendations
54
69
  1. **`<named surface>`** — <specific change>; <expected benefit>; confidence: <high|medium|low>.
55
70
  2. **`<named surface>`** — <specific change>; <expected benefit>; confidence: <high|medium|low>.
56
71
  3. **`<named surface>`** — <specific change>; <expected benefit>; confidence: <high|medium|low>.
57
- - Include only supported recommendations; if none are supported, write `None identified`.
72
+ - Include only supported recommendations. If none are supported, write `None identified`.
58
73
 
59
74
  ### Confidence / evidence
60
- - Direct evidence: <what the session demonstrates>
75
+ - Direct evidence: <what the session and any evidence queries demonstrate>
61
76
  - Inference: <what is plausible but untested>
62
- - Limits: <what the session cannot establish>
77
+ - Limits: <what the session cannot establish>
package/src/command.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
2
  import { getDebugEvents } from "@mrclrchtr/supi-core/debug";
3
3
  import { resolveToolPath } from "@mrclrchtr/supi-core/path";
4
- import { formatDebugEvents, truncateDebugOutput } from "./output.ts";
4
+ import { formatDebugEvents, truncateDebugOutput } from "./format-events.ts";
5
5
  import type { DebugToolParams } from "./query.ts";
6
6
  import { parseDebugCommandArgs } from "./query.ts";
7
7
  import { createDebugMessageDetails } from "./renderer.ts";
package/src/config.ts ADDED
@@ -0,0 +1,83 @@
1
+ // Debug config loading and live registry synchronization.
2
+
3
+ import { loadSupiConfig } from "@mrclrchtr/supi-core/config";
4
+ import {
5
+ clearDebugEvents,
6
+ configureDebugRegistry,
7
+ DEBUG_REGISTRY_DEFAULTS,
8
+ type DebugAgentAccess,
9
+ } from "@mrclrchtr/supi-core/debug";
10
+
11
+ export const DEBUG_SECTION = "debug";
12
+
13
+ export interface DebugConfig extends Record<string, unknown> {
14
+ enabled: boolean;
15
+ agentAccess: DebugAgentAccess;
16
+ maxEvents: number;
17
+ }
18
+
19
+ export const DEBUG_DEFAULTS: DebugConfig = { ...DEBUG_REGISTRY_DEFAULTS };
20
+
21
+ function normalizeAgentAccess(value: string): DebugAgentAccess {
22
+ return value === "off" || value === "raw" ? value : "sanitized";
23
+ }
24
+
25
+ export function normalizeMaxEvents(value: string | number): number {
26
+ const parsed = typeof value === "number" ? value : Number.parseInt(value, 10);
27
+ return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : DEBUG_DEFAULTS.maxEvents;
28
+ }
29
+
30
+ function normalizeEnabled(value: unknown): boolean {
31
+ if (typeof value === "boolean") {
32
+ return value;
33
+ }
34
+
35
+ if (typeof value === "string") {
36
+ const normalized = value.trim().toLowerCase();
37
+ if (
38
+ normalized === "true" ||
39
+ normalized === "on" ||
40
+ normalized === "1" ||
41
+ normalized === "yes"
42
+ ) {
43
+ return true;
44
+ }
45
+ if (
46
+ normalized === "false" ||
47
+ normalized === "off" ||
48
+ normalized === "0" ||
49
+ normalized === "no" ||
50
+ normalized === ""
51
+ ) {
52
+ return false;
53
+ }
54
+ return DEBUG_DEFAULTS.enabled;
55
+ }
56
+
57
+ if (value === 1) return true;
58
+ if (value === 0) return false;
59
+ return DEBUG_DEFAULTS.enabled;
60
+ }
61
+
62
+ export function loadDebugConfig(cwd: string): DebugConfig {
63
+ const config = loadSupiConfig(DEBUG_SECTION, cwd, DEBUG_DEFAULTS);
64
+ return {
65
+ enabled: normalizeEnabled(config.enabled),
66
+ agentAccess: normalizeAgentAccess(String(config.agentAccess)),
67
+ maxEvents: normalizeMaxEvents(config.maxEvents),
68
+ };
69
+ }
70
+
71
+ export function applyDebugConfig(cwd: string): DebugConfig {
72
+ const config = loadDebugConfig(cwd);
73
+ configureDebugRegistry(config);
74
+ return config;
75
+ }
76
+
77
+ export function syncLiveDebugRegistry(cwd: string): DebugConfig {
78
+ const config = applyDebugConfig(cwd);
79
+ if (!config.enabled) {
80
+ clearDebugEvents();
81
+ }
82
+ return config;
83
+ }
package/src/debug.ts CHANGED
@@ -1,112 +1,28 @@
1
1
  import { dirname, join } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
- import { StringEnum } from "@earendil-works/pi-ai";
4
- import type { AgentToolUpdateCallback, ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
- import { loadSupiConfig } from "@mrclrchtr/supi-core/config";
3
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
4
  import { registerContextProvider } from "@mrclrchtr/supi-core/context";
7
5
  import {
8
6
  clearDebugEvents,
9
- configureDebugRegistry,
10
- DEBUG_REGISTRY_DEFAULTS,
11
- type DebugAgentAccess,
12
- type DebugEventQuery,
13
- type DebugEventView,
14
- getDebugEvents,
15
7
  getDebugSummary,
16
- isDebugOperationId,
17
8
  subscribeDebugEvents,
18
9
  } from "@mrclrchtr/supi-core/debug";
19
- import { resolveToolPath } from "@mrclrchtr/supi-core/path";
20
10
  import { defineConfigSettings, registerSettings } from "@mrclrchtr/supi-core/settings";
21
- import { Type } from "typebox";
22
11
  import { registerDebugCommand } from "./command.ts";
23
- import { formatDebugEvents, truncateDebugOutput } from "./output.ts";
24
- import type { DebugToolParams } from "./query.ts";
25
- import { createDebugRenderDetails } from "./render-details.ts";
26
12
  import {
27
- registerDebugMessageRenderer,
28
- renderDebugToolCall,
29
- renderDebugToolResult,
30
- } from "./renderer.ts";
31
- import { DEBUG_EVENT_ENTRY_TYPE, readSessionDebugEvents } from "./session-events.ts";
13
+ applyDebugConfig,
14
+ DEBUG_DEFAULTS,
15
+ DEBUG_SECTION,
16
+ normalizeMaxEvents,
17
+ syncLiveDebugRegistry,
18
+ } from "./config.ts";
19
+ import { registerDebugMessageRenderer } from "./renderer.ts";
20
+ import { DEBUG_EVENT_ENTRY_TYPE } from "./session-events.ts";
32
21
  import { maybeLogLoadStatus } from "./status-log.ts";
33
- import { promptGuidelines, promptSnippet, toolDescription } from "./tool/guidance.ts";
22
+ import { registerDebugTool } from "./tool/debug/register.ts";
34
23
 
35
24
  const baseDir = dirname(dirname(fileURLToPath(import.meta.url)));
36
25
 
37
- const DEBUG_SECTION = "debug";
38
- interface DebugConfig extends Record<string, unknown> {
39
- enabled: boolean;
40
- agentAccess: DebugAgentAccess;
41
- maxEvents: number;
42
- }
43
-
44
- const DEBUG_DEFAULTS: DebugConfig = { ...DEBUG_REGISTRY_DEFAULTS };
45
-
46
- function normalizeAgentAccess(value: string): DebugAgentAccess {
47
- return value === "off" || value === "raw" ? value : "sanitized";
48
- }
49
-
50
- function normalizeMaxEvents(value: string | number): number {
51
- const parsed = typeof value === "number" ? value : Number.parseInt(value, 10);
52
- return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : DEBUG_DEFAULTS.maxEvents;
53
- }
54
-
55
- function normalizeEnabled(value: unknown): boolean {
56
- if (typeof value === "boolean") {
57
- return value;
58
- }
59
-
60
- if (typeof value === "string") {
61
- const normalized = value.trim().toLowerCase();
62
- if (
63
- normalized === "true" ||
64
- normalized === "on" ||
65
- normalized === "1" ||
66
- normalized === "yes"
67
- ) {
68
- return true;
69
- }
70
- if (
71
- normalized === "false" ||
72
- normalized === "off" ||
73
- normalized === "0" ||
74
- normalized === "no" ||
75
- normalized === ""
76
- ) {
77
- return false;
78
- }
79
- return DEBUG_DEFAULTS.enabled;
80
- }
81
-
82
- if (value === 1) return true;
83
- if (value === 0) return false;
84
- return DEBUG_DEFAULTS.enabled;
85
- }
86
-
87
- function loadDebugConfig(cwd: string): DebugConfig {
88
- const config = loadSupiConfig(DEBUG_SECTION, cwd, DEBUG_DEFAULTS);
89
- return {
90
- enabled: normalizeEnabled(config.enabled),
91
- agentAccess: normalizeAgentAccess(String(config.agentAccess)),
92
- maxEvents: normalizeMaxEvents(config.maxEvents),
93
- };
94
- }
95
-
96
- function applyDebugConfig(cwd: string): DebugConfig {
97
- const config = loadDebugConfig(cwd);
98
- configureDebugRegistry(config);
99
- return config;
100
- }
101
-
102
- function syncLiveDebugRegistry(cwd: string): DebugConfig {
103
- const config = applyDebugConfig(cwd);
104
- if (!config.enabled) {
105
- clearDebugEvents();
106
- }
107
- return config;
108
- }
109
-
110
26
  function registerDebugSettings(pi: ExtensionAPI): void {
111
27
  registerSettings(
112
28
  pi,
@@ -158,106 +74,6 @@ function buildSummaryData(): Record<string, string | number> | null {
158
74
  return data;
159
75
  }
160
76
 
161
- interface DebugProgressDetails {
162
- scannedLines: number;
163
- persistedEventCount: number;
164
- matchedEvents: number;
165
- }
166
-
167
- function reportDebugProgress(
168
- onUpdate: AgentToolUpdateCallback<unknown> | undefined,
169
- progress: DebugProgressDetails,
170
- ): void {
171
- onUpdate?.({
172
- content: [
173
- {
174
- type: "text",
175
- text: `Reading persisted debug events: ${progress.matchedEvents} matching events found.`,
176
- },
177
- ],
178
- details: progress,
179
- });
180
- }
181
-
182
- interface DebugToolExecutionOptions {
183
- config: DebugConfig;
184
- cwd: string;
185
- signal?: AbortSignal;
186
- onUpdate?: AgentToolUpdateCallback<unknown>;
187
- }
188
-
189
- async function buildToolResult(params: DebugToolParams, options: DebugToolExecutionOptions) {
190
- const { config, cwd, signal, onUpdate } = options;
191
- if (!config.enabled && !params.sessionFile) {
192
- throw new Error(
193
- "SuPi debug event capture is disabled. Enable Debug in /supi-settings to retain events.",
194
- );
195
- }
196
-
197
- if (config.agentAccess === "off") {
198
- throw new Error("Agent access to SuPi debug events is disabled.");
199
- }
200
-
201
- const filters = {
202
- operationId: params.operationId,
203
- source: params.source,
204
- level: params.level,
205
- category: params.category,
206
- limit: params.limit,
207
- };
208
- const query: DebugEventQuery = {
209
- ...filters,
210
- includeRaw: params.includeRaw,
211
- allowRaw: config.agentAccess === "raw",
212
- };
213
- let events: DebugEventView[];
214
- let rawAccessDenied: boolean;
215
- let rawDataUnavailable = false;
216
- let persistedEventCount: number | undefined;
217
- if (params.sessionFile) {
218
- const sessionFile = resolveToolPath(cwd, params.sessionFile);
219
- const persisted =
220
- signal || onUpdate
221
- ? await readSessionDebugEvents(sessionFile, filters, {
222
- signal,
223
- onProgress: (progress) => reportDebugProgress(onUpdate, progress),
224
- })
225
- : await readSessionDebugEvents(sessionFile, filters);
226
- events = persisted.events;
227
- persistedEventCount = persisted.persistedEventCount;
228
- rawAccessDenied = Boolean(params.includeRaw);
229
- rawDataUnavailable = rawAccessDenied;
230
- } else {
231
- const result = getDebugEvents(query);
232
- events = result.events;
233
- rawAccessDenied = result.rawAccessDenied;
234
- }
235
- const output = truncateDebugOutput(
236
- formatDebugEvents(events, rawAccessDenied, rawDataUnavailable, persistedEventCount).join("\n"),
237
- );
238
- const details = createDebugRenderDetails(events, {
239
- enabled: config.enabled,
240
- agentAccess: config.agentAccess,
241
- sessionFile: params.sessionFile,
242
- rawAccessDenied,
243
- rawDataUnavailable,
244
- persistedEventCount,
245
- eventCount: events.length,
246
- emptyReason:
247
- events.length === 0
248
- ? persistedEventCount === 0
249
- ? "no-persisted-events"
250
- : "no-matches"
251
- : undefined,
252
- truncation: output.truncation,
253
- });
254
-
255
- return {
256
- content: [{ type: "text" as const, text: output.text }],
257
- details,
258
- };
259
- }
260
-
261
77
  /** Register the shared SuPi debug command, settings, context summary, and agent tool. */
262
78
  export default function debugExtension(pi: ExtensionAPI) {
263
79
  applyDebugConfig(process.cwd());
@@ -290,45 +106,5 @@ export default function debugExtension(pi: ExtensionAPI) {
290
106
  });
291
107
 
292
108
  registerDebugCommand(pi, applyDebugConfig, normalizeMaxEvents);
293
-
294
- pi.registerTool({
295
- name: "supi_debug",
296
- label: "SuPi Debug",
297
- description: toolDescription,
298
- promptSnippet,
299
- promptGuidelines,
300
- parameters: Type.Object({
301
- operationId: Type.Optional(
302
- Type.String({
303
- description: "Filter by exact Debug Operation ID",
304
- pattern: "^op-[A-Za-z0-9_-]{21}[AQgw]$",
305
- }),
306
- ),
307
- source: Type.Optional(Type.String({ description: "Filter by extension source, e.g. lsp" })),
308
- level: Type.Optional(
309
- StringEnum(["debug", "info", "warning", "error"], {
310
- description: "Filter by debug level",
311
- }),
312
- ),
313
- category: Type.Optional(Type.String({ description: "Filter by event category" })),
314
- limit: Type.Optional(Type.Number({ description: "Maximum number of events to return" })),
315
- sessionFile: Type.Optional(
316
- Type.String({ description: "PI session JSONL file containing persisted debug events" }),
317
- ),
318
- includeRaw: Type.Optional(
319
- Type.Boolean({ description: "Request raw event data when settings permit it" }),
320
- ),
321
- }),
322
- // biome-ignore lint/complexity/useMaxParams: pi ToolDefinition.execute signature
323
- async execute(_toolCallId, params, signal, onUpdate, ctx) {
324
- const query = params as DebugToolParams;
325
- if (query.operationId !== undefined && !isDebugOperationId(query.operationId)) {
326
- throw new Error("Invalid Debug Operation ID");
327
- }
328
- const config = applyDebugConfig(ctx.cwd);
329
- return buildToolResult(query, { config, cwd: ctx.cwd, signal, onUpdate });
330
- },
331
- renderCall: renderDebugToolCall,
332
- renderResult: renderDebugToolResult,
333
- });
109
+ registerDebugTool(pi);
334
110
  }
package/src/renderer.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import {
2
2
  type ExtensionAPI,
3
3
  formatSize,
4
- keyHint,
5
4
  type Theme,
6
- type ToolRenderResultOptions,
7
5
  truncateHead,
8
6
  } from "@earendil-works/pi-coding-agent";
9
7
  import { Text } from "@earendil-works/pi-tui";
@@ -20,55 +18,6 @@ const DEBUG_REPORT_TYPE = "supi-debug-report";
20
18
  const MAX_RENDER_LINES = 240;
21
19
  const MAX_RENDER_BYTES = 16 * 1024;
22
20
 
23
- interface DebugToolResult {
24
- content?: Array<{ type: string; text?: string }>;
25
- details?: unknown;
26
- }
27
-
28
- interface DebugCallArgs {
29
- operationId?: unknown;
30
- source?: unknown;
31
- level?: unknown;
32
- category?: unknown;
33
- limit?: unknown;
34
- sessionFile?: unknown;
35
- includeRaw?: unknown;
36
- }
37
-
38
- function boundedCallValue(value: unknown, maxLength = 80): string | undefined {
39
- if (typeof value !== "string" && typeof value !== "number") return undefined;
40
- const text = String(value);
41
- return text.length > maxLength ? `${text.slice(0, maxLength - 1)}…` : text;
42
- }
43
-
44
- function formatCallArgs(args: unknown): string {
45
- const input = (args ?? {}) as DebugCallArgs;
46
- const filters: string[] = [];
47
- const operationId = boundedCallValue(input.operationId);
48
- const source = boundedCallValue(input.source);
49
- const level = boundedCallValue(input.level);
50
- const category = boundedCallValue(input.category);
51
- const limit = boundedCallValue(input.limit);
52
- const sessionFile = boundedCallValue(input.sessionFile);
53
-
54
- if (operationId) filters.push(`operationId=${operationId}`);
55
- if (source) filters.push(`source=${source}`);
56
- if (level) filters.push(`level=${level}`);
57
- if (category) filters.push(`category=${category}`);
58
- if (limit) filters.push(`limit=${limit}`);
59
- if (sessionFile) filters.push(`sessionFile=${sessionFile}`);
60
- if (input.includeRaw === true) filters.push("raw");
61
- return filters.join(" ");
62
- }
63
-
64
- /** Render the compact human-facing call header for `supi_debug`. */
65
- export function renderDebugToolCall(args: unknown, theme: Theme): Text {
66
- const callArgs = formatCallArgs(args);
67
- let content = theme.fg("toolTitle", "supi_debug");
68
- if (callArgs) content += ` ${theme.fg("dim", callArgs)}`;
69
- return new Text(content, 0, 0);
70
- }
71
-
72
21
  function formatLevel(theme: Theme, level: string): string {
73
22
  const color =
74
23
  level === "error"
@@ -162,7 +111,7 @@ function buildEventLines(details: DebugRenderDetails, theme: Theme): string[] {
162
111
  return lines;
163
112
  }
164
113
 
165
- function renderExpandedReport(details: DebugRenderDetails, theme: Theme): string {
114
+ export function renderExpandedReport(details: DebugRenderDetails, theme: Theme): string {
166
115
  const notes = buildDetailNotes(details, theme);
167
116
  const noteText = notes.join("\n");
168
117
  const noteBytes = new TextEncoder().encode(noteText).byteLength;
@@ -177,55 +126,6 @@ function renderExpandedReport(details: DebugRenderDetails, theme: Theme): string
177
126
  return [...bodyLines, ...notes].join("\n");
178
127
  }
179
128
 
180
- function renderToolSummary(details: DebugRenderDetails, theme: Theme): string {
181
- if (details.eventCount === 0) {
182
- return details.emptyReason === "no-persisted-events"
183
- ? theme.fg("muted", "No persisted debug events")
184
- : theme.fg("muted", "No matching debug events");
185
- }
186
-
187
- let summary = `${details.eventCount} event${details.eventCount === 1 ? "" : "s"}`;
188
- if (details.omittedEventCount > 0) summary += ` · ${details.omittedEventCount} omitted`;
189
- if (details.eventDataTruncated) summary += " · data bounded";
190
- if (details.truncation?.truncated) summary += " · output truncated";
191
- if (details.rawDataUnavailable || details.rawAccessDenied) summary += " · raw unavailable";
192
- return theme.fg("muted", summary);
193
- }
194
-
195
- function renderProgress(details: unknown, expanded: boolean, theme: Theme): Text {
196
- const progress =
197
- typeof details === "object" && details !== null ? (details as Record<string, unknown>) : {};
198
- const scannedLines =
199
- typeof progress.scannedLines === "number" ? progress.scannedLines.toLocaleString("en-US") : "?";
200
- const matchedEvents =
201
- typeof progress.matchedEvents === "number"
202
- ? progress.matchedEvents.toLocaleString("en-US")
203
- : "?";
204
- const line = `Reading persisted debug events… ${scannedLines} lines scanned · ${matchedEvents} matches`;
205
- return new Text(theme.fg("warning", expanded ? line : "Reading persisted debug events…"), 0, 0);
206
- }
207
-
208
- /** Render the compact or expanded result for the `supi_debug` tool. */
209
- export function renderDebugToolResult(
210
- result: DebugToolResult,
211
- options: ToolRenderResultOptions,
212
- theme: Theme,
213
- context: { isError: boolean },
214
- ): Text {
215
- if (options.isPartial) return renderProgress(result.details, options.expanded, theme);
216
- if (context.isError) return new Text(theme.fg("error", "supi_debug failed"), 0, 0);
217
-
218
- const details = readDebugRenderDetails(result.details);
219
- if (!options.expanded) {
220
- let summary = renderToolSummary(details, theme);
221
- if (details.eventCount > 0)
222
- summary += theme.fg("dim", ` ${keyHint("app.tools.expand", "to expand")}`);
223
- return new Text(summary, 0, 0);
224
- }
225
-
226
- return new Text(renderExpandedReport(details, theme), 0, 0);
227
- }
228
-
229
129
  function renderEmptyMessage(content: unknown, details: DebugRenderDetails, theme: Theme): Text {
230
130
  const text =
231
131
  typeof content === "string"
@@ -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
+ }
@@ -1,11 +1,11 @@
1
- // Prompt guidance and tool description for the supi_debug tool.
1
+ // Prompt guidance and tool description for the debug tool.
2
2
 
3
3
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "@earendil-works/pi-coding-agent";
4
4
 
5
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
6
 
7
- export const promptSnippet = "supi_debug — fetch live or persisted SuPi debug events";
7
+ export const promptSnippet = "debug — fetch live or persisted SuPi debug events";
8
8
 
9
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.",
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
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;
File without changes