@oh-my-pi/pi-coding-agent 16.2.2 → 16.2.3
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 +49 -0
- package/dist/cli.js +3624 -3568
- package/dist/types/advisor/__tests__/config.test.d.ts +1 -0
- package/dist/types/advisor/advise-tool.d.ts +8 -4
- package/dist/types/advisor/config.d.ts +88 -0
- package/dist/types/advisor/index.d.ts +1 -0
- package/dist/types/advisor/transcript-recorder.d.ts +13 -2
- package/dist/types/advisor/watchdog.d.ts +20 -0
- package/dist/types/collab/guest.d.ts +29 -0
- package/dist/types/config/settings-schema.d.ts +81 -0
- package/dist/types/debug/log-viewer.d.ts +1 -0
- package/dist/types/debug/raw-sse.d.ts +1 -0
- package/dist/types/edit/hashline/diff.d.ts +0 -11
- package/dist/types/extensibility/tool-event-input.d.ts +7 -0
- package/dist/types/extensibility/utils.d.ts +12 -0
- package/dist/types/mcp/transports/index.d.ts +1 -0
- package/dist/types/mcp/transports/sse.d.ts +20 -0
- package/dist/types/modes/components/advisor-config.d.ts +59 -0
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +9 -1
- package/dist/types/modes/components/settings-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/component.d.ts +30 -1
- package/dist/types/modes/components/status-line/types.d.ts +13 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +10 -4
- package/dist/types/modes/skill-command.d.ts +32 -0
- package/dist/types/modes/types.d.ts +7 -2
- package/dist/types/session/agent-session.d.ts +58 -10
- package/dist/types/session/indexed-session-storage.d.ts +7 -1
- package/dist/types/session/messages.d.ts +26 -0
- package/dist/types/session/messages.test.d.ts +1 -0
- package/dist/types/session/session-entries.d.ts +31 -3
- package/dist/types/session/session-history-format.d.ts +6 -0
- package/dist/types/session/session-loader.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +6 -4
- package/dist/types/session/session-storage.d.ts +11 -0
- package/dist/types/session/session-title-slot.d.ts +19 -0
- package/dist/types/ssh/connection-manager.d.ts +47 -0
- package/dist/types/ssh/utils.d.ts +16 -0
- package/dist/types/task/executor.d.ts +3 -16
- package/dist/types/task/render.d.ts +0 -5
- package/dist/types/task/renderer.d.ts +13 -0
- package/dist/types/task/types.d.ts +16 -0
- package/dist/types/task/yield-assembly.d.ts +28 -0
- package/dist/types/tiny/text.d.ts +8 -0
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/review.d.ts +6 -4
- package/dist/types/tools/ssh.d.ts +1 -1
- package/dist/types/tools/todo.d.ts +6 -0
- package/dist/types/tools/yield.d.ts +8 -3
- package/dist/types/utils/thinking-display.d.ts +4 -0
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +242 -10
- package/src/advisor/__tests__/config.test.ts +173 -0
- package/src/advisor/advise-tool.ts +11 -6
- package/src/advisor/config.ts +256 -0
- package/src/advisor/index.ts +1 -0
- package/src/advisor/runtime.ts +12 -2
- package/src/advisor/transcript-recorder.ts +25 -2
- package/src/advisor/watchdog.ts +57 -31
- package/src/autoresearch/index.ts +7 -2
- package/src/cli/gc-cli.ts +17 -10
- package/src/collab/guest.ts +43 -7
- package/src/config/model-registry.ts +80 -18
- package/src/config/settings-schema.ts +77 -0
- package/src/debug/index.ts +32 -7
- package/src/debug/log-viewer.ts +111 -53
- package/src/debug/raw-sse.ts +68 -48
- package/src/discovery/codex.ts +13 -5
- package/src/edit/hashline/diff.ts +57 -4
- package/src/eval/js/shared/local-module-loader.ts +23 -1
- package/src/export/html/template.js +13 -7
- package/src/extensibility/extensions/loader.ts +5 -3
- package/src/extensibility/extensions/wrapper.ts +9 -3
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/hooks/tool-wrapper.ts +13 -4
- package/src/extensibility/plugins/manager.ts +2 -1
- package/src/extensibility/tool-event-input.ts +23 -0
- package/src/extensibility/utils.ts +74 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/mcp/client.ts +3 -1
- package/src/mcp/manager.ts +12 -5
- package/src/mcp/transports/index.ts +1 -0
- package/src/mcp/transports/sse.ts +377 -0
- package/src/memories/index.ts +15 -6
- package/src/modes/components/advisor-config.ts +555 -0
- package/src/modes/components/advisor-message.ts +9 -2
- package/src/modes/components/agent-hub.ts +9 -4
- package/src/modes/components/index.ts +2 -0
- package/src/modes/components/model-selector.ts +79 -48
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/component.ts +144 -5
- package/src/modes/components/status-line/segments.ts +46 -21
- package/src/modes/components/status-line/types.ts +13 -1
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/controllers/command-controller.ts +23 -2
- package/src/modes/controllers/event-controller.ts +106 -0
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +61 -61
- package/src/modes/controllers/selector-controller.ts +100 -9
- package/src/modes/interactive-mode.ts +65 -5
- package/src/modes/skill-command.ts +116 -0
- package/src/modes/types.ts +7 -2
- package/src/modes/utils/ui-helpers.ts +41 -23
- package/src/prompts/agents/reviewer.md +11 -10
- package/src/prompts/review-custom-request.md +1 -2
- package/src/prompts/review-request.md +1 -2
- package/src/prompts/system/interrupted-thinking.md +7 -0
- package/src/prompts/system/recap-user.md +9 -0
- package/src/prompts/system/subagent-system-prompt.md +8 -5
- package/src/prompts/system/subagent-yield-reminder.md +6 -5
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/irc.md +2 -2
- package/src/prompts/tools/read.md +2 -2
- package/src/sdk.ts +28 -24
- package/src/session/agent-session.ts +867 -428
- package/src/session/indexed-session-storage.ts +86 -13
- package/src/session/messages.test.ts +125 -0
- package/src/session/messages.ts +172 -9
- package/src/session/redis-session-storage.ts +49 -2
- package/src/session/session-entries.ts +39 -2
- package/src/session/session-history-format.ts +29 -2
- package/src/session/session-listing.ts +54 -24
- package/src/session/session-loader.ts +66 -3
- package/src/session/session-manager.ts +113 -19
- package/src/session/session-persistence.ts +95 -1
- package/src/session/session-storage.ts +36 -0
- package/src/session/session-title-slot.ts +141 -0
- package/src/session/sql-session-storage.ts +71 -11
- package/src/slash-commands/builtin-registry.ts +16 -3
- package/src/ssh/__tests__/connection-manager-args.test.ts +123 -1
- package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +55 -18
- package/src/ssh/connection-manager.ts +139 -12
- package/src/ssh/file-transfer.ts +23 -18
- package/src/ssh/ssh-executor.ts +2 -13
- package/src/ssh/utils.ts +19 -0
- package/src/task/executor.ts +21 -23
- package/src/task/render.ts +162 -20
- package/src/task/renderer.ts +14 -0
- package/src/task/types.ts +17 -0
- package/src/task/yield-assembly.ts +207 -0
- package/src/tiny/text.ts +23 -0
- package/src/tools/ask.ts +55 -4
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/renderers.ts +8 -2
- package/src/tools/review.ts +17 -7
- package/src/tools/ssh.ts +8 -4
- package/src/tools/todo.ts +17 -1
- package/src/tools/yield.ts +140 -31
- package/src/utils/thinking-display.ts +15 -0
|
@@ -24,7 +24,7 @@ import { installLegacyPiSpecifierShim, loadLegacyPiModule } from "../plugins/leg
|
|
|
24
24
|
import { getAllPluginExtensionPaths } from "../plugins/loader";
|
|
25
25
|
import * as TypeBox from "../typebox";
|
|
26
26
|
|
|
27
|
-
import { resolvePath } from "../utils";
|
|
27
|
+
import { resolvePath, withExitGuard } from "../utils";
|
|
28
28
|
import type {
|
|
29
29
|
AssistantThinkingRenderer,
|
|
30
30
|
Extension,
|
|
@@ -290,7 +290,7 @@ async function loadExtension(
|
|
|
290
290
|
): Promise<{ extension: Extension | null; error: string | null }> {
|
|
291
291
|
const resolvedPath = resolvePath(extensionPath, cwd);
|
|
292
292
|
try {
|
|
293
|
-
const module = (await loadLegacyPiModule(resolvedPath)) as LoadedExtensionModule;
|
|
293
|
+
const module = (await withExitGuard(() => loadLegacyPiModule(resolvedPath))) as LoadedExtensionModule;
|
|
294
294
|
const factory = getExtensionFactory(module);
|
|
295
295
|
|
|
296
296
|
if (typeof factory !== "function") {
|
|
@@ -302,7 +302,9 @@ async function loadExtension(
|
|
|
302
302
|
|
|
303
303
|
const extension = createExtension(extensionPath, resolvedPath);
|
|
304
304
|
const api = new ConcreteExtensionAPI(PiCodingAgent, extension, runtime, cwd, eventBus);
|
|
305
|
-
await
|
|
305
|
+
await withExitGuard(async () => {
|
|
306
|
+
await factory(api);
|
|
307
|
+
});
|
|
306
308
|
|
|
307
309
|
return { extension, error: null };
|
|
308
310
|
} catch (err) {
|
|
@@ -6,7 +6,7 @@ import type { ImageContent, Static, TextContent, TSchema } from "@oh-my-pi/pi-ai
|
|
|
6
6
|
import type { Settings } from "../../config/settings";
|
|
7
7
|
import type { Theme } from "../../modes/theme/theme";
|
|
8
8
|
import { type ApprovalMode, formatApprovalPrompt, requiresApproval } from "../../tools/approval";
|
|
9
|
-
import { normalizeToolEventInput } from "../tool-event-input";
|
|
9
|
+
import { normalizeToolEventInput, resolveToolEventInput } from "../tool-event-input";
|
|
10
10
|
import { applyToolProxy } from "../tool-proxy";
|
|
11
11
|
import type { ExtensionRunner } from "./runner";
|
|
12
12
|
import type { RegisteredTool, ToolCallEventResult } from "./types";
|
|
@@ -186,7 +186,10 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
|
|
|
186
186
|
type: "tool_call",
|
|
187
187
|
toolName: this.tool.name,
|
|
188
188
|
toolCallId,
|
|
189
|
-
input: normalizeToolEventInput(
|
|
189
|
+
input: normalizeToolEventInput(
|
|
190
|
+
this.tool.name,
|
|
191
|
+
resolveToolEventInput(this.tool, params as Record<string, unknown>),
|
|
192
|
+
),
|
|
190
193
|
})) as ToolCallEventResult | undefined;
|
|
191
194
|
|
|
192
195
|
if (callResult?.block) {
|
|
@@ -221,7 +224,10 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
|
|
|
221
224
|
type: "tool_result",
|
|
222
225
|
toolName: this.tool.name,
|
|
223
226
|
toolCallId,
|
|
224
|
-
input: normalizeToolEventInput(
|
|
227
|
+
input: normalizeToolEventInput(
|
|
228
|
+
this.tool.name,
|
|
229
|
+
resolveToolEventInput(this.tool, params as Record<string, unknown>),
|
|
230
|
+
),
|
|
225
231
|
content: result.content,
|
|
226
232
|
details: result.details,
|
|
227
233
|
isError: !!executionError,
|
|
@@ -12,7 +12,7 @@ import { loadCapability } from "../../discovery";
|
|
|
12
12
|
import * as PiCodingAgent from "../../index";
|
|
13
13
|
import type { HookMessage } from "../../session/messages";
|
|
14
14
|
import * as typebox from "../typebox";
|
|
15
|
-
import { resolvePath } from "../utils";
|
|
15
|
+
import { resolvePath, withExitGuard } from "../utils";
|
|
16
16
|
import { execCommand } from "./runner";
|
|
17
17
|
import type { ExecOptions, HookAPI, HookFactory, HookMessageRenderer, RegisteredCommand } from "./types";
|
|
18
18
|
|
|
@@ -149,7 +149,7 @@ async function loadHook(hookPath: string, cwd: string): Promise<{ hook: LoadedHo
|
|
|
149
149
|
|
|
150
150
|
try {
|
|
151
151
|
// Import the module using native Bun import
|
|
152
|
-
const module = await import(resolvedPath);
|
|
152
|
+
const module = await withExitGuard(() => import(resolvedPath));
|
|
153
153
|
const factory = module.default as HookFactory;
|
|
154
154
|
|
|
155
155
|
if (typeof factory !== "function") {
|
|
@@ -164,7 +164,7 @@ async function loadHook(hookPath: string, cwd: string): Promise<{ hook: LoadedHo
|
|
|
164
164
|
);
|
|
165
165
|
|
|
166
166
|
// Call factory to register handlers
|
|
167
|
-
factory(api);
|
|
167
|
+
await withExitGuard(async () => factory(api));
|
|
168
168
|
|
|
169
169
|
return {
|
|
170
170
|
hook: {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { AgentTool, AgentToolContext, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
5
5
|
import type { Static, TSchema } from "@oh-my-pi/pi-ai";
|
|
6
|
-
import { normalizeToolEventInput } from "../tool-event-input";
|
|
6
|
+
import { normalizeToolEventInput, resolveToolEventInput } from "../tool-event-input";
|
|
7
7
|
import { applyToolProxy } from "../tool-proxy";
|
|
8
8
|
import type { HookRunner } from "./runner";
|
|
9
9
|
import type { ToolCallEventResult, ToolResultEventResult } from "./types";
|
|
@@ -47,7 +47,10 @@ export class HookToolWrapper<TParameters extends TSchema = TSchema, TDetails = u
|
|
|
47
47
|
type: "tool_call",
|
|
48
48
|
toolName: this.tool.name,
|
|
49
49
|
toolCallId,
|
|
50
|
-
input: normalizeToolEventInput(
|
|
50
|
+
input: normalizeToolEventInput(
|
|
51
|
+
this.tool.name,
|
|
52
|
+
resolveToolEventInput(this.tool, params as Record<string, unknown>),
|
|
53
|
+
),
|
|
51
54
|
})) as ToolCallEventResult | undefined;
|
|
52
55
|
|
|
53
56
|
if (callResult?.block) {
|
|
@@ -73,7 +76,10 @@ export class HookToolWrapper<TParameters extends TSchema = TSchema, TDetails = u
|
|
|
73
76
|
type: "tool_result",
|
|
74
77
|
toolName: this.tool.name,
|
|
75
78
|
toolCallId,
|
|
76
|
-
input: normalizeToolEventInput(
|
|
79
|
+
input: normalizeToolEventInput(
|
|
80
|
+
this.tool.name,
|
|
81
|
+
resolveToolEventInput(this.tool, params as Record<string, unknown>),
|
|
82
|
+
),
|
|
77
83
|
content: result.content,
|
|
78
84
|
details: result.details,
|
|
79
85
|
isError: false,
|
|
@@ -96,7 +102,10 @@ export class HookToolWrapper<TParameters extends TSchema = TSchema, TDetails = u
|
|
|
96
102
|
type: "tool_result",
|
|
97
103
|
toolName: this.tool.name,
|
|
98
104
|
toolCallId,
|
|
99
|
-
input: normalizeToolEventInput(
|
|
105
|
+
input: normalizeToolEventInput(
|
|
106
|
+
this.tool.name,
|
|
107
|
+
resolveToolEventInput(this.tool, params as Record<string, unknown>),
|
|
108
|
+
),
|
|
100
109
|
content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }],
|
|
101
110
|
details: undefined,
|
|
102
111
|
isError: true,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
isEnoent,
|
|
12
12
|
logger,
|
|
13
13
|
} from "@oh-my-pi/pi-utils";
|
|
14
|
+
import { withExitGuard } from "../utils";
|
|
14
15
|
import { type GitSource, parseGitUrl } from "./git-url";
|
|
15
16
|
import { installLegacyPiSpecifierShim, loadLegacyPiModule } from "./legacy-pi-compat";
|
|
16
17
|
import { resolvePluginManifestEntries } from "./loader";
|
|
@@ -363,7 +364,7 @@ export class PluginManager {
|
|
|
363
364
|
installLegacyPiSpecifierShim();
|
|
364
365
|
for (const extensionPath of loadable) {
|
|
365
366
|
try {
|
|
366
|
-
const module = await loadLegacyPiModule(extensionPath);
|
|
367
|
+
const module = await withExitGuard(() => loadLegacyPiModule(extensionPath));
|
|
367
368
|
if (!hasExtensionFactoryExport(module)) {
|
|
368
369
|
errors.push(`${extensionPath}: extension does not export a valid factory function`);
|
|
369
370
|
}
|
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
const HASHLINE_FILE_PREFIX = "¶";
|
|
2
2
|
const HASHLINE_FILE_TAG_RE = /#[0-9a-fA-F]{4}$/u;
|
|
3
3
|
|
|
4
|
+
interface ToolEventInputResolver {
|
|
5
|
+
name: string;
|
|
6
|
+
resolveEventInput?: (input: string) => string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Resolves mode-specific textual tool input before extension/hook event normalization. */
|
|
10
|
+
export function resolveToolEventInput(
|
|
11
|
+
tool: ToolEventInputResolver,
|
|
12
|
+
input: Record<string, unknown>,
|
|
13
|
+
): Record<string, unknown> {
|
|
14
|
+
if (tool.name !== "edit" || typeof tool.resolveEventInput !== "function") return input;
|
|
15
|
+
let resolved = input;
|
|
16
|
+
for (const key of ["input", "_input"] as const) {
|
|
17
|
+
const value = stringField(resolved, key);
|
|
18
|
+
if (value === undefined) continue;
|
|
19
|
+
const nextValue = tool.resolveEventInput(value);
|
|
20
|
+
if (nextValue === value) continue;
|
|
21
|
+
if (resolved === input) resolved = { ...input };
|
|
22
|
+
resolved[key] = nextValue;
|
|
23
|
+
}
|
|
24
|
+
return resolved;
|
|
25
|
+
}
|
|
26
|
+
|
|
4
27
|
function stringField(input: Record<string, unknown>, key: string): string | undefined {
|
|
5
28
|
const value = input[key];
|
|
6
29
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
@@ -42,3 +42,77 @@ export function createNoOpUIContext(): HookUIContext {
|
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Raised by {@link withExitGuard} when a guarded callback synchronously
|
|
48
|
+
* attempts to terminate the host process. Callers catch this like any other
|
|
49
|
+
* load-time failure so the extension/hook is skipped with a logged error
|
|
50
|
+
* instead of taking the CLI down with it.
|
|
51
|
+
*/
|
|
52
|
+
export class ExtensionExitError extends Error {
|
|
53
|
+
readonly code: number | string | undefined;
|
|
54
|
+
constructor(
|
|
55
|
+
code: number | string | undefined,
|
|
56
|
+
readonly alias = "process.exit",
|
|
57
|
+
) {
|
|
58
|
+
super(
|
|
59
|
+
`Module called ${alias}(${code === undefined ? "" : String(code)}) during guarded extension/hook loading; ` +
|
|
60
|
+
`OMP extension/hook modules must not terminate the host process.`,
|
|
61
|
+
);
|
|
62
|
+
this.name = "ExtensionExitError";
|
|
63
|
+
this.code = code;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type ExitAliasName = "process.exit" | "process.reallyExit";
|
|
68
|
+
|
|
69
|
+
let exitGuardDepth = 0;
|
|
70
|
+
let exitGuardOriginalProcessExit: typeof process.exit | null = null;
|
|
71
|
+
let exitGuardOriginalReallyExit: typeof process.reallyExit | null = null;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Run `fn` with hard-exit APIs patched so any synchronous attempt to terminate
|
|
75
|
+
* the host raises {@link ExtensionExitError} instead. Restored in `finally`.
|
|
76
|
+
*
|
|
77
|
+
* Guards the dynamic-import and factory-invocation sites that load third-party
|
|
78
|
+
* extension / hook modules — a `process.exit(0)` or `process.reallyExit(0)` in
|
|
79
|
+
* a stranger's script (e.g. a Codex hook script that happens to live next to
|
|
80
|
+
* OMP-shaped modules) would otherwise kill OMP during startup with no error
|
|
81
|
+
* surface, since `try/catch` cannot intercept a synchronous exit.
|
|
82
|
+
*
|
|
83
|
+
* Nested and concurrent guard windows are safe: only the outermost guard
|
|
84
|
+
* restores the real hard-exit APIs.
|
|
85
|
+
*/
|
|
86
|
+
function guardedExit(alias: ExitAliasName): (code?: number | string) => never {
|
|
87
|
+
return (code?: number | string): never => {
|
|
88
|
+
throw new ExtensionExitError(code, alias);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function withExitGuard<T>(fn: () => Promise<T>): Promise<T> {
|
|
93
|
+
if (exitGuardDepth === 0) {
|
|
94
|
+
exitGuardOriginalProcessExit = process.exit;
|
|
95
|
+
process.exit = guardedExit("process.exit") as typeof process.exit;
|
|
96
|
+
|
|
97
|
+
if (typeof process.reallyExit === "function") {
|
|
98
|
+
exitGuardOriginalReallyExit = process.reallyExit;
|
|
99
|
+
process.reallyExit = guardedExit("process.reallyExit") as typeof process.reallyExit;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exitGuardDepth++;
|
|
103
|
+
try {
|
|
104
|
+
return await fn();
|
|
105
|
+
} finally {
|
|
106
|
+
exitGuardDepth--;
|
|
107
|
+
if (exitGuardDepth === 0) {
|
|
108
|
+
if (exitGuardOriginalProcessExit) {
|
|
109
|
+
process.exit = exitGuardOriginalProcessExit;
|
|
110
|
+
exitGuardOriginalProcessExit = null;
|
|
111
|
+
}
|
|
112
|
+
if (exitGuardOriginalReallyExit) {
|
|
113
|
+
process.reallyExit = exitGuardOriginalReallyExit;
|
|
114
|
+
exitGuardOriginalReallyExit = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|