@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.
Files changed (150) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/cli.js +3624 -3568
  3. package/dist/types/advisor/__tests__/config.test.d.ts +1 -0
  4. package/dist/types/advisor/advise-tool.d.ts +8 -4
  5. package/dist/types/advisor/config.d.ts +88 -0
  6. package/dist/types/advisor/index.d.ts +1 -0
  7. package/dist/types/advisor/transcript-recorder.d.ts +13 -2
  8. package/dist/types/advisor/watchdog.d.ts +20 -0
  9. package/dist/types/collab/guest.d.ts +29 -0
  10. package/dist/types/config/settings-schema.d.ts +81 -0
  11. package/dist/types/debug/log-viewer.d.ts +1 -0
  12. package/dist/types/debug/raw-sse.d.ts +1 -0
  13. package/dist/types/edit/hashline/diff.d.ts +0 -11
  14. package/dist/types/extensibility/tool-event-input.d.ts +7 -0
  15. package/dist/types/extensibility/utils.d.ts +12 -0
  16. package/dist/types/mcp/transports/index.d.ts +1 -0
  17. package/dist/types/mcp/transports/sse.d.ts +20 -0
  18. package/dist/types/modes/components/advisor-config.d.ts +59 -0
  19. package/dist/types/modes/components/index.d.ts +1 -0
  20. package/dist/types/modes/components/model-selector.d.ts +9 -1
  21. package/dist/types/modes/components/settings-selector.d.ts +1 -0
  22. package/dist/types/modes/components/status-line/component.d.ts +30 -1
  23. package/dist/types/modes/components/status-line/types.d.ts +13 -1
  24. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  25. package/dist/types/modes/interactive-mode.d.ts +10 -4
  26. package/dist/types/modes/skill-command.d.ts +32 -0
  27. package/dist/types/modes/types.d.ts +7 -2
  28. package/dist/types/session/agent-session.d.ts +58 -10
  29. package/dist/types/session/indexed-session-storage.d.ts +7 -1
  30. package/dist/types/session/messages.d.ts +26 -0
  31. package/dist/types/session/messages.test.d.ts +1 -0
  32. package/dist/types/session/session-entries.d.ts +31 -3
  33. package/dist/types/session/session-history-format.d.ts +6 -0
  34. package/dist/types/session/session-loader.d.ts +9 -1
  35. package/dist/types/session/session-manager.d.ts +6 -4
  36. package/dist/types/session/session-storage.d.ts +11 -0
  37. package/dist/types/session/session-title-slot.d.ts +19 -0
  38. package/dist/types/ssh/connection-manager.d.ts +47 -0
  39. package/dist/types/ssh/utils.d.ts +16 -0
  40. package/dist/types/task/executor.d.ts +3 -16
  41. package/dist/types/task/render.d.ts +0 -5
  42. package/dist/types/task/renderer.d.ts +13 -0
  43. package/dist/types/task/types.d.ts +16 -0
  44. package/dist/types/task/yield-assembly.d.ts +28 -0
  45. package/dist/types/tiny/text.d.ts +8 -0
  46. package/dist/types/tools/render-utils.d.ts +2 -0
  47. package/dist/types/tools/review.d.ts +6 -4
  48. package/dist/types/tools/ssh.d.ts +1 -1
  49. package/dist/types/tools/todo.d.ts +6 -0
  50. package/dist/types/tools/yield.d.ts +8 -3
  51. package/dist/types/utils/thinking-display.d.ts +4 -0
  52. package/package.json +12 -12
  53. package/src/advisor/__tests__/advisor.test.ts +242 -10
  54. package/src/advisor/__tests__/config.test.ts +173 -0
  55. package/src/advisor/advise-tool.ts +11 -6
  56. package/src/advisor/config.ts +256 -0
  57. package/src/advisor/index.ts +1 -0
  58. package/src/advisor/runtime.ts +12 -2
  59. package/src/advisor/transcript-recorder.ts +25 -2
  60. package/src/advisor/watchdog.ts +57 -31
  61. package/src/autoresearch/index.ts +7 -2
  62. package/src/cli/gc-cli.ts +17 -10
  63. package/src/collab/guest.ts +43 -7
  64. package/src/config/model-registry.ts +80 -18
  65. package/src/config/settings-schema.ts +77 -0
  66. package/src/debug/index.ts +32 -7
  67. package/src/debug/log-viewer.ts +111 -53
  68. package/src/debug/raw-sse.ts +68 -48
  69. package/src/discovery/codex.ts +13 -5
  70. package/src/edit/hashline/diff.ts +57 -4
  71. package/src/eval/js/shared/local-module-loader.ts +23 -1
  72. package/src/export/html/template.js +13 -7
  73. package/src/extensibility/extensions/loader.ts +5 -3
  74. package/src/extensibility/extensions/wrapper.ts +9 -3
  75. package/src/extensibility/hooks/loader.ts +3 -3
  76. package/src/extensibility/hooks/tool-wrapper.ts +13 -4
  77. package/src/extensibility/plugins/manager.ts +2 -1
  78. package/src/extensibility/tool-event-input.ts +23 -0
  79. package/src/extensibility/utils.ts +74 -0
  80. package/src/internal-urls/docs-index.generated.txt +1 -1
  81. package/src/mcp/client.ts +3 -1
  82. package/src/mcp/manager.ts +12 -5
  83. package/src/mcp/transports/index.ts +1 -0
  84. package/src/mcp/transports/sse.ts +377 -0
  85. package/src/memories/index.ts +15 -6
  86. package/src/modes/components/advisor-config.ts +555 -0
  87. package/src/modes/components/advisor-message.ts +9 -2
  88. package/src/modes/components/agent-hub.ts +9 -4
  89. package/src/modes/components/index.ts +2 -0
  90. package/src/modes/components/model-selector.ts +79 -48
  91. package/src/modes/components/settings-selector.ts +1 -0
  92. package/src/modes/components/status-line/component.ts +144 -5
  93. package/src/modes/components/status-line/segments.ts +46 -21
  94. package/src/modes/components/status-line/types.ts +13 -1
  95. package/src/modes/components/tool-execution.ts +47 -6
  96. package/src/modes/controllers/command-controller.ts +23 -2
  97. package/src/modes/controllers/event-controller.ts +106 -0
  98. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  99. package/src/modes/controllers/input-controller.ts +61 -61
  100. package/src/modes/controllers/selector-controller.ts +100 -9
  101. package/src/modes/interactive-mode.ts +65 -5
  102. package/src/modes/skill-command.ts +116 -0
  103. package/src/modes/types.ts +7 -2
  104. package/src/modes/utils/ui-helpers.ts +41 -23
  105. package/src/prompts/agents/reviewer.md +11 -10
  106. package/src/prompts/review-custom-request.md +1 -2
  107. package/src/prompts/review-request.md +1 -2
  108. package/src/prompts/system/interrupted-thinking.md +7 -0
  109. package/src/prompts/system/recap-user.md +9 -0
  110. package/src/prompts/system/subagent-system-prompt.md +8 -5
  111. package/src/prompts/system/subagent-yield-reminder.md +6 -5
  112. package/src/prompts/system/system-prompt.md +0 -1
  113. package/src/prompts/tools/irc.md +2 -2
  114. package/src/prompts/tools/read.md +2 -2
  115. package/src/sdk.ts +28 -24
  116. package/src/session/agent-session.ts +867 -428
  117. package/src/session/indexed-session-storage.ts +86 -13
  118. package/src/session/messages.test.ts +125 -0
  119. package/src/session/messages.ts +172 -9
  120. package/src/session/redis-session-storage.ts +49 -2
  121. package/src/session/session-entries.ts +39 -2
  122. package/src/session/session-history-format.ts +29 -2
  123. package/src/session/session-listing.ts +54 -24
  124. package/src/session/session-loader.ts +66 -3
  125. package/src/session/session-manager.ts +113 -19
  126. package/src/session/session-persistence.ts +95 -1
  127. package/src/session/session-storage.ts +36 -0
  128. package/src/session/session-title-slot.ts +141 -0
  129. package/src/session/sql-session-storage.ts +71 -11
  130. package/src/slash-commands/builtin-registry.ts +16 -3
  131. package/src/ssh/__tests__/connection-manager-args.test.ts +123 -1
  132. package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +55 -18
  133. package/src/ssh/connection-manager.ts +139 -12
  134. package/src/ssh/file-transfer.ts +23 -18
  135. package/src/ssh/ssh-executor.ts +2 -13
  136. package/src/ssh/utils.ts +19 -0
  137. package/src/task/executor.ts +21 -23
  138. package/src/task/render.ts +162 -20
  139. package/src/task/renderer.ts +14 -0
  140. package/src/task/types.ts +17 -0
  141. package/src/task/yield-assembly.ts +207 -0
  142. package/src/tiny/text.ts +23 -0
  143. package/src/tools/ask.ts +55 -4
  144. package/src/tools/render-utils.ts +2 -0
  145. package/src/tools/renderers.ts +8 -2
  146. package/src/tools/review.ts +17 -7
  147. package/src/tools/ssh.ts +8 -4
  148. package/src/tools/todo.ts +17 -1
  149. package/src/tools/yield.ts +140 -31
  150. 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 factory(api);
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(this.tool.name, params as Record<string, unknown>),
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(this.tool.name, params as Record<string, unknown>),
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(this.tool.name, params as Record<string, unknown>),
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(this.tool.name, params as Record<string, unknown>),
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(this.tool.name, params as Record<string, unknown>),
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
+ }