@mrclrchtr/supi-context 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 +3 -3
- 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 +2 -2
- package/src/config.ts +2 -2
- package/src/context.ts +4 -44
- package/src/settings-registration.ts +1 -1
- package/src/tool/context_report/execute.ts +20 -0
- package/src/tool/{guidance.ts → context_report/guidance.ts} +2 -2
- package/src/tool/context_report/register.ts +20 -0
- package/src/tool/{render.ts → context_report/render.ts} +10 -19
- package/src/tool/{output.ts → context_report/result.ts} +36 -1
- package/src/tool/context_report/spec.ts +18 -0
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ The command appends a durable `supi-context` custom entry and uses a dedicated e
|
|
|
36
36
|
|
|
37
37
|
### Agent: Context Pressure Snapshot
|
|
38
38
|
|
|
39
|
-
`
|
|
39
|
+
`context_report` is agent-callable when enabled in configuration. It defaults to a one-line, constant-shape JSON **Context Pressure Snapshot**:
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
42
|
interface ContextPressureSnapshot {
|
|
@@ -53,7 +53,7 @@ interface ContextPressureSnapshot {
|
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Use `
|
|
56
|
+
Use `context_report({ mode: "full" })` only when diagnostic attribution is needed. Full mode returns compact JSON for the Context Usage Report. If it exceeds Pi's normal tool-output limits, it returns a small valid-JSON envelope with the path to a temporary file containing the complete report.
|
|
57
57
|
|
|
58
58
|
The tool TUI never shows raw agent JSON: concise results render as a dense snapshot that expands into its metrics; full results expand into the diagnostic report.
|
|
59
59
|
|
|
@@ -71,7 +71,7 @@ The Context Usage Report includes:
|
|
|
71
71
|
|
|
72
72
|
The human `/supi-context` command needs no configuration.
|
|
73
73
|
|
|
74
|
-
To enable the agent-callable `
|
|
74
|
+
To enable the agent-callable `context_report` tool, set `agentToolEnabled` to `true` in your supi config:
|
|
75
75
|
|
|
76
76
|
```json
|
|
77
77
|
{
|
|
@@ -28,6 +28,7 @@ pnpm add @mrclrchtr/supi-core
|
|
|
28
28
|
- `loadSupiConfig()` — merged config with resolution order `defaults <- global <- project`
|
|
29
29
|
- `loadSupiConfigForScope()` — load one scope at a time for settings UIs
|
|
30
30
|
- `writeSupiConfig()` — persist values
|
|
31
|
+
- `replaceSupiConfigSection()` — replace one nested section while preserving other sections
|
|
31
32
|
- `removeSupiConfigKey()` — remove a key or override
|
|
32
33
|
|
|
33
34
|
Config file locations:
|
|
@@ -49,6 +50,7 @@ Config file locations:
|
|
|
49
50
|
|
|
50
51
|
- context-provider registry for `/supi-context`
|
|
51
52
|
- debug-event registry and monotonic phase timers for producers that want shared debug capture
|
|
53
|
+
- optional Debug Operation IDs for exact, directly owned public Tool-call correlation; ambient events stay uncorrelated
|
|
52
54
|
- settings registry used by `/supi-settings`
|
|
53
55
|
|
|
54
56
|
### Project and session helpers
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-core",
|
|
3
|
-
"version": "
|
|
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
|
|
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
|
|
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
|
|
@@ -148,6 +148,37 @@ export function writeSupiConfig(
|
|
|
148
148
|
fs.writeFileSync(configPath, `${JSON.stringify(existing, null, 2)}\n`, "utf-8");
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Replace one complete config section while preserving other sections.
|
|
153
|
+
*
|
|
154
|
+
* This is useful for nested settings that must remove stale keys as part of
|
|
155
|
+
* one update. An empty section is removed from the config file.
|
|
156
|
+
*/
|
|
157
|
+
export function replaceSupiConfigSection(
|
|
158
|
+
loc: SupiConfigLocation,
|
|
159
|
+
value: Record<string, unknown>,
|
|
160
|
+
options?: SupiConfigOptions,
|
|
161
|
+
): void {
|
|
162
|
+
const configPath = getSupiConfigPath(loc.scope, loc.cwd, options);
|
|
163
|
+
const existing = readJsonFile(configPath) ?? {};
|
|
164
|
+
|
|
165
|
+
if (Object.keys(value).length > 0) existing[loc.section] = value;
|
|
166
|
+
else delete existing[loc.section];
|
|
167
|
+
|
|
168
|
+
const content = Object.keys(existing).length > 0 ? `${JSON.stringify(existing, null, 2)}\n` : "";
|
|
169
|
+
if (content) {
|
|
170
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
171
|
+
fs.writeFileSync(configPath, content, "utf-8");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
fs.unlinkSync(configPath);
|
|
177
|
+
} catch {
|
|
178
|
+
// File may not exist.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
151
182
|
/**
|
|
152
183
|
* Remove a key from a config section.
|
|
153
184
|
* Used by `interval default` to remove the project override.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// supi-core config domain — config loading.
|
|
2
2
|
export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
|
|
3
3
|
export {
|
|
4
|
+
getSupiConfigPath,
|
|
4
5
|
loadSupiConfig,
|
|
5
6
|
loadSupiConfigForScope,
|
|
6
7
|
loadSupiConfigSectionForScope,
|
|
7
8
|
readJsonFile,
|
|
8
9
|
removeSupiConfigKey,
|
|
10
|
+
replaceSupiConfigSection,
|
|
9
11
|
writeSupiConfig,
|
|
10
12
|
} from "./config/config.ts";
|
|
@@ -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 {
|
|
@@ -25,6 +22,8 @@ export const DEBUG_REGISTRY_DEFAULTS: DebugRegistryConfig = {
|
|
|
25
22
|
};
|
|
26
23
|
|
|
27
24
|
export interface DebugEventInput {
|
|
25
|
+
/** Opaque identity for events directly owned by one public Tool call. */
|
|
26
|
+
operationId?: string;
|
|
28
27
|
source: string;
|
|
29
28
|
level: DebugLevel;
|
|
30
29
|
category: string;
|
|
@@ -42,6 +41,8 @@ export interface DebugEvent extends DebugEventInput {
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
export interface DebugEventQuery {
|
|
44
|
+
/** Match one exact Debug Operation ID. */
|
|
45
|
+
operationId?: string;
|
|
45
46
|
source?: string;
|
|
46
47
|
level?: DebugLevel;
|
|
47
48
|
category?: string;
|
|
@@ -53,6 +54,7 @@ export interface DebugEventQuery {
|
|
|
53
54
|
export interface DebugEventView {
|
|
54
55
|
id: number;
|
|
55
56
|
timestamp: number;
|
|
57
|
+
operationId?: string;
|
|
56
58
|
source: string;
|
|
57
59
|
level: DebugLevel;
|
|
58
60
|
category: string;
|
|
@@ -84,6 +86,7 @@ interface DebugRegistryState {
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
const REGISTRY_KEY = Symbol.for("@mrclrchtr/supi-core/debug-registry");
|
|
89
|
+
const DEBUG_OPERATION_ID_RE = /^op-[A-Za-z0-9_-]{21}[AQgw]$/;
|
|
87
90
|
const SECRET_KEY_RE = /(?:token|password|passwd|secret|api[_-]?key|authorization|credential)/i;
|
|
88
91
|
const ENV_SECRET_RE =
|
|
89
92
|
/\b([A-Za-z0-9_]*(?:token|password|passwd|secret|api[_-]?key|authorization|credential)[A-Za-z0-9_]*)=(?:'[^']*'|"[^"]*"|\S+)/gi;
|
|
@@ -135,11 +138,17 @@ export function isDebugLevel(value: unknown): value is DebugLevel {
|
|
|
135
138
|
return value === "debug" || value === "info" || value === "warning" || value === "error";
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
/**
|
|
141
|
+
/** Return whether a value has the exact 16-byte base64url Debug Operation ID form. */
|
|
142
|
+
export function isDebugOperationId(value: unknown): value is string {
|
|
143
|
+
return typeof value === "string" && DEBUG_OPERATION_ID_RE.test(value);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Match a debug event against the supported exact filters. */
|
|
139
147
|
export function matchesDebugEventQuery(
|
|
140
|
-
event: Pick<DebugEventView, "source" | "level" | "category">,
|
|
141
|
-
query: Pick<DebugEventQuery, "source" | "level" | "category">,
|
|
148
|
+
event: Pick<DebugEventView, "operationId" | "source" | "level" | "category">,
|
|
149
|
+
query: Pick<DebugEventQuery, "operationId" | "source" | "level" | "category">,
|
|
142
150
|
): boolean {
|
|
151
|
+
if (query.operationId && event.operationId !== query.operationId) return false;
|
|
143
152
|
if (query.source && event.source !== query.source) return false;
|
|
144
153
|
if (query.level && event.level !== query.level) return false;
|
|
145
154
|
if (query.category && event.category !== query.category) return false;
|
|
@@ -197,6 +206,7 @@ function toSanitizedView(event: DebugEvent): DebugEventView {
|
|
|
197
206
|
return {
|
|
198
207
|
id: event.id,
|
|
199
208
|
timestamp: event.timestamp,
|
|
209
|
+
operationId: event.operationId,
|
|
200
210
|
source: event.source,
|
|
201
211
|
level: event.level,
|
|
202
212
|
category: event.category,
|
|
@@ -216,6 +226,9 @@ export function subscribeDebugEvents(listener: DebugEventListener): () => void {
|
|
|
216
226
|
/** Record a session-local debug event if debugging is enabled. */
|
|
217
227
|
export function recordDebugEvent(input: DebugEventInput): DebugEvent | null {
|
|
218
228
|
const state = getState();
|
|
229
|
+
if (input.operationId !== undefined && !isDebugOperationId(input.operationId)) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
219
232
|
if (!state.config.enabled) {
|
|
220
233
|
return null;
|
|
221
234
|
}
|
|
@@ -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
|
|
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
|
|
@@ -41,7 +41,10 @@ export interface SettingsApplyResult {
|
|
|
41
41
|
*/
|
|
42
42
|
export interface SettingsModule {
|
|
43
43
|
id: string;
|
|
44
|
+
/** Human-readable section label shown in the UI. */
|
|
44
45
|
label: string;
|
|
46
|
+
/** Optional label that groups this module within its section. */
|
|
47
|
+
subsection?: string;
|
|
45
48
|
read(context: SettingsContext): Promise<SettingsSnapshot>;
|
|
46
49
|
apply(request: SettingsActionRequest): Promise<SettingsApplyResult>;
|
|
47
50
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-context",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "Live context pressure and token-use reports",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"typebox": "*",
|
|
35
|
-
"@mrclrchtr/supi-core": "
|
|
35
|
+
"@mrclrchtr/supi-core": "6.0.0"
|
|
36
36
|
},
|
|
37
37
|
"bundledDependencies": [
|
|
38
38
|
"@mrclrchtr/supi-core"
|
package/src/config.ts
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Config shape (in supi shared config, "context" section):
|
|
4
4
|
// {
|
|
5
|
-
// "agentToolEnabled": false // enable the
|
|
5
|
+
// "agentToolEnabled": false // enable the context_report agent-callable tool
|
|
6
6
|
// }
|
|
7
7
|
|
|
8
8
|
import { loadSupiConfig } from "@mrclrchtr/supi-core/config";
|
|
9
9
|
|
|
10
10
|
export interface ContextConfig extends Record<string, unknown> {
|
|
11
|
-
/** Enable the
|
|
11
|
+
/** Enable the context_report agent-callable tool. Default: false */
|
|
12
12
|
agentToolEnabled: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
package/src/context.ts
CHANGED
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
1
|
import type { BuildSystemPromptOptions, ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import {
|
|
4
|
-
import { analyzeContext, analyzeContextPressure } from "./analysis.ts";
|
|
2
|
+
import { analyzeContext } from "./analysis.ts";
|
|
5
3
|
import { loadContextConfig } from "./config.ts";
|
|
6
4
|
import { type ContextReportEntryData, registerContextEntryRenderer } from "./entry-renderer.ts";
|
|
7
5
|
import { registerContextSettings } from "./settings-registration.ts";
|
|
8
|
-
import {
|
|
9
|
-
import { serializeFullContextAnalysis } from "./tool/output.ts";
|
|
10
|
-
import {
|
|
11
|
-
type ContextToolDetails,
|
|
12
|
-
renderContextToolCall,
|
|
13
|
-
renderContextToolResult,
|
|
14
|
-
} from "./tool/render.ts";
|
|
15
|
-
|
|
16
|
-
const contextToolParameters = Type.Object({
|
|
17
|
-
mode: Type.Optional(
|
|
18
|
-
StringEnum(["concise", "full"] as const, {
|
|
19
|
-
description: "Omit for concise capacity data, or use full for the diagnostic report.",
|
|
20
|
-
}),
|
|
21
|
-
),
|
|
22
|
-
});
|
|
6
|
+
import { registerContextReportTool } from "./tool/context_report/register.ts";
|
|
23
7
|
|
|
24
8
|
export default function contextExtension(pi: ExtensionAPI) {
|
|
25
9
|
let cachedOptions: BuildSystemPromptOptions | undefined;
|
|
@@ -48,33 +32,9 @@ export default function contextExtension(pi: ExtensionAPI) {
|
|
|
48
32
|
});
|
|
49
33
|
});
|
|
50
34
|
|
|
51
|
-
// ──
|
|
35
|
+
// ── context_report agent tool (gated on config) ────────────
|
|
52
36
|
|
|
53
37
|
if (loadContextConfig(process.cwd()).agentToolEnabled) {
|
|
54
|
-
pi
|
|
55
|
-
name: "supi_context",
|
|
56
|
-
label: "Context Usage",
|
|
57
|
-
description: toolDescription,
|
|
58
|
-
promptSnippet,
|
|
59
|
-
parameters: contextToolParameters,
|
|
60
|
-
renderCall: renderContextToolCall,
|
|
61
|
-
renderResult: renderContextToolResult,
|
|
62
|
-
// biome-ignore lint/complexity/useMaxParams: pi tool execute signature
|
|
63
|
-
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
64
|
-
if (params.mode !== "full") {
|
|
65
|
-
const snapshot = analyzeContextPressure(ctx);
|
|
66
|
-
return {
|
|
67
|
-
content: [{ type: "text", text: JSON.stringify(snapshot) }],
|
|
68
|
-
details: { mode: "concise", snapshot } satisfies ContextToolDetails,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const analysis = analyzeContext(ctx, pi, cachedOptions);
|
|
73
|
-
return {
|
|
74
|
-
content: [{ type: "text", text: await serializeFullContextAnalysis(analysis) }],
|
|
75
|
-
details: { mode: "full", analysis } satisfies ContextToolDetails,
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
});
|
|
38
|
+
registerContextReportTool(pi, () => cachedOptions);
|
|
79
39
|
}
|
|
80
40
|
}
|
|
@@ -18,7 +18,7 @@ export function registerContextSettings(pi: ExtensionAPI, homeDir?: string): voi
|
|
|
18
18
|
kind: "boolean" as const,
|
|
19
19
|
key: "agentToolEnabled",
|
|
20
20
|
label: "Agent Tool",
|
|
21
|
-
description: "Enable
|
|
21
|
+
description: "Enable context_report agent tool for context usage queries",
|
|
22
22
|
},
|
|
23
23
|
],
|
|
24
24
|
...(homeDir ? { homeDir } : {}),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BuildSystemPromptOptions, ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { analyzeContext, analyzeContextPressure } from "../../analysis.ts";
|
|
3
|
+
import { buildConciseResult, buildFullResult } from "./result.ts";
|
|
4
|
+
|
|
5
|
+
type ContextReportExecute = NonNullable<Parameters<ExtensionAPI["registerTool"]>[0]["execute"]>;
|
|
6
|
+
|
|
7
|
+
/** Build the context_report execute function. */
|
|
8
|
+
export function makeContextReportExecute(
|
|
9
|
+
pi: ExtensionAPI,
|
|
10
|
+
getOptions: () => BuildSystemPromptOptions | undefined,
|
|
11
|
+
): ContextReportExecute {
|
|
12
|
+
// biome-ignore lint/complexity/useMaxParams: pi tool execute signature
|
|
13
|
+
return async (_toolCallId, params, _signal, _onUpdate, ctx) => {
|
|
14
|
+
const mode = (params as { mode?: string }).mode;
|
|
15
|
+
if (mode !== "full") {
|
|
16
|
+
return buildConciseResult(analyzeContextPressure(ctx));
|
|
17
|
+
}
|
|
18
|
+
return buildFullResult(analyzeContext(ctx, pi, getOptions()));
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
|
|
3
|
-
// Tool description and prompt snippet for the
|
|
3
|
+
// Tool description and prompt snippet for the context_report agent tool.
|
|
4
4
|
|
|
5
5
|
export const toolDescription =
|
|
6
6
|
"Report current context capacity. Omit mode for a concise, constant-shape pressure snapshot; use mode: full only when diagnostic attribution is needed. Full output is compact JSON and is replaced with a temporary-file envelope above " +
|
|
7
7
|
`${DEFAULT_MAX_LINES} lines or ${formatSize(DEFAULT_MAX_BYTES)}.`;
|
|
8
8
|
|
|
9
9
|
export const promptSnippet =
|
|
10
|
-
"
|
|
10
|
+
"context_report — concise context-pressure snapshot (mode: full for diagnostics)";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BuildSystemPromptOptions, ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { makeContextReportExecute } from "./execute.ts";
|
|
3
|
+
import { promptSnippet, toolDescription } from "./guidance.ts";
|
|
4
|
+
import { renderContextToolCall, renderContextToolResult } from "./render.ts";
|
|
5
|
+
import { contextReportSpec } from "./spec.ts";
|
|
6
|
+
|
|
7
|
+
/** Register the context_report agent tool. */
|
|
8
|
+
export function registerContextReportTool(
|
|
9
|
+
pi: ExtensionAPI,
|
|
10
|
+
getOptions: () => BuildSystemPromptOptions | undefined,
|
|
11
|
+
): void {
|
|
12
|
+
pi.registerTool({
|
|
13
|
+
...contextReportSpec,
|
|
14
|
+
description: toolDescription,
|
|
15
|
+
promptSnippet,
|
|
16
|
+
execute: makeContextReportExecute(pi, getOptions),
|
|
17
|
+
renderCall: renderContextToolCall,
|
|
18
|
+
renderResult: renderContextToolResult,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Text } from "@earendil-works/pi-tui";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface ContextToolFullDetails {
|
|
14
|
-
mode: "full";
|
|
15
|
-
analysis: ContextAnalysis;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** TUI data mirrors the mode-specific data returned to the agent. */
|
|
19
|
-
export type ContextToolDetails = ContextToolConciseDetails | ContextToolFullDetails;
|
|
3
|
+
import { createContextPressureSnapshot } from "../../capacity.ts";
|
|
4
|
+
import { ContextReportComponent } from "../../report-component.ts";
|
|
5
|
+
import { ContextPressureComponent } from "../../snapshot-component.ts";
|
|
6
|
+
import type {
|
|
7
|
+
ContextToolConciseDetails,
|
|
8
|
+
ContextToolDetails,
|
|
9
|
+
ContextToolFullDetails,
|
|
10
|
+
} from "./result.ts";
|
|
20
11
|
|
|
21
12
|
interface ContextToolResult {
|
|
22
13
|
content: Array<{ type: string; text?: string }>;
|
|
@@ -37,7 +28,7 @@ export function renderContextToolCall(args: unknown, theme: Theme): Text {
|
|
|
37
28
|
(args as { mode?: string }).mode === "full"
|
|
38
29
|
? "full"
|
|
39
30
|
: "concise";
|
|
40
|
-
const content = `${theme.fg("toolTitle", "
|
|
31
|
+
const content = `${theme.fg("toolTitle", "context_report")} ${theme.fg("muted", mode)}`;
|
|
41
32
|
return new Text(content, 0, 0);
|
|
42
33
|
}
|
|
43
34
|
|
|
@@ -51,7 +42,7 @@ export function renderContextToolResult(
|
|
|
51
42
|
}
|
|
52
43
|
|
|
53
44
|
if (result?.isError) {
|
|
54
|
-
return new Text(theme.fg("error", "
|
|
45
|
+
return new Text(theme.fg("error", "context_report failed"), 0, 0);
|
|
55
46
|
}
|
|
56
47
|
|
|
57
48
|
const details = extractDetails(result?.details);
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
4
5
|
import {
|
|
5
6
|
DEFAULT_MAX_BYTES,
|
|
6
7
|
DEFAULT_MAX_LINES,
|
|
7
8
|
truncateHead,
|
|
8
9
|
} from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import type { ContextAnalysis } from "
|
|
10
|
+
import type { ContextAnalysis } from "../../analysis.ts";
|
|
11
|
+
import type { ContextPressureSnapshot } from "../../capacity.ts";
|
|
12
|
+
|
|
13
|
+
export interface ContextToolConciseDetails {
|
|
14
|
+
mode: "concise";
|
|
15
|
+
snapshot: ContextPressureSnapshot;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ContextToolFullDetails {
|
|
19
|
+
mode: "full";
|
|
20
|
+
analysis: ContextAnalysis;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Mode-specific details returned to the agent and mirrored for TUI rendering. */
|
|
24
|
+
export type ContextToolDetails = ContextToolConciseDetails | ContextToolFullDetails;
|
|
10
25
|
|
|
11
26
|
interface TruncatedOutputEnvelope {
|
|
12
27
|
truncated: true;
|
|
@@ -49,3 +64,23 @@ export async function serializeFullContextAnalysis(analysis: ContextAnalysis): P
|
|
|
49
64
|
};
|
|
50
65
|
return JSON.stringify(envelope);
|
|
51
66
|
}
|
|
67
|
+
|
|
68
|
+
/** Assemble the concise-mode model-facing result. */
|
|
69
|
+
export function buildConciseResult(
|
|
70
|
+
snapshot: ContextPressureSnapshot,
|
|
71
|
+
): AgentToolResult<ContextToolConciseDetails> {
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: "text", text: JSON.stringify(snapshot) }],
|
|
74
|
+
details: { mode: "concise", snapshot },
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Assemble the full-mode model-facing result. */
|
|
79
|
+
export async function buildFullResult(
|
|
80
|
+
analysis: ContextAnalysis,
|
|
81
|
+
): Promise<AgentToolResult<ContextToolFullDetails>> {
|
|
82
|
+
return {
|
|
83
|
+
content: [{ type: "text", text: await serializeFullContextAnalysis(analysis) }],
|
|
84
|
+
details: { mode: "full", analysis },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
|
|
4
|
+
export const CONTEXT_REPORT_TOOL_NAME = "context_report";
|
|
5
|
+
export const CONTEXT_REPORT_TOOL_LABEL = "Context Usage";
|
|
6
|
+
|
|
7
|
+
/** Canonical provider-facing metadata for the context_report tool. */
|
|
8
|
+
export const contextReportSpec = {
|
|
9
|
+
name: CONTEXT_REPORT_TOOL_NAME,
|
|
10
|
+
label: CONTEXT_REPORT_TOOL_LABEL,
|
|
11
|
+
parameters: Type.Object({
|
|
12
|
+
mode: Type.Optional(
|
|
13
|
+
StringEnum(["concise", "full"] as const, {
|
|
14
|
+
description: "Omit for concise capacity data, or use full for the diagnostic report.",
|
|
15
|
+
}),
|
|
16
|
+
),
|
|
17
|
+
}),
|
|
18
|
+
} as const;
|