@oh-my-pi/pi-coding-agent 13.0.2 → 13.1.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.
@@ -1,5 +1,7 @@
1
+ import * as fs from "node:fs/promises";
1
2
  import * as path from "node:path";
2
3
  import type { Skill } from "../extensibility/skills";
4
+ import { type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
3
5
  import { validateRelativePath } from "../internal-urls/skill-protocol";
4
6
  import type { InternalResource } from "../internal-urls/types";
5
7
  import { ToolError } from "./tool-errors";
@@ -9,9 +11,9 @@ const SKILL_URL_PATTERN = /'skill:\/\/[^'\s")`\\]+'|"skill:\/\/[^"\s')`\\]+"|ski
9
11
 
10
12
  /** Regex to find supported internal URL tokens in command text. */
11
13
  const INTERNAL_URL_PATTERN =
12
- /'(?:skill|agent|artifact|plan|memory|rule):\/\/[^'\s")`\\]+'|"(?:skill|agent|artifact|plan|memory|rule):\/\/[^"\s')`\\]+"|(?:skill|agent|artifact|plan|memory|rule):\/\/[^\s'")`\\]+/g;
14
+ /'(?:skill|agent|artifact|plan|memory|rule|local):\/\/[^'\s")`\\]+'|"(?:skill|agent|artifact|plan|memory|rule|local):\/\/[^"\s')`\\]+"|(?:skill|agent|artifact|plan|memory|rule|local):\/\/[^\s'")`\\]+/g;
13
15
 
14
- const SUPPORTED_INTERNAL_SCHEMES = ["skill", "agent", "artifact", "plan", "memory", "rule"] as const;
16
+ const SUPPORTED_INTERNAL_SCHEMES = ["skill", "agent", "artifact", "plan", "memory", "rule", "local"] as const;
15
17
 
16
18
  type SupportedInternalScheme = (typeof SUPPORTED_INTERNAL_SCHEMES)[number];
17
19
 
@@ -24,6 +26,8 @@ export interface InternalUrlExpansionOptions {
24
26
  skills: readonly Skill[];
25
27
  noEscape?: boolean;
26
28
  internalRouter?: InternalUrlResolver;
29
+ localOptions?: LocalProtocolOptions;
30
+ ensureLocalParentDirs?: boolean;
27
31
  }
28
32
 
29
33
  /**
@@ -102,6 +106,8 @@ async function resolveInternalUrlToPath(
102
106
  url: string,
103
107
  skills: readonly Skill[],
104
108
  internalRouter?: InternalUrlResolver,
109
+ localOptions?: LocalProtocolOptions,
110
+ ensureLocalParentDirs?: boolean,
105
111
  ): Promise<string> {
106
112
  const scheme = extractScheme(url);
107
113
  if (!scheme) {
@@ -112,6 +118,19 @@ async function resolveInternalUrlToPath(
112
118
  return resolveSkillUrlToPath(url, skills);
113
119
  }
114
120
 
121
+ if (scheme === "local") {
122
+ if (!localOptions) {
123
+ throw new ToolError(
124
+ "Cannot resolve local:// URL in bash command: local protocol options are unavailable for this session.",
125
+ );
126
+ }
127
+ const resolvedLocalPath = resolveLocalUrlToPath(url, localOptions);
128
+ if (ensureLocalParentDirs) {
129
+ await fs.mkdir(path.dirname(resolvedLocalPath), { recursive: true });
130
+ }
131
+ return resolvedLocalPath;
132
+ }
133
+
115
134
  if (!internalRouter || !internalRouter.canHandle(url)) {
116
135
  throw new ToolError(
117
136
  `Cannot resolve ${scheme}:// URL in bash command: ${url}\n` +
@@ -169,7 +188,13 @@ export async function expandInternalUrls(command: string, options: InternalUrlEx
169
188
  if (index === undefined) continue;
170
189
 
171
190
  const url = unquoteToken(token);
172
- const resolvedPath = await resolveInternalUrlToPath(url, options.skills, options.internalRouter);
191
+ const resolvedPath = await resolveInternalUrlToPath(
192
+ url,
193
+ options.skills,
194
+ options.internalRouter,
195
+ options.localOptions,
196
+ options.ensureLocalParentDirs,
197
+ );
173
198
  const replacement = options.noEscape ? resolvedPath : shellEscape(resolvedPath);
174
199
  expanded = `${expanded.slice(0, index)}${replacement}${expanded.slice(index + token.length)}`;
175
200
  }
package/src/tools/bash.ts CHANGED
@@ -173,8 +173,12 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
173
173
  const internalUrlOptions: InternalUrlExpansionOptions = {
174
174
  skills: this.session.skills ?? [],
175
175
  internalRouter: this.session.internalRouter,
176
+ localOptions: {
177
+ getArtifactsDir: this.session.getArtifactsDir,
178
+ getSessionId: this.session.getSessionId,
179
+ },
176
180
  };
177
- command = await expandInternalUrls(command, internalUrlOptions);
181
+ command = await expandInternalUrls(command, { ...internalUrlOptions, ensureLocalParentDirs: true });
178
182
 
179
183
  // Resolve protocol URLs (skill://, agent://, etc.) in extracted cwd.
180
184
  if (cwd?.includes("://")) {
@@ -380,7 +380,7 @@ const browserSchema = Type.Object({
380
380
  Type.Object({
381
381
  width: Type.Number({ description: "Viewport width in pixels" }),
382
382
  height: Type.Number({ description: "Viewport height in pixels" }),
383
- deviceScaleFactor: Type.Optional(Type.Number({ description: "Device scale factor" })),
383
+ device_scale_factor: Type.Optional(Type.Number({ description: "Device scale factor" })),
384
384
  }),
385
385
  ),
386
386
  delta_x: Type.Optional(Type.Number({ description: "Scroll delta X (scroll)" })),
@@ -515,7 +515,14 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
515
515
  async #resetBrowser(params?: BrowserParams): Promise<Page> {
516
516
  await this.#closeBrowser();
517
517
  this.#currentHeadless = this.session.settings.get("browser.headless");
518
- const initialViewport = params?.viewport ?? DEFAULT_VIEWPORT;
518
+ const vp = params?.viewport;
519
+ const initialViewport = vp
520
+ ? {
521
+ width: vp.width,
522
+ height: vp.height,
523
+ deviceScaleFactor: vp.device_scale_factor ?? DEFAULT_VIEWPORT.deviceScaleFactor,
524
+ }
525
+ : DEFAULT_VIEWPORT;
519
526
  const puppeteer = await loadPuppeteer();
520
527
  this.#browser = await puppeteer.launch({
521
528
  headless: this.#currentHeadless,
@@ -556,8 +563,15 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
556
563
  }
557
564
 
558
565
  async #applyViewport(page: Page, viewport?: BrowserParams["viewport"]): Promise<void> {
559
- const target = viewport ?? DEFAULT_VIEWPORT;
560
- await page.setViewport(target);
566
+ if (!viewport) {
567
+ await page.setViewport(DEFAULT_VIEWPORT);
568
+ return;
569
+ }
570
+ await page.setViewport({
571
+ width: viewport.width,
572
+ height: viewport.height,
573
+ deviceScaleFactor: viewport.device_scale_factor ?? DEFAULT_VIEWPORT.deviceScaleFactor,
574
+ });
561
575
  }
562
576
 
563
577
  async #clearElementCache(): Promise<void> {
@@ -15,6 +15,7 @@ import type { AgentOutputManager } from "../task/output-manager";
15
15
  import type { EventBus } from "../utils/event-bus";
16
16
  import { SearchTool } from "../web/search";
17
17
  import { AskTool } from "./ask";
18
+ import { AwaitTool } from "./await-tool";
18
19
  import { BashTool } from "./bash";
19
20
  import { BrowserTool } from "./browser";
20
21
  import { CalculatorTool } from "./calculator";
@@ -25,7 +26,6 @@ import { FindTool } from "./find";
25
26
  import { GrepTool } from "./grep";
26
27
  import { NotebookTool } from "./notebook";
27
28
  import { wrapToolWithMetaNotice } from "./output-meta";
28
- import { PollJobsTool } from "./poll-jobs";
29
29
  import { PythonTool } from "./python";
30
30
  import { ReadTool } from "./read";
31
31
  import { reportFindingTool } from "./review";
@@ -54,6 +54,7 @@ export * from "../session/streaming-output";
54
54
  export { BUNDLED_AGENTS, TaskTool } from "../task";
55
55
  export * from "../web/search";
56
56
  export { AskTool, type AskToolDetails } from "./ask";
57
+ export { AwaitTool, type AwaitToolDetails } from "./await-tool";
57
58
  export { BashTool, type BashToolDetails, type BashToolInput, type BashToolOptions } from "./bash";
58
59
  export { BrowserTool, type BrowserToolDetails } from "./browser";
59
60
  export { CalculatorTool, type CalculatorToolDetails } from "./calculator";
@@ -64,7 +65,6 @@ export { type FindOperations, FindTool, type FindToolDetails, type FindToolInput
64
65
  export { setPreferredImageProvider } from "./gemini-image";
65
66
  export { GrepTool, type GrepToolDetails, type GrepToolInput } from "./grep";
66
67
  export { NotebookTool, type NotebookToolDetails } from "./notebook";
67
- export { PollJobsTool, type PollJobsToolDetails } from "./poll-jobs";
68
68
  export { PythonTool, type PythonToolDetails, type PythonToolOptions } from "./python";
69
69
  export { ReadTool, type ReadToolDetails, type ReadToolInput } from "./read";
70
70
  export { reportFindingTool, type SubmitReviewDetails } from "./review";
@@ -169,7 +169,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
169
169
  browser: s => new BrowserTool(s),
170
170
  task: TaskTool.create,
171
171
  cancel_job: CancelJobTool.createIf,
172
- poll_jobs: PollJobsTool.createIf,
172
+ await: AwaitTool.createIf,
173
173
  todo_write: s => new TodoWriteTool(s),
174
174
  fetch: s => new FetchTool(s),
175
175
  web_search: s => new SearchTool(s),
@@ -63,7 +63,7 @@ export class NotebookTool implements AgentTool<typeof notebookSchema, NotebookTo
63
63
  readonly name = "notebook";
64
64
  readonly label = "Notebook";
65
65
  readonly description =
66
- "Completely replaces the contents of a specific cell in a Jupyter notebook (.ipynb file) with new source. Jupyter notebooks are interactive documents that combine code, text, and visualizations, commonly used for data analysis and scientific computing. The notebook_path parameter must be an absolute path, not a relative path. The cell_number is 0-indexed. Use edit_mode=insert to add a new cell at the index specified by cell_number. Use edit_mode=delete to delete the cell at the index specified by cell_number.";
66
+ "Edit, insert, or delete cells in Jupyter notebooks (.ipynb). cell_index is 0-based. Paths must be absolute.";
67
67
  readonly parameters = notebookSchema;
68
68
  readonly strict = true;
69
69
  readonly concurrency = "exclusive";