@oh-my-pi/pi-coding-agent 12.19.3 → 13.0.1

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.
Files changed (103) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/package.json +7 -7
  3. package/src/commit/prompts/analysis-system.md +3 -3
  4. package/src/commit/prompts/analysis-user.md +14 -14
  5. package/src/commit/prompts/changelog-system.md +4 -4
  6. package/src/commit/prompts/changelog-user.md +4 -4
  7. package/src/commit/prompts/file-observer-system.md +2 -2
  8. package/src/commit/prompts/file-observer-user.md +2 -2
  9. package/src/commit/prompts/reduce-system.md +4 -4
  10. package/src/commit/prompts/reduce-user.md +6 -6
  11. package/src/commit/prompts/summary-system.md +4 -4
  12. package/src/commit/prompts/summary-user.md +6 -6
  13. package/src/discovery/helpers.ts +13 -1
  14. package/src/internal-urls/docs-index.generated.ts +2 -2
  15. package/src/internal-urls/index.ts +8 -3
  16. package/src/internal-urls/local-protocol.ts +223 -0
  17. package/src/internal-urls/{docs-protocol.ts → pi-protocol.ts} +12 -12
  18. package/src/internal-urls/router.ts +1 -1
  19. package/src/internal-urls/types.ts +1 -1
  20. package/src/ipy/executor.ts +4 -32
  21. package/src/memories/index.ts +1 -1
  22. package/src/modes/controllers/event-controller.ts +4 -4
  23. package/src/modes/interactive-mode.ts +84 -64
  24. package/src/modes/types.ts +11 -3
  25. package/src/modes/utils/ui-helpers.ts +5 -3
  26. package/src/patch/hashline.ts +42 -42
  27. package/src/patch/index.ts +106 -153
  28. package/src/patch/shared.ts +21 -51
  29. package/src/plan-mode/approved-plan.ts +55 -0
  30. package/src/prompts/agents/designer.md +6 -6
  31. package/src/prompts/agents/explore.md +4 -4
  32. package/src/prompts/agents/frontmatter.md +1 -0
  33. package/src/prompts/agents/init.md +10 -10
  34. package/src/prompts/agents/plan.md +6 -6
  35. package/src/prompts/agents/reviewer.md +4 -3
  36. package/src/prompts/agents/task.md +10 -10
  37. package/src/prompts/compaction/branch-summary.md +3 -3
  38. package/src/prompts/compaction/compaction-short-summary.md +7 -7
  39. package/src/prompts/compaction/compaction-summary-context.md +1 -1
  40. package/src/prompts/compaction/compaction-summary.md +5 -5
  41. package/src/prompts/compaction/compaction-turn-prefix.md +3 -3
  42. package/src/prompts/compaction/compaction-update-summary.md +11 -11
  43. package/src/prompts/memories/consolidation.md +5 -5
  44. package/src/prompts/memories/read-path.md +11 -0
  45. package/src/prompts/memories/stage_one_input.md +1 -1
  46. package/src/prompts/memories/stage_one_system.md +5 -5
  47. package/src/prompts/review-request.md +4 -4
  48. package/src/prompts/system/agent-creation-architect.md +21 -21
  49. package/src/prompts/system/agent-creation-user.md +2 -2
  50. package/src/prompts/system/custom-system-prompt.md +6 -6
  51. package/src/prompts/system/plan-mode-active.md +20 -20
  52. package/src/prompts/system/plan-mode-approved.md +9 -7
  53. package/src/prompts/system/plan-mode-reference.md +2 -2
  54. package/src/prompts/system/plan-mode-subagent.md +8 -8
  55. package/src/prompts/system/subagent-submit-reminder.md +5 -5
  56. package/src/prompts/system/subagent-system-prompt.md +9 -9
  57. package/src/prompts/system/subagent-user-prompt.md +3 -5
  58. package/src/prompts/system/summarization-system.md +1 -1
  59. package/src/prompts/system/system-prompt.md +109 -84
  60. package/src/prompts/system/title-system.md +2 -2
  61. package/src/prompts/system/ttsr-interrupt.md +2 -2
  62. package/src/prompts/system/web-search.md +16 -16
  63. package/src/prompts/tools/ask.md +6 -6
  64. package/src/prompts/tools/bash.md +9 -9
  65. package/src/prompts/tools/browser.md +5 -5
  66. package/src/prompts/tools/cancel-job.md +2 -2
  67. package/src/prompts/tools/exit-plan-mode.md +13 -10
  68. package/src/prompts/tools/find.md +2 -2
  69. package/src/prompts/tools/gemini-image.md +7 -7
  70. package/src/prompts/tools/grep.md +4 -3
  71. package/src/prompts/tools/hashline.md +55 -56
  72. package/src/prompts/tools/patch.md +6 -6
  73. package/src/prompts/tools/poll-jobs.md +1 -1
  74. package/src/prompts/tools/python.md +10 -12
  75. package/src/prompts/tools/read.md +2 -12
  76. package/src/prompts/tools/replace.md +7 -7
  77. package/src/prompts/tools/ssh.md +2 -7
  78. package/src/prompts/tools/task.md +48 -38
  79. package/src/prompts/tools/todo-write.md +65 -49
  80. package/src/prompts/tools/web-search.md +2 -2
  81. package/src/prompts/tools/write.md +4 -3
  82. package/src/sdk.ts +11 -9
  83. package/src/session/agent-session.ts +92 -51
  84. package/src/session/artifacts.ts +1 -1
  85. package/src/session/messages.ts +1 -0
  86. package/src/task/agents.ts +1 -0
  87. package/src/task/index.ts +2 -1
  88. package/src/task/render.ts +2 -2
  89. package/src/task/types.ts +1 -0
  90. package/src/tools/bash-interactive.ts +1 -1
  91. package/src/tools/bash-skill-urls.ts +3 -2
  92. package/src/tools/bash.ts +21 -12
  93. package/src/tools/exit-plan-mode.ts +30 -2
  94. package/src/tools/grep.ts +131 -75
  95. package/src/tools/index.ts +13 -3
  96. package/src/tools/path-utils.ts +2 -1
  97. package/src/tools/plan-mode-guard.ts +8 -8
  98. package/src/tools/python.ts +0 -2
  99. package/src/tools/read.ts +2 -2
  100. package/src/tools/todo-write.ts +276 -146
  101. package/src/internal-urls/plan-protocol.ts +0 -95
  102. package/src/modes/components/todo-display.ts +0 -114
  103. package/src/prompts/memories/read_path.md +0 -11
package/src/tools/grep.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as path from "node:path";
2
2
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
3
3
 
4
- import { type GrepResult, grep } from "@oh-my-pi/pi-natives";
4
+ import { type GrepMatch, type GrepResult, grep } from "@oh-my-pi/pi-natives";
5
5
  import type { Component } from "@oh-my-pi/pi-tui";
6
6
  import { Text } from "@oh-my-pi/pi-tui";
7
7
  import { untilAborted } from "@oh-my-pi/pi-utils";
@@ -30,13 +30,13 @@ const grepSchema = Type.Object({
30
30
  pre: Type.Optional(Type.Number({ description: "Lines of context before matches" })),
31
31
  post: Type.Optional(Type.Number({ description: "Lines of context after matches" })),
32
32
  multiline: Type.Optional(Type.Boolean({ description: "Enable multiline matching" })),
33
- limit: Type.Optional(Type.Number({ description: "Limit output to first N matches (default: 100)" })),
33
+ limit: Type.Optional(Type.Number({ description: "Limit output to first N matches (default: 20)" })),
34
34
  offset: Type.Optional(Type.Number({ description: "Skip first N entries before applying limit (default: 0)" })),
35
35
  });
36
36
 
37
37
  export type GrepToolInput = Static<typeof grepSchema>;
38
38
 
39
- const DEFAULT_MATCH_LIMIT = 100;
39
+ const DEFAULT_MATCH_LIMIT = 20;
40
40
 
41
41
  export interface GrepToolDetails {
42
42
  truncation?: TruncationResult;
@@ -130,6 +130,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
130
130
 
131
131
  const effectiveOutputMode = "content";
132
132
  const effectiveLimit = normalizedLimit ?? DEFAULT_MATCH_LIMIT;
133
+ const internalLimit = Math.min(effectiveLimit * 5, 2000);
133
134
 
134
135
  // Run grep
135
136
  let result: GrepResult;
@@ -143,7 +144,7 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
143
144
  multiline: effectiveMultiline,
144
145
  hidden: true,
145
146
  cache: false,
146
- maxCount: effectiveLimit,
147
+ maxCount: internalLimit,
147
148
  offset: normalizedOffset > 0 ? normalizedOffset : undefined,
148
149
  contextBefore: normalizedContextBefore,
149
150
  contextAfter: normalizedContextAfter,
@@ -167,19 +168,49 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
167
168
  };
168
169
 
169
170
  // Build output
171
+ const roundRobinSelect = (matches: GrepMatch[], limit: number): GrepMatch[] => {
172
+ if (matches.length <= limit) return matches;
173
+ const fileOrder: string[] = [];
174
+ const byFile = new Map<string, GrepMatch[]>();
175
+ for (const match of matches) {
176
+ if (!byFile.has(match.path)) {
177
+ fileOrder.push(match.path);
178
+ byFile.set(match.path, []);
179
+ }
180
+ byFile.get(match.path)!.push(match);
181
+ }
182
+ const selected: GrepMatch[] = [];
183
+ const indices = new Map<string, number>(fileOrder.map(file => [file, 0]));
184
+ while (selected.length < limit) {
185
+ let anyAdded = false;
186
+ for (const file of fileOrder) {
187
+ if (selected.length >= limit) break;
188
+ const fileMatches = byFile.get(file)!;
189
+ const idx = indices.get(file)!;
190
+ if (idx < fileMatches.length) {
191
+ selected.push(fileMatches[idx]);
192
+ indices.set(file, idx + 1);
193
+ anyAdded = true;
194
+ }
195
+ }
196
+ if (!anyAdded) break;
197
+ }
198
+ return selected;
199
+ };
200
+ const selectedMatches = isDirectory
201
+ ? roundRobinSelect(result.matches, effectiveLimit)
202
+ : result.matches.slice(0, effectiveLimit);
203
+ const matchLimitReached = result.matches.length > effectiveLimit;
170
204
  const files = new Set<string>();
171
205
  const fileList: string[] = [];
172
206
  const fileMatchCounts = new Map<string, number>();
173
-
174
- const recordFile = (filePath: string) => {
175
- const relative = formatPath(filePath);
176
- if (!files.has(relative)) {
177
- files.add(relative);
178
- fileList.push(relative);
207
+ const recordFile = (relativePath: string) => {
208
+ if (!files.has(relativePath)) {
209
+ files.add(relativePath);
210
+ fileList.push(relativePath);
179
211
  }
180
212
  };
181
-
182
- if (result.totalMatches === 0) {
213
+ if (selectedMatches.length === 0) {
183
214
  const details: GrepToolDetails = {
184
215
  scopePath,
185
216
  matchCount: 0,
@@ -189,100 +220,120 @@ export class GrepTool implements AgentTool<typeof grepSchema, GrepToolDetails> {
189
220
  };
190
221
  return toolResult(details).text("No matches found").done();
191
222
  }
192
-
193
223
  const outputLines: string[] = [];
194
224
  let linesTruncated = false;
195
- let matchIndex = 0;
196
-
197
- for (const match of result.matches) {
198
- recordFile(match.path);
225
+ const matchesByFile = new Map<string, GrepMatch[]>();
226
+ for (const match of selectedMatches) {
199
227
  const relativePath = formatPath(match.path);
200
-
201
- matchIndex += 1;
202
- if (matchIndex > 1) {
203
- outputLines.push("");
228
+ recordFile(relativePath);
229
+ if (!matchesByFile.has(relativePath)) {
230
+ matchesByFile.set(relativePath, []);
204
231
  }
205
- outputLines.push(`${matchIndex}. ${relativePath}:${match.lineNumber}`);
206
-
207
- const lineNumbers: number[] = [match.lineNumber];
208
- if (match.contextBefore) {
209
- for (const ctx of match.contextBefore) {
210
- lineNumbers.push(ctx.lineNumber);
232
+ matchesByFile.get(relativePath)!.push(match);
233
+ }
234
+ const renderMatchesForFile = (relativePath: string) => {
235
+ const fileMatches = matchesByFile.get(relativePath) ?? [];
236
+ for (const match of fileMatches) {
237
+ const lineNumbers: number[] = [match.lineNumber];
238
+ if (match.contextBefore) {
239
+ for (const ctx of match.contextBefore) {
240
+ lineNumbers.push(ctx.lineNumber);
241
+ }
211
242
  }
212
- }
213
- if (match.contextAfter) {
214
- for (const ctx of match.contextAfter) {
215
- lineNumbers.push(ctx.lineNumber);
243
+ if (match.contextAfter) {
244
+ for (const ctx of match.contextAfter) {
245
+ lineNumbers.push(ctx.lineNumber);
246
+ }
216
247
  }
217
- }
218
- const lineWidth = Math.max(...lineNumbers.map(value => value.toString().length));
219
-
220
- const formatLine = (lineNumber: number, line: string, isMatch: boolean): string => {
221
- if (useHashLines) {
222
- const ref = `${lineNumber}#${computeLineHash(lineNumber, line)}`;
223
- return isMatch ? `>>${ref}:${line}` : ` ${ref}:${line}`;
248
+ const lineWidth = Math.max(...lineNumbers.map(value => value.toString().length));
249
+ const formatLine = (lineNumber: number, line: string, isMatch: boolean): string => {
250
+ if (useHashLines) {
251
+ const ref = `${lineNumber}#${computeLineHash(lineNumber, line)}`;
252
+ return isMatch ? `>>${ref}:${line}` : ` ${ref}:${line}`;
253
+ }
254
+ const padded = lineNumber.toString().padStart(lineWidth, " ");
255
+ return isMatch ? `>>${padded}:${line}` : ` ${padded}:${line}`;
256
+ };
257
+ if (match.contextBefore) {
258
+ for (const ctx of match.contextBefore) {
259
+ outputLines.push(formatLine(ctx.lineNumber, ctx.line, false));
260
+ }
224
261
  }
225
- const padded = lineNumber.toString().padStart(lineWidth, " ");
226
- return isMatch ? `>>${padded}:${line}` : ` ${padded}:${line}`;
227
- };
228
-
229
- // Add context before
230
- if (match.contextBefore) {
231
- for (const ctx of match.contextBefore) {
232
- outputLines.push(formatLine(ctx.lineNumber, ctx.line, false));
262
+ outputLines.push(formatLine(match.lineNumber, match.line, true));
263
+ if (match.truncated) {
264
+ linesTruncated = true;
265
+ }
266
+ if (match.contextAfter) {
267
+ for (const ctx of match.contextAfter) {
268
+ outputLines.push(formatLine(ctx.lineNumber, ctx.line, false));
269
+ }
233
270
  }
271
+ fileMatchCounts.set(relativePath, (fileMatchCounts.get(relativePath) ?? 0) + 1);
234
272
  }
235
-
236
- // Add match line
237
- outputLines.push(formatLine(match.lineNumber, match.line, true));
238
-
239
- if (match.truncated) {
240
- linesTruncated = true;
273
+ };
274
+ if (isDirectory) {
275
+ const filesByDirectory = new Map<string, string[]>();
276
+ for (const relativePath of fileList) {
277
+ const directory = path.dirname(relativePath).replace(/\\/g, "/");
278
+ if (!filesByDirectory.has(directory)) {
279
+ filesByDirectory.set(directory, []);
280
+ }
281
+ filesByDirectory.get(directory)!.push(relativePath);
241
282
  }
242
-
243
- // Add context after
244
- if (match.contextAfter) {
245
- for (const ctx of match.contextAfter) {
246
- outputLines.push(formatLine(ctx.lineNumber, ctx.line, false));
283
+ for (const [directory, directoryFiles] of filesByDirectory) {
284
+ if (directory === ".") {
285
+ for (const relativePath of directoryFiles) {
286
+ if (outputLines.length > 0) {
287
+ outputLines.push("");
288
+ }
289
+ outputLines.push(`# ${path.basename(relativePath)}`);
290
+ renderMatchesForFile(relativePath);
291
+ }
292
+ continue;
293
+ }
294
+ if (outputLines.length > 0) {
295
+ outputLines.push("");
296
+ }
297
+ outputLines.push(`# ${directory}`);
298
+ for (const relativePath of directoryFiles) {
299
+ outputLines.push(`## └─ ${path.basename(relativePath)}`);
300
+ renderMatchesForFile(relativePath);
247
301
  }
248
302
  }
249
-
250
- // Track per-file counts
251
- fileMatchCounts.set(relativePath, (fileMatchCounts.get(relativePath) ?? 0) + 1);
303
+ } else {
304
+ for (const relativePath of fileList) {
305
+ renderMatchesForFile(relativePath);
306
+ }
252
307
  }
253
-
254
308
  const rawOutput = outputLines.join("\n");
255
309
  const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
256
310
  const output = truncation.content;
257
-
258
- const truncated = Boolean(result.limitReached || truncation.truncated || linesTruncated);
311
+ const truncated = Boolean(matchLimitReached || result.limitReached || truncation.truncated || linesTruncated);
259
312
  const details: GrepToolDetails = {
260
313
  scopePath,
261
- matchCount: result.totalMatches,
262
- fileCount: result.filesWithMatches,
314
+ matchCount: selectedMatches.length,
315
+ fileCount: fileList.length,
263
316
  files: fileList,
264
317
  fileMatches: fileList.map(path => ({
265
318
  path,
266
319
  count: fileMatchCounts.get(path) ?? 0,
267
320
  })),
268
321
  truncated,
269
- matchLimitReached: result.limitReached ? effectiveLimit : undefined,
322
+ matchLimitReached: matchLimitReached ? effectiveLimit : undefined,
323
+ resultLimitReached: result.limitReached ? internalLimit : undefined,
270
324
  };
271
-
272
325
  if (truncation.truncated) details.truncation = truncation;
273
326
  if (linesTruncated) details.linesTruncated = true;
274
-
275
327
  const resultBuilder = toolResult(details)
276
328
  .text(output)
277
329
  .limits({
278
- matchLimit: result.limitReached ? effectiveLimit : undefined,
330
+ matchLimit: matchLimitReached ? effectiveLimit : undefined,
331
+ resultLimit: result.limitReached ? internalLimit : undefined,
279
332
  columnMax: linesTruncated ? DEFAULT_MAX_COLUMN : undefined,
280
333
  });
281
-
282
334
  if (truncation.truncated) {
283
335
  resultBuilder.truncation(truncation, { direction: "head" });
284
336
  }
285
-
286
337
  return resultBuilder.done();
287
338
  });
288
339
  }
@@ -428,9 +479,9 @@ export const grepToolRenderer = {
428
479
  }
429
480
  if (current.length > 0) matchGroups.push(current);
430
481
  } else {
431
- for (const line of rawLines) {
432
- if (line.trim().length === 0) continue;
433
- matchGroups.push([line]);
482
+ const nonEmpty = rawLines.filter(line => line.trim().length > 0);
483
+ if (nonEmpty.length > 0) {
484
+ matchGroups.push(nonEmpty);
434
485
  }
435
486
  }
436
487
 
@@ -472,7 +523,12 @@ export const grepToolRenderer = {
472
523
  expanded,
473
524
  maxCollapsed,
474
525
  itemType: "match",
475
- renderItem: group => group.map(line => uiTheme.fg("toolOutput", line)),
526
+ renderItem: group =>
527
+ group.map(line => {
528
+ if (line.startsWith("## ")) return uiTheme.fg("dim", line);
529
+ if (line.startsWith("# ")) return uiTheme.fg("accent", line);
530
+ return uiTheme.fg("toolOutput", line);
531
+ }),
476
532
  },
477
533
  uiTheme,
478
534
  );
@@ -31,7 +31,7 @@ import { ReadTool } from "./read";
31
31
  import { reportFindingTool } from "./review";
32
32
  import { loadSshTool } from "./ssh";
33
33
  import { SubmitResultTool } from "./submit-result";
34
- import { TodoWriteTool } from "./todo-write";
34
+ import { type TodoPhase, TodoWriteTool } from "./todo-write";
35
35
  import { WriteTool } from "./write";
36
36
 
37
37
  // Exa MCP tools (22 tools)
@@ -70,7 +70,13 @@ export { ReadTool, type ReadToolDetails, type ReadToolInput } from "./read";
70
70
  export { reportFindingTool, type SubmitReviewDetails } from "./review";
71
71
  export { loadSshTool, type SSHToolDetails, SshTool } from "./ssh";
72
72
  export { SubmitResultTool } from "./submit-result";
73
- export { type TodoItem, TodoWriteTool, type TodoWriteToolDetails } from "./todo-write";
73
+ export {
74
+ getLatestTodoPhasesFromEntries,
75
+ type TodoItem,
76
+ type TodoPhase,
77
+ TodoWriteTool,
78
+ type TodoWriteToolDetails,
79
+ } from "./todo-write";
74
80
  export { WriteTool, type WriteToolDetails, type WriteToolInput } from "./write";
75
81
 
76
82
  /** Tool type (AgentTool from pi-ai) */
@@ -112,7 +118,7 @@ export interface ToolSession {
112
118
  getSessionFile: () => string | null;
113
119
  /** Get session ID */
114
120
  getSessionId?: () => string | null;
115
- /** Get artifacts directory for artifact:// URLs and $ARTIFACTS env var */
121
+ /** Get artifacts directory for artifact:// URLs */
116
122
  getArtifactsDir?: () => string | null;
117
123
  /** Allocate a new artifact path and ID for session-scoped truncated output. */
118
124
  allocateOutputArtifact?: (toolType: string) => Promise<{ id?: string; path?: string }>;
@@ -140,6 +146,10 @@ export interface ToolSession {
140
146
  getPlanModeState?: () => PlanModeState | undefined;
141
147
  /** Get compact conversation context for subagents (excludes tool results, system prompts) */
142
148
  getCompactContext?: () => string;
149
+ /** Get cached todo phases for this session. */
150
+ getTodoPhases?: () => TodoPhase[];
151
+ /** Replace cached todo phases for this session. */
152
+ setTodoPhases?: (phases: TodoPhase[]) => void;
143
153
  }
144
154
 
145
155
  type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool | null>;
@@ -50,7 +50,8 @@ function normalizeAtPrefix(filePath: string): string {
50
50
  withoutAt.startsWith("agent://") ||
51
51
  withoutAt.startsWith("artifact://") ||
52
52
  withoutAt.startsWith("skill://") ||
53
- withoutAt.startsWith("rule://")
53
+ withoutAt.startsWith("rule://") ||
54
+ withoutAt.startsWith("local://")
54
55
  ) {
55
56
  return withoutAt;
56
57
  }
@@ -1,19 +1,19 @@
1
- import { resolvePlanUrlToPath } from "../internal-urls";
1
+ import { resolveLocalUrlToPath } from "../internal-urls";
2
2
  import type { ToolSession } from ".";
3
3
  import { resolveToCwd } from "./path-utils";
4
4
  import { ToolError } from "./tool-errors";
5
5
 
6
- const PLAN_URL_PREFIX = "plan://";
6
+ const LOCAL_URL_PREFIX = "local://";
7
7
 
8
8
  export function resolvePlanPath(session: ToolSession, targetPath: string): string {
9
- if (!targetPath.startsWith(PLAN_URL_PREFIX)) {
10
- return resolveToCwd(targetPath, session.cwd);
9
+ if (targetPath.startsWith(LOCAL_URL_PREFIX)) {
10
+ return resolveLocalUrlToPath(targetPath, {
11
+ getArtifactsDir: session.getArtifactsDir,
12
+ getSessionId: session.getSessionId,
13
+ });
11
14
  }
12
15
 
13
- return resolvePlanUrlToPath(targetPath, {
14
- getPlansDirectory: () => session.settings.getPlansDirectory(),
15
- cwd: session.cwd,
16
- });
16
+ return resolveToCwd(targetPath, session.cwd);
17
17
  }
18
18
 
19
19
  export function enforcePlanModeWrite(
@@ -253,7 +253,6 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
253
253
  };
254
254
 
255
255
  const sessionFile = this.session.getSessionFile?.() ?? undefined;
256
- const artifactsDir = this.session.getArtifactsDir?.() ?? undefined;
257
256
  const { path: artifactPath, id: artifactId } = (await this.session.allocateOutputArtifact?.("python")) ?? {};
258
257
  outputSink = new OutputSink({
259
258
  artifactPath,
@@ -272,7 +271,6 @@ export class PythonTool implements AgentTool<typeof pythonSchema> {
272
271
  kernelMode: this.session.settings.get("python.kernelMode"),
273
272
  useSharedGateway: this.session.settings.get("python.sharedGateway"),
274
273
  sessionFile: sessionFile ?? undefined,
275
- artifactsDir: artifactsDir ?? undefined,
276
274
  };
277
275
 
278
276
  for (let i = 0; i < cells.length; i++) {
package/src/tools/read.ts CHANGED
@@ -582,7 +582,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
582
582
 
583
583
  const displayMode = resolveFileDisplayMode(this.session);
584
584
 
585
- // Handle internal URLs (agent://, artifact://, plan://, memory://, skill://, rule://)
585
+ // Handle internal URLs (agent://, artifact://, memory://, skill://, rule://, local://)
586
586
  const internalRouter = this.session.internalRouter;
587
587
  if (internalRouter?.canHandle(readPath)) {
588
588
  return this.#handleInternalUrl(readPath, offset, limit);
@@ -841,7 +841,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
841
841
  }
842
842
 
843
843
  /**
844
- * Handle internal URLs (agent://, artifact://, plan://, memory://, skill://, rule://).
844
+ * Handle internal URLs (agent://, artifact://, memory://, skill://, rule://, local://).
845
845
  * Supports pagination via offset/limit but rejects them when query extraction is used.
846
846
  */
847
847
  async #handleInternalUrl(url: string, offset?: number, limit?: number): Promise<AgentToolResult<ReadToolDetails>> {