@oh-my-pi/pi-coding-agent 16.0.11 → 16.1.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/CHANGELOG.md +22 -0
- package/dist/cli.js +2872 -2908
- package/dist/types/config/settings-schema.d.ts +14 -4
- package/dist/types/modes/components/__tests__/skill-message.test.d.ts +1 -0
- package/dist/types/modes/components/assistant-message.d.ts +8 -0
- package/dist/types/modes/components/cache-invalidation-marker.d.ts +34 -0
- package/dist/types/modes/components/compaction-summary-message.d.ts +14 -1
- package/dist/types/modes/components/index.d.ts +0 -1
- package/dist/types/modes/components/message-frame.d.ts +6 -4
- package/dist/types/modes/interactive-mode.d.ts +2 -1
- package/dist/types/modes/theme/theme.d.ts +7 -1
- package/dist/types/modes/types.d.ts +7 -1
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +20 -1
- package/dist/types/session/session-context.d.ts +7 -0
- package/dist/types/session/session-dump-format.d.ts +1 -0
- package/dist/types/session/tool-choice-queue.d.ts +14 -0
- package/dist/types/system-prompt.d.ts +3 -3
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/resolve.d.ts +15 -5
- package/package.json +12 -12
- package/src/config/settings-schema.ts +16 -4
- package/src/debug/log-viewer.ts +4 -4
- package/src/debug/raw-sse.ts +4 -4
- package/src/edit/renderer.ts +2 -2
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/lsp/render.ts +7 -7
- package/src/modes/components/__tests__/skill-message.test.ts +92 -0
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/assistant-message.ts +21 -0
- package/src/modes/components/cache-invalidation-marker.ts +84 -0
- package/src/modes/components/chat-transcript-builder.ts +16 -2
- package/src/modes/components/compaction-summary-message.ts +29 -1
- package/src/modes/components/custom-message.ts +4 -1
- package/src/modes/components/dynamic-border.ts +1 -1
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +5 -5
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/index.ts +0 -1
- package/src/modes/components/message-frame.ts +10 -6
- package/src/modes/components/model-selector.ts +2 -2
- package/src/modes/components/overlay-box.ts +10 -9
- package/src/modes/components/skill-message.ts +39 -19
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/event-controller.ts +14 -0
- package/src/modes/controllers/selector-controller.ts +7 -0
- package/src/modes/interactive-mode.ts +9 -1
- package/src/modes/theme/theme.ts +14 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/utils/ui-helpers.ts +20 -2
- package/src/prompts/steering/user-interjection.md +3 -4
- package/src/sdk.ts +8 -6
- package/src/session/agent-session.ts +90 -13
- package/src/session/messages.ts +7 -9
- package/src/session/session-context.ts +54 -7
- package/src/session/session-dump-format.ts +3 -1
- package/src/session/snapcompact-inline.ts +2 -2
- package/src/session/tool-choice-queue.ts +59 -0
- package/src/system-prompt.ts +10 -9
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/index.ts +4 -0
- package/src/tools/resolve.ts +66 -41
- package/src/tui/output-block.ts +9 -9
- package/dist/types/modes/components/branch-summary-message.d.ts +0 -13
- package/src/modes/components/branch-summary-message.ts +0 -46
|
@@ -3,6 +3,8 @@ import type { Component } from "@oh-my-pi/pi-tui";
|
|
|
3
3
|
import { Box, Container, Markdown, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
5
5
|
import type { CustomMessage, SkillPromptDetails } from "../../session/messages";
|
|
6
|
+
import { shortenPath } from "../../tools/render-utils";
|
|
7
|
+
import { fileHyperlink } from "../../tui";
|
|
6
8
|
|
|
7
9
|
export class SkillMessageComponent extends Container {
|
|
8
10
|
#box: Box;
|
|
@@ -38,25 +40,26 @@ export class SkillMessageComponent extends Container {
|
|
|
38
40
|
this.removeChild(this.#box);
|
|
39
41
|
this.addChild(this.#box);
|
|
40
42
|
this.#box.clear();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.#box.addChild(new Text(label, 0, 0));
|
|
44
|
-
this.#box.addChild(new Spacer(1));
|
|
43
|
+
// Re-read symbols every rebuild so a runtime theme/preset switch refreshes the outline.
|
|
44
|
+
this.#box.setBorder({ chars: theme.boxRound, color: t => theme.fg("borderMuted", t) });
|
|
45
45
|
|
|
46
46
|
const details = this.message.details;
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
);
|
|
47
|
+
const name = details?.name?.trim() || "unknown";
|
|
48
|
+
// Collapse args to one line: a stray newline/tab in user-supplied args would split the header.
|
|
49
|
+
const args = details?.args?.replace(/\s+/g, " ").trim() ?? "";
|
|
50
|
+
|
|
51
|
+
// Header: icon-tag + skill name, with the invocation args trailing dimmed.
|
|
52
|
+
const tag = theme.fg("customMessageLabel", theme.bold(`${theme.icon.extensionSkill} skill`));
|
|
53
|
+
let header = `${tag} ${theme.fg("customMessageText", theme.bold(name))}`;
|
|
54
|
+
if (args) {
|
|
55
|
+
header += ` ${theme.fg("dim", args)}`;
|
|
56
|
+
}
|
|
57
|
+
this.#box.addChild(new Text(header, 0, 0));
|
|
58
|
+
|
|
59
|
+
const meta = this.#metaLine(details);
|
|
60
|
+
if (meta) {
|
|
61
|
+
this.#box.addChild(new Text(meta, 0, 0));
|
|
62
|
+
}
|
|
60
63
|
|
|
61
64
|
if (!this.#expanded) {
|
|
62
65
|
return;
|
|
@@ -68,8 +71,7 @@ export class SkillMessageComponent extends Container {
|
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
this.#box.addChild(new Spacer(1));
|
|
71
|
-
|
|
72
|
-
this.#box.addChild(new Text(promptHeader, 0, 0));
|
|
74
|
+
this.#box.addChild(new Text(theme.fg("muted", "prompt"), 0, 0));
|
|
73
75
|
this.#box.addChild(new Spacer(1));
|
|
74
76
|
|
|
75
77
|
this.#contentComponent = new Markdown(text, 0, 0, getMarkdownTheme(), {
|
|
@@ -78,6 +80,24 @@ export class SkillMessageComponent extends Container {
|
|
|
78
80
|
this.#box.addChild(this.#contentComponent);
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
/** Sub-line under the header: home-shortened (clickable) accent path · muted prompt size. */
|
|
84
|
+
#metaLine(details: SkillPromptDetails | undefined): string | undefined {
|
|
85
|
+
const parts: string[] = [];
|
|
86
|
+
|
|
87
|
+
const filePath = details?.path;
|
|
88
|
+
if (filePath) {
|
|
89
|
+
parts.push(fileHyperlink(filePath, theme.fg("accent", shortenPath(filePath)), { line: 1 }));
|
|
90
|
+
}
|
|
91
|
+
if (typeof details?.lineCount === "number") {
|
|
92
|
+
parts.push(theme.fg("muted", `${details.lineCount} ${details.lineCount === 1 ? "line" : "lines"}`));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (parts.length === 0) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
return ` ${parts.join(theme.fg("muted", theme.sep.dot))}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
81
101
|
#extractText(): string {
|
|
82
102
|
if (typeof this.message.content === "string") {
|
|
83
103
|
return this.message.content;
|
|
@@ -74,7 +74,7 @@ export class TinyTitleDownloadProgressComponent implements Component {
|
|
|
74
74
|
render(width: number): readonly string[] {
|
|
75
75
|
width = Math.max(1, width);
|
|
76
76
|
const spec = getTinyTitleModelSpec(this.#modelKey);
|
|
77
|
-
const border = theme.fg("border", theme.
|
|
77
|
+
const border = theme.fg("border", theme.boxRound.horizontal.repeat(width));
|
|
78
78
|
const status = statusLabel(this.#event);
|
|
79
79
|
const file = currentFile(this.#event);
|
|
80
80
|
const pct =
|
|
@@ -308,7 +308,7 @@ export class WelcomeComponent implements Component {
|
|
|
308
308
|
}
|
|
309
309
|
// Bottom border
|
|
310
310
|
if (showRightColumn) {
|
|
311
|
-
lines.push(bl + h.repeat(leftCol) + theme.fg("dim", theme.
|
|
311
|
+
lines.push(bl + h.repeat(leftCol) + theme.fg("dim", theme.boxRound.teeUp) + h.repeat(rightCol) + br);
|
|
312
312
|
} else {
|
|
313
313
|
lines.push(bl + h.repeat(leftCol) + br);
|
|
314
314
|
}
|
|
@@ -5,6 +5,7 @@ import { extractTextContent } from "../../commit/utils";
|
|
|
5
5
|
import { settings } from "../../config/settings";
|
|
6
6
|
import { getFileSnapshotStore } from "../../edit/file-snapshot-store";
|
|
7
7
|
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
8
|
+
import { detectCacheInvalidation } from "../../modes/components/cache-invalidation-marker";
|
|
8
9
|
import {
|
|
9
10
|
ReadToolGroupComponent,
|
|
10
11
|
readArgsHaveTarget,
|
|
@@ -659,6 +660,16 @@ export class EventController {
|
|
|
659
660
|
// waiting poll cannot be displaced anymore — freeze it in place.
|
|
660
661
|
this.#resolveDisplaceablePoll();
|
|
661
662
|
}
|
|
663
|
+
// Surface a prompt-cache invalidation: if the previous turn cached a
|
|
664
|
+
// meaningful prefix and this request read none of it back, flag the turn.
|
|
665
|
+
const usage = event.message.usage;
|
|
666
|
+
if (usage.cacheRead + usage.cacheWrite + usage.input > 0) {
|
|
667
|
+
if (settings.get("display.cacheMissMarker")) {
|
|
668
|
+
const invalidation = detectCacheInvalidation(this.ctx.lastAssistantUsage, usage);
|
|
669
|
+
if (invalidation) this.ctx.streamingComponent.setCacheInvalidation(invalidation);
|
|
670
|
+
}
|
|
671
|
+
this.ctx.lastAssistantUsage = usage;
|
|
672
|
+
}
|
|
662
673
|
this.#lastAssistantComponent = this.ctx.streamingComponent;
|
|
663
674
|
this.#lastAssistantComponent.markTranscriptBlockFinalized();
|
|
664
675
|
if (settings.get("display.showTokenUsage")) {
|
|
@@ -969,12 +980,14 @@ export class EventController {
|
|
|
969
980
|
}
|
|
970
981
|
this.ctx.showWarning(event.errorMessage);
|
|
971
982
|
} else if (!event.skipped) {
|
|
983
|
+
this.ctx.lastAssistantUsage = undefined;
|
|
972
984
|
this.ctx.rebuildChatFromMessages();
|
|
973
985
|
this.ctx.statusLine.invalidate();
|
|
974
986
|
this.ctx.updateEditorTopBorder();
|
|
975
987
|
this.ctx.showStatus("Auto-shake completed");
|
|
976
988
|
}
|
|
977
989
|
} else if (event.result) {
|
|
990
|
+
this.ctx.lastAssistantUsage = undefined;
|
|
978
991
|
this.ctx.rebuildChatFromMessages();
|
|
979
992
|
this.ctx.statusLine.invalidate();
|
|
980
993
|
this.ctx.updateEditorTopBorder();
|
|
@@ -982,6 +995,7 @@ export class EventController {
|
|
|
982
995
|
this.ctx.showWarning(event.errorMessage);
|
|
983
996
|
} else if (isHandoffAction) {
|
|
984
997
|
this.ctx.chatContainer.clear();
|
|
998
|
+
this.ctx.lastAssistantUsage = undefined;
|
|
985
999
|
this.ctx.rebuildChatFromMessages();
|
|
986
1000
|
this.ctx.statusLine.invalidate();
|
|
987
1001
|
this.ctx.updateEditorTopBorder();
|
|
@@ -327,6 +327,13 @@ export class SelectorController {
|
|
|
327
327
|
// InputController.toggleThinkingBlockVisibility).
|
|
328
328
|
this.ctx.ui.resetDisplay();
|
|
329
329
|
break;
|
|
330
|
+
case "display.cacheMissMarker":
|
|
331
|
+
// Rebuild re-runs the usage-based detection under the new setting so
|
|
332
|
+
// markers appear/disappear; full reset retires any already committed
|
|
333
|
+
// to native scrollback (mirrors hideThinking).
|
|
334
|
+
this.ctx.rebuildChatFromMessages();
|
|
335
|
+
this.ctx.ui.resetDisplay();
|
|
336
|
+
break;
|
|
330
337
|
case "tui.tight":
|
|
331
338
|
setTuiTight(value as boolean);
|
|
332
339
|
this.ctx.ui.invalidate();
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ThinkingLevel,
|
|
13
13
|
} from "@oh-my-pi/pi-agent-core";
|
|
14
14
|
import type { CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction";
|
|
15
|
-
import type { AssistantMessage, ImageContent, Message, Model, UsageReport } from "@oh-my-pi/pi-ai";
|
|
15
|
+
import type { AssistantMessage, ImageContent, Message, Model, Usage, UsageReport } from "@oh-my-pi/pi-ai";
|
|
16
16
|
import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
17
17
|
import type {
|
|
18
18
|
Component,
|
|
@@ -412,6 +412,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
412
412
|
isPythonMode = false;
|
|
413
413
|
streamingComponent: AssistantMessageComponent | undefined = undefined;
|
|
414
414
|
streamingMessage: AssistantMessage | undefined = undefined;
|
|
415
|
+
lastAssistantUsage: Usage | undefined = undefined;
|
|
415
416
|
loadingAnimation: Loader | undefined = undefined;
|
|
416
417
|
autoCompactionLoader: Loader | undefined = undefined;
|
|
417
418
|
retryLoader: Loader | undefined = undefined;
|
|
@@ -512,6 +513,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
512
513
|
this.compactionQueuedMessages = [];
|
|
513
514
|
this.streamingComponent = undefined;
|
|
514
515
|
this.streamingMessage = undefined;
|
|
516
|
+
this.lastAssistantUsage = undefined;
|
|
515
517
|
this.pendingTools.clear();
|
|
516
518
|
}
|
|
517
519
|
readonly #uiHelpers: UiHelpers;
|
|
@@ -1858,6 +1860,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1858
1860
|
this.#planModePreviousTools = previousTools;
|
|
1859
1861
|
this.planModePlanFilePath = planFilePath;
|
|
1860
1862
|
this.planModeEnabled = true;
|
|
1863
|
+
// Suppress cache-miss marker on the next turn: plan mode changes the system
|
|
1864
|
+
// prompt, which predictably invalidates the cache.
|
|
1865
|
+
this.lastAssistantUsage = undefined;
|
|
1861
1866
|
|
|
1862
1867
|
await this.session.setActiveToolsByName(uniquePlanTools);
|
|
1863
1868
|
this.session.setPlanModeState({
|
|
@@ -1975,6 +1980,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1975
1980
|
this.session.setStandingResolveHandler?.(null);
|
|
1976
1981
|
this.session.setPlanModeState(undefined);
|
|
1977
1982
|
this.planModeEnabled = false;
|
|
1983
|
+
// Suppress cache-miss marker on the next turn: plan exit changes the system
|
|
1984
|
+
// prompt, which predictably invalidates the cache.
|
|
1985
|
+
this.lastAssistantUsage = undefined;
|
|
1978
1986
|
this.planModePaused = options?.paused ?? false;
|
|
1979
1987
|
this.planModePlanFilePath = undefined;
|
|
1980
1988
|
this.#planModePreviousTools = undefined;
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -111,6 +111,7 @@ export type SymbolKey =
|
|
|
111
111
|
| "icon.agents"
|
|
112
112
|
| "icon.job"
|
|
113
113
|
| "icon.cache"
|
|
114
|
+
| "icon.cacheMiss"
|
|
114
115
|
| "icon.input"
|
|
115
116
|
| "icon.output"
|
|
116
117
|
| "icon.host"
|
|
@@ -310,6 +311,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
310
311
|
"icon.agents": "👥",
|
|
311
312
|
"icon.job": "⚙",
|
|
312
313
|
"icon.cache": "💾",
|
|
314
|
+
"icon.cacheMiss": "⊘",
|
|
313
315
|
"icon.input": "⤵",
|
|
314
316
|
"icon.output": "⤴",
|
|
315
317
|
"icon.host": "🖥",
|
|
@@ -579,6 +581,8 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
579
581
|
"icon.job": "\uf013",
|
|
580
582
|
// pick: | alt:
|
|
581
583
|
"icon.cache": "\uf1c0",
|
|
584
|
+
// pick: (fa-ban) | alt: ⊘
|
|
585
|
+
"icon.cacheMiss": "\uf05e",
|
|
582
586
|
// pick: | alt: →
|
|
583
587
|
"icon.input": "\uf090",
|
|
584
588
|
// pick: | alt: →
|
|
@@ -810,6 +814,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
810
814
|
"icon.agents": "AG",
|
|
811
815
|
"icon.job": "bg",
|
|
812
816
|
"icon.cache": "cache",
|
|
817
|
+
"icon.cacheMiss": "!",
|
|
813
818
|
"icon.input": "in:",
|
|
814
819
|
"icon.output": "out:",
|
|
815
820
|
"icon.host": "host",
|
|
@@ -1711,6 +1716,14 @@ export class Theme {
|
|
|
1711
1716
|
bottomRight: this.#symbols["boxRound.bottomRight"],
|
|
1712
1717
|
horizontal: this.#symbols["boxRound.horizontal"],
|
|
1713
1718
|
vertical: this.#symbols["boxRound.vertical"],
|
|
1719
|
+
// Junctions have no rounded Unicode variant, so a rounded box reuses the
|
|
1720
|
+
// sharp tee/cross glyphs. Sourcing them from the boxSharp.* tokens keeps a
|
|
1721
|
+
// theme's `boxSharp.tee*` overrides effective for rounded-box dividers.
|
|
1722
|
+
cross: this.#symbols["boxSharp.cross"],
|
|
1723
|
+
teeDown: this.#symbols["boxSharp.teeDown"],
|
|
1724
|
+
teeUp: this.#symbols["boxSharp.teeUp"],
|
|
1725
|
+
teeRight: this.#symbols["boxSharp.teeRight"],
|
|
1726
|
+
teeLeft: this.#symbols["boxSharp.teeLeft"],
|
|
1714
1727
|
};
|
|
1715
1728
|
}
|
|
1716
1729
|
|
|
@@ -1770,6 +1783,7 @@ export class Theme {
|
|
|
1770
1783
|
agents: this.#symbols["icon.agents"],
|
|
1771
1784
|
job: this.#symbols["icon.job"],
|
|
1772
1785
|
cache: this.#symbols["icon.cache"],
|
|
1786
|
+
cacheMiss: this.#symbols["icon.cacheMiss"],
|
|
1773
1787
|
input: this.#symbols["icon.input"],
|
|
1774
1788
|
output: this.#symbols["icon.output"],
|
|
1775
1789
|
host: this.#symbols["icon.host"],
|
package/src/modes/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
|
-
import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { AssistantMessage, ImageContent, Message, Usage, UsageReport } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import type { Component, Container, EditorTheme, Loader, Spacer, Text, TUI } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import type { CollabGuestLink } from "../collab/guest";
|
|
6
6
|
import type { CollabHost } from "../collab/host";
|
|
@@ -159,6 +159,12 @@ export interface InteractiveModeContext {
|
|
|
159
159
|
isPythonMode: boolean;
|
|
160
160
|
streamingComponent: AssistantMessageComponent | undefined;
|
|
161
161
|
streamingMessage: AssistantMessage | undefined;
|
|
162
|
+
/**
|
|
163
|
+
* Usage of the most recently rendered assistant turn, used to detect a
|
|
164
|
+
* prompt-cache invalidation on the next turn (cache footprint collapse).
|
|
165
|
+
* Reseeded by `renderSessionContext` on every rebuild/session switch.
|
|
166
|
+
*/
|
|
167
|
+
lastAssistantUsage: Usage | undefined;
|
|
162
168
|
loadingAnimation: Loader | undefined;
|
|
163
169
|
autoCompactionLoader: Loader | undefined;
|
|
164
170
|
retryLoader: Loader | undefined;
|
|
@@ -9,9 +9,10 @@ import { createAdvisorMessageCard } from "../../modes/components/advisor-message
|
|
|
9
9
|
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
10
10
|
import { createBackgroundTanDispatchBlock } from "../../modes/components/background-tan-message";
|
|
11
11
|
import { BashExecutionComponent } from "../../modes/components/bash-execution";
|
|
12
|
-
import {
|
|
12
|
+
import { detectCacheInvalidation } from "../../modes/components/cache-invalidation-marker";
|
|
13
13
|
import { CollabPromptMessageComponent } from "../../modes/components/collab-prompt-message";
|
|
14
14
|
import {
|
|
15
|
+
BranchSummaryMessageComponent,
|
|
15
16
|
CompactionSummaryMessageComponent,
|
|
16
17
|
createHandoffSummaryMessageComponent,
|
|
17
18
|
} from "../../modes/components/compaction-summary-message";
|
|
@@ -358,6 +359,9 @@ export class UiHelpers {
|
|
|
358
359
|
): void {
|
|
359
360
|
// Preserved: message_start handler owns this lifecycle (see #783)
|
|
360
361
|
this.ctx.pendingTools.clear();
|
|
362
|
+
// Reseed the cache-invalidation baseline: this rebuild re-derives every
|
|
363
|
+
// turn's marker from usage, and the last turn becomes the live baseline.
|
|
364
|
+
this.ctx.lastAssistantUsage = undefined;
|
|
361
365
|
|
|
362
366
|
if (options.updateFooter) {
|
|
363
367
|
this.ctx.statusLine.invalidate();
|
|
@@ -399,13 +403,27 @@ export class UiHelpers {
|
|
|
399
403
|
// updateResult armed.
|
|
400
404
|
previous.seal();
|
|
401
405
|
};
|
|
402
|
-
|
|
406
|
+
const messages = sessionContext.messages;
|
|
407
|
+
const count = messages.length;
|
|
408
|
+
for (let i = 0; i < count; i++) {
|
|
409
|
+
const message = messages[i]!;
|
|
403
410
|
if (message.role !== "toolResult") flushPendingUsage();
|
|
404
411
|
// Assistant messages need special handling for tool calls
|
|
405
412
|
if (message.role === "assistant") {
|
|
406
413
|
this.ctx.addMessageToChat(message);
|
|
407
414
|
const lastChild = this.ctx.chatContainer.children[this.ctx.chatContainer.children.length - 1];
|
|
408
415
|
const assistantComponent = lastChild instanceof AssistantMessageComponent ? lastChild : undefined;
|
|
416
|
+
if (assistantComponent) {
|
|
417
|
+
const usage = message.usage;
|
|
418
|
+
const explained = sessionContext.cacheMissExplainedAt?.[i] ?? false;
|
|
419
|
+
if (this.ctx.settings.get("display.cacheMissMarker") && !explained) {
|
|
420
|
+
const invalidation = detectCacheInvalidation(this.ctx.lastAssistantUsage, usage);
|
|
421
|
+
if (invalidation) assistantComponent.setCacheInvalidation(invalidation);
|
|
422
|
+
}
|
|
423
|
+
if (usage.cacheRead + usage.cacheWrite + usage.input > 0) {
|
|
424
|
+
this.ctx.lastAssistantUsage = usage;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
409
427
|
const hasVisibleAssistantContent = message.content.some(
|
|
410
428
|
content =>
|
|
411
429
|
(content.type === "text" && canonicalizeMessage(content.text)) ||
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<user_interjection>
|
|
2
|
-
The user sent this message while you were working
|
|
3
|
-
priority and supersedes
|
|
4
|
-
|
|
5
|
-
now.
|
|
2
|
+
The user sent this message as an interjection while you were working. It takes
|
|
3
|
+
priority and supersedes earlier instructions wherever they conflict — re-read it
|
|
4
|
+
and make sure your current work reflects their intent.
|
|
6
5
|
|
|
7
6
|
<message>
|
|
8
7
|
{{message}}
|
package/src/sdk.ts
CHANGED
|
@@ -838,7 +838,7 @@ export interface BuildSystemPromptOptions {
|
|
|
838
838
|
contextFiles?: Array<{ path: string; content: string }>;
|
|
839
839
|
cwd?: string;
|
|
840
840
|
appendPrompt?: string;
|
|
841
|
-
|
|
841
|
+
inlineToolDescriptors?: boolean;
|
|
842
842
|
}
|
|
843
843
|
|
|
844
844
|
/**
|
|
@@ -853,7 +853,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
853
853
|
skills: options.skills,
|
|
854
854
|
contextFiles: options.contextFiles,
|
|
855
855
|
appendSystemPrompt: options.appendPrompt,
|
|
856
|
-
|
|
856
|
+
inlineToolDescriptors: options.inlineToolDescriptors,
|
|
857
857
|
});
|
|
858
858
|
}
|
|
859
859
|
|
|
@@ -2130,7 +2130,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2130
2130
|
emitEvent: event => cursorEventEmitter?.(event),
|
|
2131
2131
|
});
|
|
2132
2132
|
|
|
2133
|
-
const
|
|
2133
|
+
const inlineToolDescriptors = settings.get("inlineToolDescriptors");
|
|
2134
2134
|
const eagerTasks = settings.get("task.eager") !== "default";
|
|
2135
2135
|
const eagerTasksAlways = settings.get("task.eager") === "always";
|
|
2136
2136
|
const intentField = $flag("PI_INTENT_TRACING", settings.get("tools.intentTracing")) ? INTENT_FIELD : undefined;
|
|
@@ -2198,7 +2198,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2198
2198
|
}
|
|
2199
2199
|
appendPrompt = parts.join("\n\n");
|
|
2200
2200
|
}
|
|
2201
|
-
// Owned/in-band tool
|
|
2201
|
+
// Owned/in-band tool dialects (non-native) require the catalog as `# Tool:`
|
|
2202
2202
|
// sections; native tool calling lets the compact name list suffice.
|
|
2203
2203
|
const nativeTools = resolveDialect(settings.get("tools.format"), agent?.state.model ?? model) === undefined;
|
|
2204
2204
|
const defaultPrompt = await buildSystemPromptInternal({
|
|
@@ -2211,7 +2211,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2211
2211
|
alwaysApplyRules,
|
|
2212
2212
|
skillsSettings: settings.getGroup("skills"),
|
|
2213
2213
|
appendSystemPrompt: appendPrompt,
|
|
2214
|
-
|
|
2214
|
+
inlineToolDescriptors,
|
|
2215
2215
|
nativeTools,
|
|
2216
2216
|
intentField,
|
|
2217
2217
|
mcpDiscoveryMode: hasDiscoverableTools,
|
|
@@ -2536,9 +2536,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2536
2536
|
return result;
|
|
2537
2537
|
},
|
|
2538
2538
|
intentTracing: !!intentField,
|
|
2539
|
+
pruneToolDescriptions: inlineToolDescriptors,
|
|
2539
2540
|
dialect: resolveDialect(settings.get("tools.format"), model),
|
|
2540
2541
|
abortOnFabricatedToolResult: settings.get("tools.abortOnFabricatedResult"),
|
|
2541
|
-
getToolChoice: () => session?.
|
|
2542
|
+
getToolChoice: () => session?.nextToolChoiceDirective(),
|
|
2542
2543
|
telemetry: options.telemetry,
|
|
2543
2544
|
appendOnlyContext: model
|
|
2544
2545
|
? shouldEnableAppendOnlyContext(settings.get("provider.appendOnlyContext"), model)
|
|
@@ -2606,6 +2607,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2606
2607
|
session = new AgentSession({
|
|
2607
2608
|
advisorWatchdogPrompt,
|
|
2608
2609
|
agent,
|
|
2610
|
+
pruneToolDescriptions: inlineToolDescriptors,
|
|
2609
2611
|
thinkingLevel: autoThinking ? AUTO_THINKING : effectiveThinkingLevel,
|
|
2610
2612
|
sessionManager,
|
|
2611
2613
|
settings,
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
countTokens,
|
|
36
36
|
resolveTelemetry,
|
|
37
37
|
ThinkingLevel,
|
|
38
|
+
type ToolChoiceDirective,
|
|
38
39
|
} from "@oh-my-pi/pi-agent-core";
|
|
39
40
|
import {
|
|
40
41
|
AGGRESSIVE_SHAKE_CONFIG,
|
|
@@ -102,6 +103,7 @@ import {
|
|
|
102
103
|
resolveServiceTier,
|
|
103
104
|
streamSimple,
|
|
104
105
|
} from "@oh-my-pi/pi-ai";
|
|
106
|
+
import { stripToolDescriptions } from "@oh-my-pi/pi-ai/utils/schema";
|
|
105
107
|
import { getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
106
108
|
import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
107
109
|
import { MacOSPowerAssertion } from "@oh-my-pi/pi-natives";
|
|
@@ -260,6 +262,7 @@ import type { CheckpointState } from "../tools/checkpoint";
|
|
|
260
262
|
import { outputMeta, wrapToolWithMetaNotice } from "../tools/output-meta";
|
|
261
263
|
import { normalizeLocalScheme, resolveToCwd } from "../tools/path-utils";
|
|
262
264
|
import { isAutoQaEnabled } from "../tools/report-tool-issue";
|
|
265
|
+
import { buildResolveReminderMessage } from "../tools/resolve";
|
|
263
266
|
import { getLatestTodoPhasesFromEntries, type TodoItem, type TodoPhase } from "../tools/todo";
|
|
264
267
|
import { ToolAbortError, ToolError } from "../tools/tool-errors";
|
|
265
268
|
import { clampTimeout } from "../tools/tool-timeouts";
|
|
@@ -367,6 +370,23 @@ const COMPACTION_CHECK_CONTINUATION: CompactionCheckResult = {
|
|
|
367
370
|
deferredHandoff: false,
|
|
368
371
|
continuationScheduled: true,
|
|
369
372
|
};
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Per-turn prune cache window. A tool result whose all-message suffix exceeds
|
|
376
|
+
* this is in the warm, already-sent prompt-cache prefix: re-writing it costs the
|
|
377
|
+
* cacheWrite premium on the whole suffix. Per-turn passes only reclaim inside
|
|
378
|
+
* this tail (matches the supersede pass's default `suffixTokenLimit`); deeper
|
|
379
|
+
* stale/age victims are left to compaction/shake, which rebuild the cache anyway.
|
|
380
|
+
*/
|
|
381
|
+
const PRUNE_CACHE_WARM_SUFFIX_TOKENS = 8_000;
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Idle gap after which the supersede pass may flush the whole sent region (the
|
|
385
|
+
* provider cache is cold, so re-writing it is free). MUST exceed the maximum
|
|
386
|
+
* Anthropic prompt-cache TTL — "long" retention (the OAuth default) is 1h — or a
|
|
387
|
+
* still-warm prefix is busted by the flush. 90 min leaves margin over the 1h TTL.
|
|
388
|
+
*/
|
|
389
|
+
const PRUNE_IDLE_FLUSH_MS = 90 * 60_000;
|
|
370
390
|
export type CommandMetadataChangedListener = () => void | Promise<void>;
|
|
371
391
|
export type AsyncJobSnapshotItem = Pick<AsyncJob, "id" | "type" | "status" | "label" | "startTime">;
|
|
372
392
|
|
|
@@ -516,6 +536,12 @@ export interface AgentSessionConfig {
|
|
|
516
536
|
advisorReadOnlyTools?: AgentTool[];
|
|
517
537
|
/** Preloaded watchdog prompt content for the advisor. */
|
|
518
538
|
advisorWatchdogPrompt?: string;
|
|
539
|
+
/**
|
|
540
|
+
* Strip tool descriptions from provider-bound tool specs on side requests
|
|
541
|
+
* (handoff). Must match the session-start value used to build the system
|
|
542
|
+
* prompt so inline descriptors are not also sent through provider schemas.
|
|
543
|
+
*/
|
|
544
|
+
pruneToolDescriptions?: boolean;
|
|
519
545
|
/**
|
|
520
546
|
* Disconnect this session's OWNED MCP manager on dispose. Provided only when
|
|
521
547
|
* the session created the manager (top-level sessions); subagents reuse a
|
|
@@ -1305,6 +1331,8 @@ export class AgentSession {
|
|
|
1305
1331
|
// unchanged — otherwise a mid-turn estimate would survive into idle.
|
|
1306
1332
|
#contextUsageRevision = 0;
|
|
1307
1333
|
#obfuscator: SecretObfuscator | undefined;
|
|
1334
|
+
/** Session-start value of `inlineToolDescriptors`; drives handoff tool pruning. */
|
|
1335
|
+
#pruneToolDescriptions = false;
|
|
1308
1336
|
#checkpointState: CheckpointState | undefined = undefined;
|
|
1309
1337
|
#pendingRewindReport: string | undefined = undefined;
|
|
1310
1338
|
#lastSuccessfulYieldToolCallId: string | undefined = undefined;
|
|
@@ -1513,6 +1541,7 @@ export class AgentSession {
|
|
|
1513
1541
|
this.#modelRegistry = config.modelRegistry;
|
|
1514
1542
|
this.#advisorReadOnlyTools = config.advisorReadOnlyTools;
|
|
1515
1543
|
this.#advisorWatchdogPrompt = config.advisorWatchdogPrompt;
|
|
1544
|
+
this.#pruneToolDescriptions = config.pruneToolDescriptions === true;
|
|
1516
1545
|
this.#validateRetryFallbackChains();
|
|
1517
1546
|
this.#toolRegistry = config.toolRegistry ?? new Map();
|
|
1518
1547
|
this.#requestedToolNames = config.requestedToolNames;
|
|
@@ -2124,6 +2153,36 @@ export class AgentSession {
|
|
|
2124
2153
|
return undefined;
|
|
2125
2154
|
}
|
|
2126
2155
|
|
|
2156
|
+
/**
|
|
2157
|
+
* The per-turn tool-choice directive for the agent loop's `getToolChoice`. Priority:
|
|
2158
|
+
* 1. a HARD forced choice from the queue (genuine forces: user-force, eager-todo, …) —
|
|
2159
|
+
* consuming, unchanged from `nextToolChoice`;
|
|
2160
|
+
* 2. else, when a non-forcing preview is pending, a {@link SoftToolRequirement} — a
|
|
2161
|
+
* PEEK (advances/pops nothing), so the agent-loop injects the reminder once per head
|
|
2162
|
+
* and escalates to a forced `resolve` only if the model declines. A compliant turn
|
|
2163
|
+
* pays ZERO tool_choice change (no prompt-cache messages-cache invalidation);
|
|
2164
|
+
* 3. else undefined.
|
|
2165
|
+
*/
|
|
2166
|
+
nextToolChoiceDirective(): ToolChoiceDirective | undefined {
|
|
2167
|
+
const hard = this.nextToolChoice();
|
|
2168
|
+
if (hard !== undefined) return hard;
|
|
2169
|
+
const head = this.#toolChoiceQueue.peekPendingHead();
|
|
2170
|
+
if (head !== undefined) {
|
|
2171
|
+
return {
|
|
2172
|
+
soft: true,
|
|
2173
|
+
id: head.id,
|
|
2174
|
+
toolName: "resolve",
|
|
2175
|
+
reminder: [buildResolveReminderMessage(head.sourceToolName)],
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2178
|
+
return undefined;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
/** Peek the head non-forcing pending preview invoker, for the `resolve` tool's dispatch. */
|
|
2182
|
+
peekPendingInvoker(): ((input: unknown) => Promise<unknown> | unknown) | undefined {
|
|
2183
|
+
return this.#toolChoiceQueue.peekPendingInvoker();
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2127
2186
|
/**
|
|
2128
2187
|
* Force the next model call to target a specific active tool, then terminate
|
|
2129
2188
|
* the agent loop. Pushes a two-step sequence [forced, "none"] so the model
|
|
@@ -4851,7 +4910,7 @@ export class AgentSession {
|
|
|
4851
4910
|
* cache per-tool strings without preserving this property.
|
|
4852
4911
|
*
|
|
4853
4912
|
* Inputs NOT covered: tool input schemas; memory instructions read from disk;
|
|
4854
|
-
* and SDK-init-time closure constants in `sdk.ts` (`
|
|
4913
|
+
* and SDK-init-time closure constants in `sdk.ts` (`inlineToolDescriptors`,
|
|
4855
4914
|
* `eagerTasks`, `intentField`, `mcpDiscoveryEnabled`, `secretsEnabled`). The
|
|
4856
4915
|
* closure-captured ones cannot change at runtime regardless of skip behavior.
|
|
4857
4916
|
* For everything else, callers must explicitly call `refreshBaseSystemPrompt()`
|
|
@@ -7299,11 +7358,16 @@ export class AgentSession {
|
|
|
7299
7358
|
|
|
7300
7359
|
async #pruneToolOutputs(): Promise<{ prunedCount: number; tokensSaved: number } | undefined> {
|
|
7301
7360
|
const branchEntries = this.sessionManager.getBranch();
|
|
7361
|
+
const keepBoundaryId = getLatestCompactionEntry(branchEntries)?.firstKeptEntryId;
|
|
7302
7362
|
const result = pruneToolOutputs(
|
|
7303
7363
|
branchEntries,
|
|
7304
7364
|
this.#withPlanProtection({
|
|
7305
7365
|
...DEFAULT_PRUNE_CONFIG,
|
|
7306
7366
|
pruneUseless: this.settings.getGroup("compaction").dropUseless,
|
|
7367
|
+
// Cache-stable boundary: never re-write the warm, already-sent prefix
|
|
7368
|
+
// (deep stale/age victims) or summarized-away entries every turn.
|
|
7369
|
+
keepBoundaryId,
|
|
7370
|
+
cacheWarmSuffixTokens: PRUNE_CACHE_WARM_SUFFIX_TOKENS,
|
|
7307
7371
|
}),
|
|
7308
7372
|
);
|
|
7309
7373
|
if (result.prunedCount === 0) {
|
|
@@ -7331,12 +7395,17 @@ export class AgentSession {
|
|
|
7331
7395
|
const { supersedeReads, dropUseless } = this.settings.getGroup("compaction");
|
|
7332
7396
|
if (!supersedeReads && !dropUseless) return undefined;
|
|
7333
7397
|
const branchEntries = this.sessionManager.getBranch();
|
|
7398
|
+
const keepBoundaryId = getLatestCompactionEntry(branchEntries)?.firstKeptEntryId;
|
|
7334
7399
|
const result = pruneSupersededToolResults(
|
|
7335
7400
|
branchEntries,
|
|
7336
7401
|
this.#withPlanProtection({
|
|
7337
7402
|
supersedeKey: supersedeReads ? readToolSupersedeKey : undefined,
|
|
7338
7403
|
pruneUseless: dropUseless,
|
|
7339
7404
|
protectedTools: [...DEFAULT_PRUNE_CONFIG.protectedTools],
|
|
7405
|
+
// Never re-write summarized-away entries; only flush the whole sent
|
|
7406
|
+
// region once the cache is genuinely cold (idle exceeds the 1h TTL).
|
|
7407
|
+
keepBoundaryId,
|
|
7408
|
+
idleFlushMs: PRUNE_IDLE_FLUSH_MS,
|
|
7340
7409
|
}),
|
|
7341
7410
|
);
|
|
7342
7411
|
if (result.prunedCount === 0) {
|
|
@@ -7420,8 +7489,14 @@ export class AgentSession {
|
|
|
7420
7489
|
return { mode, toolResultsDropped: 0, blocksDropped: 0, imagesDropped: removed, tokensFreed: 0 };
|
|
7421
7490
|
}
|
|
7422
7491
|
|
|
7423
|
-
const
|
|
7424
|
-
const
|
|
7492
|
+
const branchEntries = this.sessionManager.getBranch();
|
|
7493
|
+
const config = this.#withPlanProtection({
|
|
7494
|
+
...(opts.config ?? AGGRESSIVE_SHAKE_CONFIG),
|
|
7495
|
+
// Skip entries summarized away by the latest compaction — shaking them
|
|
7496
|
+
// only churns persisted history with no prompt/cache effect.
|
|
7497
|
+
keepBoundaryId: getLatestCompactionEntry(branchEntries)?.firstKeptEntryId,
|
|
7498
|
+
});
|
|
7499
|
+
const regions = collectShakeRegions(branchEntries, config);
|
|
7425
7500
|
if (regions.length === 0) {
|
|
7426
7501
|
return { mode, toolResultsDropped: 0, blocksDropped: 0, tokensFreed: 0 };
|
|
7427
7502
|
}
|
|
@@ -7598,9 +7673,6 @@ export class AgentSession {
|
|
|
7598
7673
|
convertToLlm,
|
|
7599
7674
|
model: this.model,
|
|
7600
7675
|
shape: snapcompact.resolveShape(this.model, this.settings.get("snapcompact.shape")),
|
|
7601
|
-
// Providers with hard image caps (OpenRouter: 8) silently drop
|
|
7602
|
-
// frames past the cap — keep the archive within budget.
|
|
7603
|
-
maxFrames: snapcompact.providerFrameBudget(this.model?.provider),
|
|
7604
7676
|
});
|
|
7605
7677
|
const ctxWindow = this.model?.contextWindow ?? 0;
|
|
7606
7678
|
const budget =
|
|
@@ -7849,7 +7921,10 @@ export class AgentSession {
|
|
|
7849
7921
|
this.#modelRegistry.resolver(model, this.sessionId),
|
|
7850
7922
|
{
|
|
7851
7923
|
systemPrompt: this.#obfuscateForProvider(this.#baseSystemPrompt),
|
|
7852
|
-
tools: obfuscateProviderTools(
|
|
7924
|
+
tools: obfuscateProviderTools(
|
|
7925
|
+
this.#obfuscator,
|
|
7926
|
+
this.#pruneToolDescriptions ? stripToolDescriptions(this.agent.state.tools) : this.agent.state.tools,
|
|
7927
|
+
),
|
|
7853
7928
|
customInstructions: this.#obfuscateTextForProvider(customInstructions),
|
|
7854
7929
|
convertToLlm: messages => this.#convertToLlmForSideRequest(messages),
|
|
7855
7930
|
initiatorOverride: "agent",
|
|
@@ -9154,14 +9229,15 @@ export class AgentSession {
|
|
|
9154
9229
|
*/
|
|
9155
9230
|
#projectSnapcompactContextTokens(preparation: CompactionPreparation, result: snapcompact.CompactionResult): number {
|
|
9156
9231
|
const archive = snapcompact.getPreservedArchive(result.preserveData);
|
|
9157
|
-
const
|
|
9232
|
+
const blocks = archive ? snapcompact.historyBlocks(archive) : undefined;
|
|
9158
9233
|
const summaryMessage = createCompactionSummaryMessage(
|
|
9159
9234
|
result.summary,
|
|
9160
9235
|
result.tokensBefore,
|
|
9161
9236
|
new Date().toISOString(),
|
|
9162
9237
|
result.shortSummary,
|
|
9163
9238
|
undefined,
|
|
9164
|
-
|
|
9239
|
+
undefined,
|
|
9240
|
+
blocks,
|
|
9165
9241
|
);
|
|
9166
9242
|
let tokens = computeNonMessageTokens(this) + estimateTokens(summaryMessage);
|
|
9167
9243
|
for (const message of preparation.recentMessages) {
|
|
@@ -9389,15 +9465,15 @@ export class AgentSession {
|
|
|
9389
9465
|
let details: unknown;
|
|
9390
9466
|
|
|
9391
9467
|
// Snapcompact runs locally first; if its frame archive plus the kept
|
|
9392
|
-
// history still overflows the model window (frames
|
|
9393
|
-
//
|
|
9394
|
-
// far cheaper — downgrade to context-full and take the
|
|
9468
|
+
// history still overflows the model window (frames default to
|
|
9469
|
+
// MAX_FRAMES_DEFAULT and cost ~FRAME_TOKEN_ESTIMATE each), an LLM
|
|
9470
|
+
// summary is far cheaper — downgrade to context-full and take the
|
|
9471
|
+
// summarizer path.
|
|
9395
9472
|
let snapcompactResult: snapcompact.CompactionResult | undefined;
|
|
9396
9473
|
if (action === "snapcompact" && compactionPrep.kind !== "fromHook") {
|
|
9397
9474
|
snapcompactResult = await snapcompact.compact(preparation, {
|
|
9398
9475
|
convertToLlm,
|
|
9399
9476
|
model: this.model,
|
|
9400
|
-
maxFrames: snapcompact.providerFrameBudget(this.model?.provider),
|
|
9401
9477
|
});
|
|
9402
9478
|
const ctxWindow = this.model?.contextWindow ?? 0;
|
|
9403
9479
|
const budget =
|
|
@@ -12236,6 +12312,7 @@ export class AgentSession {
|
|
|
12236
12312
|
model: this.agent.state.model,
|
|
12237
12313
|
thinkingLevel: this.#thinkingLevel,
|
|
12238
12314
|
tools: this.agent.state.tools,
|
|
12315
|
+
inlineToolDescriptors: this.#pruneToolDescriptions,
|
|
12239
12316
|
});
|
|
12240
12317
|
}
|
|
12241
12318
|
|