@oh-my-pi/pi-coding-agent 15.9.1 → 15.9.5
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 +68 -2
- package/dist/types/cli/classify-install-target.d.ts +5 -1
- package/dist/types/cli/dry-balance-cli.d.ts +104 -0
- package/dist/types/commands/dry-balance.d.ts +31 -0
- package/dist/types/config/model-registry.d.ts +2 -0
- package/dist/types/config/models-config-schema.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +13 -4
- package/dist/types/config/settings.d.ts +11 -0
- package/dist/types/discovery/helpers.d.ts +1 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -3
- package/dist/types/hindsight/bank.d.ts +17 -9
- package/dist/types/hindsight/mental-models.d.ts +1 -1
- package/dist/types/hindsight/state.d.ts +9 -3
- package/dist/types/mcp/manager.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +11 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -1
- package/dist/types/modes/components/error-banner.d.ts +11 -0
- package/dist/types/modes/components/tool-execution.d.ts +15 -0
- package/dist/types/modes/components/transcript-container.d.ts +4 -2
- package/dist/types/modes/components/user-message.d.ts +1 -1
- package/dist/types/modes/image-references.d.ts +17 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -0
- package/dist/types/modes/types.d.ts +7 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/session/auth-storage.d.ts +2 -2
- package/dist/types/session/blob-store.d.ts +12 -11
- package/dist/types/session/session-manager.d.ts +5 -3
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/types.d.ts +2 -0
- package/dist/types/tiny/title-client.d.ts +16 -1
- package/dist/types/tool-discovery/mode.d.ts +8 -0
- package/dist/types/tools/archive-reader.d.ts +5 -1
- package/dist/types/tools/index.d.ts +16 -0
- package/dist/types/tools/path-utils.d.ts +11 -0
- package/dist/types/tui/hyperlink.d.ts +12 -0
- package/dist/types/web/search/render.d.ts +1 -2
- package/package.json +9 -9
- package/src/cli/classify-install-target.ts +31 -5
- package/src/cli/dry-balance-cli.ts +823 -0
- package/src/cli/plugin-cli.ts +45 -0
- package/src/cli/web-search-cli.ts +0 -1
- package/src/cli-commands.ts +1 -0
- package/src/commands/dry-balance.ts +43 -0
- package/src/config/model-registry.ts +60 -4
- package/src/config/models-config-schema.ts +2 -0
- package/src/config/settings-schema.ts +14 -4
- package/src/config/settings.ts +38 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -0
- package/src/discovery/github.ts +37 -1
- package/src/discovery/helpers.ts +3 -1
- package/src/eval/__tests__/agent-bridge.test.ts +72 -0
- package/src/eval/py/tool-bridge.ts +43 -5
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +31 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +245 -25
- package/src/hindsight/backend.ts +184 -35
- package/src/hindsight/bank.ts +32 -22
- package/src/hindsight/mental-models.ts +1 -1
- package/src/hindsight/state.ts +21 -7
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/internal-urls/omp-protocol.ts +8 -2
- package/src/main.ts +7 -1
- package/src/mcp/manager.ts +40 -21
- package/src/modes/components/assistant-message.ts +22 -0
- package/src/modes/components/custom-editor.ts +14 -2
- package/src/modes/components/error-banner.ts +33 -0
- package/src/modes/components/tool-execution.ts +44 -0
- package/src/modes/components/transcript-container.ts +102 -30
- package/src/modes/components/tree-selector.ts +29 -2
- package/src/modes/components/user-message.ts +9 -2
- package/src/modes/controllers/event-controller.ts +42 -3
- package/src/modes/controllers/input-controller.ts +41 -3
- package/src/modes/image-references.ts +111 -0
- package/src/modes/interactive-mode.ts +48 -13
- package/src/modes/setup-wizard/scenes/sign-in.ts +27 -7
- package/src/modes/types.ts +10 -1
- package/src/modes/utils/ui-helpers.ts +23 -2
- package/src/prompts/agents/explore.md +1 -0
- package/src/prompts/agents/librarian.md +1 -0
- package/src/prompts/ci-green-request.md +5 -3
- package/src/prompts/dry-balance-bench.md +8 -0
- package/src/prompts/system/project-prompt.md +1 -0
- package/src/sdk.ts +99 -18
- package/src/session/agent-session.ts +103 -19
- package/src/session/auth-storage.ts +4 -0
- package/src/session/blob-store.ts +96 -9
- package/src/session/session-manager.ts +19 -10
- package/src/system-prompt.ts +4 -0
- package/src/task/executor.ts +6 -2
- package/src/task/index.ts +8 -7
- package/src/task/types.ts +2 -0
- package/src/tiny/title-client.ts +7 -1
- package/src/tool-discovery/mode.ts +24 -0
- package/src/tools/archive-reader.ts +339 -31
- package/src/tools/bash.ts +3 -4
- package/src/tools/fetch.ts +29 -9
- package/src/tools/gh.ts +65 -11
- package/src/tools/index.ts +22 -8
- package/src/tools/job.ts +3 -3
- package/src/tools/memory-reflect.ts +2 -2
- package/src/tools/path-utils.ts +21 -0
- package/src/tools/read.ts +58 -12
- package/src/tools/search-tool-bm25.ts +4 -6
- package/src/tools/search.ts +78 -12
- package/src/tui/hyperlink.ts +42 -7
- package/src/utils/file-mentions.ts +7 -107
- package/src/utils/title-generator.ts +58 -37
- package/src/web/search/index.ts +2 -2
- package/src/web/search/render.ts +20 -52
|
@@ -5,19 +5,90 @@ import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
|
5
5
|
|
|
6
6
|
const BLOB_PREFIX = "blob:sha256:";
|
|
7
7
|
|
|
8
|
+
export interface BlobPutOptions {
|
|
9
|
+
/** Optional file extension for a sidecar hardlink/copy that OS openers can type-detect. */
|
|
10
|
+
extension?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
8
13
|
export interface BlobPutResult {
|
|
9
14
|
hash: string;
|
|
15
|
+
/** Canonical content-addressed path, always `<dir>/<sha256-hex>`. */
|
|
10
16
|
path: string;
|
|
17
|
+
/** Path with the requested extension when supplied, otherwise the canonical path. */
|
|
18
|
+
displayPath: string;
|
|
11
19
|
get ref(): string;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
/**
|
|
15
23
|
* Content-addressed blob store for externalizing large binary data (images) from session JSONL files.
|
|
16
24
|
*
|
|
17
|
-
* Files are stored at `<dir>/<sha256-hex
|
|
18
|
-
*
|
|
19
|
-
*
|
|
25
|
+
* Files are stored canonically at `<dir>/<sha256-hex>`. Callers may also request
|
|
26
|
+
* a typed sidecar path (`<dir>/<sha256-hex>.<ext>`) for `file://` links and OS
|
|
27
|
+
* image viewers; blob refs and reads still address the extensionless hash path.
|
|
28
|
+
* The SHA-256 hash is computed over the raw binary data (not base64).
|
|
29
|
+
* Content-addressing makes writes idempotent and provides automatic deduplication
|
|
30
|
+
* across sessions.
|
|
20
31
|
*/
|
|
32
|
+
|
|
33
|
+
const IMAGE_EXTENSION_BY_MIME: Record<string, string> = {
|
|
34
|
+
"image/png": "png",
|
|
35
|
+
"image/jpeg": "jpg",
|
|
36
|
+
"image/jpg": "jpg",
|
|
37
|
+
"image/gif": "gif",
|
|
38
|
+
"image/webp": "webp",
|
|
39
|
+
"image/svg+xml": "svg",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function normalizeBlobExtension(extension: string | undefined): string | undefined {
|
|
43
|
+
if (!extension) return undefined;
|
|
44
|
+
const normalized = extension.startsWith(".") ? extension.slice(1) : extension;
|
|
45
|
+
if (normalized.length === 0 || normalized.length > 32) return undefined;
|
|
46
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(normalized)) return undefined;
|
|
47
|
+
return normalized.toLowerCase();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function ensureDisplayPath(blobPath: string, displayPath: string, data: Buffer): Promise<void> {
|
|
51
|
+
if (displayPath === blobPath) return;
|
|
52
|
+
try {
|
|
53
|
+
await fsp.link(blobPath, displayPath);
|
|
54
|
+
return;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (typeof err === "object" && err !== null && "code" in err && err.code === "EEXIST") return;
|
|
57
|
+
logger.debug("Blob display hardlink failed; falling back to copy", {
|
|
58
|
+
blobPath,
|
|
59
|
+
displayPath,
|
|
60
|
+
error: err instanceof Error ? err.message : String(err),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
await Bun.write(displayPath, data);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function ensureDisplayPathSync(blobPath: string, displayPath: string, data: Buffer): void {
|
|
67
|
+
if (displayPath === blobPath) return;
|
|
68
|
+
try {
|
|
69
|
+
fs.linkSync(blobPath, displayPath);
|
|
70
|
+
return;
|
|
71
|
+
} catch (err) {
|
|
72
|
+
if (typeof err === "object" && err !== null && "code" in err && err.code === "EEXIST") return;
|
|
73
|
+
logger.debug("Blob display hardlink failed; falling back to copy", {
|
|
74
|
+
blobPath,
|
|
75
|
+
displayPath,
|
|
76
|
+
error: err instanceof Error ? err.message : String(err),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
fs.writeFileSync(displayPath, data);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function blobExtensionForImageMimeType(mimeType: string | undefined): string | undefined {
|
|
83
|
+
if (!mimeType) return undefined;
|
|
84
|
+
const lower = mimeType.toLowerCase();
|
|
85
|
+
const known = IMAGE_EXTENSION_BY_MIME[lower];
|
|
86
|
+
if (known) return known;
|
|
87
|
+
if (!lower.startsWith("image/")) return undefined;
|
|
88
|
+
const subtype = lower.slice("image/".length).split(";")[0]?.split("+")[0];
|
|
89
|
+
return normalizeBlobExtension(subtype);
|
|
90
|
+
}
|
|
91
|
+
|
|
21
92
|
export class BlobStore {
|
|
22
93
|
constructor(readonly dir: string) {}
|
|
23
94
|
|
|
@@ -25,18 +96,22 @@ export class BlobStore {
|
|
|
25
96
|
* Write binary data to the blob store.
|
|
26
97
|
* @returns SHA-256 hex hash of the data
|
|
27
98
|
*/
|
|
28
|
-
async put(data: Buffer): Promise<BlobPutResult> {
|
|
99
|
+
async put(data: Buffer, options?: BlobPutOptions): Promise<BlobPutResult> {
|
|
29
100
|
const hash = new Bun.SHA256().update(data).digest("hex");
|
|
30
101
|
const blobPath = path.join(this.dir, hash);
|
|
102
|
+
const extension = normalizeBlobExtension(options?.extension);
|
|
103
|
+
const displayPath = extension ? `${blobPath}.${extension}` : blobPath;
|
|
31
104
|
const result = {
|
|
32
105
|
hash,
|
|
33
106
|
path: blobPath,
|
|
107
|
+
displayPath,
|
|
34
108
|
get ref() {
|
|
35
109
|
return `${BLOB_PREFIX}${hash}`;
|
|
36
110
|
},
|
|
37
111
|
};
|
|
38
112
|
|
|
39
113
|
await Bun.write(blobPath, data);
|
|
114
|
+
await ensureDisplayPath(blobPath, displayPath, data);
|
|
40
115
|
return result;
|
|
41
116
|
}
|
|
42
117
|
|
|
@@ -45,18 +120,22 @@ export class BlobStore {
|
|
|
45
120
|
* cannot afford the microtask hops of the async version (e.g. OOM-safe session writes).
|
|
46
121
|
* Returns once the bytes are in the kernel page cache.
|
|
47
122
|
*/
|
|
48
|
-
putSync(data: Buffer): BlobPutResult {
|
|
123
|
+
putSync(data: Buffer, options?: BlobPutOptions): BlobPutResult {
|
|
49
124
|
const hash = new Bun.SHA256().update(data).digest("hex");
|
|
50
125
|
const blobPath = path.join(this.dir, hash);
|
|
126
|
+
const extension = normalizeBlobExtension(options?.extension);
|
|
127
|
+
const displayPath = extension ? `${blobPath}.${extension}` : blobPath;
|
|
51
128
|
const result = {
|
|
52
129
|
hash,
|
|
53
130
|
path: blobPath,
|
|
131
|
+
displayPath,
|
|
54
132
|
get ref() {
|
|
55
133
|
return `${BLOB_PREFIX}${hash}`;
|
|
56
134
|
},
|
|
57
135
|
};
|
|
58
136
|
fs.mkdirSync(this.dir, { recursive: true });
|
|
59
137
|
fs.writeFileSync(blobPath, data);
|
|
138
|
+
ensureDisplayPathSync(blobPath, displayPath, data);
|
|
60
139
|
return result;
|
|
61
140
|
}
|
|
62
141
|
|
|
@@ -120,17 +199,25 @@ export function externalizeImageDataUrlSync(blobStore: BlobStore, dataUrl: strin
|
|
|
120
199
|
* Externalize an image's base64 data to the blob store, returning a blob reference.
|
|
121
200
|
* If the data is already a blob reference, returns it unchanged.
|
|
122
201
|
*/
|
|
123
|
-
export async function externalizeImageData(
|
|
202
|
+
export async function externalizeImageData(
|
|
203
|
+
blobStore: BlobStore,
|
|
204
|
+
base64Data: string,
|
|
205
|
+
mimeType?: string,
|
|
206
|
+
): Promise<string> {
|
|
124
207
|
if (isBlobRef(base64Data)) return base64Data;
|
|
125
208
|
const buffer = Buffer.from(base64Data, "base64");
|
|
126
|
-
const { ref } = await blobStore.put(buffer
|
|
209
|
+
const { ref } = await blobStore.put(buffer, {
|
|
210
|
+
extension: blobExtensionForImageMimeType(mimeType),
|
|
211
|
+
});
|
|
127
212
|
return ref;
|
|
128
213
|
}
|
|
129
214
|
|
|
130
215
|
/** Synchronous variant of {@link externalizeImageData}. */
|
|
131
|
-
export function externalizeImageDataSync(blobStore: BlobStore, base64Data: string): string {
|
|
216
|
+
export function externalizeImageDataSync(blobStore: BlobStore, base64Data: string, mimeType?: string): string {
|
|
132
217
|
if (isBlobRef(base64Data)) return base64Data;
|
|
133
|
-
return blobStore.putSync(Buffer.from(base64Data, "base64")
|
|
218
|
+
return blobStore.putSync(Buffer.from(base64Data, "base64"), {
|
|
219
|
+
extension: blobExtensionForImageMimeType(mimeType),
|
|
220
|
+
}).ref;
|
|
134
221
|
}
|
|
135
222
|
|
|
136
223
|
/**
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
} from "@oh-my-pi/pi-utils";
|
|
30
30
|
import { ArtifactManager } from "./artifacts";
|
|
31
31
|
import {
|
|
32
|
+
type BlobPutOptions,
|
|
32
33
|
type BlobPutResult,
|
|
33
34
|
BlobStore,
|
|
34
35
|
externalizeImageData,
|
|
@@ -336,6 +337,7 @@ export type ReadonlySessionManager = Pick<
|
|
|
336
337
|
| "getTree"
|
|
337
338
|
| "getUsageStatistics"
|
|
338
339
|
| "putBlob"
|
|
340
|
+
| "putBlobSync"
|
|
339
341
|
>;
|
|
340
342
|
|
|
341
343
|
function createSessionId(): string {
|
|
@@ -1219,7 +1221,7 @@ async function truncateForPersistence(obj: unknown, blobStore: BlobStore, key?:
|
|
|
1219
1221
|
if (key === TEXT_CONTENT_KEY && isImageBlock(item)) {
|
|
1220
1222
|
if (!isBlobRef(item.data) && item.data.length >= BLOB_EXTERNALIZE_THRESHOLD) {
|
|
1221
1223
|
changed = true;
|
|
1222
|
-
const blobRef = await externalizeImageData(blobStore, item.data);
|
|
1224
|
+
const blobRef = await externalizeImageData(blobStore, item.data, item.mimeType);
|
|
1223
1225
|
return { ...item, data: blobRef };
|
|
1224
1226
|
}
|
|
1225
1227
|
}
|
|
@@ -1313,13 +1315,15 @@ function truncateForPersistenceSync(obj: unknown, blobStore: BlobStore, key?: st
|
|
|
1313
1315
|
const result: unknown[] = new Array(obj.length);
|
|
1314
1316
|
for (let i = 0; i < obj.length; i++) {
|
|
1315
1317
|
const item = obj[i];
|
|
1316
|
-
if (
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1318
|
+
if (
|
|
1319
|
+
key === TEXT_CONTENT_KEY &&
|
|
1320
|
+
isImageBlock(item) &&
|
|
1321
|
+
!isBlobRef(item.data) &&
|
|
1322
|
+
item.data.length >= BLOB_EXTERNALIZE_THRESHOLD
|
|
1323
|
+
) {
|
|
1324
|
+
changed = true;
|
|
1325
|
+
result[i] = { ...item, data: externalizeImageDataSync(blobStore, item.data, item.mimeType) };
|
|
1326
|
+
continue;
|
|
1323
1327
|
}
|
|
1324
1328
|
const newItem = truncateForPersistenceSync(item, blobStore, key);
|
|
1325
1329
|
if (newItem !== item) changed = true;
|
|
@@ -1978,8 +1982,13 @@ export class SessionManager {
|
|
|
1978
1982
|
}
|
|
1979
1983
|
|
|
1980
1984
|
/** Puts a binary blob into the blob store and returns the blob reference */
|
|
1981
|
-
async putBlob(data: Buffer): Promise<BlobPutResult> {
|
|
1982
|
-
return this.#blobStore.put(data);
|
|
1985
|
+
async putBlob(data: Buffer, options?: BlobPutOptions): Promise<BlobPutResult> {
|
|
1986
|
+
return this.#blobStore.put(data, options);
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
/** Synchronous variant of {@link putBlob} for rebuild-only render paths. */
|
|
1990
|
+
putBlobSync(data: Buffer, options?: BlobPutOptions): BlobPutResult {
|
|
1991
|
+
return this.#blobStore.putSync(data, options);
|
|
1983
1992
|
}
|
|
1984
1993
|
|
|
1985
1994
|
captureState(): SessionManagerStateSnapshot {
|
package/src/system-prompt.ts
CHANGED
|
@@ -363,6 +363,8 @@ export interface BuildSystemPromptOptions {
|
|
|
363
363
|
workspaceTree?: WorkspaceTree | Promise<WorkspaceTree>;
|
|
364
364
|
/** Whether the local memory://root summary is active. */
|
|
365
365
|
memoryRootEnabled?: boolean;
|
|
366
|
+
/** Active model identifier (e.g. "anthropic/claude-opus-4") surfaced to the agent. */
|
|
367
|
+
model?: string;
|
|
366
368
|
}
|
|
367
369
|
|
|
368
370
|
/** Result of building provider-facing system prompt messages. */
|
|
@@ -396,6 +398,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
396
398
|
secretsEnabled = false,
|
|
397
399
|
workspaceTree: providedWorkspaceTree,
|
|
398
400
|
memoryRootEnabled = false,
|
|
401
|
+
model,
|
|
399
402
|
} = options;
|
|
400
403
|
const resolvedCwd = cwd ?? getProjectDir();
|
|
401
404
|
|
|
@@ -566,6 +569,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
566
569
|
date,
|
|
567
570
|
dateTime,
|
|
568
571
|
cwd: promptCwd,
|
|
572
|
+
model: model ?? "",
|
|
569
573
|
intentTracing: !!intentField,
|
|
570
574
|
intentField: intentField ?? "",
|
|
571
575
|
mcpDiscoveryMode,
|
package/src/task/executor.ts
CHANGED
|
@@ -531,7 +531,7 @@ function createMCPProxyTools(mcpManager: MCPManager): CustomTool[] {
|
|
|
531
531
|
});
|
|
532
532
|
}
|
|
533
533
|
|
|
534
|
-
function createSubagentSettings(baseSettings: Settings): Settings {
|
|
534
|
+
function createSubagentSettings(baseSettings: Settings, overrides?: Partial<Record<SettingPath, unknown>>): Settings {
|
|
535
535
|
const snapshot: Partial<Record<SettingPath, unknown>> = {};
|
|
536
536
|
for (const key of Object.keys(SETTINGS_SCHEMA) as SettingPath[]) {
|
|
537
537
|
snapshot[key] = baseSettings.get(key);
|
|
@@ -545,6 +545,7 @@ function createSubagentSettings(baseSettings: Settings): Settings {
|
|
|
545
545
|
// the parent task approval is the authorization boundary. Use yolo mode
|
|
546
546
|
// to preserve unattended subagent execution. User `tools.approval` policies still apply.
|
|
547
547
|
"tools.approvalMode": "yolo",
|
|
548
|
+
...overrides,
|
|
548
549
|
});
|
|
549
550
|
}
|
|
550
551
|
|
|
@@ -619,7 +620,10 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
619
620
|
}
|
|
620
621
|
|
|
621
622
|
const settings = options.settings ?? Settings.isolated();
|
|
622
|
-
const subagentSettings = createSubagentSettings(
|
|
623
|
+
const subagentSettings = createSubagentSettings(
|
|
624
|
+
settings,
|
|
625
|
+
agent.readSummarize === false ? { "read.summarize.enabled": false } : undefined,
|
|
626
|
+
);
|
|
623
627
|
const maxRecursionDepth = settings.get("task.maxRecursionDepth") ?? 2;
|
|
624
628
|
const maxRuntimeMs = Math.max(0, Math.trunc(Number(settings.get("task.maxRuntimeMs") ?? 0) || 0));
|
|
625
629
|
const parentDepth = options.taskDepth ?? 0;
|
package/src/task/index.ts
CHANGED
|
@@ -17,9 +17,8 @@ import * as os from "node:os";
|
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
19
19
|
import type { Usage } from "@oh-my-pi/pi-ai";
|
|
20
|
-
import { $env, prompt, Snowflake } from "@oh-my-pi/pi-utils";
|
|
20
|
+
import { $env, logger, prompt, Snowflake } from "@oh-my-pi/pi-utils";
|
|
21
21
|
import type { ToolSession } from "..";
|
|
22
|
-
import { AsyncJobManager } from "../async";
|
|
23
22
|
import { resolveAgentModelPatterns } from "../config/model-resolver";
|
|
24
23
|
import { MCPManager } from "../mcp/manager";
|
|
25
24
|
import type { Theme } from "../modes/theme/theme";
|
|
@@ -343,12 +342,14 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
343
342
|
return this.#executeSync(_toolCallId, params, signal, onUpdate);
|
|
344
343
|
}
|
|
345
344
|
|
|
346
|
-
const manager =
|
|
345
|
+
const manager = this.session.asyncJobManager;
|
|
347
346
|
if (!manager) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
347
|
+
// Async was requested but no manager is registered (e.g. an
|
|
348
|
+
// orphaned session whose host never wired one up). Falling back
|
|
349
|
+
// to the sync path keeps the tool usable; only background/job-poll
|
|
350
|
+
// semantics are lost.
|
|
351
|
+
logger.warn("task: async.enabled but no AsyncJobManager registered; falling back to sync execution");
|
|
352
|
+
return this.#executeSync(_toolCallId, params, signal, onUpdate);
|
|
352
353
|
}
|
|
353
354
|
|
|
354
355
|
const taskItems = params.tasks ?? [];
|
package/src/task/types.ts
CHANGED
|
@@ -174,6 +174,8 @@ export interface AgentDefinition {
|
|
|
174
174
|
output?: unknown;
|
|
175
175
|
blocking?: boolean;
|
|
176
176
|
autoloadSkills?: string[];
|
|
177
|
+
/** When `false`, the agent's `read` tool returns verbatim file content instead of structural summaries. */
|
|
178
|
+
readSummarize?: boolean;
|
|
177
179
|
source: AgentSource;
|
|
178
180
|
filePath?: string;
|
|
179
181
|
}
|
package/src/tiny/title-client.ts
CHANGED
|
@@ -261,6 +261,11 @@ export class TinyTitleClient {
|
|
|
261
261
|
#pending = new Map<string, PendingRequest>();
|
|
262
262
|
#progressListeners = new Set<(event: TinyTitleProgressEvent) => void>();
|
|
263
263
|
#nextRequestId = 0;
|
|
264
|
+
#spawnWorker: () => WorkerHandle;
|
|
265
|
+
|
|
266
|
+
constructor(spawnWorker: () => WorkerHandle = spawnTinyTitleWorker) {
|
|
267
|
+
this.#spawnWorker = spawnWorker;
|
|
268
|
+
}
|
|
264
269
|
|
|
265
270
|
onProgress(listener: (event: TinyTitleProgressEvent) => void): () => void {
|
|
266
271
|
this.#progressListeners.add(listener);
|
|
@@ -392,7 +397,7 @@ export class TinyTitleClient {
|
|
|
392
397
|
|
|
393
398
|
#ensureWorker(): WorkerHandle {
|
|
394
399
|
if (this.#worker) return this.#worker;
|
|
395
|
-
const worker =
|
|
400
|
+
const worker = this.#spawnWorker();
|
|
396
401
|
this.#worker = worker;
|
|
397
402
|
this.#unsubscribeMessage = worker.onMessage(message => this.#handleMessage(message));
|
|
398
403
|
this.#unsubscribeError = worker.onError(error => this.#handleWorkerError(error));
|
|
@@ -429,6 +434,7 @@ export class TinyTitleClient {
|
|
|
429
434
|
this.#emitProgress({ modelKey: pending.modelKey, status: "error" });
|
|
430
435
|
if (pending.kind === "generate" || pending.kind === "complete") pending.resolve(null);
|
|
431
436
|
else pending.resolve(false);
|
|
437
|
+
void this.terminate();
|
|
432
438
|
}
|
|
433
439
|
|
|
434
440
|
#emitProgress(event: TinyTitleProgressEvent): void {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Settings } from "../config/settings";
|
|
2
|
+
import type { SettingValue } from "../config/settings-schema";
|
|
3
|
+
|
|
4
|
+
export const TOOL_DISCOVERY_AUTO_THRESHOLD = 40;
|
|
5
|
+
export const TOOL_DISCOVERY_SEARCH_TOOL_NAME = "search_tool_bm25";
|
|
6
|
+
|
|
7
|
+
export type ToolDiscoveryModeSetting = SettingValue<"tools.discoveryMode">;
|
|
8
|
+
export type EffectiveToolDiscoveryMode = Exclude<ToolDiscoveryModeSetting, "auto">;
|
|
9
|
+
|
|
10
|
+
export function countToolsForAutoDiscovery(toolNames: Iterable<string>): number {
|
|
11
|
+
let count = 0;
|
|
12
|
+
for (const name of toolNames) {
|
|
13
|
+
if (name !== TOOL_DISCOVERY_SEARCH_TOOL_NAME) count++;
|
|
14
|
+
}
|
|
15
|
+
return count;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveEffectiveToolDiscoveryMode(settings: Settings, toolCount: number): EffectiveToolDiscoveryMode {
|
|
19
|
+
const configuredMode = settings.get("tools.discoveryMode");
|
|
20
|
+
if (configuredMode === "all" || configuredMode === "mcp-only") return configuredMode;
|
|
21
|
+
if (settings.get("mcp.discoveryMode")) return "mcp-only";
|
|
22
|
+
if (configuredMode === "auto" && toolCount > TOOL_DISCOVERY_AUTO_THRESHOLD) return "mcp-only";
|
|
23
|
+
return "off";
|
|
24
|
+
}
|