@oh-my-pi/pi-coding-agent 16.1.8 → 16.1.9

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 (38) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/cli.js +4621 -3814
  3. package/dist/types/cli/flag-tables.d.ts +17 -0
  4. package/dist/types/cli-commands.d.ts +4 -2
  5. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  6. package/dist/types/modes/interactive-mode.d.ts +1 -1
  7. package/dist/types/modes/types.d.ts +1 -1
  8. package/dist/types/session/agent-session.d.ts +13 -0
  9. package/dist/types/tools/browser/launch.d.ts +5 -0
  10. package/package.json +12 -12
  11. package/src/cli/args.ts +0 -1
  12. package/src/cli/flag-tables.ts +42 -0
  13. package/src/cli/profile-bootstrap.ts +1 -11
  14. package/src/cli-commands.ts +48 -3
  15. package/src/config/model-registry.ts +6 -7
  16. package/src/internal-urls/docs-index.generated.txt +1 -1
  17. package/src/lsp/client.ts +39 -25
  18. package/src/modes/controllers/command-controller.ts +18 -3
  19. package/src/modes/controllers/mcp-command-controller.ts +40 -12
  20. package/src/modes/interactive-mode.ts +1 -1
  21. package/src/modes/types.ts +1 -1
  22. package/src/session/agent-session.ts +36 -1
  23. package/src/slash-commands/builtin-registry.ts +24 -66
  24. package/src/tools/browser/launch.ts +107 -31
  25. package/src/tools/puppeteer/00_stealth_tampering.txt +16 -35
  26. package/src/tools/puppeteer/01_stealth_activity.txt +79 -19
  27. package/src/tools/puppeteer/02_stealth_hairline.txt +57 -11
  28. package/src/tools/puppeteer/03_stealth_botd.txt +10 -14
  29. package/src/tools/puppeteer/04_stealth_iframe.txt +156 -63
  30. package/src/tools/puppeteer/05_stealth_webgl.txt +222 -64
  31. package/src/tools/puppeteer/06_stealth_screen.txt +255 -67
  32. package/src/tools/puppeteer/07_stealth_fonts.txt +17 -15
  33. package/src/tools/puppeteer/08_stealth_audio.txt +32 -20
  34. package/src/tools/puppeteer/09_stealth_locale.txt +45 -40
  35. package/src/tools/puppeteer/10_stealth_plugins.txt +12 -8
  36. package/src/tools/puppeteer/11_stealth_hardware.txt +57 -6
  37. package/src/tools/puppeteer/12_stealth_codecs.txt +20 -18
  38. package/src/tools/puppeteer/13_stealth_worker.txt +206 -45
@@ -124,3 +124,20 @@ export declare const PROFILE_BOOTSTRAP_BOUNDARY_ARG = "--omp-profile-boundary";
124
124
  * fires for `--`-prefixed tokens.
125
125
  */
126
126
  export declare const VALUELESS_FLAGS: ReadonlySet<string>;
127
+ /**
128
+ * Whether a bare long option (`--xxx`, no `=`) is unclassified — not a known
129
+ * string-, optional-, or value-less flag. The bootstrap and subcommand
130
+ * resolver treat these as possible extension string flags that may consume a
131
+ * value-like successor (the extension flag table is not yet loaded). Shared so
132
+ * both call sites classify identically.
133
+ */
134
+ export declare function isUnknownLongValueCandidate(arg: string): boolean;
135
+ /**
136
+ * Whether a leading option `flag` consumes the following argv token `next` as
137
+ * its value, applying the same contract as `extractProfileFlags` / `parseArgs`.
138
+ * Single source of truth so subcommand detection ({@link resolveCliArgv}) skips
139
+ * a flag's value instead of mistaking it for the subcommand — `omp --model acp`
140
+ * means model `acp`, not the `acp` subcommand, exactly as the launch parser
141
+ * reads it.
142
+ */
143
+ export declare function flagConsumesValue(flag: string, next: string | undefined): boolean;
@@ -25,7 +25,9 @@ export type ResolvedCliArgv = {
25
25
  };
26
26
  /**
27
27
  * Decide what the CLI runner should do with raw argv: reject bare reserved
28
- * management words, pass help/version through untouched, and route everything
29
- * that is not a known subcommand to `launch`.
28
+ * management words, pass help/version through untouched, route a recognized
29
+ * subcommand (even behind leading global flags like `--approval-mode=yolo`) to
30
+ * that command with the flags preserved, and forward everything else to
31
+ * `launch` (#2970).
30
32
  */
31
33
  export declare function resolveCliArgv(argv: string[]): ResolvedCliArgv;
@@ -11,7 +11,7 @@ export declare class CommandController {
11
11
  constructor(ctx: InteractiveModeContext);
12
12
  openInBrowser(urlOrPath: string): void;
13
13
  handleExportCommand(text: string): Promise<void>;
14
- handleDumpCommand(): void;
14
+ handleDumpCommand(): Promise<void>;
15
15
  handleAdvisorDumpCommand(isRaw?: boolean): void;
16
16
  handleDebugTranscriptCommand(): Promise<void>;
17
17
  handleShareCommand(): Promise<void>;
@@ -271,7 +271,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
271
271
  findLastAssistantMessage(): AssistantMessage | undefined;
272
272
  extractAssistantText(message: AssistantMessage): string;
273
273
  handleExportCommand(text: string): Promise<void>;
274
- handleDumpCommand(): void;
274
+ handleDumpCommand(): Promise<void>;
275
275
  handleAdvisorDumpCommand(isRaw?: boolean): void;
276
276
  handleDebugTranscriptCommand(): Promise<void>;
277
277
  handleShareCommand(): Promise<void>;
@@ -270,7 +270,7 @@ export interface InteractiveModeContext {
270
270
  handleHotkeysCommand(): void;
271
271
  handleToolsCommand(): void;
272
272
  handleContextCommand(): void;
273
- handleDumpCommand(): void;
273
+ handleDumpCommand(): Promise<void>;
274
274
  handleAdvisorDumpCommand(isRaw?: boolean): void;
275
275
  handleDebugTranscriptCommand(): Promise<void>;
276
276
  handleClearCommand(): Promise<void>;
@@ -1203,6 +1203,19 @@ export declare class AgentSession {
1203
1203
  * `### Tool Call`/`### Tool Result`).
1204
1204
  */
1205
1205
  formatSessionAsText(): string;
1206
+ /**
1207
+ * Dump the current session's LLM-facing request context as JSON to a
1208
+ * auto-named file in `os.tmpdir()`. This is the synchronous
1209
+ * `convertToLlm`-boundary snapshot — system prompt, tools (wire schemas),
1210
+ * thinking/service tier, and converted messages — with no network round-trip
1211
+ * and no arming flag, so advisor/side requests cannot intercept it.
1212
+ *
1213
+ * The file persists on disk and may contain the same raw context/secrets
1214
+ * as `/dump`; treat the path accordingly.
1215
+ *
1216
+ * @returns the written file path, or `undefined` when there are no messages.
1217
+ */
1218
+ dumpLlmRequestToTmpDir(): Promise<string | undefined>;
1206
1219
  /**
1207
1220
  * Enable or disable the advisor for this session. The setting is overridden for the session,
1208
1221
  * and the runtime is started or stopped to match.
@@ -37,9 +37,14 @@ export interface UserAgentOverride {
37
37
  version: string;
38
38
  }>;
39
39
  fullVersion: string;
40
+ fullVersionList: Array<{
41
+ brand: string;
42
+ version: string;
43
+ }>;
40
44
  platform: string;
41
45
  platformVersion: string;
42
46
  architecture: string;
47
+ bitness: string;
43
48
  model: string;
44
49
  mobile: boolean;
45
50
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "16.1.8",
4
+ "version": "16.1.9",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -48,17 +48,17 @@
48
48
  "@agentclientprotocol/sdk": "0.25.0",
49
49
  "@babel/parser": "^7.29.7",
50
50
  "@mozilla/readability": "^0.6.0",
51
- "@oh-my-pi/hashline": "16.1.8",
52
- "@oh-my-pi/omp-stats": "16.1.8",
53
- "@oh-my-pi/pi-agent-core": "16.1.8",
54
- "@oh-my-pi/pi-ai": "16.1.8",
55
- "@oh-my-pi/pi-catalog": "16.1.8",
56
- "@oh-my-pi/pi-mnemopi": "16.1.8",
57
- "@oh-my-pi/pi-natives": "16.1.8",
58
- "@oh-my-pi/pi-tui": "16.1.8",
59
- "@oh-my-pi/pi-utils": "16.1.8",
60
- "@oh-my-pi/pi-wire": "16.1.8",
61
- "@oh-my-pi/snapcompact": "16.1.8",
51
+ "@oh-my-pi/hashline": "16.1.9",
52
+ "@oh-my-pi/omp-stats": "16.1.9",
53
+ "@oh-my-pi/pi-agent-core": "16.1.9",
54
+ "@oh-my-pi/pi-ai": "16.1.9",
55
+ "@oh-my-pi/pi-catalog": "16.1.9",
56
+ "@oh-my-pi/pi-mnemopi": "16.1.9",
57
+ "@oh-my-pi/pi-natives": "16.1.9",
58
+ "@oh-my-pi/pi-tui": "16.1.9",
59
+ "@oh-my-pi/pi-utils": "16.1.9",
60
+ "@oh-my-pi/pi-wire": "16.1.9",
61
+ "@oh-my-pi/snapcompact": "16.1.9",
62
62
  "@opentelemetry/api": "^1.9.1",
63
63
  "@opentelemetry/context-async-hooks": "^2.7.1",
64
64
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
package/src/cli/args.ts CHANGED
@@ -298,7 +298,6 @@ export function getExtraHelpText(): string {
298
298
  OPENCODE_API_KEY - OpenCode Zen/OpenCode Go models
299
299
  CURSOR_ACCESS_TOKEN - Cursor AI models
300
300
  AI_GATEWAY_API_KEY - Vercel AI Gateway
301
- WAFER_PASS_API_KEY - Wafer Pass (flat-rate subscription; GLM-5.1, Qwen3.5)
302
301
  WAFER_SERVERLESS_API_KEY - Wafer Serverless (pay-as-you-go)
303
302
 
304
303
  ${chalk.dim("# Cloud Providers")}
@@ -278,3 +278,45 @@ export const VALUELESS_FLAGS: ReadonlySet<string> = new Set([
278
278
  "--auto-approve",
279
279
  "--yolo",
280
280
  ]);
281
+
282
+ /**
283
+ * Whether a bare long option (`--xxx`, no `=`) is unclassified — not a known
284
+ * string-, optional-, or value-less flag. The bootstrap and subcommand
285
+ * resolver treat these as possible extension string flags that may consume a
286
+ * value-like successor (the extension flag table is not yet loaded). Shared so
287
+ * both call sites classify identically.
288
+ */
289
+ export function isUnknownLongValueCandidate(arg: string): boolean {
290
+ return (
291
+ arg.startsWith("--") &&
292
+ !arg.includes("=") &&
293
+ !STRING_VALUE_FLAGS.has(arg) &&
294
+ !OPTIONAL_VALUE_FLAGS.has(arg) &&
295
+ !VALUELESS_FLAGS.has(arg)
296
+ );
297
+ }
298
+
299
+ /**
300
+ * Whether a leading option `flag` consumes the following argv token `next` as
301
+ * its value, applying the same contract as `extractProfileFlags` / `parseArgs`.
302
+ * Single source of truth so subcommand detection ({@link resolveCliArgv}) skips
303
+ * a flag's value instead of mistaking it for the subcommand — `omp --model acp`
304
+ * means model `acp`, not the `acp` subcommand, exactly as the launch parser
305
+ * reads it.
306
+ */
307
+ export function flagConsumesValue(flag: string, next: string | undefined): boolean {
308
+ // `--flag=value` carries its own value inline.
309
+ if (flag.startsWith("--") && flag.includes("=")) return false;
310
+ if (next === undefined) return false;
311
+ // Known string flags consume any successor, even a flag-looking one
312
+ // (`--system-prompt --foo` ⇒ the system prompt is literally `--foo`).
313
+ if (STRING_VALUE_FLAGS.has(flag)) return true;
314
+ const valueLike = !next.startsWith("-");
315
+ if (EXTENSION_SHADOWABLE_STRING_FLAGS.has(flag)) return valueLike;
316
+ if (OPTIONAL_VALUE_FLAGS.has(flag)) {
317
+ const config = OPTIONAL_FLAGS[flag];
318
+ return valueLike && !(config.rejectEmpty === true && next.length === 0);
319
+ }
320
+ if (isUnknownLongValueCandidate(flag)) return valueLike;
321
+ return false;
322
+ }
@@ -36,27 +36,17 @@
36
36
  import { isSubcommand } from "../cli-commands";
37
37
  import {
38
38
  EXTENSION_SHADOWABLE_STRING_FLAGS,
39
+ isUnknownLongValueCandidate,
39
40
  OPTIONAL_FLAGS,
40
41
  OPTIONAL_VALUE_FLAGS,
41
42
  PROFILE_BOOTSTRAP_BOUNDARY_ARG,
42
43
  STRING_VALUE_FLAGS,
43
- VALUELESS_FLAGS,
44
44
  } from "./flag-tables";
45
45
 
46
46
  function isProfileBootstrapSubcommand(arg: string): boolean {
47
47
  return arg === "launch" || arg === "acp";
48
48
  }
49
49
 
50
- function isUnknownLongValueCandidate(arg: string): boolean {
51
- return (
52
- arg.startsWith("--") &&
53
- !arg.includes("=") &&
54
- !STRING_VALUE_FLAGS.has(arg) &&
55
- !OPTIONAL_VALUE_FLAGS.has(arg) &&
56
- !VALUELESS_FLAGS.has(arg)
57
- );
58
- }
59
-
60
50
  function needsBoundaryAfterGlobalStrip(stripped: readonly string[]): boolean {
61
51
  const previous = stripped[stripped.length - 1];
62
52
  return (
@@ -9,6 +9,7 @@
9
9
  * regression that motivated the split.
10
10
  */
11
11
  import type { CommandEntry } from "@oh-my-pi/pi-utils/cli";
12
+ import { flagConsumesValue } from "./cli/flag-tables";
12
13
 
13
14
  export const commands: CommandEntry[] = [
14
15
  { name: "launch", load: () => import("./commands/launch").then(m => m.default) },
@@ -44,11 +45,26 @@ export const commands: CommandEntry[] = [
44
45
  { name: "search", load: () => import("./commands/web-search").then(m => m.default), aliases: ["q"] },
45
46
  ];
46
47
 
48
+ // Documented-looking plugin-management verbs that are NOT registered top-level
49
+ // commands. Without a guard `resolveCliArgv` rewrites e.g. `omp list` to
50
+ // `omp launch list`, silently forwarding the bare verb to the model as a prompt
51
+ // instead of managing plugins (#2935; same class as the `install` leak fixed in
52
+ // #1496/#1498). A bare (single-arg) use gets a hint pointing at the real
53
+ // `omp plugin <action>` command; multi-word invocations still fall through to
54
+ // `launch`, so genuine prompts that merely begin with one of these words work.
47
55
  const RESERVED_TOP_LEVEL_WORDS = new Map<string, string>([
48
56
  [
49
57
  "extensions",
50
58
  '`omp extensions` is not a management command. Use `omp plugin list` / `omp plugin install`, or run `omp launch extensions` if you meant to send "extensions" as a prompt.',
51
59
  ],
60
+ [
61
+ "list",
62
+ '`omp list` is not a top-level command. Use `omp plugin list` to list installed plugins, or run `omp launch list` if you meant to send "list" as a prompt.',
63
+ ],
64
+ [
65
+ "remove",
66
+ '`omp remove` is not a top-level command. Use `omp plugin uninstall <name>` to remove a plugin, or run `omp launch remove` if you meant to send "remove" as a prompt.',
67
+ ],
52
68
  ]);
53
69
 
54
70
  export function reservedTopLevelWordMessage(first: string | undefined, argc = 1): string | undefined {
@@ -69,10 +85,29 @@ export function isSubcommand(first: string | undefined): boolean {
69
85
 
70
86
  export type ResolvedCliArgv = { argv: string[] } | { error: string };
71
87
 
88
+ /**
89
+ * Index of the first argv token that names a registered subcommand, skipping
90
+ * leading global option flags (and any value they consume) with the same
91
+ * contract as the launch parser ({@link flagConsumesValue}). Returns -1 when
92
+ * scanning hits a non-subcommand positional, an end-of-options `--`, or the end
93
+ * of argv first.
94
+ */
95
+ function leadingSubcommandIndex(argv: string[]): number {
96
+ for (let index = 0; index < argv.length; index += 1) {
97
+ const arg = argv[index];
98
+ if (arg === "--") return -1;
99
+ if (!arg.startsWith("-")) return isSubcommand(arg) ? index : -1;
100
+ if (flagConsumesValue(arg, argv[index + 1])) index += 1;
101
+ }
102
+ return -1;
103
+ }
104
+
72
105
  /**
73
106
  * Decide what the CLI runner should do with raw argv: reject bare reserved
74
- * management words, pass help/version through untouched, and route everything
75
- * that is not a known subcommand to `launch`.
107
+ * management words, pass help/version through untouched, route a recognized
108
+ * subcommand (even behind leading global flags like `--approval-mode=yolo`) to
109
+ * that command with the flags preserved, and forward everything else to
110
+ * `launch` (#2970).
76
111
  */
77
112
  export function resolveCliArgv(argv: string[]): ResolvedCliArgv {
78
113
  const first = argv[0];
@@ -81,5 +116,15 @@ export function resolveCliArgv(argv: string[]): ResolvedCliArgv {
81
116
  if (first === "--help" || first === "-h" || first === "--version" || first === "-v" || first === "help") {
82
117
  return { argv };
83
118
  }
84
- return { argv: isSubcommand(first) ? argv : ["launch", ...argv] };
119
+ if (isSubcommand(first)) return { argv };
120
+ // A subcommand can hide behind leading global option flags
121
+ // (`omp --approval-mode=yolo acp`). `run` dispatches strictly on argv[0], so
122
+ // hoist the subcommand to the front and keep the leading flags as its own
123
+ // argv; the command's parser then applies them. Genuine launch prompts (no
124
+ // trailing subcommand) are untouched.
125
+ const subIndex = leadingSubcommandIndex(argv);
126
+ if (subIndex >= 0) {
127
+ return { argv: [argv[subIndex], ...argv.slice(0, subIndex), ...argv.slice(subIndex + 1)] };
128
+ }
129
+ return { argv: ["launch", ...argv] };
85
130
  }
@@ -24,12 +24,6 @@ import {
24
24
  resolveVariantAlias,
25
25
  } from "@oh-my-pi/pi-catalog/variant-collapse";
26
26
 
27
- // Sentinels for local-only OAuth tokens — declared inline to avoid loading
28
- // provider modules at startup. Must match packages/ai/src/registry/lm-studio.ts
29
- // and packages/ai/src/registry/vllm.ts.
30
- const DEFAULT_LOCAL_TOKEN = "lm-studio-local";
31
- const DEFAULT_VLLM_LOCAL_TOKEN = "vllm-local";
32
-
33
27
  const SPECIAL_MODEL_MANAGER_PROVIDER_IDS: readonly string[] = [
34
28
  "google-antigravity",
35
29
  "google-gemini-cli",
@@ -41,6 +35,11 @@ const STARTUP_MODEL_CACHE_PROVIDER_IDS: readonly string[] = [
41
35
  ...SPECIAL_MODEL_MANAGER_PROVIDER_IDS,
42
36
  ];
43
37
 
38
+ // Sentinels for local-only OAuth tokens — declared inline to avoid loading
39
+ // provider modules at startup. Must match packages/ai/src/registry/llama-cpp.ts,
40
+ // packages/ai/src/registry/lm-studio.ts, and packages/ai/src/registry/vllm.ts.
41
+ const LOCAL_PROVIDER_PLACEHOLDERS = new Set<string>(["llama-cpp-local", "lm-studio-local", "vllm-local"]);
42
+
44
43
  import type { ApiKeyResolver, FetchImpl } from "@oh-my-pi/pi-ai";
45
44
  import { registerOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
46
45
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
@@ -85,7 +84,7 @@ export function isAuthenticated(apiKey: string | undefined | null): apiKey is st
85
84
  }
86
85
 
87
86
  function isDiscoveryBearerApiKey(apiKey: string | undefined | null): apiKey is string {
88
- return isAuthenticated(apiKey) && apiKey !== DEFAULT_LOCAL_TOKEN && apiKey !== DEFAULT_VLLM_LOCAL_TOKEN;
87
+ return isAuthenticated(apiKey) && !LOCAL_PROVIDER_PLACEHOLDERS.has(apiKey);
89
88
  }
90
89
 
91
90
  /** Provider override config (baseUrl, headers, apiKey, compat, transport) without custom models */