@oh-my-pi/pi-coding-agent 15.13.0 → 15.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +1656 -613
  2. package/dist/cli.js +12765 -12731
  3. package/dist/types/autolearn/managed-skills.d.ts +1 -1
  4. package/dist/types/capability/mcp.d.ts +2 -1
  5. package/dist/types/cli/args.d.ts +2 -0
  6. package/dist/types/cli/flag-tables.d.ts +126 -0
  7. package/dist/types/cli/profile-alias.d.ts +29 -0
  8. package/dist/types/cli/profile-bootstrap.d.ts +55 -0
  9. package/dist/types/commands/launch.d.ts +6 -0
  10. package/dist/types/config/model-roles.d.ts +3 -2
  11. package/dist/types/config/settings-schema.d.ts +2 -0
  12. package/dist/types/edit/file-snapshot-store.d.ts +14 -0
  13. package/dist/types/extensibility/extensions/runner.d.ts +11 -0
  14. package/dist/types/mcp/manager.d.ts +5 -1
  15. package/dist/types/mcp/oauth-credentials.d.ts +17 -0
  16. package/dist/types/mcp/oauth-flow.d.ts +41 -0
  17. package/dist/types/mcp/types.d.ts +2 -0
  18. package/dist/types/modes/components/background-tan-message.d.ts +9 -0
  19. package/dist/types/modes/components/mcp-add-wizard.d.ts +9 -5
  20. package/dist/types/modes/interactive-mode.d.ts +4 -0
  21. package/dist/types/modes/types.d.ts +3 -0
  22. package/dist/types/sdk.d.ts +1 -1
  23. package/dist/types/session/messages.d.ts +8 -0
  24. package/dist/types/session/session-manager.d.ts +6 -0
  25. package/dist/types/tools/builtin-names.d.ts +2 -0
  26. package/dist/types/tools/index.d.ts +3 -2
  27. package/dist/types/utils/external-editor.d.ts +11 -1
  28. package/package.json +12 -12
  29. package/src/autolearn/managed-skills.ts +3 -5
  30. package/src/capability/mcp.ts +2 -1
  31. package/src/cli/args.ts +61 -103
  32. package/src/cli/completion-gen.ts +2 -2
  33. package/src/cli/flag-tables.ts +270 -0
  34. package/src/cli/profile-alias.ts +338 -0
  35. package/src/cli/profile-bootstrap.ts +243 -0
  36. package/src/cli.ts +83 -16
  37. package/src/commands/launch.ts +7 -0
  38. package/src/config/mcp-schema.json +4 -0
  39. package/src/config/model-roles.ts +17 -4
  40. package/src/config/settings-schema.ts +2 -0
  41. package/src/discovery/builtin.ts +15 -9
  42. package/src/discovery/helpers.ts +25 -0
  43. package/src/discovery/mcp-json.ts +1 -0
  44. package/src/discovery/omp-extension-roots.ts +2 -2
  45. package/src/edit/file-snapshot-store.ts +43 -0
  46. package/src/eval/__tests__/agent-bridge.test.ts +3 -2
  47. package/src/eval/__tests__/helpers-local-roots.test.ts +1 -1
  48. package/src/eval/js/shared/runtime.ts +54 -0
  49. package/src/extensibility/extensions/runner.ts +25 -2
  50. package/src/goals/runtime.ts +4 -1
  51. package/src/internal-urls/docs-index.generated.ts +6 -6
  52. package/src/mcp/manager.ts +108 -71
  53. package/src/mcp/oauth-credentials.ts +104 -0
  54. package/src/mcp/oauth-flow.ts +67 -0
  55. package/src/mcp/types.ts +2 -0
  56. package/src/modes/components/agent-hub.ts +6 -0
  57. package/src/modes/components/background-tan-message.ts +36 -0
  58. package/src/modes/components/mcp-add-wizard.ts +17 -10
  59. package/src/modes/components/model-selector.ts +50 -6
  60. package/src/modes/components/tool-execution.ts +12 -0
  61. package/src/modes/controllers/input-controller.ts +21 -10
  62. package/src/modes/controllers/mcp-command-controller.ts +184 -112
  63. package/src/modes/controllers/tan-command-controller.ts +27 -11
  64. package/src/modes/interactive-mode.ts +6 -0
  65. package/src/modes/types.ts +3 -0
  66. package/src/modes/utils/ui-helpers.ts +6 -0
  67. package/src/prompts/bench.md +9 -4
  68. package/src/sdk.ts +6 -5
  69. package/src/session/agent-session.ts +30 -1
  70. package/src/session/messages.ts +9 -0
  71. package/src/session/session-manager.ts +7 -2
  72. package/src/tiny/text.ts +5 -1
  73. package/src/tools/ast-grep.ts +5 -1
  74. package/src/tools/builtin-names.ts +35 -0
  75. package/src/tools/index.ts +3 -2
  76. package/src/tools/read.ts +9 -0
  77. package/src/tools/search.ts +5 -1
  78. package/src/tts/tts-worker.ts +13 -5
  79. package/src/utils/external-editor.ts +15 -2
  80. package/src/utils/title-generator.ts +1 -1
  81. package/src/workspace-tree.ts +46 -6
  82. package/dist/types/utils/tools-manager.test.d.ts +0 -1
  83. package/src/utils/tools-manager.test.ts +0 -25
@@ -3,7 +3,7 @@ export declare const MANAGED_SKILLS_PROVIDER_ID = "omp-managed";
3
3
  /** Hard cap on a managed SKILL.md body to keep generated skills bounded. */
4
4
  export declare const MAX_MANAGED_SKILL_BYTES = 64000;
5
5
  /** Resolve the isolated managed-skills directory (`~/.omp/agent/managed-skills`). */
6
- export declare function getManagedSkillsDir(home?: string): string;
6
+ export declare function getManagedSkillsDir(agentDir?: string): string;
7
7
  /**
8
8
  * Validate + normalize a managed-skill name. Throws on anything outside the
9
9
  * strict allowlist so a bad name can never escape `getManagedSkillsDir()`
@@ -30,13 +30,14 @@ export interface MCPServer {
30
30
  clientSecret?: string;
31
31
  resource?: string;
32
32
  };
33
- /** OAuth configuration (clientId, clientSecret, redirectUri, callbackPort, callbackPath) for servers requiring explicit client credentials */
33
+ /** OAuth configuration (clientId, clientSecret, redirectUri, callbackPort, callbackPath, prompt) for servers requiring explicit client credentials */
34
34
  oauth?: {
35
35
  clientId?: string;
36
36
  clientSecret?: string;
37
37
  redirectUri?: string;
38
38
  callbackPort?: number;
39
39
  callbackPath?: string;
40
+ prompt?: string;
40
41
  };
41
42
  /** Transport type */
42
43
  transport?: "stdio" | "sse" | "http";
@@ -5,6 +5,8 @@ import { type Effort } from "@oh-my-pi/pi-catalog/effort";
5
5
  export type Mode = "text" | "json" | "rpc" | "acp" | "rpc-ui";
6
6
  export interface Args {
7
7
  cwd?: string;
8
+ profile?: string;
9
+ alias?: string;
8
10
  allowHome?: boolean;
9
11
  provider?: string;
10
12
  model?: string;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Single source of truth for argv flag classification, shared by:
3
+ * - `parseArgs` in `./args.ts` (the launch-time CLI parser)
4
+ * - `extractProfileFlags` in `./profile-bootstrap.ts` (the early
5
+ * `--profile` / `--alias` pre-parser)
6
+ *
7
+ * `parseArgs` dispatches string-valued flags by looking up their setter in
8
+ * {@link STRING_SETTERS}. Optional-value flags use {@link OPTIONAL_FLAGS} so
9
+ * per-flag quirks (currently empty-string rejection for `--resume`) live here
10
+ * instead of being hard-coded in the dispatch loop.
11
+ *
12
+ * The bootstrap doesn't dispatch — it only needs to know which flags consume
13
+ * a value — so it consults {@link STRING_VALUE_FLAGS} and
14
+ * {@link OPTIONAL_VALUE_FLAGS}, both derived from `Object.keys(...)` on the
15
+ * setter/config records below.
16
+ *
17
+ * The deliberate consequence: a string-valued flag exists in this CLI surface
18
+ * iff it has an entry here. Adding a new string-valued flag means adding a
19
+ * setter/config entry in this file; both `args.ts` and the bootstrap pick it
20
+ * up automatically, so the two cannot drift out of sync.
21
+ *
22
+ * IMPORT RULE: this module MUST NOT import any runtime value from
23
+ * `@oh-my-pi/pi-utils` (or anything that transitively does). That package's
24
+ * `env.ts` eagerly loads `.env` files from `getAgentDir()` during module
25
+ * initialization, which would race the profile bootstrap. Type-only imports
26
+ * are erased at runtime and are therefore safe.
27
+ *
28
+ * If a setter needs runtime dependencies (logging, validators, lookup
29
+ * tables), they're passed in through {@link ParseDeps} and `args.ts` wires the
30
+ * real implementations at the dispatch site.
31
+ */
32
+ import type { Effort } from "@oh-my-pi/pi-ai";
33
+ import type { Args } from "./args";
34
+ /**
35
+ * Runtime dependencies injected into setters that need to validate input or
36
+ * warn about bad values. `args.ts` constructs one object at module load and
37
+ * passes it to each {@link STRING_SETTERS} call.
38
+ *
39
+ * Keeping these out of the setter closures means this module stays free of
40
+ * runtime imports from `@oh-my-pi/pi-utils`, which is the whole reason it can
41
+ * be safely imported by `profile-bootstrap.ts` before `setProfile` runs.
42
+ */
43
+ export interface ParseDeps {
44
+ logger: {
45
+ warn: (message: string, meta?: Record<string, unknown>) => void;
46
+ };
47
+ parseEffort: (value: string | null | undefined) => Effort | undefined;
48
+ builtinToolNames: readonly string[];
49
+ thinkingEfforts: readonly string[];
50
+ }
51
+ export type StringSetter = (result: Args, value: string, deps: ParseDeps) => void;
52
+ /**
53
+ * Setter for a flag that may or may not consume the next argv token.
54
+ * Receives `undefined` for the bare form (`--resume` with no value, etc.).
55
+ */
56
+ export type OptionalSetter = (result: Args, value: string | undefined) => void;
57
+ /**
58
+ * Per-flag optional-value consumption policy.
59
+ *
60
+ * Every optional flag always rejects tokens that start with `-` — that shared
61
+ * rule lives in the dispatch site. These booleans capture the *additional*
62
+ * per-flag quirks:
63
+ *
64
+ * - `rejectEmpty`: treat `""` like “no value provided”. Needed for
65
+ * `--resume` / `-r` / `--session`. Without it, an empty string
66
+ * gets consumed as the session prefix and downstream resolution can match
67
+ * every session.
68
+ */
69
+ export interface OptionalFlagConfig {
70
+ set: OptionalSetter;
71
+ rejectEmpty?: boolean;
72
+ }
73
+ /**
74
+ * Setters for flags with string values. Most built-ins consume the next argv
75
+ * token even when it starts with `-`; flags listed in
76
+ * {@link EXTENSION_SHADOWABLE_STRING_FLAGS} use extension-style consumption so
77
+ * a registered boolean extension can shadow them before profile bootstrap.
78
+ */
79
+ export declare const STRING_SETTERS: Record<string, StringSetter>;
80
+ /**
81
+ * Optional-value flags. Setters receive `undefined` for the bare form.
82
+ *
83
+ * The dispatch in `args.ts` applies the shared "doesn't start with `-`"
84
+ * check for every flag, then consults the per-flag booleans below for the
85
+ * remaining quirks.
86
+ */
87
+ export declare const OPTIONAL_FLAGS: Record<string, OptionalFlagConfig>;
88
+ /**
89
+ * Derived from {@link STRING_SETTERS}. A flag is in this set if and only if
90
+ * it has a setter — by construction, drift between "the bootstrap thinks
91
+ * this flag accepts a value" and "the launch parser can set one" is
92
+ * structurally impossible.
93
+ */
94
+ export declare const STRING_VALUE_FLAGS: ReadonlySet<string>;
95
+ /**
96
+ * Built-in string flags known to be shadowed by bundled/common boolean
97
+ * extensions before extension metadata is available. They still accept a
98
+ * value-like successor for the built-in form (`--plan opus`), but a
99
+ * flag-looking successor remains a fresh flag (`--plan --profile work`).
100
+ */
101
+ export declare const EXTENSION_SHADOWABLE_STRING_FLAGS: ReadonlySet<string>;
102
+ /**
103
+ * Derived from {@link OPTIONAL_FLAGS}. Same single-source contract as
104
+ * {@link STRING_VALUE_FLAGS}.
105
+ */
106
+ export declare const OPTIONAL_VALUE_FLAGS: ReadonlySet<string>;
107
+ /**
108
+ * Internal marker inserted by the profile bootstrap when removing `--profile`
109
+ * or `--alias` would otherwise make the following value-like token become the
110
+ * value of a preceding optional/extension flag. `parseArgs` ignores it, but its
111
+ * flag-looking shape preserves argv boundaries during the second parse.
112
+ */
113
+ export declare const PROFILE_BOOTSTRAP_BOUNDARY_ARG = "--omp-profile-boundary";
114
+ /**
115
+ * Long-form launch flags that take NO value (booleans). The bootstrap pre-parser
116
+ * needs this to tell a known value-less flag (whose successor is a fresh
117
+ * argument — `omp --print --profile work` still selects a profile) apart from an
118
+ * UNKNOWN long option that might be an extension string flag consuming the next
119
+ * token as its value (so the bootstrap must not steal that token as a global
120
+ * `--profile`/`--alias`). MUST mirror the value-less flag arms of `parseArgs`
121
+ * in `./args.ts`: adding a new boolean launch flag there means adding it here,
122
+ * or `--<newflag> --profile X` stops selecting a profile. Short aliases
123
+ * (`-h`/`-v`/`-c`/`-p`) are intentionally omitted — the protection rule only
124
+ * fires for `--`-prefixed tokens.
125
+ */
126
+ export declare const VALUELESS_FLAGS: ReadonlySet<string>;
@@ -0,0 +1,29 @@
1
+ export type ProfileAliasShell = "bash" | "zsh" | "fish" | "powershell" | "pwsh";
2
+ export interface ProfileAliasCommand {
3
+ display: string;
4
+ posix: string;
5
+ fish: string;
6
+ powerShell: string;
7
+ }
8
+ export interface ProfileAliasInstallOptions {
9
+ profile: string;
10
+ aliasName: string;
11
+ shellPath?: string;
12
+ platform?: NodeJS.Platform;
13
+ homeDir?: string;
14
+ env?: NodeJS.ProcessEnv;
15
+ readFile?: (filePath: string) => Promise<string>;
16
+ command?: ProfileAliasCommand;
17
+ writeFile?: (filePath: string, content: string) => Promise<void>;
18
+ }
19
+ export interface ProfileAliasInstallResult {
20
+ shell: ProfileAliasShell;
21
+ configPath: string;
22
+ aliasName: string;
23
+ profile: string;
24
+ command: string;
25
+ reloadedWith: string;
26
+ }
27
+ export declare function resolveProfileAliasCommandFromProcess(argv?: readonly string[], cwd?: string): ProfileAliasCommand;
28
+ export declare function readProfileAliasConfigFile(filePath: string, readText?: (filePath: string) => Promise<string>): Promise<string>;
29
+ export declare function installProfileAlias(options: ProfileAliasInstallOptions): Promise<ProfileAliasInstallResult>;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Bootstrap-time argv preparser for the global `--profile` / `--alias` flags.
3
+ *
4
+ * Profile selection MUST happen before any module reads `getAgentDir()` (notably
5
+ * `@oh-my-pi/pi-utils/env`, which eagerly loads `.env` from the agent directory
6
+ * during its own import). The full `parseArgs` from `./args.ts` lives downstream
7
+ * of those imports, so we can't rely on it for profile bootstrap — we have to
8
+ * crack open argv before the lazy command modules load.
9
+ *
10
+ * Because of that, this preparser must respect the same value-consumption
11
+ * contract as `args.ts`: known string-valued flags usually consume the next
12
+ * token even when it starts with `-`, except for string flags that can be
13
+ * shadowed by preloaded boolean extensions (currently `--plan`). Optional-value
14
+ * flags (`--resume`, `--session`, `-r`) consume the next token only when it
15
+ * doesn't look like another flag. Without this, `omp --system-prompt --profile
16
+ * foo` silently activates profile `foo`
17
+ * instead of passing the literal `--profile` to the system prompt and `foo`
18
+ * as a positional message.
19
+ *
20
+ * The shared classification lives in {@link ./flag-tables}, imported below,
21
+ * so the bootstrap and `args.ts` reference one source of truth instead of
22
+ * maintaining parallel constants.
23
+ *
24
+ * An unclassified bare long option (one not in any flag table) is treated as a
25
+ * possible extension string flag, but the bootstrap mirrors `parseArgs`'
26
+ * extension-flag rules ({@link ./args}): a string extension flag consumes its
27
+ * successor ONLY when that successor is value-like (does not start with `-`), and
28
+ * a boolean extension flag consumes nothing. So the successor is forwarded
29
+ * untouched (and never read as a global `--profile`/`--alias`) only when it is
30
+ * value-like; a flag-looking successor is left for normal processing, so
31
+ * `omp --some-ext-flag --profile work` still selects a profile. Known value-less
32
+ * launch flags ({@link VALUELESS_FLAGS}) are exempt so a trailing profile after
33
+ * them also activates (`omp --print --profile work`).
34
+ */
35
+ export interface ProfileBootstrapResult {
36
+ argv: string[];
37
+ profile?: string;
38
+ aliasName?: string;
39
+ }
40
+ /**
41
+ * Strip `--profile` / `--alias` from argv while preserving the surrounding
42
+ * argument structure, returning the residual argv to hand to the launch parser
43
+ * and the captured flag values.
44
+ *
45
+ * Global flag extraction stops only when the first residual argv token names a
46
+ * registered command that owns its own flags (e.g. `grep`): everything from
47
+ * that token onward is forwarded verbatim so a subcommand's own flags and
48
+ * positionals are never stolen (`omp grep --profile <path>` greps for
49
+ * `--profile`; it does not select a profile). `launch` and `acp` are explicit
50
+ * spellings of launch-shaped commands, so `omp launch --profile work` and
51
+ * `omp acp --profile work` still select profile `work`.
52
+ *
53
+ * Throws when either flag is supplied without a value.
54
+ */
55
+ export declare function extractProfileFlags(argv: readonly string[]): ProfileBootstrapResult;
@@ -40,6 +40,12 @@ export default class Index extends Command {
40
40
  "allow-home": import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
41
41
  description: string;
42
42
  };
43
+ profile: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"string"> & {
44
+ description: string;
45
+ };
46
+ alias: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"string"> & {
47
+ description: string;
48
+ };
43
49
  cwd: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"string"> & {
44
50
  description: string;
45
51
  };
@@ -3,15 +3,16 @@
3
3
  */
4
4
  import { type ThemeColor } from "../modes/theme/theme";
5
5
  import type { Settings } from "./settings";
6
- export type ModelRole = "default" | "smol" | "slow" | "vision" | "plan" | "designer" | "commit" | "task";
6
+ export type ModelRole = "default" | "smol" | "slow" | "vision" | "plan" | "designer" | "commit" | "title" | "task";
7
7
  export interface ModelRoleInfo {
8
8
  tag?: string;
9
9
  name: string;
10
10
  color?: ThemeColor;
11
+ /** If true, the role is functional but not shown in the model selector UI. */
12
+ hidden?: boolean;
11
13
  }
12
14
  export declare const MODEL_ROLES: Record<ModelRole, ModelRoleInfo>;
13
15
  export declare const MODEL_ROLE_IDS: ModelRole[];
14
- /** Alias for ModelRoleInfo - used for both built-in and custom roles */
15
16
  export type RoleInfo = ModelRoleInfo;
16
17
  /**
17
18
  * Return the canonical set of known roles for selector/carousel UI.
@@ -107,6 +107,8 @@ type SettingDef = BooleanDef | StringDef | NumberDef | EnumDef<readonly string[]
107
107
  export interface ModelTagDef {
108
108
  name: string;
109
109
  color?: string;
110
+ /** If true, the role is functional but not shown in the model selector UI. */
111
+ hidden?: boolean;
110
112
  }
111
113
  export interface ModelTagsSettings {
112
114
  [key: string]: ModelTagDef;
@@ -45,4 +45,18 @@ export declare function canonicalSnapshotKey(absolutePath: string): string;
45
45
  * validates whenever the live file is byte-identical to what was read.
46
46
  */
47
47
  export declare function recordFileSnapshot(session: FileSnapshotStoreOwner, absolutePath: string): Promise<string | undefined>;
48
+ /**
49
+ * The 1-indexed file lines a hashline-formatted body actually displayed.
50
+ * Single `NN:` rows contribute that line; a collapsed summary `NN-MM:` row
51
+ * (a `{ .. }` brace pair) contributes only its boundary lines `NN` and `MM` —
52
+ * the elided interior was never shown, so editing inside it must be rejected.
53
+ */
54
+ export declare function parseSeenLinesFromHashlineBody(body: string): number[];
55
+ /**
56
+ * Attach the lines a read displayed to the snapshot it minted, so the patcher
57
+ * can reject edits anchored on lines the model never saw. Best-effort: a no-op
58
+ * when the body has no numbered rows or the snapshot already aged out. `tag`
59
+ * must be the tag returned when this exact content was recorded.
60
+ */
61
+ export declare function recordSeenLinesFromBody(session: FileSnapshotStoreOwner, absolutePath: string, tag: string, body: string): void;
48
62
  export {};
@@ -17,6 +17,17 @@ interface BeforeAgentStartCombinedResult {
17
17
  export type ExtensionErrorListener = (error: ExtensionError) => void;
18
18
  export declare const EXTENSION_HANDLER_TIMEOUT_MS = 30000;
19
19
  export declare function testSetExtensionHandlerTimeoutMs(timeoutMs: number): void;
20
+ /**
21
+ * Dedicated cap for `session_shutdown` handlers. The generic 30s budget is
22
+ * appropriate for events extensions can observe (e.g. `session_start`,
23
+ * `before_provider_request`), but `session_shutdown` is fire-and-forget
24
+ * teardown — extensions receive no result and the user has already asked to
25
+ * leave. A hung handler (e.g. an extension waiting on a stuck IPC pipe to a
26
+ * companion app) MUST NOT hold Ctrl+C / `/exit` hostage for the full window.
27
+ * See issue #2600.
28
+ */
29
+ export declare const SESSION_SHUTDOWN_HANDLER_TIMEOUT_MS = 2000;
30
+ export declare function testSetSessionShutdownHandlerTimeoutMs(timeoutMs: number): void;
20
31
  /**
21
32
  * Events handled by the generic emit() method.
22
33
  * Events with dedicated emitXxx() methods are excluded for stronger type safety.
@@ -118,8 +118,12 @@ export declare class MCPManager {
118
118
  waitForConnection(name: string): Promise<MCPServerConnection>;
119
119
  /**
120
120
  * Resolve auth and shell-command substitutions in config before connecting.
121
+ * Pass `oauth: false` to skip OAuth credential injection (used by reauth's
122
+ * unauthenticated probe, which must observe the server's bare 401).
121
123
  */
122
- prepareConfig(config: MCPServerConfig): Promise<MCPServerConfig>;
124
+ prepareConfig(config: MCPServerConfig, options?: {
125
+ oauth?: boolean;
126
+ }): Promise<MCPServerConfig>;
123
127
  /**
124
128
  * Get all connected server names.
125
129
  */
@@ -0,0 +1,17 @@
1
+ import type { AuthStorage } from "../session/auth-storage";
2
+ import { type MCPStoredOAuthCredential } from "./oauth-flow";
3
+ import type { MCPAuthConfig, MCPServerConfig } from "./types";
4
+ export interface MCPOAuthCredentialLookup {
5
+ credentialId: string;
6
+ credential: MCPStoredOAuthCredential;
7
+ }
8
+ export type MCPOAuthRefreshMaterial = MCPStoredOAuthCredential | MCPAuthConfig | undefined;
9
+ export declare function mcpOAuthCredentialIdsForServerUrl(serverUrl: string | undefined): string[];
10
+ export declare function hasMcpAuthorizationHeader(config: MCPServerConfig): boolean;
11
+ export declare function lookupMcpOAuthCredentialForServer(authStorage: AuthStorage | null | undefined, auth: MCPAuthConfig | undefined, serverUrl: string | undefined, options?: {
12
+ allowUrlKeyedFallback?: boolean;
13
+ }): MCPOAuthCredentialLookup | undefined;
14
+ export declare function lookupMcpOAuthCredential(authStorage: AuthStorage | null | undefined, config: MCPServerConfig): MCPOAuthCredentialLookup | undefined;
15
+ export declare function selectMcpOAuthRefreshMaterial(credential: MCPStoredOAuthCredential, auth: MCPAuthConfig | undefined): MCPOAuthRefreshMaterial;
16
+ export declare function removeManagedMcpOAuthCredential(authStorage: AuthStorage, credentialId: string | undefined): Promise<boolean>;
17
+ export declare function removeManagedMcpOAuthCredentials(authStorage: AuthStorage, credentialIds: readonly (string | undefined)[]): Promise<boolean>;
@@ -7,6 +7,38 @@
7
7
  import { OAuthCallbackFlow } from "@oh-my-pi/pi-ai/oauth/callback-server";
8
8
  import type { OAuthController, OAuthCredentials } from "@oh-my-pi/pi-ai/oauth/types";
9
9
  import type { FetchImpl } from "@oh-my-pi/pi-ai/types";
10
+ import type { OAuthCredential } from "../session/auth-storage";
11
+ /**
12
+ * Deterministic credential id for an MCP server URL scoped to an OMP profile.
13
+ *
14
+ * Local profile stores are already separate, but auth-broker storage shares one
15
+ * provider namespace across profiles. Including the profile in the provider key
16
+ * keeps a shared project `mcp.json` definition from making profile B overwrite
17
+ * or read profile A's OAuth row for the same server URL. The URL is used
18
+ * verbatim (query string included) because it can carry tenant selectors such
19
+ * as `?project_ref=`.
20
+ */
21
+ export declare function mcpOAuthCredentialId(serverUrl: string, profile?: string | undefined): string;
22
+ /** Whether a credential id was minted by OMP's MCP OAuth flows (either era). */
23
+ export declare function isManagedMCPOAuthCredentialId(credentialId: string | undefined): credentialId is string;
24
+ /**
25
+ * Profile segment of a profile-scoped `mcp_oauth:profile:<profile>:<serverUrl>`
26
+ * credential id, or `undefined` for legacy non-profile-scoped managed ids
27
+ * (`mcp_oauth:<url>`, `mcp_oauth_<rand>`). The server URL itself contains `:`
28
+ * and `/`, so only the segment between the prefix and the FIRST subsequent `:`
29
+ * is the profile; everything after it is the URL.
30
+ */
31
+ export declare function mcpOAuthCredentialProfile(credentialId: string): string | undefined;
32
+ /**
33
+ * Stored MCP OAuth credential. Refresh material is embedded so token refresh
34
+ * works without any `auth` block persisted in (possibly shared) config files.
35
+ */
36
+ export interface MCPStoredOAuthCredential extends OAuthCredential {
37
+ tokenUrl?: string;
38
+ clientId?: string;
39
+ clientSecret?: string;
40
+ resource?: string;
41
+ }
10
42
  export interface MCPOAuthConfig {
11
43
  /** Authorization endpoint URL */
12
44
  authorizationUrl: string;
@@ -18,6 +50,15 @@ export interface MCPOAuthConfig {
18
50
  clientSecret?: string;
19
51
  /** OAuth scopes (space-separated) */
20
52
  scopes?: string;
53
+ /**
54
+ * `prompt` parameter for the authorization request. Defaults to `"consent"`
55
+ * so the provider always shows its authorize screen instead of silently
56
+ * re-approving the browser's current session — without it, reauthorizing to
57
+ * switch accounts/workspaces is impossible once a session cookie exists
58
+ * (RFC 6749 §3.1 requires servers to ignore the param when unsupported).
59
+ * Set to `""` to omit the parameter entirely.
60
+ */
61
+ prompt?: string;
21
62
  /** Exact redirect URI to advertise to the provider */
22
63
  redirectUri?: string;
23
64
  /** Custom callback port (default: 3000) */
@@ -58,6 +58,8 @@ interface MCPServerConfigBase {
58
58
  redirectUri?: string;
59
59
  callbackPort?: number;
60
60
  callbackPath?: string;
61
+ /** `prompt` param for the authorization request (default "consent"; "" to omit) */
62
+ prompt?: string;
61
63
  };
62
64
  }
63
65
  /** Stdio server configuration */
@@ -0,0 +1,9 @@
1
+ import type { CustomMessage } from "../../session/messages";
2
+ import { TranscriptBlock } from "./transcript-container";
3
+ /**
4
+ * Single-line transcript pill for a `/tan` background-dispatch breadcrumb,
5
+ * styled as a sibling of the "Background job completed" line. The full
6
+ * system-notice content (the persisted `content`) is for the model only — the
7
+ * user sees one compact line, not the raw `<system-notice>` block.
8
+ */
9
+ export declare function createBackgroundTanDispatchBlock(message: CustomMessage<unknown>): TranscriptBlock;
@@ -8,19 +8,23 @@ import type { MCPServerConfig } from "../../mcp/types";
8
8
  type Scope = "user" | "project";
9
9
  /**
10
10
  * Result of the wizard's OAuth callback. `credentialId` is mandatory;
11
- * `clientId`/`clientSecret` are populated when the OAuth provider performed
12
- * dynamic client registration (or when the caller pre-supplied them) so the
13
- * wizard can fold them into the final `mcp.json` entry for refresh.
11
+ * `clientId` is populated when the OAuth provider performed dynamic client
12
+ * registration (or when the caller pre-supplied it) so the wizard can fold it
13
+ * into the final `mcp.json` entry. Refresh material (including any DCR client
14
+ * secret) is embedded in the stored credential, never written to config files.
14
15
  */
15
16
  export interface MCPAddWizardOAuthResult {
16
17
  credentialId: string;
17
18
  clientId?: string;
18
- clientSecret?: string;
19
+ resource?: string;
20
+ }
21
+ interface MCPAddWizardOAuthOptions {
22
+ serverUrl?: string;
19
23
  resource?: string;
20
24
  }
21
25
  export declare class MCPAddWizard extends Container {
22
26
  #private;
23
- constructor(onComplete: (name: string, config: MCPServerConfig, scope: Scope) => void, onCancel: () => void, onOAuth?: (authUrl: string, tokenUrl: string, clientId: string, clientSecret: string, scopes: string, resource?: string) => Promise<MCPAddWizardOAuthResult>, onTestConnection?: (config: MCPServerConfig) => Promise<void>, onRender?: () => void, initialName?: string);
27
+ constructor(onComplete: (name: string, config: MCPServerConfig, scope: Scope) => void, onCancel: () => void, onOAuth?: (authUrl: string, tokenUrl: string, clientId: string, clientSecret: string, scopes: string, options?: MCPAddWizardOAuthOptions) => Promise<MCPAddWizardOAuthResult>, onTestConnection?: (config: MCPServerConfig) => Promise<void>, onRender?: () => void, initialName?: string);
24
28
  handleInput(keyData: string): void;
25
29
  }
26
30
  export {};
@@ -129,6 +129,10 @@ export declare class InteractiveMode implements InteractiveModeContext {
129
129
  lastEscapeTime: number;
130
130
  lastLeftTapTime: number;
131
131
  shutdownRequested: boolean;
132
+ /** True once `shutdown()` has begun teardown. Surfaced to the input
133
+ * controller so a Ctrl+C arriving while teardown is in flight can hard-
134
+ * abort the remaining work instead of stacking another no-op call. */
135
+ get isShuttingDown(): boolean;
132
136
  hookSelector: HookSelectorComponent | undefined;
133
137
  hookInput: HookInputComponent | undefined;
134
138
  hookEditor: HookEditorComponent | undefined;
@@ -140,6 +140,9 @@ export interface InteractiveModeContext {
140
140
  lastEscapeTime: number;
141
141
  lastLeftTapTime: number;
142
142
  shutdownRequested: boolean;
143
+ /** True once `shutdown()` has started. Read-only from the context;
144
+ * controllers use this to skip work that races with teardown. */
145
+ readonly isShuttingDown: boolean;
143
146
  hookSelector: HookSelectorComponent | undefined;
144
147
  hookInput: HookInputComponent | undefined;
145
148
  hookEditor: HookEditorComponent | undefined;
@@ -48,7 +48,7 @@ export interface CreateAgentSessionOptions {
48
48
  thinkingLevel?: ThinkingLevel;
49
49
  }>;
50
50
  /** System prompt blocks. Array replaces default, function receives default blocks and returns final blocks. */
51
- systemPrompt?: string[] | ((defaultPrompt: string[]) => string[]);
51
+ systemPrompt?: string | string[] | ((defaultPrompt: string[]) => string | string[]);
52
52
  /** Optional provider-facing session identifier for prompt caches and sticky auth selection.
53
53
  * Keeps persisted session files isolated while reusing provider-side caches. */
54
54
  providerSessionId?: string;
@@ -11,6 +11,14 @@ export { type BranchSummaryMessage, type CompactionSummaryMessage, createBranchS
11
11
  import type { OutputMeta } from "../tools/output-meta";
12
12
  export declare const SKILL_PROMPT_MESSAGE_TYPE = "skill-prompt";
13
13
  export declare const LSP_LATE_DIAGNOSTIC_MESSAGE_TYPE = "lsp-late-diagnostic";
14
+ export declare const BACKGROUND_TAN_DISPATCH_MESSAGE_TYPE = "background-tan-dispatch";
15
+ /** Details persisted on a `/tan` background-dispatch breadcrumb. */
16
+ export interface BackgroundTanDispatchDetails {
17
+ jobId: string;
18
+ work: string;
19
+ /** Forked clone session file, named `<agentId>.jsonl`; the Agent Hub reads its transcript. */
20
+ sessionFile: string;
21
+ }
14
22
  export interface SkillPromptDetails {
15
23
  name: string;
16
24
  path: string;
@@ -237,9 +237,15 @@ export declare class SessionManager {
237
237
  /**
238
238
  * Fork a session into the current project directory: copy history from another
239
239
  * session file while creating a fresh session file in this sessionDir.
240
+ *
241
+ * `options.sessionFile` pins the new session's file path (default: an
242
+ * auto-named `<timestamp>_<id>.jsonl` in `sessionDir`). Callers that register
243
+ * the fork as a named agent (e.g. `/tan`) pass `<agentId>.jsonl` so the
244
+ * persisted-subagent scan keys the agent by the same id the live ref uses.
240
245
  */
241
246
  static forkFrom(sourcePath: string, cwd: string, sessionDir?: string, storage?: SessionStorage, options?: {
242
247
  suppressBreadcrumb?: boolean;
248
+ sessionFile?: string;
243
249
  }): Promise<SessionManager>;
244
250
  /**
245
251
  * Open a specific session file.
@@ -0,0 +1,2 @@
1
+ export declare const BUILTIN_TOOL_NAMES: readonly ["read", "bash", "edit", "ast_grep", "ast_edit", "render_mermaid", "ask", "debug", "eval", "ssh", "github", "find", "search", "lsp", "inspect_image", "browser", "checkpoint", "rewind", "task", "job", "irc", "todo", "web_search", "search_tool_bm25", "write", "memory_edit", "retain", "recall", "reflect", "learn", "manage_skill"];
2
+ export type BuiltinToolName = (typeof BUILTIN_TOOL_NAMES)[number];
@@ -23,6 +23,7 @@ import type { AgentOutputManager } from "../task/output-manager";
23
23
  import type { DiscoverableTool, DiscoverableToolSearchIndex } from "../tool-discovery/tool-index";
24
24
  import type { EventBus } from "../utils/event-bus";
25
25
  import type { WorkspaceTree } from "../workspace-tree";
26
+ import type { BuiltinToolName } from "./builtin-names";
26
27
  import { type CheckpointState } from "./checkpoint";
27
28
  import { type TodoPhase } from "./todo";
28
29
  export * from "../edit";
@@ -341,9 +342,9 @@ export declare function filterInitialToolsForDiscoveryAll(initialToolNames: stri
341
342
  * Public callable factory map. External callers may invoke `BUILTIN_TOOLS.read(session)` or
342
343
  * `BUILTIN_TOOLS[name](session)` to construct a tool directly.
343
344
  */
344
- export declare const BUILTIN_TOOLS: Record<string, ToolFactory>;
345
+ export declare const BUILTIN_TOOLS: Record<BuiltinToolName, ToolFactory>;
345
346
  export declare const HIDDEN_TOOLS: Record<string, ToolFactory>;
346
- export type ToolName = keyof typeof BUILTIN_TOOLS;
347
+ export type ToolName = BuiltinToolName;
347
348
  /**
348
349
  * Create tools from BUILTIN_TOOLS registry.
349
350
  */
@@ -1,4 +1,14 @@
1
- /** Returns the user's preferred editor command, or undefined if not configured. */
1
+ /**
2
+ * Returns the user's preferred editor command, or a platform default.
3
+ *
4
+ * Resolution order:
5
+ * 1. `$VISUAL`
6
+ * 2. `$EDITOR`
7
+ * 3. `notepad` on Windows (always present in `%SystemRoot%\System32`)
8
+ *
9
+ * POSIX returns `undefined` when neither variable is set so the caller can
10
+ * surface a warning that nudges the user to configure one.
11
+ */
2
12
  export declare function getEditorCommand(): string | undefined;
3
13
  export interface OpenInEditorOptions {
4
14
  /** File extension for the temp file (default: ".md"). */
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": "15.13.0",
4
+ "version": "15.13.1",
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",
@@ -47,17 +47,17 @@
47
47
  "@agentclientprotocol/sdk": "0.25.0",
48
48
  "@babel/parser": "^7.29.7",
49
49
  "@mozilla/readability": "^0.6.0",
50
- "@oh-my-pi/hashline": "15.13.0",
51
- "@oh-my-pi/omp-stats": "15.13.0",
52
- "@oh-my-pi/pi-agent-core": "15.13.0",
53
- "@oh-my-pi/pi-ai": "15.13.0",
54
- "@oh-my-pi/pi-catalog": "15.13.0",
55
- "@oh-my-pi/pi-mnemopi": "15.13.0",
56
- "@oh-my-pi/pi-natives": "15.13.0",
57
- "@oh-my-pi/pi-tui": "15.13.0",
58
- "@oh-my-pi/pi-utils": "15.13.0",
59
- "@oh-my-pi/pi-wire": "15.13.0",
60
- "@oh-my-pi/snapcompact": "15.13.0",
50
+ "@oh-my-pi/hashline": "15.13.1",
51
+ "@oh-my-pi/omp-stats": "15.13.1",
52
+ "@oh-my-pi/pi-agent-core": "15.13.1",
53
+ "@oh-my-pi/pi-ai": "15.13.1",
54
+ "@oh-my-pi/pi-catalog": "15.13.1",
55
+ "@oh-my-pi/pi-mnemopi": "15.13.1",
56
+ "@oh-my-pi/pi-natives": "15.13.1",
57
+ "@oh-my-pi/pi-tui": "15.13.1",
58
+ "@oh-my-pi/pi-utils": "15.13.1",
59
+ "@oh-my-pi/pi-wire": "15.13.1",
60
+ "@oh-my-pi/snapcompact": "15.13.1",
61
61
  "@opentelemetry/api": "^1.9.1",
62
62
  "@opentelemetry/context-async-hooks": "^2.7.1",
63
63
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",