@oh-my-pi/pi-coding-agent 14.9.1 → 14.9.2
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 +15 -0
- package/package.json +7 -7
- package/src/extensibility/extensions/runner.ts +173 -177
- package/src/hashline/apply.ts +8 -24
- package/src/hashline/execute.ts +0 -1
- package/src/hashline/grammar.lark +1 -5
- package/src/hashline/parser.ts +1 -40
- package/src/hashline/types.ts +1 -2
- package/src/mcp/transports/http.ts +49 -47
- package/src/prompts/tools/hashline.md +1 -4
- package/src/sdk.ts +12 -22
- package/src/system-prompt.ts +30 -95
- package/src/task/executor.ts +6 -4
- package/src/task/index.ts +0 -2
- package/src/task/render.ts +4 -2
- package/src/tools/index.ts +0 -3
- package/src/tools/read.ts +2 -22
- package/src/workspace-tree.ts +210 -410
|
@@ -245,11 +245,7 @@ export class HttpTransport implements MCPTransport {
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
response: Response,
|
|
250
|
-
expectedId: string | number,
|
|
251
|
-
options?: MCPRequestOptions,
|
|
252
|
-
): Promise<T> {
|
|
248
|
+
#parseSSEResponse<T>(response: Response, expectedId: string | number, options?: MCPRequestOptions): Promise<T> {
|
|
253
249
|
if (!response.body) {
|
|
254
250
|
throw new Error("No response body");
|
|
255
251
|
}
|
|
@@ -261,54 +257,60 @@ export class HttpTransport implements MCPTransport {
|
|
|
261
257
|
? AbortSignal.any([options.signal, abortController.signal])
|
|
262
258
|
: abortController.signal;
|
|
263
259
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
|
|
260
|
+
const { promise, resolve, reject } = Promise.withResolvers<T>();
|
|
261
|
+
let captured = false;
|
|
262
|
+
|
|
263
|
+
// Drain the SSE stream from a single iterator. We resolve the deferred
|
|
264
|
+
// promise as soon as the matching response arrives, then keep iterating
|
|
265
|
+
// in the background to pick up piggybacked notifications/requests.
|
|
266
|
+
// Re-reading `response.body` after `for await` breaks would lock the
|
|
267
|
+
// stream a second time and surface as "ReadableStream already has a
|
|
268
|
+
// controller", so we must not exit the loop early.
|
|
269
|
+
const drain = async (): Promise<void> => {
|
|
270
|
+
try {
|
|
271
|
+
for await (const raw of readSseJson<JsonRpcMessage | JsonRpcMessage[]>(response.body!, operationSignal)) {
|
|
272
|
+
const messages = Array.isArray(raw) ? raw : [raw];
|
|
273
|
+
for (const message of messages) {
|
|
274
|
+
if (
|
|
275
|
+
!captured &&
|
|
276
|
+
"id" in message &&
|
|
277
|
+
message.id === expectedId &&
|
|
278
|
+
("result" in message || "error" in message)
|
|
279
|
+
) {
|
|
280
|
+
captured = true;
|
|
281
|
+
clearTimeout(timeoutId);
|
|
282
|
+
if (message.error) {
|
|
283
|
+
reject(new Error(`MCP error ${message.error.code}: ${message.error.message}`));
|
|
284
|
+
} else {
|
|
285
|
+
resolve(message.result as T);
|
|
286
|
+
}
|
|
287
|
+
continue;
|
|
280
288
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
continue;
|
|
289
|
+
if (!this.#connected) continue;
|
|
290
|
+
this.#dispatchSSEMessage(message);
|
|
284
291
|
}
|
|
285
|
-
this.#dispatchSSEMessage(message);
|
|
286
292
|
}
|
|
287
|
-
if (captured)
|
|
288
|
-
|
|
289
|
-
if (!captured) {
|
|
290
|
-
throw new Error(`No response received for request ID ${expectedId}`);
|
|
291
|
-
}
|
|
292
|
-
// Reader released after break — safe to start a background drain
|
|
293
|
-
// for piggybacked notifications/requests on the same stream.
|
|
294
|
-
this.#drainSSEBackground(response.body, operationSignal);
|
|
295
|
-
return result as T;
|
|
296
|
-
} catch (error) {
|
|
297
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
298
|
-
if (options?.signal?.aborted) {
|
|
299
|
-
throw error;
|
|
293
|
+
if (!captured) {
|
|
294
|
+
reject(new Error(`No response received for request ID ${expectedId}`));
|
|
300
295
|
}
|
|
301
|
-
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (captured) return;
|
|
298
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
299
|
+
if (options?.signal?.aborted) {
|
|
300
|
+
reject(error);
|
|
301
|
+
} else {
|
|
302
|
+
reject(new Error(`SSE response timeout after ${timeout}ms`));
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
reject(error as Error);
|
|
306
|
+
}
|
|
307
|
+
} finally {
|
|
308
|
+
clearTimeout(timeoutId);
|
|
302
309
|
}
|
|
303
|
-
|
|
304
|
-
} finally {
|
|
305
|
-
clearTimeout(timeoutId);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
310
|
+
};
|
|
308
311
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
void this.#readSSEStream(body, signal);
|
|
312
|
+
void drain();
|
|
313
|
+
return promise;
|
|
312
314
|
}
|
|
313
315
|
|
|
314
316
|
async #handleServerRequest(request: JsonRpcRequest): Promise<void> {
|
|
@@ -37,6 +37,7 @@ When your edit involves brace boundaries (`{` / `}`), prefer these shapes:
|
|
|
37
37
|
- **Do not duplicate chunks inside one payload.** When emitting a long `=` payload, never paste the same multi-line block twice. If you catch yourself re-emitting an earlier run of lines, stop and rewrite the op.
|
|
38
38
|
- **Anchor only inside the visible region.** If the read output around your `=`/`-` end anchor is truncated (you cannot see the line at B+1), issue a fresh `read` before editing — anchoring blind drops or duplicates the boundary line.
|
|
39
39
|
- **Prefer the narrowest self-contained edit.** Once your range cleanly contains the construct you are changing, a `+`/`<` insert plus a small `-` delete is almost always clearer and safer than a single wide `= A..B` that re-emits unchanged context.
|
|
40
|
+
- **Anchors always reference the file as you last read it.** When stacking multiple `+`/`<`/`-`/`=` ops in one patch, do **NOT** mentally shift line numbers to account for prior ops in the same patch. Every op resolves against the original line numbering.
|
|
40
41
|
</common-failures>
|
|
41
42
|
|
|
42
43
|
<case file="a.ts">
|
|
@@ -95,10 +96,6 @@ When your edit involves brace boundaries (`{` / `}`), prefer these shapes:
|
|
|
95
96
|
+ {{hrefr 4}}
|
|
96
97
|
{{hsep}} if (clean.length === 0) return DEF;
|
|
97
98
|
|
|
98
|
-
# Append WITHIN a line
|
|
99
|
-
@a.ts
|
|
100
|
-
+ {{hrefr 4}}{{hsep}} // first run
|
|
101
|
-
|
|
102
99
|
# Append to end of file
|
|
103
100
|
@a.ts
|
|
104
101
|
+ EOF
|
package/src/sdk.ts
CHANGED
|
@@ -100,9 +100,7 @@ import { SessionManager } from "./session/session-manager";
|
|
|
100
100
|
import { closeAllConnections } from "./ssh/connection-manager";
|
|
101
101
|
import { unmountAll } from "./ssh/sshfs-mount";
|
|
102
102
|
import {
|
|
103
|
-
type AgentsMdSearch,
|
|
104
103
|
type BuildSystemPromptResult,
|
|
105
|
-
buildAgentsMdSearch,
|
|
106
104
|
buildSystemPrompt as buildSystemPromptInternal,
|
|
107
105
|
buildSystemPromptToolMetadata,
|
|
108
106
|
loadProjectContextFiles as loadContextFilesInternal,
|
|
@@ -201,8 +199,6 @@ export interface CreateAgentSessionOptions {
|
|
|
201
199
|
rules?: Rule[];
|
|
202
200
|
/** Context files (AGENTS.md content). Default: discovered walking up from cwd */
|
|
203
201
|
contextFiles?: Array<{ path: string; content: string }>;
|
|
204
|
-
/** Pre-built AGENTS.md search (skips re-scanning the workspace; passed by parents to subagents). */
|
|
205
|
-
agentsMdSearch?: AgentsMdSearch;
|
|
206
202
|
/** Pre-built workspace tree (skips re-scanning; passed by parents to subagents). */
|
|
207
203
|
workspaceTree?: WorkspaceTree;
|
|
208
204
|
/** Prompt templates. Default: discovered from cwd/.omp/prompts/ + agentDir/prompts/ */
|
|
@@ -691,16 +687,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
691
687
|
if (!options.modelRegistry) {
|
|
692
688
|
modelRegistry.refreshInBackground();
|
|
693
689
|
}
|
|
694
|
-
// Kick off
|
|
695
|
-
//
|
|
696
|
-
//
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
: logger.time("buildAgentsMdSearch", buildAgentsMdSearch, cwd);
|
|
700
|
-
agentsMdSearchPromise.catch(() => {});
|
|
690
|
+
// Kick off workspace tree discovery early. The native workspace scan returns
|
|
691
|
+
// both the rendered-tree input and the AGENTS.md directory-context index, so
|
|
692
|
+
// startup does not perform a second recursive filesystem search. Subagents
|
|
693
|
+
// inherit the parent's resolved values via options.
|
|
694
|
+
const STARTUP_SCAN_DEADLINE_MS = 5000;
|
|
701
695
|
const workspaceTreePromise: Promise<WorkspaceTree> = options.workspaceTree
|
|
702
696
|
? Promise.resolve(options.workspaceTree)
|
|
703
|
-
: logger.time("buildWorkspaceTree", buildWorkspaceTree,
|
|
697
|
+
: logger.time("buildWorkspaceTree", () => buildWorkspaceTree(cwd, { timeoutMs: STARTUP_SCAN_DEADLINE_MS }));
|
|
704
698
|
workspaceTreePromise.catch(() => {});
|
|
705
699
|
|
|
706
700
|
// Independent discoveries that depend only on cwd/agentDir — kicked off in parallel and awaited
|
|
@@ -898,12 +892,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
898
892
|
return { ttsrManager, rulebookRules, alwaysApplyRules };
|
|
899
893
|
});
|
|
900
894
|
|
|
901
|
-
// Resolve contextFiles up-front (it's needed before tool creation). The
|
|
902
|
-
//
|
|
903
|
-
//
|
|
904
|
-
// re-race
|
|
905
|
-
//
|
|
906
|
-
const STARTUP_SCAN_DEADLINE_MS = 5000;
|
|
895
|
+
// Resolve contextFiles up-front (it's needed before tool creation). The
|
|
896
|
+
// workspace tree scan is slow on large repos and we MUST NOT block startup on
|
|
897
|
+
// it. On timeout we forward `undefined` to ToolSession; buildSystemPromptInternal
|
|
898
|
+
// will re-race the same promise through its own withDeadline path. Background
|
|
899
|
+
// work continues so caches still warm.
|
|
907
900
|
const raceWithDeadline = <T>(name: string, work: Promise<T>): Promise<T | undefined> =>
|
|
908
901
|
Promise.race([
|
|
909
902
|
work,
|
|
@@ -916,9 +909,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
916
909
|
return undefined;
|
|
917
910
|
}),
|
|
918
911
|
]);
|
|
919
|
-
const [contextFiles,
|
|
912
|
+
const [contextFiles, resolvedWorkspaceTree] = await Promise.all([
|
|
920
913
|
contextFilesPromise,
|
|
921
|
-
raceWithDeadline("buildAgentsMdSearch", agentsMdSearchPromise),
|
|
922
914
|
raceWithDeadline("buildWorkspaceTree", workspaceTreePromise),
|
|
923
915
|
]);
|
|
924
916
|
|
|
@@ -1004,7 +996,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1004
996
|
},
|
|
1005
997
|
skipPythonPreflight: options.skipPythonPreflight,
|
|
1006
998
|
contextFiles,
|
|
1007
|
-
agentsMdSearch: resolvedAgentsMdSearch,
|
|
1008
999
|
workspaceTree: resolvedWorkspaceTree,
|
|
1009
1000
|
skills,
|
|
1010
1001
|
eventBus,
|
|
@@ -1456,7 +1447,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1456
1447
|
mcpDiscoveryServerSummaries: discoverableToolSummary.servers.map(formatDiscoverableMCPToolServerSummary),
|
|
1457
1448
|
eagerTasks,
|
|
1458
1449
|
secretsEnabled,
|
|
1459
|
-
agentsMdSearch: agentsMdSearchPromise,
|
|
1460
1450
|
workspaceTree: workspaceTreePromise,
|
|
1461
1451
|
});
|
|
1462
1452
|
|
package/src/system-prompt.ts
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import * as os from "node:os";
|
|
6
6
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
7
|
-
import { FileType, glob } from "@oh-my-pi/pi-natives";
|
|
8
7
|
import { $env, getGpuCachePath, getProjectDir, hasFsCode, isEnoent, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
9
8
|
import { $ } from "bun";
|
|
10
9
|
import { contextFileCapability } from "./capability/context-file";
|
|
@@ -15,7 +14,7 @@ import { loadSkills, type Skill } from "./extensibility/skills";
|
|
|
15
14
|
import customSystemPromptTemplate from "./prompts/system/custom-system-prompt.md" with { type: "text" };
|
|
16
15
|
import projectPromptTemplate from "./prompts/system/project-prompt.md" with { type: "text" };
|
|
17
16
|
import systemPromptTemplate from "./prompts/system/system-prompt.md" with { type: "text" };
|
|
18
|
-
import { buildWorkspaceTree, type WorkspaceTree } from "./workspace-tree";
|
|
17
|
+
import { AGENTS_MD_LIMIT, buildWorkspaceTree, type WorkspaceTree } from "./workspace-tree";
|
|
19
18
|
|
|
20
19
|
interface AlwaysApplyRule {
|
|
21
20
|
name: string;
|
|
@@ -84,58 +83,7 @@ function parseWmicTable(output: string, header: string): string | null {
|
|
|
84
83
|
return filtered[0] ?? null;
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
const AGENTS_MD_MIN_DEPTH = 1;
|
|
88
|
-
const AGENTS_MD_MAX_DEPTH = 4;
|
|
89
|
-
const AGENTS_MD_LIMIT = 200;
|
|
90
86
|
const SYSTEM_PROMPT_PREP_TIMEOUT_MS = 5000;
|
|
91
|
-
const AGENTS_MD_EXCLUDED_DIRS = new Set(["node_modules", ".git"]);
|
|
92
|
-
|
|
93
|
-
export interface AgentsMdSearch {
|
|
94
|
-
scopePath: string;
|
|
95
|
-
limit: number;
|
|
96
|
-
pattern: string;
|
|
97
|
-
files: string[];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async function listAgentsMdFiles(root: string, limit: number): Promise<string[]> {
|
|
101
|
-
try {
|
|
102
|
-
const result = await glob({
|
|
103
|
-
pattern: "**/AGENTS.md",
|
|
104
|
-
path: root,
|
|
105
|
-
fileType: FileType.File,
|
|
106
|
-
recursive: true,
|
|
107
|
-
hidden: false,
|
|
108
|
-
gitignore: true,
|
|
109
|
-
maxResults: limit * 4,
|
|
110
|
-
cache: true,
|
|
111
|
-
});
|
|
112
|
-
const files: string[] = [];
|
|
113
|
-
for (const m of result.matches) {
|
|
114
|
-
const rel = m.path.replace(/\\/g, "/");
|
|
115
|
-
if (!rel?.endsWith("AGENTS.md")) continue;
|
|
116
|
-
const segments = rel.split("/");
|
|
117
|
-
const depth = segments.length - 1;
|
|
118
|
-
if (depth < AGENTS_MD_MIN_DEPTH || depth > AGENTS_MD_MAX_DEPTH) continue;
|
|
119
|
-
const dirSegments = segments.slice(0, -1);
|
|
120
|
-
if (dirSegments.some(seg => AGENTS_MD_EXCLUDED_DIRS.has(seg) || seg.startsWith("."))) continue;
|
|
121
|
-
files.push(rel);
|
|
122
|
-
if (files.length >= limit) break;
|
|
123
|
-
}
|
|
124
|
-
return Array.from(new Set(files)).sort().slice(0, limit);
|
|
125
|
-
} catch {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export async function buildAgentsMdSearch(cwd: string): Promise<AgentsMdSearch> {
|
|
131
|
-
const files = await listAgentsMdFiles(cwd, AGENTS_MD_LIMIT);
|
|
132
|
-
return {
|
|
133
|
-
scopePath: ".",
|
|
134
|
-
limit: AGENTS_MD_LIMIT,
|
|
135
|
-
pattern: `AGENTS.md depth ${AGENTS_MD_MIN_DEPTH}-${AGENTS_MD_MAX_DEPTH}`,
|
|
136
|
-
files,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
87
|
|
|
140
88
|
async function getGpuModel(): Promise<string | null> {
|
|
141
89
|
switch (process.platform) {
|
|
@@ -409,8 +357,6 @@ export interface BuildSystemPromptOptions {
|
|
|
409
357
|
alwaysApplyRules?: AlwaysApplyRule[];
|
|
410
358
|
/** Whether secret obfuscation is active. When true, explains the redaction format in the prompt. */
|
|
411
359
|
secretsEnabled?: boolean;
|
|
412
|
-
/** Pre-loaded AGENTS.md search (skips discovery if provided). May be a Promise to allow early kick-off. */
|
|
413
|
-
agentsMdSearch?: AgentsMdSearch | Promise<AgentsMdSearch>;
|
|
414
360
|
/** Pre-loaded workspace tree (skips discovery if provided). May be a Promise to allow early kick-off. */
|
|
415
361
|
workspaceTree?: WorkspaceTree | Promise<WorkspaceTree>;
|
|
416
362
|
}
|
|
@@ -444,7 +390,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
444
390
|
mcpDiscoveryServerSummaries = [],
|
|
445
391
|
eagerTasks = false,
|
|
446
392
|
secretsEnabled = false,
|
|
447
|
-
agentsMdSearch: providedAgentsMdSearch,
|
|
448
393
|
workspaceTree: providedWorkspaceTree,
|
|
449
394
|
} = options;
|
|
450
395
|
const resolvedCwd = cwd ?? getProjectDir();
|
|
@@ -454,18 +399,13 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
454
399
|
resolvedAppendPrompt: undefined as string | undefined,
|
|
455
400
|
systemPromptCustomization: null as string | null,
|
|
456
401
|
contextFiles: dedupeExactContextFiles(providedContextFiles ?? []),
|
|
457
|
-
agentsMdSearch: {
|
|
458
|
-
scopePath: ".",
|
|
459
|
-
limit: AGENTS_MD_LIMIT,
|
|
460
|
-
pattern: `AGENTS.md depth ${AGENTS_MD_MIN_DEPTH}-${AGENTS_MD_MAX_DEPTH}`,
|
|
461
|
-
files: [] as string[],
|
|
462
|
-
} satisfies AgentsMdSearch,
|
|
463
402
|
skills: providedSkills ?? ([] as Skill[]),
|
|
464
403
|
workspaceTree: {
|
|
465
404
|
rootPath: resolvedCwd,
|
|
466
405
|
rendered: "",
|
|
467
406
|
truncated: false,
|
|
468
407
|
totalLines: 0,
|
|
408
|
+
agentsMdFiles: [],
|
|
469
409
|
} satisfies WorkspaceTree,
|
|
470
410
|
};
|
|
471
411
|
|
|
@@ -503,14 +443,12 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
503
443
|
const contextFilesPromise = providedContextFiles
|
|
504
444
|
? Promise.resolve(providedContextFiles)
|
|
505
445
|
: logger.time("loadProjectContextFiles", loadProjectContextFiles, { cwd: resolvedCwd });
|
|
506
|
-
const agentsMdSearchPromise =
|
|
507
|
-
providedAgentsMdSearch !== undefined
|
|
508
|
-
? Promise.resolve(providedAgentsMdSearch)
|
|
509
|
-
: logger.time("buildAgentsMdSearch", buildAgentsMdSearch, resolvedCwd);
|
|
510
446
|
const workspaceTreePromise =
|
|
511
447
|
providedWorkspaceTree !== undefined
|
|
512
448
|
? Promise.resolve(providedWorkspaceTree)
|
|
513
|
-
: logger.time("buildWorkspaceTree",
|
|
449
|
+
: logger.time("buildWorkspaceTree", () =>
|
|
450
|
+
buildWorkspaceTree(resolvedCwd, { timeoutMs: SYSTEM_PROMPT_PREP_TIMEOUT_MS }),
|
|
451
|
+
);
|
|
514
452
|
const skillsPromise: Promise<Skill[]> =
|
|
515
453
|
providedSkills !== undefined
|
|
516
454
|
? Promise.resolve(providedSkills)
|
|
@@ -518,33 +456,30 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
518
456
|
? loadSkills({ ...skillsSettings, cwd: resolvedCwd }).then(result => result.skills)
|
|
519
457
|
: Promise.resolve([]);
|
|
520
458
|
|
|
521
|
-
const [
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
prepDefaults.
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
)
|
|
544
|
-
|
|
545
|
-
withDeadline("loadSkills", skillsPromise, prepDefaults.skills),
|
|
546
|
-
withDeadline("buildWorkspaceTree", workspaceTreePromise, prepDefaults.workspaceTree),
|
|
547
|
-
]);
|
|
459
|
+
const [resolvedCustomPrompt, resolvedAppendPrompt, systemPromptCustomization, contextFiles, skills, workspaceTree] =
|
|
460
|
+
await Promise.all([
|
|
461
|
+
withDeadline(
|
|
462
|
+
"customPrompt",
|
|
463
|
+
resolvePromptInput(customPrompt, "system prompt"),
|
|
464
|
+
prepDefaults.resolvedCustomPrompt,
|
|
465
|
+
),
|
|
466
|
+
withDeadline(
|
|
467
|
+
"appendSystemPrompt",
|
|
468
|
+
resolvePromptInput(appendSystemPrompt, "append system prompt"),
|
|
469
|
+
prepDefaults.resolvedAppendPrompt,
|
|
470
|
+
),
|
|
471
|
+
withDeadline(
|
|
472
|
+
"loadSystemPromptFiles",
|
|
473
|
+
systemPromptCustomizationPromise,
|
|
474
|
+
prepDefaults.systemPromptCustomization,
|
|
475
|
+
),
|
|
476
|
+
withDeadline("loadProjectContextFiles", contextFilesPromise, prepDefaults.contextFiles).then(
|
|
477
|
+
dedupeExactContextFiles,
|
|
478
|
+
),
|
|
479
|
+
withDeadline("loadSkills", skillsPromise, prepDefaults.skills),
|
|
480
|
+
withDeadline("buildWorkspaceTree", workspaceTreePromise, prepDefaults.workspaceTree),
|
|
481
|
+
]);
|
|
482
|
+
const agentsMdFiles = Array.from(new Set(workspaceTree.agentsMdFiles)).sort().slice(0, AGENTS_MD_LIMIT);
|
|
548
483
|
|
|
549
484
|
if (timedOut.length > 0) {
|
|
550
485
|
logger.warn("System prompt preparation steps timed out; using minimal fallback for those steps", {
|
|
@@ -617,7 +552,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
617
552
|
toolRefs,
|
|
618
553
|
environment,
|
|
619
554
|
contextFiles,
|
|
620
|
-
agentsMdSearch,
|
|
555
|
+
agentsMdSearch: { files: agentsMdFiles },
|
|
621
556
|
workspaceTree,
|
|
622
557
|
skills: filteredSkills,
|
|
623
558
|
rules: rules ?? [],
|
package/src/task/executor.ts
CHANGED
|
@@ -28,7 +28,6 @@ import { createAgentSession, discoverAuthStorage } from "../sdk";
|
|
|
28
28
|
import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
|
|
29
29
|
import type { AuthStorage } from "../session/auth-storage";
|
|
30
30
|
import { SessionManager } from "../session/session-manager";
|
|
31
|
-
import type { AgentsMdSearch } from "../system-prompt";
|
|
32
31
|
import { type ContextFileEntry, truncateTail } from "../tools";
|
|
33
32
|
import { jtdToJsonSchema, normalizeSchema } from "../tools/jtd-to-json-schema";
|
|
34
33
|
import { ToolAbortError } from "../tools/tool-errors";
|
|
@@ -165,7 +164,6 @@ export interface ExecutorOptions {
|
|
|
165
164
|
contextFiles?: ContextFileEntry[];
|
|
166
165
|
skills?: Skill[];
|
|
167
166
|
promptTemplates?: PromptTemplate[];
|
|
168
|
-
agentsMdSearch?: AgentsMdSearch;
|
|
169
167
|
workspaceTree?: WorkspaceTree;
|
|
170
168
|
mcpManager?: MCPManager;
|
|
171
169
|
authStorage?: AuthStorage;
|
|
@@ -941,8 +939,13 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
941
939
|
checkAbort();
|
|
942
940
|
const authStorage = options.authStorage ?? (await discoverAuthStorage());
|
|
943
941
|
checkAbort();
|
|
942
|
+
const registryFromParent = options.modelRegistry !== undefined;
|
|
944
943
|
const modelRegistry = options.modelRegistry ?? new ModelRegistry(authStorage);
|
|
945
|
-
|
|
944
|
+
if (!registryFromParent) {
|
|
945
|
+
await modelRegistry.refresh();
|
|
946
|
+
} else {
|
|
947
|
+
logger.debug("runSubagent: reusing parent modelRegistry; skipping refresh");
|
|
948
|
+
}
|
|
946
949
|
checkAbort();
|
|
947
950
|
|
|
948
951
|
const {
|
|
@@ -990,7 +993,6 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
990
993
|
contextFiles: options.contextFiles,
|
|
991
994
|
skills: options.skills,
|
|
992
995
|
promptTemplates: options.promptTemplates,
|
|
993
|
-
agentsMdSearch: options.agentsMdSearch,
|
|
994
996
|
workspaceTree: options.workspaceTree,
|
|
995
997
|
systemPrompt: defaultPrompt => [
|
|
996
998
|
prompt.render(subagentSystemPromptTemplate, {
|
package/src/task/index.ts
CHANGED
|
@@ -866,7 +866,6 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
866
866
|
mcpManager: this.session.mcpManager,
|
|
867
867
|
contextFiles,
|
|
868
868
|
skills: availableSkills,
|
|
869
|
-
agentsMdSearch: this.session.agentsMdSearch,
|
|
870
869
|
workspaceTree: this.session.workspaceTree,
|
|
871
870
|
promptTemplates,
|
|
872
871
|
localProtocolOptions,
|
|
@@ -924,7 +923,6 @@ export class TaskTool implements AgentTool<TSchema, TaskToolDetails, Theme> {
|
|
|
924
923
|
mcpManager: this.session.mcpManager,
|
|
925
924
|
contextFiles,
|
|
926
925
|
skills: availableSkills,
|
|
927
|
-
agentsMdSearch: this.session.agentsMdSearch,
|
|
928
926
|
workspaceTree: this.session.workspaceTree,
|
|
929
927
|
promptTemplates,
|
|
930
928
|
localProtocolOptions,
|
package/src/task/render.ts
CHANGED
|
@@ -470,14 +470,16 @@ export function renderCall(args: TaskParams, _options: RenderResultOptions, them
|
|
|
470
470
|
lines.push(` ${vertical} ${content}`);
|
|
471
471
|
}
|
|
472
472
|
const taskPrefix = showIsolated ? branch : last;
|
|
473
|
-
lines.push(
|
|
473
|
+
lines.push(
|
|
474
|
+
` ${taskPrefix} ${theme.fg("dim", "Tasks")}: ${theme.fg("muted", `${args.tasks?.length ?? 0} agents`)}`,
|
|
475
|
+
);
|
|
474
476
|
if (showIsolated) {
|
|
475
477
|
lines.push(` ${last} ${theme.fg("dim", "Isolated")}: ${theme.fg("muted", "true")}`);
|
|
476
478
|
}
|
|
477
479
|
return new Text(lines.join("\n"), 0, 0);
|
|
478
480
|
}
|
|
479
481
|
|
|
480
|
-
lines.push(`${theme.fg("dim", "Tasks")}: ${theme.fg("muted", `${args.tasks
|
|
482
|
+
lines.push(`${theme.fg("dim", "Tasks")}: ${theme.fg("muted", `${args.tasks?.length ?? 0} agents`)}`);
|
|
481
483
|
if (showIsolated) {
|
|
482
484
|
lines.push(`${theme.fg("dim", "Isolated")}: ${theme.fg("muted", "true")}`);
|
|
483
485
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -14,7 +14,6 @@ import type { PlanModeState } from "../plan-mode/state";
|
|
|
14
14
|
import type { AgentRegistry } from "../registry/agent-registry";
|
|
15
15
|
import type { CustomMessage } from "../session/messages";
|
|
16
16
|
import type { ToolChoiceQueue } from "../session/tool-choice-queue";
|
|
17
|
-
import type { AgentsMdSearch } from "../system-prompt";
|
|
18
17
|
import { TaskTool } from "../task";
|
|
19
18
|
import type { AgentOutputManager } from "../task/output-manager";
|
|
20
19
|
import type { DiscoverableTool, DiscoverableToolSearchIndex } from "../tool-discovery/tool-index";
|
|
@@ -122,8 +121,6 @@ export interface ToolSession {
|
|
|
122
121
|
skipPythonPreflight?: boolean;
|
|
123
122
|
/** Pre-loaded context files (AGENTS.md, etc) */
|
|
124
123
|
contextFiles?: ContextFileEntry[];
|
|
125
|
-
/** Pre-loaded AGENTS.md search (forwarded to subagents to skip re-scanning) */
|
|
126
|
-
agentsMdSearch?: AgentsMdSearch;
|
|
127
124
|
/** Pre-loaded workspace tree (forwarded to subagents to skip re-scanning) */
|
|
128
125
|
workspaceTree?: WorkspaceTree;
|
|
129
126
|
/** Pre-loaded skills */
|
package/src/tools/read.ts
CHANGED
|
@@ -73,20 +73,6 @@ const PROSE_SUMMARY_EXTENSIONS = new Set([".md", ".txt"]);
|
|
|
73
73
|
// Remote mount path prefix (sshfs mounts) - skip fuzzy matching to avoid hangs
|
|
74
74
|
const REMOTE_MOUNT_PREFIX = getRemoteDir() + path.sep;
|
|
75
75
|
|
|
76
|
-
const READ_DIRECTORY_EXCLUDED_DIRS = new Set([
|
|
77
|
-
"node_modules",
|
|
78
|
-
".git",
|
|
79
|
-
".next",
|
|
80
|
-
"dist",
|
|
81
|
-
"build",
|
|
82
|
-
"target",
|
|
83
|
-
".venv",
|
|
84
|
-
".cache",
|
|
85
|
-
".turbo",
|
|
86
|
-
".parcel-cache",
|
|
87
|
-
"coverage",
|
|
88
|
-
]);
|
|
89
|
-
|
|
90
76
|
function isRemoteMountPath(absolutePath: string): boolean {
|
|
91
77
|
return absolutePath.startsWith(REMOTE_MOUNT_PREFIX);
|
|
92
78
|
}
|
|
@@ -1581,15 +1567,9 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1581
1567
|
try {
|
|
1582
1568
|
tree = await buildDirectoryTree(absolutePath, {
|
|
1583
1569
|
maxDepth: READ_DIRECTORY_MAX_DEPTH,
|
|
1584
|
-
|
|
1585
|
-
|
|
1570
|
+
perDirLimit: READ_DIRECTORY_CHILD_LIMIT,
|
|
1571
|
+
rootLimit: null,
|
|
1586
1572
|
lineCap: limit ?? null,
|
|
1587
|
-
lineCapProtectedDepth: 1,
|
|
1588
|
-
hidden: true,
|
|
1589
|
-
gitignore: false,
|
|
1590
|
-
cache: true,
|
|
1591
|
-
excludedDirectoryNames: READ_DIRECTORY_EXCLUDED_DIRS,
|
|
1592
|
-
rootLabel: ".",
|
|
1593
1573
|
});
|
|
1594
1574
|
} catch (error) {
|
|
1595
1575
|
const message = error instanceof Error ? error.message : String(error);
|