@oh-my-pi/pi-coding-agent 17.1.8 → 17.2.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 (246) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/dist/{CHANGELOG-vt8ene9g.md → CHANGELOG-dj46zzrm.md} +110 -0
  3. package/dist/cli.js +10009 -9654
  4. package/dist/{template-dys3vk5b.js → template-8vdv6xb4.js} +1 -1
  5. package/dist/types/advisor/runtime.d.ts +8 -2
  6. package/dist/types/advisor/transcript-recorder.d.ts +15 -0
  7. package/dist/types/auto-thinking/classifier.d.ts +9 -2
  8. package/dist/types/cli/args.d.ts +2 -0
  9. package/dist/types/cli/session-picker.d.ts +14 -7
  10. package/dist/types/collab/guest.d.ts +21 -2
  11. package/dist/types/commands/launch.d.ts +6 -0
  12. package/dist/types/config/model-discovery.d.ts +12 -0
  13. package/dist/types/config/settings-schema.d.ts +73 -8
  14. package/dist/types/cursor-bridge-tools.d.ts +54 -0
  15. package/dist/types/cursor.d.ts +193 -8
  16. package/dist/types/edit/edit-clipboard.d.ts +19 -0
  17. package/dist/types/edit/hashline/diff.d.ts +9 -1
  18. package/dist/types/edit/index.d.ts +8 -1
  19. package/dist/types/edit/streaming.d.ts +8 -1
  20. package/dist/types/extensibility/extensions/runner.d.ts +19 -2
  21. package/dist/types/extensibility/extensions/types.d.ts +25 -1
  22. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +25 -0
  23. package/dist/types/internal-urls/index.d.ts +1 -0
  24. package/dist/types/internal-urls/local-protocol.d.ts +3 -2
  25. package/dist/types/internal-urls/security-protocol.d.ts +15 -0
  26. package/dist/types/internal-urls/types.d.ts +1 -1
  27. package/dist/types/launch/terminal-output-worker-client.d.ts +5 -0
  28. package/dist/types/launch/terminal-output-worker-protocol.d.ts +14 -0
  29. package/dist/types/launch/terminal-output-worker.d.ts +1 -0
  30. package/dist/types/lsp/client.d.ts +2 -0
  31. package/dist/types/main.d.ts +2 -0
  32. package/dist/types/mcp/manager.d.ts +33 -4
  33. package/dist/types/modes/components/codex-reset-fireworks.d.ts +51 -0
  34. package/dist/types/modes/components/read-tool-group.d.ts +12 -0
  35. package/dist/types/modes/components/session-selector.d.ts +6 -0
  36. package/dist/types/modes/components/status-line/component.d.ts +23 -0
  37. package/dist/types/modes/components/tool-execution.d.ts +16 -7
  38. package/dist/types/modes/components/usage-row.d.ts +2 -0
  39. package/dist/types/modes/controllers/mcp-command-controller.d.ts +26 -1
  40. package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
  41. package/dist/types/modes/interactive-mode.d.ts +4 -2
  42. package/dist/types/modes/rpc/rpc-client.d.ts +7 -0
  43. package/dist/types/modes/rpc/rpc-types.d.ts +16 -0
  44. package/dist/types/modes/theme/theme.d.ts +12 -1
  45. package/dist/types/modes/types.d.ts +2 -1
  46. package/dist/types/sdk.d.ts +18 -0
  47. package/dist/types/security/auth.d.ts +30 -0
  48. package/dist/types/security/cloud.d.ts +79 -0
  49. package/dist/types/security/comparison.d.ts +49 -0
  50. package/dist/types/security/contracts/ids.d.ts +15 -0
  51. package/dist/types/security/contracts/index.d.ts +4 -0
  52. package/dist/types/security/contracts/schemas.d.ts +660 -0
  53. package/dist/types/security/contracts/types.d.ts +234 -0
  54. package/dist/types/security/contracts/validation.d.ts +5 -0
  55. package/dist/types/security/coordinator.d.ts +100 -0
  56. package/dist/types/security/importers/codex-security.d.ts +7 -0
  57. package/dist/types/security/importers/index.d.ts +2 -0
  58. package/dist/types/security/importers/sarif.d.ts +9 -0
  59. package/dist/types/security/index.d.ts +13 -0
  60. package/dist/types/security/preflight.d.ts +61 -0
  61. package/dist/types/security/provenance.d.ts +24 -0
  62. package/dist/types/security/publication.d.ts +78 -0
  63. package/dist/types/security/remediation.d.ts +27 -0
  64. package/dist/types/security/resource-output.d.ts +8 -0
  65. package/dist/types/security/sarif.d.ts +2 -0
  66. package/dist/types/security/store.d.ts +40 -0
  67. package/dist/types/session/agent-session-events.d.ts +2 -0
  68. package/dist/types/session/agent-session-types.d.ts +36 -1
  69. package/dist/types/session/agent-session.d.ts +4 -2
  70. package/dist/types/session/claude-session-store.d.ts +13 -0
  71. package/dist/types/session/codex-session-store.d.ts +14 -0
  72. package/dist/types/session/credential-pin.d.ts +58 -0
  73. package/dist/types/session/foreign-session-import.d.ts +15 -0
  74. package/dist/types/session/foreign-session-jsonl.d.ts +9 -0
  75. package/dist/types/session/foreign-session-store.d.ts +23 -0
  76. package/dist/types/session/session-advisors.d.ts +47 -2
  77. package/dist/types/session/session-entries.d.ts +19 -1
  78. package/dist/types/session/session-handoff.d.ts +4 -1
  79. package/dist/types/session/session-loader.d.ts +2 -0
  80. package/dist/types/session/session-maintenance.d.ts +1 -1
  81. package/dist/types/session/session-manager.d.ts +21 -0
  82. package/dist/types/session/session-tools.d.ts +25 -1
  83. package/dist/types/session/turn-recovery.d.ts +10 -4
  84. package/dist/types/slash-commands/helpers/security.d.ts +2 -0
  85. package/dist/types/system-prompt.d.ts +4 -1
  86. package/dist/types/task/executor.d.ts +14 -2
  87. package/dist/types/task/render.d.ts +7 -0
  88. package/dist/types/thinking.d.ts +18 -6
  89. package/dist/types/tools/acp-bridge.d.ts +28 -5
  90. package/dist/types/tools/builtin-names.d.ts +1 -1
  91. package/dist/types/tools/grep.d.ts +17 -1
  92. package/dist/types/tools/hub/launch.d.ts +5 -1
  93. package/dist/types/tools/index.d.ts +10 -2
  94. package/dist/types/tools/path-utils.d.ts +19 -0
  95. package/dist/types/tools/read.d.ts +2 -0
  96. package/dist/types/tools/security-scan.d.ts +96 -0
  97. package/dist/types/tools/xdev.d.ts +2 -1
  98. package/dist/types/utils/changelog.d.ts +19 -0
  99. package/dist/types/utils/git.d.ts +10 -0
  100. package/dist/types/utils/jj.d.ts +11 -4
  101. package/dist/types/web/search/types.d.ts +2 -2
  102. package/package.json +12 -12
  103. package/scripts/security-compare.ts +40 -0
  104. package/src/advisor/runtime.ts +138 -12
  105. package/src/advisor/transcript-recorder.ts +56 -0
  106. package/src/auto-thinking/classifier.ts +48 -10
  107. package/src/cli/args.ts +6 -0
  108. package/src/cli/flag-tables.ts +2 -0
  109. package/src/cli/gallery-fixtures/fs.ts +25 -0
  110. package/src/cli/session-picker.ts +34 -17
  111. package/src/cli/ttsr-cli.ts +19 -1
  112. package/src/cli.ts +9 -0
  113. package/src/collab/guest.ts +37 -4
  114. package/src/collab/host.ts +1 -0
  115. package/src/commands/launch.ts +6 -0
  116. package/src/config/model-discovery.ts +32 -5
  117. package/src/config/model-registry.ts +107 -25
  118. package/src/config/settings-schema.ts +77 -6
  119. package/src/config/settings.ts +25 -0
  120. package/src/cursor-bridge-tools.ts +81 -0
  121. package/src/cursor.ts +463 -12
  122. package/src/edit/edit-clipboard.ts +23 -0
  123. package/src/edit/hashline/diff.ts +49 -10
  124. package/src/edit/hashline/execute.ts +38 -6
  125. package/src/edit/hashline/filesystem.ts +27 -3
  126. package/src/edit/index.ts +12 -2
  127. package/src/edit/renderer.ts +4 -4
  128. package/src/edit/streaming.ts +17 -2
  129. package/src/eval/py/prelude.py +7 -3
  130. package/src/export/html/template.js +1 -1
  131. package/src/extensibility/extensions/runner.ts +70 -0
  132. package/src/extensibility/extensions/types.ts +30 -0
  133. package/src/extensibility/legacy-pi-coding-agent-shim.ts +65 -13
  134. package/src/internal-urls/index.ts +1 -0
  135. package/src/internal-urls/local-protocol.ts +3 -2
  136. package/src/internal-urls/mcp-protocol.ts +4 -1
  137. package/src/internal-urls/router.ts +4 -1
  138. package/src/internal-urls/security-protocol.ts +261 -0
  139. package/src/internal-urls/types.ts +1 -1
  140. package/src/launch/terminal-output-worker-client.ts +53 -0
  141. package/src/launch/terminal-output-worker-protocol.ts +11 -0
  142. package/src/launch/terminal-output-worker.ts +23 -0
  143. package/src/lsp/client.ts +18 -3
  144. package/src/lsp/index.ts +7 -0
  145. package/src/lsp/render.ts +1 -1
  146. package/src/main.ts +127 -56
  147. package/src/mcp/manager.ts +120 -18
  148. package/src/modes/acp/acp-agent.ts +15 -9
  149. package/src/modes/components/chat-transcript-builder.ts +30 -11
  150. package/src/modes/components/codex-reset-fireworks.ts +369 -0
  151. package/src/modes/components/read-tool-group.ts +159 -17
  152. package/src/modes/components/session-selector.ts +19 -3
  153. package/src/modes/components/status-line/component.ts +381 -54
  154. package/src/modes/components/tool-execution.ts +78 -31
  155. package/src/modes/components/usage-row.ts +10 -5
  156. package/src/modes/controllers/event-controller.ts +26 -4
  157. package/src/modes/controllers/mcp-command-controller.ts +68 -3
  158. package/src/modes/controllers/selector-controller.ts +94 -36
  159. package/src/modes/controllers/tan-command-controller.ts +14 -0
  160. package/src/modes/interactive-mode.ts +31 -17
  161. package/src/modes/rpc/host-uris.ts +6 -0
  162. package/src/modes/rpc/rpc-client.ts +18 -0
  163. package/src/modes/rpc/rpc-mode.ts +16 -1
  164. package/src/modes/rpc/rpc-types.ts +11 -0
  165. package/src/modes/theme/theme.ts +151 -6
  166. package/src/modes/types.ts +2 -1
  167. package/src/modes/utils/ui-helpers.ts +38 -21
  168. package/src/prompts/agents/security-reviewer.md +75 -0
  169. package/src/prompts/security/scan-coordinator.md +7 -0
  170. package/src/prompts/security/scan-request.md +21 -0
  171. package/src/prompts/security/validate-request.md +8 -0
  172. package/src/prompts/system/auto-thinking-difficulty.md +4 -2
  173. package/src/prompts/system/plan-mode-active.md +2 -2
  174. package/src/prompts/system/system-prompt.md +6 -0
  175. package/src/prompts/system/xdev-mount-notice.md +1 -1
  176. package/src/prompts/tools/bash.md +15 -17
  177. package/src/prompts/tools/checkpoint.md +1 -1
  178. package/src/prompts/tools/glob.md +7 -6
  179. package/src/prompts/tools/grep.md +7 -6
  180. package/src/prompts/tools/security-publish.md +1 -0
  181. package/src/prompts/tools/security-scan.md +1 -0
  182. package/src/sdk.ts +212 -21
  183. package/src/security/auth.ts +98 -0
  184. package/src/security/cloud.ts +686 -0
  185. package/src/security/comparison.ts +255 -0
  186. package/src/security/contracts/ids.ts +111 -0
  187. package/src/security/contracts/index.ts +4 -0
  188. package/src/security/contracts/schemas.ts +201 -0
  189. package/src/security/contracts/types.ts +254 -0
  190. package/src/security/contracts/validation.ts +65 -0
  191. package/src/security/coordinator.ts +708 -0
  192. package/src/security/importers/codex-security.ts +387 -0
  193. package/src/security/importers/index.ts +2 -0
  194. package/src/security/importers/sarif.ts +357 -0
  195. package/src/security/index.ts +13 -0
  196. package/src/security/preflight.ts +405 -0
  197. package/src/security/provenance.ts +106 -0
  198. package/src/security/publication.ts +326 -0
  199. package/src/security/remediation.ts +93 -0
  200. package/src/security/resource-output.ts +50 -0
  201. package/src/security/sarif.ts +78 -0
  202. package/src/security/store.ts +430 -0
  203. package/src/session/agent-session-events.ts +1 -0
  204. package/src/session/agent-session-types.ts +40 -1
  205. package/src/session/agent-session.ts +302 -175
  206. package/src/session/claude-session-store.ts +426 -0
  207. package/src/session/codex-session-store.ts +673 -0
  208. package/src/session/credential-pin.ts +93 -0
  209. package/src/session/foreign-session-import.ts +52 -0
  210. package/src/session/foreign-session-jsonl.ts +29 -0
  211. package/src/session/foreign-session-store.ts +26 -0
  212. package/src/session/model-controls.ts +11 -3
  213. package/src/session/session-advisors.ts +140 -16
  214. package/src/session/session-entries.ts +21 -1
  215. package/src/session/session-handoff.ts +20 -3
  216. package/src/session/session-loader.ts +25 -9
  217. package/src/session/session-maintenance.ts +59 -11
  218. package/src/session/session-manager.ts +61 -0
  219. package/src/session/session-tools.ts +107 -5
  220. package/src/session/settings-stream-fn.ts +1 -0
  221. package/src/session/turn-recovery.ts +97 -51
  222. package/src/slash-commands/builtin-registry.ts +152 -3
  223. package/src/slash-commands/helpers/security.ts +451 -0
  224. package/src/system-prompt.ts +7 -2
  225. package/src/task/agents.ts +2 -0
  226. package/src/task/executor.ts +20 -4
  227. package/src/task/render.ts +20 -4
  228. package/src/task/structured-subagent.ts +6 -4
  229. package/src/thinking.ts +29 -10
  230. package/src/tools/acp-bridge.ts +52 -8
  231. package/src/tools/browser.ts +5 -0
  232. package/src/tools/builtin-names.ts +1 -0
  233. package/src/tools/checkpoint.ts +0 -13
  234. package/src/tools/grep.ts +64 -8
  235. package/src/tools/hub/launch.ts +10 -15
  236. package/src/tools/index.ts +37 -5
  237. package/src/tools/path-utils.ts +91 -0
  238. package/src/tools/read.ts +3 -0
  239. package/src/tools/security-scan.ts +287 -0
  240. package/src/tools/write.ts +9 -3
  241. package/src/tools/xdev.ts +39 -14
  242. package/src/utils/changelog.ts +144 -4
  243. package/src/utils/git.ts +61 -14
  244. package/src/utils/jj.ts +19 -13
  245. package/src/web/search/providers/codex.ts +34 -0
  246. package/src/web/search/types.ts +1 -1
@@ -16,14 +16,37 @@ import type { ToolSession } from "./index.js";
16
16
  * that must stay off the editor's buffer.
17
17
  */
18
18
  export declare function shouldRouteWriteThroughBridge(session: ToolSession, requestedPath: string, absolutePath: string): boolean;
19
+ /**
20
+ * Result of a bridge-routed write: the content actually verified on disk
21
+ * after the client processed the write, plus whether that content diverges
22
+ * from what the tool asked to persist.
23
+ *
24
+ * ACP's `fs/write_text_file` has no "verbatim, no side effects" guarantee —
25
+ * a client (e.g. Zed with `format_on_save: on`) may reformat the buffer as
26
+ * part of handling the write before it settles on disk. Silently trusting
27
+ * the requested `content` as "what's now on disk" lets that drift poison
28
+ * every snapshot/tag/hash a caller derives from the write, which then reads
29
+ * back as unrelated whole-file corruption on the *next* edit. Reading the
30
+ * file back and reporting what's actually there keeps callers honest.
31
+ */
32
+ export interface BridgeWriteResult {
33
+ /** Content actually present on disk immediately after the bridge write. */
34
+ text: string;
35
+ /** `true` when `text` differs from the content the tool asked to write. */
36
+ driftedFromRequest: boolean;
37
+ }
19
38
  /**
20
39
  * Try to route a file write through the ACP client bridge.
21
40
  *
22
41
  * Performs the full guard check, bridge call (wrapped in {@link ToolError}),
23
- * FS-scan cache invalidation, and session mutation-version bump.
42
+ * a post-write read-back to detect client-side transformation (e.g.
43
+ * format-on-save), FS-scan cache invalidation, and session mutation-version
44
+ * bump.
24
45
  *
25
- * Returns `true` when the bridge was used and the caller must skip the
26
- * writethrough path. Returns `false` when the bridge is unavailable or the
27
- * path should not be routed through it.
46
+ * Returns `undefined` when the bridge is unavailable or the path should not
47
+ * be routed through it the caller must fall back to the writethrough path.
48
+ * Returns a {@link BridgeWriteResult} when the bridge was used; callers MUST
49
+ * use `result.text` (not the content they requested) for any snapshot, hash,
50
+ * or tag derived from this write.
28
51
  */
29
- export declare function routeWriteThroughBridge(session: ToolSession, requestedPath: string, absolutePath: string, content: string, signal?: AbortSignal): Promise<boolean>;
52
+ export declare function routeWriteThroughBridge(session: ToolSession, requestedPath: string, absolutePath: string, content: string, signal?: AbortSignal): Promise<BridgeWriteResult | undefined>;
@@ -1,4 +1,4 @@
1
- export declare const BUILTIN_TOOL_NAMES: readonly ["read", "bash", "edit", "ast_grep", "ast_edit", "ask", "debug", "eval", "github", "glob", "grep", "lsp", "inspect_image", "browser", "computer", "checkpoint", "rewind", "task", "hub", "todo", "web_search", "write", "memory_edit", "retain", "recall", "reflect", "learn", "manage_skill"];
1
+ export declare const BUILTIN_TOOL_NAMES: readonly ["read", "bash", "edit", "ast_grep", "ast_edit", "ask", "debug", "eval", "github", "glob", "grep", "lsp", "inspect_image", "browser", "computer", "checkpoint", "rewind", "security_scan", "task", "hub", "todo", "web_search", "write", "memory_edit", "retain", "recall", "reflect", "learn", "manage_skill"];
2
2
  export type BuiltinToolName = (typeof BUILTIN_TOOL_NAMES)[number];
3
3
  /** Hidden built-ins: constructible and `--tools`-addressable, but never part of the default active set. */
4
4
  export declare const HIDDEN_TOOL_NAMES: readonly ["yield", "goal"];
@@ -55,7 +55,23 @@ export interface GrepToolDetails {
55
55
  missingPaths?: string[];
56
56
  }
57
57
  type SearchParams = typeof searchSchema.infer;
58
+ /**
59
+ * Construction-time overrides for callers that are not the model.
60
+ *
61
+ * The model-facing schema deliberately does not grow these: they exist for
62
+ * wire bridges (the Cursor `pi_grep` frame) whose protocol carries an explicit
63
+ * context width and total match cap, and which would otherwise have to drop
64
+ * them. Unset means "use the session settings / built-in caps" — the behavior
65
+ * every model-issued call keeps.
66
+ */
67
+ export interface GrepToolOptions {
68
+ /** Overrides `grep.contextBefore`/`grep.contextAfter` for every call on this instance. */
69
+ context?: number;
70
+ /** Caps total surfaced matches. Applied on top of the built-in per-file and file-window caps, never above them. */
71
+ totalMatchLimit?: number;
72
+ }
58
73
  export declare class GrepTool implements AgentTool<typeof searchSchema, GrepToolDetails> {
74
+ #private;
59
75
  private readonly session;
60
76
  readonly name = "grep";
61
77
  readonly approval: (args: unknown) => ToolTier;
@@ -71,7 +87,7 @@ export declare class GrepTool implements AgentTool<typeof searchSchema, GrepTool
71
87
  skip?: number | null | undefined;
72
88
  }, {}>;
73
89
  readonly strict = true;
74
- constructor(session: ToolSession);
90
+ constructor(session: ToolSession, options?: GrepToolOptions);
75
91
  execute(_toolCallId: string, params: SearchParams, signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<GrepToolDetails>, _toolContext?: AgentToolContext): Promise<AgentToolResult<GrepToolDetails>>;
76
92
  }
77
93
  interface GrepRenderArgs {
@@ -7,7 +7,7 @@
7
7
  import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
8
8
  import type { Component } from "@oh-my-pi/pi-tui";
9
9
  import type { RenderResultOptions } from "../../extensibility/custom-tools/types.js";
10
- import type { DaemonSnapshot, DaemonSpec, DaemonState } from "../../launch/protocol.js";
10
+ import type { DaemonRpcResult, DaemonSnapshot, DaemonSpec, DaemonState } from "../../launch/protocol.js";
11
11
  import type { Theme } from "../../modes/theme/theme.js";
12
12
  import type { ToolSession } from "../index.js";
13
13
  /** Broker-facing launch parameters; the hub adapts its `ps` op to `list` before calling in. */
@@ -57,6 +57,10 @@ export interface LaunchToolDetails {
57
57
  /** describe: immutable launch spec backing the command/cwd detail lines. */
58
58
  spec?: DaemonSpec;
59
59
  }
60
+ /** Resolve display rows while keeping legacy raw replay outside the client process. */
61
+ export declare function renderLaunchLogTerminalRows(result: Extract<DaemonRpcResult, {
62
+ op: "logs";
63
+ }>, params: Pick<LaunchParams, "head" | "lines">): Promise<string[] | undefined>;
60
64
  /** Run one broker operation for the calling session's project. */
61
65
  export declare function executeLaunch(session: ToolSession, params: LaunchParams, signal?: AbortSignal): Promise<AgentToolResult<LaunchToolDetails>>;
62
66
  /** Args shape visible to the renderer, possibly mid-stream (every field optional). */
@@ -1,5 +1,5 @@
1
- import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
2
- import type { AgentTelemetryConfig, AgentTool } from "@oh-my-pi/pi-agent-core";
1
+ import type { Clipboard, InMemorySnapshotStore } from "@oh-my-pi/hashline";
2
+ import type { AgentOptions, AgentTelemetryConfig, AgentTool } from "@oh-my-pi/pi-agent-core";
3
3
  import type { FetchImpl, ImageContent, Model, ServiceTierByFamily, ToolChoice } from "@oh-my-pi/pi-ai";
4
4
  import type { AsyncJobManager } from "../async/job-manager.js";
5
5
  import type { Rule } from "../capability/rule.js";
@@ -65,6 +65,7 @@ export * from "./read.js";
65
65
  export * from "./report-tool-issue.js";
66
66
  export * from "./resolve.js";
67
67
  export * from "./review.js";
68
+ export * from "./security-scan.js";
68
69
  export * from "./todo.js";
69
70
  export * from "./tts.js";
70
71
  export * from "./vibe.js";
@@ -123,6 +124,8 @@ export interface ToolSession {
123
124
  suppressSpawnAdvisory?: boolean;
124
125
  /** Optional fetch implementation injected into the URL read pipeline (tests, proxies). Defaults to global fetch. */
125
126
  fetch?: FetchImpl;
127
+ /** Provider credential resolver forwarded unchanged to restricted child sessions. */
128
+ getApiKey?: AgentOptions["getApiKey"];
126
129
  /** Skip subprocess-kernel availability checks and warmup */
127
130
  skipPythonPreflight?: boolean;
128
131
  /** Pre-loaded context files (AGENTS.md, etc) */
@@ -152,6 +155,8 @@ export interface ToolSession {
152
155
  customToolPaths?: ToolPathWithSource[];
153
156
  /** Whether LSP integrations are enabled */
154
157
  enableLsp?: boolean;
158
+ /** Whether LSP is limited to navigation and diagnostics. */
159
+ lspReadOnly?: boolean;
155
160
  /** Whether this invocation may expose IRC. `false` removes it even for subagents. */
156
161
  enableIrc?: boolean;
157
162
  /**
@@ -321,6 +326,9 @@ export interface ToolSession {
321
326
  * reconstruct the version the model authored anchors against when the
322
327
  * file changed out-of-band. Lazily initialized by `getFileSnapshotStore`. */
323
328
  fileSnapshotStore?: InMemorySnapshotStore;
329
+ /** Per-session `CUT`/`PASTE` clipboard register shared across edit
330
+ * calls. Lazily initialized by `getEditClipboard`. */
331
+ editClipboard?: Clipboard;
324
332
  /** Per-session log of unresolved git merge conflict regions surfaced by
325
333
  * `read`. Each entry gets a stable id N referenced by `write conflict://N`
326
334
  * to splice the recorded region with replacement content. Lazily initialized
@@ -129,6 +129,25 @@ export declare function isReadableUrlPath(value: string): boolean;
129
129
  * filesystem root is almost never what they intended.
130
130
  */
131
131
  export declare function resolveToCwd(filePath: string, cwd: string): string;
132
+ /**
133
+ * Resolve a path that MUST stay inside `cwd`, or `null` when it would escape.
134
+ *
135
+ * {@link resolveToCwd} deliberately honors absolute paths, `~`, and `..` —
136
+ * correct for a path a user typed, wrong for one a remote peer supplied.
137
+ * Callers handling untrusted input (Cursor's `download_path`) use this instead:
138
+ * only a non-empty relative path landing under the live cwd is accepted, so
139
+ * neither `/etc/passwd` nor `../../escape` can be written through.
140
+ *
141
+ * The lexical check alone is not containment: a symlink inside the workspace
142
+ * can point anywhere, so `out/config` under a `ws/out -> /elsewhere` link is
143
+ * relative, `..`-free, and still writes outside. Both the target and its
144
+ * deepest existing ancestor are therefore realpath-resolved — the ancestor
145
+ * because a download names a file that does not exist yet, so the link in its
146
+ * path is the only thing that can be resolved before the write.
147
+ *
148
+ * The cwd itself is rejected: a download names a file, never the directory.
149
+ */
150
+ export declare function confineToWorkspace(filePath: string, cwd: string): string | null;
132
151
  export declare function formatPathRelativeToCwd(filePath: string, cwd: string, options?: {
133
152
  trailingSlash?: boolean;
134
153
  }): string;
@@ -24,6 +24,8 @@ export interface ReadToolDetails {
24
24
  method?: string;
25
25
  notes?: string[];
26
26
  meta?: OutputMeta;
27
+ /** Full on-disk byte size recorded before applying a file range. */
28
+ fileSize?: number;
27
29
  /** Raw text + start line for user-visible TUI rendering, set when content is text-like.
28
30
  * Mirrors the same lines the model receives but without hashline/line-number prefixes,
29
31
  * so the TUI can render the file content with its own gutter without re-parsing the formatted text. */
@@ -0,0 +1,96 @@
1
+ import type { AgentTool, AgentToolResult, ToolTier } from "@oh-my-pi/pi-agent-core";
2
+ import { type CodexSecurityCloudConfiguration, type CodexSecurityCloudStats } from "../security/cloud.js";
3
+ import { type SecurityValidationStatus } from "../security/contracts/index.js";
4
+ import type { SecurityOperationSnapshot } from "../security/coordinator.js";
5
+ import type { ToolSession } from "./index.js";
6
+ declare const securityScanSchema: import("arktype/internal/variants/object.ts").ObjectType<{
7
+ action: "cancel" | "cloud_pull" | "cloud_scans" | "cloud_start" | "cloud_status" | "preflight" | "start" | "status" | "validate";
8
+ plan_id?: string | undefined;
9
+ operation_id?: string | undefined;
10
+ target_kind?: "ref_diff" | "repository" | "scoped_path" | "working_tree" | undefined;
11
+ include_paths?: string[] | undefined;
12
+ exclude_paths?: string[] | undefined;
13
+ base_revision?: string | undefined;
14
+ head_revision?: string | undefined;
15
+ knowledge_base_paths?: string[] | undefined;
16
+ output_root?: string | undefined;
17
+ archive_existing?: boolean | undefined;
18
+ credential_id?: number | undefined;
19
+ scan_id?: string | undefined;
20
+ finding_id?: string | undefined;
21
+ validation_status?: "error" | "partial" | "rejected" | "unvalidated" | "validated" | undefined;
22
+ validation_summary?: string | undefined;
23
+ validation_evidence?: {
24
+ label: string;
25
+ explanation: string;
26
+ }[] | undefined;
27
+ cloud_configuration_id?: string | undefined;
28
+ repository_id?: string | undefined;
29
+ repository_url?: string | undefined;
30
+ environment_id?: string | undefined;
31
+ lookback_days?: number | "all" | undefined;
32
+ }, {}>;
33
+ type SecurityScanParams = typeof securityScanSchema.infer;
34
+ export interface SecurityScanToolDetails {
35
+ action: SecurityScanParams["action"];
36
+ plan?: {
37
+ id: string;
38
+ fingerprint: string;
39
+ };
40
+ operation?: SecurityOperationSnapshot;
41
+ cancelled?: boolean;
42
+ finding?: {
43
+ id: string;
44
+ validationStatus: SecurityValidationStatus;
45
+ };
46
+ cloudConfigurations?: CodexSecurityCloudConfiguration[];
47
+ cloudStats?: CodexSecurityCloudStats;
48
+ cloudScan?: {
49
+ id: string;
50
+ repositoryUrl: string;
51
+ };
52
+ importedScan?: {
53
+ id: string;
54
+ findingCount: number;
55
+ };
56
+ }
57
+ export declare class SecurityScanTool implements AgentTool<typeof securityScanSchema, SecurityScanToolDetails> {
58
+ readonly session: ToolSession;
59
+ readonly name = "security_scan";
60
+ readonly approval: ToolTier;
61
+ readonly label = "Security Scan";
62
+ readonly loadMode = "discoverable";
63
+ readonly summary = "Run OMP-native scans and explicit Codex Security cloud operations";
64
+ readonly description: string;
65
+ readonly parameters: import("arktype/internal/variants/object.ts").ObjectType<{
66
+ action: "cancel" | "cloud_pull" | "cloud_scans" | "cloud_start" | "cloud_status" | "preflight" | "start" | "status" | "validate";
67
+ plan_id?: string | undefined;
68
+ operation_id?: string | undefined;
69
+ target_kind?: "ref_diff" | "repository" | "scoped_path" | "working_tree" | undefined;
70
+ include_paths?: string[] | undefined;
71
+ exclude_paths?: string[] | undefined;
72
+ base_revision?: string | undefined;
73
+ head_revision?: string | undefined;
74
+ knowledge_base_paths?: string[] | undefined;
75
+ output_root?: string | undefined;
76
+ archive_existing?: boolean | undefined;
77
+ credential_id?: number | undefined;
78
+ scan_id?: string | undefined;
79
+ finding_id?: string | undefined;
80
+ validation_status?: "error" | "partial" | "rejected" | "unvalidated" | "validated" | undefined;
81
+ validation_summary?: string | undefined;
82
+ validation_evidence?: {
83
+ label: string;
84
+ explanation: string;
85
+ }[] | undefined;
86
+ cloud_configuration_id?: string | undefined;
87
+ repository_id?: string | undefined;
88
+ repository_url?: string | undefined;
89
+ environment_id?: string | undefined;
90
+ lookback_days?: number | "all" | undefined;
91
+ }, {}>;
92
+ readonly strict = true;
93
+ constructor(session: ToolSession);
94
+ execute(_toolCallId: string, params: SecurityScanParams, signal?: AbortSignal): Promise<AgentToolResult<SecurityScanToolDetails>>;
95
+ }
96
+ export {};
@@ -103,10 +103,11 @@ export declare function resolveMountedXdevTool(state: XdevState, name: string):
103
103
  export declare function resolveMountedXdevExecutable(state: XdevState, name: string): Tool | undefined;
104
104
  /** Mounted tools in presentation order, resolved from the canonical map. */
105
105
  export declare function listXdevTools(state: XdevState): Tool[];
106
- /** `{name, summary}` pairs for prompt templates and `/tools` display. */
106
+ /** `{name, summary, dynamic}` triples for prompt templates and `/tools` display. */
107
107
  export declare function xdevEntries(state: XdevState): Array<{
108
108
  name: string;
109
109
  summary: string;
110
+ dynamic: boolean;
110
111
  }>;
111
112
  /** `read xd://` listing with one device per line. */
112
113
  export declare function xdevListing(state: XdevState): string;
@@ -1,3 +1,4 @@
1
+ import type { SettingValue } from "../config/settings.js";
1
2
  export interface ChangelogEntry {
2
3
  major: number;
3
4
  minor: number;
@@ -21,7 +22,13 @@ export interface StartupChangelogSelection {
21
22
  persistCurrentVersion: boolean;
22
23
  truncated: boolean;
23
24
  selectedEntries: number;
25
+ totalUnseenEntries: number;
26
+ latestVersion: string | undefined;
27
+ changeCount: number;
28
+ categoryCounts: Record<string, number>;
24
29
  }
30
+ /** Format the compact, deterministic startup update notice. */
31
+ export declare function formatStartupChangelogSummary(selection: StartupChangelogSelection): string;
25
32
  /**
26
33
  * Parse changelog entries from omp's package asset when available, falling back
27
34
  * to the copy embedded in compiled binaries.
@@ -55,6 +62,18 @@ export declare function renderChangelogEntries(entries: ChangelogEntry[], option
55
62
  * Select bounded release notes for interactive startup.
56
63
  */
57
64
  export declare function selectStartupChangelog(entries: ChangelogEntry[], lastVersion: string | undefined, currentVersion: string): StartupChangelogSelection;
65
+ /**
66
+ * Resolve and persist the automatic startup changelog decision.
67
+ *
68
+ * Hidden mode advances the marker only for an upgrade, so downgrades do not
69
+ * erase knowledge of a newer version the user has already seen.
70
+ */
71
+ export declare function resolveStartupChangelogForDisplay(options: {
72
+ mode: SettingValue<"startup.changelogMode">;
73
+ currentVersion: string;
74
+ changelogPath?: string;
75
+ agentDir?: string;
76
+ }): Promise<StartupChangelogSelection | undefined>;
58
77
  export { getChangelogPath } from "../config.js";
59
78
  /**
60
79
  * Last omp version whose changelog the user has seen. Stored as a plain-text
@@ -147,6 +147,14 @@ export declare const GIT_COMMAND_TIMEOUT_MS: number;
147
147
  export declare const GIT_NETWORK_TIMEOUT_MS: number;
148
148
  /** Maximum captured stdout or stderr bytes retained from git and gh subprocesses. */
149
149
  export declare const GIT_COMMAND_OUTPUT_LIMIT_BYTES: number;
150
+ /**
151
+ * Deadline for synchronous git plumbing commands launched via
152
+ * {@link gitSpawnSyncText}. These run on the render path (e.g. reftable HEAD
153
+ * resolution), so the deadline is short: a command that has not exited by then
154
+ * is killed and reported as {@link GIT_COMMAND_TIMEOUT_EXIT_CODE} so the caller
155
+ * degrades instead of freezing the UI indefinitely.
156
+ */
157
+ export declare const GIT_SPAWN_SYNC_TIMEOUT_MS = 5000;
150
158
  interface CommandOptions {
151
159
  readonly env?: Record<string, string | undefined>;
152
160
  readonly maxOutputBytes?: number;
@@ -271,6 +279,8 @@ export declare const log: {
271
279
  export declare const revList: {
272
280
  /** Commits in `base..head`, oldest first. */
273
281
  range(cwd: string, base: string, head: string, signal?: AbortSignal): Promise<string[]>;
282
+ /** Commits reachable from `ref` that touched `file`, newest first, capped at `limit`. */
283
+ touching(cwd: string, ref: string, file: string, limit: number, signal?: AbortSignal): Promise<string[]>;
274
284
  };
275
285
  export declare const branch: {
276
286
  /** Current branch name, or null if detached/unavailable. */
@@ -16,14 +16,21 @@ export interface JjRepository {
16
16
  storeDir: string;
17
17
  }
18
18
  /** Options for `jj diff` invocations. */
19
- export interface DiffOptions {
19
+ export interface DiffOptions extends JjCommandOptions {
20
20
  /** Optional file paths to restrict the diff with `-- <files>`. */
21
21
  readonly files?: readonly string[];
22
22
  /** Return only changed file names instead of Git-format diff text. */
23
23
  readonly nameOnly?: boolean;
24
- /** Optional abort signal passed to the spawned `jj` process. */
24
+ }
25
+ /** Options for a bounded `jj` subprocess query. */
26
+ export interface JjCommandOptions {
27
+ /** Optional cancellation signal for the subprocess. */
25
28
  readonly signal?: AbortSignal;
29
+ /** Deadline in milliseconds. Defaults to {@link JJ_COMMAND_TIMEOUT_MS}. */
30
+ readonly timeoutMs?: number;
26
31
  }
32
+ /** Default finite deadline for local jj subprocesses. */
33
+ export declare const JJ_COMMAND_TIMEOUT_MS = 5000;
27
34
  /** Error thrown when a checked `jj` command exits non-zero. */
28
35
  export declare class JjCommandError extends Error {
29
36
  /** Arguments passed after the common `jj --no-pager --color=never` prefix. */
@@ -46,7 +53,7 @@ export declare const workingCopy: {
46
53
  * Label `@` with its nearest bookmark, falling back to its short change ID.
47
54
  * Returns `null` when `jj` is unavailable or the query fails.
48
55
  */
49
- label(cwd: string, signal?: AbortSignal): Promise<string | null>;
56
+ label(cwd: string, options?: JjCommandOptions): Promise<string | null>;
50
57
  /** Parse working-copy label query output. */
51
58
  parseLabel: typeof parseWorkingCopyLabel;
52
59
  };
@@ -56,7 +63,7 @@ export declare const status: {
56
63
  * Count changes in `@` relative to its parent using the Git status shape.
57
64
  * Jujutsu has no index, so `staged` is always zero.
58
65
  */
59
- summary(cwd: string, signal?: AbortSignal): Promise<git.GitStatusSummary | null>;
66
+ summary(cwd: string, options?: JjCommandOptions): Promise<git.GitStatusSummary | null>;
60
67
  /** Parse `jj diff --summary` output into status counts. */
61
68
  parse: typeof parseStatusSummary;
62
69
  };
@@ -34,7 +34,7 @@ export declare const SEARCH_PROVIDER_OPTIONS: readonly [{
34
34
  }, {
35
35
  readonly value: "exa";
36
36
  readonly label: "Exa";
37
- readonly description: "Uses Exa API when EXA_API_KEY is set; falls back to Exa MCP";
37
+ readonly description: "API via /login exa or EXA_API_KEY; explicit keyless fallback via MCP";
38
38
  }, {
39
39
  readonly value: "tinyfish";
40
40
  readonly label: "TinyFish";
@@ -136,7 +136,7 @@ export declare const SEARCH_PROVIDER_CHOICES: ({
136
136
  } | {
137
137
  readonly value: "exa";
138
138
  readonly label: "Exa";
139
- readonly description: "Uses Exa API when EXA_API_KEY is set; falls back to Exa MCP";
139
+ readonly description: "API via /login exa or EXA_API_KEY; explicit keyless fallback via MCP";
140
140
  } | {
141
141
  readonly value: "tinyfish";
142
142
  readonly label: "TinyFish";
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": "17.1.8",
4
+ "version": "17.2.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",
@@ -52,17 +52,17 @@
52
52
  "@agentclientprotocol/sdk": "1.2.1",
53
53
  "@babel/parser": "^7.29.7",
54
54
  "@mozilla/readability": "^0.6.0",
55
- "@oh-my-pi/hashline": "17.1.8",
56
- "@oh-my-pi/omp-stats": "17.1.8",
57
- "@oh-my-pi/pi-agent-core": "17.1.8",
58
- "@oh-my-pi/pi-ai": "17.1.8",
59
- "@oh-my-pi/pi-catalog": "17.1.8",
60
- "@oh-my-pi/pi-mnemopi": "17.1.8",
61
- "@oh-my-pi/pi-natives": "17.1.8",
62
- "@oh-my-pi/pi-tui": "17.1.8",
63
- "@oh-my-pi/pi-utils": "17.1.8",
64
- "@oh-my-pi/pi-wire": "17.1.8",
65
- "@oh-my-pi/snapcompact": "17.1.8",
55
+ "@oh-my-pi/hashline": "17.2.1",
56
+ "@oh-my-pi/omp-stats": "17.2.1",
57
+ "@oh-my-pi/pi-agent-core": "17.2.1",
58
+ "@oh-my-pi/pi-ai": "17.2.1",
59
+ "@oh-my-pi/pi-catalog": "17.2.1",
60
+ "@oh-my-pi/pi-mnemopi": "17.2.1",
61
+ "@oh-my-pi/pi-natives": "17.2.1",
62
+ "@oh-my-pi/pi-tui": "17.2.1",
63
+ "@oh-my-pi/pi-utils": "17.2.1",
64
+ "@oh-my-pi/pi-wire": "17.2.1",
65
+ "@oh-my-pi/snapcompact": "17.2.1",
66
66
  "@opentelemetry/api": "^1.9.1",
67
67
  "@opentelemetry/api-logs": "^0.220.0",
68
68
  "@opentelemetry/context-async-hooks": "^2.9.0",
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bun
2
+ import * as path from "node:path";
3
+ import { isEnoent } from "@oh-my-pi/pi-utils";
4
+ import { compareSecurityProducers, importCodexSecurityBundle, parseSecurityScanBundle } from "../src/security";
5
+
6
+ async function readBundle(directory: string) {
7
+ const root = path.resolve(directory);
8
+ let scan: unknown;
9
+ try {
10
+ scan = JSON.parse(await Bun.file(path.join(root, "scan.json")).text()) as unknown;
11
+ } catch (error) {
12
+ if (!isEnoent(error)) throw error;
13
+ return importCodexSecurityBundle(root, { repositoryRoot: root });
14
+ }
15
+ const findings = JSON.parse(await Bun.file(path.join(root, "findings.json")).text()) as unknown;
16
+ const report = await Bun.file(path.join(root, "report.md"))
17
+ .text()
18
+ .catch(() => undefined);
19
+ const sarifText = await Bun.file(path.join(root, "results.sarif"))
20
+ .text()
21
+ .catch(() => undefined);
22
+ return parseSecurityScanBundle({
23
+ scan,
24
+ findings,
25
+ report,
26
+ sarif: sarifText ? (JSON.parse(sarifText) as Record<string, unknown>) : undefined,
27
+ });
28
+ }
29
+
30
+ const [referenceDirectory, candidateDirectory, outputPath] = process.argv.slice(2);
31
+ if (!referenceDirectory || !candidateDirectory) {
32
+ process.stderr.write(
33
+ "Usage: bun scripts/security-compare.ts <reference-scan-dir> <candidate-scan-dir> [output.json]\n",
34
+ );
35
+ process.exit(2);
36
+ }
37
+ const report = compareSecurityProducers(await readBundle(referenceDirectory), await readBundle(candidateDirectory));
38
+ const serialized = `${JSON.stringify(report, null, 2)}\n`;
39
+ if (outputPath) await Bun.write(path.resolve(outputPath), serialized);
40
+ else process.stdout.write(serialized);