@oh-my-pi/pi-coding-agent 16.1.15 → 16.1.17

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 (143) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/dist/cli.js +3531 -3812
  3. package/dist/types/auto-thinking/classifier.d.ts +4 -2
  4. package/dist/types/cli/args.d.ts +2 -5
  5. package/dist/types/cli/flag-tables.d.ts +2 -2
  6. package/dist/types/cli/session-picker.d.ts +4 -2
  7. package/dist/types/commands/launch.d.ts +1 -1
  8. package/dist/types/config/model-discovery.d.ts +1 -0
  9. package/dist/types/config/model-registry.d.ts +4 -0
  10. package/dist/types/config/settings-schema.d.ts +12 -1
  11. package/dist/types/eval/agent-bridge.d.ts +19 -0
  12. package/dist/types/eval/js/shared/helpers.d.ts +1 -13
  13. package/dist/types/eval/js/shared/types.d.ts +1 -1
  14. package/dist/types/eval/js/worker-protocol.d.ts +1 -1
  15. package/dist/types/eval/py/executor.d.ts +1 -1
  16. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +22 -7
  17. package/dist/types/extensibility/plugins/loader.d.ts +16 -9
  18. package/dist/types/extensibility/tool-event-input.d.ts +2 -0
  19. package/dist/types/hindsight/content.d.ts +2 -10
  20. package/dist/types/internal-urls/local-protocol.d.ts +18 -1
  21. package/dist/types/main.d.ts +2 -0
  22. package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
  23. package/dist/types/modes/components/custom-editor.d.ts +3 -0
  24. package/dist/types/modes/components/plugin-settings.d.ts +5 -0
  25. package/dist/types/modes/components/session-selector.d.ts +25 -0
  26. package/dist/types/modes/components/status-line/types.d.ts +1 -0
  27. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  28. package/dist/types/modes/controllers/input-controller.d.ts +14 -0
  29. package/dist/types/modes/controllers/selector-controller.d.ts +11 -0
  30. package/dist/types/session/agent-session.d.ts +6 -6
  31. package/dist/types/session/provider-image-budget.d.ts +3 -0
  32. package/dist/types/session/session-context.d.ts +6 -5
  33. package/dist/types/task/isolation-runner.d.ts +128 -0
  34. package/dist/types/task/parallel.d.ts +4 -0
  35. package/dist/types/task/worktree.d.ts +14 -1
  36. package/dist/types/thinking.d.ts +23 -1
  37. package/dist/types/tiny/title-client.d.ts +2 -2
  38. package/dist/types/tools/eval-render.d.ts +3 -0
  39. package/dist/types/tools/eval.d.ts +11 -17
  40. package/dist/types/tools/todo.d.ts +26 -28
  41. package/dist/types/tui/output-block.d.ts +8 -0
  42. package/dist/types/utils/image-resize.d.ts +2 -0
  43. package/dist/types/utils/tools-manager.d.ts +2 -0
  44. package/dist/types/web/search/providers/exa.d.ts +2 -0
  45. package/package.json +12 -12
  46. package/scripts/build-binary.ts +18 -4
  47. package/src/auto-thinking/classifier.ts +7 -2
  48. package/src/cli/args.ts +4 -5
  49. package/src/cli/flag-tables.ts +3 -3
  50. package/src/cli/gallery-fixtures/interaction.ts +6 -9
  51. package/src/cli/gallery-fixtures/shell.ts +15 -23
  52. package/src/cli/profile-alias.ts +38 -7
  53. package/src/cli/session-picker.ts +17 -3
  54. package/src/cli/usage-cli.ts +5 -1
  55. package/src/commands/launch.ts +3 -3
  56. package/src/config/model-discovery.ts +59 -8
  57. package/src/config/model-registry.ts +74 -3
  58. package/src/config/settings-schema.ts +13 -1
  59. package/src/discovery/omp-extension-roots.ts +1 -3
  60. package/src/edit/renderer.ts +34 -12
  61. package/src/eval/__tests__/agent-bridge.test.ts +462 -3
  62. package/src/eval/__tests__/helpers-local-roots.test.ts +2 -5
  63. package/src/eval/__tests__/julia-prelude.test.ts +1 -30
  64. package/src/eval/__tests__/prelude-agent.test.ts +42 -8
  65. package/src/eval/agent-bridge.ts +301 -71
  66. package/src/eval/jl/prelude.jl +32 -227
  67. package/src/eval/jl/runner.jl +38 -12
  68. package/src/eval/js/shared/helpers.ts +1 -114
  69. package/src/eval/js/shared/prelude.txt +13 -27
  70. package/src/eval/js/shared/runtime.ts +0 -6
  71. package/src/eval/js/shared/types.ts +1 -1
  72. package/src/eval/js/worker-protocol.ts +1 -1
  73. package/src/eval/py/__tests__/prelude.test.ts +13 -0
  74. package/src/eval/py/executor.ts +1 -1
  75. package/src/eval/py/prelude.py +47 -105
  76. package/src/eval/py/runner.py +0 -6
  77. package/src/eval/rb/prelude.rb +21 -189
  78. package/src/eval/rb/runner.rb +116 -9
  79. package/src/export/html/tool-views.generated.js +29 -29
  80. package/src/extensibility/extensions/wrapper.ts +3 -2
  81. package/src/extensibility/hooks/tool-wrapper.ts +4 -3
  82. package/src/extensibility/plugins/legacy-pi-compat.ts +40 -8
  83. package/src/extensibility/plugins/loader.ts +71 -23
  84. package/src/extensibility/plugins/marketplace/manager.ts +134 -0
  85. package/src/extensibility/tool-event-input.ts +57 -0
  86. package/src/hindsight/content.ts +21 -12
  87. package/src/internal-urls/docs-index.generated.txt +1 -1
  88. package/src/internal-urls/local-protocol.ts +100 -53
  89. package/src/main.ts +15 -4
  90. package/src/mcp/tool-bridge.ts +27 -2
  91. package/src/mnemopi/state.ts +5 -2
  92. package/src/modes/acp/acp-event-mapper.ts +7 -2
  93. package/src/modes/components/agent-transcript-viewer.ts +193 -41
  94. package/src/modes/components/chat-transcript-builder.ts +6 -0
  95. package/src/modes/components/custom-editor.test.ts +18 -1
  96. package/src/modes/components/custom-editor.ts +77 -45
  97. package/src/modes/components/hook-editor.ts +15 -2
  98. package/src/modes/components/plugin-settings.ts +7 -1
  99. package/src/modes/components/session-selector.ts +143 -29
  100. package/src/modes/components/settings-selector.ts +2 -2
  101. package/src/modes/components/status-line/component.ts +52 -8
  102. package/src/modes/components/status-line/segments.ts +5 -1
  103. package/src/modes/components/status-line/types.ts +1 -0
  104. package/src/modes/components/welcome.ts +12 -14
  105. package/src/modes/controllers/command-controller.ts +21 -5
  106. package/src/modes/controllers/input-controller.ts +115 -3
  107. package/src/modes/controllers/selector-controller.ts +19 -1
  108. package/src/modes/interactive-mode.ts +3 -3
  109. package/src/modes/rpc/rpc-mode.ts +6 -0
  110. package/src/modes/utils/copy-targets.ts +7 -2
  111. package/src/modes/utils/ui-helpers.ts +3 -3
  112. package/src/prompts/system/system-prompt.md +3 -3
  113. package/src/prompts/system/workflow-notice.md +3 -3
  114. package/src/prompts/tools/bash.md +16 -0
  115. package/src/prompts/tools/eval.md +19 -19
  116. package/src/prompts/tools/todo.md +1 -1
  117. package/src/sdk.ts +8 -10
  118. package/src/session/agent-session.ts +422 -97
  119. package/src/session/provider-image-budget.ts +86 -0
  120. package/src/session/session-context.ts +14 -7
  121. package/src/session/session-storage.ts +24 -2
  122. package/src/session/snapcompact-inline.ts +19 -3
  123. package/src/slash-commands/builtin-registry.ts +0 -22
  124. package/src/slash-commands/helpers/usage-report.ts +9 -1
  125. package/src/task/index.ts +61 -207
  126. package/src/task/isolation-runner.ts +354 -0
  127. package/src/task/parallel.ts +6 -1
  128. package/src/task/worktree.ts +46 -9
  129. package/src/thinking.ts +29 -2
  130. package/src/tiny/title-client.ts +75 -21
  131. package/src/tools/ask.ts +44 -38
  132. package/src/tools/bash.ts +9 -2
  133. package/src/tools/browser/tab-worker.ts +1 -1
  134. package/src/tools/eval-render.ts +34 -27
  135. package/src/tools/eval.ts +100 -103
  136. package/src/tools/index.ts +8 -1
  137. package/src/tools/read.ts +136 -60
  138. package/src/tools/todo.ts +60 -64
  139. package/src/tui/code-cell.ts +1 -1
  140. package/src/tui/output-block.ts +11 -0
  141. package/src/utils/image-resize.ts +30 -0
  142. package/src/utils/tools-manager.ts +67 -10
  143. package/src/web/search/providers/exa.ts +85 -1
@@ -0,0 +1,128 @@
1
+ import type * as natives from "@oh-my-pi/pi-natives";
2
+ import type { ToolSession } from "../tools";
3
+ import type { ExecutorOptions } from "./executor";
4
+ import type { SingleResult } from "./types";
5
+ import { type WorktreeBaseline } from "./worktree";
6
+ type IsoBackendKind = natives.IsoBackendKind;
7
+ /** Resolved repo + baseline used by every isolated spawn in a single call. */
8
+ export interface IsolationContext {
9
+ repoRoot: string;
10
+ baseline: WorktreeBaseline;
11
+ }
12
+ /**
13
+ * Resolve the git repo root and capture the worktree baseline used to diff
14
+ * each isolated spawn against. Throws when the cwd is not inside a git
15
+ * repository; callers surface the error as a task-tool failure.
16
+ */
17
+ export declare function prepareIsolationContext(cwd: string): Promise<IsolationContext>;
18
+ /** Build a commit-message callback for branch/nested commits; `undefined` ⇒ fall back to generic message. */
19
+ export type BuildCommitMessage = () => undefined | ((diff: string) => Promise<string | null>);
20
+ /**
21
+ * Construct the commit-message factory used by isolation branch commits and
22
+ * nested-repo patch commits. Returns a closure that, each time it's called,
23
+ * either yields an AI-backed `(diff) => Promise<string|null>` callback (when
24
+ * `task.isolation.commits === "ai"` and a model registry is available) or
25
+ * `undefined` so the caller falls back to a generic commit message.
26
+ *
27
+ * Centralized so `TaskTool` and the eval `agent()` bridge share one wiring;
28
+ * a drift here previously meant the two callers built subtly different
29
+ * generators for the same setting.
30
+ */
31
+ export declare function makeIsolationCommitMessage(session: ToolSession): BuildCommitMessage;
32
+ export interface IsolatedRunOptions {
33
+ /**
34
+ * Base run options handed to the subagent subprocess. This helper sets
35
+ * `worktree`, clears `preloadedExtensionPaths` / `preloadedCustomToolPaths`
36
+ * (isolated runs re-discover inside the worktree), and forwards everything
37
+ * else unchanged.
38
+ */
39
+ baseOptions: ExecutorOptions;
40
+ /** Context returned by {@link prepareIsolationContext}. Baseline is cloned per spawn. */
41
+ context: IsolationContext;
42
+ /** PAL backend hint from `parseIsolationMode(...)` (undefined ⇒ resolver picks). */
43
+ preferredBackend: IsoBackendKind | undefined;
44
+ /** Stable id used as the isolation worktree namespace and as the branch suffix. */
45
+ agentId: string;
46
+ /** Merge mode driving how changes are captured ("branch" commits, "patch" diffs). */
47
+ mergeMode: "patch" | "branch";
48
+ /** Output dir for `${agentId}.patch` artifacts (patch mode). */
49
+ artifactsDir: string;
50
+ /** Human description carried onto the branch commit (branch mode). */
51
+ description?: string;
52
+ /** Build a commit-message callback (`task.isolation.commits === "ai"`). */
53
+ buildCommitMessage?: BuildCommitMessage;
54
+ /**
55
+ * Construct a `SingleResult` when isolation setup throws — the caller has
56
+ * the full metadata (index, agent, assignment, modelOverride) needed to
57
+ * build a result shape consistent with their non-isolated path.
58
+ */
59
+ buildFailureResult: (err: unknown) => SingleResult;
60
+ }
61
+ /**
62
+ * Run a subagent inside an isolation worktree and capture its changes.
63
+ *
64
+ * Branch mode: on success, commits the diff onto `omp/task/${agentId}` and
65
+ * returns `branchName` + `nestedPatches`. On commit failure the branch is
66
+ * deleted and `result.error` carries the merge-failure message.
67
+ *
68
+ * Patch mode: on success, writes `${artifactsDir}/${agentId}.patch` and
69
+ * returns `patchPath` + `nestedPatches`.
70
+ *
71
+ * Failure paths preserve the underlying `SingleResult` whenever possible so
72
+ * the caller can still surface the subagent's output; only isolation setup
73
+ * itself routes through {@link IsolatedRunOptions.buildFailureResult}.
74
+ *
75
+ * The isolation handle is always torn down in `finally`.
76
+ */
77
+ export declare function runIsolatedSubprocess(opts: IsolatedRunOptions): Promise<SingleResult>;
78
+ export interface IsolationMergeOptions {
79
+ result: SingleResult;
80
+ repoRoot: string;
81
+ mergeMode: "patch" | "branch";
82
+ }
83
+ export interface IsolationMergeOutcome {
84
+ /** Trailing summary appended to the subagent's result text. May be empty. */
85
+ summary: string;
86
+ /**
87
+ * Tri-state apply outcome:
88
+ * - `true` — merge ran (or had nothing to apply) and left the repo clean.
89
+ * - `false` — merge attempted and failed; artifacts are preserved.
90
+ * - `null` — caller skipped the merge phase entirely (e.g. `apply=false`).
91
+ */
92
+ changesApplied: boolean | null;
93
+ hadAnyChanges: boolean;
94
+ /** True iff the root branch actually merged — gates nested-repo patch application. */
95
+ mergedBranchForNestedPatches: boolean;
96
+ }
97
+ /**
98
+ * Apply changes captured by {@link runIsolatedSubprocess} back to the parent
99
+ * repo: patch apply (patch mode) or cherry-pick + cleanup (branch mode).
100
+ *
101
+ * The caller decides whether to run this at all — eval `agent()` with
102
+ * `apply=False` skips this step and surfaces the patch artifact / branch name
103
+ * instead.
104
+ */
105
+ export declare function mergeIsolatedChanges(opts: IsolationMergeOptions): Promise<IsolationMergeOutcome>;
106
+ export interface NestedPatchApplyOptions {
107
+ /** Subagent result carrying `nestedPatches`/`exitCode`/`aborted`. */
108
+ result: SingleResult;
109
+ repoRoot: string;
110
+ mergeMode: "patch" | "branch";
111
+ /** Parent merge outcome — patch mode skips nested apply when this is `false`. */
112
+ changesApplied: boolean | null;
113
+ /** Branch mode gates nested apply on whether the root branch merged. */
114
+ mergedBranchForNestedPatches: boolean;
115
+ /** Optional AI commit-message callback for nested commits; falls back to a generic message. */
116
+ commitMessage?: (diff: string) => Promise<string | null>;
117
+ }
118
+ /**
119
+ * Apply nested-repo patches after the parent merge phase. Centralizes the
120
+ * three-way gate (exitCode/aborted, patch-mode failed parent, branch-mode
121
+ * branch-merged) and the non-fatal failure handling so `TaskTool` and the
122
+ * eval `agent()` bridge use one implementation.
123
+ *
124
+ * Returns a system-notification suffix to append to the parent merge summary,
125
+ * or an empty string when nothing was applied or the nested apply succeeded.
126
+ */
127
+ export declare function applyEligibleNestedPatches(opts: NestedPatchApplyOptions): Promise<string>;
128
+ export {};
@@ -25,6 +25,10 @@ export interface ParallelResult<R> {
25
25
  export declare function mapWithConcurrencyLimit<T, R>(items: T[], concurrency: number, fn: (item: T, index: number, signal: AbortSignal) => Promise<R>, signal?: AbortSignal): Promise<ParallelResult<R>>;
26
26
  /**
27
27
  * Simple counting semaphore for limiting concurrency across independently-scheduled async work.
28
+ *
29
+ * `max <= 0` (or any non-finite input) means unbounded — every `acquire()` resolves
30
+ * immediately — matching `task.maxConcurrency = 0`'s "Unlimited" semantics in the
31
+ * settings UI ([#3305](https://github.com/can1357/oh-my-pi/issues/3305)).
28
32
  */
29
33
  export declare class Semaphore {
30
34
  #private;
@@ -33,10 +33,23 @@ export interface DeltaPatchResult {
33
33
  export declare function captureDeltaPatch(isolationDir: string, baseline: WorktreeBaseline): Promise<DeltaPatchResult>;
34
34
  /**
35
35
  * Apply nested repo patches directly to their working directories after parent merge.
36
+ *
37
+ * Pre-existing dirty state in a nested repo is stashed before the patch is
38
+ * applied and popped back (with `--index` so staged WIP stays staged) after
39
+ * the commit, so unrelated user edits never get folded into the agent's
40
+ * commit. A failing `git stash pop` (e.g. user edits collide with the patched
41
+ * lines) leaves the stash entry intact, emits a `logger.warn`, and is
42
+ * returned to the caller as a human-readable warning string — the agent
43
+ * commit already landed, so this is a partial success the workflow needs to
44
+ * see, not a thrown failure.
45
+ *
46
+ * Returns the collected stash-restore warnings (empty when every nested repo
47
+ * was restored cleanly). Throws when the patch apply itself fails.
48
+ *
36
49
  * @param commitMessage Optional async function to generate a commit message from the combined diff.
37
50
  * If omitted or returns null, falls back to a generic message.
38
51
  */
39
- export declare function applyNestedPatches(repoRoot: string, patches: NestedRepoPatch[], commitMessage?: (diff: string) => Promise<string | null>): Promise<void>;
52
+ export declare function applyNestedPatches(repoRoot: string, patches: NestedRepoPatch[], commitMessage?: (diff: string) => Promise<string | null>): Promise<string[]>;
40
53
  /**
41
54
  * User-facing isolation mode names exposed by the `task.isolation.mode`
42
55
  * setting. Mapped to a backend-kind hint via {@link parseIsolationMode};
@@ -55,6 +55,21 @@ export interface ConfiguredThinkingLevelMetadata {
55
55
  export declare function parseConfiguredThinkingLevel(value: string | null | undefined): ConfiguredThinkingLevel | undefined;
56
56
  /** Returns display metadata for a configured selector, including `auto`. */
57
57
  export declare function getConfiguredThinkingLevelMetadata(level: ConfiguredThinkingLevel): ConfiguredThinkingLevelMetadata;
58
+ /**
59
+ * Thinking selectors accepted by the `--thinking` CLI flag, in display order:
60
+ * `off`, every concrete effort (`minimal`..`xhigh`), then `auto`. Single source
61
+ * for the flag's `options` list, shell completions, and the "invalid level"
62
+ * warning so all three stay in sync.
63
+ */
64
+ export declare const CLI_THINKING_LEVELS: readonly string[];
65
+ /**
66
+ * Parses a `--thinking` CLI value. Accepts every {@link parseConfiguredThinkingLevel}
67
+ * selector (`off`, `auto`, `minimal`..`xhigh`, plus the `max` alias) but rejects
68
+ * `inherit`: an explicit `inherit` on the command line would suppress the
69
+ * settings/scoped-model fallback during startup resolution only to resolve back
70
+ * to the provider default, which is never what the user means.
71
+ */
72
+ export declare function parseCliThinkingLevel(value: string | null | undefined): ConfiguredThinkingLevel | undefined;
58
73
  /**
59
74
  * Resolves an auto-classified effort against the active model's supported
60
75
  * range. Unlike {@link clampThinkingLevelForModel}, `auto` never resolves below
@@ -62,8 +77,15 @@ export declare function getConfiguredThinkingLevelMetadata(level: ConfiguredThin
62
77
  * above Low (falling back to the full supported set only when the model maxes
63
78
  * out below Low). Within that pool the request snaps to the highest level not
64
79
  * exceeding it, or the pool minimum when the request is below the pool.
80
+ *
81
+ * Returns `undefined` for reasoning-capable models without a controllable
82
+ * effort surface (`thinking.efforts` empty — e.g. devin-agent models, where
83
+ * Cascade selects effort by routing to sibling model ids). Matches
84
+ * {@link clampThinkingLevelForModel}: with no effort to pick, `auto` must not
85
+ * forward a concrete effort that would then trip {@link requireSupportedEffort}
86
+ * downstream.
65
87
  */
66
- export declare function clampAutoThinkingEffort(model: Model | undefined, effort: Effort): Effort;
88
+ export declare function clampAutoThinkingEffort(model: Model | undefined, effort: Effort): Effort | undefined;
67
89
  /**
68
90
  * The provisional concrete level shown while `auto` is configured but before a
69
91
  * turn has been classified. Prefers the model's `defaultLevel`, otherwise High,
@@ -1,4 +1,4 @@
1
- import { type SpawnedSubprocess, type WorkerHandle } from "../subprocess/worker-client";
1
+ import { type RefCountedWorkerHandle, type SpawnedSubprocess } from "../subprocess/worker-client";
2
2
  import type { TinyTitleProgressEvent, TinyTitleWorkerInbound, TinyTitleWorkerOutbound } from "./title-protocol";
3
3
  export interface TinyTitleDownloadOptions {
4
4
  signal?: AbortSignal;
@@ -44,7 +44,7 @@ export declare function tinyWorkerEnv(): Record<string, string>;
44
44
  export declare function createTinyTitleSubprocess(): SpawnedSubprocess<TinyTitleWorkerOutbound>;
45
45
  export declare class TinyTitleClient {
46
46
  #private;
47
- constructor(spawnWorker?: () => WorkerHandle<TinyTitleWorkerInbound, TinyTitleWorkerOutbound>);
47
+ constructor(spawnWorker?: () => RefCountedWorkerHandle<TinyTitleWorkerInbound, TinyTitleWorkerOutbound>);
48
48
  onProgress(listener: (event: TinyTitleProgressEvent) => void): () => void;
49
49
  generate(modelKey: string, message: string, signal?: AbortSignal): Promise<string | null>;
50
50
  generate(modelKey: string, message: string, options?: TinyTitleGenerateOptions): Promise<string | null>;
@@ -20,6 +20,9 @@ interface EvalRenderCellArg {
20
20
  title?: string;
21
21
  }
22
22
  interface EvalRenderArgs {
23
+ language?: string;
24
+ code?: string;
25
+ title?: string;
23
26
  cells?: EvalRenderCellArg[];
24
27
  __partialJson?: string;
25
28
  }
@@ -6,29 +6,21 @@ export { EVAL_DEFAULT_PREVIEW_LINES, evalToolRenderer } from "./eval-render";
6
6
  /** Language tokens the eval tool accepts, in stable display order. */
7
7
  export type EvalLanguageToken = "py" | "js" | "rb" | "jl";
8
8
  /**
9
- * Per-cell input. Each cell runs in order; state persists within a language
10
- * across cells and across tool calls. This static schema carries the full
11
- * language union for typing; {@link buildEvalSchema} narrows the wire copy per
12
- * session so disabled backends are never advertised to the model.
9
+ * Per-call input: a single cell. State persists within a language across
10
+ * separate eval calls and across tool calls, so each call is one logical step
11
+ * and later calls reuse what earlier ones defined. This static schema carries
12
+ * the full language union for typing; {@link buildEvalSchema} narrows the wire
13
+ * copy per session so disabled backends are never advertised to the model.
13
14
  */
14
- declare const evalCellSchema: import("arktype/internal/variants/object.ts").ObjectType<{
15
+ export declare const evalSchema: import("arktype/internal/variants/object.ts").ObjectType<{
15
16
  title?: string | undefined;
16
17
  timeout?: number | undefined;
17
18
  reset?: boolean | undefined;
18
19
  language: "jl" | "js" | "py" | "rb";
19
20
  code: string;
20
21
  }, {}>;
21
- export type EvalCellInput = typeof evalCellSchema.infer;
22
- export declare const evalSchema: import("arktype/internal/variants/object.ts").ObjectType<{
23
- cells: {
24
- title?: string | undefined;
25
- timeout?: number | undefined;
26
- reset?: boolean | undefined;
27
- language: "jl" | "js" | "py" | "rb";
28
- code: string;
29
- }[];
30
- }, {}>;
31
22
  export type EvalToolParams = typeof evalSchema.infer;
23
+ export type EvalCellInput = EvalToolParams;
32
24
  export type EvalToolResult = {
33
25
  content: Array<{
34
26
  type: "text";
@@ -62,10 +54,12 @@ export declare class EvalTool implements AgentTool<typeof evalSchema> {
62
54
  readonly approval: "exec";
63
55
  readonly formatApprovalDetails: (args: unknown) => string[];
64
56
  get summary(): string;
65
- readonly loadMode = "discoverable";
57
+ readonly loadMode = "essential";
66
58
  readonly label = "Eval";
67
59
  get description(): string;
68
- readonly examples: readonly ToolExample<typeof evalSchema.infer>[];
60
+ /** All reuse-chain examples; the `examples` getter filters by enabled languages. */
61
+ private static readonly ALL_EXAMPLES;
62
+ get examples(): readonly ToolExample<typeof evalSchema.infer>[];
69
63
  get parameters(): typeof evalSchema;
70
64
  readonly concurrency = "exclusive";
71
65
  readonly strict = true;
@@ -24,16 +24,14 @@ export interface TodoToolDetails {
24
24
  completedTasks?: TodoCompletionTransition[];
25
25
  }
26
26
  declare const todoSchema: import("arktype/internal/variants/object.ts").ObjectType<{
27
- ops: {
28
- op: "append" | "done" | "drop" | "init" | "rm" | "start" | "view";
29
- list?: {
30
- phase: string;
31
- items: string[];
32
- }[] | undefined;
33
- task?: string | undefined;
34
- phase?: string | undefined;
35
- items?: string[] | undefined;
36
- }[];
27
+ op: "append" | "done" | "drop" | "init" | "rm" | "start" | "view";
28
+ list?: {
29
+ phase: string;
30
+ items: string[];
31
+ }[] | undefined;
32
+ task?: string | undefined;
33
+ phase?: string | undefined;
34
+ items?: string[] | undefined;
37
35
  }, {}>;
38
36
  type TodoParams = TodoSchema;
39
37
  type TodoSchema = typeof todoSchema.infer;
@@ -70,7 +68,7 @@ export declare function selectStickyTodoWindow(tasks: TodoItem[], maxVisible?: n
70
68
  */
71
69
  export declare function todoMatchesAnyDescription(content: string, descriptions: readonly string[]): boolean;
72
70
  /** Apply an array of `todo`-style ops to existing phases. Used by /todo slash command. */
73
- export declare function applyOpsToPhases(currentPhases: TodoPhase[], ops: TodoParams["ops"]): {
71
+ export declare function applyOpsToPhases(currentPhases: TodoPhase[], ops: TodoParams[]): {
74
72
  phases: TodoPhase[];
75
73
  errors: string[];
76
74
  };
@@ -90,16 +88,14 @@ export declare class TodoTool implements AgentTool<typeof todoSchema, TodoToolDe
90
88
  readonly summary = "Write a structured todo list to track progress within a session";
91
89
  readonly description: string;
92
90
  readonly parameters: import("arktype/internal/variants/object.ts").ObjectType<{
93
- ops: {
94
- op: "append" | "done" | "drop" | "init" | "rm" | "start" | "view";
95
- list?: {
96
- phase: string;
97
- items: string[];
98
- }[] | undefined;
99
- task?: string | undefined;
100
- phase?: string | undefined;
101
- items?: string[] | undefined;
102
- }[];
91
+ op: "append" | "done" | "drop" | "init" | "rm" | "start" | "view";
92
+ list?: {
93
+ phase: string;
94
+ items: string[];
95
+ }[] | undefined;
96
+ task?: string | undefined;
97
+ phase?: string | undefined;
98
+ items?: string[] | undefined;
103
99
  }, {}>;
104
100
  readonly concurrency = "exclusive";
105
101
  readonly strict = true;
@@ -108,13 +104,15 @@ export declare class TodoTool implements AgentTool<typeof todoSchema, TodoToolDe
108
104
  constructor(session: ToolSession);
109
105
  execute(_toolCallId: string, params: TodoParams, _signal?: AbortSignal, _onUpdate?: AgentToolUpdateCallback<TodoToolDetails>, _context?: AgentToolContext): Promise<AgentToolResult<TodoToolDetails>>;
110
106
  }
111
- type TodoRenderArgs = {
112
- ops?: Array<{
113
- op?: string;
114
- task?: string;
115
- phase?: string;
116
- items?: string[];
117
- }>;
107
+ type TodoRenderOp = {
108
+ op?: string;
109
+ task?: string;
110
+ phase?: string;
111
+ items?: string[];
112
+ };
113
+ /** New single-op shape `{op,...}`; legacy `{ops:[...]}` still seen in old transcripts. */
114
+ type TodoRenderArgs = TodoRenderOp & {
115
+ ops?: TodoRenderOp[];
118
116
  };
119
117
  /** One-based ASCII roman numeral for display (I, II, III, IV, …). */
120
118
  export declare function phaseRomanNumeral(oneBasedIndex: number): string;
@@ -26,6 +26,14 @@ export type FramedBlockComponent = Component & {
26
26
  };
27
27
  export declare function markFramedBlockComponent<T extends Component>(component: T): T & FramedBlockComponent;
28
28
  export declare function isFramedBlockComponent(component: Component): boolean;
29
+ /**
30
+ * Inner content width that {@link renderOutputBlock} wraps its body to, for a
31
+ * given outer `width`: both vertical borders (1 cell each) plus the left
32
+ * content padding. Renderers that size a tail window MUST budget visual rows
33
+ * against this, not the outer width — otherwise the block re-wraps their lines
34
+ * into more rows than they counted and the box overflows its intended height.
35
+ */
36
+ export declare function outputBlockContentWidth(width: number, contentPaddingLeft?: number): number;
29
37
  export declare function renderOutputBlock(options: OutputBlockOptions, theme: Theme): string[];
30
38
  /**
31
39
  * Cached wrapper around `renderOutputBlock`.
@@ -2,6 +2,8 @@ import type { ImageContent } from "@oh-my-pi/pi-ai";
2
2
  export interface ImageResizeOptions {
3
3
  maxWidth?: number;
4
4
  maxHeight?: number;
5
+ /** Smallest allowed edge length (px). Inputs below this are scaled up. */
6
+ minDimension?: number;
5
7
  maxBytes?: number;
6
8
  jpegQuality?: number;
7
9
  excludeWebP?: boolean;
@@ -1,6 +1,8 @@
1
1
  export declare function ffmpegAssetName(_version: string, plat: string, architecture: string): string | null;
2
2
  export type ToolName = "sd" | "sg" | "yt-dlp" | "trafilatura" | "ffmpeg";
3
3
  export declare function getToolPath(tool: ToolName): string | null;
4
+ /** Download a tool asset without handing the streaming Response to Bun.write. */
5
+ export declare function downloadFile(url: string, dest: string, signal?: AbortSignal): Promise<void>;
4
6
  type EnsureToolOptions = {
5
7
  signal?: AbortSignal;
6
8
  silent?: boolean;
@@ -10,6 +10,8 @@ import { type AuthStorage, type FetchImpl } from "@oh-my-pi/pi-ai";
10
10
  import type { SearchResponse } from "../../../web/search/types";
11
11
  import type { SearchParams } from "./base";
12
12
  import { SearchProvider } from "./base";
13
+ /** Reset Exa request pacing state for isolated provider tests. */
14
+ export declare function resetExaSearchThrottleForTest(): void;
13
15
  type ExaSearchType = "neural" | "fast" | "auto" | "deep";
14
16
  type ExaSearchParamType = ExaSearchType | "keyword";
15
17
  export interface ExaSearchParams {
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": "16.1.15",
4
+ "version": "16.1.17",
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",
@@ -48,17 +48,17 @@
48
48
  "@agentclientprotocol/sdk": "0.25.0",
49
49
  "@babel/parser": "^7.29.7",
50
50
  "@mozilla/readability": "^0.6.0",
51
- "@oh-my-pi/hashline": "16.1.15",
52
- "@oh-my-pi/omp-stats": "16.1.15",
53
- "@oh-my-pi/pi-agent-core": "16.1.15",
54
- "@oh-my-pi/pi-ai": "16.1.15",
55
- "@oh-my-pi/pi-catalog": "16.1.15",
56
- "@oh-my-pi/pi-mnemopi": "16.1.15",
57
- "@oh-my-pi/pi-natives": "16.1.15",
58
- "@oh-my-pi/pi-tui": "16.1.15",
59
- "@oh-my-pi/pi-utils": "16.1.15",
60
- "@oh-my-pi/pi-wire": "16.1.15",
61
- "@oh-my-pi/snapcompact": "16.1.15",
51
+ "@oh-my-pi/hashline": "16.1.17",
52
+ "@oh-my-pi/omp-stats": "16.1.17",
53
+ "@oh-my-pi/pi-agent-core": "16.1.17",
54
+ "@oh-my-pi/pi-ai": "16.1.17",
55
+ "@oh-my-pi/pi-catalog": "16.1.17",
56
+ "@oh-my-pi/pi-mnemopi": "16.1.17",
57
+ "@oh-my-pi/pi-natives": "16.1.17",
58
+ "@oh-my-pi/pi-tui": "16.1.17",
59
+ "@oh-my-pi/pi-utils": "16.1.17",
60
+ "@oh-my-pi/pi-wire": "16.1.17",
61
+ "@oh-my-pi/snapcompact": "16.1.17",
62
62
  "@opentelemetry/api": "^1.9.1",
63
63
  "@opentelemetry/context-async-hooks": "^2.7.1",
64
64
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
@@ -5,7 +5,15 @@ import * as path from "node:path";
5
5
 
6
6
  const packageDir = path.join(import.meta.dir, "..");
7
7
  const repoRoot = path.join(packageDir, "..", "..");
8
- const outputPath = path.join(packageDir, "dist", "omp");
8
+ // Optional cross-compile target, e.g. CROSS_TARGET=linux-arm64 → bun build
9
+ // --target=bun-linux-arm64, embeds the matching native, outputs dist/omp-<target>.
10
+ const crossTarget = Bun.env.CROSS_TARGET || null;
11
+ const [crossPlatform, crossArch] = crossTarget ? crossTarget.split("-") : [null, null];
12
+ // x64 uses the baseline bun runtime so it runs under Rosetta / pre-AVX2 CPUs
13
+ // (the modern bun-linux-x64 target SIGILLs under Apple-Silicon Rosetta).
14
+ const bunTarget = crossTarget ? (crossTarget === "linux-x64" ? "bun-linux-x64-baseline" : `bun-${crossTarget}`) : null;
15
+ const outName = crossTarget ? `omp-${crossTarget}` : "omp";
16
+ const outputPath = path.join(packageDir, "dist", outName);
9
17
 
10
18
  // Transformers.js is an optional, native-heavy dependency that is never bundled
11
19
  // into the binary; the tiny-model worker `bun install`s it into a runtime cache
@@ -17,7 +25,7 @@ const transformersVersion = (
17
25
  ).version;
18
26
 
19
27
  function shouldAdhocSignDarwinBinary(): boolean {
20
- return process.platform === "darwin";
28
+ return process.platform === "darwin" && !crossTarget;
21
29
  }
22
30
 
23
31
  async function runCommand(
@@ -43,7 +51,12 @@ async function main(): Promise<void> {
43
51
  try {
44
52
  await runCommand(["bun", "--cwd=../stats", "scripts/generate-client-bundle.ts", "--generate"]);
45
53
  await runCommand(["bun", "scripts/generate-docs-index.ts", "--generate"]);
46
- await runCommand(["bun", "--cwd=../natives", "run", "embed:native"]);
54
+ await runCommand(
55
+ ["bun", "--cwd=../natives", "run", "embed:native"],
56
+ crossTarget
57
+ ? { ...Bun.env, TARGET_PLATFORM: crossPlatform as string, TARGET_ARCH: crossArch as string }
58
+ : Bun.env,
59
+ );
47
60
  await runCommand(["bun", "scripts/embed-mupdf-wasm.ts", "--generate"]);
48
61
  try {
49
62
  const buildEnv = shouldAdhocSignDarwinBinary() ? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" } : Bun.env;
@@ -52,6 +65,7 @@ async function main(): Promise<void> {
52
65
  "bun",
53
66
  "build",
54
67
  "--compile",
68
+ ...(bunTarget ? ["--target", bunTarget] : []),
55
69
  "--no-compile-autoload-bunfig",
56
70
  "--no-compile-autoload-dotenv",
57
71
  "--no-compile-autoload-tsconfig",
@@ -85,7 +99,7 @@ async function main(): Promise<void> {
85
99
  "./packages/coding-agent/src/extensibility/legacy-pi-ai-shim.ts",
86
100
  "./packages/coding-agent/src/extensibility/legacy-pi-coding-agent-shim.ts",
87
101
  "--outfile",
88
- "packages/coding-agent/dist/omp",
102
+ `packages/coding-agent/dist/${outName}`,
89
103
  ],
90
104
  buildEnv,
91
105
  repoRoot,
@@ -55,10 +55,15 @@ export interface ClassifyDifficultyDeps {
55
55
  }
56
56
 
57
57
  /**
58
- * Classify `promptText` and return a concrete effort clamped to `deps.model`.
58
+ * Classify `promptText` and return a concrete effort clamped to `deps.model`,
59
+ * or `undefined` when the model has no controllable effort surface (auto has
60
+ * nothing to pick — the caller leaves the prior reasoning level in place).
59
61
  * @throws when the backend cannot produce a usable classification.
60
62
  */
61
- export async function classifyDifficulty(promptText: string, deps: ClassifyDifficultyDeps): Promise<Effort> {
63
+ export async function classifyDifficulty(
64
+ promptText: string,
65
+ deps: ClassifyDifficultyDeps,
66
+ ): Promise<Effort | undefined> {
62
67
  const backend = deps.settings.get("providers.autoThinkingModel");
63
68
  const input = prepareClassifierInput(promptText);
64
69
  const effort =
package/src/cli/args.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * CLI argument parsing and help display
3
3
  */
4
- import { type Effort, THINKING_EFFORTS } from "@oh-my-pi/pi-catalog/effort";
5
4
  import { APP_NAME, CONFIG_DIR_NAME, logger } from "@oh-my-pi/pi-utils";
6
5
  import chalk from "chalk";
7
- import { parseEffort } from "../thinking";
6
+ import { CLI_THINKING_LEVELS, type ConfiguredThinkingLevel, parseCliThinkingLevel } from "../thinking";
8
7
  import { BUILTIN_TOOL_NAMES } from "../tools/builtin-names";
9
8
  import {
10
9
  OPTIONAL_FLAGS,
@@ -32,7 +31,7 @@ export interface Args {
32
31
  apiKey?: string;
33
32
  systemPrompt?: string;
34
33
  appendSystemPrompt?: string;
35
- thinking?: Effort;
34
+ thinking?: ConfiguredThinkingLevel;
36
35
  hideThinking?: boolean;
37
36
  advisor?: boolean;
38
37
  continue?: boolean;
@@ -89,9 +88,9 @@ export interface Args {
89
88
  */
90
89
  const PARSE_DEPS: ParseDeps = {
91
90
  logger,
92
- parseEffort,
91
+ parseThinking: parseCliThinkingLevel,
93
92
  builtinToolNames: BUILTIN_TOOL_NAMES,
94
- thinkingEfforts: THINKING_EFFORTS,
93
+ thinkingEfforts: CLI_THINKING_LEVELS,
95
94
  };
96
95
 
97
96
  export function parseArgs(inputArgs: string[], extensionFlags?: Map<string, { type: "boolean" | "string" }>): Args {
@@ -30,7 +30,7 @@
30
30
  * real implementations at the dispatch site.
31
31
  */
32
32
 
33
- import type { Effort } from "@oh-my-pi/pi-ai";
33
+ import type { ConfiguredThinkingLevel } from "../thinking";
34
34
  import type { Args } from "./args";
35
35
 
36
36
  /**
@@ -44,7 +44,7 @@ import type { Args } from "./args";
44
44
  */
45
45
  export interface ParseDeps {
46
46
  logger: { warn: (message: string, meta?: Record<string, unknown>) => void };
47
- parseEffort: (value: string | null | undefined) => Effort | undefined;
47
+ parseThinking: (value: string | null | undefined) => ConfiguredThinkingLevel | undefined;
48
48
  builtinToolNames: readonly string[];
49
49
  thinkingEfforts: readonly string[];
50
50
  }
@@ -165,7 +165,7 @@ export const STRING_SETTERS: Record<string, StringSetter> = {
165
165
  result.tools = valid;
166
166
  },
167
167
  "--thinking": (result, value, deps) => {
168
- const thinking = deps.parseEffort(value);
168
+ const thinking = deps.parseThinking(value);
169
169
  if (thinking !== undefined) {
170
170
  result.thinking = thinking;
171
171
  } else {
@@ -5,17 +5,14 @@ export const interactionFixtures: Record<string, GalleryFixture> = {
5
5
  todo: {
6
6
  label: "Todo",
7
7
  streamingArgs: {
8
- ops: [{ op: "init", list: [{ phase: "Foundation", items: ["Scaffold crate"] }] }],
8
+ op: "init",
9
+ list: [{ phase: "Foundation", items: ["Scaffold crate"] }],
9
10
  },
10
11
  args: {
11
- ops: [
12
- {
13
- op: "init",
14
- list: [
15
- { phase: "Foundation", items: ["Scaffold crate", "Wire workspace"] },
16
- { phase: "Auth", items: ["Port credential store", "Wire OAuth providers"] },
17
- ],
18
- },
12
+ op: "init",
13
+ list: [
14
+ { phase: "Foundation", items: ["Scaffold crate", "Wire workspace"] },
15
+ { phase: "Auth", items: ["Port credential store", "Wire OAuth providers"] },
19
16
  ],
20
17
  },
21
18
  result: {