@oh-my-pi/pi-coding-agent 14.9.0 → 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.
@@ -245,11 +245,7 @@ export class HttpTransport implements MCPTransport {
245
245
  }
246
246
  }
247
247
 
248
- async #parseSSEResponse<T>(
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
- try {
265
- let result: T | undefined;
266
- let captured = false;
267
- for await (const raw of readSseJson<JsonRpcMessage | JsonRpcMessage[]>(response.body, operationSignal)) {
268
- // Flatten batches (JSON-RPC 2.0 section 6) into individual messages.
269
- const messages = Array.isArray(raw) ? raw : [raw];
270
- for (const message of messages) {
271
- if (
272
- !captured &&
273
- "id" in message &&
274
- message.id === expectedId &&
275
- ("result" in message || "error" in message)
276
- ) {
277
- clearTimeout(timeoutId);
278
- if (message.error) {
279
- throw new Error(`MCP error ${message.error.code}: ${message.error.message}`);
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
- result = message.result as T;
282
- captured = true;
283
- continue;
289
+ if (!this.#connected) continue;
290
+ this.#dispatchSSEMessage(message);
284
291
  }
285
- this.#dispatchSSEMessage(message);
286
292
  }
287
- if (captured) break;
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
- throw new Error(`SSE response timeout after ${timeout}ms`);
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
- throw error;
304
- } finally {
305
- clearTimeout(timeoutId);
306
- }
307
- }
310
+ };
308
311
 
309
- /** Continue reading SSE messages in the background after the primary response is captured. */
310
- #drainSSEBackground(body: ReadableStream<Uint8Array>, signal: AbortSignal): void {
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 AGENTS.md filesystem search and workspace tree in parallel they are the slowest pieces of
695
- // buildSystemPrompt (can be many seconds on large repos) and only need `cwd`, so they overlap with
696
- // everything that follows. Subagents inherit the parent's resolved values via options.
697
- const agentsMdSearchPromise: Promise<AgentsMdSearch> = options.agentsMdSearch
698
- ? Promise.resolve(options.agentsMdSearch)
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, cwd);
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 agentsMd / workspace tree
902
- // scans are slowest on large repos and we MUST NOT block startup on them — race them against a
903
- // short deadline. On timeout we forward `undefined` to ToolSession; buildSystemPromptInternal will
904
- // re-race them through its own withDeadline path, and subagents will scan independently (still
905
- // cheaper than an unbounded parent hang). Background work continues so caches still warm.
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, resolvedAgentsMdSearch, resolvedWorkspaceTree] = await Promise.all([
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
 
@@ -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", buildWorkspaceTree, resolvedCwd);
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
- resolvedCustomPrompt,
523
- resolvedAppendPrompt,
524
- systemPromptCustomization,
525
- contextFiles,
526
- agentsMdSearch,
527
- skills,
528
- workspaceTree,
529
- ] = await Promise.all([
530
- withDeadline(
531
- "customPrompt",
532
- resolvePromptInput(customPrompt, "system prompt"),
533
- prepDefaults.resolvedCustomPrompt,
534
- ),
535
- withDeadline(
536
- "appendSystemPrompt",
537
- resolvePromptInput(appendSystemPrompt, "append system prompt"),
538
- prepDefaults.resolvedAppendPrompt,
539
- ),
540
- withDeadline("loadSystemPromptFiles", systemPromptCustomizationPromise, prepDefaults.systemPromptCustomization),
541
- withDeadline("loadProjectContextFiles", contextFilesPromise, prepDefaults.contextFiles).then(
542
- dedupeExactContextFiles,
543
- ),
544
- withDeadline("buildAgentsMdSearch", agentsMdSearchPromise, prepDefaults.agentsMdSearch),
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 ?? [],
@@ -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
- await modelRegistry.refresh();
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,
@@ -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(` ${taskPrefix} ${theme.fg("dim", "Tasks")}: ${theme.fg("muted", `${args.tasks.length} agents`)}`);
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.length} agents`)}`);
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
  }
@@ -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
- directoryEntryLimit: READ_DIRECTORY_CHILD_LIMIT,
1585
- rootEntryLimit: null,
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);