@oh-my-pi/pi-coding-agent 17.2.9 → 17.2.11

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 (281) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/{CHANGELOG-8yy8h3bc.md → CHANGELOG-d9xenpn9.md} +83 -0
  3. package/dist/cli.js +10208 -12596
  4. package/dist/types/capability/mcp.d.ts +11 -0
  5. package/dist/types/capability/skill.d.ts +6 -0
  6. package/dist/types/cli/args.d.ts +1 -0
  7. package/dist/types/cli/command-help.d.ts +3 -0
  8. package/dist/types/commands/share.d.ts +25 -0
  9. package/dist/types/commit/agentic/index.d.ts +3 -1
  10. package/dist/types/commit/execute.d.ts +32 -0
  11. package/dist/types/commit/index.d.ts +2 -1
  12. package/dist/types/commit/pipeline.d.ts +7 -1
  13. package/dist/types/config/model-registry.d.ts +4 -0
  14. package/dist/types/config/model-resolver.d.ts +13 -0
  15. package/dist/types/config/settings-schema.d.ts +1 -34
  16. package/dist/types/discovery/agent-plugin-format.d.ts +143 -0
  17. package/dist/types/discovery/agent-plugins.d.ts +1 -0
  18. package/dist/types/discovery/contained-path.d.ts +34 -0
  19. package/dist/types/discovery/index.d.ts +1 -0
  20. package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
  21. package/dist/types/extensibility/custom-tools/types.d.ts +3 -3
  22. package/dist/types/extensibility/extensions/loader.d.ts +9 -2
  23. package/dist/types/extensibility/extensions/types.d.ts +45 -8
  24. package/dist/types/extensibility/hooks/types.d.ts +5 -5
  25. package/dist/types/extensibility/shared-events.d.ts +3 -2
  26. package/dist/types/extensibility/skills.d.ts +5 -0
  27. package/dist/types/index.d.ts +2 -2
  28. package/dist/types/main.d.ts +1 -1
  29. package/dist/types/mcp/transports/header-policy.d.ts +45 -0
  30. package/dist/types/mcp/types.d.ts +15 -0
  31. package/dist/types/modes/acp/acp-agent.d.ts +1 -1
  32. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
  33. package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
  34. package/dist/types/modes/acp/acp-mode.d.ts +1 -1
  35. package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
  36. package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
  37. package/dist/types/modes/components/agent-hub.d.ts +14 -5
  38. package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
  39. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  40. package/dist/types/modes/components/custom-editor.d.ts +1 -2
  41. package/dist/types/modes/components/model-browser.d.ts +9 -1
  42. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  43. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  44. package/dist/types/modes/interactive-mode.d.ts +8 -1
  45. package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
  46. package/dist/types/registry/agent-registry.d.ts +47 -0
  47. package/dist/types/registry/persisted-agents.d.ts +3 -1
  48. package/dist/types/secrets/index.d.ts +23 -1
  49. package/dist/types/session/agent-session-events.d.ts +2 -2
  50. package/dist/types/session/agent-session.d.ts +4 -2
  51. package/dist/types/session/messages.d.ts +1 -0
  52. package/dist/types/session/prewalk.d.ts +3 -1
  53. package/dist/types/session/session-entries.d.ts +10 -0
  54. package/dist/types/session/session-loader.d.ts +11 -1
  55. package/dist/types/session/session-manager.d.ts +9 -1
  56. package/dist/types/session/session-tools.d.ts +18 -1
  57. package/dist/types/session/turn-recovery.d.ts +7 -2
  58. package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
  59. package/dist/types/slash-commands/available-commands.d.ts +1 -1
  60. package/dist/types/task/executor.d.ts +6 -0
  61. package/dist/types/task/index.d.ts +4 -3
  62. package/dist/types/task/read-only-policy.d.ts +3 -0
  63. package/dist/types/task/structured-subagent.d.ts +2 -0
  64. package/dist/types/task/types.d.ts +4 -0
  65. package/dist/types/tools/bash.d.ts +3 -3
  66. package/dist/types/tools/browser/launch.d.ts +2 -1
  67. package/dist/types/tools/browser/tab-worker.d.ts +2 -2
  68. package/dist/types/tools/path-utils.d.ts +1 -0
  69. package/dist/types/tools/read.d.ts +1 -5
  70. package/dist/types/tools/run-scope.d.ts +27 -1
  71. package/dist/types/tools/shell-tokenize.d.ts +16 -0
  72. package/dist/types/tools/terminal-output.d.ts +1 -1
  73. package/dist/types/utils/turndown.d.ts +1 -1
  74. package/dist/types/vibe/runtime.d.ts +2 -3
  75. package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
  76. package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
  77. package/examples/custom-tools/README.md +1 -1
  78. package/examples/extensions/README.md +1 -1
  79. package/examples/extensions/api-demo.ts +5 -6
  80. package/examples/extensions/chalk-logger.ts +1 -1
  81. package/examples/extensions/hello.ts +2 -2
  82. package/examples/extensions/reload-runtime.ts +1 -1
  83. package/examples/extensions/tools.ts +2 -2
  84. package/examples/extensions/with-deps/index.ts +1 -1
  85. package/examples/sdk/06-extensions.ts +1 -1
  86. package/package.json +15 -31
  87. package/scripts/bundle-dist.ts +1 -11
  88. package/scripts/legacy-pi-virtual-module.ts +4 -1
  89. package/src/advisor/runtime.ts +54 -0
  90. package/src/capability/mcp.ts +11 -0
  91. package/src/capability/skill.ts +6 -0
  92. package/src/cli/agents-cli.ts +1 -1
  93. package/src/cli/args.ts +24 -2
  94. package/src/cli/auth-broker-cli.ts +1 -1
  95. package/src/cli/auth-gateway-cli.ts +1 -1
  96. package/src/cli/bench-cli.ts +1 -1
  97. package/src/cli/claude-trace-cli.ts +1 -1
  98. package/src/cli/command-help.ts +4 -0
  99. package/src/cli/config-cli.ts +1 -1
  100. package/src/cli/dry-balance-cli.ts +1 -1
  101. package/src/cli/file-processor.ts +1 -1
  102. package/src/cli/flag-tables.ts +4 -0
  103. package/src/cli/gallery-cli.ts +1 -1
  104. package/src/cli/grep-cli.ts +1 -1
  105. package/src/cli/grievances-cli.ts +1 -1
  106. package/src/cli/help-extra.ts +1 -1
  107. package/src/cli/models-cli.ts +1 -1
  108. package/src/cli/plugin-cli.ts +1 -1
  109. package/src/cli/read-cli.ts +1 -1
  110. package/src/cli/setup-cli.ts +1 -1
  111. package/src/cli/shell-cli.ts +1 -1
  112. package/src/cli/ssh-cli.ts +1 -1
  113. package/src/cli/stats-cli.ts +1 -1
  114. package/src/cli/tiny-models-cli.ts +1 -1
  115. package/src/cli/ttsr-cli.ts +1 -1
  116. package/src/cli/update-cli.ts +1 -1
  117. package/src/cli/usage-cli.ts +1 -1
  118. package/src/cli/web-search-cli.ts +1 -1
  119. package/src/cli/worktree-cli.ts +1 -1
  120. package/src/cli-commands.ts +5 -0
  121. package/src/commands/commit.ts +16 -3
  122. package/src/commands/say.ts +1 -1
  123. package/src/commands/share.ts +71 -0
  124. package/src/commands/token.ts +1 -1
  125. package/src/commit/agentic/agent.ts +1 -1
  126. package/src/commit/agentic/index.ts +39 -21
  127. package/src/commit/cli.ts +1 -1
  128. package/src/commit/execute.ts +56 -0
  129. package/src/commit/index.ts +2 -1
  130. package/src/commit/pipeline.ts +20 -7
  131. package/src/config/model-discovery.ts +1 -1
  132. package/src/config/model-registry.ts +21 -1
  133. package/src/config/model-resolver.ts +54 -7
  134. package/src/config/settings-schema.ts +2 -33
  135. package/src/config/settings.ts +46 -0
  136. package/src/discovery/agent-plugin-format.ts +551 -0
  137. package/src/discovery/agent-plugins.ts +341 -0
  138. package/src/discovery/agents.ts +4 -2
  139. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
  140. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
  141. package/src/discovery/claude-plugins.ts +21 -5
  142. package/src/discovery/contained-path.ts +78 -0
  143. package/src/discovery/helpers.ts +23 -9
  144. package/src/discovery/index.ts +1 -0
  145. package/src/discovery/omp-plugins.ts +20 -7
  146. package/src/eval/py/runner.py +38 -1
  147. package/src/exec/non-interactive-env.ts +1 -0
  148. package/src/extensibility/custom-commands/loader.ts +4 -4
  149. package/src/extensibility/custom-commands/types.ts +5 -5
  150. package/src/extensibility/custom-tools/loader.ts +4 -4
  151. package/src/extensibility/custom-tools/types.ts +3 -3
  152. package/src/extensibility/extensions/loader.ts +85 -20
  153. package/src/extensibility/extensions/runner.ts +6 -0
  154. package/src/extensibility/extensions/types.ts +51 -8
  155. package/src/extensibility/hooks/loader.ts +4 -5
  156. package/src/extensibility/hooks/types.ts +5 -5
  157. package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
  158. package/src/extensibility/plugins/marketplace/manager.ts +2 -1
  159. package/src/extensibility/shared-events.ts +3 -2
  160. package/src/extensibility/skills.ts +9 -0
  161. package/src/index.ts +2 -2
  162. package/src/internal-urls/memory-protocol.ts +5 -1
  163. package/src/internal-urls/skill-protocol.ts +14 -0
  164. package/src/internal-urls/vault-protocol.ts +2 -2
  165. package/src/launch/client.ts +11 -1
  166. package/src/launch/protocol.ts +7 -2
  167. package/src/launch/terminal-output.ts +1 -1
  168. package/src/main.ts +88 -23
  169. package/src/markit/converters/docx.ts +1 -1
  170. package/src/markit/converters/epub.ts +1 -1
  171. package/src/markit/converters/pptx.ts +1 -1
  172. package/src/markit/converters/xlsx.ts +1 -1
  173. package/src/mcp/config.ts +3 -0
  174. package/src/mcp/manager.ts +12 -9
  175. package/src/mcp/transports/header-policy.ts +95 -0
  176. package/src/mcp/transports/http.ts +35 -52
  177. package/src/mcp/transports/sse.ts +20 -27
  178. package/src/mcp/types.ts +15 -0
  179. package/src/modes/acp/acp-agent.ts +20 -9
  180. package/src/modes/acp/acp-client-bridge.ts +1 -1
  181. package/src/modes/acp/acp-event-mapper.ts +1 -1
  182. package/src/modes/acp/acp-mode.ts +1 -1
  183. package/src/modes/components/agent-dashboard.ts +2 -0
  184. package/src/modes/components/agent-hub-projection.ts +248 -0
  185. package/src/modes/components/agent-hub-renderer.ts +194 -0
  186. package/src/modes/components/agent-hub.ts +674 -207
  187. package/src/modes/components/agent-transcript-viewer.ts +3 -0
  188. package/src/modes/components/assistant-message.ts +1 -1
  189. package/src/modes/components/chat-transcript-builder.ts +3 -0
  190. package/src/modes/components/custom-editor.ts +0 -9
  191. package/src/modes/components/extensions/inspector-panel.ts +11 -6
  192. package/src/modes/components/footer.ts +1 -3
  193. package/src/modes/components/model-browser.ts +11 -3
  194. package/src/modes/components/model-hub.ts +4 -1
  195. package/src/modes/components/status-line/component.ts +1 -0
  196. package/src/modes/components/status-line/segments.ts +10 -7
  197. package/src/modes/components/status-line/types.ts +2 -0
  198. package/src/modes/components/tool-execution.ts +13 -16
  199. package/src/modes/components/tree-selector.ts +62 -3
  200. package/src/modes/controllers/command-controller.ts +4 -1
  201. package/src/modes/controllers/event-controller.ts +44 -20
  202. package/src/modes/controllers/extension-ui-controller.ts +2 -2
  203. package/src/modes/controllers/input-controller.ts +20 -2
  204. package/src/modes/controllers/mcp-command-controller.ts +81 -20
  205. package/src/modes/controllers/selector-controller.ts +41 -49
  206. package/src/modes/controllers/streaming-reveal.ts +1 -1
  207. package/src/modes/interactive-mode.ts +13 -2
  208. package/src/modes/rpc/rpc-mode.ts +2 -2
  209. package/src/modes/runtime-init.ts +1 -1
  210. package/src/modes/theme/theme.ts +2 -2
  211. package/src/modes/utils/transcript-render-helpers.ts +4 -2
  212. package/src/modes/utils/ui-helpers.ts +1 -0
  213. package/src/prompts/tools/computer.md +1 -1
  214. package/src/registry/agent-lifecycle.ts +14 -7
  215. package/src/registry/agent-registry.ts +65 -2
  216. package/src/registry/persisted-agents.ts +341 -16
  217. package/src/sdk.ts +6 -49
  218. package/src/secrets/index.ts +52 -1
  219. package/src/session/agent-session-events.ts +2 -2
  220. package/src/session/agent-session.ts +54 -16
  221. package/src/session/messages.ts +1 -0
  222. package/src/session/prewalk.ts +57 -17
  223. package/src/session/session-context.ts +3 -5
  224. package/src/session/session-entries.ts +10 -0
  225. package/src/session/session-handoff.ts +5 -1
  226. package/src/session/session-listing.ts +1 -1
  227. package/src/session/session-loader.ts +74 -8
  228. package/src/session/session-manager.ts +22 -2
  229. package/src/session/session-paths.ts +57 -9
  230. package/src/session/session-tools.ts +65 -4
  231. package/src/session/todo-tracker.ts +11 -1
  232. package/src/session/turn-recovery.ts +96 -74
  233. package/src/slash-commands/acp-builtins.ts +1 -1
  234. package/src/slash-commands/available-commands.ts +1 -1
  235. package/src/slash-commands/builtin-registry.ts +12 -8
  236. package/src/task/executor.ts +70 -20
  237. package/src/task/index.ts +30 -34
  238. package/src/task/isolation-runner.ts +24 -11
  239. package/src/task/persisted-revive.ts +12 -5
  240. package/src/task/read-only-policy.ts +27 -0
  241. package/src/task/structured-subagent.ts +14 -4
  242. package/src/task/types.ts +4 -0
  243. package/src/tools/auto-generated-guard.ts +1 -1
  244. package/src/tools/bash-interactive.ts +4 -4
  245. package/src/tools/bash-skill-urls.ts +15 -0
  246. package/src/tools/bash.ts +16 -17
  247. package/src/tools/browser/cmux/cmux-tab.ts +66 -18
  248. package/src/tools/browser/launch.ts +41 -12
  249. package/src/tools/browser/readable.ts +9 -9
  250. package/src/tools/browser/tab-supervisor.ts +12 -3
  251. package/src/tools/browser/tab-worker-entry.ts +1 -1
  252. package/src/tools/browser/tab-worker.ts +101 -12
  253. package/src/tools/debug.ts +1 -1
  254. package/src/tools/fetch.ts +1 -1
  255. package/src/tools/jtd-to-json-schema.ts +123 -21
  256. package/src/tools/path-utils.ts +9 -2
  257. package/src/tools/read.ts +21 -9
  258. package/src/tools/run-scope.ts +290 -4
  259. package/src/tools/shell-tokenize.ts +98 -0
  260. package/src/tools/terminal-output.ts +1 -1
  261. package/src/tools/todo.ts +1 -1
  262. package/src/utils/clipboard.ts +10 -2
  263. package/src/utils/external-editor.ts +4 -2
  264. package/src/utils/jj.ts +1 -1
  265. package/src/utils/turndown.ts +1 -2
  266. package/src/vibe/runtime.ts +5 -5
  267. package/src/web/scrapers/arxiv.ts +1 -1
  268. package/src/web/scrapers/go-pkg.ts +1 -1
  269. package/src/web/scrapers/iacr.ts +1 -1
  270. package/src/web/scrapers/readthedocs.ts +2 -1
  271. package/src/web/scrapers/twitter.ts +2 -2
  272. package/src/web/scrapers/types.ts +1 -1
  273. package/src/web/scrapers/wikipedia.ts +1 -1
  274. package/src/web/search/providers/browser-headers.ts +12 -39
  275. package/src/web/search/providers/codex.ts +3 -26
  276. package/src/web/search/providers/ecosia.ts +1 -1
  277. package/src/web/search/providers/exa.ts +1 -1
  278. package/src/web/search/providers/google.ts +1 -1
  279. package/src/web/search/providers/mojeek.ts +1 -1
  280. package/src/web/search/providers/startpage.ts +1 -1
  281. package/src/markit/converters/mammoth.d.ts +0 -24
@@ -8,7 +8,7 @@ import * as fs from "node:fs/promises";
8
8
  import * as os from "node:os";
9
9
  import path from "node:path";
10
10
  import { $env, prompt, Snowflake } from "@oh-my-pi/pi-utils";
11
- import { resolveAgentModelPatterns } from "../config/model-resolver";
11
+ import { resolveAgentModelPatterns, resolveAgentModelSource, resolveExplicitModelRole } from "../config/model-resolver";
12
12
  import type { LocalProtocolOptions } from "../internal-urls";
13
13
  import { registerArtifactsDir } from "../internal-urls/registry-helpers";
14
14
  import { MCPManager } from "../mcp/manager";
@@ -123,6 +123,8 @@ export interface EffectiveSubagentPolicy {
123
123
  agent: AgentDefinition;
124
124
  effectiveAgent: AgentDefinition;
125
125
  modelOverride?: string | string[];
126
+ /** Explicit pre-expansion model role alias selected for this run. */
127
+ modelRole?: string;
126
128
  parentActiveModelPattern?: string;
127
129
  schema: StructuredSubagentSchemaResolution;
128
130
  planMode: boolean;
@@ -278,13 +280,18 @@ export async function resolveEffectiveSubagentPolicy(
278
280
  }
279
281
  const agentModelOverrides = request.session.settings.get("task.agentModelOverrides");
280
282
  const parentActiveModelPattern = request.session.getActiveModelString?.();
281
- const modelOverride = resolveAgentModelPatterns({
282
- settingsOverride: request.model ?? agentModelOverrides[agentName],
283
+ const modelResolution = {
284
+ requestModel: request.model,
285
+ settingsOverride: agentModelOverrides[agentName],
283
286
  agentModel: effectiveAgent.model,
284
287
  settings: request.session.settings,
285
288
  activeModelPattern: parentActiveModelPattern,
286
289
  fallbackModelPattern: request.session.getModelString?.(),
287
- });
290
+ };
291
+ // Keep role identity from the same effective non-empty source that supplies
292
+ // model selection: caller request, settings override, then agent definition.
293
+ const modelRole = resolveExplicitModelRole(resolveAgentModelSource(modelResolution), request.session.settings);
294
+ const modelOverride = resolveAgentModelPatterns(modelResolution);
288
295
  const isolationMode = request.session.settings.get("task.isolation.mode");
289
296
  const isIsolated = request.isolation?.requested === true;
290
297
  if (isIsolated && isolationMode === "none") {
@@ -299,6 +306,7 @@ export async function resolveEffectiveSubagentPolicy(
299
306
  agent,
300
307
  effectiveAgent,
301
308
  modelOverride,
309
+ modelRole,
302
310
  parentActiveModelPattern,
303
311
  schema,
304
312
  planMode,
@@ -394,6 +402,7 @@ function buildExecutorOptions(
394
402
  invokedAt: request.invokedAt,
395
403
  acquiredAt: request.acquiredAt,
396
404
  modelOverride: policy.modelOverride,
405
+ modelRole: policy.modelRole,
397
406
  parentActiveModelPattern: policy.parentActiveModelPattern,
398
407
  thinkingLevel: policy.effectiveAgent.thinkingLevel,
399
408
  effort: request.effort,
@@ -476,6 +485,7 @@ function buildFailureResult(
476
485
  tokens: 0,
477
486
  requests: 0,
478
487
  modelOverride: policy.modelOverride,
488
+ modelRole: policy.modelRole,
479
489
  error: message,
480
490
  };
481
491
  };
package/src/task/types.ts CHANGED
@@ -426,6 +426,8 @@ export interface AgentProgress {
426
426
  cost: number;
427
427
  durationMs: number;
428
428
  modelOverride?: string | string[];
429
+ /** Explicit pre-expansion model role alias selected for this run. */
430
+ modelRole?: string;
429
431
  /** Resolved model display string in the form `<provider>/<id>`, optionally suffixed with `:<thinkingLevel>` when the level was set explicitly. Undefined when the model could not be resolved. */
430
432
  resolvedModel?: string;
431
433
  /** True when {@link resolvedModel} is the target of an active retry fallback (not the originally configured model). Lets observer-only UIs (collab guests, Agent Hub rows with no live session) flag the fallback and keep the provider. */
@@ -495,6 +497,8 @@ export interface SingleResult {
495
497
  /** Model's context window in tokens, when known. */
496
498
  contextWindow?: number;
497
499
  modelOverride?: string | string[];
500
+ /** Explicit pre-expansion model role alias selected for this run. */
501
+ modelRole?: string;
498
502
  /** Resolved model display string in the form `<provider>/<id>`, optionally suffixed with `:<thinkingLevel>` when the level was set explicitly. Omitted from tool-result JSON when undefined to keep wire payloads small. */
499
503
  resolvedModel?: string;
500
504
  /** True when {@link resolvedModel} is the target of an active retry fallback. Mirrors {@link AgentProgress.resolvedModelIsFallback} onto the settled result. */
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import * as path from "node:path";
8
8
  import { isEnoent, peekFile } from "@oh-my-pi/pi-utils";
9
- import { LRUCache } from "lru-cache/raw";
9
+ import { LRUCache } from "@oh-my-pi/pi-utils/lru";
10
10
  import { settings as globalSettings, isSettingsInitialized, type Settings } from "../config/settings";
11
11
  import { getDefault } from "../config/settings-schema";
12
12
  import { ToolError } from "./tool-errors";
@@ -11,8 +11,8 @@ import {
11
11
  visibleWidth,
12
12
  } from "@oh-my-pi/pi-tui";
13
13
  import { sanitizeText } from "@oh-my-pi/pi-utils";
14
- import type * as XtermModule from "@xterm/headless";
15
- import type { Terminal as XtermTerminalType } from "@xterm/headless";
14
+ import type * as XtermModule from "@oh-my-pi/pi-utils/vterm";
15
+ import type { Terminal as XtermTerminalType } from "@oh-my-pi/pi-utils/vterm";
16
16
  import { Settings } from "../config/settings";
17
17
  import type { Theme } from "../modes/theme/theme";
18
18
  import { OutputSink, type OutputSummary } from "../session/streaming-output";
@@ -36,13 +36,13 @@ function normalizeCaptureChunk(chunk: string): string {
36
36
  // source of truth for the final captured output.
37
37
  const MAX_LIVE_WRITE_QUEUE_CHUNKS = 512;
38
38
 
39
- // @xterm/headless is only needed once an interactive PTY session actually starts,
39
+ // The virtual terminal is only needed once an interactive PTY session actually starts,
40
40
  // so it is loaded lazily (and memoized) instead of weighing down CLI startup.
41
41
  let xtermTerminalCtor: typeof XtermModule.Terminal | undefined;
42
42
 
43
43
  async function loadXtermTerminal(): Promise<typeof XtermModule.Terminal> {
44
44
  if (!xtermTerminalCtor) {
45
- const mod = (await import("@xterm/headless")) as typeof XtermModule & { default?: typeof XtermModule };
45
+ const mod = (await import("@oh-my-pi/pi-utils/vterm")) as typeof XtermModule & { default?: typeof XtermModule };
46
46
  xtermTerminalCtor = (mod.default ?? mod).Terminal;
47
47
  }
48
48
  return xtermTerminalCtor;
@@ -1,5 +1,6 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
+ import { resolveContainedPathSync } from "../discovery/contained-path";
3
4
  import type { Skill } from "../extensibility/skills";
4
5
  import { type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
5
6
  import { validateRelativePath } from "../internal-urls/skill-protocol";
@@ -89,6 +90,20 @@ export function resolveSkillUrlToPath(url: string, skills: readonly Skill[]): st
89
90
  if (!resolvedPath.startsWith(resolvedBaseDir + path.sep) && resolvedPath !== resolvedBaseDir) {
90
91
  throw new ToolError("Path traversal is not allowed in skill:// URLs");
91
92
  }
93
+ // Agent Plugin skills (§4.1): the resource must canonically resolve within
94
+ // the plugin root. Fail closed: a dangling or unresolvable path is rejected
95
+ // rather than handed to bash, where writing through it could create the
96
+ // outside target. Symlinks may target other files inside the same package.
97
+ if (skill.containRoot) {
98
+ const contained = resolveContainedPathSync(skill.containRoot, resolvedPath);
99
+ if (contained.status === "outside") {
100
+ throw new ToolError(`skill:// path resolves outside the plugin root: ${url}`);
101
+ }
102
+ if (contained.status === "missing") {
103
+ throw new ToolError(`skill:// path does not exist: ${url}`);
104
+ }
105
+ return contained.realPath;
106
+ }
92
107
 
93
108
  return resolvedPath;
94
109
  }
package/src/tools/bash.ts CHANGED
@@ -49,7 +49,7 @@ import {
49
49
  previewWindowRows,
50
50
  replaceTabs,
51
51
  } from "./render-utils";
52
- import { tokenizeShellSegments } from "./shell-tokenize";
52
+ import { extractLeadingCdTarget, tokenizeShellSegments } from "./shell-tokenize";
53
53
  import { ToolAbortError, ToolError } from "./tool-errors";
54
54
  import { toolResult } from "./tool-result";
55
55
  import { clampTimeout, TOOL_TIMEOUTS } from "./tool-timeouts";
@@ -384,8 +384,8 @@ function normalizeBashEnv(env: Record<string, string> | undefined): Record<strin
384
384
  return normalized;
385
385
  }
386
386
 
387
- function escapeBashEnvValueForDisplay(value: string): string {
388
- return value
387
+ function escapeBashEnvValueForDisplay(value: unknown): string {
388
+ return String(value)
389
389
  .replaceAll("\\", "\\\\")
390
390
  .replaceAll("\n", "\\n")
391
391
  .replaceAll("\r", "\\r")
@@ -395,7 +395,7 @@ function escapeBashEnvValueForDisplay(value: string): string {
395
395
  .replaceAll("`", "\\`");
396
396
  }
397
397
 
398
- function formatBashEnvAssignments(env: Record<string, string> | undefined): string {
398
+ function formatBashEnvAssignments(env: Record<string, unknown> | undefined): string {
399
399
  if (!env || Object.keys(env).length === 0) return "";
400
400
  return Object.entries(env)
401
401
  .sort(([a], [b]) => a.localeCompare(b))
@@ -960,17 +960,16 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
960
960
  let command = rawCommand;
961
961
  const env = normalizeBashEnv(rawEnv);
962
962
 
963
- // Extract leading `cd <path> && ...` into cwd when the model ignores the cwd parameter.
964
- // Constrained to a single line so a `&&` that sits on a later line of a multiline
965
- // script can't pull the entire script into the "cwd" capture.
963
+ // Extract a leading `cd <path> && ...` into cwd when the model ignores the
964
+ // cwd parameter. The scanner captures only a single path token and defers
965
+ // to the shell for anything else (redirects, extra args, shell expansion),
966
+ // so it never absorbs shell syntax like `cd /tmp 2>/dev/null && ...` into
967
+ // the structured cwd. Constrained to a top-level `&&` on the first line.
966
968
  if (!cwd) {
967
- const cdMatch = command.match(/^cd[ \t]+((?:[^&\\\n\r]|\\.)+?)[ \t]*&&[ \t]*/);
968
- // Skip extraction when the path needs shell expansion ($VAR, $(...),
969
- // backticks) — resolveToCwd only expands `~`, so routing those through
970
- // cwd would reject commands the shell itself handles fine.
971
- if (cdMatch && !/[$`(]/.test(cdMatch[1])) {
972
- cwd = cdMatch[1].trim().replace(/^["']|["']$/g, "");
973
- command = command.slice(cdMatch[0].length);
969
+ const cd = extractLeadingCdTarget(command);
970
+ if (cd) {
971
+ cwd = cd.path;
972
+ command = cd.rest;
974
973
  }
975
974
  }
976
975
  if (asyncRequested && !this.#asyncEnabled) {
@@ -1498,7 +1497,7 @@ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSc
1498
1497
  // =============================================================================
1499
1498
  export interface BashRenderArgs {
1500
1499
  command?: string;
1501
- env?: Record<string, string>;
1500
+ env?: Record<string, unknown>;
1502
1501
  timeout?: number;
1503
1502
  cwd?: string;
1504
1503
  __partialJson?: string;
@@ -1522,7 +1521,7 @@ export interface ShellRendererConfig<TArgs> {
1522
1521
  resolveTitle: (args: TArgs | undefined, options: RenderResultOptions) => string;
1523
1522
  resolveCommand?: (args: TArgs | undefined) => string | undefined;
1524
1523
  resolveCwd?: (args: TArgs | undefined) => string | undefined;
1525
- resolveEnv?: (args: TArgs | undefined) => Record<string, string> | undefined;
1524
+ resolveEnv?: (args: TArgs | undefined) => Record<string, unknown> | undefined;
1526
1525
  showHeader?: boolean;
1527
1526
  }
1528
1527
 
@@ -1532,7 +1531,7 @@ function getPartialJson<TArgs>(args: TArgs | undefined): string | undefined {
1532
1531
  return typeof value === "string" ? value : undefined;
1533
1532
  }
1534
1533
 
1535
- export function getBashEnvForDisplay(args: BashRenderArgs): Record<string, string> | undefined {
1534
+ export function getBashEnvForDisplay(args: BashRenderArgs): Record<string, unknown> | undefined {
1536
1535
  // The parsed args don't always mirror the exact current stream prefix, so recover
1537
1536
  // env from the raw JSON buffer to surface `NAME="..." cmd` in the preview as it
1538
1537
  // streams rather than only once the args object finishes.
@@ -8,7 +8,16 @@ import { resizeImage } from "../../../utils/image-resize";
8
8
  import type { ToolSession } from "../../index";
9
9
  import { resolveToCwd } from "../../path-utils";
10
10
  import { formatScreenshot } from "../../render-utils";
11
- import { bindRunFacade, resolvePredicateTimeout, type WaitPredicateOptions, waitForRun } from "../../run-scope";
11
+ import {
12
+ bindRunFacade,
13
+ isBrowserRunOwnedRejection,
14
+ markBrowserRunRejection,
15
+ observeBrowserRunPromise,
16
+ resolvePredicateTimeout,
17
+ type WaitPredicateOptions,
18
+ waitForRun,
19
+ withBrowserPromiseCombinatorTracking,
20
+ } from "../../run-scope";
12
21
  import { ToolAbortError, ToolError, throwIfAborted } from "../../tool-errors";
13
22
  import { type AriaSnapshotOptions, assertSelectorString, buildAriaSnapshotScript } from "../aria/aria-snapshot";
14
23
  import { DEFAULT_VIEWPORT } from "../launch";
@@ -1368,6 +1377,7 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
1368
1377
  const signal = AbortSignal.any(
1369
1378
  opts.signal ? [timeoutSignal, opts.signal, runAc.signal] : [timeoutSignal, runAc.signal],
1370
1379
  );
1380
+ const runEndedError = postmortem.markExpectedCleanupError(new ToolAbortError("Browser run ended"));
1371
1381
  const output = new RunOutput();
1372
1382
  const screenshots: ScreenshotResult[] = [];
1373
1383
  const runId = crypto.randomUUID();
@@ -1382,6 +1392,28 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
1382
1392
  // handler to this promise; keep its armed rejection from surfacing as an
1383
1393
  // unhandled rejection — the postmortem-fatal path this run guards against.
1384
1394
  cancelRejection.catch(() => {});
1395
+ const rejectionOwner = {};
1396
+ const { promise: floatingFailure, reject: rejectFloatingFailure } = Promise.withResolvers<never>();
1397
+ floatingFailure.catch(() => {});
1398
+ let runActive = true;
1399
+ let hasFloatingFailure = false;
1400
+ const recordFloatingFailure = (reason: unknown): void => {
1401
+ if (hasFloatingFailure || postmortem.isExpectedCleanupError(reason)) return;
1402
+ const message = reason instanceof Error ? reason.message : String(reason);
1403
+ if (!runActive) {
1404
+ logger.warn("Unhandled rejection after browser run ended", { runId, error: message });
1405
+ return;
1406
+ }
1407
+ hasFloatingFailure = true;
1408
+ const error = new Error(`Unhandled rejection (missing await?): ${message}`, { cause: reason });
1409
+ if (reason instanceof Error) error.name = reason.name;
1410
+ rejectFloatingFailure(error);
1411
+ };
1412
+ const uninstallRejectionInterceptor = postmortem.interceptUnhandledRejections(reason => {
1413
+ if (!isBrowserRunOwnedRejection(reason, rejectionOwner, `cmux-run-${runId}.js`)) return false;
1414
+ recordFloatingFailure(reason);
1415
+ return true;
1416
+ });
1385
1417
  const onAbort = (): void => {
1386
1418
  if (timeoutSignal.aborted) {
1387
1419
  reject(new ToolError(`Browser code execution timed out after ${opts.timeoutMs}ms`));
@@ -1402,24 +1434,30 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
1402
1434
  // Keep both inside try so a concurrent in-process eval/browser run surfaces as
1403
1435
  // a rejected promise the supervisor can report, never an unhandled rejection.
1404
1436
  runtime.setCwd(opts.snapshot.cwd);
1405
- const runTab = bindRunFacade(tab, signal);
1437
+ const runTab = bindRunFacade(tab, signal, rejectionOwner, recordFloatingFailure);
1406
1438
  runtime.setRunScope({
1407
- page: bindRunFacade(tab.page, signal),
1408
- browser: bindRunFacade(tab.browser, signal),
1439
+ page: bindRunFacade(tab.page, signal, rejectionOwner, recordFloatingFailure),
1440
+ browser: bindRunFacade(tab.browser, signal, rejectionOwner, recordFloatingFailure),
1409
1441
  tab: runTab,
1410
1442
  assert: (cond: unknown, text?: string): void => {
1411
1443
  if (!cond) throw new ToolError(text ?? "Assertion failed");
1412
1444
  },
1413
1445
  wait: (msOrPredicate: number | (() => unknown), waitOpts?: WaitPredicateOptions): Promise<unknown> =>
1414
- waitForRun(
1415
- msOrPredicate,
1416
- signal,
1417
- typeof msOrPredicate === "number"
1418
- ? waitOpts
1419
- : {
1420
- timeout: resolvePredicateTimeout(opts.timeoutMs, waitOpts?.timeout),
1421
- interval: waitOpts?.interval,
1422
- },
1446
+ observeBrowserRunPromise(
1447
+ waitForRun(
1448
+ msOrPredicate,
1449
+ signal,
1450
+ typeof msOrPredicate === "number"
1451
+ ? waitOpts
1452
+ : {
1453
+ timeout: resolvePredicateTimeout(opts.timeoutMs, waitOpts?.timeout),
1454
+ interval: waitOpts?.interval,
1455
+ },
1456
+ ).catch(error => {
1457
+ throw markBrowserRunRejection(error, rejectionOwner);
1458
+ }),
1459
+ rejectionOwner,
1460
+ recordFloatingFailure,
1423
1461
  ),
1424
1462
  });
1425
1463
 
@@ -1444,16 +1482,24 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
1444
1482
  let runError: unknown;
1445
1483
  let runFailed = false;
1446
1484
  try {
1447
- returnValue = await Promise.race([
1448
- runtime.run(opts.code, filename, hooks, { runId, cwd: opts.snapshot.cwd }),
1449
- cancelRejection,
1450
- ]);
1485
+ returnValue = await withBrowserPromiseCombinatorTracking(
1486
+ rejectionOwner,
1487
+ recordFloatingFailure,
1488
+ async () =>
1489
+ await Promise.race([
1490
+ runtime.run(opts.code, filename, hooks, { runId, cwd: opts.snapshot.cwd }),
1491
+ cancelRejection,
1492
+ floatingFailure,
1493
+ ]),
1494
+ );
1451
1495
  } catch (error) {
1452
1496
  runFailed = true;
1453
1497
  runError = error;
1454
1498
  }
1499
+ runAc.abort(runEndedError);
1455
1500
  // Let rejection callbacks run while this run can still own guest-created promises.
1456
1501
  await Bun.sleep(0);
1502
+ if (hasFloatingFailure && !runFailed) await floatingFailure;
1457
1503
  if (runFailed) {
1458
1504
  for (const reason of activeRun.floatingRejections) {
1459
1505
  logger.warn("Unhandled rejection accompanied a failed cmux browser run", { filename, error: reason });
@@ -1470,8 +1516,10 @@ export async function runCmuxCode(tab: CmuxTab, opts: RunCmuxCodeOptions): Promi
1470
1516
  }
1471
1517
  return { displays: output.finish(), returnValue: cloneSafe(returnValue), screenshots };
1472
1518
  } finally {
1519
+ runActive = false;
1520
+ uninstallRejectionInterceptor();
1473
1521
  signal.removeEventListener("abort", onAbort);
1474
- runAc.abort(postmortem.markExpectedCleanupError(new ToolAbortError("Browser run ended")));
1522
+ runAc.abort(runEndedError);
1475
1523
  activeCmuxRuns.delete(filename);
1476
1524
  rememberCmuxRunFile(filename);
1477
1525
  tab.clearRunContext();
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { $which, getPuppeteerDir, logger, removeWithRetries } from "@oh-my-pi/pi-utils";
5
- import type * as BrowsersNs from "@puppeteer/browsers";
5
+ import type * as BrowsersNs from "@oh-my-pi/pi-utils/browsers";
6
6
  import type { Browser, CDPSession, Page, default as Puppeteer, Target } from "puppeteer-core";
7
7
  import stealthTamperingScript from "../puppeteer/00_stealth_tampering.txt" with { type: "text" };
8
8
  import stealthActivityScript from "../puppeteer/01_stealth_activity.txt" with { type: "text" };
@@ -115,7 +115,7 @@ export async function loadPuppeteerInWorker(safeDir: string): Promise<typeof Pup
115
115
  let browsersModule: typeof BrowsersNs | undefined;
116
116
  async function loadBrowsers(): Promise<typeof BrowsersNs> {
117
117
  if (!browsersModule) {
118
- browsersModule = await import("@puppeteer/browsers");
118
+ browsersModule = await import("@oh-my-pi/pi-utils/browsers");
119
119
  }
120
120
  return browsersModule;
121
121
  }
@@ -133,7 +133,7 @@ let chromiumExecutablePromise: Promise<string | undefined> | undefined;
133
133
  export async function ensureChromiumExecutable(): Promise<string | undefined> {
134
134
  const envPath = process.env.PUPPETEER_EXECUTABLE_PATH;
135
135
  if (envPath) return envPath;
136
- const sysChrome = resolveSystemChromium();
136
+ const sysChrome = await resolveSystemChromium();
137
137
  if (sysChrome) return sysChrome;
138
138
  if (chromiumExecutablePromise) return chromiumExecutablePromise;
139
139
 
@@ -166,13 +166,13 @@ export async function ensureChromiumExecutable(): Promise<string | undefined> {
166
166
  buildId,
167
167
  cacheDir,
168
168
  platform,
169
- downloadProgressCallback: (downloaded, total) => {
170
- if (total <= 0) return;
171
- const pct = Math.floor((downloaded / total) * 100);
172
- if (pct >= lastReportedPercent + 10 || downloaded === total) {
169
+ downloadProgressCallback: ({ downloadedBytes, totalBytes }) => {
170
+ if (totalBytes <= 0) return;
171
+ const pct = Math.floor((downloadedBytes / totalBytes) * 100);
172
+ if (pct >= lastReportedPercent + 10 || downloadedBytes === totalBytes) {
173
173
  lastReportedPercent = pct;
174
174
  logger.debug(
175
- `Chromium download: ${pct}% (${Math.round(downloaded / 1_000_000)} / ${Math.round(total / 1_000_000)} MB)`,
175
+ `Chromium download: ${pct}% (${Math.round(downloadedBytes / 1_000_000)} / ${Math.round(totalBytes / 1_000_000)} MB)`,
176
176
  );
177
177
  }
178
178
  },
@@ -193,7 +193,32 @@ let resolvedChromium: string | null | undefined; // undefined = unchecked; null
193
193
  function isExecutableFile(p: string): boolean {
194
194
  try {
195
195
  const st = fs.statSync(p);
196
- return st.isFile();
196
+ if (!st.isFile()) return false;
197
+ if (process.platform === "win32") return true;
198
+ fs.accessSync(p, fs.constants.X_OK);
199
+ return true;
200
+ } catch {
201
+ return false;
202
+ }
203
+ }
204
+
205
+ async function isChromiumExecutable(p: string): Promise<boolean> {
206
+ if (!isExecutableFile(p)) return false;
207
+ try {
208
+ const probeTimeoutMs = 3000;
209
+ const proc = Bun.spawn([p, "--version"], {
210
+ stdout: "pipe",
211
+ stderr: "ignore",
212
+ signal: AbortSignal.timeout(probeTimeoutMs),
213
+ killSignal: "SIGKILL",
214
+ });
215
+ const stdout = await Promise.race([
216
+ new Response(proc.stdout).text(),
217
+ Bun.sleep(probeTimeoutMs + 500).then(() => null),
218
+ ]);
219
+ if (stdout === null) return false;
220
+ await proc.exited;
221
+ return proc.exitCode === 0 && /Chrom|Edg/i.test(stdout);
197
222
  } catch {
198
223
  return false;
199
224
  }
@@ -278,13 +303,13 @@ function systemChromiumCandidates(
278
303
  return candidates;
279
304
  }
280
305
 
281
- function resolveSystemChromium(): string | undefined {
306
+ async function resolveSystemChromium(): Promise<string | undefined> {
282
307
  if (resolvedChromium !== undefined) return resolvedChromium ?? undefined;
283
308
  const seen = new Set<string>();
284
309
  for (const candidate of systemChromiumCandidates()) {
285
310
  if (!candidate || seen.has(candidate)) continue;
286
311
  seen.add(candidate);
287
- if (isExecutableFile(candidate)) {
312
+ if (await isChromiumExecutable(candidate)) {
288
313
  resolvedChromium = candidate;
289
314
  logger.debug("Using system Chrome/Chromium", { path: candidate });
290
315
  return candidate;
@@ -889,11 +914,15 @@ export async function applyStealthPatches(
889
914
  export function systemChromiumCandidatesForTest(
890
915
  platform: NodeJS.Platform = process.platform,
891
916
  home?: string,
892
- which?: (name: string) => string | undefined,
917
+ which?: (name: string) => string | null | undefined,
893
918
  ): string[] {
894
919
  return systemChromiumCandidates(platform, home, which);
895
920
  }
896
921
 
922
+ export async function chromiumExecutableProbeForTest(executablePath: string): Promise<boolean> {
923
+ return isChromiumExecutable(executablePath);
924
+ }
925
+
897
926
  export function stealthIgnoreDefaultArgsForTest(executablePath: string | undefined): string[] {
898
927
  return stealthIgnoreDefaultArgs(executablePath);
899
928
  }
@@ -1,5 +1,5 @@
1
- import type * as ReadabilityNs from "@mozilla/readability";
2
- import type * as LinkedomNs from "linkedom";
1
+ import type * as DomNs from "@oh-my-pi/pi-utils/dom";
2
+ import type * as ReadabilityNs from "@oh-my-pi/pi-utils/readability";
3
3
  import { htmlToBasicMarkdown } from "../../web/scrapers/types";
4
4
 
5
5
  export type ReadableFormat = "text" | "markdown";
@@ -23,17 +23,17 @@ function normalize(text: string | null | undefined): string | undefined {
23
23
  let readabilityModule: typeof ReadabilityNs | undefined;
24
24
  async function loadReadability(): Promise<typeof ReadabilityNs> {
25
25
  if (!readabilityModule) {
26
- readabilityModule = await import("@mozilla/readability");
26
+ readabilityModule = await import("@oh-my-pi/pi-utils/readability");
27
27
  }
28
28
  return readabilityModule;
29
29
  }
30
30
 
31
- let linkedomModule: typeof LinkedomNs | undefined;
32
- async function loadLinkedom(): Promise<typeof LinkedomNs> {
33
- if (!linkedomModule) {
34
- linkedomModule = await import("linkedom");
31
+ let domModule: typeof DomNs | undefined;
32
+ async function loadDom(): Promise<typeof DomNs> {
33
+ if (!domModule) {
34
+ domModule = await import("@oh-my-pi/pi-utils/dom");
35
35
  }
36
- return linkedomModule;
36
+ return domModule;
37
37
  }
38
38
 
39
39
  /**
@@ -47,7 +47,7 @@ export async function extractReadableFromHtml(
47
47
  url: string,
48
48
  format: ReadableFormat,
49
49
  ): Promise<ReadableResult | null> {
50
- const [{ parseHTML }, { Readability }] = await Promise.all([loadLinkedom(), loadReadability()]);
50
+ const [{ parseHTML }, { Readability }] = await Promise.all([loadDom(), loadReadability()]);
51
51
  const { document } = parseHTML(html);
52
52
 
53
53
  // --- Primary: Readability article extraction ---
@@ -134,6 +134,7 @@ const tabs = new Map<string, TabSession>();
134
134
  // awaits) cannot interleave and leak a worker + browser refCount.
135
135
  const acquireChains = new Map<string, Promise<void>>();
136
136
  const GRACE_MS = 750;
137
+ const WORKER_INIT_TIMEOUT_MS = 15_000;
137
138
  // Names of tabs the supervisor force-killed (timeout past grace, failed recycle),
138
139
  // mapped to the kill reason. Lets the next `run` on that name explain WHY the tab
139
140
  // vanished instead of a bare "not alive". Cleared when the name is opened again.
@@ -272,7 +273,11 @@ async function acquireTabImpl(
272
273
  }
273
274
  let info: ReadyInfo;
274
275
  try {
275
- info = await initializeTabWorker(worker, initPayload, opts.timeoutMs + GRACE_MS);
276
+ info = await initializeTabWorker(
277
+ worker,
278
+ initPayload,
279
+ Math.max(WORKER_INIT_TIMEOUT_MS, opts.timeoutMs + GRACE_MS),
280
+ );
276
281
  } catch (error) {
277
282
  // `BuildMessage`-class failures arrive asynchronously via the worker's `error` event,
278
283
  // after `spawnTabWorker`'s synchronous try/catch has already returned. Fall back to
@@ -287,7 +292,11 @@ async function acquireTabImpl(
287
292
  });
288
293
  worker = await spawnInlineWorker();
289
294
  try {
290
- info = await initializeTabWorker(worker, initPayload, opts.timeoutMs + GRACE_MS);
295
+ info = await initializeTabWorker(
296
+ worker,
297
+ initPayload,
298
+ Math.max(WORKER_INIT_TIMEOUT_MS, opts.timeoutMs + GRACE_MS),
299
+ );
291
300
  } catch (inlineError) {
292
301
  await worker.terminate().catch(() => undefined);
293
302
  if (tempHold || browser.refCount === 0) await releaseBrowser(browser, { kill: false });
@@ -1014,7 +1023,7 @@ async function spawnInlineWorker(): Promise<WorkerHandle> {
1014
1023
  close: () => {},
1015
1024
  };
1016
1025
  const { WorkerCore } = await import("./tab-worker");
1017
- new WorkerCore(workerTransport);
1026
+ new WorkerCore(workerTransport, false);
1018
1027
  return {
1019
1028
  mode: "inline",
1020
1029
  send: msg =>
@@ -26,4 +26,4 @@ const transport: Transport = {
26
26
  },
27
27
  };
28
28
 
29
- new WorkerCore(transport);
29
+ new WorkerCore(transport, true);