@oh-my-pi/pi-coding-agent 17.3.4 → 17.3.7

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 (137) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/dist/{CHANGELOG-trcc215s.md → CHANGELOG-1hmwwt45.md} +88 -0
  3. package/dist/cli.js +3156 -3163
  4. package/dist/docs-index.generated.txt +1 -1
  5. package/dist/types/cli/stats-cli.d.ts +1 -6
  6. package/dist/types/commands/stats.d.ts +4 -0
  7. package/dist/types/config/model-discovery.d.ts +2 -0
  8. package/dist/types/config/model-registry.d.ts +6 -1
  9. package/dist/types/config/settings-schema.d.ts +10 -0
  10. package/dist/types/debug/report-bundle.d.ts +7 -2
  11. package/dist/types/extensibility/extensions/loader.d.ts +2 -0
  12. package/dist/types/extensibility/extensions/runner.d.ts +24 -6
  13. package/dist/types/extensibility/extensions/types.d.ts +60 -0
  14. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +4 -0
  15. package/dist/types/launch/presence.d.ts +11 -0
  16. package/dist/types/lsp/client.d.ts +22 -0
  17. package/dist/types/lsp/workspace-diagnostics.d.ts +2 -0
  18. package/dist/types/mcp/config.d.ts +3 -1
  19. package/dist/types/modes/components/tool-execution.d.ts +3 -1
  20. package/dist/types/registry/agent-lifecycle.d.ts +11 -0
  21. package/dist/types/registry/agent-registry.d.ts +4 -0
  22. package/dist/types/session/agent-session-types.d.ts +0 -2
  23. package/dist/types/session/agent-session.d.ts +20 -0
  24. package/dist/types/session/date-cwd-reminder.d.ts +22 -0
  25. package/dist/types/session/irc-bridge.d.ts +2 -0
  26. package/dist/types/session/messages.d.ts +4 -0
  27. package/dist/types/session/session-loader.d.ts +18 -11
  28. package/dist/types/session/session-maintenance.d.ts +6 -2
  29. package/dist/types/session/session-manager.d.ts +19 -3
  30. package/dist/types/session/session-tools.d.ts +0 -1
  31. package/dist/types/session/turn-recovery.d.ts +7 -7
  32. package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +1 -0
  33. package/dist/types/tools/browser/attach.d.ts +16 -0
  34. package/dist/types/tools/browser/launch.d.ts +2 -2
  35. package/dist/types/tools/browser.d.ts +1 -1
  36. package/dist/types/tools/builtin-names.d.ts +1 -1
  37. package/dist/types/tools/file-write-fallback.d.ts +124 -0
  38. package/dist/types/tools/hub/jobs.d.ts +6 -0
  39. package/dist/types/tools/hub/types.d.ts +6 -0
  40. package/dist/types/tools/index.d.ts +1 -0
  41. package/dist/types/tools/path-utils.d.ts +23 -0
  42. package/dist/types/vibe/runtime.d.ts +3 -3
  43. package/package.json +13 -13
  44. package/src/auto-thinking/classifier.ts +37 -23
  45. package/src/cli/models-cli.ts +1 -1
  46. package/src/cli/stats-cli.ts +6 -72
  47. package/src/commands/stats.ts +7 -4
  48. package/src/commit/analysis/conventional.ts +15 -9
  49. package/src/commit/analysis/summary.ts +15 -9
  50. package/src/commit/changelog/generate.ts +15 -9
  51. package/src/commit/map-reduce/map-phase.ts +3 -19
  52. package/src/commit/map-reduce/reduce-phase.ts +15 -9
  53. package/src/config/model-discovery.ts +3 -3
  54. package/src/config/model-registry.ts +29 -16
  55. package/src/config/settings-schema.ts +13 -0
  56. package/src/debug/report-bundle.ts +25 -59
  57. package/src/discovery/claude-plugins.ts +2 -1
  58. package/src/discovery/claude.ts +4 -2
  59. package/src/discovery/helpers.ts +5 -0
  60. package/src/edit/hashline/filesystem.ts +9 -3
  61. package/src/edit/modes/patch.ts +31 -5
  62. package/src/edit/renderer.ts +48 -19
  63. package/src/extensibility/extensions/loader.ts +20 -5
  64. package/src/extensibility/extensions/runner.ts +239 -24
  65. package/src/extensibility/extensions/types.ts +62 -0
  66. package/src/extensibility/extensions/wrapper.ts +26 -11
  67. package/src/extensibility/legacy-pi-ai-shim.ts +4 -0
  68. package/src/extensibility/plugins/marketplace/manager.ts +15 -18
  69. package/src/irc/bus.ts +1 -1
  70. package/src/launch/broker.ts +8 -1
  71. package/src/launch/presence.ts +77 -1
  72. package/src/lsp/client.ts +35 -3
  73. package/src/lsp/clients/biome-client.ts +47 -93
  74. package/src/lsp/servers.ts +17 -11
  75. package/src/lsp/tool.ts +2 -4
  76. package/src/lsp/workspace-diagnostics.ts +24 -2
  77. package/src/lsp/writethrough.ts +20 -10
  78. package/src/mcp/config.ts +50 -6
  79. package/src/memories/index.ts +29 -25
  80. package/src/mnemopi/backend.ts +13 -11
  81. package/src/modes/components/ask-dialog.ts +25 -5
  82. package/src/modes/components/tool-execution.ts +10 -6
  83. package/src/modes/components/welcome.ts +4 -1
  84. package/src/modes/controllers/extension-ui-controller.ts +52 -32
  85. package/src/modes/controllers/selector-controller.ts +6 -0
  86. package/src/modes/controllers/tan-command-controller.ts +1 -0
  87. package/src/modes/interactive-mode.ts +49 -10
  88. package/src/modes/theme/defaults/birch.json +2 -2
  89. package/src/prompts/system/checkpoint-active-notice.md +5 -0
  90. package/src/prompts/system/date-cwd-reminder.md +3 -0
  91. package/src/prompts/system/project-prompt.md +0 -1
  92. package/src/prompts/system/rewind-report.md +1 -3
  93. package/src/prompts/tools/browser.md +1 -0
  94. package/src/prompts/tools/rewind.md +1 -13
  95. package/src/registry/agent-lifecycle.ts +34 -0
  96. package/src/registry/agent-registry.ts +27 -2
  97. package/src/registry/persisted-agents.ts +40 -20
  98. package/src/sdk.ts +32 -2
  99. package/src/session/agent-session-types.ts +0 -2
  100. package/src/session/agent-session.ts +178 -46
  101. package/src/session/date-cwd-reminder.ts +77 -0
  102. package/src/session/irc-bridge.ts +5 -0
  103. package/src/session/messages.ts +5 -1
  104. package/src/session/session-loader.ts +106 -64
  105. package/src/session/session-maintenance.ts +189 -115
  106. package/src/session/session-manager.ts +142 -35
  107. package/src/session/session-persistence.ts +4 -4
  108. package/src/session/session-storage.ts +18 -5
  109. package/src/session/session-tools.ts +5 -10
  110. package/src/session/turn-recovery.ts +102 -20
  111. package/src/session/unexpected-stop-classifier.ts +33 -21
  112. package/src/slash-commands/builtin-session.ts +1 -1
  113. package/src/slash-commands/helpers/stats-dashboard.ts +23 -9
  114. package/src/system-prompt.ts +0 -5
  115. package/src/task/executor.ts +4 -15
  116. package/src/task/persisted-revive.ts +3 -6
  117. package/src/tools/ask.ts +10 -3
  118. package/src/tools/browser/attach.ts +80 -25
  119. package/src/tools/browser/launch.ts +44 -15
  120. package/src/tools/browser/relay/daemon.ts +3 -7
  121. package/src/tools/browser/render.ts +1 -1
  122. package/src/tools/browser/shared-daemon.ts +3 -7
  123. package/src/tools/browser.ts +5 -5
  124. package/src/tools/builtin-names.ts +7 -3
  125. package/src/tools/checkpoint.ts +1 -7
  126. package/src/tools/file-write-fallback.ts +467 -0
  127. package/src/tools/hub/index.ts +1 -1
  128. package/src/tools/hub/jobs.ts +20 -3
  129. package/src/tools/hub/types.ts +6 -0
  130. package/src/tools/index.ts +1 -0
  131. package/src/tools/path-utils.ts +79 -0
  132. package/src/tools/render-utils.ts +53 -21
  133. package/src/tools/think.ts +15 -3
  134. package/src/tts/speech-enhancer.ts +19 -14
  135. package/src/utils/commit-message-generator.ts +14 -12
  136. package/src/utils/title-generator.ts +23 -19
  137. package/src/vibe/runtime.ts +32 -17
@@ -121,23 +121,36 @@ async function loadBrowsers(): Promise<typeof BrowsersNs> {
121
121
  }
122
122
 
123
123
  /**
124
- * Resolve the Chromium executable puppeteer will launch, honoring
125
- * PUPPETEER_EXECUTABLE_PATH before system browser detection and lazily
126
- * downloading Chromium otherwise. The browser is cached under
127
- * ~/.omp/puppeteer (getPuppeteerDir). Returns undefined when platform
128
- * detection fails (puppeteer default resolution takes over). Exported so
129
- * real-browser tests can probe launchability and skip on hosts missing
130
- * Chrome's system libraries.
124
+ * Resolve the Chromium executable puppeteer will launch.
125
+ *
126
+ * `PUPPETEER_EXECUTABLE_PATH` always wins. On macOS the isolated Chrome for
127
+ * Testing binary is preferred over a detected system Chrome: a headless
128
+ * daemon launched from a system `Google Chrome.app` bundle shares its
129
+ * LaunchServices bundle identity (`com.google.Chrome`), so macOS can deliver
130
+ * the user's open-URL Apple Events to the daemon and silently swallow their
131
+ * link clicks (#8673). Chrome for Testing uses a dedicated bundle id
132
+ * (`com.google.chrome.for.testing`) that is never a user's default handler;
133
+ * system Chrome is used on macOS only when Chrome for Testing cannot be
134
+ * obtained. Other platforms keep the download-avoiding system Chrome
135
+ * preference and fall back to Chrome for Testing. The managed browser is
136
+ * cached under ~/.omp/puppeteer (getPuppeteerDir). Returns undefined when
137
+ * platform detection fails (puppeteer default resolution takes over).
138
+ * Exported so real-browser tests can probe launchability and skip on hosts
139
+ * missing Chrome's system libraries.
131
140
  */
132
141
  let chromiumExecutablePromise: Promise<string | undefined> | undefined;
133
142
  export async function ensureChromiumExecutable(): Promise<string | undefined> {
134
143
  const envPath = process.env.PUPPETEER_EXECUTABLE_PATH;
135
144
  if (envPath) return envPath;
136
- const sysChrome = await resolveSystemChromium();
137
- if (sysChrome) return sysChrome;
138
- if (chromiumExecutablePromise) return chromiumExecutablePromise;
145
+ // macOS: never route a background daemon through the user's GUI Chrome
146
+ // bundle; prefer the isolated Chrome for Testing binary instead (#8673).
147
+ const preferManagedChromium = process.platform === "darwin";
148
+ if (!preferManagedChromium) {
149
+ const sysChrome = await resolveSystemChromium();
150
+ if (sysChrome) return sysChrome;
151
+ }
139
152
 
140
- chromiumExecutablePromise = (async () => {
153
+ chromiumExecutablePromise ??= (async () => {
141
154
  const browsers = await loadBrowsers();
142
155
  const platform = browsers.detectBrowserPlatform();
143
156
  if (!platform) {
@@ -185,7 +198,23 @@ export async function ensureChromiumExecutable(): Promise<string | undefined> {
185
198
  "Set PUPPETEER_EXECUTABLE_PATH to use an existing Chrome/Chromium binary, or install one manually.",
186
199
  );
187
200
  });
188
- return chromiumExecutablePromise;
201
+
202
+ try {
203
+ return await chromiumExecutablePromise;
204
+ } catch (err) {
205
+ if (!preferManagedChromium) throw err;
206
+ // Chrome for Testing could not be obtained on macOS; degrade to the
207
+ // system Chrome bundle rather than leaving the browser tool unusable.
208
+ const sysChrome = await resolveSystemChromium();
209
+ if (!sysChrome) throw err;
210
+ logger.warn(
211
+ "Chrome for Testing unavailable; falling back to the system Chrome bundle. On macOS this can let the " +
212
+ "headless browser daemon capture your link clicks (#8673). Set PUPPETEER_EXECUTABLE_PATH to a " +
213
+ "dedicated Chromium to avoid this.",
214
+ { path: sysChrome, error: (err as Error).message },
215
+ );
216
+ return sysChrome;
217
+ }
189
218
  }
190
219
 
191
220
  let resolvedChromium: string | null | undefined; // undefined = unchecked; null = not found
@@ -430,8 +459,8 @@ export interface SharedBrowserLaunchSpec {
430
459
  * Resolve the executable and complete argv for a shared Chromium the daemon
431
460
  * broker spawns directly (no puppeteer inside the broker). Mirrors
432
461
  * `launchHeadlessBrowser` flag assembly — puppeteer's default args minus the
433
- * stealth-suppressed set — plus `--remote-debugging-port=0` so every client
434
- * attaches over CDP. Returns null when no Chromium executable resolves;
462
+ * stealth-suppressed set — suppresses Puppeteer's unowned startup window, and
463
+ * exposes CDP on an ephemeral port. Returns null when no executable resolves;
435
464
  * callers fall back to a process-local launch.
436
465
  */
437
466
  export async function resolveSharedBrowserLaunchSpec(opts: {
@@ -451,7 +480,7 @@ export async function resolveSharedBrowserLaunchSpec(opts: {
451
480
  });
452
481
  return {
453
482
  executablePath,
454
- args: [...defaults.filter(arg => !ignored.has(arg)), "--remote-debugging-port=0"],
483
+ args: [...defaults.filter(arg => !ignored.has(arg)), "--no-startup-window", "--remote-debugging-port=0"],
455
484
  };
456
485
  }
457
486
 
@@ -17,6 +17,7 @@ import { daemonClientForGlobal } from "../../../launch/client";
17
17
  import { describeQuietly, stopQuietly, waitReady } from "../../../launch/ensure";
18
18
  import { resolveWorkerSpawnCmd } from "../../../subprocess/worker-client";
19
19
  import { throwIfAborted } from "../../tool-errors";
20
+ import { probeCdpStatus } from "../attach";
20
21
 
21
22
  /** Stable broker daemon name for the relay server. */
22
23
  export const RELAY_DAEMON_NAME = "omp.browser.relay";
@@ -30,13 +31,8 @@ const ENSURE_ATTEMPTS = 3;
30
31
 
31
32
  /** True when the relay HTTP server answers /json/version at all (200 = extension connected, 503 = waiting for it). */
32
33
  export async function probeRelayServer(cdpUrl: string): Promise<boolean> {
33
- try {
34
- const res = await fetch(`${cdpUrl}/json/version`, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });
35
- await res.body?.cancel();
36
- return res.ok || res.status === 503;
37
- } catch {
38
- return false;
39
- }
34
+ const status = await probeCdpStatus(`${cdpUrl}/json/version`, { timeoutMs: PROBE_TIMEOUT_MS });
35
+ return status === 503 || (status !== null && status >= 200 && status < 300);
40
36
  }
41
37
 
42
38
  /** Auto-start is only safe for endpoints this machine can own. */
@@ -162,7 +162,7 @@ function renderOpenOrCloseLine(
162
162
  let title: string;
163
163
  if (action === "close") {
164
164
  const all = args.all === true || (args.name === undefined && details?.name === undefined);
165
- title = all ? "Close all tabs" : `Close ${tabLabel(args, details)}`;
165
+ title = all ? "Release all tabs" : `Release ${tabLabel(args, details)}`;
166
166
  if (args.kill) title += " (kill)";
167
167
  } else {
168
168
  title = `Open ${tabLabel(args, details)}`;
@@ -16,6 +16,7 @@ import { describeQuietly, stopQuietly, waitReady } from "../../launch/ensure";
16
16
  import { daemonRuntimeDir } from "../../launch/paths";
17
17
  import type { DaemonSnapshot } from "../../launch/protocol";
18
18
  import { throwIfAborted } from "../tool-errors";
19
+ import { probeCdpStatus } from "./attach";
19
20
  import { resolveSharedBrowserLaunchSpec } from "./launch";
20
21
 
21
22
  /** Chrome prints this on stderr once the CDP listener is up; the broker's ready probe captures the line. */
@@ -50,13 +51,8 @@ async function probeEndpoint(wsEndpoint: string): Promise<boolean> {
50
51
  } catch {
51
52
  return false;
52
53
  }
53
- try {
54
- const res = await fetch(`http://${host}/json/version`, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });
55
- await res.body?.cancel();
56
- return res.ok;
57
- } catch {
58
- return false;
59
- }
54
+ const status = await probeCdpStatus(`http://${host}/json/version`, { timeoutMs: PROBE_TIMEOUT_MS });
55
+ return status !== null && status >= 200 && status < 300;
60
56
  }
61
57
 
62
58
  /**
@@ -69,7 +69,7 @@ const browserSchema = type({
69
69
  "dialogs?": type("'accept' | 'dismiss'").describe("auto-handle dialogs"),
70
70
  "code?": type("string").describe("js body to run in tab"),
71
71
  "timeout?": type("number").describe("timeout in seconds"),
72
- "all?": type("boolean").describe("close every tab"),
72
+ "all?": type("boolean").describe("release every managed tab"),
73
73
  "kill?": type("boolean").describe("also kill spawned-app browsers"),
74
74
  });
75
75
 
@@ -133,7 +133,7 @@ function resolveBrowserKind(params: BrowserParams, session: ToolSession): Browse
133
133
  /**
134
134
  * Browser tool: stateful, multi-tab. Three actions:
135
135
  * - `open` → acquire/create a named tab on a browser kind (headless | spawned | connected) and optionally goto a url.
136
- * - `close` → release a named tab (or all tabs); dispose browser when refcount hits 0.
136
+ * - `close` → release a named tab handle (or all handles); attached/relay pages remain open, and spawned pages remain unless killed.
137
137
  * - `run` → execute JS code against an existing tab with `page`/`browser`/`tab` helpers in scope.
138
138
  */
139
139
  export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolDetails> {
@@ -204,7 +204,7 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
204
204
  },
205
205
  },
206
206
  {
207
- caption: "Close every tab and kill spawned-app processes",
207
+ caption: "Release every managed tab and kill spawned-app processes",
208
208
  call: { action: "close", all: true, kill: true },
209
209
  },
210
210
  ];
@@ -362,11 +362,11 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
362
362
  const kill = !!params.kill;
363
363
  if (params.all) {
364
364
  const count = await untilAborted(signal, () => releaseAllTabs({ kill, timeoutMs }));
365
- details.result = `Closed ${count} tab(s)`;
365
+ details.result = `Released ${count} managed tab${count === 1 ? "" : "s"}`;
366
366
  return toolResult(details).text(details.result).done();
367
367
  }
368
368
  const closed = await untilAborted(signal, () => releaseTab(name, { kill, timeoutMs }));
369
- details.result = closed ? `Closed tab ${JSON.stringify(name)}` : `No tab named ${JSON.stringify(name)}`;
369
+ details.result = closed ? `Released managed tab ${JSON.stringify(name)}` : `No tab named ${JSON.stringify(name)}`;
370
370
  return toolResult(details).text(details.result).done();
371
371
  }
372
372
 
@@ -41,10 +41,14 @@ const LEGACY_BUILTIN_TOOL_NAME_ALIASES: ReadonlyMap<string, BuiltinToolName> = n
41
41
  ["find", "glob"],
42
42
  ]);
43
43
 
44
- /** Return the canonical tool name for current and legacy built-in tool IDs. */
44
+ const CANONICAL_TOOL_NAMES: Record<string, true> = Object.fromEntries(
45
+ [...BUILTIN_TOOL_NAMES, ...HIDDEN_TOOL_NAMES].map(name => [name, true]),
46
+ );
47
+
48
+ /** Canonicalize built-in IDs and legacy aliases. Leave plugin names unchanged. */
45
49
  export function normalizeToolName(name: string): string {
46
- const normalized = name.toLowerCase();
47
- return LEGACY_BUILTIN_TOOL_NAME_ALIASES.get(normalized) ?? normalized;
50
+ const lower = name.toLowerCase();
51
+ return LEGACY_BUILTIN_TOOL_NAME_ALIASES.get(lower) ?? (Object.hasOwn(CANONICAL_TOOL_NAMES, lower) ? lower : name);
48
52
  }
49
53
 
50
54
  /** Normalize and deduplicate tool names while preserving first-seen order. */
@@ -81,13 +81,7 @@ export class CheckpointTool implements AgentTool<typeof checkpointSchema, Checkp
81
81
  }
82
82
  const startedAt = new Date().toISOString();
83
83
  return toolResult<CheckpointToolDetails>({ goal: params.goal, startedAt })
84
- .text(
85
- [
86
- "Checkpoint created.",
87
- `Goal: ${params.goal}`,
88
- "Run your investigation, then call rewind with a concise report.",
89
- ].join("\n"),
90
- )
84
+ .text([`Checkpoint: ${params.goal}`, "Finish exploration and formulate findings."].join("\n"))
91
85
  .done();
92
86
  }
93
87
  }