@oh-my-pi/pi-coding-agent 17.0.4 → 17.0.6
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.
- package/CHANGELOG.md +209 -0
- package/dist/cli.js +3616 -3676
- package/dist/types/advisor/advise-tool.d.ts +3 -0
- package/dist/types/advisor/runtime.d.ts +7 -1
- package/dist/types/async/job-manager.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +7 -0
- package/dist/types/config/model-resolver.d.ts +8 -0
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/models-config.d.ts +6 -0
- package/dist/types/dap/client.d.ts +10 -0
- package/dist/types/dap/types.d.ts +6 -5
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/extensions/load-errors.d.ts +3 -0
- package/dist/types/extensibility/extensions/runner.d.ts +4 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +16 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +8 -0
- package/dist/types/launch/spawn-options.d.ts +10 -0
- package/dist/types/launch/spawn-options.test.d.ts +1 -0
- package/dist/types/modes/components/status-line/component.d.ts +10 -3
- package/dist/types/modes/components/status-line/types.d.ts +3 -1
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/components/transcript-container.d.ts +4 -3
- package/dist/types/modes/components/tree-selector.d.ts +6 -2
- package/dist/types/modes/components/usage-row.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/print-mode.d.ts +4 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +1 -1
- package/dist/types/modes/rpc/rpc-frame.d.ts +9 -0
- package/dist/types/modes/rpc/rpc-input.d.ts +5 -0
- package/dist/types/modes/rpc/rpc-mode.d.ts +1 -1
- package/dist/types/modes/setup-wizard/index.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/model.d.ts +3 -0
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +42 -6
- package/dist/types/session/messages.d.ts +6 -0
- package/dist/types/session/session-paths.d.ts +21 -4
- package/dist/types/tools/browser/aria/aria-snapshot.d.ts +17 -7
- package/dist/types/tools/browser/cmux/socket-client.d.ts +2 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/gh.d.ts +5 -3
- package/dist/types/tools/hub/index.d.ts +1 -1
- package/dist/types/tools/hub/jobs.d.ts +1 -0
- package/dist/types/tools/hub/types.d.ts +2 -0
- package/dist/types/tools/tool-timeouts.d.ts +1 -1
- package/dist/types/tools/xdev.d.ts +5 -3
- package/dist/types/utils/git.d.ts +33 -0
- package/dist/types/vibe/__tests__/token-rate.test.d.ts +1 -0
- package/dist/types/vibe/runtime.d.ts +23 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +150 -0
- package/src/advisor/advise-tool.ts +4 -0
- package/src/advisor/runtime.ts +38 -14
- package/src/async/job-manager.ts +3 -0
- package/src/cli/bench-cli.ts +8 -2
- package/src/cli/dry-balance-cli.ts +1 -0
- package/src/config/model-registry.ts +89 -8
- package/src/config/model-resolver.ts +78 -14
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings.ts +3 -1
- package/src/dap/client.ts +168 -1
- package/src/dap/config.ts +51 -1
- package/src/dap/session.ts +575 -234
- package/src/dap/types.ts +6 -5
- package/src/discovery/agents.ts +2 -2
- package/src/exec/bash-executor.ts +68 -8
- package/src/extensibility/extensions/load-errors.ts +13 -0
- package/src/extensibility/extensions/runner.ts +6 -4
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +26 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +41 -6
- package/src/launch/broker.ts +34 -31
- package/src/launch/client.ts +7 -1
- package/src/launch/spawn-options.test.ts +31 -0
- package/src/launch/spawn-options.ts +17 -0
- package/src/lsp/types.ts +5 -1
- package/src/main.ts +25 -4
- package/src/mcp/transports/stdio.test.ts +9 -1
- package/src/modes/components/ask-dialog.ts +137 -73
- package/src/modes/components/chat-transcript-builder.ts +10 -1
- package/src/modes/components/status-line/component.ts +57 -3
- package/src/modes/components/status-line/segments.ts +20 -3
- package/src/modes/components/status-line/types.ts +3 -1
- package/src/modes/components/tool-execution.ts +5 -0
- package/src/modes/components/transcript-container.ts +23 -122
- package/src/modes/components/tree-selector.ts +10 -4
- package/src/modes/components/usage-row.ts +17 -1
- package/src/modes/controllers/command-controller.ts +41 -1
- package/src/modes/controllers/event-controller.ts +7 -3
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +29 -8
- package/src/modes/interactive-mode.ts +102 -60
- package/src/modes/noninteractive-dispose.test.ts +14 -1
- package/src/modes/print-mode.ts +81 -9
- package/src/modes/rpc/rpc-client.ts +76 -35
- package/src/modes/rpc/rpc-frame.ts +156 -0
- package/src/modes/rpc/rpc-input.ts +38 -0
- package/src/modes/rpc/rpc-mode.ts +11 -4
- package/src/modes/setup-wizard/index.ts +2 -0
- package/src/modes/setup-wizard/scenes/model.ts +134 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +3 -1
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +9 -3
- package/src/prompts/system/workflow-notice.md +89 -74
- package/src/prompts/tools/browser.md +3 -2
- package/src/prompts/tools/debug.md +2 -7
- package/src/prompts/tools/github.md +6 -1
- package/src/prompts/tools/hub.md +4 -2
- package/src/prompts/tools/task-async-contract.md +1 -0
- package/src/prompts/tools/task.md +9 -2
- package/src/sdk.ts +38 -6
- package/src/session/agent-session.ts +484 -188
- package/src/session/messages.test.ts +91 -0
- package/src/session/messages.ts +248 -110
- package/src/session/session-manager.ts +34 -3
- package/src/session/session-paths.ts +38 -9
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/task/executor.ts +104 -33
- package/src/task/index.ts +46 -9
- package/src/task/worktree.ts +10 -0
- package/src/tools/browser/aria/aria-snapshot.ts +36 -8
- package/src/tools/browser/cmux/cmux-tab.ts +2 -1
- package/src/tools/browser/cmux/socket-client.ts +139 -3
- package/src/tools/browser/run-cancellation.ts +4 -0
- package/src/tools/browser/tab-protocol.ts +2 -0
- package/src/tools/browser/tab-supervisor.ts +21 -11
- package/src/tools/browser/tab-worker.ts +199 -33
- package/src/tools/debug.ts +3 -0
- package/src/tools/gh.ts +40 -2
- package/src/tools/hub/index.ts +4 -1
- package/src/tools/hub/jobs.ts +42 -1
- package/src/tools/hub/types.ts +2 -0
- package/src/tools/tool-timeouts.ts +1 -1
- package/src/tools/xdev.ts +11 -4
- package/src/tools/yield.ts +4 -1
- package/src/utils/git.ts +237 -0
- package/src/{modes/components/status-line → utils}/token-rate.ts +6 -0
- package/src/vibe/__tests__/token-rate.test.ts +96 -0
- package/src/vibe/runtime.ts +50 -0
- package/src/web/search/index.ts +9 -5
- package/src/web/search/providers/codex.ts +195 -99
- /package/dist/types/{modes/components/status-line → utils}/token-rate.d.ts +0 -0
package/src/dap/types.ts
CHANGED
|
@@ -484,10 +484,9 @@ export interface DapAdapterConfig {
|
|
|
484
484
|
launchDefaults?: Record<string, unknown>;
|
|
485
485
|
attachDefaults?: Record<string, unknown>;
|
|
486
486
|
/** "stdio" (default): communicate via stdin/stdout pipes.
|
|
487
|
-
* "socket": adapter
|
|
488
|
-
*
|
|
489
|
-
|
|
490
|
-
connectMode?: "stdio" | "socket";
|
|
487
|
+
* "socket": adapter-specific socket launch (currently Delve).
|
|
488
|
+
* "tcp": spawn a DAP server with `${port}` substituted in `args`, then connect to it. */
|
|
489
|
+
connectMode?: "stdio" | "socket" | "tcp";
|
|
491
490
|
/** When true, the adapter accepts a directory as the launch `program`
|
|
492
491
|
* (e.g. dlv treats it as a Go package path). When false/undefined, the
|
|
493
492
|
* debug tool rejects directory programs upfront. */
|
|
@@ -504,7 +503,7 @@ export interface DapResolvedAdapter {
|
|
|
504
503
|
rootMarkers: string[];
|
|
505
504
|
launchDefaults: Record<string, unknown>;
|
|
506
505
|
attachDefaults: Record<string, unknown>;
|
|
507
|
-
connectMode: "stdio" | "socket";
|
|
506
|
+
connectMode: "stdio" | "socket" | "tcp";
|
|
508
507
|
acceptsDirectoryProgram: boolean;
|
|
509
508
|
}
|
|
510
509
|
|
|
@@ -581,6 +580,8 @@ export interface DapSessionSummary {
|
|
|
581
580
|
outputTruncated: boolean;
|
|
582
581
|
exitCode?: number;
|
|
583
582
|
needsConfigurationDone: boolean;
|
|
583
|
+
parentSessionId?: string;
|
|
584
|
+
childSessionIds?: string[];
|
|
584
585
|
}
|
|
585
586
|
|
|
586
587
|
export interface DapContinueOutcome {
|
package/src/discovery/agents.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Agent Dirs (.agent/.agents) Provider
|
|
3
3
|
*
|
|
4
4
|
* Loads skills, rules, prompts, commands, context files, and system prompts
|
|
5
5
|
* from .agent/ and .agents/ directories at both user (~/) and project levels.
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from "./helpers";
|
|
25
25
|
|
|
26
26
|
const PROVIDER_ID = "agents";
|
|
27
|
-
const DISPLAY_NAME = "
|
|
27
|
+
const DISPLAY_NAME = "Agent Dirs (.agent/.agents)";
|
|
28
28
|
const PRIORITY = 70;
|
|
29
29
|
const AGENT_DIR_CANDIDATES = [".agent", ".agents"] as const;
|
|
30
30
|
|
|
@@ -150,9 +150,56 @@ function isBashShell(shell: string): boolean {
|
|
|
150
150
|
return basename.includes("bash");
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
const UNSUPPORTED_UNQUOTED_CD_CHARS = "\\$`;&|<>(){}*?[]!#\"'";
|
|
154
|
+
|
|
155
|
+
function hasUnsupportedUnquotedCdSyntax(value: string): boolean {
|
|
156
|
+
for (const char of value) {
|
|
157
|
+
if (/\s/.test(char) || UNSUPPORTED_UNQUOTED_CD_CHARS.includes(char)) return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function isPersistentShellCdCommand(command: string): boolean {
|
|
163
|
+
if (/[\r\n]/.test(command)) return false;
|
|
164
|
+
|
|
165
|
+
const trimmed = command.trim();
|
|
166
|
+
if (trimmed === "cd") return true;
|
|
167
|
+
if (!trimmed.startsWith("cd") || !/[ \t]/.test(trimmed[2] ?? "")) return false;
|
|
168
|
+
|
|
169
|
+
let rest = trimmed.slice(2).trim();
|
|
170
|
+
if (rest === "" || rest === "--") return true;
|
|
171
|
+
|
|
172
|
+
let hasOptionTerminator = false;
|
|
173
|
+
if (/^--[ \t]/.test(rest)) {
|
|
174
|
+
hasOptionTerminator = true;
|
|
175
|
+
rest = rest.slice(2).trimStart();
|
|
176
|
+
}
|
|
177
|
+
if (rest === "") return true;
|
|
178
|
+
|
|
179
|
+
const quote = rest[0];
|
|
180
|
+
let target: string;
|
|
181
|
+
let quoted = false;
|
|
182
|
+
if (quote === `"` || quote === "'") {
|
|
183
|
+
if (rest.length < 2 || rest[rest.length - 1] !== quote) return false;
|
|
184
|
+
target = rest.slice(1, -1);
|
|
185
|
+
if (target.includes(quote)) return false;
|
|
186
|
+
if (quote === `"` && /[\\$`\r\n]/.test(target)) return false;
|
|
187
|
+
quoted = true;
|
|
188
|
+
} else {
|
|
189
|
+
if (hasUnsupportedUnquotedCdSyntax(rest)) return false;
|
|
190
|
+
target = rest;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (target === "") return false;
|
|
194
|
+
if (/^[+-]\d+$/.test(target)) return false;
|
|
195
|
+
if (!hasOptionTerminator && target.startsWith("-") && target !== "-") return false;
|
|
196
|
+
if (!quoted && target.startsWith("~") && target !== "~" && !target.startsWith("~/")) return false;
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
153
200
|
function needsInteractiveShellArg(shell: string): boolean {
|
|
154
201
|
const basename = shellBasename(shell);
|
|
155
|
-
return basename.includes("zsh");
|
|
202
|
+
return basename.includes("zsh") || basename.includes("fish");
|
|
156
203
|
}
|
|
157
204
|
|
|
158
205
|
function supportsAutoUserShell(shell: string): boolean {
|
|
@@ -165,19 +212,31 @@ function hasInteractiveShellArg(args: string[]): boolean {
|
|
|
165
212
|
}
|
|
166
213
|
|
|
167
214
|
function ensureInteractiveShellArgs(shell: string, args: string[]): string[] {
|
|
168
|
-
if (!needsInteractiveShellArg(shell)
|
|
215
|
+
if (!needsInteractiveShellArg(shell)) return args;
|
|
216
|
+
|
|
217
|
+
// fish sources the same config files (config.fish + conf.d) for interactive
|
|
218
|
+
// shells as for login shells, so the inherited `-l` adds nothing — it only
|
|
219
|
+
// marks the shell as login, firing `status is-login` blocks in user config
|
|
220
|
+
// (agent/keychain setup, path mutation) on every `!` command. zsh keeps `-l`
|
|
221
|
+
// because .zprofile is login-only. Args originate from procmgr's
|
|
222
|
+
// getShellArgs(), so login only ever appears as a standalone `-l`/`--login`.
|
|
223
|
+
const effectiveArgs = shellBasename(shell).includes("fish")
|
|
224
|
+
? args.filter(arg => arg !== "-l" && arg !== "--login")
|
|
225
|
+
: args;
|
|
226
|
+
|
|
227
|
+
if (hasInteractiveShellArg(effectiveArgs)) return effectiveArgs;
|
|
169
228
|
|
|
170
|
-
const commandIndex =
|
|
229
|
+
const commandIndex = effectiveArgs.findIndex(arg => arg === "-c" || arg === "--command");
|
|
171
230
|
if (commandIndex !== -1) {
|
|
172
|
-
return [...
|
|
231
|
+
return [...effectiveArgs.slice(0, commandIndex), "-i", ...effectiveArgs.slice(commandIndex)];
|
|
173
232
|
}
|
|
174
233
|
|
|
175
|
-
const compactCommandIndex =
|
|
234
|
+
const compactCommandIndex = effectiveArgs.findIndex(arg => /^-[^-]*c[^-]*$/.test(arg));
|
|
176
235
|
if (compactCommandIndex !== -1) {
|
|
177
|
-
return
|
|
236
|
+
return effectiveArgs.map((arg, index) => (index === compactCommandIndex ? arg.replace("c", "ic") : arg));
|
|
178
237
|
}
|
|
179
238
|
|
|
180
|
-
return [...
|
|
239
|
+
return [...effectiveArgs, "-i"];
|
|
181
240
|
}
|
|
182
241
|
|
|
183
242
|
function quoteShellArg(value: string): string {
|
|
@@ -224,8 +283,9 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
|
|
224
283
|
|
|
225
284
|
// Apply command prefix if configured
|
|
226
285
|
const prefixedCommand = prefix ? `${prefix} ${command}` : command;
|
|
286
|
+
const runCdInPersistentShell = options?.useUserShell === true && !prefix && isPersistentShellCdCommand(command);
|
|
227
287
|
const finalCommand =
|
|
228
|
-
options?.useUserShell === true && !bashShell
|
|
288
|
+
options?.useUserShell === true && !bashShell && !runCdInPersistentShell
|
|
229
289
|
? buildUserShellCommand(shell, args, prefixedCommand)
|
|
230
290
|
: prefixedCommand;
|
|
231
291
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { replaceTabs, shortenPath, TRUNCATE_LENGTHS, truncateToWidth } from "../../tools/render-utils";
|
|
2
|
+
import type { LoadExtensionsResult } from "./types";
|
|
3
|
+
|
|
4
|
+
/** Formats extension load failures for user-visible startup diagnostics. */
|
|
5
|
+
export function formatExtensionLoadNotifications(errors: LoadExtensionsResult["errors"]): string[] {
|
|
6
|
+
const messages: string[] = [];
|
|
7
|
+
for (const { path, error } of errors) {
|
|
8
|
+
const displayPath = truncateToWidth(replaceTabs(shortenPath(path)), TRUNCATE_LENGTHS.CONTENT);
|
|
9
|
+
const displayError = truncateToWidth(replaceTabs(error.replace(/\s+/g, " ").trim()), TRUNCATE_LENGTHS.LONG);
|
|
10
|
+
messages.push(`Failed to load extension ${displayPath}: ${displayError}`);
|
|
11
|
+
}
|
|
12
|
+
return messages;
|
|
13
|
+
}
|
|
@@ -534,8 +534,9 @@ export class ExtensionRunner {
|
|
|
534
534
|
return undefined;
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
-
|
|
538
|
-
|
|
537
|
+
/** Creates an extension context, optionally scoped to a provider request model. */
|
|
538
|
+
createContext(model?: Model): ExtensionContext {
|
|
539
|
+
const getModel = model ? () => model : this.#getModel;
|
|
539
540
|
return {
|
|
540
541
|
ui: this.#uiContext,
|
|
541
542
|
getContextUsage: () => this.#getContextUsageFn(),
|
|
@@ -965,8 +966,9 @@ export class ExtensionRunner {
|
|
|
965
966
|
return currentMessages;
|
|
966
967
|
}
|
|
967
968
|
|
|
968
|
-
|
|
969
|
-
|
|
969
|
+
/** Runs request payload hooks with the model used for that provider request. */
|
|
970
|
+
async emitBeforeProviderRequest(payload: unknown, model?: Model): Promise<BeforeProviderRequestEventResult> {
|
|
971
|
+
const ctx = this.createContext(model);
|
|
970
972
|
let currentPayload = payload;
|
|
971
973
|
|
|
972
974
|
for (const ext of this.extensions) {
|
|
@@ -22,8 +22,10 @@ import {
|
|
|
22
22
|
getAgentDbPath,
|
|
23
23
|
getAgentDir,
|
|
24
24
|
getProjectDir,
|
|
25
|
+
isCompiledBinary,
|
|
25
26
|
parseFrontmatter as parseOmpFrontmatter,
|
|
26
27
|
} from "@oh-my-pi/pi-utils";
|
|
28
|
+
import { getPackageDir as getOmpPackageDir } from "../config";
|
|
27
29
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
28
30
|
import { type SettingPath, Settings } from "../config/settings";
|
|
29
31
|
import { EditTool } from "../edit";
|
|
@@ -1334,6 +1336,30 @@ export function readStoredCredential(provider: string): AuthCredential | undefin
|
|
|
1334
1336
|
return storage.get(provider);
|
|
1335
1337
|
}
|
|
1336
1338
|
|
|
1339
|
+
// Pi SDK path helpers. `export * from "../index"` above only forwards
|
|
1340
|
+
// `getAgentDir`; `getProjectDir` (a `@oh-my-pi/pi-utils` helper) and
|
|
1341
|
+
// `getPackageDir` are absent from that barrel, so legacy extensions importing
|
|
1342
|
+
// either fail Bun's static export check during validation (issue #5968).
|
|
1343
|
+
export { getProjectDir } from "@oh-my-pi/pi-utils";
|
|
1344
|
+
|
|
1345
|
+
/**
|
|
1346
|
+
* Coding-agent package install directory, matching pi's string-valued
|
|
1347
|
+
* `getPackageDir()` contract (extensions do `path.join(getPackageDir(), ...)`
|
|
1348
|
+
* to auto-allow bundled docs/resources).
|
|
1349
|
+
*
|
|
1350
|
+
* omp's canonical `getPackageDir()` (`../config`) returns `undefined` inside a
|
|
1351
|
+
* `bun --compile` binary — `import.meta.dir` is `/$bunfs/root` and no owning
|
|
1352
|
+
* `package.json` exists (issue #1423). Returning `undefined` there would crash
|
|
1353
|
+
* every legacy `path.join(getPackageDir(), ...)` at runtime in the shipped
|
|
1354
|
+
* binary, the primary distribution. So fall back to the executable's own
|
|
1355
|
+
* directory in compiled mode, where the binary *is* the install root. The
|
|
1356
|
+
* `PI_PACKAGE_DIR` override and dev/source/npm-dist walk-up still win via the
|
|
1357
|
+
* canonical helper.
|
|
1358
|
+
*/
|
|
1359
|
+
export function getPackageDir(): string {
|
|
1360
|
+
return getOmpPackageDir() ?? (isCompiledBinary() ? path.dirname(process.execPath) : process.cwd());
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1337
1363
|
export * from "../index";
|
|
1338
1364
|
export { formatBytes as formatSize } from "../tools/render-utils";
|
|
1339
1365
|
export { Type } from "./typebox";
|
|
@@ -32,6 +32,16 @@ type BundledModule = Readonly<Record<string, unknown>>;
|
|
|
32
32
|
type BundledModules = Readonly<Record<string, BundledModule>>;
|
|
33
33
|
type BundledModuleLoaders = Readonly<Record<string, () => Promise<BundledModule>>>;
|
|
34
34
|
|
|
35
|
+
interface LegacyPiResolveResult {
|
|
36
|
+
path: string;
|
|
37
|
+
namespace?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface BundledVirtualResolveResult {
|
|
41
|
+
path: string;
|
|
42
|
+
namespace: typeof BUNDLED_VIRTUAL_NAMESPACE;
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
const loadedBundledModules: Record<string, BundledModule> = {};
|
|
36
46
|
let bundledModuleLoadersPromise: Promise<BundledModuleLoaders> | null = null;
|
|
37
47
|
|
|
@@ -72,6 +82,25 @@ function isBundledVirtualSpecifier(value: string): boolean {
|
|
|
72
82
|
return value.startsWith(BUNDLED_VIRTUAL_SCHEME);
|
|
73
83
|
}
|
|
74
84
|
|
|
85
|
+
function toLegacyPiResolveResult(resolvedPath: string): LegacyPiResolveResult {
|
|
86
|
+
if (isBundledVirtualSpecifier(resolvedPath)) {
|
|
87
|
+
const registryKey = resolvedPath.slice(BUNDLED_VIRTUAL_SCHEME.length);
|
|
88
|
+
return { path: registryKey, namespace: BUNDLED_VIRTUAL_NAMESPACE };
|
|
89
|
+
}
|
|
90
|
+
return { path: resolvedPath };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Maps a bundled virtual specifier or registry key to Bun's plugin namespace shape. */
|
|
94
|
+
export function resolveBundledVirtualSpecifier(specifier: string): BundledVirtualResolveResult {
|
|
95
|
+
const registryKey = isBundledVirtualSpecifier(specifier)
|
|
96
|
+
? specifier.slice(BUNDLED_VIRTUAL_SCHEME.length)
|
|
97
|
+
: specifier;
|
|
98
|
+
if (!registryKey) {
|
|
99
|
+
throw new Error("omp:legacy-pi-shim: bundled virtual specifier has no registry key");
|
|
100
|
+
}
|
|
101
|
+
return { path: registryKey, namespace: BUNDLED_VIRTUAL_NAMESPACE };
|
|
102
|
+
}
|
|
103
|
+
|
|
75
104
|
/**
|
|
76
105
|
* Build a synthetic ES module for one live bundled namespace. Every export
|
|
77
106
|
* reads through the global bridge; no bunfs path or copied package is involved.
|
|
@@ -1611,7 +1640,7 @@ function getLoader(path: string): "js" | "jsx" | "ts" | "tsx" {
|
|
|
1611
1640
|
return "js";
|
|
1612
1641
|
}
|
|
1613
1642
|
|
|
1614
|
-
function resolveLegacyPiSpecifier(args: { path: string; importer: string }):
|
|
1643
|
+
function resolveLegacyPiSpecifier(args: { path: string; importer: string }): LegacyPiResolveResult | undefined {
|
|
1615
1644
|
const remappedSpecifier = remapLegacyPiSpecifier(args.path);
|
|
1616
1645
|
if (!remappedSpecifier) {
|
|
1617
1646
|
return undefined;
|
|
@@ -1620,7 +1649,7 @@ function resolveLegacyPiSpecifier(args: { path: string; importer: string }): { p
|
|
|
1620
1649
|
// Primary: resolve the canonical @oh-my-pi/* specifier from the host binary
|
|
1621
1650
|
// location. Works in dev mode and in source-link installs.
|
|
1622
1651
|
try {
|
|
1623
|
-
return
|
|
1652
|
+
return toLegacyPiResolveResult(resolveCanonicalPiSpecifier(remappedSpecifier));
|
|
1624
1653
|
} catch {
|
|
1625
1654
|
// Fallback for compiled binary mode: the bundled packages live inside
|
|
1626
1655
|
// /$bunfs/root and aren't reachable by filesystem resolution. Prefer the
|
|
@@ -1630,10 +1659,10 @@ function resolveLegacyPiSpecifier(args: { path: string; importer: string }): { p
|
|
|
1630
1659
|
// @earendil-works peer deps.
|
|
1631
1660
|
const importerDir = path.dirname(args.importer);
|
|
1632
1661
|
try {
|
|
1633
|
-
return
|
|
1662
|
+
return toLegacyPiResolveResult(Bun.resolveSync(remappedSpecifier, importerDir));
|
|
1634
1663
|
} catch {
|
|
1635
1664
|
try {
|
|
1636
|
-
return
|
|
1665
|
+
return toLegacyPiResolveResult(Bun.resolveSync(args.path, importerDir));
|
|
1637
1666
|
} catch {
|
|
1638
1667
|
return undefined;
|
|
1639
1668
|
}
|
|
@@ -1641,8 +1670,8 @@ function resolveLegacyPiSpecifier(args: { path: string; importer: string }): { p
|
|
|
1641
1670
|
}
|
|
1642
1671
|
}
|
|
1643
1672
|
|
|
1644
|
-
function resolveTypeBoxSpecifier():
|
|
1645
|
-
return TYPEBOX_SHIM_PATH ?
|
|
1673
|
+
function resolveTypeBoxSpecifier(): LegacyPiResolveResult | undefined {
|
|
1674
|
+
return TYPEBOX_SHIM_PATH ? toLegacyPiResolveResult(TYPEBOX_SHIM_PATH) : undefined;
|
|
1646
1675
|
}
|
|
1647
1676
|
|
|
1648
1677
|
export function installLegacyPiSpecifierShim(): void {
|
|
@@ -1656,6 +1685,12 @@ export function installLegacyPiSpecifierShim(): void {
|
|
|
1656
1685
|
setup(build) {
|
|
1657
1686
|
build.onResolve({ filter: LEGACY_PI_SPECIFIER_FILTER, namespace: "file" }, resolveLegacyPiSpecifier);
|
|
1658
1687
|
build.onResolve({ filter: TYPEBOX_SPECIFIER_FILTER, namespace: "file" }, resolveTypeBoxSpecifier);
|
|
1688
|
+
build.onResolve({ filter: /^omp-legacy-pi-bundled:.+$/, namespace: "file" }, args =>
|
|
1689
|
+
resolveBundledVirtualSpecifier(args.path),
|
|
1690
|
+
);
|
|
1691
|
+
build.onResolve({ filter: /.*/, namespace: BUNDLED_VIRTUAL_NAMESPACE }, args =>
|
|
1692
|
+
resolveBundledVirtualSpecifier(args.path),
|
|
1693
|
+
);
|
|
1659
1694
|
// Compiled mode serves `omp-legacy-pi-bundled:<key>` imports from
|
|
1660
1695
|
// live host module references. No bunfs path leaves this loader.
|
|
1661
1696
|
build.onLoad({ filter: /.*/, namespace: BUNDLED_VIRTUAL_NAMESPACE }, async args => {
|
package/src/launch/broker.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { Process, type PtyRunResult, PtySession } from "@oh-my-pi/pi-natives";
|
|
6
6
|
import { isEexist, isEnoent, logger, postmortem, procmgr, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { hostHasInheritableConsole } from "../eval/py/spawn-options";
|
|
7
8
|
import { truncateHead, truncateHeadBytes, truncateTail, truncateTailBytes } from "../session/streaming-output";
|
|
8
9
|
import { workerEnvFromParent } from "../subprocess/worker-client";
|
|
9
10
|
import { daemonBrokerEndpoint } from "./paths";
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
parseDaemonSpec,
|
|
25
26
|
parseDaemonWireRequest,
|
|
26
27
|
} from "./protocol";
|
|
28
|
+
import { resolveDaemonSpawnOptions } from "./spawn-options";
|
|
27
29
|
|
|
28
30
|
const DEFAULT_IDLE_GRACE_MS = 3_000;
|
|
29
31
|
const MAX_REQUEST_BYTES = 1024 * 1024;
|
|
@@ -36,6 +38,10 @@ const PID_FILE = "broker.pid";
|
|
|
36
38
|
const META_FILE = "meta.json";
|
|
37
39
|
const LOG_FILE = "output.log";
|
|
38
40
|
const PREVIOUS_LOG_FILE = "output.previous.log";
|
|
41
|
+
const DAEMON_SPAWN_OPTIONS = resolveDaemonSpawnOptions({
|
|
42
|
+
platform: process.platform,
|
|
43
|
+
hostHasInheritableConsole: hostHasInheritableConsole(),
|
|
44
|
+
});
|
|
39
45
|
|
|
40
46
|
const SIGNAL_NUMBER: Record<DaemonSignal, number> = {
|
|
41
47
|
SIGINT: os.constants.signals.SIGINT,
|
|
@@ -537,6 +543,15 @@ class DaemonBroker {
|
|
|
537
543
|
if (error) record.log?.append(`PTY output error: ${error.message}\n`);
|
|
538
544
|
if (chunk) this.#onOutput(record, generation, chunk);
|
|
539
545
|
};
|
|
546
|
+
const started = Promise.withResolvers<number | undefined>();
|
|
547
|
+
const onStart = (error: Error | null, pid: number): void => {
|
|
548
|
+
if (error) {
|
|
549
|
+
record.log?.append(`PTY startup callback failed: ${error.message}\n`);
|
|
550
|
+
started.resolve(undefined);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
started.resolve(Number.isSafeInteger(pid) && pid > 0 ? pid : undefined);
|
|
554
|
+
};
|
|
540
555
|
let run: Promise<PtyRunResult>;
|
|
541
556
|
if (process.platform === "win32") {
|
|
542
557
|
run = session.startArgv(
|
|
@@ -546,41 +561,29 @@ class DaemonBroker {
|
|
|
546
561
|
...options,
|
|
547
562
|
},
|
|
548
563
|
onChunk,
|
|
564
|
+
onStart,
|
|
549
565
|
);
|
|
550
566
|
} else {
|
|
551
|
-
const pidPath = path.join(record.dir, "process.pid");
|
|
552
|
-
await fs.rm(pidPath, { force: true });
|
|
553
567
|
const argv = [record.spec.application, ...record.spec.args];
|
|
554
|
-
const command =
|
|
555
|
-
`printf '%s' "$$" > ${quoteShellArg(pidPath)}`,
|
|
556
|
-
`exec ${argv.map(quoteShellArg).join(" ")}`,
|
|
557
|
-
].join("; ");
|
|
568
|
+
const command = `exec ${argv.map(quoteShellArg).join(" ")}`;
|
|
558
569
|
const shell = procmgr.getShellConfig().shell;
|
|
559
|
-
run = session.start({ command, shell, ...options }, onChunk);
|
|
570
|
+
run = session.start({ command, shell, ...options }, onChunk, onStart);
|
|
560
571
|
}
|
|
561
|
-
void run
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
572
|
+
void run.then(
|
|
573
|
+
async result => {
|
|
574
|
+
await this.#onPtyExit(record, generation, result);
|
|
575
|
+
started.resolve(undefined);
|
|
576
|
+
},
|
|
577
|
+
async error => {
|
|
578
|
+
await this.#settle(record, generation, undefined, error instanceof Error ? error.message : String(error));
|
|
579
|
+
started.resolve(undefined);
|
|
580
|
+
},
|
|
581
|
+
);
|
|
566
582
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
while (Date.now() < deadline && generation === record.generation) {
|
|
572
|
-
try {
|
|
573
|
-
const pid = Number.parseInt((await pidFile.text()).trim(), 10);
|
|
574
|
-
if (Number.isSafeInteger(pid) && pid > 0) {
|
|
575
|
-
record.snapshot.pid = pid;
|
|
576
|
-
this.#persist(record);
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
} catch (error) {
|
|
580
|
-
if (!isEnoent(error)) throw error;
|
|
581
|
-
}
|
|
582
|
-
if (terminalState(record.snapshot.state)) return;
|
|
583
|
-
await Bun.sleep(20);
|
|
583
|
+
const pid = await started.promise;
|
|
584
|
+
if (pid !== undefined && generation === record.generation) {
|
|
585
|
+
record.snapshot.pid = pid;
|
|
586
|
+
this.#persist(record);
|
|
584
587
|
}
|
|
585
588
|
}
|
|
586
589
|
|
|
@@ -591,7 +594,7 @@ class DaemonBroker {
|
|
|
591
594
|
stdin: "pipe",
|
|
592
595
|
stdout: "pipe",
|
|
593
596
|
stderr: "pipe",
|
|
594
|
-
|
|
597
|
+
...DAEMON_SPAWN_OPTIONS,
|
|
595
598
|
});
|
|
596
599
|
record.process = process;
|
|
597
600
|
record.input = process.stdin;
|
|
@@ -614,7 +617,7 @@ class DaemonBroker {
|
|
|
614
617
|
cwd: record.spec.cwd,
|
|
615
618
|
env: workerEnvFromParent(record.spec.env),
|
|
616
619
|
stdio: ["ignore", output.fd, output.fd],
|
|
617
|
-
|
|
620
|
+
...DAEMON_SPAWN_OPTIONS,
|
|
618
621
|
});
|
|
619
622
|
record.process = process;
|
|
620
623
|
record.snapshot.pid = process.pid;
|
package/src/launch/client.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as net from "node:net";
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { isEexist, isEisdir, isEnoent, postmortem } from "@oh-my-pi/pi-utils";
|
|
6
|
+
import { hostHasInheritableConsole } from "../eval/py/spawn-options";
|
|
6
7
|
import { resolveWorkerSpawnCmd, workerEnvFromParent } from "../subprocess/worker-client";
|
|
7
8
|
import { daemonBrokerEndpoint, daemonRuntimeDir } from "./paths";
|
|
8
9
|
import {
|
|
@@ -16,10 +17,15 @@ import {
|
|
|
16
17
|
parseDaemonRpcResult,
|
|
17
18
|
parseDaemonWireResponse,
|
|
18
19
|
} from "./protocol";
|
|
20
|
+
import { resolveDaemonSpawnOptions } from "./spawn-options";
|
|
19
21
|
|
|
20
22
|
const CONNECT_TIMEOUT_MS = 10_000;
|
|
21
23
|
const CONNECT_RETRY_MS = 50;
|
|
22
24
|
const TOKEN_FILE = "broker.token";
|
|
25
|
+
const BROKER_SPAWN_OPTIONS = resolveDaemonSpawnOptions({
|
|
26
|
+
platform: process.platform,
|
|
27
|
+
hostHasInheritableConsole: hostHasInheritableConsole(),
|
|
28
|
+
});
|
|
23
29
|
|
|
24
30
|
interface PendingRequest {
|
|
25
31
|
operation: DaemonOperation;
|
|
@@ -228,7 +234,7 @@ class SocketDaemonClient implements DaemonBrokerClient {
|
|
|
228
234
|
stdin: "ignore",
|
|
229
235
|
stdout: "ignore",
|
|
230
236
|
stderr: "ignore",
|
|
231
|
-
|
|
237
|
+
...BROKER_SPAWN_OPTIONS,
|
|
232
238
|
});
|
|
233
239
|
child.unref();
|
|
234
240
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { resolveDaemonSpawnOptions } from "./spawn-options";
|
|
3
|
+
|
|
4
|
+
describe("resolveDaemonSpawnOptions", () => {
|
|
5
|
+
it("hides Windows daemons when the host has no console", () => {
|
|
6
|
+
expect(
|
|
7
|
+
resolveDaemonSpawnOptions({
|
|
8
|
+
platform: "win32",
|
|
9
|
+
hostHasInheritableConsole: false,
|
|
10
|
+
}),
|
|
11
|
+
).toEqual({ detached: false, windowsHide: true });
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("inherits the Windows host console instead of detaching", () => {
|
|
15
|
+
expect(
|
|
16
|
+
resolveDaemonSpawnOptions({
|
|
17
|
+
platform: "win32",
|
|
18
|
+
hostHasInheritableConsole: true,
|
|
19
|
+
}),
|
|
20
|
+
).toEqual({ detached: false, windowsHide: false });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("keeps POSIX daemons in their own session", () => {
|
|
24
|
+
expect(
|
|
25
|
+
resolveDaemonSpawnOptions({
|
|
26
|
+
platform: "linux",
|
|
27
|
+
hostHasInheritableConsole: false,
|
|
28
|
+
}),
|
|
29
|
+
).toEqual({ detached: true });
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Platform-specific options for the launch broker and its non-PTY children. */
|
|
2
|
+
export interface DaemonSpawnOptions {
|
|
3
|
+
detached: boolean;
|
|
4
|
+
windowsHide?: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** Keep launch processes headless without discarding an inheritable Windows console. */
|
|
8
|
+
export function resolveDaemonSpawnOptions(opts: {
|
|
9
|
+
platform: NodeJS.Platform;
|
|
10
|
+
hostHasInheritableConsole: boolean;
|
|
11
|
+
}): DaemonSpawnOptions {
|
|
12
|
+
if (opts.platform !== "win32") return { detached: true };
|
|
13
|
+
return {
|
|
14
|
+
detached: false,
|
|
15
|
+
windowsHide: !opts.hostHasInheritableConsole,
|
|
16
|
+
};
|
|
17
|
+
}
|
package/src/lsp/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ptree } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import { type } from "arktype";
|
|
3
|
+
import { TOOL_TIMEOUTS } from "../tools/tool-timeouts";
|
|
3
4
|
|
|
4
5
|
// =============================================================================
|
|
5
6
|
// Tool Schema
|
|
@@ -14,7 +15,10 @@ export const lspSchema = type({
|
|
|
14
15
|
query: "string?",
|
|
15
16
|
new_name: "string?",
|
|
16
17
|
apply: "boolean?",
|
|
17
|
-
timeout:
|
|
18
|
+
"timeout?": type.number
|
|
19
|
+
.atLeast(TOOL_TIMEOUTS.lsp.min)
|
|
20
|
+
.atMost(TOOL_TIMEOUTS.lsp.max)
|
|
21
|
+
.describe("Timeout in seconds (default 20; range 5–300)."),
|
|
18
22
|
payload: "string?",
|
|
19
23
|
});
|
|
20
24
|
|
package/src/main.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
logger,
|
|
18
18
|
normalizePathForComparison,
|
|
19
19
|
postmortem,
|
|
20
|
+
setInteractiveHost,
|
|
20
21
|
setProjectDir,
|
|
21
22
|
VERSION,
|
|
22
23
|
} from "@oh-my-pi/pi-utils";
|
|
@@ -49,6 +50,7 @@ import {
|
|
|
49
50
|
resolveActiveProjectRegistryPath,
|
|
50
51
|
} from "./discovery/helpers";
|
|
51
52
|
import { injectOmpExtensionCliRoots } from "./discovery/omp-extension-roots";
|
|
53
|
+
import { formatExtensionLoadNotifications } from "./extensibility/extensions/load-errors";
|
|
52
54
|
import { ExtensionRunner } from "./extensibility/extensions/runner";
|
|
53
55
|
import type { ExtensionUIContext } from "./extensibility/extensions/types";
|
|
54
56
|
import { scheduleMarketplaceAutoUpdate } from "./extensibility/plugins/marketplace-auto-update";
|
|
@@ -56,6 +58,7 @@ import { registerDaemonProjectPresence } from "./launch/presence";
|
|
|
56
58
|
import type { MCPManager } from "./mcp";
|
|
57
59
|
import { InteractiveMode } from "./modes/interactive-mode";
|
|
58
60
|
import type { PrintModeOptions } from "./modes/print-mode";
|
|
61
|
+
import { claimRpcInput } from "./modes/rpc/rpc-input";
|
|
59
62
|
import { CURRENT_SETUP_VERSION } from "./modes/setup-version";
|
|
60
63
|
import { initTheme, stopThemeWatcher } from "./modes/theme/theme";
|
|
61
64
|
import type { SubmittedUserInput } from "./modes/types";
|
|
@@ -97,6 +100,7 @@ type RunRpcMode = (
|
|
|
97
100
|
session: AgentSession,
|
|
98
101
|
setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void,
|
|
99
102
|
eventBus?: EventBus,
|
|
103
|
+
input?: ReadableStream<Uint8Array>,
|
|
100
104
|
) => Promise<never>;
|
|
101
105
|
|
|
102
106
|
export function writeStartupNotice(parsedArgs: Pick<Args, "mode">, text: string): void {
|
|
@@ -884,8 +888,12 @@ export async function buildSessionOptions(
|
|
|
884
888
|
if (resolved.warning) {
|
|
885
889
|
process.stderr.write(`${chalk.yellow(`Warning: ${resolved.warning}`)}\n`);
|
|
886
890
|
}
|
|
887
|
-
|
|
888
|
-
|
|
891
|
+
const matchedAfterMissingRolePattern = (resolved.configuredPatternIndex ?? 0) > 0;
|
|
892
|
+
if (matchedAfterMissingRolePattern) {
|
|
893
|
+
// Extensions may register an earlier configured role candidate.
|
|
894
|
+
options.modelPattern = parsed.model;
|
|
895
|
+
} else if (resolved.error) {
|
|
896
|
+
if (!parsed.provider && ((resolved.configuredPatterns?.length ?? 0) > 0 || !parsed.model.includes(":"))) {
|
|
889
897
|
// Model not found in built-in registry — defer resolution to after extensions load
|
|
890
898
|
// (extensions may register additional providers/models via registerProvider)
|
|
891
899
|
options.modelPattern = parsed.model;
|
|
@@ -1106,6 +1114,9 @@ export async function runRootCommand(
|
|
|
1106
1114
|
process.stderr.write(`${chalk.red("Error: @file arguments are not supported in RPC mode")}\n`);
|
|
1107
1115
|
process.exit(1);
|
|
1108
1116
|
}
|
|
1117
|
+
const mode = parsedArgs.mode || "text";
|
|
1118
|
+
// RPC owns stdin. Claim its singleton stream before plugin/extension discovery can load an in-process consumer.
|
|
1119
|
+
const rpcInput = mode === "rpc" || mode === "rpc-ui" ? claimRpcInput() : undefined;
|
|
1109
1120
|
|
|
1110
1121
|
// Kick off plugin-root preload in parallel with the remaining startup work.
|
|
1111
1122
|
// Awaited later (before extension/skill discovery in createAgentSession needs it).
|
|
@@ -1152,12 +1163,15 @@ export async function runRootCommand(
|
|
|
1152
1163
|
if (parsedArgs.noTitle || parsedArgs.mode === "rpc" || parsedArgs.mode === "rpc-ui" || parsedArgs.mode === "acp") {
|
|
1153
1164
|
Bun.env.PI_NO_TITLE = "1";
|
|
1154
1165
|
}
|
|
1155
|
-
const mode = parsedArgs.mode || "text";
|
|
1156
1166
|
const isProtocolMode = mode === "rpc" || mode === "rpc-ui" || mode === "acp";
|
|
1157
1167
|
// Protocol modes own stdin; treating it as prompt text would consume JSON-RPC frames before their transports start.
|
|
1158
1168
|
const pipedInput = isProtocolMode ? undefined : await logger.time("readPipedInput", readPipedInput);
|
|
1159
1169
|
const autoPrint = pipedInput !== undefined && !parsedArgs.print && parsedArgs.mode === undefined;
|
|
1160
1170
|
const isInteractive = !parsedArgs.print && !autoPrint && parsedArgs.mode === undefined;
|
|
1171
|
+
// Only the interactive host renders a focusable Agent Hub / subagent session
|
|
1172
|
+
// tree; declare it so headless subagent optimizations (e.g. skipping replan
|
|
1173
|
+
// title refresh) can tell a focusable process from a print/RPC/eval one.
|
|
1174
|
+
setInteractiveHost(isInteractive);
|
|
1161
1175
|
|
|
1162
1176
|
// Initialize discovery system with settings for provider persistence
|
|
1163
1177
|
logger.time("initializeWithSettings", initializeWithSettings, settingsInstance);
|
|
@@ -1420,6 +1434,13 @@ export async function runRootCommand(
|
|
|
1420
1434
|
};
|
|
1421
1435
|
const initialArgs = applyExtensionFlags(extensionFlagSink, rawArgs) ?? parsedArgs;
|
|
1422
1436
|
normalizeContinueSessionArgs(initialArgs, rawArgs);
|
|
1437
|
+
for (const message of formatExtensionLoadNotifications(extensionsResult.errors)) {
|
|
1438
|
+
if (isInteractive) {
|
|
1439
|
+
notifs.push({ kind: "warn", message });
|
|
1440
|
+
} else {
|
|
1441
|
+
process.stderr.write(`${chalk.yellow(`${message}\n`)}`);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1423
1444
|
// Fail fast on stale/typo flags (e.g. `omp --list-models`) now that we
|
|
1424
1445
|
// know the real extension flag set. Without this check the unrecognized
|
|
1425
1446
|
// token gets silently consumed and any following positional leaks as the
|
|
@@ -1509,7 +1530,7 @@ export async function runRootCommand(
|
|
|
1509
1530
|
// Branch-only protocol runner: keep RPC host code out of normal interactive startup.
|
|
1510
1531
|
const runRpcMode: RunRpcMode = (await import("./modes/rpc/rpc-mode")).runRpcMode;
|
|
1511
1532
|
stopStartupWatchdog();
|
|
1512
|
-
await runRpcMode(session, mode === "rpc-ui" ? setToolUIContext : undefined, eventBus);
|
|
1533
|
+
await runRpcMode(session, mode === "rpc-ui" ? setToolUIContext : undefined, eventBus, rpcInput);
|
|
1513
1534
|
} else if (isInteractive) {
|
|
1514
1535
|
const versionCheckPromise = checkForNewVersion(VERSION).catch(() => undefined);
|
|
1515
1536
|
const changelogMarkdown = await logger.time("main:getChangelogForDisplay", getChangelogForDisplay, parsedArgs);
|