pi-ui-extend 1.0.39 → 1.0.41
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 +1 -1
- package/dist/app/commands/command-registry.js +2 -2
- package/dist/app/commands/command-session-actions.d.ts +0 -1
- package/dist/app/commands/command-session-actions.js +22 -13
- package/dist/app/icons.d.ts +14 -0
- package/dist/app/icons.js +33 -0
- package/dist/app/rendering/conversation-tool-renderer.js +2 -2
- package/dist/app/rendering/dcp-stats.d.ts +6 -1
- package/dist/app/rendering/dcp-stats.js +214 -46
- package/dist/app/rendering/editor-panels.js +8 -5
- package/dist/app/session/lazy-session-manager.js +12 -1
- package/dist/app/session/tabs-controller.d.ts +2 -5
- package/dist/app/session/tabs-controller.js +12 -21
- package/dist/app/subagents/subagents-model.d.ts +14 -1
- package/dist/app/subagents/subagents-model.js +34 -15
- package/dist/app/types.d.ts +2 -0
- package/dist/bundled-extensions/session-title/config.js +1 -1
- package/dist/markdown-format.js +27 -9
- package/dist/schemas/pi-tools-suite-schema.d.ts +29 -16
- package/dist/schemas/pi-tools-suite-schema.js +46 -31
- package/external/pi-tools-suite/README.md +392 -52
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +31 -21
- package/external/pi-tools-suite/docs/context-gateway-p00-adr.md +216 -0
- package/external/pi-tools-suite/docs/context-gateway-p01n-gate-review.md +122 -0
- package/external/pi-tools-suite/docs/context-gateway-p01n-measurement.md +133 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-ra-evidence.md +111 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rb-evidence.md +100 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rc-evidence.md +69 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rd-evidence.md +100 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-re-evidence.md +74 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rf-evidence.md +153 -0
- package/external/pi-tools-suite/docs/context-gateway-p01r-rg-evidence.md +235 -0
- package/external/pi-tools-suite/docs/evals.md +684 -0
- package/external/pi-tools-suite/docs/subagent-model-pools.md +109 -0
- package/external/pi-tools-suite/package.json +10 -3
- package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/scripts/browser-qa-runner.mjs +82 -1
- package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/SKILL.md → agents/browser-qa.md} +261 -12
- package/external/pi-tools-suite/src/async-subagents/agents/implement.md +20 -0
- package/external/pi-tools-suite/src/async-subagents/agents/oracle.md +16 -0
- package/external/pi-tools-suite/src/async-subagents/agents/research.md +18 -0
- package/external/pi-tools-suite/src/async-subagents/agents/verify.md +18 -0
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +27 -243
- package/external/pi-tools-suite/src/async-subagents/commands.ts +6 -2
- package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +41 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +13 -93
- package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +494 -0
- package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +9 -0
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +200 -143
- package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +1 -1
- package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +54 -0
- package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +7 -6
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +52 -45
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +12 -4
- package/external/pi-tools-suite/src/async-subagents/index.ts +11 -1
- package/external/pi-tools-suite/src/async-subagents/lib.ts +6 -2
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +46 -18
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +3 -2
- package/external/pi-tools-suite/src/async-subagents/types.ts +2 -0
- package/external/pi-tools-suite/src/coding-discipline/index.ts +41 -142
- package/external/pi-tools-suite/src/config.ts +1 -22
- package/external/pi-tools-suite/src/context-gateway/accounting.ts +151 -0
- package/external/pi-tools-suite/src/context-gateway/config.ts +111 -0
- package/external/pi-tools-suite/src/context-gateway/index.ts +160 -0
- package/external/pi-tools-suite/src/context-gateway/metadata-normalization.ts +88 -0
- package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +89 -0
- package/external/pi-tools-suite/src/context-gateway/telemetry.ts +429 -0
- package/external/pi-tools-suite/src/context-gateway/test-output-parser.ts +326 -0
- package/external/pi-tools-suite/src/context-gateway/types.ts +152 -0
- package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +106 -0
- package/external/pi-tools-suite/src/dcp/auto-compress.ts +810 -106
- package/external/pi-tools-suite/src/dcp/commands.ts +64 -139
- package/external/pi-tools-suite/src/dcp/compress-tool.ts +369 -35
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +510 -64
- package/external/pi-tools-suite/src/dcp/compression-preview.ts +113 -0
- package/external/pi-tools-suite/src/dcp/compression-progress.ts +70 -0
- package/external/pi-tools-suite/src/dcp/config.ts +36 -61
- package/external/pi-tools-suite/src/dcp/conversation-index.ts +421 -0
- package/external/pi-tools-suite/src/dcp/debug-log.ts +7 -5
- package/external/pi-tools-suite/src/dcp/index.ts +617 -203
- package/external/pi-tools-suite/src/dcp/journal.ts +566 -0
- package/external/pi-tools-suite/src/dcp/progress-controller.ts +244 -0
- package/external/pi-tools-suite/src/dcp/prompts.ts +10 -7
- package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +189 -0
- package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +298 -78
- package/external/pi-tools-suite/src/dcp/pruner-compression-blocks.ts +173 -281
- package/external/pi-tools-suite/src/dcp/pruner-emergency.ts +2 -4
- package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +17 -5
- package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +11 -1
- package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +30 -82
- package/external/pi-tools-suite/src/dcp/pruner-tools.ts +22 -133
- package/external/pi-tools-suite/src/dcp/pruner.ts +18 -33
- package/external/pi-tools-suite/src/dcp/recovery.ts +129 -0
- package/external/pi-tools-suite/src/dcp/shadow-plan.ts +127 -0
- package/external/pi-tools-suite/src/dcp/state-transaction.ts +102 -0
- package/external/pi-tools-suite/src/dcp/state.ts +158 -580
- package/external/pi-tools-suite/src/dcp/ui.ts +1 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +55 -220
- package/external/pi-tools-suite/src/index.ts +9 -0
- package/external/pi-tools-suite/src/model-tools/index.ts +76 -42
- package/external/pi-tools-suite/src/repo-discovery/index.ts +84 -18
- package/external/pi-tools-suite/src/repo-discovery/native-compact.ts +458 -0
- package/external/pi-tools-suite/src/session-recovery/index.ts +189 -43
- package/external/pi-tools-suite/src/tool-descriptions.ts +43 -38
- package/external/pi-tools-suite/src/truncation-metadata-normalizer/index.ts +17 -0
- package/package.json +6 -6
- package/schemas/pi-tools-suite.json +159 -78
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/auth-scaffold-spec.md +0 -78
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +0 -223
- package/external/pi-tools-suite/src/dcp/state-persistence.ts +0 -195
- /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-auth.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-flow.example.jsonc +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.LICENSE +0 -0
- /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.mjs +0 -0
|
@@ -43,6 +43,11 @@ type Section = {
|
|
|
43
43
|
label: string;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
type RecoveryCursor =
|
|
47
|
+
| { version: 1; kind: "overview"; scope: Scope; afterSectionId: string }
|
|
48
|
+
| { version: 1; kind: "search"; scope: Scope; query: string; caseSensitive: boolean; afterEntryId: string }
|
|
49
|
+
| { version: 1; kind: "read"; scope: Scope; sectionId?: string; entryId: string; offset: number };
|
|
50
|
+
|
|
46
51
|
const SCOPE_SCHEMA = StringEnum(["active", "all"] as const, {
|
|
47
52
|
description: "Raw session scope. Defaults to the active root-to-leaf branch; all includes abandoned branches.",
|
|
48
53
|
});
|
|
@@ -61,6 +66,8 @@ const MAX_RECENT_ERRORS = 20;
|
|
|
61
66
|
const MAX_RECOVERY_FILES = 200;
|
|
62
67
|
const MAX_PENDING_TOOL_CALLS = 50;
|
|
63
68
|
const MAX_SEARCH_QUERY_CHARS = 500;
|
|
69
|
+
const CURSOR_VERSION = 1;
|
|
70
|
+
const DCP_CONTROL_CUSTOM_TYPES = new Set(["dcp-journal", "dcp-nudge"]);
|
|
64
71
|
|
|
65
72
|
const READ_TOOL_NAMES = new Set([
|
|
66
73
|
"read",
|
|
@@ -88,6 +95,10 @@ function isEntry(value: unknown): value is EntryLike {
|
|
|
88
95
|
&& typeof value.type === "string" && value.type.length > 0;
|
|
89
96
|
}
|
|
90
97
|
|
|
98
|
+
function isDcpControlEntry(entry: EntryLike): boolean {
|
|
99
|
+
return entry.type === "custom" && typeof entry.customType === "string" && DCP_CONTROL_CUSTOM_TYPES.has(entry.customType);
|
|
100
|
+
}
|
|
101
|
+
|
|
91
102
|
function clampInteger(value: unknown, fallback: number, minimum: number, maximum: number): number {
|
|
92
103
|
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
93
104
|
return Math.min(maximum, Math.max(minimum, Math.floor(value)));
|
|
@@ -119,7 +130,39 @@ function entriesFor(manager: SessionManagerLike | undefined, scope: Scope): Entr
|
|
|
119
130
|
: () => manager.getBranch?.(),
|
|
120
131
|
[],
|
|
121
132
|
);
|
|
122
|
-
return Array.isArray(raw) ? raw.filter(isEntry) : [];
|
|
133
|
+
return Array.isArray(raw) ? raw.filter(isEntry).filter((entry) => !isDcpControlEntry(entry)) : [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function encodeCursor(cursor: RecoveryCursor): string {
|
|
137
|
+
return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function decodeCursor(value: unknown): RecoveryCursor | undefined {
|
|
141
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 2_000) return undefined;
|
|
142
|
+
try {
|
|
143
|
+
const parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
|
|
144
|
+
if (!isRecord(parsed) || parsed.version !== CURSOR_VERSION || typeof parsed.kind !== "string") return undefined;
|
|
145
|
+
if (parsed.scope !== "active" && parsed.scope !== "all") return undefined;
|
|
146
|
+
if (parsed.kind === "overview" && typeof parsed.afterSectionId === "string") {
|
|
147
|
+
return parsed as RecoveryCursor;
|
|
148
|
+
}
|
|
149
|
+
if (
|
|
150
|
+
parsed.kind === "search" && typeof parsed.query === "string" && typeof parsed.caseSensitive === "boolean"
|
|
151
|
+
&& typeof parsed.afterEntryId === "string"
|
|
152
|
+
) {
|
|
153
|
+
return parsed as RecoveryCursor;
|
|
154
|
+
}
|
|
155
|
+
if (
|
|
156
|
+
parsed.kind === "read" && typeof parsed.entryId === "string" && typeof parsed.offset === "number"
|
|
157
|
+
&& Number.isInteger(parsed.offset) && parsed.offset >= 0
|
|
158
|
+
&& (parsed.sectionId === undefined || typeof parsed.sectionId === "string")
|
|
159
|
+
) {
|
|
160
|
+
return parsed as RecoveryCursor;
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
123
166
|
}
|
|
124
167
|
|
|
125
168
|
function messageFrom(entry: EntryLike): UnknownRecord | undefined {
|
|
@@ -358,16 +401,16 @@ function leafCount(entries: EntryLike[]): number {
|
|
|
358
401
|
return entries.reduce((count, entry) => count + (parents.has(entry.id) ? 0 : 1), 0);
|
|
359
402
|
}
|
|
360
403
|
|
|
361
|
-
function
|
|
404
|
+
function renderEntryFull(entry: EntryLike): string {
|
|
362
405
|
const heading = [`[${entry.id}]`, entry.timestamp, entry.type, messageRole(entry)].filter(Boolean).join(" ");
|
|
363
406
|
const lines = [heading];
|
|
364
407
|
const message = messageFrom(entry);
|
|
365
408
|
|
|
366
409
|
if (message) {
|
|
367
410
|
const text = textFromContent(message.content);
|
|
368
|
-
if (text) lines.push(
|
|
411
|
+
if (text) lines.push(text);
|
|
369
412
|
for (const call of toolCallsFrom(entry)) {
|
|
370
|
-
lines.push(`tool_call ${call.name}#${call.id} ${
|
|
413
|
+
lines.push(`tool_call ${call.name}#${call.id} ${serializeJson(call.arguments)}`);
|
|
371
414
|
}
|
|
372
415
|
if (message.role === "toolResult") {
|
|
373
416
|
const toolName = typeof message.toolName === "string" ? message.toolName : "unknown";
|
|
@@ -375,25 +418,39 @@ function renderEntry(entry: EntryLike, bodyChars: number): string {
|
|
|
375
418
|
lines.push(`tool_result ${toolName}#${callId}${message.isError === true ? " error" : ""}`);
|
|
376
419
|
}
|
|
377
420
|
} else if (entry.type === "compaction") {
|
|
378
|
-
lines.push(
|
|
421
|
+
lines.push(summaryFrom(entry));
|
|
379
422
|
if (typeof entry.firstKeptEntryId === "string") lines.push(`firstKeptEntryId: ${entry.firstKeptEntryId}`);
|
|
380
423
|
if (typeof entry.tokensBefore === "number") lines.push(`tokensBefore: ${entry.tokensBefore}`);
|
|
381
424
|
} else if (entry.type === "branch_summary") {
|
|
382
|
-
lines.push(
|
|
425
|
+
lines.push(summaryFrom(entry));
|
|
383
426
|
if (typeof entry.fromId === "string") lines.push(`fromId: ${entry.fromId}`);
|
|
384
427
|
} else if (entry.type === "custom_message") {
|
|
385
428
|
const text = textFromContent(entry.content);
|
|
386
|
-
if (text) lines.push(
|
|
429
|
+
if (text) lines.push(text);
|
|
387
430
|
} else {
|
|
388
431
|
const fields = Object.fromEntries(
|
|
389
432
|
Object.entries(entry).filter(([key]) => !["id", "parentId", "timestamp", "type"].includes(key)),
|
|
390
433
|
);
|
|
391
|
-
if (Object.keys(fields).length > 0) lines.push(safeJson(fields,
|
|
434
|
+
if (Object.keys(fields).length > 0) lines.push(safeJson(fields, MAX_BODY_CHARS));
|
|
392
435
|
}
|
|
393
436
|
|
|
394
437
|
return lines.join("\n");
|
|
395
438
|
}
|
|
396
439
|
|
|
440
|
+
function renderEntry(entry: EntryLike, bodyChars: number): string {
|
|
441
|
+
return truncate(renderEntryFull(entry), bodyChars);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function readEntryChunk(entry: EntryLike, offset: number, bodyChars: number): { text: string; nextOffset?: number } {
|
|
445
|
+
const rendered = renderEntryFull(entry);
|
|
446
|
+
const start = Math.min(Math.max(0, offset), rendered.length);
|
|
447
|
+
const end = Math.min(rendered.length, start + bodyChars);
|
|
448
|
+
return {
|
|
449
|
+
text: rendered.slice(start, end),
|
|
450
|
+
...(end < rendered.length ? { nextOffset: end } : {}),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
397
454
|
function contentResult(payload: unknown, details: UnknownRecord): { content: Array<{ type: "text"; text: string }>; details: UnknownRecord } {
|
|
398
455
|
const serialized = typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
|
|
399
456
|
return {
|
|
@@ -448,14 +505,19 @@ export default function sessionRecovery(pi: ExtensionAPI): void {
|
|
|
448
505
|
...SESSION_RECOVERY_TOOL_DESCRIPTIONS.overview,
|
|
449
506
|
parameters: Type.Object({
|
|
450
507
|
scope: Type.Optional(SCOPE_SCHEMA),
|
|
508
|
+
cursor: Type.Optional(Type.String({ description: "Opaque continuation cursor returned by a previous session_overview call.", maxLength: 2_000 })),
|
|
451
509
|
max_sections: Type.Optional(Type.Number({
|
|
452
|
-
description: "Maximum section summaries to return
|
|
510
|
+
description: "Maximum consecutive section summaries to return.",
|
|
453
511
|
minimum: 1,
|
|
454
512
|
maximum: MAX_OVERVIEW_SECTIONS,
|
|
455
513
|
})),
|
|
456
514
|
}, { additionalProperties: false }),
|
|
457
|
-
async execute(_toolCallId: string, params: { scope?: Scope; max_sections?: number }, _signal: AbortSignal, _onUpdate: unknown, ctx: unknown) {
|
|
515
|
+
async execute(_toolCallId: string, params: { scope?: Scope; cursor?: string; max_sections?: number }, _signal: AbortSignal, _onUpdate: unknown, ctx: unknown) {
|
|
458
516
|
const scope = scopeFrom(params.scope);
|
|
517
|
+
const cursor = params.cursor ? decodeCursor(params.cursor) : undefined;
|
|
518
|
+
if (params.cursor && (!cursor || cursor.kind !== "overview" || cursor.scope !== scope)) {
|
|
519
|
+
return contentResult("Invalid or stale session_overview cursor for this scope.", { scope, cursorValid: false });
|
|
520
|
+
}
|
|
459
521
|
const manager = sessionManagerFrom(ctx);
|
|
460
522
|
const entries = entriesFor(manager, scope);
|
|
461
523
|
if (entries.length === 0) return emptyResult(scope);
|
|
@@ -464,7 +526,19 @@ export default function sessionRecovery(pi: ExtensionAPI): void {
|
|
|
464
526
|
const allEntries = entriesFor(manager, "all");
|
|
465
527
|
const sections = buildSections(entries);
|
|
466
528
|
const maximum = clampInteger(params.max_sections, DEFAULT_OVERVIEW_SECTIONS, 1, MAX_OVERVIEW_SECTIONS);
|
|
467
|
-
|
|
529
|
+
let startIndex = 0;
|
|
530
|
+
if (cursor?.kind === "overview") {
|
|
531
|
+
const index = sections.findIndex((section) => section.id === cursor.afterSectionId);
|
|
532
|
+
if (index < 0) {
|
|
533
|
+
return contentResult("The session changed and the overview cursor no longer resolves on this branch.", { scope, cursorValid: false });
|
|
534
|
+
}
|
|
535
|
+
startIndex = index + 1;
|
|
536
|
+
}
|
|
537
|
+
const selected = sections.slice(startIndex, startIndex + maximum);
|
|
538
|
+
const hasMore = startIndex + selected.length < sections.length;
|
|
539
|
+
const nextCursor = hasMore && selected.length > 0
|
|
540
|
+
? encodeCursor({ version: CURSOR_VERSION, kind: "overview", scope, afterSectionId: selected[selected.length - 1]!.id })
|
|
541
|
+
: undefined;
|
|
468
542
|
const allLeaves = leafCount(allEntries);
|
|
469
543
|
const header = callSafely<unknown>(() => manager?.getHeader?.(), undefined);
|
|
470
544
|
const sessionId = callSafely<unknown>(() => manager?.getSessionId?.(), undefined);
|
|
@@ -487,57 +561,101 @@ export default function sessionRecovery(pi: ExtensionAPI): void {
|
|
|
487
561
|
leaves: allLeaves,
|
|
488
562
|
otherBranches: Math.max(0, allLeaves - (activeEntries.length > 0 ? 1 : 0)),
|
|
489
563
|
},
|
|
490
|
-
sections: selected.
|
|
491
|
-
|
|
564
|
+
sections: selected.map(sectionSummary),
|
|
565
|
+
hasMore,
|
|
566
|
+
nextCursor: nextCursor ?? null,
|
|
492
567
|
};
|
|
493
|
-
return contentResult(payload, { scope, entryCount: entries.length, sectionCount: sections.length,
|
|
568
|
+
return contentResult(payload, { scope, entryCount: entries.length, sectionCount: sections.length, returnedSections: selected.length, hasMore, nextCursor });
|
|
494
569
|
},
|
|
495
570
|
});
|
|
496
571
|
|
|
497
572
|
pi.registerTool({
|
|
498
573
|
...SESSION_RECOVERY_TOOL_DESCRIPTIONS.readSection,
|
|
499
574
|
parameters: Type.Object({
|
|
500
|
-
section_id: Type.String({ description: "Stable section ID returned by session_overview or session_search.", maxLength: 200 }),
|
|
575
|
+
section_id: Type.Optional(Type.String({ description: "Stable section ID returned by session_overview or session_search.", maxLength: 200 })),
|
|
576
|
+
entry_id: Type.Optional(Type.String({ description: "Read one exact raw session entry by ID without scanning from the start of its section.", maxLength: 200 })),
|
|
577
|
+
cursor: Type.Optional(Type.String({ description: "Opaque continuation cursor returned by a previous session_read_section call.", maxLength: 2_000 })),
|
|
501
578
|
scope: Type.Optional(SCOPE_SCHEMA),
|
|
502
579
|
max_entries: Type.Optional(Type.Number({ description: "Maximum entries to render from the section.", minimum: 1, maximum: MAX_SECTION_ENTRIES })),
|
|
503
|
-
max_body_chars: Type.Optional(Type.Number({ description: "Maximum rendered
|
|
580
|
+
max_body_chars: Type.Optional(Type.Number({ description: "Maximum rendered characters per entry page. Use the returned cursor to continue long entries.", minimum: 100, maximum: MAX_BODY_CHARS })),
|
|
504
581
|
}, { additionalProperties: false }),
|
|
505
|
-
async execute(_toolCallId: string, params: { section_id
|
|
582
|
+
async execute(_toolCallId: string, params: { section_id?: string; entry_id?: string; cursor?: string; scope?: Scope; max_entries?: number; max_body_chars?: number }, _signal: AbortSignal, _onUpdate: unknown, ctx: unknown) {
|
|
506
583
|
const scope = scopeFrom(params.scope);
|
|
584
|
+
if (!params.cursor && Boolean(params.section_id) === Boolean(params.entry_id)) {
|
|
585
|
+
return contentResult("Pass exactly one of section_id or entry_id, or continue with cursor.", { scope, found: false });
|
|
586
|
+
}
|
|
587
|
+
const cursor = params.cursor ? decodeCursor(params.cursor) : undefined;
|
|
588
|
+
if (params.cursor && (!cursor || cursor.kind !== "read" || cursor.scope !== scope)) {
|
|
589
|
+
return contentResult("Invalid or stale session_read_section cursor for this scope.", { scope, cursorValid: false, sourceAvailable: false });
|
|
590
|
+
}
|
|
507
591
|
const entries = entriesFor(sessionManagerFrom(ctx), scope);
|
|
508
592
|
if (entries.length === 0) return emptyResult(scope);
|
|
509
|
-
const
|
|
510
|
-
|
|
593
|
+
const sections = buildSections(entries);
|
|
594
|
+
const requestedSectionId = cursor?.kind === "read" ? cursor.sectionId : params.section_id;
|
|
595
|
+
const requestedEntryId = cursor?.kind === "read" ? cursor.entryId : params.entry_id;
|
|
596
|
+
const offset = cursor?.kind === "read" ? cursor.offset : 0;
|
|
597
|
+
const section = requestedSectionId ? sections.find((candidate) => candidate.id === requestedSectionId) : undefined;
|
|
598
|
+
if (requestedSectionId && !section) {
|
|
511
599
|
return contentResult(
|
|
512
|
-
`Section ${
|
|
513
|
-
{ scope, sectionId:
|
|
600
|
+
`Section ${requestedSectionId} was not found in scope ${scope}. Run session_overview with the same scope to refresh section IDs.`,
|
|
601
|
+
{ scope, sectionId: requestedSectionId, found: false, sourceAvailable: false },
|
|
514
602
|
);
|
|
515
603
|
}
|
|
516
604
|
|
|
517
605
|
const maximum = clampInteger(params.max_entries, DEFAULT_SECTION_ENTRIES, 1, MAX_SECTION_ENTRIES);
|
|
518
606
|
const bodyChars = clampInteger(params.max_body_chars, DEFAULT_BODY_CHARS, 100, MAX_BODY_CHARS);
|
|
607
|
+
const candidates = section ? section.entries : entries;
|
|
608
|
+
let startIndex = requestedEntryId ? candidates.findIndex((entry) => entry.id === requestedEntryId) : 0;
|
|
609
|
+
if (startIndex < 0) {
|
|
610
|
+
return contentResult(
|
|
611
|
+
`Entry ${requestedEntryId} was not found in scope ${scope}${section ? ` within ${section.id}` : ""}.`,
|
|
612
|
+
{ scope, sectionId: section?.id, entryId: requestedEntryId, found: false, sourceAvailable: false },
|
|
613
|
+
);
|
|
614
|
+
}
|
|
519
615
|
const rendered: string[] = [];
|
|
520
616
|
let renderedChars = 0;
|
|
521
|
-
let
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
617
|
+
let nextCursor: string | undefined;
|
|
618
|
+
let renderedCount = 0;
|
|
619
|
+
for (let index = startIndex; index < candidates.length && renderedCount < maximum; index += 1) {
|
|
620
|
+
const entry = candidates[index]!;
|
|
621
|
+
const entryOffset = index === startIndex ? offset : 0;
|
|
622
|
+
const chunk = readEntryChunk(entry, entryOffset, bodyChars);
|
|
623
|
+
const heading = entryOffset > 0 ? `[${entry.id}] continuation @${entryOffset}` : undefined;
|
|
624
|
+
const next = [heading, chunk.text].filter(Boolean).join("\n");
|
|
625
|
+
if (renderedChars + next.length + 2 > MAX_OUTPUT_CHARS - 1_000) break;
|
|
528
626
|
rendered.push(next);
|
|
529
627
|
renderedChars += next.length + 2;
|
|
628
|
+
renderedCount += 1;
|
|
629
|
+
if (chunk.nextOffset !== undefined) {
|
|
630
|
+
nextCursor = encodeCursor({ version: CURSOR_VERSION, kind: "read", scope, sectionId: section?.id, entryId: entry.id, offset: chunk.nextOffset });
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
const nextEntry = candidates[index + 1];
|
|
634
|
+
if (nextEntry && (renderedCount >= maximum || renderedChars >= MAX_OUTPUT_CHARS - 1_000)) {
|
|
635
|
+
nextCursor = encodeCursor({ version: CURSOR_VERSION, kind: "read", scope, sectionId: section?.id, entryId: nextEntry.id, offset: 0 });
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
530
638
|
}
|
|
531
|
-
|
|
639
|
+
if (!nextCursor) {
|
|
640
|
+
const lastRendered = renderedCount > 0 ? startIndex + renderedCount - 1 : startIndex - 1;
|
|
641
|
+
const nextEntry = candidates[lastRendered + 1];
|
|
642
|
+
if (nextEntry) nextCursor = encodeCursor({ version: CURSOR_VERSION, kind: "read", scope, sectionId: section?.id, entryId: nextEntry.id, offset: 0 });
|
|
643
|
+
}
|
|
644
|
+
const hasMore = nextCursor !== undefined;
|
|
645
|
+
const title = section ? `Section ${section.id}: ${section.label}` : `Entry ${requestedEntryId}`;
|
|
532
646
|
return contentResult(
|
|
533
|
-
[
|
|
647
|
+
[title, ...rendered, hasMore ? "… more available; continue with next_cursor" : ""].filter(Boolean).join("\n\n"),
|
|
534
648
|
{
|
|
535
649
|
scope,
|
|
536
|
-
sectionId: section
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
650
|
+
sectionId: section?.id,
|
|
651
|
+
entryId: requestedEntryId,
|
|
652
|
+
entryCount: candidates.length,
|
|
653
|
+
renderedCount,
|
|
654
|
+
found: true,
|
|
655
|
+
sourceAvailable: true,
|
|
656
|
+
hasMore,
|
|
657
|
+
truncated: hasMore,
|
|
658
|
+
nextCursor,
|
|
541
659
|
},
|
|
542
660
|
);
|
|
543
661
|
},
|
|
@@ -552,10 +670,11 @@ export default function sessionRecovery(pi: ExtensionAPI): void {
|
|
|
552
670
|
maxLength: MAX_SEARCH_QUERY_CHARS,
|
|
553
671
|
}),
|
|
554
672
|
scope: Type.Optional(SCOPE_SCHEMA),
|
|
673
|
+
cursor: Type.Optional(Type.String({ description: "Opaque continuation cursor returned by a previous session_search call.", maxLength: 2_000 })),
|
|
555
674
|
case_sensitive: Type.Optional(Type.Boolean({ description: "Use exact case matching. Defaults to false." })),
|
|
556
675
|
limit: Type.Optional(Type.Number({ description: "Maximum matches to return.", minimum: 1, maximum: MAX_SEARCH_RESULTS })),
|
|
557
676
|
}, { additionalProperties: false }),
|
|
558
|
-
async execute(_toolCallId: string, params: { query: string; scope?: Scope; case_sensitive?: boolean; limit?: number }, _signal: AbortSignal, _onUpdate: unknown, ctx: unknown) {
|
|
677
|
+
async execute(_toolCallId: string, params: { query: string; scope?: Scope; cursor?: string; case_sensitive?: boolean; limit?: number }, _signal: AbortSignal, _onUpdate: unknown, ctx: unknown) {
|
|
559
678
|
const scope = scopeFrom(params.scope);
|
|
560
679
|
const entries = entriesFor(sessionManagerFrom(ctx), scope);
|
|
561
680
|
if (entries.length === 0) return emptyResult(scope);
|
|
@@ -563,20 +682,24 @@ export default function sessionRecovery(pi: ExtensionAPI): void {
|
|
|
563
682
|
if (!query) return contentResult("Search query must not be empty.", { scope, query, matchCount: 0 });
|
|
564
683
|
|
|
565
684
|
const caseSensitive = params.case_sensitive === true;
|
|
685
|
+
const cursor = params.cursor ? decodeCursor(params.cursor) : undefined;
|
|
686
|
+
if (
|
|
687
|
+
params.cursor && (!cursor || cursor.kind !== "search" || cursor.scope !== scope
|
|
688
|
+
|| cursor.query !== query || cursor.caseSensitive !== caseSensitive)
|
|
689
|
+
) {
|
|
690
|
+
return contentResult("Invalid or stale session_search cursor for this scope/query.", { scope, query, cursorValid: false });
|
|
691
|
+
}
|
|
566
692
|
const needle = caseSensitive ? query : query.toLocaleLowerCase();
|
|
567
693
|
const limit = clampInteger(params.limit, DEFAULT_SEARCH_RESULTS, 1, MAX_SEARCH_RESULTS);
|
|
568
694
|
const sections = buildSections(entries);
|
|
569
695
|
const entrySections = sectionIdByEntry(sections);
|
|
570
|
-
const
|
|
571
|
-
let totalMatches = 0;
|
|
696
|
+
const allMatches: UnknownRecord[] = [];
|
|
572
697
|
|
|
573
698
|
for (const entry of entries) {
|
|
574
699
|
const text = entryText(entry);
|
|
575
700
|
const haystack = caseSensitive ? text : text.toLocaleLowerCase();
|
|
576
701
|
if (!text || !haystack.includes(needle)) continue;
|
|
577
|
-
|
|
578
|
-
if (matches.length >= limit) continue;
|
|
579
|
-
matches.push({
|
|
702
|
+
allMatches.push({
|
|
580
703
|
entryId: entry.id,
|
|
581
704
|
sectionId: entrySections.get(entry.id),
|
|
582
705
|
type: entry.type,
|
|
@@ -586,8 +709,31 @@ export default function sessionRecovery(pi: ExtensionAPI): void {
|
|
|
586
709
|
});
|
|
587
710
|
}
|
|
588
711
|
|
|
589
|
-
|
|
590
|
-
|
|
712
|
+
let startIndex = 0;
|
|
713
|
+
if (cursor?.kind === "search") {
|
|
714
|
+
const index = allMatches.findIndex((match) => match.entryId === cursor.afterEntryId);
|
|
715
|
+
if (index < 0) {
|
|
716
|
+
return contentResult("The session changed and the search cursor no longer resolves on this branch.", { scope, query, cursorValid: false });
|
|
717
|
+
}
|
|
718
|
+
startIndex = index + 1;
|
|
719
|
+
}
|
|
720
|
+
const matches = allMatches.slice(startIndex, startIndex + limit);
|
|
721
|
+
const hasMore = startIndex + matches.length < allMatches.length;
|
|
722
|
+
const lastEntryId = matches.at(-1)?.entryId;
|
|
723
|
+
const nextCursor = hasMore && typeof lastEntryId === "string"
|
|
724
|
+
? encodeCursor({ version: CURSOR_VERSION, kind: "search", scope, query, caseSensitive, afterEntryId: lastEntryId })
|
|
725
|
+
: undefined;
|
|
726
|
+
const payload = {
|
|
727
|
+
scope,
|
|
728
|
+
query,
|
|
729
|
+
caseSensitive,
|
|
730
|
+
totalMatches: allMatches.length,
|
|
731
|
+
returnedMatches: matches.length,
|
|
732
|
+
hasMore,
|
|
733
|
+
nextCursor: nextCursor ?? null,
|
|
734
|
+
matches,
|
|
735
|
+
};
|
|
736
|
+
return contentResult(payload, { scope, query, matchCount: allMatches.length, returnedCount: matches.length, hasMore, truncated: hasMore, nextCursor });
|
|
591
737
|
},
|
|
592
738
|
});
|
|
593
739
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { COMPRESS_RANGE_DESCRIPTION } from "./dcp/prompts.js";
|
|
2
|
+
import { SUBAGENT_TYPE_SELECTION_GUIDANCE } from "./async-subagents/core/agent-catalog.js";
|
|
3
|
+
import { SUBAGENT_DELEGATION_GUIDANCE } from "./async-subagents/core/agent-strategy.js";
|
|
2
4
|
|
|
3
5
|
export type ToolDescription = {
|
|
4
6
|
name: string;
|
|
@@ -39,10 +41,11 @@ export function astGrepToolDescriptions(maxLines: number, maxBytesLabel: string)
|
|
|
39
41
|
astGrep: {
|
|
40
42
|
name: "ast_grep",
|
|
41
43
|
label: "ast-grep",
|
|
42
|
-
description: `Read-only AST structural search/scan.
|
|
43
|
-
promptSnippet: "
|
|
44
|
+
description: `Read-only AST structural search/scan. MANDATORY ROUTING: for structural, syntax-aware, AST, language-aware, or code-shape matching, ast_grep must be the FIRST tool call. Never preflight with Glob, Grep, Read, or shell even when files are unknown; omit paths to scan the current project. Use for sgconfig/rule scans, JSON matches, and rewrite previews. Use Grep for exact literals/regex, Glob only for filename/path-only requests, and ast_apply for mutations. Output truncates at ${maxLines} lines or ${maxBytesLabel} with full output saved to a temp file.`,
|
|
45
|
+
promptSnippet: "MANDATORY: structural/syntax-aware/AST/code-shape matching => call ast_grep FIRST. Do not call Glob, Grep, Read, or shell before it; unknown files are not a reason to pre-search because ast_grep scans the project by default. Exact literal/regex only => Grep; filename/path only => Glob. Use ast_apply for mutations.",
|
|
44
46
|
promptGuidelines: [
|
|
45
|
-
"
|
|
47
|
+
"The first tool for syntax relationships or code-shape matching must be ast_grep, even when the file or language is unknown. Do not make a preliminary Glob/Grep/Read/shell call; start at the current project and narrow within ast_grep when needed.",
|
|
48
|
+
"Use Grep/Read directly for exact literal or regex lookups and Glob only for filename/path-only discovery; those text-only tasks must not trigger ast_grep.",
|
|
46
49
|
"ast_grep is read-only: use rewrite to preview only; use ast_apply for mutations or command=scan fixes.",
|
|
47
50
|
],
|
|
48
51
|
},
|
|
@@ -70,9 +73,9 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
|
|
|
70
73
|
"For every real-browser QA request, immediately spawn subagentType='browser-qa' before checking files, URLs, servers, or other prerequisites; the QA sub-agent owns feasibility checks and blocked reports, so the parent must not attempt browser QA itself.",
|
|
71
74
|
"If browser-qa reports that credentials are required, it must identify the generated project-local template and explicitly ask the user to fill it; the parent relays that request without reading or editing the credential file.",
|
|
72
75
|
"After browser testing, browser-qa must return clickable links for every available screenshot, video, and trace; the parent must preserve those links in its user-facing report.",
|
|
73
|
-
|
|
74
|
-
"Presets
|
|
75
|
-
|
|
76
|
+
SUBAGENT_DELEGATION_GUIDANCE,
|
|
77
|
+
"Presets declare available models; each agent's ordered models selects the first usable model in that pool. AGENTS_PRESET or /subagent-preset session <name> selects the current session pool; /subagent-preset init creates a sample config. Do not override the model merely to choose a role.",
|
|
78
|
+
SUBAGENT_TYPE_SELECTION_GUIDANCE,
|
|
76
79
|
repoDiscovery
|
|
77
80
|
? "Use for broad independent tracks, review axes, or hypotheses even though repo_* tools are available."
|
|
78
81
|
: "Use first for broad codebase discovery split into tracks, review axes, or incident-triage hypotheses when repo_* tools are unavailable.",
|
|
@@ -82,25 +85,26 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
|
|
|
82
85
|
promptSnippet:
|
|
83
86
|
"For every browser-based visual QA, UI bug reproduction, or real-browser fix-verification request, immediately spawn subagentType='browser-qa' even for a single track and before inspecting files or checking prerequisites. The browser-qa sub-agent must discover the target and report missing prerequisites; do not preflight, perform, or substitute browser QA in the parent agent. " +
|
|
84
87
|
"Give browser-qa a concise acceptance brief: the known target URL/app, user-visible flow, expected observable result, and required artifacts. Do not prescribe repository files, searches, commands, server setup, or mock/synthetic substitutes; unknown setup belongs to the QA sub-agent's discovery. " +
|
|
85
|
-
"For other work, use subagents action='spawn' for
|
|
86
|
-
|
|
88
|
+
"For other work, use subagents action='spawn' for economical execution or context isolation, including one bounded sequential task or explicit delegate/parallelize/split work requests. " +
|
|
89
|
+
SUBAGENT_TYPE_SELECTION_GUIDANCE + " Avoid trivial reads/edits and do not call status/wait immediately after spawn just for progress. " +
|
|
87
90
|
(repoDiscovery
|
|
88
91
|
? "For one semantic code-discovery question, use repo_search; for independent tracks/hypotheses/review axes, delegate even when repo_* tools exist. Read result only after completion when findings are needed."
|
|
89
|
-
: "For one focused code-discovery question, use direct read/grep. Without repo_* tools,
|
|
92
|
+
: "For one focused code-discovery question, use direct read/grep. Without repo_* tools, delegate bounded research tracks for broad discovery rather than flooding parent context. Read result only after completion when findings are needed."),
|
|
90
93
|
promptGuidelines: [
|
|
91
94
|
"Treat every real-browser QA request as a mandatory delegation trigger and an explicit exception to the large/parallel threshold: immediately spawn with `subagentType: \"browser-qa\"` before checking prerequisites. The QA sub-agent owns target discovery, feasibility checks, browser automation, evidence, and blocked reports; the parent must not inspect the project first or substitute non-browser checks.",
|
|
92
95
|
"Keep the browser-qa task payload at the user-visible acceptance level: known target URL/app, actions to perform, expected observable outcome, and requested evidence. Do not turn it into a repository investigation plan, name internal files or commands, dictate server setup, or invent a mock/synthetic target. Leave unknown prerequisites to the QA sub-agent.",
|
|
93
96
|
"When browser-qa reports missing credentials, relay its explicit request and generated `.pi/qa_auth.jsonc` template path; never inspect, populate, or edit that credential file in the parent.",
|
|
94
97
|
"After browser-qa completes a test, preserve its clickable screenshot, video, and trace links in the final user-facing response whenever those artifacts exist.",
|
|
95
|
-
|
|
98
|
+
SUBAGENT_DELEGATION_GUIDANCE,
|
|
96
99
|
repoDiscovery
|
|
97
100
|
? "For one discovery question, use repo_search; spawn for independent tracks/hypotheses/review axes, and do not let repo_* availability suppress delegation."
|
|
98
|
-
: "For one discovery question, use direct read/grep; when repo_* tools are unavailable,
|
|
101
|
+
: "For one small discovery question, use direct read/grep; when repo_* tools are unavailable, delegate scoped research to keep broad search output outside the parent context.",
|
|
99
102
|
repoDiscovery
|
|
100
103
|
? "For incident triage, release readiness, or risk/test strategy with separate hypotheses/review tracks, prefer focused agents over serial parent-context work."
|
|
101
104
|
: "For incident triage, release readiness, or risk/test strategy with separate hypotheses/review tracks and no repo_* tools, call action='spawn' as the first discovery step; direct read/grep can follow.",
|
|
102
|
-
"Do not use subagents for exact-string lookups,
|
|
103
|
-
"Spawn multiple focused agents in one action='spawn' call for independent questions; for
|
|
105
|
+
"Do not use subagents for trivial exact-string lookups, typo replacements, or interactive user input; use the cheapest direct path. A substantive bounded edit can be delegated even in one file.",
|
|
106
|
+
"Spawn multiple focused agents in one action='spawn' call for independent questions; set subagentType for clear role matches, timeoutSeconds for bounded probes, and use oracle sparingly for high-stakes uncertainty/final checks.",
|
|
107
|
+
"If spawn reports a routing error, no agents from that batch were launched. Correct the invalid or unresolved subagentType values using the available catalog and resubmit the whole batch; do not blindly retry omitted types or substitute an unsuitable role to suppress the error.",
|
|
104
108
|
"For screenshot/image inspection by blind models, use lookup; subagents only receive imagePaths when a broader delegated track genuinely needs them.",
|
|
105
109
|
"If asked to start/run/launch/test parallel sub-agents, spawn and stop; do not status/wait just for progress. Use status for recovery, wait only when needed/requested, result only after completion; compact results include artifact links.",
|
|
106
110
|
"Use action='stop' for stop/cancel/kill requests and action='cleanup' with delete=true only after collecting results.",
|
|
@@ -154,11 +158,10 @@ export const REPO_DISCOVERY_TOOLS: RepoDiscoveryToolDescription[] = [
|
|
|
154
158
|
name: "repo_architecture",
|
|
155
159
|
label: "Repo Architecture",
|
|
156
160
|
command: "architecture",
|
|
157
|
-
description: "Indexed
|
|
158
|
-
promptSnippet: "
|
|
161
|
+
description: "Indexed entrypoints, module boundaries, cycles and unresolved dependencies for broad, unfamiliar code.",
|
|
162
|
+
promptSnippet: "Map an unfamiliar area once; skip repo_architecture for known paths or exact lookups.",
|
|
159
163
|
promptGuidelines: [
|
|
160
|
-
"
|
|
161
|
-
"Broad unfamiliar codebase: make one narrow repo_architecture call first, add --path-prefix when a subsystem is known, then use repo_structure for files/symbols and repo_search for behavior.",
|
|
164
|
+
"Scope with --path-prefix; then use repo_structure or repo_search only for remaining gaps.",
|
|
162
165
|
],
|
|
163
166
|
},
|
|
164
167
|
{
|
|
@@ -166,19 +169,19 @@ export const REPO_DISCOVERY_TOOLS: RepoDiscoveryToolDescription[] = [
|
|
|
166
169
|
label: "Repo Structure",
|
|
167
170
|
command: "structure",
|
|
168
171
|
description: "Indexed file tree and exported-symbol view for a directory/module. Use to choose files/ranges without dumping source.",
|
|
169
|
-
promptSnippet: "
|
|
172
|
+
promptSnippet: "List one area with --max-files 20 --max-depth 2; continue with --cursor.",
|
|
170
173
|
promptGuidelines: [
|
|
171
|
-
"
|
|
174
|
+
"Narrow with --path-prefix/--kind. Add --include-internal or --include-tests-summary only for a named gap; page instead of listing hundreds of files.",
|
|
172
175
|
],
|
|
173
176
|
},
|
|
174
177
|
{
|
|
175
178
|
name: "repo_ast",
|
|
176
179
|
label: "Repo AST",
|
|
177
180
|
command: "ast",
|
|
178
|
-
description: "Indexed AST
|
|
179
|
-
promptSnippet: "
|
|
181
|
+
description: "Indexed AST outline for one known large file; locate exact ranges before reading source.",
|
|
182
|
+
promptSnippet: "Start --max-depth 3 --max-nodes 40 --no-include-text; read needed ranges with offset/limit.",
|
|
180
183
|
promptGuidelines: [
|
|
181
|
-
"
|
|
184
|
+
"Continue with --cursor; include snippets only when the outline cannot answer the question.",
|
|
182
185
|
],
|
|
183
186
|
targetDescription: "File path to map, e.g. src/api/client.ts.",
|
|
184
187
|
},
|
|
@@ -186,12 +189,12 @@ export const REPO_DISCOVERY_TOOLS: RepoDiscoveryToolDescription[] = [
|
|
|
186
189
|
name: "repo_search",
|
|
187
190
|
label: "Repo Search",
|
|
188
191
|
command: "search",
|
|
189
|
-
description: "Indexed hybrid
|
|
190
|
-
promptSnippet: "
|
|
192
|
+
description: "Indexed hybrid search for behavior when files or symbols are unknown. First pass: at most 3 results, no --include-content.",
|
|
193
|
+
promptSnippet: "Search behavior, not synonyms; keep hybrid unless lexical matches mislead. Read best returned ranges with offset/limit, not whole files.",
|
|
191
194
|
promptGuidelines: [
|
|
192
|
-
"
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
+
"Use --path-prefix/--dedupe-file when appropriate. Expand only for a named gap; use Grep for exact identifiers.",
|
|
196
|
+
"--include-content only for a narrow follow-up needing inline code, with --max-files 1; otherwise use read.",
|
|
197
|
+
"After finding the causal code, stop broad search; inspect callers, persistence or tests only for a named gap.",
|
|
195
198
|
],
|
|
196
199
|
targetDescription: "Natural-language behavior query, e.g. auth session token validation.",
|
|
197
200
|
},
|
|
@@ -200,9 +203,9 @@ export const REPO_DISCOVERY_TOOLS: RepoDiscoveryToolDescription[] = [
|
|
|
200
203
|
label: "Repo Explain",
|
|
201
204
|
command: "explain",
|
|
202
205
|
description: "Indexed explanation for a known symbol. Prefer file::symbol when the name may be ambiguous.",
|
|
203
|
-
promptSnippet: "Use
|
|
206
|
+
promptSnippet: "Use file::symbol; start --signature-only when signatures suffice.",
|
|
204
207
|
promptGuidelines: [
|
|
205
|
-
"
|
|
208
|
+
"Add --include-body --body-lines 20 only for implementation details; use repo_search when the symbol is unknown.",
|
|
206
209
|
],
|
|
207
210
|
targetDescription: "Symbol or file-scoped symbol, e.g. createClient or src/api/client.ts::createClient.",
|
|
208
211
|
},
|
|
@@ -210,10 +213,10 @@ export const REPO_DISCOVERY_TOOLS: RepoDiscoveryToolDescription[] = [
|
|
|
210
213
|
name: "repo_deps",
|
|
211
214
|
label: "Repo Deps",
|
|
212
215
|
command: "deps",
|
|
213
|
-
description: "Indexed
|
|
214
|
-
promptSnippet: "
|
|
216
|
+
description: "Indexed import/call dependencies for a known path or file::symbol.",
|
|
217
|
+
promptSnippet: "Start --depth 1 and choose --direction callers or callees for the question.",
|
|
215
218
|
promptGuidelines: [
|
|
216
|
-
"Use
|
|
219
|
+
"Use --mode calls for call relationships. Add --show-edges/--tests or deeper traversal only for a named impact-analysis gap.",
|
|
217
220
|
],
|
|
218
221
|
targetDescription: "Path or file-scoped symbol, e.g. src/api/client.ts or src/api/client.ts::createClient.",
|
|
219
222
|
},
|
|
@@ -263,10 +266,10 @@ export const SESSION_RECOVERY_TOOL_DESCRIPTIONS = {
|
|
|
263
266
|
readSection: {
|
|
264
267
|
name: "session_read_section",
|
|
265
268
|
label: "Session Read Section",
|
|
266
|
-
description: "Read
|
|
267
|
-
promptSnippet: "Read one
|
|
269
|
+
description: "Read bounded raw session history by stable section ID or exact entry ID, with opaque continuation cursors for long sections and long entries.",
|
|
270
|
+
promptSnippet: "Read one raw-session section or exact entry; continue with the returned cursor when more text is available.",
|
|
268
271
|
promptGuidelines: [
|
|
269
|
-
"Pass
|
|
272
|
+
"Pass either section_id or entry_id with the same active/all scope; use next_cursor to continue instead of restarting from the section head.",
|
|
270
273
|
],
|
|
271
274
|
},
|
|
272
275
|
search: {
|
|
@@ -275,7 +278,7 @@ export const SESSION_RECOVERY_TOOL_DESCRIPTIONS = {
|
|
|
275
278
|
description: "Search raw session messages, summaries, tool results, and serialized tool arguments with a bounded literal substring query.",
|
|
276
279
|
promptSnippet: "Search raw session history lexically when a concrete phrase, path, symbol, tool, or error is known.",
|
|
277
280
|
promptGuidelines: [
|
|
278
|
-
"Use session_search
|
|
281
|
+
"Use session_search when a concrete query is known; it is lexical rather than semantic, scope defaults to the active branch, and next_cursor continues large match sets.",
|
|
279
282
|
],
|
|
280
283
|
},
|
|
281
284
|
recoveryContext: {
|
|
@@ -314,6 +317,8 @@ export const WEB_SEARCH_TOOL_DESCRIPTIONS = {
|
|
|
314
317
|
},
|
|
315
318
|
} satisfies Record<string, ToolDescription>;
|
|
316
319
|
|
|
320
|
+
const SHELL_TEST_OUTPUT_GUIDANCE = "Tests: save full stdout/stderr to a unique log; emit only TEST_RESULT (passed/failed/incomplete), command, original exit code, verified counts (or unknown), and log path. Omit per-test PASS lines; show bounded exact failure diagnostics and flag omissions. Read only needed log ranges, never dump it. Preserve test exit status; timeout/abort is incomplete. Prefer supported compact reporters; do not alter tests/config just to shorten output.";
|
|
321
|
+
|
|
317
322
|
export function claudeAliasToolDescriptions(options: ToolDescriptionSetOptions | boolean = false) {
|
|
318
323
|
const repoDiscovery = hasRepoDiscovery(options);
|
|
319
324
|
|
|
@@ -338,7 +343,7 @@ export function claudeAliasToolDescriptions(options: ToolDescriptionSetOptions |
|
|
|
338
343
|
Bash: {
|
|
339
344
|
name: "Bash",
|
|
340
345
|
label: "Bash",
|
|
341
|
-
description:
|
|
346
|
+
description: `Run shell commands for builds, tests, package managers, git, and project CLIs. Prefer Read/Edit/Write/Grep/Glob for file operations. ${SHELL_TEST_OUTPUT_GUIDANCE}`,
|
|
342
347
|
},
|
|
343
348
|
Grep: {
|
|
344
349
|
name: "Grep",
|
|
@@ -364,7 +369,7 @@ export const CODEX_ALIAS_TOOL_DESCRIPTIONS = {
|
|
|
364
369
|
shellCommand: {
|
|
365
370
|
name: "shell",
|
|
366
371
|
label: "shell",
|
|
367
|
-
description:
|
|
372
|
+
description: `Run shell commands for builds, tests, package managers, git, and project CLIs. Set workdir/cwd instead of cd; prefer read for simple file reads. ${SHELL_TEST_OUTPUT_GUIDANCE}`,
|
|
368
373
|
},
|
|
369
374
|
applyPatch: {
|
|
370
375
|
name: "apply_patch",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
import { normalizeRedundantTruncationMetadata } from "../context-gateway/metadata-normalization.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Optional non-store optimization for SDK tool results.
|
|
7
|
+
*
|
|
8
|
+
* The module removes only truncation metadata text that is proven to duplicate
|
|
9
|
+
* the already-delivered visible text. It is disabled by default so existing
|
|
10
|
+
* sessions, including Context Gateway off/observe, remain byte-equivalent.
|
|
11
|
+
*/
|
|
12
|
+
export default function truncationMetadataNormalizer(pi: ExtensionAPI): void {
|
|
13
|
+
pi.on("tool_result", async (event) => {
|
|
14
|
+
const normalized = normalizeRedundantTruncationMetadata(event);
|
|
15
|
+
return normalized.changed ? { details: normalized.details } : undefined;
|
|
16
|
+
});
|
|
17
|
+
}
|