@oh-my-pi/pi-coding-agent 13.12.3 → 13.12.4
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 +10 -0
- package/package.json +7 -7
- package/src/extensibility/custom-tools/types.ts +3 -0
- package/src/sdk.ts +22 -28
- package/src/session/agent-session.ts +3 -3
- package/src/tools/output-meta.ts +24 -25
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [13.12.4] - 2026-03-15
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Exposed `settings` instance in `CustomToolContext` for session-specific configuration access
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Improved artifact spill configuration to use session settings with schema defaults as fallback
|
|
13
|
+
- Refactored type annotations for better type safety in tool result handling
|
|
14
|
+
|
|
5
15
|
## [13.12.2] - 2026-03-15
|
|
6
16
|
|
|
7
17
|
### Added
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "13.12.
|
|
4
|
+
"version": "13.12.4",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@mozilla/readability": "^0.6",
|
|
44
|
-
"@oh-my-pi/omp-stats": "13.12.
|
|
45
|
-
"@oh-my-pi/pi-agent-core": "13.12.
|
|
46
|
-
"@oh-my-pi/pi-ai": "13.12.
|
|
47
|
-
"@oh-my-pi/pi-natives": "13.12.
|
|
48
|
-
"@oh-my-pi/pi-tui": "13.12.
|
|
49
|
-
"@oh-my-pi/pi-utils": "13.12.
|
|
44
|
+
"@oh-my-pi/omp-stats": "13.12.4",
|
|
45
|
+
"@oh-my-pi/pi-agent-core": "13.12.4",
|
|
46
|
+
"@oh-my-pi/pi-ai": "13.12.4",
|
|
47
|
+
"@oh-my-pi/pi-natives": "13.12.4",
|
|
48
|
+
"@oh-my-pi/pi-tui": "13.12.4",
|
|
49
|
+
"@oh-my-pi/pi-utils": "13.12.4",
|
|
50
50
|
"@sinclair/typebox": "^0.34",
|
|
51
51
|
"@xterm/headless": "^6.0",
|
|
52
52
|
"ajv": "^8.18",
|
|
@@ -10,6 +10,7 @@ import type { Component } from "@oh-my-pi/pi-tui";
|
|
|
10
10
|
import type { Static, TSchema } from "@sinclair/typebox";
|
|
11
11
|
import type { Rule } from "../../capability/rule";
|
|
12
12
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
13
|
+
import type { Settings } from "../../config/settings";
|
|
13
14
|
import type { ExecOptions, ExecResult } from "../../exec/exec";
|
|
14
15
|
import type { HookUIContext } from "../../extensibility/hooks/types";
|
|
15
16
|
import type { Theme } from "../../modes/theme/theme";
|
|
@@ -76,6 +77,8 @@ export interface CustomToolContext {
|
|
|
76
77
|
hasQueuedMessages(): boolean;
|
|
77
78
|
/** Abort the current agent operation (fire-and-forget, does not wait) */
|
|
78
79
|
abort(): void;
|
|
80
|
+
/** Settings instance for the current session. Prefer over the global singleton. */
|
|
81
|
+
settings?: Settings;
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
/** Session event passed to onSession callback */
|
package/src/sdk.ts
CHANGED
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
loadProjectContextFiles as loadContextFilesInternal,
|
|
80
80
|
} from "./system-prompt";
|
|
81
81
|
import { AgentOutputManager } from "./task/output-manager";
|
|
82
|
-
import { resolveThinkingLevelForModel, toReasoningEffort } from "./thinking";
|
|
82
|
+
import { parseThinkingLevel, resolveThinkingLevelForModel, toReasoningEffort } from "./thinking";
|
|
83
83
|
import {
|
|
84
84
|
BashTool,
|
|
85
85
|
BUILTIN_TOOLS,
|
|
@@ -626,7 +626,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
626
626
|
if (!options.modelRegistry) {
|
|
627
627
|
modelRegistry.refreshInBackground();
|
|
628
628
|
}
|
|
629
|
-
const skillsSettings = settings.getGroup("skills")
|
|
629
|
+
const skillsSettings = settings.getGroup("skills");
|
|
630
630
|
const disabledExtensionIds = settings.get("disabledExtensions") ?? [];
|
|
631
631
|
const discoveredSkillsPromise =
|
|
632
632
|
options.skills === undefined
|
|
@@ -711,7 +711,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
711
711
|
|
|
712
712
|
// If session has data and includes a thinking entry, restore it
|
|
713
713
|
if (thinkingLevel === undefined && hasExistingSession && hasThinkingEntry) {
|
|
714
|
-
thinkingLevel = existingSession.thinkingLevel
|
|
714
|
+
thinkingLevel = parseThinkingLevel(existingSession.thinkingLevel);
|
|
715
715
|
}
|
|
716
716
|
|
|
717
717
|
if (thinkingLevel === undefined && !hasExplicitModel && !hasThinkingEntry && defaultRoleSpec.explicitThinkingLevel) {
|
|
@@ -935,7 +935,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
935
935
|
// Always filter Exa - we have native integration
|
|
936
936
|
filterExa: true,
|
|
937
937
|
// Filter browser MCP servers when builtin browser tool is active
|
|
938
|
-
filterBrowser:
|
|
938
|
+
filterBrowser: settings.get("browser.enabled") ?? false,
|
|
939
939
|
cacheStorage: settings.getStorage(),
|
|
940
940
|
authStorage,
|
|
941
941
|
}),
|
|
@@ -1005,10 +1005,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1005
1005
|
extensionsResult = options.preloadedExtensions;
|
|
1006
1006
|
} else {
|
|
1007
1007
|
// Merge CLI extension paths with settings extension paths
|
|
1008
|
-
const configuredPaths = [
|
|
1009
|
-
...(options.additionalExtensionPaths ?? []),
|
|
1010
|
-
...((settings.get("extensions") as string[]) ?? []),
|
|
1011
|
-
];
|
|
1008
|
+
const configuredPaths = [...(options.additionalExtensionPaths ?? []), ...(settings.get("extensions") ?? [])];
|
|
1012
1009
|
const disabledExtensionIds = settings.get("disabledExtensions") ?? [];
|
|
1013
1010
|
extensionsResult = await logger.timeAsync(
|
|
1014
1011
|
"discoverAndLoadExtensions",
|
|
@@ -1118,11 +1115,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1118
1115
|
abort: () => {
|
|
1119
1116
|
session.abort();
|
|
1120
1117
|
},
|
|
1118
|
+
settings,
|
|
1121
1119
|
});
|
|
1122
1120
|
const toolContextStore = new ToolContextStore(getSessionContext);
|
|
1123
1121
|
|
|
1124
1122
|
const registeredTools = extensionRunner?.getAllRegisteredTools() ?? [];
|
|
1125
|
-
let wrappedExtensionTools:
|
|
1123
|
+
let wrappedExtensionTools: Tool[];
|
|
1126
1124
|
|
|
1127
1125
|
if (extensionRunner) {
|
|
1128
1126
|
// With extension runner: convert CustomTools to ToolDefinitions and wrap all together
|
|
@@ -1144,16 +1142,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1144
1142
|
isIdle: () => !session?.isStreaming,
|
|
1145
1143
|
hasQueuedMessages: () => (session?.queuedMessageCount ?? 0) > 0,
|
|
1146
1144
|
abort: () => session?.abort(),
|
|
1145
|
+
settings,
|
|
1147
1146
|
});
|
|
1148
1147
|
wrappedExtensionTools = (options.customTools ?? [])
|
|
1149
1148
|
.filter(isCustomTool)
|
|
1150
|
-
.map(tool => CustomToolAdapter.wrap(tool, customToolContext)
|
|
1149
|
+
.map(tool => CustomToolAdapter.wrap(tool, customToolContext));
|
|
1151
1150
|
}
|
|
1152
1151
|
|
|
1153
1152
|
// All built-in tools are active (conditional tools like git/ask return null from factory if disabled)
|
|
1154
|
-
const toolRegistry = new Map<string,
|
|
1153
|
+
const toolRegistry = new Map<string, Tool>();
|
|
1155
1154
|
for (const tool of builtinTools) {
|
|
1156
|
-
toolRegistry.set(tool.name, tool
|
|
1155
|
+
toolRegistry.set(tool.name, tool);
|
|
1157
1156
|
}
|
|
1158
1157
|
for (const tool of wrappedExtensionTools) {
|
|
1159
1158
|
toolRegistry.set(tool.name, tool);
|
|
@@ -1173,7 +1172,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1173
1172
|
} else if (!toolRegistry.has("resolve")) {
|
|
1174
1173
|
const resolveTool = await logger.timeAsync("createTools:resolve:session", HIDDEN_TOOLS.resolve, toolSession);
|
|
1175
1174
|
if (resolveTool) {
|
|
1176
|
-
toolRegistry.set(resolveTool.name, wrapToolWithMetaNotice(resolveTool)
|
|
1175
|
+
toolRegistry.set(resolveTool.name, wrapToolWithMetaNotice(resolveTool));
|
|
1177
1176
|
}
|
|
1178
1177
|
}
|
|
1179
1178
|
|
|
@@ -1218,7 +1217,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1218
1217
|
tools,
|
|
1219
1218
|
toolNames,
|
|
1220
1219
|
rules: rulebookRules,
|
|
1221
|
-
skillsSettings: settings.getGroup("skills")
|
|
1220
|
+
skillsSettings: settings.getGroup("skills"),
|
|
1222
1221
|
appendSystemPrompt: appendPrompt,
|
|
1223
1222
|
repeatToolDescriptions,
|
|
1224
1223
|
eagerTasks,
|
|
@@ -1236,7 +1235,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1236
1235
|
tools,
|
|
1237
1236
|
toolNames,
|
|
1238
1237
|
rules: rulebookRules,
|
|
1239
|
-
skillsSettings: settings.getGroup("skills")
|
|
1238
|
+
skillsSettings: settings.getGroup("skills"),
|
|
1240
1239
|
customPrompt: options.systemPrompt,
|
|
1241
1240
|
appendSystemPrompt: appendPrompt,
|
|
1242
1241
|
repeatToolDescriptions,
|
|
@@ -1297,17 +1296,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1297
1296
|
if (hasImages) {
|
|
1298
1297
|
const filteredContent = content
|
|
1299
1298
|
.map(c => (c.type === "image" ? { type: "text" as const, text: "Image reading is disabled." } : c))
|
|
1300
|
-
.filter(
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
i > 0 &&
|
|
1307
|
-
arr[i - 1].type === "text" &&
|
|
1308
|
-
(arr[i - 1] as { type: "text"; text: string }).text === "Image reading is disabled."
|
|
1309
|
-
),
|
|
1310
|
-
);
|
|
1299
|
+
.filter((c, i, arr) => {
|
|
1300
|
+
// Dedupe consecutive "Image reading is disabled." texts
|
|
1301
|
+
if (!(c.type === "text" && c.text === "Image reading is disabled." && i > 0)) return true;
|
|
1302
|
+
const prev = arr[i - 1];
|
|
1303
|
+
return !(prev.type === "text" && prev.text === "Image reading is disabled.");
|
|
1304
|
+
});
|
|
1311
1305
|
return { ...msg, content: filteredContent };
|
|
1312
1306
|
}
|
|
1313
1307
|
}
|
|
@@ -1439,7 +1433,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1439
1433
|
customCommands: customCommandsResult.commands,
|
|
1440
1434
|
skills,
|
|
1441
1435
|
skillWarnings,
|
|
1442
|
-
skillsSettings: settings.getGroup("skills")
|
|
1436
|
+
skillsSettings: settings.getGroup("skills"),
|
|
1443
1437
|
modelRegistry,
|
|
1444
1438
|
toolRegistry,
|
|
1445
1439
|
transformContext,
|
|
@@ -1514,7 +1508,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1514
1508
|
mcpManager.setOnResourcesChanged((serverName, uri) => {
|
|
1515
1509
|
logger.debug("MCP resources changed", { path: `mcp:${serverName}`, uri });
|
|
1516
1510
|
if (!settings.get("mcp.notifications")) return;
|
|
1517
|
-
const debounceMs =
|
|
1511
|
+
const debounceMs = settings.get("mcp.notificationDebounceMs");
|
|
1518
1512
|
const key = `${serverName}:${uri}`;
|
|
1519
1513
|
const existing = notificationDebounceTimers.get(key);
|
|
1520
1514
|
if (existing) clearTimeout(existing);
|
|
@@ -191,7 +191,7 @@ export interface AgentSessionConfig {
|
|
|
191
191
|
skillWarnings?: SkillWarning[];
|
|
192
192
|
/** Custom commands (TypeScript slash commands) */
|
|
193
193
|
customCommands?: LoadedCustomCommand[];
|
|
194
|
-
skillsSettings?:
|
|
194
|
+
skillsSettings?: SkillsSettings;
|
|
195
195
|
/** Model registry for API key resolution and model discovery */
|
|
196
196
|
modelRegistry: ModelRegistry;
|
|
197
197
|
/** Tool registry for LSP and settings */
|
|
@@ -383,7 +383,7 @@ export class AgentSession {
|
|
|
383
383
|
/** MCP prompt commands (updated dynamically when prompts are loaded) */
|
|
384
384
|
#mcpPromptCommands: LoadedCustomCommand[] = [];
|
|
385
385
|
|
|
386
|
-
#skillsSettings:
|
|
386
|
+
#skillsSettings: SkillsSettings | undefined;
|
|
387
387
|
|
|
388
388
|
// Model registry for API key resolution
|
|
389
389
|
#modelRegistry: ModelRegistry;
|
|
@@ -2518,7 +2518,7 @@ export class AgentSession {
|
|
|
2518
2518
|
return undefined;
|
|
2519
2519
|
}
|
|
2520
2520
|
|
|
2521
|
-
get skillsSettings():
|
|
2521
|
+
get skillsSettings(): SkillsSettings | undefined {
|
|
2522
2522
|
return this.#skillsSettings;
|
|
2523
2523
|
}
|
|
2524
2524
|
|
package/src/tools/output-meta.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
AgentToolUpdateCallback,
|
|
13
13
|
} from "@oh-my-pi/pi-agent-core";
|
|
14
14
|
import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
15
|
-
import {
|
|
15
|
+
import { getDefault, type Settings } from "../config/settings";
|
|
16
16
|
import { formatGroupedDiagnosticMessages } from "../lsp/utils";
|
|
17
17
|
import type { Theme } from "../modes/theme/theme";
|
|
18
18
|
import { type OutputSummary, type TruncationResult, truncateTail } from "../session/streaming-output";
|
|
@@ -415,16 +415,17 @@ export function formatStyledTruncationWarning(meta: OutputMeta | undefined, them
|
|
|
415
415
|
* Append output notice to tool result content if meta is present.
|
|
416
416
|
*/
|
|
417
417
|
function appendOutputNotice(
|
|
418
|
-
content:
|
|
418
|
+
content: (TextContent | ImageContent)[],
|
|
419
419
|
meta: OutputMeta | undefined,
|
|
420
|
-
):
|
|
420
|
+
): (TextContent | ImageContent)[] {
|
|
421
421
|
const notice = formatOutputNotice(meta);
|
|
422
422
|
if (!notice) return content;
|
|
423
423
|
|
|
424
424
|
const result = [...content];
|
|
425
425
|
for (let i = result.length - 1; i >= 0; i--) {
|
|
426
|
-
|
|
427
|
-
|
|
426
|
+
const item = result[i];
|
|
427
|
+
if (item.type === "text") {
|
|
428
|
+
result[i] = { ...item, text: item.text + notice };
|
|
428
429
|
return result;
|
|
429
430
|
}
|
|
430
431
|
}
|
|
@@ -439,19 +440,16 @@ const kUnwrappedExecute = Symbol("OutputMeta.UnwrappedExecute");
|
|
|
439
440
|
// Centralized artifact spill for large tool results
|
|
440
441
|
// =============================================================================
|
|
441
442
|
|
|
442
|
-
/**
|
|
443
|
-
function
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
/** When spilling, keep at most this many lines of tail. */
|
|
453
|
-
function getArtifactTailLines(): number {
|
|
454
|
-
return settings.get("tools.artifactTailLines");
|
|
443
|
+
/** Resolved artifact spill config sourced from the session settings (or schema defaults). */
|
|
444
|
+
function getSpillConfig(s: Settings | undefined) {
|
|
445
|
+
const get = <P extends "tools.artifactSpillThreshold" | "tools.artifactTailBytes" | "tools.artifactTailLines">(
|
|
446
|
+
path: P,
|
|
447
|
+
) => s?.get(path) ?? getDefault(path);
|
|
448
|
+
return {
|
|
449
|
+
threshold: get("tools.artifactSpillThreshold") * 1024,
|
|
450
|
+
tailBytes: get("tools.artifactTailBytes") * 1024,
|
|
451
|
+
tailLines: get("tools.artifactTailLines"),
|
|
452
|
+
};
|
|
455
453
|
}
|
|
456
454
|
|
|
457
455
|
/**
|
|
@@ -467,9 +465,10 @@ async function spillLargeResultToArtifact(
|
|
|
467
465
|
): Promise<AgentToolResult> {
|
|
468
466
|
const sessionManager = context?.sessionManager;
|
|
469
467
|
if (!sessionManager) return result;
|
|
468
|
+
const { threshold, tailBytes, tailLines } = getSpillConfig(context?.settings);
|
|
470
469
|
|
|
471
470
|
// Skip if tool already saved an artifact
|
|
472
|
-
const existingMeta =
|
|
471
|
+
const existingMeta: OutputMeta | undefined = result.details?.meta;
|
|
473
472
|
if (existingMeta?.truncation?.artifactId) return result;
|
|
474
473
|
|
|
475
474
|
// Measure total text content
|
|
@@ -483,7 +482,7 @@ async function spillLargeResultToArtifact(
|
|
|
483
482
|
|
|
484
483
|
const fullText = textParts.length === 1 ? textParts[0] : textParts.join("\n");
|
|
485
484
|
const totalBytes = Buffer.byteLength(fullText, "utf-8");
|
|
486
|
-
if (totalBytes <=
|
|
485
|
+
if (totalBytes <= threshold) return result;
|
|
487
486
|
|
|
488
487
|
// Save full output as artifact
|
|
489
488
|
const artifactId = await sessionManager.saveArtifact(fullText, toolName);
|
|
@@ -491,8 +490,8 @@ async function spillLargeResultToArtifact(
|
|
|
491
490
|
|
|
492
491
|
// Truncate to tail
|
|
493
492
|
const truncated = truncateTail(fullText, {
|
|
494
|
-
maxBytes:
|
|
495
|
-
maxLines:
|
|
493
|
+
maxBytes: tailBytes,
|
|
494
|
+
maxLines: tailLines,
|
|
496
495
|
});
|
|
497
496
|
|
|
498
497
|
// Replace text blocks with single tail-truncated block, keep images
|
|
@@ -515,7 +514,7 @@ async function spillLargeResultToArtifact(
|
|
|
515
514
|
totalBytes: truncated.totalBytes,
|
|
516
515
|
outputLines,
|
|
517
516
|
outputBytes,
|
|
518
|
-
maxBytes:
|
|
517
|
+
maxBytes: tailBytes,
|
|
519
518
|
shownRange: { start: shownStart, end: truncated.totalLines },
|
|
520
519
|
artifactId,
|
|
521
520
|
};
|
|
@@ -547,11 +546,11 @@ async function wrappedExecute(
|
|
|
547
546
|
result = await spillLargeResultToArtifact(result, this.name, context);
|
|
548
547
|
|
|
549
548
|
// Append notices from meta
|
|
550
|
-
const meta =
|
|
549
|
+
const meta: OutputMeta | undefined = result.details?.meta;
|
|
551
550
|
if (meta) {
|
|
552
551
|
return {
|
|
553
552
|
...result,
|
|
554
|
-
content: appendOutputNotice(result.content, meta)
|
|
553
|
+
content: appendOutputNotice(result.content, meta),
|
|
555
554
|
};
|
|
556
555
|
}
|
|
557
556
|
return result;
|